Files
libite/src/runbg.c
T
Joachim Wiberg 92bb0ca399 runbg: new helper function inspired by the Infix project
The runbg() function is a delayed execvp() that has double-forked to the
background (to not create zombies).  It is useful for running commands
in the background for APIs that need to return a reply, e.g. a NETCONF
RPC to reboot the system.

Compared to the original in Infix, this version double forks to reparent
itself to PID 1 (init), closes all open files, including stdio.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 11:00:09 +02:00

94 lines
2.4 KiB
C

/* Run a command in the background: fork() + usleep() + execvp()
*
* Copyright (c) 2023 Joachim Wiberg <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @file runbg.c
* @author Joachim Wiberg
* @date 2023
* @copyright ISC License
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
/**
* Run command in the background
* @param cmd NULL terminated list of command and optional arguments
* @param delay Microseconds after fork() to wait before calling cmd
*
* This function forks to run the given command @p cmd in the background.
* To ensure there are no lingering zombies the function actuall forks
* twice, and also calls setsid(), before it calls usleep() with the @p
* delay argument. The command @p cmd is handed over to execvp().
*
* Since it runs in the background it is not possible to get the return
* code of the command.
*
* @returns on successful (first) fork(), this function returns POSIX
* OK(0), otherwise -1 and @a errno is set to indicate the error.
*/
int runbg(char *const cmd[], int delay)
{
int pid = fork();
int rc;
if (!pid) {
int fd, maxfd;
if (setsid() == -1)
_exit(errno);
/* reparent to init */
pid = fork();
if (pid == -1)
_exit(errno);
if (pid > 0)
_exit(0);
maxfd = sysconf(_SC_OPEN_MAX);
if (maxfd == -1)
maxfd = 8192;
for (fd = 0; fd < maxfd; fd++)
close(fd);
usleep(delay);
_exit(execvp(cmd[0], cmd));
}
if (pid == -1)
return -1;
if (waitpid(pid, &rc, 0) == -1)
return -1;
if (WIFEXITED(rc)) {
errno = WEXITSTATUS(rc);
if (errno)
rc = -1;
else
rc = 0;
} else if (WIFSIGNALED(rc)) {
errno = EINTR;
rc = -1;
} else
rc = -1;
return rc;
}