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

1.1.2.5 ! misho       1: /*************************************************************************
        !             2: * (C) 2013 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
        !             3: *  by Michael Pounov <misho@elwix.org>
        !             4: *
        !             5: * $Author: misho $
        !             6: * $Id: apio.h,v 1.1.2.5 2013/12/05 15:38:14 misho Exp $
        !             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: 
        !            15: Copyright 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
        !            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: */
1.1.2.1   misho      46: #include "global.h"
                     47: 
                     48: 
                     49: extern char **environ;
                     50: 
1.1.2.2   misho      51: pio_pid_t pio_pidlist = SLIST_HEAD_INITIALIZER(pio_pidlist);
1.1.2.1   misho      52: static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
                     53: 
                     54: #define        THREAD_LOCK()   if (__isthreaded) pthread_mutex_lock(&pidlist_mutex)
                     55: #define        THREAD_UNLOCK() if (__isthreaded) pthread_mutex_unlock(&pidlist_mutex)
                     56: 
                     57: 
                     58: /*
                     59:  * e_popen() - ELWIX replacement of standard popen
                     60:  *
                     61:  * @command = command
                     62:  * @type = type
                     63:  * @ppid = return pid of child program
                     64:  * return: NULL error or !=NULL open program
                     65:  */
                     66: FILE *
                     67: e_popen(const char *command, const char *type, pid_t *ppid)
                     68: {
1.1.2.2   misho      69:        struct tagPIOPID *cur, *p;
1.1.2.1   misho      70:        FILE *iop;
                     71:        int pdes[2], pid, twoway, cloexec;
                     72:        char *argv[4];
                     73: 
1.1.2.3   misho      74:        if (!command || !type)
                     75:                return NULL;
                     76: 
1.1.2.1   misho      77:        cloexec = strchr(type, 'e') != NULL;
                     78:        /*
                     79:         * Lite2 introduced two-way popen() pipes using _socketpair().
                     80:         * FreeBSD's pipe() is bidirectional, so we use that.
                     81:         */
                     82:        if (strchr(type, '+')) {
                     83:                twoway = 1;
                     84:                type = "r+";
                     85:        } else  {
                     86:                twoway = 0;
                     87:                if ((*type != 'r' && *type != 'w') ||
                     88:                    (type[1] && (type[1] != 'e' || type[2])))
                     89:                        return (NULL);
                     90:        }
                     91:        if ((cloexec ? pipe2(pdes, O_CLOEXEC) : pipe(pdes)) < 0)
                     92:                return (NULL);
                     93: 
1.1.2.2   misho      94:        if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
1.1.2.1   misho      95:                close(pdes[0]);
                     96:                close(pdes[1]);
                     97:                return (NULL);
                     98:        }
                     99: 
                    100:        argv[0] = "sh";
                    101:        argv[1] = "-c";
                    102:        argv[2] = (char *)command;
                    103:        argv[3] = NULL;
                    104: 
                    105:        THREAD_LOCK();
                    106:        switch (pid = vfork()) {
                    107:        case -1:                        /* Error. */
                    108:                THREAD_UNLOCK();
                    109:                close(pdes[0]);
                    110:                close(pdes[1]);
                    111:                e_free(cur);
                    112:                return (NULL);
                    113:                /* NOTREACHED */
                    114:        case 0:                         /* Child. */
                    115:                if (*type == 'r') {
                    116:                        /*
                    117:                         * The _dup2() to STDIN_FILENO is repeated to avoid
                    118:                         * writing to pdes[1], which might corrupt the
                    119:                         * parent's copy.  This isn't good enough in
                    120:                         * general, since the _exit() is no return, so
                    121:                         * the compiler is free to corrupt all the local
                    122:                         * variables.
                    123:                         */
                    124:                        if (!cloexec)
                    125:                                close(pdes[0]);
                    126:                        if (pdes[1] != STDOUT_FILENO) {
                    127:                                dup2(pdes[1], STDOUT_FILENO);
                    128:                                if (!cloexec)
                    129:                                        close(pdes[1]);
                    130:                                if (twoway)
                    131:                                        dup2(STDOUT_FILENO, STDIN_FILENO);
                    132:                        } else if (twoway && (pdes[1] != STDIN_FILENO)) {
                    133:                                dup2(pdes[1], STDIN_FILENO);
                    134:                                if (cloexec)
                    135:                                        fcntl(pdes[1], F_SETFD, 0);
                    136:                        } else if (cloexec)
                    137:                                fcntl(pdes[1], F_SETFD, 0);
                    138:                } else {
                    139:                        if (pdes[0] != STDIN_FILENO) {
                    140:                                dup2(pdes[0], STDIN_FILENO);
                    141:                                if (!cloexec)
                    142:                                        close(pdes[0]);
                    143:                        } else if (cloexec)
                    144:                                fcntl(pdes[0], F_SETFD, 0);
                    145:                        if (!cloexec)
                    146:                                close(pdes[1]);
                    147:                }
                    148:                SLIST_FOREACH(p, &pio_pidlist, next)
                    149:                        close(fileno(p->fp));
                    150:                execve(_PATH_BSHELL, argv, environ);
                    151:                _exit(127);
                    152:                /* NOTREACHED */
                    153:        default:
                    154:                if (ppid)
                    155:                        *ppid = pid;
                    156:        }
                    157:        THREAD_UNLOCK();
                    158: 
                    159:        /* Parent; assume fdopen can't fail. */
                    160:        if (*type == 'r') {
                    161:                iop = fdopen(pdes[0], type);
                    162:                close(pdes[1]);
                    163:        } else {
                    164:                iop = fdopen(pdes[1], type);
                    165:                close(pdes[0]);
                    166:        }
                    167: 
                    168:        /* Link into list of file descriptors. */
                    169:        cur->fp = iop;
                    170:        cur->pid = pid;
                    171:        THREAD_LOCK();
                    172:        SLIST_INSERT_HEAD(&pio_pidlist, cur, next);
                    173:        THREAD_UNLOCK();
                    174: 
                    175:        return (iop);
                    176: }
                    177: 
                    178: /*
                    179:  * e_pclose() - ELWIX replacement of standard pclose
                    180:  *
                    181:  * @iop = popen handle
                    182:  * return: -1 error or !=-1 pid status
                    183:  */
                    184: int
                    185: e_pclose(FILE *iop)
                    186: {
1.1.2.2   misho     187:        struct tagPIOPID *cur, *last = NULL;
1.1.2.1   misho     188:        int pstat;
                    189:        pid_t pid;
                    190: 
1.1.2.3   misho     191:        if (!iop)
                    192:                return -1;
                    193: 
1.1.2.1   misho     194:        /*
                    195:         * Find the appropriate file pointer and remove it from the list.
                    196:         */
                    197:        THREAD_LOCK();
                    198:        SLIST_FOREACH(cur, &pio_pidlist, next) {
                    199:                if (cur->fp == iop)
                    200:                        break;
                    201:                last = cur;
                    202:        }
1.1.2.2   misho     203:        if (!cur) {
1.1.2.1   misho     204:                THREAD_UNLOCK();
                    205:                return (-1);
                    206:        }
1.1.2.2   misho     207:        if (!last)
1.1.2.1   misho     208:                SLIST_REMOVE_HEAD(&pio_pidlist, next);
                    209:        else
                    210:                SLIST_REMOVE_AFTER(last, next);
                    211:        THREAD_UNLOCK();
                    212: 
                    213:        fclose(iop);
                    214: 
                    215:        do {
1.1.2.2   misho     216:                pid = wait4(cur->pid, &pstat, 0, NULL);
1.1.2.1   misho     217:        } while (pid == -1 && errno == EINTR);
                    218: 
                    219:        e_free(cur);
                    220: 
                    221:        return (pid == -1 ? -1 : pstat);
                    222: }
