Пример #1
0
struct subprocess * subprocess_create (struct subprocess_manager *sm)
{
    int fds[2];
    int saved_errno;
    struct subprocess *p = xzmalloc (sizeof (*p));

    memset (p, 0, sizeof (*p));
    p->childfd = -1;
    p->parentfd = -1;
    fda_zero (p->child_fda);

    p->sm = sm;
    if (!(p->zhash = zhash_new ())
     || hooks_table_init (p) < 0) {
        errno = ENOMEM;
        goto error;
    }

    p->pid = (pid_t) -1;
    p->refcount = 1;

    if (socketpair (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, fds) < 0)
        goto error;
    p->childfd = fds[0];
    p->parentfd = fds[1];

    p->started = 0;
    p->running = 0;
    p->exited = 0;
    p->completed = 0;

    if (!(p->zio_in = zio_pipe_writer_create ("stdin", (void *) p)))
        goto error;
    if (!(p->zio_out = zio_pipe_reader_create ("stdout", NULL, (void *) p)))
        goto error;
    if (!(p->zio_err = zio_pipe_reader_create ("stderr", NULL, (void *) p)))
        goto error;

    zio_set_send_cb (p->zio_out, output_handler);
    zio_set_send_cb (p->zio_err, output_handler);

    if (zlist_append (sm->processes, (void *)p) < 0) {
        errno = ENOMEM;
        goto error;
    }

    if (sm->reactor) {
        zio_reactor_attach (p->zio_in, sm->reactor);
        zio_reactor_attach (p->zio_err, sm->reactor);
        zio_reactor_attach (p->zio_out, sm->reactor);
    }
    return (p);
error:
    saved_errno = errno;
    subprocess_destroy (p);
    errno = saved_errno;
    return (NULL);
}
Пример #2
0
static int exit_handler (struct subprocess *p, void *arg)
{
    ok (p != NULL, "exit_handler: valid subprocess");
    ok (arg != NULL, "exit_handler: arg is expected");
    ok (subprocess_exited (p), "exit_handler: subprocess exited");
    ok (subprocess_exit_code (p) == 0, "exit_handler: subprocess exited normally");
    subprocess_destroy (p);
    return (0);
}
Пример #3
0
void runlevel_destroy (runlevel_t *r)
{
    if (r) {
        int i;
        for (i = 0; i < 4; i++) {
            if (r->rc[i])
                subprocess_destroy (r->rc[i]);
        }
        free (r);
    }
}
Пример #4
0
struct subprocess *
subprocess_manager_run (struct subprocess_manager *sm, int ac, char **av,
    char **env)
{
    struct subprocess *p = subprocess_create (sm);
    if (p == NULL)
        return (NULL);

    if ((subprocess_set_args (p, ac, av) < 0) ||
        (env && subprocess_set_environ (p, env) < 0)) {
        subprocess_destroy (p);
        return (NULL);
    }

    if (subprocess_run (p) < 0) {
        subprocess_destroy (p);
        return (NULL);
    }

    return (p);
}
Пример #5
0
/* See POSIX 2008 Volume 3 Shell and Utilities, Issue 7
 * Section 2.8.2 Exit status for shell commands (page 2315)
 */
