aboutsummaryrefslogtreecommitdiff
path: root/forkexec.c
diff options
context:
space:
mode:
authorLucas Castro <lucas@gnuabordo.com.br>2024-01-10 22:33:44 -0300
committerLucas Castro <lucas@gnuabordo.com.br>2024-01-10 22:33:44 -0300
commita4270020addc324fb60d2b1b446b41bc31e3baf8 (patch)
tree8708f08c66a63f08c0cd88ab1a46faadcd3f4706 /forkexec.c
Import Upstream version 1.0.4upstream/1.0.4
Diffstat (limited to 'forkexec.c')
-rw-r--r--forkexec.c390
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 */