user3808088
user3808088

Reputation: 87

Launch execlp as background process

I'm trying to launch a program using execlp, continue with the program. and get control of the shell back.

This is what I did after some research from SO.

pid_t child;
pid_t sid;  
child = fork();
if (!child) {
    sid = setsid();
    if(!sid) {   
        exit(1);
    }
    execlp(RUN_EXE, RUN_EXE, SPEC_RUN.run_args[j], (char *)0);
}

But I'm unable to print anything after execlp. The execlp works correctly for me.

What do I need to do for the shell to return back?

Upvotes: 0

Views: 974

Answers (2)

zwol
zwol

Reputation: 140455

If I understand you correctly, you want to create a subprocess, run a program in that process, and then wait for it to finish. Each of those three steps is its own operation on Unix, when working with system primitives directly. You know about fork() and execlp() already; the third step, waiting for a subprocess to finish, is done with waitpid() and its relatives.

Building on what Basile wrote, here is the missing piece:

#define _POSIX_C_SOURCE 200809L /* strsignal */
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

void run_program(void)
{
    int status;
    pid_t pid;

    fflush(0);

    /* create the subprocess */
    pid = fork();
    if (p < 0) { /* fork failed */
        perror("fork");
        exit(1);
    }

    /* in the child only, execute a program */
    if (p == 0) {
        execlp(RUN_EXE, RUN_EXE, SPEC_RUN.run_args[j], (char *)0);
        /* If execlp returns, it failed.  It is unsafe to call `exit` if this
           happens; you must instead use `_exit`.  This means you have to flush
           output manually. */
        fprintf(stderr, "execlp: %s: %s\n", RUN_EXE, strerror(errno));
        fflush(stderr);
        _exit(1);
    }

    /* in the parent, wait for the child to finish */
    if (waitpid(pid, &status, 0) != pid) {
        perror("waitpid");
        exit(1);
    }

    /* decode and report any failure of the child */
    if (WIFEXITED(status)) {
        if (WEXITSTATUS(status) == 0)
            return; /* success! */

        fprintf(stderr, "%s: unsuccessful exit %d\n",
                RUN_EXE, WEXITSTATUS(status));
        exit(1);
    }
    if (WIFSIGNALED(status)) {
        fprintf(stderr, "%s: %s%s\n",
                RUN_EXE,
                strsignal(WTERMSIG(status)),
                WCOREDUMP(status) ? " (core dumped)" : "");
        exit(1);
    }
    fprintf(stderr, "%s: impossible exit status %04x\n",
            RUN_EXE, status);
    exit(1);
}

... If that looks like a giant ball of hair that you don't want to deal with, you should consider using the higher-level library functions system() and/or popen() instead. They have their own flaws -- most importantly, they go through /bin/sh, which is often Not What You Want -- but they are easier to work with in simple cases.

Upvotes: 1

When successful, the execve(2) syscall does not return (it can return only on failure). So does the execlp(3) wrapper.

You usually want to execve inside the child process. And fork(2) could fail. You usually should call fflush(3) before fork-ing . So code:

fflush(NULL);
pid_t p = fork();
if (p < 0) { perror("fork"); exit (EXIT_FAILURE); };
if (p == 0) { /* child process */
    execlp(RUN_EXE, RUN_EXE, SPEC_RUN.run_args[j], (char *)0);
    perror("execlp");
    fflush(NULL);
    _exit(EXIT_FAILURE);
}
/// continue in the parent process
printf("child process is %d\n", (int)p);

Don't forget to wait the child, e.g. using waitpid(2)

See also system(3), popen(3), daemon(3), posix_spawn and read Advanced Linux Programming (which has a nice chapter explaining these things).

Also, use strace(1) to understand how things work.

Upvotes: 1

Related Questions