static int subprocess_cb (struct subprocess *p)
{
    runlevel_t *r = subprocess_get_context (p, "runlevel");
    int rc = subprocess_exit_code (p);
    const char *exit_string = subprocess_exit_string (p);

    assert (r->rc[r->level] == p);
    r->rc[r->level] = NULL;

    if (r->cb)
        r->cb (r, r->level, rc, exit_string, r->cb_arg);

    subprocess_destroy (p);

    return 0;
}
Пример #6
0
int runlevel_set_rc (runlevel_t *r, int level, const char *command,
                     const char *local_uri)
{
    struct subprocess *p = NULL;
    const char *shell = getenv ("SHELL");
    if (!shell)
        shell = "/bin/bash";

    if (level < 1 || level > 3 || r->rc[level] != NULL || !r->sm) {
        errno = EINVAL;
        goto error;
    }

    if (!(p = subprocess_create (r->sm))
            || subprocess_set_context (p, "runlevel", r) < 0
            || subprocess_add_hook (p, SUBPROCESS_COMPLETE, subprocess_cb) < 0
            || subprocess_argv_append (p, shell) < 0
            || (command && subprocess_argv_append (p, "-c") < 0)
            || (command && subprocess_argv_append (p, command) < 0)
            || subprocess_set_environ (p, environ) < 0
            || subprocess_unsetenv (p, "PMI_FD") < 0
            || subprocess_unsetenv (p, "PMI_RANK") < 0
            || subprocess_unsetenv (p, "PMI_SIZE") < 0
            || (local_uri && subprocess_setenv (p, "FLUX_URI",
                                                local_uri, 1) < 0))
        goto error;
    if (level == 1 || level == 3) {
        if (subprocess_setenv (p, "FLUX_NODESET_MASK", r->nodeset, 1) < 0)
            goto error;
        if (subprocess_set_io_callback (p, subprocess_io_cb) < 0)
            goto error;
        if (subprocess_set_context (p, "runlevel_t", r) < 0)
            goto error;
    }
    r->rc[level] = p;
    return 0;
error:
    if (p)
        subprocess_destroy (p);
    return -1;
}
Пример #7
0
int main (int ac, char **av)
{
    int rc;
    struct subprocess_manager *sm;
    struct subprocess *p, *q;
    const char *s;
    char *buf;
    char *args[] = { "hello", NULL };
    char *args2[] = { "goodbye", NULL };
    char *args3[] = { "/bin/true", NULL };
    char *args4[] = { "/bin/sleep", "10", NULL };
    int start_fdcount, end_fdcount;

    start_fdcount = fdcount ();
    diag ("initial fd count %d", start_fdcount);

    plan (NO_PLAN);

    if (!(sm = subprocess_manager_create ()))
        BAIL_OUT ("Failed to create subprocess manager");
    ok (sm != NULL, "create subprocess manager");

    diag ("subprocess accessors tests");
    if (!(p = subprocess_create (sm)))
        BAIL_OUT ("Failed to create subprocess handle: %s", strerror (errno));
    ok (p != NULL, "create subprocess handle");

    rc = subprocess_set_args (p, 1, args);
    ok (rc >= 0, "subprocess_set_args: %s", strerror (errno));

    ok (subprocess_get_argc (p) == 1, "subprocess argc is 1");

    s = subprocess_get_arg (p, 0);
    is (s, "hello", "subprocess argv[0] is 'hello'");

    rc = subprocess_argv_append (p, "foo");
    ok (rc >= 0, "subprocess_arg_append");
    ok (subprocess_get_argc (p) == 2, "subprocess argc is now 2");

    s = subprocess_get_arg (p, 2);
    ok (s == NULL, "subprocess_get_arg() out of bounds returns NULL");

    rc = subprocess_set_args (p, 1, args2);
    ok (rc >= 0, "set_args replaces existing");

    s = subprocess_get_arg (p, 0);
    is (s, "goodbye", "subprocess argv[0] is 'goodbye'");

    rc = subprocess_setenv (p, "FOO", "bar", 1);
    ok (rc >= 0, "subprocess_setenv");

    s = subprocess_getenv (p, "FOO");
    is (s, "bar", "subprocess_getenv works");

    rc = subprocess_setenv (p, "FOO", "bar2", 0);
    ok (rc == -1, "subprocess_setenv without overwrite fails for existing var");
    ok (errno == EEXIST, "and with appropriate errno");

    s = subprocess_getenv (p, "FOO");
    is (s, "bar", "subproces_getenv still shows correct variable");

    subprocess_unsetenv (p, "FOO");
    s = subprocess_getenv (p, "FOO");
    ok (s == NULL, "subproces_getenv fails for unset variable");

    rc = subprocess_setenvf (p, "FOO", 1, "%d", 42);
    ok (rc >= 0, "subprocess_setenvf");

    s = subprocess_getenv (p, "FOO");
    is (s, "42", "subprocess_getenv works after setenvf");

    is (subprocess_state_string (p), "Pending",
        "Unstarted process has state 'Pending'");

    subprocess_destroy (p);
    diag ("fd count after subproc create/destroy %d", fdcount ());

    /* Test running an executable */
    diag ("test subprocess_manager_run");
    p = subprocess_manager_run (sm, 1, args3, NULL);
    ok (p != NULL, "subprocess_manager_run");
    ok (subprocess_pid (p) != (pid_t) -1, "process has valid pid");

    q = subprocess_manager_wait (sm);
    ok (p == q, "subprocess_manager_wait returns correct process");

    ok (subprocess_exited (p), "subprocess has exited after wait returns");
    is (subprocess_state_string (p), "Exited", "State is now 'Exited'");
    ok (subprocess_exit_code (p) == 0, "With expected exit code");
    subprocess_destroy (p);
    q = NULL;

    /*  Test failing program */
    diag ("test expected failure from subprocess_manager_run");
    args3[0] = "/bin/false";
    p = subprocess_manager_run (sm, 1, args3, NULL);
    if (p) {
        ok (p != NULL, "subprocess_manager_run");
        ok (subprocess_pid (p) != (pid_t) -1, "process has valid pid");
        q = subprocess_manager_wait (sm);
        ok (p == q, "subprocess_manager_wait returns correct process");
        is (subprocess_state_string (p), "Exited", "State is now 'Exited'");
        is (subprocess_exit_string (p), "Exited with non-zero status",
                "State is now 'Exited with non-zero status'");
        ok (subprocess_exit_code (p) == 1, "Exit code is 1.");
        subprocess_destroy (p);
        q = NULL;
    }

    diag ("Test signaled program");

    /* Test signaled program */
    p = subprocess_manager_run (sm, 2, args4, NULL);
    ok (p != NULL, "subprocess_manager_run: %s", strerror (errno));
    if (p) {
        ok (subprocess_pid (p) != (pid_t) -1, "process has valid pid");

        ok (subprocess_kill (p, SIGKILL) >= 0, "subprocess_kill");

        q = subprocess_manager_wait (sm);
        ok (p == q, "subprocess_manager_wait returns correct process");
        is (subprocess_state_string (p), "Exited", "State is now 'Exited'");
        is (subprocess_exit_string (p), "Killed", "Exit string is 'Killed'");
        ok (subprocess_signaled (p) == 9, "Killed by signal 9.");
        ok (subprocess_exit_status (p) == 0x9, "Exit status is 0x9 (Killed)");
        ok (subprocess_exit_code (p) == 137, "Exit code is 137 (128+9)");
        subprocess_destroy (p);
    }

    q = NULL;

    diag ("Test fork/exec interface");
    /* Test separate fork/exec interface */
    p = subprocess_create (sm);
    ok (p != NULL, "subprocess_create works");
    ok (subprocess_pid (p) == (pid_t) -1, "Initial pid value is -1");
    ok (subprocess_fork (p) == -1, "fork on unitialized subprocess should fail");
    ok (subprocess_kill (p, 1) == -1, "kill on unitialized subprocess should fail");
    is (subprocess_state_string (p), "Pending",
        "initial subprocess state is 'Pending'");

    ok (subprocess_argv_append (p, "true") >= 0, "set argv");
    ok (subprocess_setenv (p, "PATH", getenv ("PATH"), 1) >= 0, "set dnv");

    ok (subprocess_fork (p) == 0, "subprocess_fork");
    is (subprocess_state_string (p), "Waiting", "subprocess is Waiting");
    ok (subprocess_pid (p) > 0, "subprocess_pid() is valid");

    ok (subprocess_exec (p) == 0, "subprocess_run");
    is (subprocess_state_string (p), "Running", "subprocess is Running");
    q = subprocess_manager_wait (sm);
    ok (q != NULL, "subprocess_manager_wait");
    ok (q == p, "got correct child after wait");

    ok (subprocess_exit_code (p) == 0, "Child exited normally");

    subprocess_destroy (p);
    q = NULL;

    diag ("Test exec failure");
    /* Test exec failure */
    p = subprocess_create (sm);
    ok (p != NULL, "subprocess create");
    ok (subprocess_argv_append (p, "/unlikely/program") >= 0, "set argv");
    ok (subprocess_setenv (p, "PATH", getenv ("PATH"), 1) >= 0, "setnv");

    ok (subprocess_fork (p) == 0, "subprocess_fork");
    rc = subprocess_exec (p);
    ok (rc < 0, "subprocess_exec should fail");
    ok (errno == ENOENT, "errno should be ENOENT");
    is (subprocess_state_string (p), "Exec Failure", "State is Exec Failed");
    is (subprocess_exit_string (p), "Exec Failure", "Exit state is Exec Failed");
    subprocess_destroy (p);

    diag ("Test set working directory");
    /* Test set working directory */
    p = subprocess_create (sm);
    ok (p != NULL, "subprocess create");
    ok (subprocess_get_cwd (p) == NULL, "CWD is not set");
    ok (subprocess_set_cwd (p, "/tmp") >= 0, "Set CWD to /tmp");
    is (subprocess_get_cwd (p), "/tmp", "CWD is now /tmp");
    ok (subprocess_setenv (p, "PATH", getenv ("PATH"), 1) >= 0, "set PATH");
    ok (subprocess_set_command (p, "test `pwd` = '/tmp'" ) >= 0, "Set args");
    ok (subprocess_run (p) >= 0, "subprocess_run");
    is (subprocess_state_string (p), "Running", "subprocess now running");
    q = subprocess_manager_wait (sm);
    ok (q != NULL, "subprocess_manager_wait: %s", strerror (errno));
    ok (q == p, "subprocess_manager_wait() got expected subprocess");
    ok (subprocess_exited (p), "subprocess exited");
    ok (!subprocess_signaled (p), "subprocess didn't die from signal");
    ok (subprocess_exit_code (p) == 0, "subprocess successfully run in /tmp");
    subprocess_destroy (p);

    diag ("Test subprocess_reap interface");
    /* Test subprocess_reap */
    p = subprocess_create (sm);
    q = subprocess_create (sm);

    ok (subprocess_argv_append (p, "/bin/true") >= 0,
        "set argv for first subprocess");
    ok (subprocess_argv_append (q, "/bin/true") >= 0,
        "set argv for second subprocess");
    ok (subprocess_run (p) >= 0, "run process 1");
    ok (subprocess_run (q) >= 0, "run process 2");

    ok (subprocess_reap (q) >= 0, "reap process 2");
    ok (subprocess_exited (q), "process 2 is now exited");
    ok (subprocess_exit_code (q) == 0, "process 2 exited with code 0");

    ok (subprocess_reap (p) >= 0, "reap process 1");
    ok (subprocess_exited (p), "process 1 is now exited");
    ok (subprocess_exit_code (p) == 0, "process 1 exited with code 0");

    subprocess_destroy (p);
    subprocess_destroy (q);

    diag ("Test subprocess I/O");
    /* Test subprocess output */
    p = subprocess_create (sm);
    ok (p != NULL, "subprocess_create");
    ok (subprocess_argv_append (p, "/bin/echo") >= 0,  "subprocess_argv_append");
    ok (subprocess_argv_append (p, "Hello, 123") >= 0, "subprocess_argv_append");

    buf = NULL;
    subprocess_set_context (p, "io", (void *) &buf);
    ok (subprocess_get_context (p, "io") == (void *) &buf, "able to set subprocess context");

    ok (subprocess_set_io_callback (p, testio_cb) >= 0, "set io callback");

    ok (subprocess_run (p) >= 0, "run process with IO");

    ok (subprocess_reap (p) >= 0, "reap process");
    ok (subprocess_flush_io (p) >=0, "flush io");

    ok (subprocess_exited (p) >= 0, "process is now exited");
    ok (subprocess_exit_code (p) == 0, "process exited normally");

    ok (buf != NULL, "io buffer is allocated");
    if (buf) {
        ok (strcmp (buf, "Hello, 123\n") == 0, "io buffer is correct");
        free (buf);
    }
    subprocess_destroy (p);


    /* Test subprocess input */
    diag ("test subprocess stdin");
    p = subprocess_create (sm);
    ok (p != NULL, "subprocess_create");
    ok (subprocess_argv_append (p, "/bin/cat") >= 0,  "subprocess_argv_append");

    buf = NULL;
    subprocess_set_context (p, "io", (void *) &buf);
    ok (subprocess_get_context (p, "io") == (void *) &buf, "able to set subprocess context");

    ok (subprocess_set_io_callback (p, testio_cb) >= 0, "set io callback");

    ok (subprocess_run (p) >= 0, "run process with IO");

    ok (subprocess_write (p, "Hello\n", 7, true) >= 0, "write to subprocess");
    ok (subprocess_reap (p) >= 0, "reap process");
    ok (subprocess_flush_io (p) >= 0, "manually flush io");
    ok (subprocess_io_complete (p) == 1, "io is now complete");

    ok (subprocess_exited (p) >= 0, "process is now exited");
    ok (subprocess_exit_code (p) == 0, "process exited normally");

    ok (buf != NULL, "io buffer is allocated");
    if (buf) {
        ok (strcmp (buf, "Hello\n") == 0, "io buffer is correct");
        free (buf);
    }
    subprocess_destroy (p);
    subprocess_manager_destroy (sm);

    end_fdcount = fdcount ();
    diag ("final fd count %d", end_fdcount);
    ok (start_fdcount == end_fdcount,
        "no file descriptors were leaked");

    done_testing ();
}