Detecting failure to find program when fork()-ing a'la popen - c

When I execute this on my system:
FILE* pipe = popen("something_that_doesnt_exist", "r");
printf("pipe: %p\n", pipe);
I get a valid pointer for the pipe, despite that program not existing. I'd like to write a popen that can detect that and return a NULL pointer indicating launch failure. However, I'm not sure how to achieve that while keeping the /bin/sh call for interpretation. Does anyone know how I could check the return status of a call like:
execl("/bin/sh", "sh", "-c", "something_that_doesnt_exist");

To do this, you need to use the low level facilities. You need to create an extra pipe, close-on-exec, that the child uses to write the error code when exec fails.
Since the pipe is close-on-exec, it will be closed by the kernel at the start of the execution of the new binary. (We do not actually know if it is running at that point; we only know that the exec did not fail. So, do not assume that a closed pipe means the command is already running. It only means it did not fail yet.)
The parent process closes the unnecessary pipe ends, and reads from the control pipe. If the read succeeds, the child process failed to execute the command, and the data read describes the error. If the pipe is closed (read returns 0), the command execution will start (there was no error barring its execution).
After that, we can continue reading from the pipe as usual. When the child process closes the pipe, we should use waitpid() to reap it.
Consider the following example program. It executes the command specified on the command line -- use sh -c 'command' if you want the same behaviour as system() and popen(). (That is, pathname == "sh" and argv == { "sh", "-c", "command", NULL }.)
It reads the output from the command character by character, counting them, until the child ends (by closing the pipe). After that, we reap the child process, and report the status.
If the command could not be executed, the reason is also reported. (Since non-executables are reported as ENOENT ("No such file or directory"), the exec_subproc() modifies that case to EACCESS ("Permission denied").)
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
int reap_subproc(pid_t pid)
{
int status;
pid_t p;
if (pid < 1) {
errno = EINVAL;
return -1;
}
do {
status = 0;
p = waitpid(pid, &status, 0);
} while (p == -1 && errno == EINTR);
if (p == -1)
return -1;
errno = 0;
return status;
}
FILE *exec_subproc(pid_t *pidptr, const char *pathname, char **argv)
{
char buffer[1];
int datafd[2], controlfd[2], result;
FILE *out;
ssize_t n;
pid_t pid, p;
if (pidptr)
*pidptr = (pid_t)0;
if (!pidptr || !pathname || !*pathname || !argv || !argv[0]) {
errno = EINVAL;
return NULL;
}
if (pipe(datafd) == -1)
return NULL;
if (pipe(controlfd) == -1) {
const int saved_errno = errno;
close(datafd[0]);
close(datafd[1]);
errno = saved_errno;
return NULL;
}
if (fcntl(datafd[0], F_SETFD, FD_CLOEXEC) == -1 ||
fcntl(controlfd[1], F_SETFD, FD_CLOEXEC) == -1) {
const int saved_errno = errno;
close(datafd[0]);
close(datafd[1]);
close(controlfd[0]);
close(controlfd[1]);
errno = saved_errno;
return NULL;
}
pid = fork();
if (pid == (pid_t)-1) {
const int saved_errno = errno;
close(datafd[0]);
close(datafd[1]);
close(controlfd[0]);
close(controlfd[1]);
errno = saved_errno;
return NULL;
}
if (!pid) {
/* Child process. */
close(datafd[0]);
close(controlfd[0]);
if (datafd[1] != STDOUT_FILENO) {
do {
result = dup2(datafd[1], STDOUT_FILENO);
} while (result == -1 && errno == EINTR);
if (result == -1) {
buffer[0] = errno;
close(datafd[1]);
do {
n = write(controlfd[1], buffer, 1);
} while (n == -1 && errno == EINTR);
exit(127);
}
close(datafd[1]);
}
if (pathname[0] == '/')
execv(pathname, argv);
else
execvp(pathname, argv);
buffer[0] = errno;
close(datafd[1]);
/* In case it exists, we return EACCES instead of ENOENT. */
if (buffer[0] == ENOENT)
if (access(pathname, R_OK) == 0)
buffer[0] = EACCES;
do {
n = write(controlfd[1], buffer, 1);
} while (n == -1 && errno == EINTR);
exit(127);
}
*pidptr = pid;
close(datafd[1]);
close(controlfd[1]);
do {
n = read(controlfd[0], buffer, 1);
} while (n == -1 && errno == EINTR);
if (n == -1) {
close(datafd[0]);
close(controlfd[0]);
kill(pid, SIGKILL);
do {
p = waitpid(pid, NULL, 0);
} while (p == (pid_t)-1 && errno == EINTR);
errno = EIO;
return NULL;
} else
if (n == 1) {
close(datafd[0]);
close(controlfd[0]);
do {
p = waitpid(pid, NULL, 0);
} while (p == (pid_t)-1 && errno == EINTR);
errno = (int)buffer[0];
return NULL;
}
close(controlfd[0]);
out = fdopen(datafd[0], "r");
if (!out) {
close(datafd[0]);
kill(pid, SIGKILL);
do {
p = waitpid(pid, NULL, 0);
} while (p == (pid_t)-1 && errno == EINTR);
errno = EIO;
return NULL;
}
errno = 0;
return out;
}
int main(int argc, char *argv[])
{
FILE *cmd;
pid_t pid;
int c;
unsigned long bytes = 0UL;
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 EXIT_SUCCESS;
}
cmd = exec_subproc(&pid, argv[1], argv + 1);
if (!cmd) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
while ((c = getc(cmd)) != EOF) {
bytes++;
putchar(c);
}
fflush(stdout);
fclose(cmd);
c = reap_subproc(pid);
if (errno) {
fprintf(stderr, "%s: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
if (WIFEXITED(c) && !WEXITSTATUS(c)) {
fprintf(stderr, "%s: %lu bytes; success.\n", argv[1], bytes);
return 0;
}
if (WIFEXITED(c)) {
fprintf(stderr, "%s: %lu bytes; failed (exit status %d)\n", argv[1], bytes, WEXITSTATUS(c));
return WEXITSTATUS(c);
}
if (WIFSIGNALED(c)) {
fprintf(stderr, "%s: %lu bytes; killed by signal %d (%s)\n", argv[1], bytes, WTERMSIG(c), strsignal(WTERMSIG(c)));
return 128 + WTERMSIG(c);
}
fprintf(stderr, "%s: %lu bytes; child lost.\n", argv[1], bytes);
return EXIT_FAILURE;
}
Compile using e.g.
gcc -Wall -Wextra -O2 example.c -o example
and run e.g.
./example date
./example ./example date -u
./example /bin/sh -c 'date | tr A-Za-z a-zA-Z'

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
int fd = open("/dev/null", O_WRONLY);
/* backup */
int f1 = dup(1);
int f2 = dup(2);
dup2(fd, 1);
dup2(fd, 2);
int res = system("something_that_doesnt_exist");
/* recovery */
dup2(f1, 1);
dup2(f2, 2);
FILE* pipe = popen("something_that_doesnt_exist", "r");
if (res != 0 )
{
pipe = NULL;
}
}
redirect stdout and stderr in order to avoid unexcepted output.

