Beispiel #1
0
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]);
    }
}
Beispiel #2
0
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]);
    }
}
Beispiel #3
0
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]);
    }
}