Annotation of libelwix/src/pio.c, revision 1.4
1.2 misho 1: /*************************************************************************
2: * (C) 2013 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
3: * by Michael Pounov <misho@elwix.org>
4: *
5: * $Author: misho $
1.4 ! misho 6: * $Id: pio.c,v 1.3.20.1 2015/06/25 00:36:48 misho Exp $
1.2 misho 7: *
8: **************************************************************************
9: The ELWIX and AITNET software is distributed under the following
10: terms:
11:
12: All of the documentation and software included in the ELWIX and AITNET
13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
14:
1.4 ! misho 15: Copyright 2004 - 2015
1.2 misho 16: by Michael Pounov <misho@elwix.org>. All rights reserved.
17:
18: Redistribution and use in source and binary forms, with or without
19: modification, are permitted provided that the following conditions
20: are met:
21: 1. Redistributions of source code must retain the above copyright
22: notice, this list of conditions and the following disclaimer.
23: 2. Redistributions in binary form must reproduce the above copyright
24: notice, this list of conditions and the following disclaimer in the
25: documentation and/or other materials provided with the distribution.
26: 3. All advertising materials mentioning features or use of this software
27: must display the following acknowledgement:
28: This product includes software developed by Michael Pounov <misho@elwix.org>
29: ELWIX - Embedded LightWeight unIX and its contributors.
30: 4. Neither the name of AITNET nor the names of its contributors
31: may be used to endorse or promote products derived from this software
32: without specific prior written permission.
33:
34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37: ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44: SUCH DAMAGE.
45: */
46: #include "global.h"
47:
48:
49: extern char **environ;
50:
51: pio_pid_t pio_pidlist = SLIST_HEAD_INITIALIZER(pio_pidlist);
52: static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
53:
54: #define THREAD_LOCK() if (__isthreaded) pthread_mutex_lock(&pidlist_mutex)
55: #define THREAD_UNLOCK() if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex)
56:
57:
58: /*
59: * e_popen() - ELWIX replacement of standard popen
60: *
61: * @command = command
62: * @type = type
63: * @ppid = return pid of child program
64: * return: NULL error or !=NULL open program
65: */
66: #ifdef POPEN_STREAM
67: FILE *
68: #else
69: int
70: #endif
71: e_popen(const char *command, const char *type, pid_t *ppid)
72: {
73: struct tagPIOPID *cur, *p;
74: int pdes[2], pid, twoway, cloexec;
75: char *argv[4];
76:
77: if (!command || !type)
78: #ifdef POPEN_STREAM
79: return NULL;
80: #else
81: return -1;
82: #endif
83:
84: cloexec = strchr(type, 'e') != NULL;
85: /*
86: * Lite2 introduced two-way popen() pipes using _socketpair().
87: * FreeBSD's pipe() is bidirectional, so we use that.
88: */
89: if (strchr(type, '+')) {
90: twoway = 1;
91: type = "r+";
92: } else {
93: twoway = 0;
94: if ((*type != 'r' && *type != 'w') ||
95: (type[1] && (type[1] != 'e' || type[2])))
96: #ifdef POPEN_STREAM
97: return NULL;
98: #else
99: return -1;
100: #endif
101: }
102: if (socketpair(AF_UNIX, SOCK_STREAM | (cloexec ? O_CLOEXEC : 0),
103: 0, pdes) < 0) {
104: LOGERR;
105: #ifdef POPEN_STREAM
106: return NULL;
107: #else
108: return -1;
109: #endif
110: }
111:
112: if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
113: close(pdes[0]);
114: close(pdes[1]);
115: #ifdef POPEN_STREAM
116: return NULL;
117: #else
118: return -1;
119: #endif
120: }
121:
122: argv[0] = "sh";
123: argv[1] = "-c";
124: argv[2] = (char *) command;
125: argv[3] = NULL;
126:
127: THREAD_LOCK();
128: switch (pid = vfork()) {
129: case -1: /* Error. */
130: LOGERR;
131: THREAD_UNLOCK();
132: close(pdes[0]);
133: close(pdes[1]);
134: e_free(cur);
135: #ifdef POPEN_STREAM
136: return NULL;
137: #else
138: return -1;
139: #endif
140: /* NOTREACHED */
141: case 0: /* Child. */
142: if (*type == 'r') {
143: /*
144: * The _dup2() to STDIN_FILENO is repeated to avoid
145: * writing to pdes[1], which might corrupt the
146: * parent's copy. This isn't good enough in
147: * general, since the _exit() is no return, so
148: * the compiler is free to corrupt all the local
149: * variables.
150: */
151: if (!cloexec)
152: close(pdes[0]);
153: if (pdes[1] != STDOUT_FILENO) {
154: dup2(pdes[1], STDOUT_FILENO);
155: if (!cloexec)
156: close(pdes[1]);
157: if (twoway)
158: dup2(STDOUT_FILENO, STDIN_FILENO);
159: } else if (twoway && (pdes[1] != STDIN_FILENO)) {
160: dup2(pdes[1], STDIN_FILENO);
161: if (cloexec)
162: fcntl(pdes[1], F_SETFD, 0);
163: } else if (cloexec)
164: fcntl(pdes[1], F_SETFD, 0);
165: } else {
166: if (pdes[0] != STDIN_FILENO) {
167: dup2(pdes[0], STDIN_FILENO);
168: if (!cloexec)
169: close(pdes[0]);
170: } else if (cloexec)
171: fcntl(pdes[0], F_SETFD, 0);
172: if (!cloexec)
173: close(pdes[1]);
174: }
175: SLIST_FOREACH(p, &pio_pidlist, next)
176: #ifdef POPEN_STREAM
177: close(fileno(p->f.fp));
178: #else
179: close(p->f.fd);
180: #endif
181: execve(_PATH_BSHELL, argv, environ);
182: _exit(127);
183: /* NOTREACHED */
184: default:
185: if (ppid)
186: *ppid = pid;
187: }
188: THREAD_UNLOCK();
189:
190: /* Parent; assume fdopen can't fail. */
191: if (*type == 'r') {
192: #ifdef POPEN_STREAM
193: cur->f.fp = fdopen(pdes[0], type);
194: #else
195: cur->f.fd = pdes[0];
196: #endif
197: close(pdes[1]);
198: } else {
199: #ifdef POPEN_STREAM
200: cur->f.fp = fdopen(pdes[1], type);
201: #else
202: cur->f.fd = pdes[1];
203: #endif
204: close(pdes[0]);
205: }
206:
207: /* Link into list of file descriptors. */
208: cur->pid = pid;
209: THREAD_LOCK();
210: SLIST_INSERT_HEAD(&pio_pidlist, cur, next);
211: THREAD_UNLOCK();
212:
213: #ifdef POPEN_STREAM
214: return cur->f.fp;
215: #else
216: return cur->f.fd;
217: #endif
218: }
219:
220: /*
221: * e_pclose() - ELWIX replacement of standard pclose
222: *
223: * @iop = popen handle
224: * return: -1 error or !=-1 pid status
225: */
226: int
227: #ifdef POPEN_STREAM
228: e_pclose(FILE *iop)
229: #else
230: e_pclose(int iop)
231: #endif
232: {
233: struct tagPIOPID *cur, *last = NULL;
234: int pstat;
235: pid_t pid;
236:
237: if (!iop)
238: return -1;
239:
240: /*
241: * Find the appropriate file pointer and remove it from the list.
242: */
243: THREAD_LOCK();
244: SLIST_FOREACH(cur, &pio_pidlist, next) {
245: #ifdef POPEN_STREAM
246: if (cur->f.fp == iop)
247: #else
248: if (cur->f.fd == iop)
249: #endif
250: break;
251: last = cur;
252: }
253: if (!cur) {
254: THREAD_UNLOCK();
255: return (-1);
256: }
257: if (!last)
258: SLIST_REMOVE_HEAD(&pio_pidlist, next);
259: else
260: SLIST_REMOVE_AFTER(last, next);
261: THREAD_UNLOCK();
262:
263: #ifdef POPEN_STREAM
264: fclose(iop);
265: #else
266: close(iop);
267: #endif
268:
269: do {
270: pid = wait4(cur->pid, &pstat, 0, NULL);
271: } while (pid == -1 && errno == EINTR);
272:
273: e_free(cur);
274:
275: return (pid == -1 ? -1 : pstat);
276: }
277:
278: /*
279: * pio_pgetpid() - Get tagPIOPID structure from file handle
280: *
281: * @iop = popen handle
282: * return: NULL error or !=NULL tagPIOPID structure
283: */
284: struct tagPIOPID *
285: #ifdef POPEN_STREAM
286: pio_pgetpid(FILE *iop)
287: #else
288: pio_pgetpid(int iop)
289: #endif
290: {
291: struct tagPIOPID *p;
292:
293: if (!iop)
294: return NULL;
295:
296: THREAD_LOCK();
297: SLIST_FOREACH(p, &pio_pidlist, next)
298: #ifdef POPEN_STREAM
299: if (p->f.fp == iop)
300: #else
301: if (p->f.fd == iop)
302: #endif
303: break;
304: THREAD_UNLOCK();
305:
306: return p;
307: }
308:
309: /*
310: * pio_pchkpid() - Check exit status of child programs
311: *
312: * @pids = return tagPIOPID structures of exited programs,
313: * if !=NULL must call array_Destroy()
314: * return: -1 error or >-1 exited programs
315: */
316: int
317: pio_pchkpid(array_t ** __restrict pids)
318: {
319: register int ret = 0;
320: struct tagPIOPID *p;
1.3 misho 321: array_t *pa = NULL;
1.2 misho 322:
323: if (pids) {
324: if (!(pa = array_Init(0)))
325: return -1;
326: else
327: *pids = pa;
328: }
329:
330: THREAD_LOCK();
331: SLIST_FOREACH(p, &pio_pidlist, next)
332: #ifdef POPEN_STREAM
333: if (p->f.fp && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
334: #else
335: if (p->f.fd && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
336: #endif
337: if (pids)
338: array_Push(pa, p, 0);
339: ret++;
340: }
341: THREAD_UNLOCK();
342:
343: return ret;
344: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>