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