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

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>