Executing omxplayer, on a C program, via execve/l won't output video on non-X console on child process after fork()

StackOverflow https://stackoverflow.com/questions/17400954

Question

Hy.

I'm trying to execute omxplayer (http://elinux.org/Omxplayer) on the Raspberry Pi after a C fork() via the execve or execl functions so that I can save the PID for the video playing process (so System will not do the work). If I execute the program on a X console/terminal it works but if its via a standard terminal (without starting X) it will run but not output the video it to the screen if execve is called on the child process. By the way, executing the player via "omxplayer ..." commnad in the console will play the video and output to the screen. I'm a bit new to this kind of things so this is a situation I haven't been able to solve or find an answer to. Anyone here has an ideia on how to solve this or a direction to give for me to find a possible solution?

Note: The code is just a execve call wich I know is right because in X it works perfectly.

Was it helpful?

Solution

The execve() call supplies a new environment to the executed program. For the program to be able to access the X display, you need to retain certain environment variables -- DISPLAY at minimum. Have you inadvertently omitted DISPLAY from the new environment?

For OMXPlayer to work without X, it has to have access to the video device itself (/dev/video, in this case; see OMXPlayer builds page for details). It's usually configured so that all members of the video group are allowed to access it.

You can use popen("id -Gn", "r") in your program, to run the id -Gn command which lists the current group memberships. (Read the list as a string from the file handle, then close it using pclose().) If the list does not contain video, then the problem is that the privileges of the user that is running the original program do not include access to the video device. The fix is simple: adding video to the groups that user is a member of.


Here is an example program, run.c, to illustrate basic use of execvp():

#include <unistd.h>

/* For the example main(): */
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

/* Try executing a command in a child process.
 * Returns the PID of the child process,
 * but does not tell whether the execution was
 * successful or not.
 * Returns (pid_t)-1 with errno set if fork() fails.
*/
pid_t run(char *const command[])
{
    pid_t   child;

    child = fork();
    if (child == (pid_t)-1)
        return (pid_t)-1;

    if (!child) {
        execvp(command[0], command);
        _exit(127);
    }

    return child;
}

int main(int argc, char *argv[])
{
    pid_t child, p;
    int   status;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s COMMAND [ ARGUMENTS .. ]\n", argv[0]);
        fprintf(stderr, "\n");
        return 1;
    }

    child = run(argv + 1);
    if (child == (pid_t)-1) {
        fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
        return 1;
    }

    fprintf(stderr, "(%s: PID %d)\n", argv[1], (int)child);
    fflush(stderr);

    do {
        p = waitpid(child, &status, 0);
        if (p == (pid_t)-1 && errno == EINTR)
            continue;
    } while (p != child && p != (pid_t)-1);
    if (p == (pid_t)-1) {
        fprintf(stderr, "(%s: %s.)\n", argv[1], strerror(errno));
        return 1;
    }

    if (WIFEXITED(status)) {
        if (WEXITSTATUS(status) == 127)
            fprintf(stderr, "(%s: Could not execute command.)\n", argv[1]);
        else
        if (WEXITSTATUS(status) == 0)
            fprintf(stderr, "(%s: Exited successfully.)\n", argv[1]);
        else
            fprintf(stderr, "(%s: Exited with error %d.)\n", argv[1], WEXITSTATUS(status));
    } else
    if (WIFSIGNALED(status))
        fprintf(stderr, "(%s: Killed by %s.)\n", argv[1], strsignal(WTERMSIG(status)));
    else
        fprintf(stderr, "(%s: Died from unknown causes.)\n", argv[1]);

    return status;
}

You can compile and test it using e.g.

gcc -W -Wall -O3 run.c -o run
./run date --utc

Note that the run() function does not attempt to check whether the command was actually executed or not; it just returns the child process PID, or (pid_t)-1 if fork() fails.

Many implementations, including GNU C library popen(), use the 127 exit status as an indication that the execution failed. That is, it is not returned by the command that should have been executed, but by the child process, because the command execution failed. The above run() does so too.


You can use a close-on-exec pipe between the parent and child processes in the run() function, to let the parent process know whether the child process successfully started the desired command or not, and if not, why not. The parent process can then also immediately reap the defunct child process. This leaves very little extra effort to the caller in case of errors, so I personally highly recommend this approach. Here is an example implementation:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>

