Annotation of libaitio/src/exec.c, revision 1.1.2.5
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;
1.1.2.5 ! misho 143: ret = -1;
1.1.2.1 misho 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);
1.1.2.5 ! misho 148: ret = -1;
1.1.2.4 misho 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>