bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode) { const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key)); const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR); const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600); #ifdef O_CLOEXEC // First try with O_CLOEXEC flag, if that fails, fall back to normal flags EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag | O_CLOEXEC, omode)); if (hand == -1) EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode)); #else EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode)); #endif if (hand == -1) { const int errorNumber = errno; const QLatin1String function("QSharedMemory::attach (shm_open)"); switch (errorNumber) { case ENAMETOOLONG: case EINVAL: errorString = QSharedMemory::tr("%1: bad name").arg(function); error = QSharedMemory::KeyError; break; default: setErrorString(function); } hand = -1; return false; } // grab the size QT_STATBUF st; if (QT_FSTAT(hand, &st) == -1) { setErrorString(QLatin1String("QSharedMemory::attach (fstat)")); cleanHandle(); return false; } size = st.st_size; // grab the memory const int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE); memory = QT_MMAP(0, size, mprot, MAP_SHARED, hand, 0); if (memory == MAP_FAILED || !memory) { setErrorString(QLatin1String("QSharedMemory::attach (mmap)")); cleanHandle(); memory = 0; size = 0; return false; } #ifdef F_ADD_SEALS // Make sure the shared memory region will not shrink // otherwise someone could cause SIGBUS on us. // (see http://lwn.net/Articles/594919/) fcntl(hand, F_ADD_SEALS, F_SEAL_SHRINK); #endif return true; }
bool QWSSharedMemory::create(int size) { if (shmId != -1) detach(); #ifndef QT_POSIX_IPC shmId = shmget(IPC_PRIVATE, size, IPC_CREAT | 0600); #else // ### generate really unique IDs shmId = (getpid() << 16) + (localUniqueId.fetchAndAddRelaxed(1) % ushort(-1)); QByteArray shmName = makeKey(shmId); EINTR_LOOP(hand, shm_open(shmName.constData(), O_RDWR | O_CREAT, 0660)); if (hand != -1) { // the size may only be set once; ignore errors int ret; EINTR_LOOP(ret, ftruncate(hand, size)); if (ret == -1) shmId = -1; } else { shmId = -1; } #endif if (shmId == -1) { #ifdef QT_SHM_DEBUG perror("QWSSharedMemory::create():"); qWarning("Error allocating shared memory of size %d", size); #endif detach(); return false; } #ifndef QT_POSIX_IPC shmBase = shmat(shmId, 0, 0); // On Linux, it is possible to attach a shared memory segment even if it // is already marked to be deleted. However, POSIX.1-2001 does not specify // this behaviour and many other implementations do not support it. shmctl(shmId, IPC_RMID, 0); #else // grab the size QT_STATBUF st; if (QT_FSTAT(hand, &st) != -1) { shmSize = st.st_size; // grab the memory shmBase = mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, hand, 0); } #endif if (shmBase == (void*)-1 || !shmBase) { #ifdef QT_SHM_DEBUG perror("QWSSharedMemory::create():"); qWarning("Error attaching to shared memory id %d", shmId); #endif detach(); return false; } return true; }
FUNCTION CODE c_getmsg ( struct PATH *path, /* In: Path Control Block */ GENPTR buffer, /* Out: Buffer to store msg in */ COUNT *size /* In: Buffer size */ ) { GENPTR bufptr; /* extra buffer ptr to handle */ /* split messages. */ FUNINT bufsiz; CODE retcode = SUCCESS; /* Return status */ COUNT len; /* Length of msg read */ struct sockaddr from; /* Socket address of sender */ socklen_t fromlen; /* Length of socket address */ COUNT snew; /* Socket ID */ fromlen = sizeof (from); EINTR_LOOP(snew, accept ((*path).chnl, &from, &fromlen)); if (snew < 0) { c_errtxt (path, errno); retcode = FAIL; } else { /* Connection accepted, read message */ bufptr = buffer; bufsiz = *size; do { /* until all of the message is in or error. */ #ifdef apollo EINTR_LOOP(len, read (snew, bufptr, bufsiz)); #else EINTR_LOOP(len, recv (snew, bufptr, bufsiz, 0)); #endif if (len <= 0) break; bufptr += len; bufsiz -= len; } while (bufsiz > 0); close (snew); if (len < 0) { c_errtxt (path, errno); retcode = FAIL; } } return (retcode); }
void Process::dumpCore() { int child = fork(); switch (child) { case -1: // fork error fprintf(stderr, "Process.dumpCore: fork error: %s\n", strerror(errno)); break; case 0: { // child abort(); break; } default: { // parent int status = 0; EINTR_LOOP(::waitpid(child, &status, 0)); char filename[80]; time_t now = time(nullptr); struct tm tm; localtime_r(&now, &tm); strftime(filename, sizeof(filename), "%Y%m%d-%H%M%S.core", &tm); int rv = ::rename("core", filename); if (rv < 0) { fprintf(stderr, "Process.dumpCore: core rename error: %s\n", strerror(errno)); } else { fprintf(stderr, "Process.dumpCore: %s\n", filename); } } } }
Process::~Process() { if (pid_ > 0) EINTR_LOOP(::waitpid(pid_, &status_, 0)); //fprintf(stderr, "~Process(): rv=%d, errno=%s\n", rv, strerror(errno)); }
FUNCTION COUNT c_write ( struct PATH *path, /* in: path structure */ FUNINT fd, /* in: file desc from c_accept */ GENPTR buffer, /* in: buffer to receive next message */ FUNINT size /* in: buffer size */ ) { GENPTR bufptr; /* extra buffer ptr to handle */ COUNT bufsiz; COUNT len; /* Length of msg read */ COUNT totalBytes = 0; bufptr = buffer; bufsiz = size; do /* until all of the message is in or error. */ { EINTR_LOOP(len, send (fd, bufptr, bufsiz, 0)); if (len <= 0) break; bufptr += len; bufsiz -= len; totalBytes += len; } while (bufsiz > 0); if (len < 0) { c_errtxt (path, errno); return (0); } return (totalBytes); }
int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, const struct timeval *orig_timeout) { if (!orig_timeout) { // no timeout -> block forever register int ret; EINTR_LOOP(ret, select(nfds, fdread, fdwrite, fdexcept, 0)); return ret; } timeval start = qt_gettime(); timeval timeout = *orig_timeout; // loop and recalculate the timeout as needed int ret; forever { ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeout); if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *orig_timeout)) { // timeout during update // or clock reset, fake timeout error return 0; } } }
int qt_safe_select(int nfds, fd_set *fdread, fd_set *fdwrite, fd_set *fdexcept, const struct timespec *orig_timeout) { if (!orig_timeout) { // no timeout -> block forever int ret; EINTR_LOOP(ret, select(nfds, fdread, fdwrite, fdexcept, 0)); return ret; } timespec start = qt_gettime(); timespec timeout = *orig_timeout; // loop and recalculate the timeout as needed int ret; forever { #ifndef Q_OS_QNX ret = ::pselect(nfds, fdread, fdwrite, fdexcept, &timeout, 0); #else timeval timeoutVal; timeoutVal.tv_sec = timeout.tv_sec; timeoutVal.tv_usec = timeout.tv_nsec / 1000; ret = ::select(nfds, fdread, fdwrite, fdexcept, &timeoutVal); #endif if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *orig_timeout)) { // timeout during update // or clock reset, fake timeout error return 0; } } }
void QStorageInfoPrivate::retrieveVolumeInfo() { QT_STATFSBUF statfs_buf; int result; EINTR_LOOP(result, QT_STATFS(QFile::encodeName(rootPath).constData(), &statfs_buf)); if (result == 0) { valid = true; ready = true; #if defined(Q_OS_BSD4) && !defined(Q_OS_NETBSD) bytesTotal = statfs_buf.f_blocks * statfs_buf.f_bsize; bytesFree = statfs_buf.f_bfree * statfs_buf.f_bsize; bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_bsize; #else bytesTotal = statfs_buf.f_blocks * statfs_buf.f_frsize; bytesFree = statfs_buf.f_bfree * statfs_buf.f_frsize; bytesAvailable = statfs_buf.f_bavail * statfs_buf.f_frsize; #endif blockSize = statfs_buf.f_bsize; #if defined(Q_OS_ANDROID) || defined (Q_OS_BSD4) #if defined(_STATFS_F_FLAGS) readOnly = (statfs_buf.f_flags & ST_RDONLY) != 0; #endif #else readOnly = (statfs_buf.f_flag & ST_RDONLY) != 0; #endif } }
/*! \internal */ bool QSystemSemaphorePrivate::modifySemaphore(int count) { if (-1 == handle()) return false; struct sembuf operation; operation.sem_num = 0; operation.sem_op = count; operation.sem_flg = SEM_UNDO; int res; EINTR_LOOP(res, semop(semaphore, &operation, 1)); if (-1 == res) { // If the semaphore was removed be nice and create it and then modifySemaphore again if (errno == EINVAL || errno == EIDRM) { semaphore = -1; cleanHandle(); handle(); return modifySemaphore(count); } setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); #if defined QSYSTEMSEMAPHORE_DEBUG qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL; #endif return false; } clearError(); return true; }
/*! \internal Behaves as close to POSIX poll(2) as practical but may be implemented using select(2) where necessary. In that case, returns -1 and sets errno to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE. */ int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts) { if (!timeout_ts) { // no timeout -> block forever int ret; EINTR_LOOP(ret, qt_ppoll(fds, nfds, Q_NULLPTR)); return ret; } timespec start = qt_gettime(); timespec timeout = *timeout_ts; // loop and recalculate the timeout as needed forever { const int ret = qt_ppoll(fds, nfds, &timeout); if (ret != -1 || errno != EINTR) return ret; // recalculate the timeout if (!time_update(&timeout, start, *timeout_ts)) { // timeout during update // or clock reset, fake timeout error return 0; } } }
void qt_nanosleep(timespec amount) { // Mac doesn't have clock_nanosleep, but it does have nanosleep. // nanosleep is POSIX.1-1993 int r; EINTR_LOOP(r, nanosleep(&amount, &amount)); }
bool QSharedMemoryPrivate::create(int size) { if (!handle()) return false; const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key)); int fd; #ifdef O_CLOEXEC // First try with O_CLOEXEC flag, if that fails, fall back to normal flags EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600)); if (fd == -1) EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600)); #else EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600)); #endif if (fd == -1) { const int errorNumber = errno; const QLatin1String function("QSharedMemory::attach (shm_open)"); switch (errorNumber) { case ENAMETOOLONG: case EINVAL: errorString = QSharedMemory::tr("%1: bad name").arg(function); error = QSharedMemory::KeyError; break; default: setErrorString(function); } return false; } // the size may only be set once int ret; EINTR_LOOP(ret, QT_FTRUNCATE(fd, size)); if (ret == -1) { setErrorString(QLatin1String("QSharedMemory::create (ftruncate)")); qt_safe_close(fd); return false; } qt_safe_close(fd); return true; }
/*! Unlocks the semaphore. If other processes were blocking waiting to lock() the semaphore, one of them will wake up and succeed in locking. */ void QLock::unlock() { if (!isValid()) return; if (data->count > 0) { data->count--; if (!data->count) { int rv; #if defined(QT_NO_SEMAPHORE) EINTR_LOOP(rv, flock(data->id, LOCK_UN)); #elif !defined(QT_POSIX_IPC) sembuf sops; sops.sem_num = 0; sops.sem_op = type == Write ? MAX_LOCKS : 1; sops.sem_flg = SEM_UNDO; EINTR_LOOP(rv, semop(data->id, &sops, 1)); #else if (type == Write) { sem_post(data->wsem); rv = sem_post(data->rsem); } else { EINTR_LOOP(rv, sem_wait(data->wsem)); if (rv != -1) { sem_post(data->id); int semval; sem_getvalue(data->id, &semval); if (semval == MAX_LOCKS) sem_post(data->rsem); rv = sem_post(data->wsem); } } #endif if (rv == -1) qDebug("QLock::unlock(): %s", strerror(errno)); } } else { qDebug("QLock::unlock(): Unlock without corresponding lock"); } }
void qt_nanosleep(timespec amount) { // We'd like to use clock_nanosleep. // // But clock_nanosleep is from POSIX.1-2001 and both are *not* // affected by clock changes when using relative sleeps, even for // CLOCK_REALTIME. // // nanosleep is POSIX.1-1993 int r; EINTR_LOOP(r, nanosleep(&amount, &amount)); }
/*! Locks the semaphore with a lock of type \a t. Locks can either be \c Read or \c Write. If a lock is \c Read, attempts by other processes to obtain \c Read locks will succeed, and \c Write attempts will block until the lock is unlocked. If locked as \c Write, all attempts to lock by other processes will block until the lock is unlocked. Locks are stacked: i.e. a given QLock can be locked multiple times by the same process without blocking, and will only be unlocked after a corresponding number of unlock() calls. */ void QLock::lock(Type t) { if (!isValid()) return; if (!data->count) { type = t; int rv; #if defined(QT_NO_SEMAPHORE) int op = type == Write ? LOCK_EX : LOCK_SH; EINTR_LOOP(rv, flock(data->id, op)); #elif !defined(QT_POSIX_IPC) sembuf sops; sops.sem_num = 0; sops.sem_op = type == Write ? -MAX_LOCKS : -1; sops.sem_flg = SEM_UNDO; EINTR_LOOP(rv, semop(data->id, &sops, 1)); #else if (type == Write) { EINTR_LOOP(rv, sem_wait(data->rsem)); if (rv != -1) { EINTR_LOOP(rv, sem_wait(data->wsem)); if (rv == -1) sem_post(data->rsem); } } else { EINTR_LOOP(rv, sem_wait(data->wsem)); if (rv != -1) { EINTR_LOOP(rv, sem_trywait(data->rsem)); if (rv != -1 || errno == EAGAIN) { EINTR_LOOP(rv, sem_wait(data->id)); if (rv == -1) { int semval; sem_getvalue(data->id, &semval); if (semval == MAX_LOCKS) sem_post(data->rsem); } } rv = sem_post(data->wsem); } } #endif if (rv == -1) { qDebug("QLock::lock(): %s", strerror(errno)); return; } } else if (type == Read && t == Write) { qDebug("QLock::lock(): Attempt to lock for write while locked for read"); } data->count++; }
bool QWSLock::down(unsigned short semNum, int) { int ret; #ifndef QT_POSIX_IPC sembuf sops = { semNum, -1, 0 }; // As the BackingStore lock is a mutex, and only one process may own // the lock, it's safe to use SEM_UNDO. On the other hand, the // Communication lock is locked by the client but unlocked by the // server and therefore can't use SEM_UNDO. if (semNum == BackingStore) sops.sem_flg |= SEM_UNDO; EINTR_LOOP(ret, semop(semId, &sops, 1)); #else EINTR_LOOP(ret, sem_wait(sems[semNum])); #endif if (ret == -1) { qDebug("QWSLock::down(): %s", strerror(errno)); return false; } return true; }
void QEventDispatcherUNIX::wakeUp() { Q_D(QEventDispatcherUNIX); if (d->wakeUps.testAndSetAcquire(0, 1)) { #ifndef QT_NO_EVENTFD if (d->thread_pipe[1] == -1) { // eventfd eventfd_t value = 1; int ret; EINTR_LOOP(ret, eventfd_write(d->thread_pipe[0], value)); return; } #endif char c = 0; qt_safe_write( d->thread_pipe[1], &c, 1 ); } }
FUNCTION COUNT c_accept ( struct PATH *path /* in: path name */ ) { struct sockaddr from; /* Socket address of sender */ socklen_t fromlen; /* Length of socket address */ COUNT socketFd; fromlen = sizeof (from); EINTR_LOOP(socketFd, accept ((*path).chnl, &from, &fromlen)); if (socketFd < 0) { c_errtxt (path, errno); return (-1); } return (socketFd); }
/** tests whether child Process has exited already. */ bool Process::expired() { if (pid_ <= 0) return true; int rv; EINTR_LOOP(rv = ::waitpid(pid_, &status_, WNOHANG)); if (rv == 0) // child not exited yet return false; if (rv < 0) // error return false; pid_ = -1; return true; }
FUNCTION CODE c_putmsg ( struct PATH *path, /* In: Path Control Block */ GENPTR buffer, /* In: Message to be output */ FUNINT size /* In: Message size */ ) { CODE retcode = SUCCESS; /* Return status */ CODE status; /* Local status code */ EINTR_LOOP(status,send ( (*path).chnl, buffer, size, 0 )); if (status < 0) { c_errtxt (path, errno); retcode = FAIL; } return (retcode); }
QT_BEGIN_NAMESPACE #define EINTR_LOOP(var, cmd) \ do { \ var = cmd; \ } while (var == -1 && errno == EINTR) // don't call QT_OPEN or ::open // call qt_safe_open static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 0777) { #ifdef O_CLOEXEC flags |= O_CLOEXEC; #endif int fd; EINTR_LOOP(fd, ::open(pathname, flags, mode)); // unknown flags are ignored, so we have no way of verifying if // O_CLOEXEC was accepted if (fd != -1) ::fcntl(fd, F_SETFD, FD_CLOEXEC); return fd; }
bool QWSSharedMemory::attach(int id) { if (shmId == id) return id != -1; detach(); if (id == -1) return false; shmId = id; #ifndef QT_POSIX_IPC shmBase = shmat(shmId, 0, 0); #else QByteArray shmName = makeKey(shmId); EINTR_LOOP(hand, shm_open(shmName.constData(), O_RDWR, 0660)); if (hand != -1) { // grab the size QT_STATBUF st; if (QT_FSTAT(hand, &st) != -1) { shmSize = st.st_size; // grab the memory shmBase = mmap(0, shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, hand, 0); } } #endif if (shmBase == (void*)-1 || !shmBase) { #ifdef QT_SHM_DEBUG perror("QWSSharedMemory::attach():"); qWarning("Error attaching to shared memory id %d", shmId); #endif detach(); return false; } return true; }
static inline qint64 qt_safe_write(int fd, const void *data, qint64 len) { qint64 ret = 0; EINTR_LOOP(ret, ::write(fd, data, len)); return ret; }
void Process::setupChild(const std::string& _exe, const ArgumentList& _args, const Environment& _env, const std::string& _workdir) { // restore signal handler(s) ::signal(SIGPIPE, SIG_DFL); // setup environment int k = 0; std::vector<char *> env(_env.size() + 1); for (Environment::const_iterator i = _env.cbegin(), e = _env.cend(); i != e; ++i) { char *buf = new char[i->first.size() + i->second.size() + 2]; ::memcpy(buf, i->first.c_str(), i->first.size()); buf[i->first.size()] = '='; ::memcpy(buf + i->first.size() + 1, i->second.c_str(), i->second.size() + 1); //::fprintf(stderr, "proc[%d]: setting env[%d]: %s\n", getpid(), k, buf); //::fflush(stderr); env[k++] = buf; } env[_env.size()] = 0; // setup args std::vector<char *> args(_args.size() + 2); args[0] = const_cast<char *>(_exe.c_str()); //::fprintf(stderr, "args[%d] = %s\n", 0, args[0]); for (int i = 0, e = _args.size(); i != e; ++i) { args[i + 1] = const_cast<char *>(_args[i].c_str()); //::fprintf(stderr, "args[%d] = %s\n", i + 1, args[i + 1]); } args[args.size() - 1] = 0; // chdir if (!_workdir.empty()) { ::chdir(_workdir.c_str()); } // setup I/O EINTR_LOOP(::close(STDIN_FILENO)); EINTR_LOOP(::close(STDOUT_FILENO)); EINTR_LOOP(::close(STDERR_FILENO)); EINTR_LOOP(::dup2(input_.remote(), STDIN_FILENO)); EINTR_LOOP(::dup2(output_.remote(), STDOUT_FILENO)); EINTR_LOOP(::dup2(error_.remote(), STDERR_FILENO)); #if 0 // this is basically working but a very bad idea for high performance (XXX better get O_CLOEXEC working) for (int i = 3; i < 1024; ++i) ::close(i); #endif // input_.close(); // output_.close(); // error_.close(); // finally execute ::execve(args[0], &args[0], &env[0]); // OOPS ::fprintf(stderr, "proc[%d]: execve(%s) error: %s\n", getpid(), args[0], strerror(errno)); ::fflush(stderr); ::_exit(1); }
/*! \internal */ bool QSystemSemaphorePrivate::modifySemaphore(int count) { #ifndef QT_POSIX_IPC if (-1 == handle()) return false; struct sembuf operation; operation.sem_num = 0; operation.sem_op = count; operation.sem_flg = SEM_UNDO; register int res; EINTR_LOOP(res, semop(semaphore, &operation, 1)); if (-1 == res) { // If the semaphore was removed be nice and create it and then modifySemaphore again if (errno == EINVAL || errno == EIDRM) { semaphore = -1; cleanHandle(); handle(); return modifySemaphore(count); } setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore")); #ifdef QSYSTEMSEMAPHORE_DEBUG qDebug() << QLatin1String("QSystemSemaphore::modify failed") << count << semctl(semaphore, 0, GETVAL) << errno << EIDRM << EINVAL; #endif return false; } #else if (!handle()) return false; if (count > 0) { int cnt = count; do { if (sem_post(semaphore) == -1) { setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_post)")); #ifdef QSYSTEMSEMAPHORE_DEBUG qDebug() << QLatin1String("QSystemSemaphore::modify sem_post failed") << count << errno; #endif // rollback changes to preserve the SysV semaphore behavior for ( ; cnt < count; ++cnt) { register int res; EINTR_LOOP(res, sem_wait(semaphore)); } return false; } --cnt; } while (cnt > 0); } else { register int res; EINTR_LOOP(res, sem_wait(semaphore)); if (res == -1) { // If the semaphore was removed be nice and create it and then modifySemaphore again if (errno == EINVAL || errno == EIDRM) { semaphore = SEM_FAILED; return modifySemaphore(count); } setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_wait)")); #ifdef QSYSTEMSEMAPHORE_DEBUG qDebug() << QLatin1String("QSystemSemaphore::modify sem_wait failed") << count << errno; #endif return false; } } #endif // QT_POSIX_IPC return true; }