FILE *LightProcess::LightPopenImpl(const char *cmd, const char *type, const char *cwd) { int id = GetId(); Lock lock(g_procs[id].m_procMutex); auto fout = g_procs[id].m_afdt_fd; lwp_write(fout, "popen"); lwp_write(fout, type); lwp_write(fout, cmd); lwp_write(fout, cwd ? cwd : ""); std::string buf; auto fin = g_procs[id].m_afdt_fd; lwp_read(fin, buf); if (buf == "error") { return nullptr; } int64_t fptr = 0; lwp_read_int64(fin, fptr); if (!fptr) { Logger::Error("Light process failed to return the file pointer."); return nullptr; } int fd = recv_fd(fin); if (fd < 0) { Logger::Error("Light process failed to send the file descriptor."); return nullptr; } FILE *f = fdopen(fd, type); g_procs[id].m_popenMap[(int64_t)f] = fptr; return f; }
pid_t do_proc_open_helper(int afdt_fd) { std::string cmd, cwd; std::vector<std::string> env; std::vector<int> pvals; lwp_read(afdt_fd, cmd, cwd, env, pvals); std::vector<int> pkeys; for (int i = 0; i < pvals.size(); i++) { int fd = recv_fd(afdt_fd); if (fd < 0) { lwp_write(afdt_fd, "error", (int32_t)EPROTO); close_fds(pkeys); return -1; } pkeys.push_back(fd); } // indicate error if an empty command was received if (cmd.length() == 0) { lwp_write(afdt_fd, "error", (int32_t)ENOENT); return -1; } // now ready to start the child process pid_t child = fork(); if (child == 0) { mprotect_1g_pages(PROT_READ); Process::OOMScoreAdj(1000); for (int i = 0; i < pvals.size(); i++) { dup2(pkeys[i], pvals[i]); } if (!cwd.empty() && chdir(cwd.c_str())) { // non-zero for error // chdir failed, the working directory remains unchanged } if (!env.empty()) { char **envp = build_envp(env); execle("/bin/sh", "sh", "-c", cmd.c_str(), nullptr, envp); free(envp); } else { execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); } _Exit(HPHP_EXIT_FAILURE); } if (child > 0) { // successfully created the child process lwp_write(afdt_fd, "success", child); } else { // failed creating the child process lwp_write(afdt_fd, "error", errno); } close_fds(pkeys); return child; }
static void do_popen(int afdt_fd) { std::string buf; std::string cwd; lwp_read(afdt_fd, buf); bool read_only = (buf[0] == 'r'); lwp_read(afdt_fd, buf); std::string old_cwd; lwp_read(afdt_fd, cwd); if (!cwd.empty()) { old_cwd = Process::GetCurrentDirectory(); if (old_cwd != cwd) { if (chdir(cwd.c_str())) { // Ignore chdir failures, because the compiled version might not // have the directory any more. Logger::Warning("Light Process failed chdir to %s.", cwd.c_str()); } } } FILE *f = buf[0] ? ::popen(buf.c_str(), read_only ? "r" : "w") : nullptr; if (!old_cwd.empty() && chdir(old_cwd.c_str())) { // only here if we can't change the cwd back } if (f == nullptr) { Logger::Error("Light process failed popen: %d (%s).", errno, folly::errnoStr(errno).c_str()); lwp_write(afdt_fd, "error"); } else { lwp_write(afdt_fd, "success"); lwp_write_int64(afdt_fd, (int64_t)f); int fd = fileno(f); send_fd(afdt_fd, fd); } }
pid_t LightProcess::proc_open(const char *cmd, const std::vector<int> &created, const std::vector<int> &desired, const char *cwd, const std::vector<std::string> &env) { int id = GetId(); Lock lock(g_procs[id].m_procMutex); always_assert(Available()); always_assert(created.size() == desired.size()); auto fout = g_procs[id].m_afdt_fd; lwp_write(fout, "proc_open"); lwp_write(fout, cmd); lwp_write(fout, cwd ? cwd : ""); lwp_write_int32(fout, (int)env.size()); for (unsigned int i = 0; i < env.size(); i++) { lwp_write(fout, env[i]); } lwp_write_int32(fout, (int)created.size()); for (unsigned int i = 0; i < desired.size(); i++) { lwp_write_int32(fout, desired[i]); } bool error_send = false; int save_errno = 0; for (unsigned int i = 0; i < created.size(); i++) { if (!send_fd(g_procs[id].m_afdt_fd, created[i])) { error_send = true; save_errno = errno; break; } } std::string buf; auto fin = g_procs[id].m_afdt_fd; lwp_read(fin, buf); if (buf == "error") { lwp_read_int32(fin, errno); if (error_send) { // On this error, the receiver side returns dummy errno, // use the sender side errno here. errno = save_errno; } return -1; } always_assert_flog(buf == "success", "Unexpected message from light process: `{}'", buf); int64_t pid = -1; lwp_read_int64(fin, pid); always_assert(pid); return (pid_t)pid; }
static void do_change_user(int afdt_fd) { std::string uname; lwp_read(afdt_fd, uname); if (uname.length() > 0) { struct passwd *pw = getpwnam(uname.c_str()); if (pw) { if (pw->pw_gid) { initgroups(pw->pw_name, pw->pw_gid); setgid(pw->pw_gid); } if (pw->pw_uid) { setuid(pw->pw_uid); } } } }
void LightProcess::runShadow() { std::string buf; pollfd pfd[1]; pfd[0].fd = m_afdt_fd; pfd[0].events = POLLIN; try { while (true) { int ret = poll(pfd, 1, -1); if (ret < 0 && errno == EINTR) { continue; } if (pfd[0].revents & POLLHUP) { // no more command can come in Logger::Error("Lost parent, LightProcess exiting"); break; } if (pfd[0].revents & POLLIN) { lwp_read(m_afdt_fd, buf); if (buf == "exit") { Logger::Verbose("LightProcess exiting upon request"); break; } else if (buf == "popen") { do_popen(m_afdt_fd); } else if (buf == "pclose") { do_pclose(m_afdt_fd); } else if (buf == "proc_open") { do_proc_open(m_afdt_fd); } else if (buf == "waitpid") { do_waitpid(m_afdt_fd); } else if (buf == "change_user") { do_change_user(m_afdt_fd); } else if (buf[0]) { Logger::Info("LightProcess got invalid command: %.20s", buf.c_str()); } } } } catch (const std::exception& e) { Logger::Error("LightProcess exiting due to exception: %s", e.what()); } catch (...) { Logger::Error("LightProcess exiting due to unknown exception"); } ::close(m_afdt_fd); _Exit(0); }
static int list_read(int listt, int elemn) { char idstr[P_MAXVAL]; list_t *list; id_info_t *id; if (listt == L_LWP) return (lwp_read(elemn)); while (elemn-- > 0) { switch (listt) { case L_PRC_SI : list = &processes; break; case L_USR_SI : list = &users; break; case L_PRJ_SI : list = &projects; break; } if (list->l_head == NULL) { /* first element */ list->l_head = list->l_tail = id = Zalloc(sizeof (id_info_t)); list->l_count++; } else { /* a new element */ id = list->l_tail; id->id_next = Zalloc(sizeof (id_info_t)); id->id_next->id_prev = list->l_tail; id->id_next->id_next = NULL; list->l_tail = id->id_next; id = list->l_tail; list->l_count++; } if (r_element((char *)id, idstr) == -1) { list_clear(list); return (-1); } } return (0); }
pid_t do_popen_helper(int afdt_fd) { std::string mode, buf, cwd; lwp_read(afdt_fd, mode, buf, cwd); std::string old_cwd; if (!cwd.empty()) { old_cwd = Process::GetCurrentDirectory(); if (old_cwd != cwd) { if (chdir(cwd.c_str())) { // Ignore chdir failures, because the compiled version might not // have the directory any more. Logger::Warning("Light Process failed chdir to %s.", cwd.c_str()); } } } pid_t pid; auto fd = buf.empty() ? -1 : popen_impl(buf.c_str(), mode.c_str(), &pid); if (!old_cwd.empty() && chdir(old_cwd.c_str())) { // only here if we can't change the cwd back } if (fd < 0) { Logger::Error("Light process failed popen: %d (%s).", errno, folly::errnoStr(errno).c_str()); lwp_write(afdt_fd, "error"); } else { lwp_write(afdt_fd, "success", pid); send_fd(afdt_fd, fd); // the fd is now owned by the main process, close our copy close(fd); } return pid; }
void do_waitpid(int afdt_fd) { pid_t pid = -1; int options = 0; int timeout = 0; lwp_read(afdt_fd, pid, options, timeout); int stat; if (timeout > 0) { waited = pid; signal(SIGALRM, kill_handler); alarm(timeout); } rusage ru; int64_t time_us = 0; const auto ret = ::wait4(pid, &stat, options, &ru); alarm(0); // cancel the previous alarm if not triggered yet waited = 0; int64_t events[] = { 0, 0, 0 }; if (ret > 0 && s_trackProcessTimes) { time_us = ru2microseconds(ru); auto it = s_pidToHCWMap.find(ret); if (it == s_pidToHCWMap.end()) { throw Exception("pid not in map: %s", folly::errnoStr(errno).c_str()); } auto hcw = std::move(it->second); s_pidToHCWMap.erase(it); hcw->events = events; hcw->barrier.wait(); pthread_join(hcw->thr, nullptr); } lwp_write(afdt_fd, ret, errno, stat, time_us, events[0], events[1], events[2]); }
static void do_proc_open(int afdt_fd) { std::string cmd; lwp_read(afdt_fd, cmd); std::string cwd; lwp_read(afdt_fd, cwd); std::string buf; int env_size = 0; std::vector<std::string> env; lwp_read_int32(afdt_fd, env_size); for (int i = 0; i < env_size; i++) { lwp_read(afdt_fd, buf); env.push_back(buf); } int pipe_size = 0; lwp_read_int32(afdt_fd, pipe_size); std::vector<int> pvals; for (int i = 0; i < pipe_size; i++) { int fd_value; lwp_read_int32(afdt_fd, fd_value); pvals.push_back(fd_value); } std::vector<int> pkeys; for (int i = 0; i < pipe_size; i++) { int fd = recv_fd(afdt_fd); if (fd < 0) { lwp_write(afdt_fd, "error"); lwp_write_int32(afdt_fd, EPROTO); close_fds(pkeys); return; } pkeys.push_back(fd); } // indicate error if an empty command was received if (cmd.length() == 0) { lwp_write(afdt_fd, "error"); lwp_write_int32(afdt_fd, ENOENT); return; } // now ready to start the child process pid_t child = fork(); if (child == 0) { for (int i = 0; i < pipe_size; i++) { dup2(pkeys[i], pvals[i]); } if (!cwd.empty() && chdir(cwd.c_str())) { // non-zero for error // chdir failed, the working directory remains unchanged } if (!env.empty()) { char **envp = build_envp(env); execle("/bin/sh", "sh", "-c", cmd.c_str(), nullptr, envp); free(envp); } else { execl("/bin/sh", "sh", "-c", cmd.c_str(), nullptr); } _Exit(HPHP_EXIT_FAILURE); } else if (child > 0) { // successfully created the child process lwp_write(afdt_fd, "success"); lwp_write_int64(afdt_fd, (int64_t)child); } else { // failed creating the child process lwp_write(afdt_fd, "error"); lwp_write_int32(afdt_fd, errno); } close_fds(pkeys); }