1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlocalsocket.h"
5#include "qlocalsocket_p.h"
6#include "qnet_unix_p.h"
7
8#include <sys/types.h>
9#include <sys/socket.h>
10#include <sys/un.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <errno.h>
14
15#include <qdir.h>
16#include <qdeadlinetimer.h>
17#include <qdebug.h>
18#include <qstringconverter.h>
19
20#ifdef Q_OS_VXWORKS
21# include <selectLib.h>
22#endif
23
24using namespace std::chrono_literals;
25
26#define QT_CONNECT_TIMEOUT 30000
27
28QT_BEGIN_NAMESPACE
29
30using namespace Qt::StringLiterals;
31
32namespace {
33// determine the full server path
34static QString pathNameForConnection(const QString &connectingName,
35 QLocalSocket::SocketOptions options)
36{
37 if (options.testFlag(flag: QLocalSocket::AbstractNamespaceOption)
38 || connectingName.startsWith(c: u'/')) {
39 return connectingName;
40 }
41
42 return QDir::tempPath() + u'/' + connectingName;
43}
44
45static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
46{
47 // For OS that does not support abstract namespace the AbstractNamespaceOption
48 // option is cleared.
49 if (!PlatformSupportsAbstractNamespace)
50 return QLocalSocket::NoOptions;
51 return srcOptions;
52}
53}
54
55QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
56 delayConnect(nullptr),
57 connectTimer(nullptr),
58 connectingSocket(-1),
59 state(QLocalSocket::UnconnectedState),
60 socketOptions(QLocalSocket::NoOptions)
61{
62}
63
64void QLocalSocketPrivate::init()
65{
66 Q_Q(QLocalSocket);
67 // QIODevice signals
68 q->connect(sender: &unixSocket, SIGNAL(bytesWritten(qint64)),
69 receiver: q, SIGNAL(bytesWritten(qint64)));
70 q->connect(sender: &unixSocket, SIGNAL(readyRead()), receiver: q, SIGNAL(readyRead()));
71 // QAbstractSocket signals
72 q->connect(sender: &unixSocket, SIGNAL(connected()), receiver: q, SIGNAL(connected()));
73 q->connect(sender: &unixSocket, SIGNAL(disconnected()), receiver: q, SIGNAL(disconnected()));
74 q->connect(sender: &unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
75 receiver: q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
76 q->connect(sender: &unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
77 receiver: q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
78 q->connect(sender: &unixSocket, SIGNAL(readChannelFinished()), receiver: q, SIGNAL(readChannelFinished()));
79 unixSocket.setParent(q);
80}
81
82void QLocalSocketPrivate::_q_errorOccurred(QAbstractSocket::SocketError socketError)
83{
84 Q_Q(QLocalSocket);
85 QString function = "QLocalSocket"_L1;
86 QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
87 QString errorString = generateErrorString(error, function);
88 q->setErrorString(errorString);
89 emit q->errorOccurred(socketError: error);
90}
91
92void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
93{
94 Q_Q(QLocalSocket);
95 QLocalSocket::LocalSocketState currentState = state;
96 switch(newState) {
97 case QAbstractSocket::UnconnectedState:
98 state = QLocalSocket::UnconnectedState;
99 serverName.clear();
100 fullServerName.clear();
101 break;
102 case QAbstractSocket::ConnectingState:
103 state = QLocalSocket::ConnectingState;
104 break;
105 case QAbstractSocket::ConnectedState:
106 state = QLocalSocket::ConnectedState;
107 break;
108 case QAbstractSocket::ClosingState:
109 state = QLocalSocket::ClosingState;
110 break;
111 default:
112#if defined QLOCALSOCKET_DEBUG
113 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
114#endif
115 return;
116 }
117 if (currentState != state)
118 emit q->stateChanged(socketState: state);
119}
120
121QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
122{
123 QString errorString;
124 switch (error) {
125 case QLocalSocket::ConnectionRefusedError:
126 errorString = QLocalSocket::tr(s: "%1: Connection refused").arg(a: function);
127 break;
128 case QLocalSocket::PeerClosedError:
129 errorString = QLocalSocket::tr(s: "%1: Remote closed").arg(a: function);
130 break;
131 case QLocalSocket::ServerNotFoundError:
132 errorString = QLocalSocket::tr(s: "%1: Invalid name").arg(a: function);
133 break;
134 case QLocalSocket::SocketAccessError:
135 errorString = QLocalSocket::tr(s: "%1: Socket access error").arg(a: function);
136 break;
137 case QLocalSocket::SocketResourceError:
138 errorString = QLocalSocket::tr(s: "%1: Socket resource error").arg(a: function);
139 break;
140 case QLocalSocket::SocketTimeoutError:
141 errorString = QLocalSocket::tr(s: "%1: Socket operation timed out").arg(a: function);
142 break;
143 case QLocalSocket::DatagramTooLargeError:
144 errorString = QLocalSocket::tr(s: "%1: Datagram too large").arg(a: function);
145 break;
146 case QLocalSocket::ConnectionError:
147 errorString = QLocalSocket::tr(s: "%1: Connection error").arg(a: function);
148 break;
149 case QLocalSocket::UnsupportedSocketOperationError:
150 errorString = QLocalSocket::tr(s: "%1: The socket operation is not supported").arg(a: function);
151 break;
152 case QLocalSocket::OperationError:
153 errorString = QLocalSocket::tr(s: "%1: Operation not permitted when socket is in this state").arg(a: function);
154 break;
155 case QLocalSocket::UnknownSocketError:
156 default:
157 errorString = QLocalSocket::tr(s: "%1: Unknown error %2").arg(a: function).arg(errno);
158 }
159 return errorString;
160}
161
162void QLocalSocketPrivate::setErrorAndEmit(QLocalSocket::LocalSocketError error, const QString &function)
163{
164 Q_Q(QLocalSocket);
165 switch (error) {
166 case QLocalSocket::ConnectionRefusedError:
167 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
168 break;
169 case QLocalSocket::PeerClosedError:
170 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
171 break;
172 case QLocalSocket::ServerNotFoundError:
173 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
174 break;
175 case QLocalSocket::SocketAccessError:
176 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
177 break;
178 case QLocalSocket::SocketResourceError:
179 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
180 break;
181 case QLocalSocket::SocketTimeoutError:
182 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
183 break;
184 case QLocalSocket::DatagramTooLargeError:
185 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
186 break;
187 case QLocalSocket::ConnectionError:
188 unixSocket.setSocketError(QAbstractSocket::NetworkError);
189 break;
190 case QLocalSocket::UnsupportedSocketOperationError:
191 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
192 break;
193 case QLocalSocket::UnknownSocketError:
194 default:
195 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
196 }
197
198 QString errorString = generateErrorString(error, function);
199 q->setErrorString(errorString);
200 emit q->errorOccurred(socketError: error);
201
202 // errors cause a disconnect
203 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
204 bool stateChanged = (state != QLocalSocket::UnconnectedState);
205 state = QLocalSocket::UnconnectedState;
206 q->close();
207 if (stateChanged)
208 q->emit stateChanged(socketState: state);
209}
210
211void QLocalSocket::connectToServer(OpenMode openMode)
212{
213 Q_D(QLocalSocket);
214 if (state() == ConnectedState || state() == ConnectingState) {
215 QString errorString = d->generateErrorString(error: QLocalSocket::OperationError, function: "QLocalSocket::connectToserver"_L1);
216 setErrorString(errorString);
217 emit errorOccurred(socketError: QLocalSocket::OperationError);
218 return;
219 }
220
221 d->errorString.clear();
222 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
223 d->state = ConnectingState;
224 emit stateChanged(socketState: d->state);
225
226 if (d->serverName.isEmpty()) {
227 d->setErrorAndEmit(error: ServerNotFoundError, function: "QLocalSocket::connectToServer"_L1);
228 return;
229 }
230
231 // create the socket
232 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, protocol: 0, O_NONBLOCK))) {
233 d->setErrorAndEmit(error: UnsupportedSocketOperationError, function: "QLocalSocket::connectToServer"_L1);
234 return;
235 }
236
237 // _q_connectToSocket does the actual connecting
238 d->connectingName = d->serverName;
239 d->connectingOpenMode = openMode;
240 d->_q_connectToSocket();
241 return;
242}
243
244/*!
245 \internal
246
247 Tries to connect connectingName and connectingOpenMode
248
249 \sa connectToServer(), waitForConnected()
250 */
251void QLocalSocketPrivate::_q_connectToSocket()
252{
253 Q_Q(QLocalSocket);
254
255 QLocalSocket::SocketOptions options = optionsForPlatform(srcOptions: socketOptions);
256 const QString connectingPathName = pathNameForConnection(connectingName, options);
257 const QByteArray encodedConnectingPathName = QFile::encodeName(fileName: connectingPathName);
258 struct ::sockaddr_un addr;
259 addr.sun_family = PF_UNIX;
260 memset(s: addr.sun_path, c: 0, n: sizeof(addr.sun_path));
261
262 // for abstract socket add 2 to length, to take into account trailing AND leading null
263 constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
264
265 if (sizeof(addr.sun_path) < static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
266 QString function = "QLocalSocket::connectToServer"_L1;
267 setErrorAndEmit(error: QLocalSocket::ServerNotFoundError, function);
268 return;
269 }
270
271 QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
272 if (options.testFlag(flag: QLocalSocket::AbstractNamespaceOption)) {
273 ::memcpy(dest: addr.sun_path + 1, src: encodedConnectingPathName.constData(),
274 n: encodedConnectingPathName.size() + 1);
275 addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
276 } else {
277 ::memcpy(dest: addr.sun_path, src: encodedConnectingPathName.constData(),
278 n: encodedConnectingPathName.size() + 1);
279 }
280 if (-1 == qt_safe_connect(sockfd: connectingSocket, addr: (struct sockaddr *)&addr, addrlen: addrSize)) {
281 QString function = "QLocalSocket::connectToServer"_L1;
282 switch (errno)
283 {
284 case EINVAL:
285 case ECONNREFUSED:
286 setErrorAndEmit(error: QLocalSocket::ConnectionRefusedError, function);
287 break;
288 case ENOENT:
289 setErrorAndEmit(error: QLocalSocket::ServerNotFoundError, function);
290 break;
291 case EACCES:
292 case EPERM:
293 setErrorAndEmit(error: QLocalSocket::SocketAccessError, function);
294 break;
295 case ETIMEDOUT:
296 setErrorAndEmit(error: QLocalSocket::SocketTimeoutError, function);
297 break;
298 case EAGAIN:
299 // Try again later, all of the sockets listening are full
300 if (!delayConnect) {
301 delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
302 q->connect(sender: delayConnect, SIGNAL(activated(QSocketDescriptor)), receiver: q, SLOT(_q_connectToSocket()));
303 }
304 if (!connectTimer) {
305 connectTimer = new QTimer(q);
306 q->connect(sender: connectTimer, SIGNAL(timeout()),
307 receiver: q, SLOT(_q_abortConnectionAttempt()),
308 Qt::DirectConnection);
309 connectTimer->start(QT_CONNECT_TIMEOUT);
310 }
311 delayConnect->setEnabled(true);
312 break;
313 default:
314 setErrorAndEmit(error: QLocalSocket::UnknownSocketError, function);
315 }
316 return;
317 }
318
319 // connected!
320 cancelDelayedConnect();
321
322 serverName = connectingName;
323 fullServerName = connectingPathName;
324 if (unixSocket.setSocketDescriptor(socketDescriptor: connectingSocket,
325 state: QAbstractSocket::ConnectedState, openMode: connectingOpenMode)) {
326 q->QIODevice::open(mode: connectingOpenMode);
327 q->emit connected();
328 } else {
329 QString function = "QLocalSocket::connectToServer"_L1;
330 setErrorAndEmit(error: QLocalSocket::UnknownSocketError, function);
331 }
332 connectingSocket = -1;
333 connectingName.clear();
334 connectingOpenMode = { };
335}
336
337bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
338 LocalSocketState socketState, OpenMode openMode)
339{
340 Q_D(QLocalSocket);
341 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
342 switch (socketState) {
343 case ConnectingState:
344 newSocketState = QAbstractSocket::ConnectingState;
345 break;
346 case ConnectedState:
347 newSocketState = QAbstractSocket::ConnectedState;
348 break;
349 case ClosingState:
350 newSocketState = QAbstractSocket::ClosingState;
351 break;
352 case UnconnectedState:
353 newSocketState = QAbstractSocket::UnconnectedState;
354 break;
355 }
356 QIODevice::open(mode: openMode);
357 d->state = socketState;
358 d->describeSocket(socketDescriptor);
359 return d->unixSocket.setSocketDescriptor(socketDescriptor,
360 state: newSocketState, openMode);
361}
362
363void QLocalSocketPrivate::describeSocket(qintptr socketDescriptor)
364{
365 bool abstractAddress = false;
366
367 struct ::sockaddr_un addr;
368 QT_SOCKLEN_T len = sizeof(addr);
369 memset(s: &addr, c: 0, n: sizeof(addr));
370 const int getpeernameStatus = ::getpeername(fd: socketDescriptor, addr: (sockaddr *)&addr, len: &len);
371 if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
372 // this is the case when we call it from QLocalServer, then there is no peername
373 len = sizeof(addr);
374 if (::getsockname(fd: socketDescriptor, addr: (sockaddr *)&addr, len: &len) != 0)
375 return;
376 }
377 if (parseSockaddr(addr, len: static_cast<uint>(len), fullServerName, serverName, abstractNamespace&: abstractAddress)) {
378 QLocalSocket::SocketOptions options = socketOptions.value();
379 socketOptions = options.setFlag(flag: QLocalSocket::AbstractNamespaceOption, on: abstractAddress);
380 }
381}
382
383bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
384 uint len,
385 QString &fullServerName,
386 QString &serverName,
387 bool &abstractNamespace)
388{
389 if (len <= offsetof(::sockaddr_un, sun_path))
390 return false;
391 len -= offsetof(::sockaddr_un, sun_path);
392 // check for abstract socket address
393 abstractNamespace = PlatformSupportsAbstractNamespace
394 && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
395 QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
396 // An abstract socket address can be arbitrary binary. To properly handle such a case,
397 // we'd have to add new access functions for this very specific case. Instead, we just
398 // attempt to decode it according to OS text encoding. If it fails we ignore the result.
399 QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
400 len - (abstractNamespace ? 1 : 0));
401 QString name = toUtf16(textData);
402 if (!name.isEmpty() && !toUtf16.hasError()) {
403 //conversion encodes the trailing zeros. So, in case of non-abstract namespace we
404 //chop them off as \0 character is not allowed in filenames
405 if (!abstractNamespace && (name.at(i: name.size() - 1) == QChar::fromLatin1(c: '\0'))) {
406 int truncPos = name.size() - 1;
407 while (truncPos > 0 && name.at(i: truncPos - 1) == QChar::fromLatin1(c: '\0'))
408 truncPos--;
409 name.truncate(pos: truncPos);
410 }
411 fullServerName = name;
412 serverName = abstractNamespace
413 ? name
414 : fullServerName.mid(position: fullServerName.lastIndexOf(c: u'/') + 1);
415 if (serverName.isEmpty())
416 serverName = fullServerName;
417 }
418 return true;
419}
420
421void QLocalSocketPrivate::_q_abortConnectionAttempt()
422{
423 Q_Q(QLocalSocket);
424 q->close();
425}
426
427void QLocalSocketPrivate::cancelDelayedConnect()
428{
429 if (delayConnect) {
430 delayConnect->setEnabled(false);
431 delete delayConnect;
432 delayConnect = nullptr;
433 connectTimer->stop();
434 delete connectTimer;
435 connectTimer = nullptr;
436 }
437}
438
439qintptr QLocalSocket::socketDescriptor() const
440{
441 Q_D(const QLocalSocket);
442 return d->unixSocket.socketDescriptor();
443}
444
445qint64 QLocalSocket::readData(char *data, qint64 c)
446{
447 Q_D(QLocalSocket);
448 return d->unixSocket.read(data, maxlen: c);
449}
450
451qint64 QLocalSocket::readLineData(char *data, qint64 maxSize)
452{
453 if (!maxSize)
454 return 0;
455
456 // QIODevice::readLine() reserves space for the trailing '\0' byte,
457 // so we must read 'maxSize + 1' bytes.
458 return d_func()->unixSocket.readLine(data, maxlen: maxSize + 1);
459}
460
461qint64 QLocalSocket::skipData(qint64 maxSize)
462{
463 return d_func()->unixSocket.skip(maxSize);
464}
465
466qint64 QLocalSocket::writeData(const char *data, qint64 c)
467{
468 Q_D(QLocalSocket);
469 return d->unixSocket.writeData(data, maxSize: c);
470}
471
472void QLocalSocket::abort()
473{
474 Q_D(QLocalSocket);
475 d->unixSocket.abort();
476 close();
477}
478
479qint64 QLocalSocket::bytesAvailable() const
480{
481 Q_D(const QLocalSocket);
482 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
483}
484
485qint64 QLocalSocket::bytesToWrite() const
486{
487 Q_D(const QLocalSocket);
488 return d->unixSocket.bytesToWrite();
489}
490
491bool QLocalSocket::canReadLine() const
492{
493 Q_D(const QLocalSocket);
494 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
495}
496
497void QLocalSocket::close()
498{
499 Q_D(QLocalSocket);
500
501 QIODevice::close();
502 d->unixSocket.close();
503 d->cancelDelayedConnect();
504 if (d->connectingSocket != -1)
505 ::close(fd: d->connectingSocket);
506 d->connectingSocket = -1;
507 d->connectingName.clear();
508 d->connectingOpenMode = { };
509 d->serverName.clear();
510 d->fullServerName.clear();
511}
512
513bool QLocalSocket::waitForBytesWritten(int msecs)
514{
515 Q_D(QLocalSocket);
516 return d->unixSocket.waitForBytesWritten(msecs);
517}
518
519bool QLocalSocket::flush()
520{
521 Q_D(QLocalSocket);
522 return d->unixSocket.flush();
523}
524
525void QLocalSocket::disconnectFromServer()
526{
527 Q_D(QLocalSocket);
528 d->unixSocket.disconnectFromHost();
529}
530
531QLocalSocket::LocalSocketError QLocalSocket::error() const
532{
533 Q_D(const QLocalSocket);
534 switch (d->unixSocket.error()) {
535 case QAbstractSocket::ConnectionRefusedError:
536 return QLocalSocket::ConnectionRefusedError;
537 case QAbstractSocket::RemoteHostClosedError:
538 return QLocalSocket::PeerClosedError;
539 case QAbstractSocket::HostNotFoundError:
540 return QLocalSocket::ServerNotFoundError;
541 case QAbstractSocket::SocketAccessError:
542 return QLocalSocket::SocketAccessError;
543 case QAbstractSocket::SocketResourceError:
544 return QLocalSocket::SocketResourceError;
545 case QAbstractSocket::SocketTimeoutError:
546 return QLocalSocket::SocketTimeoutError;
547 case QAbstractSocket::DatagramTooLargeError:
548 return QLocalSocket::DatagramTooLargeError;
549 case QAbstractSocket::NetworkError:
550 return QLocalSocket::ConnectionError;
551 case QAbstractSocket::UnsupportedSocketOperationError:
552 return QLocalSocket::UnsupportedSocketOperationError;
553 case QAbstractSocket::UnknownSocketError:
554 return QLocalSocket::UnknownSocketError;
555 default:
556#if defined QLOCALSOCKET_DEBUG
557 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
558#endif
559 break;
560 }
561 return UnknownSocketError;
562}
563
564bool QLocalSocket::isValid() const
565{
566 Q_D(const QLocalSocket);
567 return d->unixSocket.isValid();
568}
569
570qint64 QLocalSocket::readBufferSize() const
571{
572 Q_D(const QLocalSocket);
573 return d->unixSocket.readBufferSize();
574}
575
576void QLocalSocket::setReadBufferSize(qint64 size)
577{
578 Q_D(QLocalSocket);
579 d->unixSocket.setReadBufferSize(size);
580}
581
582bool QLocalSocket::waitForConnected(int msec)
583{
584 Q_D(QLocalSocket);
585
586 if (state() != ConnectingState)
587 return (state() == ConnectedState);
588
589 pollfd pfd = qt_make_pollfd(fd: d->connectingSocket, POLLIN);
590
591 QDeadlineTimer deadline{msec};
592 auto remainingTime = deadline.remainingTimeAsDuration();
593
594 do {
595 const int result = qt_safe_poll(fds: &pfd, nfds: 1, deadline);
596 if (result == -1)
597 d->setErrorAndEmit(error: QLocalSocket::UnknownSocketError,
598 function: "QLocalSocket::waitForConnected"_L1);
599 else if (result > 0)
600 d->_q_connectToSocket();
601 } while (state() == ConnectingState
602 && (remainingTime = deadline.remainingTimeAsDuration()) > 0ns);
603
604 return (state() == ConnectedState);
605}
606
607bool QLocalSocket::waitForDisconnected(int msecs)
608{
609 Q_D(QLocalSocket);
610 if (state() == UnconnectedState) {
611 qWarning(msg: "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
612 return false;
613 }
614 return (d->unixSocket.waitForDisconnected(msecs));
615}
616
617bool QLocalSocket::waitForReadyRead(int msecs)
618{
619 Q_D(QLocalSocket);
620 if (state() == QLocalSocket::UnconnectedState)
621 return false;
622 return (d->unixSocket.waitForReadyRead(msecs));
623}
624
625QT_END_NAMESPACE
626

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/network/socket/qlocalsocket_unix.cpp