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>