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; }
void LightProcess::ChangeUser(const std::string &username) { if (username.empty()) return; for (int i = 0; i < g_procsCount; i++) { Lock lock(g_procs[i].m_procMutex); auto fout = g_procs[i].m_afdt_fd; lwp_write(fout, "change_user"); lwp_write(fout, username); } }
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; }
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; }
pid_t LightProcess::waitpid(pid_t pid, int *stat_loc, int options, int timeout) { if (!Available()) { // light process is not really there return ::waitpid(pid, stat_loc, options); } int id = GetId(); Lock lock(g_procs[id].m_procMutex); auto fout = g_procs[id].m_afdt_fd; lwp_write(fout, "waitpid"); lwp_write_int64(fout, (int64_t)pid); lwp_write_int32(fout, options); lwp_write_int32(fout, timeout); int64_t ret; int stat; auto fin = g_procs[id].m_afdt_fd; lwp_read_int64(fin, ret); lwp_read_int32(fin, stat); *stat_loc = stat; if (ret < 0) { lwp_read_int32(fin, errno); } return (pid_t)ret; }
int LightProcess::pclose(FILE *f) { if (!Available()) { return ::pclose(f); } int id = GetId(); Lock lock(g_procs[id].m_procMutex); std::map<int64_t, int64_t>::iterator it = g_procs[id].m_popenMap.find((int64_t)f); if (it == g_procs[id].m_popenMap.end()) { // try to close it with normal pclose return ::pclose(f); } int64_t f2 = it->second; g_procs[id].m_popenMap.erase((int64_t)f); fclose(f); lwp_write(g_procs[id].m_afdt_fd, "pclose"); lwp_write_int64(g_procs[id].m_afdt_fd, f2); int ret = -1; lwp_read_int32(g_procs[id].m_afdt_fd, ret); if (ret < 0) { lwp_read_int32(g_procs[id].m_afdt_fd, errno); } return ret; }
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); } }
void LightProcess::closeShadow() { Lock lock(m_procMutex); if (m_shadowProcess) { lwp_write(m_afdt_fd, "exit"); // removes the "zombie" process, so not to interfere with later waits ::waitpid(m_shadowProcess, nullptr, 0); } if (m_afdt_fd >= 0) { ::close(m_afdt_fd); m_afdt_fd = -1; } m_shadowProcess = 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]); }
/* * This procedure writes a list of type 'listt' according to the * rds interface protocol. It uses the already opened and initialized * protocol module (see file protocol.[c,h]). * param listt - the type of the list, see rdimpl.h * param Po - print option, if 1 the list will be also printed on stdout. * return 0, or -1 on error and store the error message in * the global buffer 'errmsg'. */ int list_write(int listt, int Po) { char idstr[P_MAXVAL]; list_t *list; id_info_t *id = NULL, *nextid; if (listt == L_LWP) { return (lwp_write(&lwps)); } else if (listt == L_SYSTEM) { if (wr_lhead(listt, 1) != 0) { format_err( "RDS protocol error: cannot write list header"); return (-1); } (void) snprintf(idstr, sizeof (idstr), "%s", sys_info.name); if (wr_element(listt, (char *)(&sys_info), idstr) != 0) { format_err( "RDS protocol error: cannot write list header"); return (-1); } } else { switch (listt) { case L_PRC_SI : list = &processes; break; case L_AC_USR : case L_USR_SI : list = &users; break; case L_AC_PRJ : case L_PRJ_SI : list = &projects; break; } id = list->l_head; if (wr_lhead(listt, list->l_count) != 0) { format_err( "RDS protocol error: cannot write list header"); return (-1); } while (id != NULL) { switch (listt) { case L_PRC_SI : (void) sprintf(idstr, "%d", id->id_pid); break; case L_AC_USR : case L_USR_SI : (void) sprintf(idstr, "%d", id->id_uid); break; case L_AC_PRJ : case L_PRJ_SI : (void) snprintf(idstr, sizeof (idstr), "%s", id->id_name); break; } if (wr_element(listt, (char *)id, idstr) != 0) { format_err( "RDS protocol error: cannot write list header"); } if (Po == 1) prtelement(stderr, id); nextid = id->id_next; id = nextid; } } return (0); }
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); }