Annotation of libelwix/src/pio.c, revision 1.6

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.6     ! misho       6: * $Id: pio.c,v 1.5.44.1 2019/12/30 18:10:17 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;
1.6     ! misho     238:        int pstat = 0;
1.2       misho     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>