Annotation of libelwix/src/pio.c, revision 1.5
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.5 ! misho 6: * $Id: pio.c,v 1.4.4.1 2016/05/14 11:31:38 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);
1.5 ! misho 52: #ifdef HAVE_LIBPTHREAD
1.2 misho 53: static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
54:
1.5 ! misho 55: #define THREAD_LOCK() pthread_mutex_lock(&pidlist_mutex)
! 56: #define THREAD_UNLOCK() pthread_mutex_unlock(&pidlist_mutex)
! 57: #else
! 58: #define THREAD_LOCK()
! 59: #define THREAD_UNLOCK()
! 60: #endif
1.2 misho 61:
62: /*
63: * e_popen() - ELWIX replacement of standard popen
64: *
65: * @command = command
66: * @type = type
67: * @ppid = return pid of child program
68: * return: NULL error or !=NULL open program
69: */
70: #ifdef POPEN_STREAM
71: FILE *
72: #else
73: int
74: #endif
75: e_popen(const char *command, const char *type, pid_t *ppid)
76: {
77: struct tagPIOPID *cur, *p;
78: int pdes[2], pid, twoway, cloexec;
79: char *argv[4];
80:
81: if (!command || !type)
82: #ifdef POPEN_STREAM
83: return NULL;
84: #else
85: return -1;
86: #endif
87:
88: cloexec = strchr(type, 'e') != NULL;
89: /*
90: * Lite2 introduced two-way popen() pipes using _socketpair().
91: * FreeBSD's pipe() is bidirectional, so we use that.
92: */
93: if (strchr(type, '+')) {
94: twoway = 1;
95: type = "r+";
96: } else {
97: twoway = 0;
98: if ((*type != 'r' && *type != 'w') ||
99: (type[1] && (type[1] != 'e' || type[2])))
100: #ifdef POPEN_STREAM
101: return NULL;
102: #else
103: return -1;
104: #endif
105: }
106: if (socketpair(AF_UNIX, SOCK_STREAM | (cloexec ? O_CLOEXEC : 0),
107: 0, pdes) < 0) {
108: LOGERR;
109: #ifdef POPEN_STREAM
110: return NULL;
111: #else
112: return -1;
113: #endif
114: }
115:
116: if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
117: close(pdes[0]);
118: close(pdes[1]);
119: #ifdef POPEN_STREAM
120: return NULL;
121: #else
122: return -1;
123: #endif
124: }
125:
126: argv[0] = "sh";
127: argv[1] = "-c";
128: argv[2] = (char *) command;
129: argv[3] = NULL;
130:
131: THREAD_LOCK();
132: switch (pid = vfork()) {
133: case -1: /* Error. */
134: LOGERR;
135: THREAD_UNLOCK();
136: close(pdes[0]);
137: close(pdes[1]);
138: e_free(cur);
139: #ifdef POPEN_STREAM
140: return NULL;
141: #else
142: return -1;
143: #endif
144: /* NOTREACHED */
145: case 0: /* Child. */
146: if (*type == 'r') {
147: /*
148: * The _dup2() to STDIN_FILENO is repeated to avoid
149: * writing to pdes[1], which might corrupt the
150: * parent's copy. This isn't good enough in
151: * general, since the _exit() is no return, so
152: * the compiler is free to corrupt all the local
153: * variables.
154: */
155: if (!cloexec)
156: close(pdes[0]);
157: if (pdes[1] != STDOUT_FILENO) {
158: dup2(pdes[1], STDOUT_FILENO);
159: if (!cloexec)
160: close(pdes[1]);
161: if (twoway)
162: dup2(STDOUT_FILENO, STDIN_FILENO);
163: } else if (twoway && (pdes[1] != STDIN_FILENO)) {
164: dup2(pdes[1], STDIN_FILENO);
165: if (cloexec)
166: fcntl(pdes[1], F_SETFD, 0);
167: } else if (cloexec)
168: fcntl(pdes[1], F_SETFD, 0);
169: } else {
170: if (pdes[0] != STDIN_FILENO) {
171: dup2(pdes[0], STDIN_FILENO);
172: if (!cloexec)
173: close(pdes[0]);
174: } else if (cloexec)
175: fcntl(pdes[0], F_SETFD, 0);
176: if (!cloexec)
177: close(pdes[1]);
178: }
179: SLIST_FOREACH(p, &pio_pidlist, next)
180: #ifdef POPEN_STREAM
181: close(fileno(p->f.fp));
182: #else
183: close(p->f.fd);
184: #endif
185: execve(_PATH_BSHELL, argv, environ);
186: _exit(127);
187: /* NOTREACHED */
188: default:
189: if (ppid)
190: *ppid = pid;
191: }
192: THREAD_UNLOCK();
193:
194: /* Parent; assume fdopen can't fail. */
195: if (*type == 'r') {
196: #ifdef POPEN_STREAM
197: cur->f.fp = fdopen(pdes[0], type);
198: #else
199: cur->f.fd = pdes[0];
200: #endif
201: close(pdes[1]);
202: } else {
203: #ifdef POPEN_STREAM
204: cur->f.fp = fdopen(pdes[1], type);
205: #else
206: cur->f.fd = pdes[1];
207: #endif
208: close(pdes[0]);
209: }
210:
211: /* Link into list of file descriptors. */
212: cur->pid = pid;
213: THREAD_LOCK();
214: SLIST_INSERT_HEAD(&pio_pidlist, cur, next);
215: THREAD_UNLOCK();
216:
217: #ifdef POPEN_STREAM
218: return cur->f.fp;
219: #else
220: return cur->f.fd;
221: #endif
222: }
223:
224: /*
225: * e_pclose() - ELWIX replacement of standard pclose
226: *
227: * @iop = popen handle
228: * return: -1 error or !=-1 pid status
229: */
230: int
231: #ifdef POPEN_STREAM
232: e_pclose(FILE *iop)
233: #else
234: e_pclose(int iop)
235: #endif
236: {
237: struct tagPIOPID *cur, *last = NULL;
238: int pstat;
239: pid_t pid;
240:
241: if (!iop)
242: return -1;
243:
244: /*
245: * Find the appropriate file pointer and remove it from the list.
246: */
247: THREAD_LOCK();
248: SLIST_FOREACH(cur, &pio_pidlist, next) {
249: #ifdef POPEN_STREAM
250: if (cur->f.fp == iop)
251: #else
252: if (cur->f.fd == iop)
253: #endif
254: break;
255: last = cur;
256: }
257: if (!cur) {
258: THREAD_UNLOCK();
259: return (-1);
260: }
261: if (!last)
262: SLIST_REMOVE_HEAD(&pio_pidlist, next);
263: else
264: SLIST_REMOVE_AFTER(last, next);
265: THREAD_UNLOCK();
266:
267: #ifdef POPEN_STREAM
268: fclose(iop);
269: #else
270: close(iop);
271: #endif
272:
273: do {
274: pid = wait4(cur->pid, &pstat, 0, NULL);
275: } while (pid == -1 && errno == EINTR);
276:
277: e_free(cur);
278:
279: return (pid == -1 ? -1 : pstat);
280: }
281:
282: /*
283: * pio_pgetpid() - Get tagPIOPID structure from file handle
284: *
285: * @iop = popen handle
286: * return: NULL error or !=NULL tagPIOPID structure
287: */
288: struct tagPIOPID *
289: #ifdef POPEN_STREAM
290: pio_pgetpid(FILE *iop)
291: #else
292: pio_pgetpid(int iop)
293: #endif
294: {
295: struct tagPIOPID *p;
296:
297: if (!iop)
298: return NULL;
299:
300: THREAD_LOCK();
301: SLIST_FOREACH(p, &pio_pidlist, next)
302: #ifdef POPEN_STREAM
303: if (p->f.fp == iop)
304: #else
305: if (p->f.fd == iop)
306: #endif
307: break;
308: THREAD_UNLOCK();
309:
310: return p;
311: }
312:
313: /*
314: * pio_pchkpid() - Check exit status of child programs
315: *
316: * @pids = return tagPIOPID structures of exited programs,
317: * if !=NULL must call array_Destroy()
318: * return: -1 error or >-1 exited programs
319: */
320: int
321: pio_pchkpid(array_t ** __restrict pids)
322: {
323: register int ret = 0;
324: struct tagPIOPID *p;
1.3 misho 325: array_t *pa = NULL;
1.2 misho 326:
327: if (pids) {
328: if (!(pa = array_Init(0)))
329: return -1;
330: else
331: *pids = pa;
332: }
333:
334: THREAD_LOCK();
335: SLIST_FOREACH(p, &pio_pidlist, next)
336: #ifdef POPEN_STREAM
337: if (p->f.fp && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
338: #else
339: if (p->f.fd && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
340: #endif
341: if (pids)
342: array_Push(pa, p, 0);
343: ret++;
344: }
345: THREAD_UNLOCK();
346:
347: return ret;
348: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>