void Process::handleInput(int fd) { EventLoop::instance()->removeFileDescriptor(fd); //static int ting = 0; //printf("Process::handleInput (cnt=%d)\n", ++ting); for (;;) { if (mStdInBuffer.empty()) return; //printf("Process::handleInput in loop\n"); int w, want; const ByteArray& front = mStdInBuffer.front(); if (mStdInIndex) { want = front.size() - mStdInIndex; eintrwrap(w, ::write(fd, front.mid(mStdInIndex).constData(), want)); } else { want = front.size(); eintrwrap(w, ::write(fd, front.constData(), want)); } if (w == -1) { EventLoop::instance()->addFileDescriptor(fd, EventLoop::Write, processCallback, this); break; } else if (w == want) { mStdInBuffer.pop_front(); mStdInIndex = 0; } else mStdInIndex += w; } }
void Process::finish(int returnCode) { { std::lock_guard<std::mutex> lock(mMutex); mReturn = returnCode; mStdInBuffer.clear(); closeStdIn(CloseForce); mWantStdInClosed = false; if (mMode == Async) { // try to read all remaining data on stdout and stderr handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut); handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr); closeStdOut(); closeStdErr(); } else { int w; char q = 'q'; eintrwrap(w, ::write(mSync[1], &q, 1)); eintrwrap(w, ::close(mSync[1])); mSync[1] = -1; } } if (mMode == Async) mFinished(this); }
void Process::handleInput(int fd) { assert(EventLoop::eventLoop()); EventLoop::eventLoop()->unregisterSocket(fd); //static int ting = 0; //printf("Process::handleInput (cnt=%d)\n", ++ting); for (;;) { if (mStdInBuffer.empty()) return; //printf("Process::handleInput in loop\n"); int w, want; const String& front = mStdInBuffer.front(); if (mStdInIndex) { want = front.size() - mStdInIndex; eintrwrap(w, ::write(fd, front.mid(mStdInIndex).constData(), want)); } else { want = front.size(); eintrwrap(w, ::write(fd, front.constData(), want)); } if (w == -1) { EventLoop::eventLoop()->registerSocket(fd, EventLoop::SocketWrite, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2)); break; } else if (w == want) { mStdInBuffer.pop_front(); mStdInIndex = 0; } else mStdInIndex += w; } }
static inline bool connectInternal(int &fd, int domain, sockaddr* address, size_t addressSize, int maxTime) { StopWatch timer; while (true) { fd = ::socket(domain, SOCK_STREAM, 0); if (fd == -1) return false; addFlags(fd, O_NONBLOCK); errno = 0; int ret; eintrwrap(ret, ::connect(fd, address, addressSize)); if (!ret) { #ifdef HAVE_NOSIGPIPE ret = 1; ::setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&ret, sizeof(int)); #endif return true; } int left = -1; if (maxTime >= 0) { left = maxTime - static_cast<int>(timer.elapsed()); if (left <= 0) break; } if (errno == EINPROGRESS) { fd_set write; FD_ZERO(&write); FD_SET(fd, &write); timeval timeout; if (left != -1) { timeout.tv_sec = left / 1000; timeout.tv_usec = (left % 1000) * 1000; } eintrwrap(ret, select(fd + 1, 0, &write, 0, left == -1 ? 0 : &timeout)); if (ret > 0 && FD_ISSET(fd, &write)) { int error; socklen_t len = sizeof(error); if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) && !error) return true; } break; } else if (errno == EAGAIN) { eintrwrap(ret, ::close(fd)); fd = -1; usleep(100000); } else { break; } } if (fd != -1) { int ret; eintrwrap(ret, ::close(fd)); fd = -1; } return false; }
void Process::clear() { // don't clear a running process please assert(mPid == -1); if (mStdIn[0] != -1 && EventLoop::eventLoop()) { // try to finish off any pending writes handleInput(mStdIn[1]); } closeStdIn(CloseForce); closeStdOut(); closeStdErr(); int w; if (mSync[0] != -1) eintrwrap(w, ::close(mSync[0])); if (mSync[1] != -1) eintrwrap(w, ::close(mSync[1])); mReturn = ReturnUnset; mStdInIndex = mStdOutIndex = mStdErrIndex = 0; mWantStdInClosed = false; mMode = Sync; }
static inline void addFlags(int fd, int flags) { int ret; eintrwrap(ret, fcntl(fd, F_GETFL, 0)); if (ret != -1) flags |= ret; eintrwrap(ret, fcntl(fd, F_SETFL, flags)); }
LocalClient::LocalClient(int fd) : mFd(fd), mBufferIdx(0), mReadBufferPos(0) { int flags; eintrwrap(flags, fcntl(mFd, F_GETFL, 0)); eintrwrap(flags, fcntl(mFd, F_SETFL, flags | O_NONBLOCK)); #ifdef HAVE_NOSIGPIPE flags = 1; ::setsockopt(mFd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&flags, sizeof(int)); #endif EventLoop::instance()->addFileDescriptor(mFd, EventLoop::Read, dataCallback, this); }
bool LocalClient::writeMore() { bool ret = true; int written = 0; #ifdef HAVE_NOSIGNAL const int sendflags = MSG_NOSIGNAL; #else const int sendflags = 0; #endif for (;;) { if (mBuffers.empty()) { EventLoop::instance()->removeFileDescriptor(mFd, EventLoop::Write); break; } const String& front = mBuffers.front(); int w; eintrwrap(w, ::send(mFd, &front[mBufferIdx], front.size() - mBufferIdx, sendflags)); if (w == -1) { ret = (errno == EWOULDBLOCK || errno == EAGAIN); // apparently these can be different break; } written += w; mBufferIdx += w; if (mBufferIdx == front.size()) { assert(!mBuffers.empty()); mBuffers.pop_front(); mBufferIdx = 0; continue; } } if (written) mBytesWritten(this, written); return ret; }
void SocketServer::socketCallback(int /*fd*/, int mode) { sockaddr_in client4; sockaddr_in6 client6; sockaddr* client = 0; socklen_t size = 0; if (isIPv6) { size = sizeof(client6); client = reinterpret_cast<sockaddr*>(&client6); } else { size = sizeof(client4); client = reinterpret_cast<sockaddr*>(&client4); } int e; if (! ( mode & EventLoop::SocketRead ) ) return; for (;;) { eintrwrap(e, ::accept(fd, client, &size)); if (e == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { return; } serverError(this, AcceptError); close(); return; } //EventLoop::eventLoop()->unregisterSocket( fd ); accepted.push(e); serverNewConnection(this); } }
bool LocalClient::connect(const Path& path, int maxTime) { if (!path.isSocket()) return false; StopWatch timer; struct sockaddr_un address; memset(&address, 0, sizeof(struct sockaddr_un)); address.sun_family = AF_UNIX; const int sz = std::min<int>(sizeof(address.sun_path) - 1, path.size()); memcpy(address.sun_path, path.constData(), sz); address.sun_path[sz] = '\0'; while (true) { mFd = ::socket(PF_UNIX, SOCK_STREAM, 0); if (mFd == -1) { Path::rm(path); return false; } int ret; eintrwrap(ret, ::connect(mFd, (struct sockaddr *)&address, sizeof(struct sockaddr_un))); if (!ret) { #ifdef HAVE_NOSIGPIPE ret = 1; ::setsockopt(mFd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&ret, sizeof(int)); #endif break; } eintrwrap(ret, ::close(mFd)); mFd = -1; if (maxTime > 0 && timer.elapsed() >= maxTime) { Path::rm(path); return false; } } assert(mFd != -1); int flags; eintrwrap(flags, fcntl(mFd, F_GETFL, 0)); eintrwrap(flags, fcntl(mFd, F_SETFL, flags | O_NONBLOCK)); unsigned int fdflags = EventLoop::Read; if (!mBuffers.empty()) fdflags |= EventLoop::Write; EventLoop::instance()->addFileDescriptor(mFd, fdflags, dataCallback, this); mConnected(this); return true; }
ProcessThread::ProcessThread() { ::signal(SIGCHLD, ProcessThread::processSignalHandler); int flg; eintrwrap(flg, ::pipe(sProcessPipe)); SocketClient::setFlags(sProcessPipe[1], O_NONBLOCK, F_GETFL, F_SETFL); }
void Semaphore::op(short num) { sembuf buf = { 0, num, SEM_UNDO }; int ret; eintrwrap(ret, semop(mSem, &buf, 1)); if (ret == -1 && errno == EIDRM) mSem = -1; }
static void signalHandler(int sig) { char b = 'q'; int w; const int pipe = mainEventPipe; if (pipe != -1) eintrwrap(w, ::write(pipe, &b, 1)); }
void Semaphore::acquire(short num) { sembuf buf = { 0, static_cast<short>(num * -1), SEM_UNDO }; int ret; eintrwrap(ret, semop(mSem, &buf, 1)); if (ret == -1 && errno == EIDRM) mSem = -1; }
ProcessThread::ProcessThread() { int flg; eintrwrap(flg, ::pipe(sProcessPipe)); eintrwrap(flg, ::fcntl(sProcessPipe[1], F_GETFL, 0)); eintrwrap(flg, ::fcntl(sProcessPipe[1], F_SETFL, flg | O_NONBLOCK)); #ifdef HAVE_SIGINFO struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_sigaction = ProcessThread::processSignalHandler; sa.sa_flags = SA_RESTART | SA_SIGINFO; ::sigaction(SIGCHLD, &sa, 0); #else ::signal(SIGCHLD, ProcessThread::processSignalHandler); #endif }
void ProcessThread::run() { ssize_t r; for (;;) { char ch; r = ::read(sProcessPipe[0], &ch, sizeof(char)); if (r == 1) { if (ch == 's') { break; } else { int ret; pid_t p; std::unique_lock<std::mutex> lock(sProcessMutex); bool done = false; do { //printf("testing pid %d\n", proc->first); eintrwrap(p, ::waitpid(0, &ret, WNOHANG)); switch(p) { case 0: // we're done done = true; break; case -1: done = true; if (errno == ECHILD) break; // this is bad error() << "waitpid error" << errno; break; default: //printf("successfully waited for pid (got %d)\n", p); if (WIFEXITED(ret)) { ret = WEXITSTATUS(ret); } else { ret = Process::ReturnCrashed; } auto proc = sProcesses.find(p); if (proc != sProcesses.end()) { Process *process = proc->second.proc; EventLoop::SharedPtr loop = proc->second.loop.lock(); sProcesses.erase(proc++); lock.unlock(); if (loop) { loop->callLater([process, ret]() { process->finish(ret); }); } else { process->finish(ret); } lock.lock(); } else { error() << "couldn't find process for pid" << p; } } } while (!done); } } } //printf("process thread died for some reason\n"); }
void EventLoop::wakeup() { if (std::this_thread::get_id() == threadId) return; char b = 'w'; int w; eintrwrap(w, ::write(eventPipe[1], &b, 1)); }
void SocketClient::disconnect() { if (mFd != -1) { int ret; eintrwrap(ret, ::close(mFd)); EventLoop::instance()->removeFileDescriptor(mFd); mFd = -1; mDisconnected(this); } }
void Process::closeStdErr() { if (mStdErr[0] == -1) return; EventLoop::instance()->removeFileDescriptor(mStdErr[0]); int err; eintrwrap(err, ::close(mStdErr[0])); mStdErr[0] = -1; }
void Process::closeStdErr() { if (mStdErr[0] == -1) return; EventLoop::eventLoop()->unregisterSocket(mStdErr[0]); int err; eintrwrap(err, ::close(mStdErr[0])); mStdErr[0] = -1; }
void Process::closeStdOut() { if (mStdOut[0] == -1) return; if (EventLoop::SharedPtr eventLoop = EventLoop::eventLoop()) eventLoop->unregisterSocket(mStdOut[0]); int err; eintrwrap(err, ::close(mStdOut[0])); mStdOut[0] = -1; }
Process::~Process() { if (mPid != -1) ProcessThread::removePid(mPid); if (mStdIn[0] != -1) { // try to finish off any pending writes handleInput(mStdIn[1]); } closeStdIn(); closeStdOut(); closeStdErr(); int w; if (mSync[0] != -1) eintrwrap(w, ::close(mSync[0])); if (mSync[1] != -1) eintrwrap(w, ::close(mSync[1])); }
bool SocketClient::writeMore() { bool ret = true; int written = 0; #ifdef HAVE_NOSIGNAL const int sendflags = MSG_NOSIGNAL; #else const int sendflags = 0; #endif for (;;) { if (mBuffers.empty()) { EventLoop::instance()->removeFileDescriptor(mFd, EventLoop::Write); break; } const sockaddr_in& addr = mBuffers.front().first; const String& front = mBuffers.front().second; int w; if (!addr.sin_port) { eintrwrap(w, ::send(mFd, &front[mBufferIdx], front.size() - mBufferIdx, sendflags)); } else { eintrwrap(w, ::sendto(mFd, &front[mBufferIdx], front.size() - mBufferIdx, sendflags, reinterpret_cast<const sockaddr*>(&addr), sizeof(sockaddr_in))); } if (w == -1) { ret = (errno == EWOULDBLOCK || errno == EAGAIN); // apparently these can be different break; } written += w; mBufferIdx += w; if (mBufferIdx == front.size()) { assert(!mBuffers.empty()); mBuffers.pop_front(); mBufferIdx = 0; continue; } } if (written) mBytesWritten(this, written); return ret; }
Process::~Process() { { std::lock_guard<std::mutex> lock(mMutex); assert(mReturn != ReturnUnset || mPid == -1); } if (mStdIn[0] != -1 && EventLoop::eventLoop()) { // try to finish off any pending writes handleInput(mStdIn[1]); } closeStdIn(CloseForce); closeStdOut(); closeStdErr(); int w; if (mSync[0] != -1) eintrwrap(w, ::close(mSync[0])); if (mSync[1] != -1) eintrwrap(w, ::close(mSync[1])); }
bool EventLoop::registerSocket(int fd, unsigned int mode, std::function<void(int, unsigned int)>&& func) { std::lock_guard<std::mutex> locker(mutex); sockets[fd] = std::make_pair(mode, std::forward<std::function<void(int, unsigned int)> >(func)); int e; #if defined(HAVE_EPOLL) epoll_event ev; memset(&ev, 0, sizeof(ev)); ev.events = EPOLLET|EPOLLRDHUP; if (mode & SocketRead) ev.events |= EPOLLIN; if (mode & SocketWrite) ev.events |= EPOLLOUT; if (mode & SocketOneShot) ev.events |= EPOLLONESHOT; ev.data.fd = fd; e = epoll_ctl(pollFd, EPOLL_CTL_ADD, fd, &ev); #elif defined(HAVE_KQUEUE) const struct { int rf; int kf; } flags[] = { { SocketRead, EVFILT_READ }, { SocketWrite, EVFILT_WRITE }, { 0, 0 } }; for (int i = 0; flags[i].rf; ++i) { if (!(mode & flags[i].rf)) continue; struct kevent ev; memset(&ev, '\0', sizeof(struct kevent)); ev.ident = fd; ev.flags = EV_ADD|EV_ENABLE; if (mode & SocketOneShot) ev.flags |= EV_ONESHOT; ev.filter = flags[i].kf; eintrwrap(e, kevent(pollFd, &ev, 1, 0, 0, 0)); } #elif defined(HAVE_SELECT) e = 0; // fake ok wakeup(); #endif if (e == -1) { if (errno != EEXIST) { fprintf(stderr, "Unable to register socket %d with mode %x: %d (%s)\n", fd, mode, errno, rct_strerror(errno)); return false; } } return true; }
void Process::closeStdIn(CloseStdInFlag flag) { if (mStdIn[1] == -1) return; if (flag == CloseForce || mStdInBuffer.empty()) { if (EventLoop::SharedPtr loop = EventLoop::eventLoop()) loop->unregisterSocket(mStdIn[1]); int err; eintrwrap(err, ::close(mStdIn[1])); mStdIn[1] = -1; mWantStdInClosed = false; } else { mWantStdInClosed = true; } }
void Process::handleOutput(int fd, String &buffer, int &index, Signal<std::function<void(Process*)> > &signal) { //printf("Process::handleOutput %d\n", fd); enum { BufSize = 1024, MaxSize = (1024 * 1024 * 256) }; char buf[BufSize]; int total = 0; for (;;) { int r; eintrwrap(r, ::read(fd, buf, BufSize)); if (r == -1) { //printf("Process::handleOutput %d returning -1, errno %d %s\n", fd, errno, Rct::strerror().constData()); break; } else if (r == 0) { // file descriptor closed, remove it //printf("Process::handleOutput %d returning 0\n", fd); if (auto eventLoop = EventLoop::eventLoop()) eventLoop->unregisterSocket(fd); break; } else { //printf("Process::handleOutput in loop %d\n", fd); //printf("data: '%s'\n", String(buf, r).constData()); int sz = buffer.size(); if (sz + r > MaxSize) { if (sz + r - index > MaxSize) { error("Process::handleOutput, buffer too big, dropping data"); buffer.clear(); index = sz = 0; } else { sz = buffer.size() - index; memmove(buffer.data(), buffer.data() + index, sz); buffer.resize(sz); index = 0; } } buffer.resize(sz + r); memcpy(buffer.data() + sz, buf, r); total += r; } } //printf("total data '%s'\n", buffer.nullTerminated()); if (total) signal(this); }
void Process::handleOutput(int fd, ByteArray& buffer, int& index, signalslot::Signal0& signal) { //printf("Process::handleOutput %d\n", fd); enum { BufSize = 1024, MaxSize = (1024 * 1024 * 16) }; char buf[BufSize]; int total = 0; for (;;) { int r; eintrwrap(r, ::read(fd, buf, BufSize)); if (r == -1) { //printf("Process::handleOutput %d returning -1, errno %d %s\n", fd, errno, strerror(errno)); break; } else if (r == 0) { // file descriptor closed, remove it //printf("Process::handleOutput %d returning 0\n", fd); EventLoop::instance()->removeFileDescriptor(fd); break; } else { //printf("Process::handleOutput in loop %d\n", fd); //printf("data: '%s'\n", std::string(buf, r).c_str()); int sz = buffer.size(); if (sz + r > MaxSize) { if (sz + r - index > MaxSize) { error("Process::handleOutput, buffer too big, dropping data"); buffer.clear(); index = sz = 0; } else { sz = buffer.size() - index; memmove(buffer.data(), buffer.data() + index, sz); buffer.resize(sz); index = 0; } } buffer.resize(sz + r); memcpy(buffer.data() + sz, buf, r); total += r; } } //printf("total data '%s'\n", buffer.nullTerminated()); if (total) signal(); }
void EventLoop::unregisterSocket(int fd) { std::lock_guard<std::mutex> locker(mutex); auto socket = sockets.find(fd); if (socket == sockets.end()) return; #ifdef HAVE_KQUEUE const int mode = socket->second.first; #endif sockets.erase(socket); int e; #if defined(HAVE_EPOLL) epoll_event ev; memset(&ev, 0, sizeof(ev)); e = epoll_ctl(pollFd, EPOLL_CTL_DEL, fd, &ev); #elif defined(HAVE_KQUEUE) const struct { int rf; int kf; } flags[] = { { SocketRead, EVFILT_READ }, { SocketWrite, EVFILT_WRITE }, { 0, 0 } }; for (int i = 0; flags[i].rf; ++i) { if (!(mode & flags[i].rf)) continue; struct kevent ev; memset(&ev, '\0', sizeof(struct kevent)); ev.ident = fd; ev.flags = EV_DELETE|EV_DISABLE; ev.filter = flags[i].kf; eintrwrap(e, kevent(pollFd, &ev, 1, 0, 0, 0)); } #elif defined(HAVE_SELECT) e = 0; // fake ok wakeup(); #endif if (e == -1) { if (errno != ENOENT) { fprintf(stderr, "Unable to unregister socket %d: %d (%s)\n", fd, errno, rct_strerror(errno)); } } }
void LocalClient::readMore() { enum { BufSize = 1024, MaxBufferSize = 1024 * 1024 * 16 }; #ifdef HAVE_NOSIGNAL const int recvflags = MSG_NOSIGNAL; #else const int recvflags = 0; #endif char buf[BufSize]; int read = 0; bool wasDisconnected = false; for (;;) { int r; eintrwrap(r, ::recv(mFd, buf, BufSize, recvflags)); if (r == -1) { break; } else if (!r) { wasDisconnected = true; break; } read += r; mReadBuffer.resize(r + mReadBuffer.size()); memcpy(mReadBuffer.data() + mReadBuffer.size() - r, buf, r); if (mReadBuffer.size() + r >= MaxBufferSize) { if (mReadBuffer.size() + r - mReadBufferPos < MaxBufferSize) { mReadBuffer.remove(0, mReadBufferPos); mReadBufferPos = 0; } else { error("Buffer exhausted (%d), dropping on the floor", mReadBuffer.size()); mReadBuffer.clear(); } } } if (read && !mReadBuffer.isEmpty()) mDataAvailable(this); if (wasDisconnected) disconnect(); }