#include "global.h" extern char **environ; extern int __isthreaded; struct pid { SLIST_ENTRY(pid) next; FILE *fp; pid_t pid; }; static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; #define THREAD_LOCK() if (__isthreaded) pthread_mutex_lock(&pidlist_mutex) #define THREAD_UNLOCK() if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex) /* * io_progInit() - Init program pool * * @progName = program name for execution * @initNum = initial started programs * @maxNum = maximum started programs * return: NULL error or !=NULL allocated pool (must destroied with io_progDestroy()) */ prog_t * io_progInit(const char *progName, u_int initNum, u_int maxNum) { prog_t *prg = NULL; if (initNum > maxNum) return NULL; prg = e_malloc(sizeof(prog_t)); if (!prg) { io_SetErr(elwix_GetErrno(), "%s", elwix_GetError()); return NULL; } else memset(prg, 0, sizeof(prog_t)); prg->prog_inin = initNum; prg->prog_maxn = maxNum; strlcpy(prg->prog_name, progName, sizeof prg->prog_name); prg->prog_fds = array_Init(prg->prog_maxn); if (!prg->prog_fds) { io_SetErr(elwix_GetErrno(), "%s", elwix_GetError()); e_free(prg); return NULL; } pthread_mutex_init(&prg->prog_mtx, NULL); if (io_progOpen(prg, prg->prog_inin) < 0) io_progDestroy(&prg); return prg; } /* * io_progDestroy() - Destroy entire program pool * * @pprg = program pool * return: none */ void io_progDestroy(prog_t ** __restrict pprg) { if (!pprg || !*pprg) return; io_progClose(*pprg, 0); array_Destroy(&(*pprg)->prog_fds); pthread_mutex_destroy(&(*pprg)->prog_mtx); e_free(*pprg); *pprg = NULL; } /* * io_progClose() - Close all programs in pool * * @prg = program pool * @closeNum = close program(s) (0 all) * return: 0 error, >0 closed programs */ int io_progClose(prog_t * __restrict prg, u_int closeNum) { register int i; int ret = 0; if (!prg) return 0; if (closeNum > prg->prog_maxn) { io_SetErr(EINVAL, "Requested number for close program is over pool's limit"); return 0; } pthread_mutex_lock(&prg->prog_mtx); for (i = array_Size(prg->prog_fds) - 1; (closeNum ? ret < closeNum : 42) && i > -1; i--) if (array_Get(prg->prog_fds, i)) { io_pclose(array(prg->prog_fds, i, FILE*)); array_Del(prg->prog_fds, i, 0); prg->prog_cnum--; ret++; } pthread_mutex_unlock(&prg->prog_mtx); return ret; } /* * io_progOpen() - Execute number of program(s) * * @prg = program pool * @execNum = execute program(s) (0 max) * return: 0 error, >0 executed programs and abs(<0) executed programs with logged error */ int io_progOpen(prog_t * __restrict prg, u_int execNum) { FILE *f; int stat, ret = 0; register int i; pid_t pid; if (!prg) return 0; if (execNum > prg->prog_maxn) { io_SetErr(EINVAL, "Requested number for program execution is over pool's limit"); return 0; } pthread_mutex_lock(&prg->prog_mtx); for (i = 0; (execNum ? ret < execNum : 42) && i < array_Size(prg->prog_fds); i++) if (!array_Get(prg->prog_fds, i)) { f = io_popen(prg->prog_name, "r+", &pid); if (!f) { LOGERR; ret = -1; break; } else if (waitpid(pid, &stat, WNOHANG) > 0) { io_SetErr(ECHILD, "Program exit with status %d", WIFEXITED(stat) ? WEXITSTATUS(stat) : -1); ret = -1; break; } else array_Set(prg->prog_fds, i, f); prg->prog_cnum++; ret++; } pthread_mutex_unlock(&prg->prog_mtx); return ret; } /* * io_progVacuum() - Vacuum pool to running number of programs * * @prg = program pool * @toNum = vacuum to number of programs (0 to init number) * return: 0 error or >0 closed programs */ int io_progVacuum(prog_t * __restrict prg, u_int toNum) { register int i; int ret = 0; if (!prg) return 0; if (toNum > prg->prog_maxn) { io_SetErr(EINVAL, "Requested number for close program is over pool's limit"); return 0; } if (!toNum) toNum = prg->prog_inin; pthread_mutex_lock(&prg->prog_mtx); for (i = array_Size(prg->prog_fds) - 1; prg->prog_cnum > toNum && i > -1; i--) if (array_Get(prg->prog_fds, i)) { io_pclose(array(prg->prog_fds, i, FILE*)); array_Del(prg->prog_fds, i, 0); prg->prog_cnum--; ret++; } pthread_mutex_unlock(&prg->prog_mtx); return ret; } /* * io_popen() - ELWIX replacement of standard popen * * @command = command * @type = type * @ppid = return pid of child program * return: NULL error or !=NULL open program */ FILE * io_popen(const char *command, const char *type, pid_t *ppid) { struct pid *cur; FILE *iop; int pdes[2], pid, twoway, cloexec; char *argv[4]; struct pid *p; cloexec = strchr(type, 'e') != NULL; /* * Lite2 introduced two-way popen() pipes using _socketpair(). * FreeBSD's pipe() is bidirectional, so we use that. */ if (strchr(type, '+')) { twoway = 1; type = "r+"; } else { twoway = 0; if ((*type != 'r' && *type != 'w') || (type[1] && (type[1] != 'e' || type[2]))) return (NULL); } if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0) return (NULL); if ((cur = e_malloc(sizeof(struct pid))) == NULL) { close(pdes[0]); close(pdes[1]); return (NULL); } argv[0] = "sh"; argv[1] = "-c"; argv[2] = (char *)command; argv[3] = NULL; THREAD_LOCK(); switch (pid = vfork()) { case -1: /* Error. */ THREAD_UNLOCK(); close(pdes[0]); close(pdes[1]); e_free(cur); return (NULL); /* NOTREACHED */ case 0: /* Child. */ if (*type == 'r') { /* * The _dup2() to STDIN_FILENO is repeated to avoid * writing to pdes[1], which might corrupt the * parent's copy. This isn't good enough in * general, since the _exit() is no return, so * the compiler is free to corrupt all the local * variables. */ if (!cloexec) close(pdes[0]); if (pdes[1] != STDOUT_FILENO) { dup2(pdes[1], STDOUT_FILENO); if (!cloexec) close(pdes[1]); if (twoway) dup2(STDOUT_FILENO, STDIN_FILENO); } else if (twoway && (pdes[1] != STDIN_FILENO)) { dup2(pdes[1], STDIN_FILENO); if (cloexec) fcntl(pdes[1], F_SETFD, 0); } else if (cloexec) fcntl(pdes[1], F_SETFD, 0); } else { if (pdes[0] != STDIN_FILENO) { dup2(pdes[0], STDIN_FILENO); if (!cloexec) close(pdes[0]); } else if (cloexec) fcntl(pdes[0], F_SETFD, 0); if (!cloexec) close(pdes[1]); } SLIST_FOREACH(p, &pidlist, next) close(fileno(p->fp)); execve(_PATH_BSHELL, argv, environ); _exit(127); /* NOTREACHED */ default: if (ppid) *ppid = pid; } THREAD_UNLOCK(); /* Parent; assume fdopen can't fail. */ if (*type == 'r') { iop = fdopen(pdes[0], type); close(pdes[1]); } else { iop = fdopen(pdes[1], type); close(pdes[0]); } /* Link into list of file descriptors. */ cur->fp = iop; cur->pid = pid; THREAD_LOCK(); SLIST_INSERT_HEAD(&pidlist, cur, next); THREAD_UNLOCK(); return (iop); } /* * io_pclose() - ELWIX replacement of standard pclose * * @iop = popen handle * return: -1 error or !=-1 pid status */ int io_pclose(FILE *iop) { struct pid *cur, *last = NULL; int pstat; pid_t pid; /* * Find the appropriate file pointer and remove it from the list. */ THREAD_LOCK(); SLIST_FOREACH(cur, &pidlist, next) { if (cur->fp == iop) break; last = cur; } if (cur == NULL) { THREAD_UNLOCK(); return (-1); } if (last == NULL) SLIST_REMOVE_HEAD(&pidlist, next); else SLIST_REMOVE_AFTER(last, next); THREAD_UNLOCK(); fclose(iop); do { pid = wait4(cur->pid, &pstat, 0, (struct rusage *)0); } while (pid == -1 && errno == EINTR); e_free(cur); return (pid == -1 ? -1 : pstat); }