1.1.2.3   misho     223: 
                    224: /*
                    225:  * pio_pgetpid() - Get tagPIOPID structure from file handle
                    226:  *
                    227:  * @iop = popen handle
                    228:  * return: NULL error or !=NULL tagPIOPID structure
                    229:  */
                    230: struct tagPIOPID *
                    231: pio_pgetpid(FILE * __restrict iop)
                    232: {
                    233:        struct tagPIOPID *p;
                    234: 
                    235:        if (!iop)
                    236:                return NULL;
                    237: 
                    238:        THREAD_LOCK();
                    239:        SLIST_FOREACH(p, &pio_pidlist, next)
                    240:                if (p->fp == iop)
                    241:                        break;
                    242:        THREAD_UNLOCK();
                    243: 
                    244:        return p;
                    245: }
1.1.2.4   misho     246: 
                    247: /*
                    248:  * pio_pchkpid() - Check exit status of child programs
                    249:  *
                    250:  * @pids = return tagPIOPID structures of exited programs, 
                    251:  *             if !=NULL must call array_Destroy()
                    252:  * return: -1 error or >-1 exited programs
                    253:  */
                    254: int
                    255: pio_pchkpid(array_t ** __restrict pids)
                    256: {
                    257:        register int ret = 0;
                    258:        struct tagPIOPID *p;
                    259:        array_t *pa;
                    260: 
                    261:        if (pids) {
                    262:                if (!(pa = array_Init(0)))
                    263:                        return -1;
                    264:                else
                    265:                        *pids = pa;
                    266:        }
                    267: 
                    268:        THREAD_LOCK();
                    269:        SLIST_FOREACH(p, &pio_pidlist, next)
                    270:                if (p->fp && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
                    271:                        if (pids)
                    272:                                array_Push(pa, p, 0);
                    273:                        ret++;
                    274:                }
                    275:        THREAD_UNLOCK();
                    276: 
                    277:        return ret;
                    278: }

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