/* Helper function: Close file descriptor, without modifying errno.
 * Returns 0 if successful, otherwise the errno reported by close().
*/
static int closefd(const int fd)
{
    int saved_errno, result;

    /* Invalid descriptor? */
    if (fd == -1)
        return EINVAL;

    /* Save errno. It's thread-local, so as long as we restore
     * it before returning, no-one will notice any change in it. */
    saved_errno = errno;

    /* Close descriptor, and save errno (or 0) in result. */
    do {
        result = close(fd);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        result = errno;
    else
        result = 0;

    /* Restore errno. Done. */
    errno = saved_errno;

    return result;
}

/* Helper function: Create a close-on-exec pipe.
 * Return 0 if success, errno otherwise.
*/
int close_on_exec_pipe(int fds[2])
{
    int result;

    result = pipe(fds);
    if (result == -1) {
        fds[0] = -1;
        fds[1] = -1;
        return errno;
    }

    do {

        do {
            result = fcntl(fds[0], F_SETFD, FD_CLOEXEC);
        } while (result == -1 && errno == EINTR);
        if (result == -1)
            break;

        do {
            result = fcntl(fds[1], F_SETFD, FD_CLOEXEC);
        } while (result == -1 && errno == EINTR);
        if (result == -1)
            break;

        /* Success. */
        return 0;

    } while (0);

    /* Failed. */
    closefd(fds[0]);
    closefd(fds[1]);
    fds[0] = -1;
    fds[1] = -1;

    return errno;
}

/* Run an external command in a child process.
 * command[0] is the path or name of the command,
 * and the array must be terminated with a NULL.
 *
 * If successful, this function will return the PID
 * of the child process. Otherwise, it will return
 * (pid_t)-1, with errno indicating the error.
*/
pid_t run(char *const command[])
{
    pid_t   child, p;
    int     commfd[2], errcode;

    /* Create a close-on-exec pipe between the parent and child. */
    if (close_on_exec_pipe(commfd))
        return (pid_t)-1;

    /* Fork the new child process. */
    child = fork();
    if (child == (pid_t)-1) {
        closefd(commfd[0]);
        closefd(commfd[1]);
        return (pid_t)-1;
    }

    if (!child) {
        /* Child process: */

        /* Close the read/parent end of the pipe. */
        closefd(commfd[0]);

        /* In case of C library bugs, prepare errno. */
        errno = EINVAL;

        /* Execute the desired command. */
        execvp(command[0], command);

        /* Failed. errno describes why. */
        errcode = errno;

        /* Send it to the parent via the pipe. */
        {
            const char       *p = (char *)&errcode;
            const char *const q = (char *)&errcode + sizeof errcode;
            ssize_t           n;

            while (p < q) {
                n = write(commfd[1], p, (size_t)(q - p));
                if (n > (ssize_t)0)
                    p += n;
                else
                if (n != (ssize_t)-1)
                    break;
                else
                if (errno != EINTR)
                    break;
            }
        }

        /* Close write/child end of the pipe. */
        closefd(commfd[1]);

        /* Exit with a failure (127). */
        _exit(127);
    }

    /* Parent: */

    /* Close the write/child end of the pipe. */
    closefd(commfd[1]);

    /* Try to read the execution error. */
    {
        char       *p = (char *)&errcode;
        char *const q = (char *)&errcode + sizeof errcode;
        ssize_t     n;

        errcode = 0;

        while (p < q) {
            n = read(commfd[0], p, (size_t)(q - p));
            if (n > (ssize_t)0)
                p += n;
            else
            if (n != (ssize_t)-1)
                break; /* n == 0 is pipe closed */
            else
            if (errno != EINTR)
                break;
        }

        /* Close the read/parent end of the pipe. */
        closefd(commfd[0]);

        /* Pipe closed (on exec), no data read? */
        if (n == (ssize_t)0 && p == (char *)&errcode) {
            /* Yes, success! */
            errno = 0;
            return child;
        }

        /* Execution failed.
         * If we didn't get the reason, use EINVAL. */
        if (!errcode || p != q)
            errcode = EINVAL;
    }

    /* Reap the child process. */
    do {
        p = waitpid(child, NULL, 0);
        if (p == (pid_t)-1) {
            if (errno == EINTR)
                continue;
            else
                break;
        }
    } while (p != child);

    /* Return with failure. */
    errno = errcode;
    return (pid_t)-1;
}

The only downside to this approach, in my opinion, is the extra two descriptors used in the parent process, albeit only temporarily. In almost all cases this is irrelevant, but if you have a server-type application that uses a lot of file descriptors, this is something you should be aware of.


The Phidgets library uses threads. The thread that executes the callbacks is different than the one that say, waits for the keypress in the RFID Phidgets example. One option would be to use posix_spawn() to execute the player (from a non-main thread).

However, in general, it is better to have the main thread monitor both the player using waitpid(child, &status, WNOHANG) to check if the player has exited, and to handle any new RFID events, launching the player as needed (killing an existing instance if new RFID), and even killing the player if the RFID is moved outside the reader range.

This requires a simple threaded event queue:

#define  _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>

/* RFID tag event types: tag read, tag lost.
*/
typedef enum {
    RFID_TAG_LOST = 0,
    RFID_TAG_READ
} rfid_event_type_t;

/* Structure describing all possible RFID tag events.
*/
typedef struct rfid_event_st  rfid_event_t;
struct rfid_event_st {
    struct rfid_event_st     *next;
    rfid_event_type_t         type;
    CPhidgetRFIDHandle        rfid;
    CPhidgetRFID_Protocol     protocol;
    void                     *userptr;
    char                      tag[];
};

static pthread_mutex_t  event_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t   event_wait = PTHREAD_COND_INITIALIZER;
static rfid_event_t    *event_queue = NULL;

/* Add event to event queue.
*/
static int add_event(const CPhidgetRFIDHandle rfid,
                     const CPhidgetRFID_Protocol protocol,
                     const rfid_event_type_t type,
                     const char *const tag,
                     void *const userptr)
{
    const size_t  taglen = (tag) ? strlen(tag) : 0;
    rfid_event_t *ev;

    /* Allocate memory for a new event. */
    ev = malloc(sizeof (rfid_event_t) + taglen + 1);
    if (!ev)
        return errno = ENOMEM;

    /* Fill in the fields. */
    ev->next = NULL;
    ev->type = type;
    ev->rfid = rfid;
    ev->protocol = protocol;
    ev->userptr = userptr;
    if (taglen > 0)
        memcpy(ev->tag, tag, taglen);
    ev->tag[taglen] = '\0';

    /* Lock event queue. */
    pthread_mutex_lock(&event_lock);

    /* Append to the event queue. */
    if (event_queue) {
        rfid_event_t *prev = event_queue;
        while (prev->next)
            prev = prev->next;
        prev->next = ev;
    } else
        event_queue = ev;

    /* Signal and unlock. */
    pthread_cond_signal(&event_wait);
    pthread_mutex_unlock(&event_lock);

    return 0;
}

/* Get next event, waiting at most 'maxwait' seconds.
*/
static rfid_event_t *get_event(const long maxwait)
{
    struct timespec until;
    rfid_event_t   *ev;

    pthread_mutex_lock(&event_lock);

    /* Event already in the queue? */
    if (event_queue) {
        ev = event_queue;
        event_queue = ev->next;
        ev->next = NULL;
        pthread_mutex_unlock(&event_lock);
        return ev;
    }

    /* No waiting requested? */
    if (maxwait <= 0L) {
        pthread_mutex_unlock(&event_lock);
        return NULL;
    }

    /* Get current wall clock time, */
    clock_gettime(CLOCK_REALTIME, &until);
    /* and add maxwait seconds. */
    until.tv_sec += maxwait;

    /* Wait for a signal. */
    pthread_cond_timedwait(&event_wait, &event_lock, &until);

    /* Event arrived in the queue? */
    if (event_queue) {
        ev = event_queue;
        event_queue = ev->next;
        ev->next = NULL;
        pthread_mutex_unlock(&event_lock);
        return ev;
    }

    /* No event; timed out. */
    pthread_mutex_unlock(&event_lock);
    return NULL;
}

As per the Phidgets RFID example, the tag and tag lost handlers are

int CCONV TagHandler(CPhidgetRFIDHandle RFID, void *usrptr, char *TagVal, CPhidgetRFID_Protocol proto)
{
    return add_event(RFID, proto, RFID_TAG_READ, TagVal, usrptr);
}

int CCONV TagLostHandler(CPhidgetRFIDHandle RFID, void *usrptr, char *TagVal, CPhidgetRFID_Protocol proto)
{
    return add_event(RFID, proto, RFID_TAG_LOST, TagVal, usrptr);
}

Instead of waiting for a keypress after everything has been set up, you create a loop, something like

    pid_t         child = (pid_t)-1; /* Not running */
    pid_t         p;
    rfid_event_t *event;

    /* Infinite loop */
    while (1) {

        /* Do we have a player child process? */
        if (child != (pid_t)-1) {

            /* Yes. Has it exited yet? */
            p = waitpid(child, NULL, WNOHANG);
            if (p == child) {
                /* Yes. No more player. */
                child == (pid_t)-1;
            }
        }

        /* Check for a new event.
         * If we have a child, only wait one second only
         * for the event; otherwise, wait up to 30 secs.
        */
        if (child == (pid_t)-1)
            event = get_event(30L);
        else
            event = get_event(1L);

        /* If no event yet, start at the beginning of the loop. */
        if (!event)
            continue;

        /*
         * TODO: Handle the event.
         *       You can stop the existing player via e.g.
         *       if (child != (pid_t)-1)
         *           kill(child, SIGKILL);
         *       and then start a new one.
        */

        /* Discard the event. It's dynamically allocated. */
        free(event);
    }

If you start the player, the above loop detects it is not playing within a second. If there is no player running, then it's okay for the loop to wait for an RFID signal for as long as it wants -- I used 30 seconds.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top