WinPortFSNotify(LPCWSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter) : WinPortEvent(true, false), _watcher(0), _fd(-1), _filter(dwNotifyFilter), _watching(false) { #if defined(__APPLE__) || defined(__FreeBSD__) _fd = kqueue(); if (_fd == -1) return; #else _fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); if (_fd == -1) return; #endif AddWatch( Wide2MB(lpPathName).c_str() ); if (bWatchSubtree) { AddWatchRecursive(Wide2MB(lpPathName), 0); } if (!_watches.empty() && pipe_cloexec(_pipe)==0) { #if defined(__APPLE__) || defined(__FreeBSD__) _events.emplace_back(); EV_SET(&_events.back(), _pipe[0], EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0); #endif _watching = true; if (pthread_create(&_watcher, nullptr, sWatcherProc, this)==0) { fprintf(stderr, "WinPortFSNotify('%ls') - watching\n", lpPathName); } else { fprintf(stderr, "WinPortFSNotify('%ls') - pthread error %u\n", lpPathName, errno); close(_pipe[0]); close(_pipe[1]); _watching = false; } } else { fprintf(stderr, "WinPortFSNotify('%ls') - not watching\n", lpPathName); } }
void PerfSource::run() { int pipefd[2]; pthread_t procThread; ProcThreadArgs procThreadArgs; { DynBuf printb; DynBuf b1; DynBuf b2; const uint64_t currTime = getTime(); // Start events before reading proc to avoid race conditions if (!mCountersGroup.start() || !mIdleGroup.start()) { logg->logError(__FILE__, __LINE__, "PerfGroup::start failed", __FUNCTION__, __FILE__, __LINE__); handleException(); } if (!readProcComms(currTime, &mBuffer, &printb, &b1, &b2)) { logg->logError(__FILE__, __LINE__, "readProcComms failed"); handleException(); } mBuffer.commit(currTime); // Postpone reading kallsyms as on android adb gets too backed up and data is lost procThreadArgs.mBuffer = &mBuffer; procThreadArgs.mCurrTime = currTime; procThreadArgs.mIsDone = false; if (pthread_create(&procThread, NULL, procFunc, &procThreadArgs)) { logg->logError(__FILE__, __LINE__, "pthread_create failed", __FUNCTION__, __FILE__, __LINE__); handleException(); } } if (pipe_cloexec(pipefd) != 0) { logg->logError(__FILE__, __LINE__, "pipe failed"); handleException(); } mInterruptFd = pipefd[1]; if (!mMonitor.add(pipefd[0])) { logg->logError(__FILE__, __LINE__, "Monitor::add failed"); handleException(); } int timeout = -1; if (gSessionData->mLiveRate > 0) { timeout = gSessionData->mLiveRate/NS_PER_MS; } sem_post(mStartProfile); while (gSessionData->mSessionIsActive) { // +1 for uevents, +1 for pipe struct epoll_event events[NR_CPUS + 2]; int ready = mMonitor.wait(events, ARRAY_LENGTH(events), timeout); if (ready < 0) { logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); handleException(); } const uint64_t currTime = getTime(); for (int i = 0; i < ready; ++i) { if (events[i].data.fd == mUEvent.getFd()) { if (!handleUEvent(currTime)) { logg->logError(__FILE__, __LINE__, "PerfSource::handleUEvent failed"); handleException(); } break; } } // send a notification that data is ready sem_post(mSenderSem); // In one shot mode, stop collection once all the buffers are filled // Assume timeout == 0 in this case if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { logg->logMessage("%s(%s:%i): One shot", __FUNCTION__, __FILE__, __LINE__); child->endSession(); } } procThreadArgs.mIsDone = true; pthread_join(procThread, NULL); mIdleGroup.stop(); mCountersGroup.stop(); mBuffer.setDone(); mIsDone = true; // send a notification that data is ready sem_post(mSenderSem); mInterruptFd = -1; close(pipefd[0]); close(pipefd[1]); }
// Gator data flow: collector -> collector fifo -> sender int main(int argc, char** argv) { // Ensure proper signal handling by making gatord the process group leader // e.g. it may not be the group leader when launched as 'sudo gatord' setsid(); // Set up global thread-safe logging logg = new Logging(hasDebugFlag(argc, argv)); // Global data class gSessionData = new SessionData(); // Set up global utility class util = new OlyUtility(); // Initialize drivers new CCNDriver(); prctl(PR_SET_NAME, (unsigned long)&"gatord-main", 0, 0, 0); pthread_mutex_init(&numSessions_mutex, NULL); signal(SIGINT, handler); signal(SIGTERM, handler); signal(SIGABRT, handler); // Set to high priority if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) { logg->logMessage("setpriority() failed"); } // Parse the command line parameters struct cmdline_t cmdline = parseCommandLine(argc, argv); if (cmdline.update) { update(argv[0]); cmdline.update = false; gSessionData->mAllowCommands = true; } // Verify root permissions uid_t euid = geteuid(); if (euid) { logg->logError("gatord must be launched with root privileges"); handleException(); } // Call before setting up the SIGCHLD handler, as system() spawns child processes if (!setupFilesystem(cmdline.module)) { logg->logMessage("Unable to set up gatorfs, trying perf"); if (!gSessionData->perf.setup()) { logg->logError( "Unable to locate gator.ko driver:\n" " >>> gator.ko should be co-located with gatord in the same directory\n" " >>> OR insmod gator.ko prior to launching gatord\n" " >>> OR specify the location of gator.ko on the command line\n" " >>> OR run Linux 3.4 or later with perf (CONFIG_PERF_EVENTS and CONFIG_HW_PERF_EVENTS) and tracing (CONFIG_TRACING and CONFIG_CONTEXT_SWITCH_TRACER) support to collect data via userspace only"); handleException(); } } { EventsXML eventsXML; mxml_node_t *xml = eventsXML.getTree(); // Initialize all drivers for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { driver->readEvents(xml); } mxmlDelete(xml); } // Handle child exit codes signal(SIGCHLD, child_exit); // Ignore the SIGPIPE signal so that any send to a broken socket will return an error code instead of asserting a signal // Handling the error at the send function call is much easier than trying to do anything intelligent in the sig handler signal(SIGPIPE, SIG_IGN); // If the command line argument is a session xml file, no need to open a socket if (gSessionData->mSessionXMLPath) { child = new Child(); child->run(); delete child; } else { annotateListener.setup(); int pipefd[2]; if (pipe_cloexec(pipefd) != 0) { logg->logError("Unable to set up annotate pipe"); handleException(); } gSessionData->mAnnotateStart = pipefd[1]; sock = new OlyServerSocket(cmdline.port); udpListener.setup(cmdline.port); if (!monitor.init() || !monitor.add(sock->getFd()) || !monitor.add(udpListener.getReq()) || !monitor.add(annotateListener.getSockFd()) || !monitor.add(annotateListener.getUdsFd()) || !monitor.add(pipefd[0]) || false) { logg->logError("Monitor setup failed"); handleException(); } // Forever loop, can be exited via a signal or exception while (1) { struct epoll_event events[2]; logg->logMessage("Waiting on connection..."); int ready = monitor.wait(events, ARRAY_LENGTH(events), -1); if (ready < 0) { logg->logError("Monitor::wait failed"); handleException(); } for (int i = 0; i < ready; ++i) { if (events[i].data.fd == sock->getFd()) { handleClient(); } else if (events[i].data.fd == udpListener.getReq()) { udpListener.handle(); } else if (events[i].data.fd == annotateListener.getSockFd()) { annotateListener.handleSock(); } else if (events[i].data.fd == annotateListener.getUdsFd()) { annotateListener.handleUds(); } else if (events[i].data.fd == pipefd[0]) { uint64_t val; if (read(pipefd[0], &val, sizeof(val)) != sizeof(val)) { logg->logMessage("Reading annotate pipe failed"); } annotateListener.signal(); } } } } cleanUp(); return 0; }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { pid_t pid; int to_cgi_fds[2]; int from_cgi_fds[2]; struct stat st; UNUSED(p); #ifndef __WIN32 if (!buffer_string_is_empty(cgi_handler)) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe_cloexec(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe_cloexec(from_cgi_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char_array env; char *c; const char *s; http_cgi_opts opts = { 0, 0, NULL, NULL }; /* move stdout to from_cgi_fd[1] */ dup2(from_cgi_fds[1], STDOUT_FILENO); #ifndef FD_CLOEXEC close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); #endif /* move the stdin to to_cgi_fd[0] */ dup2(to_cgi_fds[0], STDIN_FILENO); #ifndef FD_CLOEXEC close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); #endif /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; http_cgi_headers(srv, con, &opts, cgi_env_add, &env); /* for valgrind */ if (NULL != (s = getenv("LD_PRELOAD"))) { cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); } if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (NULL != (s = getenv("SYSTEMROOT"))) { cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); } #endif if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } env.ptr[env.used] = NULL; /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); force_assert(args); i = 0; if (!buffer_string_is_empty(cgi_handler)) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i ] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { /* handle special case of file in root directory */ const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr; /* temporarily shorten con->physical.path to directory without terminating '/' */ *c = '\0'; /* change to the physical directory */ if (-1 == chdir(physdir)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); } *c = '/'; } /* we don't need the client socket */ for (i = 3; i < 256; i++) { if (i != srv->errorlog_fd) close(i); } /* exec the cgi */ execve(args[0], args, env.ptr); /* most log files may have been closed/redirected by this point, * though stderr might still point to lighttpd.breakage.log */ perror(args[0]); _exit(1); } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; default: { /* parent process */ close(from_cgi_fds[1]); close(to_cgi_fds[0]); /* register PID and wait for them asynchronously */ hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; ++srv->cur_fds; if (0 == con->request.content_length) { close(to_cgi_fds[1]); } else { /* there is content to send */ if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } ++srv->cur_fds; } fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_connection_close(srv, hctx); return -1; } fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); break; } } return 0; #else return -1; #endif }
void *commandThread(void *) { prctl(PR_SET_NAME, (unsigned long)&"gatord-command", 0, 0, 0); const char *const name = gSessionData->mCaptureUser == NULL ? "nobody" : gSessionData->mCaptureUser; const int uid = getUid(name); if (uid < 0) { logg->logError("Unable to look up the user %s, please double check that the user exists", name); handleException(); } sleep(3); char buf[128]; int pipefd[2]; if (pipe_cloexec(pipefd) != 0) { logg->logError("pipe failed"); handleException(); } const int pid = fork(); if (pid < 0) { logg->logError("fork failed"); handleException(); } if (pid == 0) { char cargv0l[] = "/bin/sh"; char cargv0a[] = "/system/bin/sh"; char cargv1[] = "-c"; char *cargv[] = { cargv0l, cargv1, gSessionData->mCaptureCommand, NULL, }; buf[0] = '\0'; close(pipefd[0]); // Gator runs at a high priority, reset the priority to the default if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), 0) == -1) { snprintf(buf, sizeof(buf), "setpriority failed"); goto fail_exit; } if (setuid(uid) != 0) { snprintf(buf, sizeof(buf), "setuid failed"); goto fail_exit; } { const char *const path = gSessionData->mCaptureWorkingDir == NULL ? "/" : gSessionData->mCaptureWorkingDir; if (chdir(path) != 0) { snprintf(buf, sizeof(buf), "Unable to cd to %s, please verify the directory exists and is accessable to %s", path, name); goto fail_exit; } } execv(cargv[0], cargv); cargv[0] = cargv0a; execv(cargv[0], cargv); snprintf(buf, sizeof(buf), "execv failed"); fail_exit: if (buf[0] != '\0') { const ssize_t bytes = write(pipefd[1], buf, sizeof(buf)); // Can't do anything if this fails (void)bytes; } exit(-1); } close(pipefd[1]); const ssize_t bytes = read(pipefd[0], buf, sizeof(buf)); if (bytes > 0) { logg->logError("%s", buf); handleException(); } close(pipefd[0]); return NULL; }
void ExternalSource::run() { int pipefd[2]; prctl(PR_SET_NAME, (unsigned long)&"gatord-external", 0, 0, 0); if (pipe_cloexec(pipefd) != 0) { logg->logError(__FILE__, __LINE__, "pipe failed"); handleException(); } mInterruptFd = pipefd[1]; if (!mMonitor.add(pipefd[0])) { logg->logError(__FILE__, __LINE__, "Monitor::add failed"); handleException(); } // Notify annotate clients to retry connecting to gatord gSessionData->annotateListener.signal(); while (gSessionData->mSessionIsActive) { struct epoll_event events[16]; // Clear any pending sem posts while (sem_trywait(&mBufferSem) == 0); int ready = mMonitor.wait(events, ARRAY_LENGTH(events), -1); if (ready < 0) { logg->logError(__FILE__, __LINE__, "Monitor::wait failed"); handleException(); } const uint64_t currTime = getTime(); for (int i = 0; i < ready; ++i) { const int fd = events[i].data.fd; if (fd == mMveStartupUds.getFd()) { // Mali Video Engine says it's alive int client = mMveStartupUds.acceptConnection(); // Don't read from this connection, establish a new connection to Mali-V500 close(client); if (!connectMve()) { logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali video connection"); handleException(); } } else if (fd == mMaliStartupUds.getFd()) { // Mali Graphics says it's alive int client = mMaliStartupUds.acceptConnection(); // Don't read from this connection, establish a new connection to Mali Graphics close(client); if (!connectMali()) { logg->logError(__FILE__, __LINE__, "Unable to configure incoming Mali graphics connection"); handleException(); } } else if (fd == mAnnotate.getFd()) { int client = mAnnotate.acceptConnection(); if (!setNonblock(client) || !mMonitor.add(client)) { logg->logError(__FILE__, __LINE__, "Unable to set socket options on incoming annotation connection"); handleException(); } } else if (fd == pipefd[0]) { // Means interrupt has been called and mSessionIsActive should be reread } else { /* This can result in some starvation if there are multiple * threads which are annotating heavily, but it is not * recommended that threads annotate that much as it can also * starve out the gator data. */ while (gSessionData->mSessionIsActive) { // Wait until there is enough room for the fd, two headers and two ints waitFor(7*Buffer::MAXSIZE_PACK32 + 2*sizeof(uint32_t)); mBuffer.packInt(fd); const int contiguous = mBuffer.contiguousSpaceAvailable(); const int bytes = read(fd, mBuffer.getWritePos(), contiguous); if (bytes < 0) { if (errno == EAGAIN) { // Nothing left to read mBuffer.commit(currTime); break; } // Something else failed, close the socket mBuffer.commit(currTime); mBuffer.packInt(-1); mBuffer.packInt(fd); mBuffer.commit(currTime); close(fd); break; } else if (bytes == 0) { // The other side is closed mBuffer.commit(currTime); mBuffer.packInt(-1); mBuffer.packInt(fd); mBuffer.commit(currTime); close(fd); break; } mBuffer.advanceWrite(bytes); mBuffer.commit(currTime); // Short reads also mean nothing is left to read if (bytes < contiguous) { break; } } } } } mBuffer.setDone(); if (mMveUds >= 0) { gSessionData->maliVideo.stop(mMveUds); } mInterruptFd = -1; close(pipefd[0]); close(pipefd[1]); }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { char *args[3]; int to_cgi_fds[2]; int from_cgi_fds[2]; int dfd = -1; UNUSED(p); if (!buffer_string_is_empty(cgi_handler)) { if (NULL == cgi_stat(srv, con, cgi_handler)) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe_cloexec(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe_cloexec(from_cgi_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } fdevent_setfd_cloexec(to_cgi_fds[1]); fdevent_setfd_cloexec(from_cgi_fds[0]); { size_t i = 0; http_cgi_opts opts = { 0, 0, NULL, NULL }; env_accum *env = &p->env; env->used = 0; env->oused = 0; /* create environment */ http_cgi_headers(srv, con, &opts, cgi_env_add, env); /* for valgrind */ if (p->env.ld_preload) { cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); } if (p->env.ld_library_path) { cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (p->env.systemroot) { cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); } #endif if (env->esize <= env->oused) { env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); force_assert(env->eptr); } for (i = 0; i < env->oused; ++i) { env->eptr[i] = env->ptr + env->offsets[i]; } env->eptr[env->oused] = NULL; /* set up args */ i = 0; if (!buffer_string_is_empty(cgi_handler)) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i ] = NULL; } dfd = fdevent_open_dirname(con->physical.path->ptr, con->conf.follow_symlink); if (-1 == dfd) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path); } hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], -1, dfd) : -1; if (-1 == hctx->pid) { /* log error with errno prior to calling close() (might change errno) */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); if (-1 != dfd) close(dfd); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } else { if (-1 != dfd) close(dfd); close(from_cgi_fds[1]); close(to_cgi_fds[0]); hctx->fd = from_cgi_fds[0]; ++srv->cur_fds; cgi_pid_add(p, hctx->pid, hctx); if (0 == con->request.content_length) { close(to_cgi_fds[1]); } else { /* there is content to send */ if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } ++srv->cur_fds; } hctx->fdn = fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_connection_close(srv, hctx); return -1; } fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); return 0; } }