Annotation of libaitio/src/exec.c, revision 1.1.2.4
1.1.2.1 misho 1: #include "global.h"
2:
3:
1.1.2.4 ! misho 4: extern char **environ;
! 5: extern int __isthreaded;
! 6:
! 7: struct pid {
! 8: SLIST_ENTRY(pid) next;
! 9: FILE *fp;
! 10: pid_t pid;
! 11: };
! 12: static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist);
! 13: static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
! 14:
! 15: #define THREAD_LOCK() if (__isthreaded) pthread_mutex_lock(&pidlist_mutex)
! 16: #define THREAD_UNLOCK() if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex)
! 17:
! 18:
1.1.2.1 misho 19: /*
20: * io_progInit() - Init program pool
21: *
22: * @progName = program name for execution
23: * @initNum = initial started programs
24: * @maxNum = maximum started programs
25: * return: NULL error or !=NULL allocated pool (must destroied with io_progDestroy())
26: */
27: prog_t *
28: io_progInit(const char *progName, u_int initNum, u_int maxNum)
29: {
30: prog_t *prg = NULL;
31:
32: if (initNum > maxNum)
33: return NULL;
34:
35: prg = e_malloc(sizeof(prog_t));
36: if (!prg) {
37: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
38: return NULL;
39: } else
40: memset(prg, 0, sizeof(prog_t));
41:
42: prg->prog_inin = initNum;
43: prg->prog_maxn = maxNum;
44: strlcpy(prg->prog_name, progName, sizeof prg->prog_name);
45:
46: prg->prog_fds = array_Init(prg->prog_maxn);
47: if (!prg->prog_fds) {
48: io_SetErr(elwix_GetErrno(), "%s", elwix_GetError());
49: e_free(prg);
50: return NULL;
51: }
52:
53: pthread_mutex_init(&prg->prog_mtx, NULL);
1.1.2.2 misho 54:
1.1.2.3 misho 55: if (io_progOpen(prg, prg->prog_inin) < 0)
1.1.2.2 misho 56: io_progDestroy(&prg);
1.1.2.1 misho 57: return prg;
58: }
59:
60: /*
61: * io_progDestroy() - Destroy entire program pool
62: *
63: * @pprg = program pool
64: * return: none
65: */
66: void
67: io_progDestroy(prog_t ** __restrict pprg)
68: {
69: if (!pprg || !*pprg)
70: return;
71:
72: io_progClose(*pprg, 0);
73:
74: array_Destroy(&(*pprg)->prog_fds);
75: pthread_mutex_destroy(&(*pprg)->prog_mtx);
76:
77: e_free(*pprg);
78: *pprg = NULL;
79: }
80:
81: /*
82: * io_progClose() - Close all programs in pool
83: *
84: * @prg = program pool
85: * @closeNum = close program(s) (0 all)
86: * return: 0 error, >0 closed programs
87: */
88: int
89: io_progClose(prog_t * __restrict prg, u_int closeNum)
90: {
91: register int i;
92: int ret = 0;
93:
94: if (!prg)
95: return 0;
96: if (closeNum > prg->prog_maxn) {
97: io_SetErr(EINVAL, "Requested number for close program is over pool's limit");
98: return 0;
99: }
100:
101: pthread_mutex_lock(&prg->prog_mtx);
102: for (i = array_Size(prg->prog_fds) - 1;
103: (closeNum ? ret < closeNum : 42) && i > -1; i--)
104: if (array_Get(prg->prog_fds, i)) {
1.1.2.4 ! misho 105: io_pclose(array(prg->prog_fds, i, FILE*));
1.1.2.1 misho 106: array_Del(prg->prog_fds, i, 0);
107: prg->prog_cnum--;
108: ret++;
109: }
110: pthread_mutex_unlock(&prg->prog_mtx);
111:
112: return ret;
113: }
114:
115: /*
116: * io_progOpen() - Execute number of program(s)
117: *
118: * @prg = program pool
119: * @execNum = execute program(s) (0 max)
120: * return: 0 error, >0 executed programs and abs(<0) executed programs with logged error
121: */
122: int
123: io_progOpen(prog_t * __restrict prg, u_int execNum)
124: {
125: FILE *f;
1.1.2.4 ! misho 126: int stat, ret = 0;
1.1.2.1 misho 127: register int i;
1.1.2.4 ! misho 128: pid_t pid;
1.1.2.1 misho 129:
130: if (!prg)
131: return 0;
132: if (execNum > prg->prog_maxn) {
133: io_SetErr(EINVAL, "Requested number for program execution is over pool's limit");
134: return 0;
135: }
136:
137: pthread_mutex_lock(&prg->prog_mtx);
138: for (i = 0; (execNum ? ret < execNum : 42) && i < array_Size(prg->prog_fds); i++)
139: if (!array_Get(prg->prog_fds, i)) {
1.1.2.4 ! misho 140: f = io_popen(prg->prog_name, "r+", &pid);
1.1.2.1 misho 141: if (!f) {
142: LOGERR;
143: ret *= -1;
144: break;
1.1.2.4 ! misho 145: } else if (waitpid(pid, &stat, WNOHANG) > 0) {
! 146: io_SetErr(ECHILD, "Program exit with status %d",
! 147: WIFEXITED(stat) ? WEXITSTATUS(stat) : -1);
! 148: ret *= -1;
! 149: break;
1.1.2.1 misho 150: } else
151: array_Set(prg->prog_fds, i, f);
152: prg->prog_cnum++;
153: ret++;
154: }
155: pthread_mutex_unlock(&prg->prog_mtx);
156:
157: return ret;
158: }
159:
160: /*
161: * io_progVacuum() - Vacuum pool to running number of programs
162: *
163: * @prg = program pool
164: * @toNum = vacuum to number of programs (0 to init number)
165: * return: 0 error or >0 closed programs
166: */
167: int
168: io_progVacuum(prog_t * __restrict prg, u_int toNum)
169: {
170: register int i;
171: int ret = 0;
172:
173: if (!prg)
174: return 0;
175: if (toNum > prg->prog_maxn) {
176: io_SetErr(EINVAL, "Requested number for close program is over pool's limit");
177: return 0;
178: }
179: if (!toNum)
180: toNum = prg->prog_inin;
181:
182: pthread_mutex_lock(&prg->prog_mtx);
183: for (i = array_Size(prg->prog_fds) - 1; prg->prog_cnum > toNum && i > -1; i--)
184: if (array_Get(prg->prog_fds, i)) {
1.1.2.4 ! misho 185: io_pclose(array(prg->prog_fds, i, FILE*));
1.1.2.1 misho 186: array_Del(prg->prog_fds, i, 0);
187: prg->prog_cnum--;
188: ret++;
189: }
190: pthread_mutex_unlock(&prg->prog_mtx);
191:
192: return ret;
193: }
1.1.2.4 ! misho 194:
! 195:
! 196: /*
! 197: * io_popen() - ELWIX replacement of standard popen
! 198: *
! 199: * @command = command
! 200: * @type = type
! 201: * @ppid = return pid of child program
! 202: * return: NULL error or !=NULL open program
! 203: */
! 204: FILE *
! 205: io_popen(const char *command, const char *type, pid_t *ppid)
! 206: {
! 207: struct pid *cur;
! 208: FILE *iop;
! 209: int pdes[2], pid, twoway, cloexec;
! 210: char *argv[4];
! 211: struct pid *p;
! 212:
! 213: cloexec = strchr(type, 'e') != NULL;
! 214: /*
! 215: * Lite2 introduced two-way popen() pipes using _socketpair().
! 216: * FreeBSD's pipe() is bidirectional, so we use that.
! 217: */
! 218: if (strchr(type, '+')) {
! 219: twoway = 1;
! 220: type = "r+";
! 221: } else {
! 222: twoway = 0;
! 223: if ((*type != 'r' && *type != 'w') ||
! 224: (type[1] && (type[1] != 'e' || type[2])))
! 225: return (NULL);
! 226: }
! 227: if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0)
! 228: return (NULL);
! 229:
! 230: if ((cur = e_malloc(sizeof(struct pid))) == NULL) {
! 231: close(pdes[0]);
! 232: close(pdes[1]);
! 233: return (NULL);
! 234: }
! 235:
! 236: argv[0] = "sh";
! 237: argv[1] = "-c";
! 238: argv[2] = (char *)command;
! 239: argv[3] = NULL;
! 240:
! 241: THREAD_LOCK();
! 242: switch (pid = vfork()) {
! 243: case -1: /* Error. */
! 244: THREAD_UNLOCK();
! 245: close(pdes[0]);
! 246: close(pdes[1]);
! 247: e_free(cur);
! 248: return (NULL);
! 249: /* NOTREACHED */
! 250: case 0: /* Child. */
! 251: if (*type == 'r') {
! 252: /*
! 253: * The _dup2() to STDIN_FILENO is repeated to avoid
! 254: * writing to pdes[1], which might corrupt the
! 255: * parent's copy. This isn't good enough in
! 256: * general, since the _exit() is no return, so
! 257: * the compiler is free to corrupt all the local
! 258: * variables.
! 259: */
! 260: if (!cloexec)
! 261: close(pdes[0]);
! 262: if (pdes[1] != STDOUT_FILENO) {
! 263: dup2(pdes[1], STDOUT_FILENO);
! 264: if (!cloexec)
! 265: close(pdes[1]);
! 266: if (twoway)
! 267: dup2(STDOUT_FILENO, STDIN_FILENO);
! 268: } else if (twoway && (pdes[1] != STDIN_FILENO)) {
! 269: dup2(pdes[1], STDIN_FILENO);
! 270: if (cloexec)
! 271: fcntl(pdes[1], F_SETFD, 0);
! 272: } else if (cloexec)
! 273: fcntl(pdes[1], F_SETFD, 0);
! 274: } else {
! 275: if (pdes[0] != STDIN_FILENO) {
! 276: dup2(pdes[0], STDIN_FILENO);
! 277: if (!cloexec)
! 278: close(pdes[0]);
! 279: } else if (cloexec)
! 280: fcntl(pdes[0], F_SETFD, 0);
! 281: if (!cloexec)
! 282: close(pdes[1]);
! 283: }
! 284: SLIST_FOREACH(p, &pidlist, next)
! 285: close(fileno(p->fp));
! 286: execve(_PATH_BSHELL, argv, environ);
! 287: _exit(127);
! 288: /* NOTREACHED */
! 289: default:
! 290: if (ppid)
! 291: *ppid = pid;
! 292: }
! 293: THREAD_UNLOCK();
! 294:
! 295: /* Parent; assume fdopen can't fail. */
! 296: if (*type == 'r') {
! 297: iop = fdopen(pdes[0], type);
! 298: close(pdes[1]);
! 299: } else {
! 300: iop = fdopen(pdes[1], type);
! 301: close(pdes[0]);
! 302: }
! 303:
! 304: /* Link into list of file descriptors. */
! 305: cur->fp = iop;
! 306: cur->pid = pid;
! 307: THREAD_LOCK();
! 308: SLIST_INSERT_HEAD(&pidlist, cur, next);
! 309: THREAD_UNLOCK();
! 310:
! 311: return (iop);
! 312: }
! 313:
! 314: /*
! 315: * io_pclose() - ELWIX replacement of standard pclose
! 316: *
! 317: * @iop = popen handle
! 318: * return: -1 error or !=-1 pid status
! 319: */
! 320: int
! 321: io_pclose(FILE *iop)
! 322: {
! 323: struct pid *cur, *last = NULL;
! 324: int pstat;
! 325: pid_t pid;
! 326:
! 327: /*
! 328: * Find the appropriate file pointer and remove it from the list.
! 329: */
! 330: THREAD_LOCK();
! 331: SLIST_FOREACH(cur, &pidlist, next) {
! 332: if (cur->fp == iop)
! 333: break;
! 334: last = cur;
! 335: }
! 336: if (cur == NULL) {
! 337: THREAD_UNLOCK();
! 338: return (-1);
! 339: }
! 340: if (last == NULL)
! 341: SLIST_REMOVE_HEAD(&pidlist, next);
! 342: else
! 343: SLIST_REMOVE_AFTER(last, next);
! 344: THREAD_UNLOCK();
! 345:
! 346: fclose(iop);
! 347:
! 348: do {
! 349: pid = wait4(cur->pid, &pstat, 0, (struct rusage *)0);
! 350: } while (pid == -1 && errno == EINTR);
! 351:
! 352: e_free(cur);
! 353:
! 354: return (pid == -1 ? -1 : pstat);
! 355: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>