#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);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>