diff options
| author | Lucas Castro <lucas@gnuabordo.com.br> | 2024-01-10 22:33:44 -0300 |
|---|---|---|
| committer | Lucas Castro <lucas@gnuabordo.com.br> | 2024-01-10 22:33:44 -0300 |
| commit | a4270020addc324fb60d2b1b446b41bc31e3baf8 (patch) | |
| tree | 8708f08c66a63f08c0cd88ab1a46faadcd3f4706 /forkexec.c | |
Import Upstream version 1.0.4upstream/1.0.4
Diffstat (limited to 'forkexec.c')
| -rw-r--r-- | forkexec.c | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/forkexec.c b/forkexec.c new file mode 100644 index 0000000..b1b2cd9 --- /dev/null +++ b/forkexec.c @@ -0,0 +1,390 @@ +/* + +(C) 2009-2011 Mika Ilmaranta <ilmis@nullnet.fi> + +License: GPLv2 + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdarg.h> +#include <syslog.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "config.h" +#include "forkexec.h" + +static void sigchld_hdl(int sig); + +typedef struct exec_queue +{ + pid_t pid; + char **argv; + char **envp; + struct exec_queue *next; +} EXEC_QUEUE; + +typedef struct exec_queues +{ + char *name; + EXEC_QUEUE *first; + EXEC_QUEUE *last; + struct exec_queues *next; +} EXEC_QUEUES; + +static EXEC_QUEUES *exec_queues_first = NULL; +static EXEC_QUEUES *exec_queues_last = NULL; + +pid_t forkexec(char **argv, char **envp) +{ + pid_t pid; +#if defined(DEBUG) + int i; +#endif + + if((pid = fork()) == -1) { + syslog(LOG_ERR, "%s: %s: %d: fork() failed \"%s\"", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return(0); + } + + if(pid) { + /* parent */ + if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: child process forked with pid: %d", __FILE__, __FUNCTION__, __LINE__, pid); + return(pid); + } + + /* child */ + closelog(); /* openlog doesn't return a fd to set close on exec */ + +#if defined(DEBUG) + for(i = 0; argv[i]; i++) { + fprintf(stderr, "argv[%d] = \"%s\"\n", i, argv[i]); + } + for(i = 0; envp[i]; i++) { + fprintf(stderr, "envp[%d] = \"%s\"\n", i, envp[i]); + } +#endif + + execve(argv[0], argv, envp); + syslog(LOG_ERR, "%s: %s: %d: child process failed to execute external command", __FILE__, __FUNCTION__, __LINE__); + exit(1); /* exec failed ... */ +} + +/* + Set up child signal handler to handle termination of children. + This should be done once only for the main program. +*/ +void create_sigchld_hdl(void) +{ + struct sigaction act; + + memset (&act, 0, sizeof(act)); + act.sa_handler = sigchld_hdl; + if (sigaction(SIGCHLD, &act, 0)) { + syslog(LOG_ERR, "%s: %s: %d: failed to set up child signal handler: %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return; + } else { + if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: successfully set up child signal handler", __FILE__, __FUNCTION__, __LINE__); + } +} + +/* + SIGCHLD handler. Will be called for all children. +*/ +static void sigchld_hdl(int sig) +{ + /* Wait for the dead process. + * We use a non-blocking call to be sure this signal handler will not + * block if a child was cleaned up in another part of the program. */ + int exitval; + pid_t pid; + + if((pid = waitpid(-1, &exitval, WNOHANG)) == -1) { + if(cfg.debug >= 9 && errno != ECHILD) syslog(LOG_ERR, "%s: %s: %d: waitpid failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + } else { + if(cfg.debug >= 9 && exitval) syslog(LOG_ERR, "%s: %s: %d: child script with pid %d exited with non null exit value %d", __FILE__, __FUNCTION__, __LINE__, pid, exitval); + else if(cfg.debug >= 9) syslog(LOG_ERR, "%s: %s: %d: child script with pid %d exited successfully", __FILE__, __FUNCTION__, __LINE__, pid); + exec_queue_delete(pid); + } +} + +void exec_queue_add(char *queue, char **argv, char **envp) +{ + EXEC_QUEUES *eqs; + EXEC_QUEUE *eq; + + if(!exec_queues_first) { /* first queue */ + if((eqs = malloc(sizeof(EXEC_QUEUES))) == NULL) { + syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return; + } + eqs->name = strdup(queue); + eqs->next = NULL; + + if((eq = malloc(sizeof(EXEC_QUEUE))) == NULL) { + syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return; + } + eq->pid = 0; + eq->argv = argv; + eq->envp = envp; + eq->next = NULL; + + eqs->first = eq; + eqs->last = eq; + + exec_queues_first = eqs; + exec_queues_last = eqs; + + } else { /* not first queue */ + for(eqs = exec_queues_first; eqs; eqs = eqs->next) { + if(!strcmp(eqs->name, queue)) { + /* queue exists, add to it */ + if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: found queue %s", __FILE__, __FUNCTION__, __LINE__, eqs->name); + + if((eq = malloc(sizeof(EXEC_QUEUE))) == NULL) { + syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return; + } + + eq->pid = 0; + eq->argv = argv; + eq->envp = envp; + eq->next = NULL; + + if(!eqs->first) { /* empty queue */ + eqs->first = eq; + eqs->last = eq; + + } else { /* add after last */ + eqs->last->next = eq; + eqs->last = eq; + } + + break; + } + } + + if(!eqs) { /* not found, create a new queue and add to it */ + if(cfg.debug >= 9) syslog(LOG_INFO, "%s: %s: %d: queue %s not found adding new queue", __FILE__, __FUNCTION__, __LINE__, queue); + + if((eqs = malloc(sizeof(EXEC_QUEUES))) == NULL) { + syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return; + } + eqs->name = strdup(queue); + eqs->next = NULL; + + exec_queues_last->next = eqs; + exec_queues_last = eqs; + + if((eq = malloc(sizeof(EXEC_QUEUE))) == NULL) { + syslog(LOG_ERR, "%s: %s: %d: malloc failed %s", __FILE__, __FUNCTION__, __LINE__, strerror(errno)); + return; + } + + eq->pid = 0; + eq->argv = argv; + eq->envp = envp; + eq->next = NULL; + + eqs->first = eq; + eqs->last = eq; + } + } + return; +} + +#if defined(DEBUG) +void exec_queue_dump(void) +{ + EXEC_QUEUES *eqs; + EXEC_QUEUE *eq; + int i; + + + for(eqs = exec_queues_first; eqs; eqs = eqs->next) { + syslog(LOG_INFO, "%s: %s: %d: eqs->name %s", __FILE__, __FUNCTION__, __LINE__, eqs->name); + for(eq = eqs->first; eq; eq = eq->next) { + syslog(LOG_INFO, "%s: %s: %d: eq->pid %d", __FILE__, __FUNCTION__, __LINE__, eq->pid); + for(i = 0; eq->argv[i]; i++) { + syslog(LOG_INFO, "%s: %s: %d: argv[%d] = %s", __FILE__, __FUNCTION__, __LINE__, i, eq->argv[i]); + } + } + } +} +#endif + +void exec_queue_process(void) +{ + EXEC_QUEUES *eqs; + EXEC_QUEUE *eq; + + for(eqs = exec_queues_first; eqs; eqs = eqs->next) { + eq = eqs->first; + if(eq && eq->pid == 0) eq->pid = forkexec(eq->argv, eq->envp); + } +} + +void exec_queue_delete(pid_t pid) +{ + EXEC_QUEUES *eqs; + EXEC_QUEUE *eq; + + for(eqs = exec_queues_first; eqs; eqs = eqs->next) { + EXEC_QUEUE *prev = NULL; + + for(eq = eqs->first; eq; eq = eq->next) { + if(eq->pid == pid) { + if(!prev) { /* this is first */ + if(!eq->next) { /* last */ + eqs->first = NULL; + eqs->last = NULL; + } else { /* not last */ + eqs->first = eq->next; + } + } else { /* not first */ + prev->next = eq->next; + } + exec_queue_argv_free(eq->argv); + exec_queue_envp_free(eq->envp); + free(eq); + return; + } + + prev = eq; + } + } + if(cfg.debug >= 9) syslog(LOG_ERR, "%s: %s: %d: child pid %d not found", __FILE__, __FUNCTION__, __LINE__, pid); +} + +void exec_queue_free(void) +{ + EXEC_QUEUES *eqs; + EXEC_QUEUE *eq; + + eqs = exec_queues_first; + while(eqs) { + EXEC_QUEUES *prev_eqs = eqs; + + eq = eqs->first; + while(eq) { + EXEC_QUEUE *prev_eq = eq; + + eq = eq->next; + free(prev_eq); + } + + eqs = eqs->next; + free(prev_eqs); + } + + exec_queues_first = NULL; + exec_queues_last = NULL; +} + +char **exec_queue_argv(char *fmt, ...) +{ + va_list vl; + char **argv; + char *s; + int fmt_cnt; + char buf[BUFSIZ]; + int i; + + s = fmt; + fmt_cnt = 0; + while(*s) { + if(*s++ == '%') fmt_cnt++; + } + + if((argv = malloc((fmt_cnt + 1) * sizeof(char *))) == NULL) { + syslog(LOG_ERR, "%s: %s: failed to malloc %s", __FILE__, __FUNCTION__, strerror(errno)); + return(NULL); + } + + s = fmt; + i = 0; + va_start(vl, fmt); + while(*s) { + if(*s == '%') { + s++; + switch(*s++) { + case 's': /* string */ + argv[i] = strdup(va_arg(vl, char *)); + i++; + break; + + case 'd': /* int */ + snprintf(buf, BUFSIZ - 1, "%d", va_arg(vl, int)); + argv[i] = strdup(buf); + i++; + break; + + default: /* skip unknown directives */ + break; + } + } else { + s++; + } + } + va_end(vl); + + argv[i] = NULL; + + return(argv); +} + +void exec_queue_argv_free(char **argv) +{ + int i; + + for(i = 0; argv[i]; i++) { + free(argv[i]); + } + + free(argv); +} + +char **exec_queue_envp(void) +{ + char **envp; + char buf[BUFSIZ]; + + if((envp = malloc(4 * sizeof(char *))) == NULL) { + syslog(LOG_ERR, "%s: %s: malloc failed %s", __FILE__, __FUNCTION__, strerror(errno)); + return(NULL); + } + + snprintf(buf, BUFSIZ - 1, "LANG=%s", getenv("LANG")); + envp[0] = strdup(buf); + + snprintf(buf, BUFSIZ - 1, "PATH=%s", getenv("PATH")); + envp[1] = strdup(buf); + + snprintf(buf, BUFSIZ - 1, "TERM=%s", getenv("TERM")); + envp[2] = strdup(buf); + + envp[3] = NULL; + + return(envp); +} + +void exec_queue_envp_free(char **envp) +{ + int i; + + for(i = 0; envp[i]; i++) { + free(envp[i]); + } + + free(envp); +} + +/* EOF */ |
