2009-02-25 14:17:23 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <pty.h>
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include "list.h"
|
|
|
|
#include "logging.h"
|
2009-05-03 14:20:03 +02:00
|
|
|
#include "process.h"
|
2009-02-25 14:17:23 +01:00
|
|
|
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
|
|
|
|
static LIST_HEAD(child_proc_list);
|
|
|
|
|
2009-05-03 14:20:03 +02:00
|
|
|
struct child_process * childproc_alloc(char *const argv[], const char *pwd)
|
2009-02-25 14:17:23 +01:00
|
|
|
{
|
|
|
|
/* count args */
|
|
|
|
int cnt = 0;
|
|
|
|
while (argv[cnt++] != NULL);
|
|
|
|
|
|
|
|
/* alloc struct child_process and array of char **argv */
|
|
|
|
int size = sizeof(struct child_process) + sizeof(char *) * cnt;
|
|
|
|
struct child_process *child = malloc(size);
|
|
|
|
if (child == NULL) {
|
|
|
|
log_print(LOG_ERROR, "create_child_process(): malloc()");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(child, 0, sizeof(struct child_process));
|
|
|
|
|
|
|
|
/* argv points to first byte after struct child_process */
|
|
|
|
child->argv = (char **)(child +1);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(child->fd); i++)
|
|
|
|
child->fd[i] = -1;
|
|
|
|
|
|
|
|
/* copy argv */
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
|
|
child->argv[i] = (argv[i] != NULL) ? strdup(argv[i]) : NULL;
|
|
|
|
|
|
|
|
/* copy pwd */
|
|
|
|
if (pwd != NULL)
|
|
|
|
child->pwd = strdup(pwd);
|
|
|
|
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
2009-05-03 14:20:03 +02:00
|
|
|
int childproc_free(struct child_process *child)
|
2009-02-25 14:17:23 +01:00
|
|
|
{
|
2009-05-03 14:20:03 +02:00
|
|
|
/* child already running, return error */
|
|
|
|
if (child->pid != 0)
|
|
|
|
return -1;
|
|
|
|
|
2009-02-25 14:17:23 +01:00
|
|
|
int i;
|
|
|
|
for (i = 0; child->argv[i] != NULL; i++)
|
|
|
|
free(child->argv[i]);
|
|
|
|
|
|
|
|
if (child->pwd != NULL)
|
|
|
|
free(child->pwd);
|
|
|
|
|
|
|
|
free(child);
|
2009-05-03 14:20:03 +02:00
|
|
|
return 0;
|
2009-02-25 14:17:23 +01:00
|
|
|
}
|
|
|
|
|
2009-05-03 14:20:03 +02:00
|
|
|
pid_t childproc_fork(struct child_process *child, void (*exit_cb)(struct child_process *child, int exit_code, void *privdata), void *privdata)
|
2009-02-25 14:17:23 +01:00
|
|
|
{
|
|
|
|
struct stat stat_buf;
|
|
|
|
if (stat(child->argv[0], &stat_buf) != 0) {
|
|
|
|
log_print(LOG_ERROR, "spawn_child(): stat()");
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* not a regular file, or not executable */
|
|
|
|
} else if (!S_ISREG(stat_buf.st_mode) || !(stat_buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
|
|
|
|
log_print(LOG_ERROR, "spawn_child(): stat()");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* exit callback */
|
|
|
|
child->exit_cb = exit_cb;
|
|
|
|
child->privdata = privdata;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fd[0] - if -1 pipe() and dup2() read-side as child stdin, store write-side for parent
|
|
|
|
* else dup2() this fd as child stdin
|
|
|
|
* fd[1] - if -1 pipe() and dup2() write-side as child stdout, store read-side for parent
|
|
|
|
* else dup2() this fd as child stdout
|
|
|
|
* fd[2] - if -1 pipe() and dup2() write-side as child stderr, store read-side for parent
|
|
|
|
* else dup2() this fd as child stderr
|
|
|
|
*/
|
|
|
|
int child_pipes[3][2];
|
|
|
|
if (child->fd[STDIN_FILENO] == -1) {
|
|
|
|
if (pipe(child_pipes[STDIN_FILENO]) < 0) {
|
|
|
|
log_print(LOG_ERROR, "spawn_child(): pipe(STDIN_FILENO)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
child_pipes[STDIN_FILENO][0] = child->fd[STDIN_FILENO];
|
|
|
|
child_pipes[STDIN_FILENO][1] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child->fd[STDOUT_FILENO] == -1) {
|
|
|
|
if (pipe(child_pipes[STDOUT_FILENO]) < 0) {
|
|
|
|
log_print(LOG_ERROR, "spawn_child(): pipe(STDOUT_FILENO)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
child_pipes[STDOUT_FILENO][0] = -1;
|
|
|
|
child_pipes[STDOUT_FILENO][1] = child->fd[STDOUT_FILENO];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child->fd[STDERR_FILENO] == -1) {
|
|
|
|
if (pipe(child_pipes[STDERR_FILENO]) < 0) {
|
|
|
|
log_print(LOG_ERROR, "spawn_child(): pipe(STDERR_FILENO)");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
child_pipes[STDERR_FILENO][0] = -1;
|
|
|
|
child_pipes[STDERR_FILENO][1] = child->fd[STDERR_FILENO];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add this to list now, so sigchld_handler can find it */
|
|
|
|
list_add_tail(&child->list, &child_proc_list);
|
|
|
|
|
|
|
|
child->pid = fork();
|
|
|
|
if (child->pid == 0) { /* child */
|
|
|
|
close(child_pipes[STDIN_FILENO][1]);
|
|
|
|
close(child_pipes[STDOUT_FILENO][0]);
|
|
|
|
close(child_pipes[STDERR_FILENO][0]);
|
|
|
|
|
|
|
|
if (dup2(child_pipes[STDIN_FILENO][0], STDIN_FILENO) < 0) {
|
|
|
|
perror("dup2(STDIN_FILENO)");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dup2(child_pipes[STDOUT_FILENO][1], STDOUT_FILENO) < 0) {
|
|
|
|
perror("dup2(STDOUT_FILENO)");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dup2(child_pipes[STDERR_FILENO][1], STDERR_FILENO) < 0) {
|
|
|
|
perror("dup2(STDERR_FILENO)");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(child_pipes[STDIN_FILENO][0]);
|
|
|
|
close(child_pipes[STDOUT_FILENO][1]);
|
|
|
|
close(child_pipes[STDERR_FILENO][1]);
|
|
|
|
|
|
|
|
if (child->pwd != NULL && chdir(child->pwd) < 0) {
|
|
|
|
perror("chdir()");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
execv(child->argv[0], child->argv);
|
|
|
|
perror("execv()");
|
|
|
|
exit(1);
|
|
|
|
|
|
|
|
} else if (child->pid < 0) { /* fork error */
|
|
|
|
log_print(LOG_ERROR, "spawn_child(): fork()");
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
} else { /* parent */
|
|
|
|
if (child->fd[STDIN_FILENO] == -1) {
|
|
|
|
close(child_pipes[STDIN_FILENO][0]);
|
|
|
|
child->fd[STDIN_FILENO] = child_pipes[STDIN_FILENO][1];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child->fd[STDOUT_FILENO] == -1) {
|
|
|
|
close(child_pipes[STDOUT_FILENO][1]);
|
|
|
|
child->fd[STDOUT_FILENO] = child_pipes[STDOUT_FILENO][0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (child->fd[STDERR_FILENO] == -1) {
|
|
|
|
close(child_pipes[STDERR_FILENO][1]);
|
|
|
|
child->fd[STDERR_FILENO] = child_pipes[STDERR_FILENO][0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return child->pid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-03 14:20:03 +02:00
|
|
|
void childproc_cleanup(void)
|
2009-02-25 14:17:23 +01:00
|
|
|
{
|
|
|
|
struct child_process *child, *tmp;
|
|
|
|
list_for_each_entry_safe(child, tmp, &child_proc_list, list) {
|
|
|
|
int status = 0;
|
|
|
|
int ret = waitpid(child->pid, &status, WNOHANG);
|
|
|
|
if (ret == -1) {
|
|
|
|
log_print(LOG_WARN, "sigchld_handler(): waitpid(%d)", child->pid);
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} else if (ret != 0 && WIFEXITED(status)) {
|
|
|
|
list_del(&child->list);
|
|
|
|
|
|
|
|
if (child->exit_cb != NULL)
|
|
|
|
child->exit_cb(child, WEXITSTATUS(status), child->privdata);
|
|
|
|
|
2009-05-03 14:20:03 +02:00
|
|
|
child->pid = 0;
|
|
|
|
childproc_free(child);
|
2009-02-25 14:17:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|