void Process::start(const QString &commandLine) { m_state = Starting; SECURITY_ATTRIBUTES sa = {0}; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; if (!setupPipe(d->stdinPipe, &sa, InputPipe)) qFatal("Cannot setup pipe for stdin."); if (!setupPipe(d->stdoutPipe, &sa, OutputPipe)) qFatal("Cannot setup pipe for stdout."); if (!setupPipe(d->stderrPipe, &sa, OutputPipe)) qFatal("Cannot setup pipe for stderr."); IoCompletionPort::instance()->registerObserver(&d->stdoutChannel, d->stdoutPipe.hRead); IoCompletionPort::instance()->registerObserver(&d->stderrChannel, d->stderrPipe.hRead); if (!d->startRead()) { m_state = NotRunning; emit error(FailedToStart); qWarning("Can't read output channels."); return; } STARTUPINFO si = {0}; si.cb = sizeof(si); si.hStdInput = d->stdinPipe.hRead; si.hStdOutput = d->stdoutPipe.hWrite; si.hStdError = d->stderrPipe.hWrite; si.dwFlags = STARTF_USESTDHANDLES; DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT; PROCESS_INFORMATION pi; wchar_t *strCommandLine = _wcsdup((const wchar_t*)commandLine.utf16()); // CreateProcess can modify this string const wchar_t *strWorkingDir = 0; if (!m_workingDirectory.isEmpty()) { m_workingDirectory = QDir::toNativeSeparators(m_workingDirectory); strWorkingDir = (const wchar_t*)m_workingDirectory.utf16(); } void *envBlock = (m_envBlock.isEmpty() ? 0 : m_envBlock.data()); BOOL bResult = CreateProcess(NULL, strCommandLine, 0, 0, TRUE, dwCreationFlags, envBlock, strWorkingDir, &si, &pi); free(strCommandLine); strCommandLine = 0; if (!bResult) { m_state = NotRunning; emit error(FailedToStart); return; } // Close the pipe handles. This process doesn't need them anymore. safelyCloseHandle(d->stdinPipe.hRead); safelyCloseHandle(d->stdinPipe.hWrite); safelyCloseHandle(d->stdoutPipe.hWrite); safelyCloseHandle(d->stderrPipe.hWrite); d->deathNotifier.setHandle(pi.hProcess); d->deathNotifier.setEnabled(true); d->hProcess = pi.hProcess; d->hProcessThread = pi.hThread; m_state = Running; }
void ForkExecParent::start() { if (m_watchChild) { SE_THROW("child already started"); } // boost::shared_ptr<ForkExecParent> me = ...; GDBusCXX::DBusErrorCXX dbusError; SE_LOG_DEBUG(NULL, NULL, "ForkExecParent: preparing for child process %s", m_helper.c_str()); m_server = GDBusCXX::DBusServerCXX::listen("", &dbusError); if (!m_server) { dbusError.throwFailure("starting server"); } m_server->setNewConnectionCallback(boost::bind(&ForkExecParent::newClientConnection, this, _2)); // look for helper binary std::string helper; GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD; if (m_helper.find('/') == m_helper.npos) { helper = getEnv("SYNCEVOLUTION_LIBEXEC_DIR", ""); if (helper.empty()) { // env variable not set, look in libexec dir helper = SYNCEVO_LIBEXEC; helper += "/"; helper += m_helper; if (access(helper.c_str(), R_OK)) { // some error, try PATH flags = (GSpawnFlags)(flags | G_SPAWN_SEARCH_PATH); helper = m_helper; } } else { // use env variable without further checks, must work helper += "/"; helper += m_helper; } } else { // absolute path, use it helper = m_helper; } m_argvStrings.push_back(helper); m_argv.reset(AllocStringArray(m_argvStrings)); for (char **env = environ; *env; env++) { if (!boost::starts_with(*env, ForkExecEnvVar)) { m_envStrings.push_back(*env); } } // pass D-Bus address via env variable m_envStrings.push_back(ForkExecEnvVar + m_server->getAddress()); m_env.reset(AllocStringArray(m_envStrings)); SE_LOG_DEBUG(NULL, NULL, "ForkExecParent: running %s with D-Bus address %s", helper.c_str(), m_server->getAddress().c_str()); // Check which kind of output redirection is wanted. m_mergedStdoutStderr = !m_onOutput.empty(); if (!m_onOutput.empty()) { m_mergedStdoutStderr = true; } GErrorCXX gerror; int err = -1, out = -1; if (!g_spawn_async_with_pipes(NULL, // working directory static_cast<gchar **>(m_argv.get()), static_cast<gchar **>(m_env.get()), flags, // child setup function: redirect stdout to stderr m_mergedStdoutStderr ? setStdoutToStderr : NULL, NULL, // child setup user data &m_childPid, NULL, // set stdin to /dev/null (m_mergedStdoutStderr || m_onStdout.empty()) ? NULL : &out, (m_mergedStdoutStderr || !m_onStderr.empty()) ? &err : NULL, gerror)) { m_childPid = 0; gerror.throwError("spawning child"); } // set up output redirection, ignoring failures setupPipe(m_err, m_errID, err); setupPipe(m_out, m_outID, out); SE_LOG_DEBUG(NULL, NULL, "ForkExecParent: child process for %s has pid %ld", helper.c_str(), (long)m_childPid); // TODO: introduce C++ wrapper around GSource m_watchChild = g_child_watch_source_new(m_childPid); g_source_set_callback(m_watchChild, (GSourceFunc)watchChildCallback, this, NULL); g_source_attach(m_watchChild, NULL); }
int main(int argc, char** argv) { int fd = setupPipe(); service(fd); return 0; }