If popen() will successfully execute a command or not, you can not tell. This is because the process calls fork() first, so the pipe and child process will always be created. But if the execv() call following the fork() fails, the child will die and the parent will not be able to tell if this is caused by execv() failure or the command you wanted just completed without any output.

If your process has no other child process, maybe you can use waitpid
int stat;
File* fp = popen("something_that_doesnt_exist", "r");
waitpid(-1, &stat, 0);
then you can determine the value of stat, if popen succeed, stat = 0. Not very sure about this way, need someone to confirm

Related

How to enable SIGINT signal for system() call in a pthread in C?

The below manual on system() says it blocks SIGINT and SIGQUIT signal for any binary program run through system() call.
https://man7.org/linux/man-pages/man3/system.3.html#:~:text=The%20system()%20library%20function,the%20command%20has%20been%20completed.
Psedo Code:
thread_1()
{
...
system("binary application");
}
main() {
...
pid = pthread_create(thread_1);
pthread_cancel(pid);
}
pthread_cancel issues SIGINT to thread 1 which kill the thread 1, but not the binary application.
How to make the "binary application" receive the SIGINT signal?
The below manual on system() says it blocks SIGINT and SIGQUIT signal for any binary program run through system() call.
No, it doesn't. It says this:
During execution of the command, SIGCHLD will be blocked, and SIGINT
and SIGQUIT will be ignored, in the process that calls system().
(These signals will be handled according to their defaults inside the
child process that executes command.)
(Emphasis added.) It is the process that calls system() whose signal-handling is affected, not the (separate) process in which the command runs. Moreover, this is purposeful, and you should not lightly attempt to interfere with it.
pthread_cancel issues SIGINT to thread 1
Doubtful. POSIX does not document how thread cancellation is implemented, but sending SIGINT is an unlikely choice, as its default behavior is to terminate the process. The Linux manual for pthread_cancel() does say that it is implemented via signals, but it also says that the first realtime signal is used if realtime signals are available, and otherwise SIGUSR2 is used. Neither of those is documented as being blocked while system() is running.
which kill the thread 1, but not the binary application.
Yes, forcible termination of the thread that calls system() while that function is running would not be expected to kill the separate process in which the specified command is executing. That has nothing to do with signals being blocked. If you want to terminate the process in which the command is running before it completes then you're probably looking for the kill() function. For that purpose, you'll need to find the PID of the child you want to kill, which might turn out to require considerable effort and trouble.
Overall, if you don't like the semantics of system() then you are probably better off rolling your own version, based on fork() and exec*(), so that you can have the level of control you want.
The man page also says:
(These signals will be handled according to their defaults inside the
child process that executes command.)
So to answer your question "How to make the "binary application" receive the SIGINT signal?"; it's ok, it will anyway. The blocking happens in the thread that calls the command, not the command process.
EDIT:
To answer #Hanu's comment below, use the wait() set of system calls: you can get the pid of the command inside the system() call from there, and you can safely close your child thread or take action depending on the result of wait(). But I don't know what resources you would need to clean if the process has terminated: Linux will free all the resources associated with the process called by system: there is a distinction between how the OS cleans pthreads when they finish and process resources - see this SO answer .
Instead of using system(), you fork() a child process, and execl("/bin/sh", "-c", "system-command-goes-here", (char *)0); in that child process.
When you call fork(), it returns twice: once in the parent with a positive value – the process identifier, "pid", of the child process; and once in the child with a zero value.
To send the child process an INT signal, just use kill(pid, SIGINT);.
You can use pthread_cleanup_push(kill_int, (intptr_t)pid) in the thread to kill the child process if the thread exits (is canceled or killed), with
static void kill_int(void *pidptr)
{
const pid_t pid = (intptr_t)pidptr;
pid_t p;
if (pid > 1)
kill(pid, SIGINT);
}
Here are some Public Domain helper functions you might find useful. run.h:
/* SPDX-License-Identifier: CC0-1.0 */
#ifndef RUN_H
#define RUN_H
#include <unistd.h>
#include <sys/types.h>
/* Execute command. First parameter is the binary to execute (path or name),
and the second parameter is the argument array. First element in the
argument array is command name, and the last element must be (char *)0.
Returns the child process ID if successful, -1 with errno set if error.
*/
pid_t run(const char *, const char *[]);
/* Execute shell command. The parameter is the shell command,
otherwise this behaves like run().
*/
pid_t run_sh(const char *);
/* Check if child process has exited.
Returns the PID of the child if it has returned,
with the status (use WIFEXITED(), WEXITSTATUS(), WIFSIGNALED(), WTERMSIG())
stored at the location specified by the int pointer, if not NULL.
Returns 0 if the child hasn't exited yet, or
-1 if an error occurred (with errno set).
try_reap() tries to reap a specific child,
try_reap_any() checks if any child processes have exited, and
try_reap_group() checks if a child belonging to a process group has exited.
*/
pid_t try_reap(pid_t, int *);
pid_t try_reap_any(int *);
pid_t try_reap_group(pid_t, int *);
/* Wait until a specific child exits.
Returns the child PID with status set if not NULL,
or -1 if an error occurs.
*/
pid_t reap(pid_t, int *);
/* Wait until all child processes have exited.
If non-NULL, the callback is called for each reaped child.
If the callback returns nonzero, the function returns immediately
without waiting for other children.
Returns 0 if success, callback return value if it returns nonzero,
or -1 with errno set if an error occurs.
*/
pid_t reap_all(int (*report)(pid_t, int));
pid_t reap_group(pid_t, int (*report)(pid_t, int));
#endif /* RUN_H */
Implementation, run.c:
/* SPDX-License-Identifier: CC0-1.0 */
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#ifndef RUN_FAILURE_EXIT_STATUS
#define RUN_FAILURE_EXIT_STATUS 69
#endif
static inline int has_slash(const char *cmd)
{
while (*cmd)
if (*(cmd++) == '/')
return 1;
return 0;
}
pid_t run(const char *cmd, const char *args[])
{
int ctrl[2] = { -1, -1 };
int cause;
pid_t child, p;
/* Sanity checks. */
if (!cmd || !*cmd || !args) {
errno = EINVAL;
return -1;
}
/* Create a close-on-exec control pipe. */
if (pipe2(ctrl, O_CLOEXEC) == -1) {
/* Failed; errno already set. */
return -1;
}
/* Fork the child process. */
child = fork();
if (child == (pid_t)-1) {
/* Failed; errno set. */
cause = errno;
close(ctrl[0]);
close(ctrl[1]);
errno = cause;
return -1;
} else
if (!child) {
/* This is the child process. */
/* Close parent end of control pipe. */
close(ctrl[0]);
/* Try and execute the command. */
if (has_slash(cmd))
execv(cmd, (char *const *)args);
else
execvp(cmd, (char *const *)args);
/* Failed. Try and report cause to parent. */
cause = errno;
{
const char *ptr = (const char *)(&cause);
const char *const end = (const char *)(&cause) + sizeof cause;
ssize_t n;
while (ptr < end) {
n = write(ctrl[1], ptr, (size_t)(end - ptr));
if (n > 0) {
ptr += n;
} else
if (n != -1 || errno != EINTR)
break;
}
}
exit(RUN_FAILURE_EXIT_STATUS);
}
/* This is the parent process. */
/* Close child end of control pipe. */
close(ctrl[1]);
/* Try reading from the control pipe. */
{
char *ptr = (char *)(&cause) + sizeof cause;
char *const end = (char *)(&cause) + sizeof cause;
int err = 0;
ssize_t n;
while (ptr < end) {
n = read(ctrl[0], ptr, (size_t)(end - ptr));
if (n > 0) {
ptr += n;
} else
if (!n) {
break;
} else
if (n != -1) {
err = EIO;
break;
} else
if (errno != EINTR) {
err = errno;
break;
}
}
/* If we failed, and didn't get a full cause,
use the error from the read. */
if (err && ptr != end)
cause = err;
}
/* Close parent end of the control pipe. */
close(ctrl[0]);
/* If we failed, reap the child and exit. */
if (cause) {
do {
p = waitpid(child, NULL, 0);
} while (p == -1 && errno == EINTR);
errno = cause;
return -1;
}
/* Everything looks okay! */
return child;
}
pid_t run_shell(const char *command)
{
const char *args[4] = { "sh", "-c", command, (char *)0 };
return run("/bin/sh", args);
}
pid_t try_reap(const pid_t pid, int *status)
{
int temp_status;
pid_t p;
if (pid <= 1) {
errno = EINVAL;
return -1;
}
do {
p = waitpid(pid, &temp_status, WNOHANG);
} while (p == -1 && errno == EINTR);
if (status && p > 0)
*status = temp_status;
return p;
}
pid_t try_reap_any(int *status)
{
int temp_status;
pid_t p;
do {
p = waitpid(-1, &temp_status, WNOHANG);
} while (p == -1 && errno == EINTR);
if (status && p > 0)
*status = temp_status;
return p;
}
pid_t try_reap_group(pid_t pgid, int *status)
{
int temp_status;
pid_t p;
if (pgid <= 1) {
errno = EINVAL;
return -1;
}
do {
p = waitpid(-1, &temp_status, WNOHANG);
} while (p == -1 && errno == EINTR);
if (status && p > 0)
*status = temp_status;
return p;
}
pid_t reap(const pid_t pid, int *status)
{
int temp_status;
pid_t p;
if (pid <= 1) {
errno = EINVAL;
return -1;
}
do {
p = waitpid(pid, &temp_status, 0);
} while (p == -1 && errno == EINTR);
if (status && p > 0)
*status = temp_status;
return p;
}
int reap_all(int (*report)(pid_t pid, int status))
{
int status, retval;
pid_t p;
while (1) {
p = waitpid(-1, &status, 0);
if (p == -1) {
if (errno == ECHILD)
return 0;
else
if (errno != EINTR)
return -1;
} else
if (p > 0 && report) {
retval = report(p, status);
if (retval)
return retval;
}
}
}
int reap_group(pid_t pgid, int (*report)(pid_t pid, int status))
{
int status, retval;
pid_t p;
if (pgid <= 1) {
errno = EINVAL;
return -1;
}
while (1) {
p = waitpid(-pgid, &status, 0);
if (p == -1) {
if (errno == ECHILD)
return 0;
else
if (errno != EINTR)
return -1;
} else
if (p > 0 && report) {
retval = report(p, status);
if (retval)
return retval;
}
}
}
and here is an example of use, example.c, which runs the binary specified by command-line parameters:
/* SPDX-License-Identifier: CC0-1.0 */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "run.h"
int main(int argc, char *argv[])
{
pid_t child, p;
int status;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *argv0 = (argc > 0 && argv && argv[0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
fprintf(stderr, " %s COMMAND [ ARGS ... ]\n", argv0);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}
child = run(argv[1], (const char **)(argv + 1));
if (child == -1) {
fprintf(stderr, "%s: Cannot execute: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "%s: Started process %d.\n", argv[1], (int)child);
p = reap(child, &status);
if (p == -1) {
fprintf(stderr, "%s: Cannot reap child: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
} else
if (p != child) {
fprintf(stderr, "%s: Internal bug: reaped the wrong child process (%d, expected %d).\n", argv[1], (int)p, (int)child);
return EXIT_FAILURE;
}
if (WIFEXITED(status)) {
if (WEXITSTATUS(status) == EXIT_SUCCESS) {
fprintf(stderr, "%s: Exited successfully.\n", argv[1]);
return EXIT_SUCCESS;
} else {
fprintf(stderr, "%s: Exited with status %d.\n", argv[1], WEXITSTATUS(status));
return WEXITSTATUS(status);
}
} else
if (WIFSIGNALED(status)) {
fprintf(stderr, "%s: Died from signal %d.\n", argv[1], WTERMSIG(status));
return EXIT_FAILURE;
} else {
fprintf(stderr, "%s: Child process vanished!\n", argv[1]);
return EXIT_FAILURE;
}
}
To tie all these together, Makefile:
CC := gcc
CFLAGS := -Wall -O2
LDFLAGS :=
PROGS := example
all: $(PROGS)
clean:
rm -f *.o $(PROGS)
%.o:%.c
$(CC) $(CFLAGS) -c $^
example: run.o example.o
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $#
Note that this forum eats Tabs, so you need to run sed -e 's|^ *|\t|' -i Makefile to fix the indentation. To compile, just run make.
To run, run e.g
./example date
The parent process examines how and why the child process exited, and will report both process identifier (pid) and the exit status.

How to duplicate a child descriptor onto STDOUT_FILENO and STDERR_FILENO

I am trying to implement the GNU popen library but having issues with dup2. I want to duplicate the child pipe to both STDOUT_FILENO and STDERR_FILENO.
I am little confuse here with the dup2, because it says on the man page that dup2 closes the old descriptor, how how can use duplicate pipe onto STDERR_FILENO after doing this dup2(pipe_list[child_fd], child_fd). Because from what i do understand upon a call to dup2 the oldfp will be closed and this dup2(pipe_list[child_fd], STDERR_FILENO) will not work because pipe_list[child_fd] was already closed. Should i use dup and after that close fd or there is a way to achieve this with dup2?
#define tpsr(a,b) (*type == 'r' ? (b) : (a))
#define tpsw(a,b) (*type == 'w' ? (b) : (a))
static FILE *fp_stream = NULL;
static pid_t popen_pid = -1;
static const char * const shell_path = "/bin/sh";
FILE *mypopen(const char *command, const char *type) {
int pipe_list[2];
int parent_fd, child_fd;
if (type == NULL || type[1] != 0) {
errno = EINVAL;
return NULL;
}
if (type[0] != 'w' && type[0] != 'r') {
errno = EINVAL;
return NULL;
}
if (pipe(pipe_list) == -1) {
return NULL; //errno will be set
}
child_fd = tpsr(STDIN_FILENO,STDOUT_FILENO);
parent_fd = tpsw(STDIN_FILENO,STDOUT_FILENO);
/*The above (tpsr and tpsw) are the same as this
if type[0] == 'r'
child_fd = STDOUT_FILENO; //1 child will be doing the writing
parent_fd = STDIN_FILENO; //0 parent read
if type[0] == 'w'
child_fd = STDIN_FILENO; //0 child doing the reading
parent_fd = STDOUT_FILENO;//1 parent do the writing
}*/
if ((popen_pid = fork()) == -1) {
close(pipe_list[0]);
close(pipe_list[1]);
return NULL;
}
if (popen_pid == 0) {
// we got a child here
if (pipe_list[child_fd] != child_fd) {
if (dup2(pipe_list[child_fd], child_fd) == -1) {
(void) close(pipe_list[child_fd]);
_exit(EXIT_FAILURE);
}
//is this the right way after the oldfd is closed by dup2
if (child_fd == STDOUT_FILENO) {
if (dup2(pipe_list[child_fd], STDERR_FILENO) == -1){
(void) close(pipe_list[child_fd]);
_exit(EXIT_FAILURE);
}
}
(void) close(pipe_list[child_fd]);
}
(void) pipe_list[parent_fd];
(void) execl(shell_path, "sh", "-c", command, (char *) NULL);
_exit(EXIT_FAILURE); //exit(127) required by man page
} else {
(void) close(pipe_list[child_fd]);
if ((fp_stream = fdopen(pipe_list[parent_fd], type)) == NULL) {
(void) close(pipe_list[parent_fd]);
return NULL;
}
}
return fp_stream;
}
When you call dup2(fd1, fd2):
If fd1 is not an open file descriptor, the function will return -1 and set errno to EBADF. fd2 is not closed.
If fd2 is outside the allowed range, or if it is not open but the process already has the maximum number of open file descriptors (RLIMIT_NOFILE), the function will return -1 and set errno to EBADF.
If any other problem occurs, the function will return -1 with errno set to some error code. It is not specified whether fd2 is untouched or closed in this case.
The rest of the cases below assume the operation is successful.
If fd1 == fd2, the function will return fd1 (which is the same as d2).
If fd2 is not an open descriptor, then fd1 is duplicated to it. The two descriptors then refer to the same file description (the stuff like file position that the kernel keeps).
If fd2 is an open file descriptor, it gets closed when fd1 is duplicated to it. The closing is silent, meaning that any error due to the closing is silently ignored.
The main point is, that only fd2 may be closed, and only if fd2 != fd1 and if fd2 was previously open. In no case is fd1 closed.
I cannot really follow your logic with regards to your code. In particular, using the parent_fd and client_fd as indexes to pipe_list[] seems suspect to me. Furthermore, the function should return both a file handle, as well as the child process PID.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
FILE *child_pipe;
pid_t child_pid;
int exit_status;
} cmd;
/* open() analog, except moves the descriptor to the
number specified. You cannot use O_CREAT flag.
Returns 0 if success, -1 if an error occurred,
with reason in errno. */
static int open_fd(const int fd, const char *path, const int flags)
{
int tempfd;
if (fd == -1 || !path || !*path || (flags & O_CREAT)) {
errno = EINVAL;
return -1;
}
tempfd = open(path, flags);
if (tempfd == -1)
return -1;
if (tempfd != fd) {
if (dup2(tempfd, fd) == -1) {
const int failure = errno;
close(tempfd);
errno = failure;
return -1;
}
if (close(tempfd)) {
const int failure = errno;
close(fd);
errno = failure;
return -1;
}
}
return 0;
}
/* pipe[] analog, except ensures neither endpoint
matches STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO.
*/
static int create_pipe(int fd[2])
{
/* I like to initialize return parameters.. */
fd[0] = -1;
fd[1] = -1;
if (pipe(fd) == -1)
return -1;
else {
const int close_stdin = (fd[0] == STDIN_FILENO) || (fd[1] == STDIN_FILENO);
const int close_stdout = (fd[0] == STDOUT_FILENO) || (fd[1] == STDOUT_FILENO);
const int close_stderr = (fd[0] == STDERR_FILENO) || (fd[1] == STDERR_FILENO);
int failure = 0;
do {
while (fd[0] == STDIN_FILENO ||
fd[0] == STDOUT_FILENO ||
fd[0] == STDERR_FILENO) {
fd[0] = dup(fd[0]);
if (fd[0] == -1) {
failure = -1;
break;
}
}
if (failure)
break;
while (fd[1] == STDIN_FILENO ||
fd[1] == STDOUT_FILENO ||
fd[1] == STDERR_FILENO) {
fd[1] = dup(fd[1]);
if (fd[1] == -1) {
failure = -1;
break;
}
}
if (failure)
break;
if (close_stdin)
close(STDIN_FILENO);
if (close_stdout)
close(STDOUT_FILENO);
if (close_stderr)
close(STDERR_FILENO);
return 0;
} while (0);
/* Whoops, failed: cleanup time. */
failure = errno;
if (fd[0] != STDIN_FILENO &&
fd[0] != STDOUT_FILENO &&
fd[0] != STDERR_FILENO)
close(fd[0]);
if (fd[1] != STDIN_FILENO &&
fd[1] != STDOUT_FILENO &&
fd[1] != STDERR_FILENO)
close(fd[1]);
if (close_stdin)
close(STDIN_FILENO);
if (close_stdout)
close(STDOUT_FILENO);
if (close_stderr)
close(STDERR_FILENO);
errno = failure;
return -1;
}
}
#define CMD_PASS 0
#define CMD_READ 1
#define CMD_DISCARD 2
int cmd_read(cmd *pipe,
const char *path,
char *args[],
const int stdout_mode,
const int stderr_mode)
{
int pipefd[2], controlfd[2], cause;
FILE *handle;
pid_t child, p;
/* If pipe is not NULL, initialize it. */
if (pipe) {
pipe->child_pipe = NULL;
pipe->child_pid = 0;
pipe->exit_status = 0;
}
/* Verify the parameters make sense. */
if (!path || !args || !pipe ||
stdout_mode < 0 || stdout_mode > 2 ||
stderr_mode < 0 || stderr_mode > 2) {
errno = EINVAL;
return -1;
}
/* Do we need the pipe? */
if (stdout_mode == CMD_READ || stderr_mode == CMD_READ) {
if (create_pipe(pipefd) == -1)
return -1;
} else {
pipefd[0] = -1;
pipefd[1] = -1;
}
/* We use a control pipe to detect exec errors. */
if (create_pipe(controlfd) == -1) {
cause = errno;
if (pipefd[0] != -1)
close(pipefd[0]);
if (pipefd[1] != -1)
close(pipefd[1]);
errno = cause;
return -1;
}
/* Parent reads from the control pipe,
and the child writes to it, but only
if exec fails. We mark the write end
close-on-exec, so the parent notices
if the exec is successful. */
fcntl(controlfd[1], F_SETFD, O_CLOEXEC);
/* Fork the child process. */
child = fork();
if (child == (pid_t)-1) {
cause = errno;
close(controlfd[0]);
close(controlfd[1]);
if (pipefd[0] != -1)
close(pipefd[0]);
if (pipefd[1] != -1)
close(pipefd[1]);
errno = cause;
return -1;
}
if (!child) {
/* This is the child process. */
close(controlfd[0]);
if (pipefd[0] != -1)
close(pipefd[0]);
cause = 0;
do {
if (stdout_mode == CMD_READ) {
if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
cause = -1;
break;
}
} else
if (stdout_mode == CMD_DISCARD) {
if (open_fd(STDOUT_FILENO, "/dev/null", O_WRONLY) == -1) {
cause = -1;
break;
}
}
if (cause)
break;
if (stderr_mode == CMD_READ) {
if (dup2(pipefd[1], STDERR_FILENO) == -1) {
cause = -1;
break;
}
} else
if (stderr_mode == CMD_DISCARD) {
if (open_fd(STDERR_FILENO, "/dev/null", O_WRONLY) == -1) {
cause = -1;
break;
}
}
if (cause)
break;
if (pipefd[1] != -1)
close(pipefd[1]);
if (path[0] == '/')
execv(path, (char *const *)args);
else
execvp(path, (char *const *)args);
/* Failed. */
} while (0);
/* Tell parent, why. */
cause = errno;
/* To silence the warn_unused_result warning: */
if (write(controlfd[1], &cause, sizeof cause))
;
close(controlfd[1]);
exit(127);
}
/* Parent process. */
close(controlfd[1]);
if (pipefd[1] != -1)
close(pipefd[1]);
do {
ssize_t n;
/* Read from the pipe to see if exec failed. */
n = read(controlfd[0], &cause, sizeof cause);
if (n == (ssize_t)sizeof cause)
break;
if (n != 0) {
cause = EIO;
kill(child, SIGKILL);
break;
}
close(controlfd[0]);
if (pipefd[0] != -1) {
handle = fdopen(pipefd[0], "r");
if (!handle) {
cause = errno;
kill(child, SIGKILL);
break;
}
} else
handle = NULL;
/* Success! */
pipe->child_pipe = handle;
pipe->child_pid = child;
return 0;
} while (0);
/* Failed; reason is in cause. */
if (pipefd[0] != -1)
close(pipefd[0]);
/* Reap child. */
while (1) {
p = waitpid(child, NULL, 0);
if ((p == child) || (p == (pid_t)-1 && errno != EINTR))
break;
}
errno = cause;
return -1;
}
int cmd_wait(cmd *pipe)
{
pid_t p;
if (!pipe || pipe->child_pid == -1)
return errno = EINVAL;
while (1) {
p = waitpid(pipe->child_pid, &(pipe->exit_status), 0);
if (p == pipe->child_pid) {
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
return 0;
} else
if (p != -1) {
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
errno = EIO;
return -1;
} else
if (errno != EINTR) {
const int cause = errno;
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
errno = cause;
return -1;
}
}
}
int main(int argc, char *argv[])
{
off_t total = 0;
char *line = NULL;
size_t size = 0;
ssize_t len;
int stdout_mode, stderr_mode;
cmd run;
if (argc < 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("\n");
printf("Usage:\n");
printf(" %s [ -h | --help ]\n", argv[0]);
printf(" %s MODE COMMAND [ ARGS ... ]\n", argv[0]);
printf("Where MODE may contain:\n");
printf(" o to retain output,\n");
printf(" O to discard/hide output,\n");
printf(" e to retain errors, and\n");
printf(" E to discard/hide errors.\n");
printf("All other characters are ignored.\n");
printf("If there is no 'o' or 'O' in MODE, then output\n");
printf("is passed through to standard output; similarly,\n");
printf("if there is no 'e' or 'E' in MODE, then errors\n");
printf("are passed through to standard error.\n");
printf("\n");
return EXIT_SUCCESS;
}
if (strchr(argv[1], 'o'))
stdout_mode = CMD_READ;
else
if (strchr(argv[1], 'O'))
stdout_mode = CMD_DISCARD;
else
stdout_mode = CMD_PASS;
if (strchr(argv[1], 'e'))
stderr_mode = CMD_READ;
else
if (strchr(argv[1], 'E'))
stderr_mode = CMD_DISCARD;
else
stderr_mode = CMD_PASS;
if (cmd_read(&run, argv[2], argv + 2, stdout_mode, stderr_mode) == -1) {
fprintf(stderr, "%s: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
if (run.child_pipe) {
while (1) {
len = getline(&line, &size, run.child_pipe);
if (len == -1)
break;
total += (off_t)len;
#ifdef PRINT_PIPE_CONTENTS
if (len > 0)
fwrite(line, (size_t)len, 1, stdout);
#endif
}
if (ferror(run.child_pipe) || !feof(run.child_pipe)) {
fprintf(stderr, "%s: Error reading from pipe.\n", argv[2]);
return EXIT_FAILURE;
}
}
if (cmd_wait(&run) == -1) {
fprintf(stderr, "%s: Lost child process: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "Read %llu bytes from child process.\n", (unsigned long long)total);
if (WIFEXITED(run.exit_status))
fprintf(stderr, "Child exited with status %d.\n", WEXITSTATUS(run.exit_status));
else
if (WIFSIGNALED(run.exit_status))
fprintf(stderr, "Child terminated due to signal %d.\n", WTERMSIG(run.exit_status));
else
fprintf(stderr, "Child lost.\n");
return EXIT_SUCCESS;
}
If you save the above as pipe_example.c, you can compile it using e.g.
gcc -Wall -O2 pipe_example.c -o pipe_ex
and test it by running e.g.
./pipe_ex oe sh -c 'printf "OUT\n"; printf "ERR\n" >&2; exit 5'
./pipe_ex Oe sh -c 'printf "OUT\n"; printf "ERR\n" >&2;'
./pipe_ex -E sh -c 'printf "OUT\n"; printf "ERR\n" >&2; kill -TERM $$'
The above code is, as you can obviously see, quite complicated -- and overall, it is just a popen() analog, but with fewer features.
However, it does everything quite carefully (except perhaps passing the reason the exec failed from the child to the parent -- there I am simply assuming the write succeeds). As such, it might be useful as an example of how many details (and at least one way of dealing with such details) there are in practical multiprocess programs. And, of course, how useful standard POSIX.1 popen() is.
A bit more detailed example with stdout and stderr at the same time.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
int fdout[2]; /* pipe fd array for the stdout */
int fderr[2]; /* pipe fd array for the stderr */
pid_t pid; /* process id */
char buf[4096]; /* stdout and stderr buffer */
int status; /* place to store child process status */
if (pipe(fdout)==-1) /* first pipe for the stdout */
perror("pipe out"); /* exit with error if something wrong */
if (pipe(fderr)==-1) /* second pipe for the stderr */
perror("pipe err");
/*
* On success, the PID of the child process is returned in the
* parent, and 0 is returned in the child. On failure, -1 is
* returned in the parent, no child process is created, and errno is
* set to indicate the error.
*/
if ((pid = fork()) == -1) /* fork current process and store pid */
perror("fork error");
if(pid == 0) { /* if pid == 0 than this is a child process */
dup2 (fdout[1], STDOUT_FILENO); /* send stdout to the pipe fdout */
dup2 (fderr[1], STDERR_FILENO); /* send stderr to the pipe fderr */
/* close both sides of the pipe in the child */
close(fdout[0]);
close(fdout[1]);
/* close both sides of the pipe in the child */
close(fderr[0]);
close(fderr[1]);
/* execvp is called in the child ps
* argv[1] is a ptr to the first arg
* &argv[1] is a ptr to a ptr to the first arg
*/
execvp(argv[1], &argv[1]);
/* this part is never reached if execvp success */
return errno;
} else { /* parent */
while(wait(&status) > 0){} /* wait for the child processes to finish */
/* close write ends of the pipes */
close(fdout[1]);
close(fderr[1]);
/* if child process finished with errorprint error
* and return error code from parent process
*/
if (WIFEXITED(status) && WEXITSTATUS(status)){
printf("%s\n", strerror(WEXITSTATUS(status)));
return WEXITSTATUS(status);
}
ssize_t size;
size = read(fdout[0], buf, sizeof(buf));
printf("OUT: \"%.*s\"\n", (int)size, buf);
size = read(fderr[0], buf, sizeof(buf));
printf("ERR: \"%.*s\"\n", (int)size, buf);
/* close read ends of the pipes */
close(fdout[0]);
close(fderr[0]);
}
return 0;
}
compile and test:
ruslan#localhost:~/exec$ gcc main.c -o a && ./a /bin/ls
OUT: "a
main.c
"
ERR: ""
ruslan#localhost:~/exec$ gcc main.c -o a && ./a /bin/ls ---sdfadsfsdf
OUT: ""
ERR: "/bin/ls: unrecognized option '---sdfadsfsdf'
Try '/bin/ls --help' for more information.
"
ruslan#localhost:~/exec$

PseudoTerminal C - How To

I am currently trying to make an application that will receive information from a client and send it to be executed in a server. For that I need to use PseudoTerminals.
I followed the example from a book and I ended up with this code
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
int ptyMasterOpen(char *slavName, size_t snLen)
{
int masterFd, savedErrno;
char *p;
masterFd = posix_openpt(O_RDWR | O_NOCTTY);
printf("Master: %d \n", masterFd);
if(masterFd == -1)
return -1;
if(grantpt(masterFd) == -1 || unlockpt(masterFd) == -1)
{
savedErrno = errno;
close(masterFd);
errno = savedErrno;
return -1;
}
p = ptsname(masterFd); /*Get the name of the of the slave*/
if(p == NULL)
{
savedErrno = errno;
close(masterFd);
errno = savedErrno;
return -1;
}
printf("Slave: %s \n", p);
//*
if(strlen(p) < snLen)
{
strncpy(slavName, p, snLen);
}
else
{
close(masterFd);
errno = EOVERFLOW;
return -1;
}
//*/
return masterFd;
}
pid_t ptyFork(int *masterFd, char *slavName, size_t snLen)
{
int mfd, slaveFd, saveErrno;
pid_t childPid;
char slname[MAX_SNAME];
char cIn[100], cOut[100];
int out = -1, in = -1;
mfd = ptyMasterOpen(slname, MAX_SNAME);
if(mfd == -1)
return -1;
if(slavName != NULL)
{
if(strlen(slname) < snLen)
{
strncpy(slavName, slname, snLen);
}
else
{
close(mfd);
errno = EOVERFLOW;
return -1;
}
}
childPid = fork();
if(childPid == -1)
{
saveErrno = errno;
close(mfd);
errno = saveErrno;
return -1;
}
if(childPid != 0)
{
*masterFd = mfd;
return childPid;
}
close(mfd);
slaveFd = open(slname, O_RDWR);
if(slaveFd != -1)
{
sprintf(cOut, "/tmp/out%d", slaveFd);
sprintf(cIn, "/tmp/in%d", slaveFd);
mkfifo(cOut, S_IRUSR|S_IWUSR);
out = open(cOut, O_RDWR|O_TRUNC|O_NONBLOCK);
mkfifo(cIn, S_IRUSR|S_IWUSR);
in = open(cIn, O_RDONLY|O_TRUNC);dup2(out, STDERR_FILENO);
dup2(out, STDOUT_FILENO);
dup2(in, STDIN_FILENO);
}
else
{
printf("Problem with slave\n\r");
fflush(stdout);
}
//*
if(dup2(slaveFd, STDIN_FILENO) != STDIN_FILENO || dup2(slaveFd, STDERR_FILENO) != STDERR_FILENO || dup2(slaveFd, STDOUT_FILENO) != STDOUT_FILENO)
{
printf("Error on duplicating Files");
close(slaveFd);
exit(0);
}
else
{
dup2(out, STDERR_FILENO);
dup2(out, STDOUT_FILENO);
dup2(in, STDIN_FILENO);
}
//*/
return slaveFd;
}
The problem is:
sprintf(cOut, "/tmp/out%d", childPid);
sprintf(cIn, "/tmp/in%d", childPid);
always return 0 even when I print them in main and they are values like 952...
also using echo "ls" > "/tmp/inxx" and cat /tmp/outxx does nothing.
I believe the problem is when the fork is done, the variables lose their values or something, because, it prints the error messages now, without the while(1) on main
int main(int argc, const char * argv[])
{
// insert code here...
int masterFd;
pid_t slaveFd;
char slavName[MAX_SNAME];
slaveFd = ptyFork(&masterFd, slavName, MAX_SNAME);
//close(masterFd);
//while(1);
return 0;
}
Can anyone help?
Thanks

Why do my program behave unexpectedly when I use sigaction?

I'm writing my own simple shell as an exercise. I need to register to the SIGCHLD signal in order to handle zombie-processes. For some reason, when I add the handler using sigaction the program exits, and I don't understand why.
You can see in main() that we exit if process_arglist() returned 0 but I return 1 and I don't see how the signal handling could affect that.
Here's my code. It should handle a command ending with an & (we fork() and use execvp in the child code).
For example: ping 127.0.0.1 -c 5 &.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
void sigchld_handler(int signal) {
int my_errno = errno;
while (waitpid(-1, 0, WNOHANG) > 0); // WNOHANG so handler will be non-blocking.
errno = my_errno;
}
int handle_background_command(int count, char** arglist) {
pid_t pid;
arglist[count - 1] = NULL; // remove "&" from arglist
//Handle SIGCHLD signal
struct sigaction sa, sa_dft;
sa.sa_handler = sigchld_handler;
sa.sa_flags = SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, &sa_dft) == -1) {
perror("error when trying to set signal action");
exit(-1);
}
if((pid = fork()) == -1) {
perror("error when trying to fork() from handle_background_command()");
exit(1);
}
if(pid == 0) {
// Child code
sigaction(SIGCHLD, &sa_dft, NULL);
if(execvp(arglist[0], arglist) == -1) {
perror("error when trying to execvp() from handle_background_command()");
exit(1);
}
}
// Parent code
return 1;
}
int process_arglist(int count, char** arglist)
{
return handle_background_command(count, arglist);
}
int main(void)
{
while (1)
{
char** arglist = NULL;
char* line = NULL;
size_t size;
int count = 0;
if (getline(&line, &size, stdin) == -1) {
printf("out!");
break;
}
arglist = (char**) malloc(sizeof(char*));
if (arglist == NULL) {
printf("malloc failed: %s\n", strerror(errno));
exit(-1);
}
arglist[0] = strtok(line, " \t\n");
while (arglist[count] != NULL) {
++count;
arglist = (char**) realloc(arglist, sizeof(char*) * (count + 1));
if (arglist == NULL) {
printf("realloc failed: %s\n", strerror(errno));
exit(-1);
}
arglist[count] = strtok(NULL, " \t\n");
}
if (count != 0) {
int result = process_arglist(count, arglist);
printf("result = %d\n", result);
if (!result) {
free(line);
free(arglist);
printf("out\n");
break;
}
}
free(line);
free(arglist);
}
pthread_exit(NULL);
return 0;
}
Again, if I get rid of the signal handling code then it works.
What's the reason?
EDIT
Here's the output (last rows) of strace utility:
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=2818, si_status=0, si_utime=0, si_stime=0} ---
wait4(-1, NULL, WNOHANG, NULL) = 2818
wait4(-1, NULL, WNOHANG, NULL) = -1 ECHILD (No child processes)
rt_sigreturn() = -1 EINTR (Interrupted system call)
write(1, "out!", 4out!) = 4
exit_group(0) = ?
+++ exited with 0 +++
Your program exits with EINTR (interrupted system call) from getline function (getline is interrupted by signal handler).
Check this: How to handle EINTR (interrupted System Call)

linux system c quoting

On an embedded system with Linux as OS I want to call a 3rd party binary to retrieve data from a database and to append this data to an existing file.
The command string which is handed over to the system() function looks like:
"export_from_db >> /tmp/myFile"
Unfortunately, this doesn't work. /tmp/myFile never gets created! If I omit the redirection, then the database export is printed to stdout.
I wonder if system() and the redirection via ">>" go together well? On the prompt I successfully tested this command "export_fom_db >> /tmp/myFile"! Does anybody know how to achieve it using system()? Is there some kind of quoting necessary?
hm.. Actually, it seems ok for me.. this is exactly what system() is for - to execute a line under current shell. Does that embedded linux's shell support >> operator? Have you tried it manually in the terminal?
Another thought is that your application may be run under some other user account and that account could have some weird configuration, like having some csh or ksh instead of bash (or viceversa, depending on what you like). Check what user actually owns the process and check the /etc/passwd for the shell setup.
Also, there is small possibility that the user account that the app runs under simply does not have rights to write to /tmp :) be sure to check that too
Also... there is small possibility that on yours 'embedded linux' simply has the system() implemented in a simplistic way, that just invokes the application with given parameters and skips all other shell-wise operators. This could have been done to save on resources, as system() might be though to be rarely used, or just though to be "too heavy" by your linux designers.. it depends on the distro.. If you tell us which one it is, then people with more knowledge will probably be able to say if this is the case.
On an embedded system, you are better off implementing the system() yourself. Consider the following code (untested!):
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
/* Helper function: Open the specified file at the desired descriptor.
*/
static int openfd(const int descriptor,
const char *const filename,
const int flags, const mode_t mode)
{
int fd, result;
if (!filename || descriptor == -1)
return errno = EINVAL;
/* Close existing descriptor. Ignore errors. Hopefully it is reused. */
do {
result = close(descriptor);
} while (result == -1 && errno == EINTR);
/* Open the desired file. */
do {
fd = open(filename, flags, mode);
} while (fd == -1 && errno == EINTR);
if (fd == -1)
return errno;
/* Did we get lucky, and get the correct descriptor already? */
if (fd == descriptor)
return 0;
/* Move the descriptor. */
do {
result = dup2(fd, descriptor);
} while (result == -1 && errno == EINTR);
if (result == -1) {
const int saved_errno = errno;
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
return errno = saved_errno;
}
/* Close the temporary descriptor. */
do {
result = close(fd);
} while (result == -1 && errno == EINTR);
if (result == -1) {
const int saved_errno = errno;
do {
result = close(descriptor);
} while (result == -1 && errno == EINTR);
return errno = saved_errno;
}
return 0;
}
/* Start command on the background.
* Note: args[1] is the first argument, args[0] is the command name.
* NULL input/output/error redirects from/to /dev/null.
* Empty string for input/output/error does no redirections;
* the command the uses the same input/output/error.
* For non-empty output or error, specify the 'man 2 open' O_ flags too.
*
* Returns (pid_t)0 with errno set if an error occurs,
* otherwise the PID of the child process started.
*/
pid_t run(const char *file,
char *const args[],
const char *const input,
const char *const output, const int output_flags,
const char *const error, const int error_flags)
{
pid_t child;
int result, flags;
if (!cmd || !arg || !arg[0]) {
errno = EINVAL;
return (pid_t)0;
}
child = fork();
if (child == (pid_t)-1)
return (pid_t)0;
if (child)
return child;
/* This is the child process. */
if (input && *input)
result = openfd(STDIN_FILENO, input, O_RDONLY | O_NOCTTY, 0);
else
if (!input)
result = openfd(STDIN_FILENO, "/dev/null", O_RDONLY | O_NOCTTY, 0);
else
result = 0;
if (result)
exit(127);
if (output && *output)
result = openfd(STDOUT_FILENO, output, output_flags, 0666);
else
if (!output)
result = openfd(STDOUT_FILENO, "/dev/null", O_WRONLY | O_NOCTTY, 0);
else
result = 0;
if (result)
exit(127);
if (error && *error)
result = openfd(STDERR_FILENO, error, error_flags, 0666);
else
if (!error)
result = openfd(STDERR_FILENO, "/dev/null", O_WRONLY | O_NOCTTY, 0);
else
result = 0;
if (result)
exit(127);
execvp(file, args);
exit(127);
}
The run() only starts the command, you'll need to wait for it to complete. Note that your main program can do meaningful work at the same time, unless it needs the exit status (or the files the command is supposed to create) right away. Example use:
/* Command to run. NULL terminates the list. */
char *const cmd[] = { "ls", "-l", NULL };
pid_t child, p;
int status;
child = run(cmd[0], cmd,
NULL /* < /dev/null */,
"/tmp/some-log-file", O_WRONLY | O_CREAT | O_APPEND,
"", 0 /* No redirection for standard error */);
if (!child) {
fprintf(stderr, "Cannot run '%s': %s.\n", cmd[0], strerror(errno));
exit(1);
}
do {
status = 0;
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost '%s': %s.\n", cmd[0], strerror(errno));
exit(1);
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
printf("Command executed successfully.\n");
else
printf("Command failed with exit status %d.\n", WEXITSTATUS(status));
} else
if (WSIGNALED(status))
printf("Command died from signal %s.\n", strsignal(WTERMSIG(status)));
else
printf("Command died unexpectedly.\n");
although the last part is often abbreviated to
if (WIFEXITED(status) && !WEXITSTATUS(status))
printf("'%s': Successful.\n", cmd[0]);
else
printf("'%s': Failed.\n", cmd[0]);
Note that if you process the output anyway, you probably should use a pipe (either popen() or an extended version of the above function) instead.
Hope you find this useful.

Resources