void MythSystemUnix::Fork(time_t timeout) { QString LOC_ERR = QString("myth_system('%1'): Error: ").arg(GetLogCmd()); // For use in the child char locerr[MAX_BUFLEN]; strncpy(locerr, (const char *)LOC_ERR.toUtf8().constData(), MAX_BUFLEN); locerr[MAX_BUFLEN-1] = '\0'; LOG(VB_SYSTEM, LOG_DEBUG, QString("Launching: %1").arg(GetLogCmd())); GetBuffer(0)->setBuffer(0); GetBuffer(1)->setBuffer(0); GetBuffer(2)->setBuffer(0); int p_stdin[] = {-1,-1}; int p_stdout[] = {-1,-1}; int p_stderr[] = {-1,-1}; /* set up pipes */ if( GetSetting("UseStdin") ) { if( pipe(p_stdin) == -1 ) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdin pipe() failed"); SetStatus( GENERIC_EXIT_NOT_OK ); } else fcntl(p_stdin[1], F_SETFL, O_NONBLOCK); } if( GetSetting("UseStdout") ) { if( pipe(p_stdout) == -1 ) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout pipe() failed"); SetStatus( GENERIC_EXIT_NOT_OK ); } else fcntl(p_stdout[0], F_SETFL, O_NONBLOCK); } if( GetSetting("UseStderr") ) { if( pipe(p_stderr) == -1 ) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr pipe() failed"); SetStatus( GENERIC_EXIT_NOT_OK ); } else fcntl(p_stderr[0], F_SETFL, O_NONBLOCK); } // set up command args if (GetSetting("UseShell")) { QStringList args = QStringList("-c"); args << GetCommand() + " " + GetArgs().join(" "); SetArgs( args ); QString cmd = "/bin/sh"; SetCommand( cmd ); } QStringList args = GetArgs(); args.prepend(GetCommand().split('/').last()); SetArgs( args ); QByteArray cmdUTF8 = GetCommand().toUtf8(); const char *command = strdup(cmdUTF8.constData()); char **cmdargs = (char **)malloc((args.size() + 1) * sizeof(char *)); int i; QStringList::const_iterator it; for( i = 0, it = args.constBegin(); it != args.constEnd(); it++, i++ ) { cmdargs[i] = strdup( it->toUtf8().constData() ); } cmdargs[i] = NULL; const char *directory = NULL; QString dir = GetDirectory(); if (GetSetting("SetDirectory") && !dir.isEmpty()) directory = strdup(dir.toUtf8().constData()); // check before fork to avoid QString use in child bool setpgidsetting = GetSetting("SetPGID"); /* Do this before forking in case the child miserably fails */ m_timeout = timeout; if( timeout ) m_timeout += time(NULL); pid_t child = fork(); if (child < 0) { /* Fork failed, still in parent */ LOG(VB_SYSTEM, LOG_ERR, "fork() failed: " + ENO); SetStatus( GENERIC_EXIT_NOT_OK ); } else if( child > 0 ) { /* parent */ m_pid = child; SetStatus( GENERIC_EXIT_RUNNING ); LOG(VB_SYSTEM, LOG_INFO, QString("Managed child (PID: %1) has started! " "%2%3 command=%4, timeout=%5") .arg(m_pid) .arg(GetSetting("UseShell") ? "*" : "") .arg(GetSetting("RunInBackground") ? "&" : "") .arg(GetLogCmd()) .arg(timeout)); /* close unused pipe ends */ CLOSE(p_stdin[0]); CLOSE(p_stdout[1]); CLOSE(p_stderr[1]); // store the rest m_stdpipe[0] = p_stdin[1]; m_stdpipe[1] = p_stdout[0]; m_stdpipe[2] = p_stderr[0]; } else if (child == 0) { /* Child - NOTE: it is not safe to use LOG or QString between the * fork and execv calls in the child. It causes occasional locking * issues that cause deadlocked child processes. */ /* handle standard input */ if( p_stdin[0] >= 0 ) { /* try to attach stdin to input pipe - failure is fatal */ if( dup2(p_stdin[0], 0) < 0 ) { cerr << locerr << "Cannot redirect input pipe to standard input: " << strerror(errno) << endl; _exit(GENERIC_EXIT_PIPE_FAILURE); } } else { /* try to attach stdin to /dev/null */ int fd = open("/dev/null", O_RDONLY); if( fd >= 0 ) { if( dup2(fd, 0) < 0) { cerr << locerr << "Cannot redirect /dev/null to standard input," "\n\t\t\tfailed to duplicate file descriptor: " << strerror(errno) << endl; } } else { cerr << locerr << "Cannot redirect /dev/null to standard input, " "failed to open: " << strerror(errno) << endl; } } /* handle standard output */ if( p_stdout[1] >= 0 ) { /* try to attach stdout to output pipe - failure is fatal */ if( dup2(p_stdout[1], 1) < 0) { cerr << locerr << "Cannot redirect output pipe to standard output: " << strerror(errno) << endl; _exit(GENERIC_EXIT_PIPE_FAILURE); } } /* handle standard err */ if( p_stderr[1] >= 0 ) { /* try to attach stderr to error pipe - failure is fatal */ if( dup2(p_stderr[1], 2) < 0) { cerr << locerr << "Cannot redirect error pipe to standard error: " << strerror(errno) << endl; _exit(GENERIC_EXIT_PIPE_FAILURE); } } /* Close all open file descriptors except stdin/stdout/stderr */ for( int i = sysconf(_SC_OPEN_MAX) - 1; i > 2; i-- ) close(i); /* set directory */ if( directory && chdir(directory) < 0 ) { cerr << locerr << "chdir() failed: " << strerror(errno) << endl; } /* Set the process group id to be the same as the pid of this child * process. This ensures that any subprocesses launched by this * process can be killed along with the process itself. */ if (setpgidsetting && setpgid(0,0) < 0 ) { cerr << locerr << "setpgid() failed: " << strerror(errno) << endl; } /* run command */ if( execv(command, cmdargs) < 0 ) { // Can't use LOG due to locking fun. cerr << locerr << "execv() failed: " << strerror(errno) << endl; } /* Failed to exec */ _exit(GENERIC_EXIT_DAEMONIZING_ERROR); // this exit is ok } /* Parent */ // clean up the memory use if( command ) free((void *)command); if( directory ) free((void *)directory); if( cmdargs ) { for (i = 0; cmdargs[i]; i++) free( cmdargs[i] ); free( cmdargs ); } if( GetStatus() != GENERIC_EXIT_RUNNING ) { CLOSE(p_stdin[0]); CLOSE(p_stdin[1]); CLOSE(p_stdout[0]); CLOSE(p_stdout[1]); CLOSE(p_stderr[0]); CLOSE(p_stderr[1]); } }
void MythSystemWindows::Fork(time_t timeout) { QString LOC_ERR = QString("myth_system('%1'): Error: ").arg(GetLogCmd()); // For use in the child char locerr[MAX_BUFLEN]; strncpy(locerr, (const char *)LOC_ERR.toUtf8().constData(), MAX_BUFLEN); locerr[MAX_BUFLEN-1] = '\0'; VERBOSE(VB_SYSTEM|VB_EXTRA, QString("Launching: %1").arg(GetLogCmd())); GetBuffer(0)->setBuffer(0); GetBuffer(1)->setBuffer(0); GetBuffer(2)->setBuffer(0); HANDLE p_stdin[2] = { NULL, NULL }; HANDLE p_stdout[2] = { NULL, NULL }; HANDLE p_stderr[2] = { NULL, NULL }; SECURITY_ATTRIBUTES saAttr; STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; /* set up pipes */ if( GetSetting("UseStdin") ) { if (!CreatePipe(&p_stdin[0], &p_stdin[1], &saAttr, 0)) { VERBOSE(VB_IMPORTANT|VB_SYSTEM, (LOC_ERR + "stdin pipe() failed")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(p_stdin[1], HANDLE_FLAG_INHERIT, 0)) { VERBOSE(VB_SYSTEM|VB_IMPORTANT, (LOC_ERR + "stdin inheritance error")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { si.hStdInput = p_stdin[0]; si.dwFlags |= STARTF_USESTDHANDLES; } } } if( GetSetting("UseStdout") ) { if (!CreatePipe(&p_stdout[0], &p_stdout[1], &saAttr, 0)) { VERBOSE(VB_IMPORTANT|VB_SYSTEM, (LOC_ERR + "stdout pipe() failed")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(p_stdout[0], HANDLE_FLAG_INHERIT, 0)) { VERBOSE(VB_IMPORTANT|VB_SYSTEM, (LOC_ERR + "stdout inheritance error")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { si.hStdOutput = p_stdout[1]; si.dwFlags |= STARTF_USESTDHANDLES; } } } if( GetSetting("UseStderr") ) { if (!CreatePipe(&p_stderr[0], &p_stderr[1], &saAttr, 0)) { VERBOSE(VB_IMPORTANT|VB_SYSTEM, (LOC_ERR + "stderr pipe() failed")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { // Ensure the read handle to the pipe for STDERR is not inherited. if (!SetHandleInformation(p_stderr[0], HANDLE_FLAG_INHERIT, 0)) { VERBOSE(VB_IMPORTANT|VB_SYSTEM, (LOC_ERR + "stderr inheritance error")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { si.hStdError = p_stderr[2]; si.dwFlags |= STARTF_USESTDHANDLES; } } } // set up command args QString cmd = GetCommand() + " " + GetArgs().join(" "); if (GetSetting("UseShell")) cmd.prepend("cmd.exe /c "); SetCommand( cmd ); QByteArray cmdUTF8 = GetCommand().toUtf8(); TCHAR *command = TEXT((char *)cmdUTF8.constData()); const char *directory = NULL; QString dir = GetDirectory(); if (GetSetting("SetDirectory") && !dir.isEmpty()) directory = strdup(dir.toUtf8().constData()); PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); m_timeout = timeout; if( timeout ) m_timeout += time(NULL); bool success = CreateProcess(NULL, command, // command line NULL, // process security attributes NULL, // primary thread security attributes TRUE, // handles are inherited 0, // creation flags NULL, // use parent's environment directory, // use parent's current directory &si, // STARTUPINFO pointer &pi); // receives PROCESS_INFORMATION if (!success) { VERBOSE(VB_IMPORTANT|VB_SYSTEM, (LOC_ERR + "CreateProcess() failed")); SetStatus( GENERIC_EXIT_NOT_OK ); } else { /* parent */ m_child = pi.hProcess; SetStatus( GENERIC_EXIT_RUNNING ); VERBOSE(VB_SYSTEM|VB_EXTRA, QString("Managed child (Handle: %1) has started! " "%2%3 command=%4, timeout=%5") .arg((long long)m_child) .arg(GetSetting("UseShell") ? "*" : "") .arg(GetSetting("RunInBackground") ? "&" : "") .arg(GetLogCmd()) .arg(timeout)); /* close unused pipe ends */ CLOSE(p_stdin[0]); CLOSE(p_stdout[1]); CLOSE(p_stderr[1]); // store the rest m_stdpipe[0] = p_stdin[1]; m_stdpipe[1] = p_stdout[0]; m_stdpipe[2] = p_stderr[0]; // clean up the memory use if( directory ) free((void *)directory); } /* Parent */ if( GetStatus() != GENERIC_EXIT_RUNNING ) { CLOSE(p_stdin[0]); CLOSE(p_stdin[1]); CLOSE(p_stdout[0]); CLOSE(p_stdout[1]); CLOSE(p_stderr[0]); CLOSE(p_stderr[1]); } }
void MythSystemLegacyWindows::Fork(time_t timeout) { BOOL bInherit = FALSE; QString LOC_ERR = QString("myth_system('%1'): Error: ").arg(GetLogCmd()); LOG(VB_SYSTEM, LOG_DEBUG, QString("Launching: %1").arg(GetLogCmd())); HANDLE p_stdin[2] = { nullptr, nullptr }; HANDLE p_stdout[2] = { nullptr, nullptr }; HANDLE p_stderr[2] = { nullptr, nullptr }; SECURITY_ATTRIBUTES saAttr; STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); // Set the bInheritHandle flag so pipe handles are inherited. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = true; saAttr.lpSecurityDescriptor = nullptr; /* set up pipes */ if( GetSetting("UseStdin") ) { bInherit = TRUE; if (!CreatePipe(&p_stdin[0], &p_stdin[1], &saAttr, 0)) { LOG(VB_GENERAL, LOG_ERR, LOC_ERR + "stdin pipe() failed"); SetStatus( GENERIC_EXIT_NOT_OK ); } else { // Ensure the write handle to the pipe for STDIN is not inherited. if (!SetHandleInformation(p_stdin[1], HANDLE_FLAG_INHERIT, 0)) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdin inheritance error"); SetStatus( GENERIC_EXIT_NOT_OK ); } else { si.hStdInput = p_stdin[0]; si.dwFlags |= STARTF_USESTDHANDLES; } } } if( GetSetting("UseStdout") ) { bInherit = TRUE; if (!CreatePipe(&p_stdout[0], &p_stdout[1], &saAttr, 0)) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout pipe() failed"); SetStatus( GENERIC_EXIT_NOT_OK ); } else { // Ensure the read handle to the pipe for STDOUT is not inherited. if (!SetHandleInformation(p_stdout[0], HANDLE_FLAG_INHERIT, 0)) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stdout inheritance error"); SetStatus( GENERIC_EXIT_NOT_OK ); } else { si.hStdOutput = p_stdout[1]; si.dwFlags |= STARTF_USESTDHANDLES; } } } if( GetSetting("UseStderr") ) { bInherit = TRUE; if (!CreatePipe(&p_stderr[0], &p_stderr[1], &saAttr, 0)) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr pipe() failed"); SetStatus( GENERIC_EXIT_NOT_OK ); } else { // Ensure the read handle to the pipe for STDERR is not inherited. if (!SetHandleInformation(p_stderr[0], HANDLE_FLAG_INHERIT, 0)) { LOG(VB_SYSTEM, LOG_ERR, LOC_ERR + "stderr inheritance error"); SetStatus( GENERIC_EXIT_NOT_OK ); } else { si.hStdError = p_stderr[1]; si.dwFlags |= STARTF_USESTDHANDLES; } } } // set up command args QString cmd = GetCommand() + " " + GetArgs().join(" "); if (GetSetting("UseShell")) cmd.prepend("cmd.exe /c "); SetCommand( cmd ); QString sCmd = GetCommand(); QString dir = GetDirectory(); PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); m_timeout = timeout; if( timeout ) m_timeout += time(nullptr); LPCWSTR pDir = nullptr; if (dir.length() > 0) pDir = (LPCWSTR)dir.utf16(); bool success = CreateProcess( nullptr, (LPWSTR)sCmd.utf16(), // command line nullptr, // process security attributes nullptr, // primary thread security attributes bInherit, // handles are inherited 0, // creation flags nullptr, // use parent's environment pDir, // use parent's current directory &si, // STARTUPINFO pointer &pi); // receives PROCESS_INFORMATION if (!success) { DWORD dwErr = GetLastError(); LOG(VB_SYSTEM, LOG_ERR, QString( "%1 CreateProcess() failed (%2)") .arg( LOC_ERR ) .arg( dwErr )); SetStatus( GENERIC_EXIT_NOT_OK ); } else { /* parent */ m_child = pi.hProcess; SetStatus( GENERIC_EXIT_RUNNING ); LOG(VB_SYSTEM, LOG_INFO, QString("Managed child (Handle: %1) has started! " "%2%3 command=%4, timeout=%5") .arg((long long)m_child) .arg(GetSetting("UseShell") ? "*" : "") .arg(GetSetting("RunInBackground") ? "&" : "") .arg(GetLogCmd()) .arg(timeout)); /* close unused pipe ends */ CLOSE(p_stdin[0]); CLOSE(p_stdout[1]); CLOSE(p_stderr[1]); // store the rest m_stdpipe[0] = p_stdin[1]; m_stdpipe[1] = p_stdout[0]; m_stdpipe[2] = p_stderr[0]; } /* Parent */ if( GetStatus() != GENERIC_EXIT_RUNNING ) { CLOSE(p_stdin[0]); CLOSE(p_stdin[1]); CLOSE(p_stdout[0]); CLOSE(p_stdout[1]); CLOSE(p_stderr[0]); CLOSE(p_stderr[1]); } }