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

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 $
1.1.2.6 ! misho       6: * $Id: pio.c,v 1.1.2.5 2013/12/05 15:38:48 misho Exp $
1.1.2.5   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: 
                     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:        }
1.1.2.6 ! misho      91:        if (socketpair(AF_UNIX, SOCK_STREAM | (cloexec ? O_CLOEXEC : 0), 
        !            92:                                0, pdes) < 0) {
        !            93:                LOGERR;
1.1.2.1   misho      94:                return (NULL);
1.1.2.6 ! misho      95:        }
1.1.2.1   misho      96: 
1.1.2.2   misho      97:        if (!(cur = e_malloc(sizeof(struct tagPIOPID)))) {
1.1.2.1   misho      98:                close(pdes[0]);
                     99:                close(pdes[1]);
                    100:                return (NULL);
                    101:        }
                    102: 
                    103:        argv[0] = "sh";
                    104:        argv[1] = "-c";
1.1.2.6 ! misho     105:        argv[2] = (char *) command;
1.1.2.1   misho     106:        argv[3] = NULL;
                    107: 
                    108:        THREAD_LOCK();
                    109:        switch (pid = vfork()) {
                    110:        case -1:                        /* Error. */
1.1.2.6 ! misho     111:                LOGERR;
1.1.2.1   misho     112:                THREAD_UNLOCK();
                    113:                close(pdes[0]);
                    114:                close(pdes[1]);
                    115:                e_free(cur);
                    116:                return (NULL);
                    117:                /* NOTREACHED */
                    118:        case 0:                         /* Child. */
                    119:                if (*type == 'r') {
                    120:                        /*
                    121:                         * The _dup2() to STDIN_FILENO is repeated to avoid
                    122:                         * writing to pdes[1], which might corrupt the
                    123:                         * parent's copy.  This isn't good enough in
                    124:                         * general, since the _exit() is no return, so
                    125:                         * the compiler is free to corrupt all the local
                    126:                         * variables.
                    127:                         */
                    128:                        if (!cloexec)
                    129:                                close(pdes[0]);
                    130:                        if (pdes[1] != STDOUT_FILENO) {
                    131:                                dup2(pdes[1], STDOUT_FILENO);
                    132:                                if (!cloexec)
                    133:                                        close(pdes[1]);
                    134:                                if (twoway)
                    135:                                        dup2(STDOUT_FILENO, STDIN_FILENO);
                    136:                        } else if (twoway && (pdes[1] != STDIN_FILENO)) {
                    137:                                dup2(pdes[1], STDIN_FILENO);
                    138:                                if (cloexec)
                    139:                                        fcntl(pdes[1], F_SETFD, 0);
                    140:                        } else if (cloexec)
                    141:                                fcntl(pdes[1], F_SETFD, 0);
                    142:                } else {
                    143:                        if (pdes[0] != STDIN_FILENO) {
                    144:                                dup2(pdes[0], STDIN_FILENO);
                    145:                                if (!cloexec)
                    146:                                        close(pdes[0]);
                    147:                        } else if (cloexec)
                    148:                                fcntl(pdes[0], F_SETFD, 0);
                    149:                        if (!cloexec)
                    150:                                close(pdes[1]);
                    151:                }
                    152:                SLIST_FOREACH(p, &pio_pidlist, next)
                    153:                        close(fileno(p->fp));
                    154:                execve(_PATH_BSHELL, argv, environ);
                    155:                _exit(127);
                    156:                /* NOTREACHED */
                    157:        default:
                    158:                if (ppid)
                    159:                        *ppid = pid;
                    160:        }
                    161:        THREAD_UNLOCK();
                    162: 
                    163:        /* Parent; assume fdopen can't fail. */
                    164:        if (*type == 'r') {
                    165:                iop = fdopen(pdes[0], type);
                    166:                close(pdes[1]);
                    167:        } else {
                    168:                iop = fdopen(pdes[1], type);
                    169:                close(pdes[0]);
                    170:        }
                    171: 
                    172:        /* Link into list of file descriptors. */
                    173:        cur->fp = iop;
                    174:        cur->pid = pid;
                    175:        THREAD_LOCK();
                    176:        SLIST_INSERT_HEAD(&pio_pidlist, cur, next);
                    177:        THREAD_UNLOCK();
                    178: 
                    179:        return (iop);
                    180: }
                    181: 
                    182: /*
                    183:  * e_pclose() - ELWIX replacement of standard pclose
                    184:  *
                    185:  * @iop = popen handle
                    186:  * return: -1 error or !=-1 pid status
                    187:  */
                    188: int
                    189: e_pclose(FILE *iop)
                    190: {
1.1.2.2   misho     191:        struct tagPIOPID *cur, *last = NULL;
1.1.2.1   misho     192:        int pstat;
                    193:        pid_t pid;
                    194: 
1.1.2.3   misho     195:        if (!iop)
                    196:                return -1;
                    197: 
1.1.2.1   misho     198:        /*
                    199:         * Find the appropriate file pointer and remove it from the list.
                    200:         */
                    201:        THREAD_LOCK();
                    202:        SLIST_FOREACH(cur, &pio_pidlist, next) {
                    203:                if (cur->fp == iop)
                    204:                        break;
                    205:                last = cur;
                    206:        }
1.1.2.2   misho     207:        if (!cur) {
1.1.2.1   misho     208:                THREAD_UNLOCK();
                    209:                return (-1);
                    210:        }
1.1.2.2   misho     211:        if (!last)
1.1.2.1   misho     212:                SLIST_REMOVE_HEAD(&pio_pidlist, next);
                    213:        else
                    214:                SLIST_REMOVE_AFTER(last, next);
                    215:        THREAD_UNLOCK();
                    216: 
                    217:        fclose(iop);
                    218: 
                    219:        do {
1.1.2.2   misho     220:                pid = wait4(cur->pid, &pstat, 0, NULL);
1.1.2.1   misho     221:        } while (pid == -1 && errno == EINTR);
                    222: 
                    223:        e_free(cur);
                    224: 
                    225:        return (pid == -1 ? -1 : pstat);
                    226: }
1.1.2.3   misho     227: 
                    228: /*
                    229:  * pio_pgetpid() - Get tagPIOPID structure from file handle
                    230:  *
                    231:  * @iop = popen handle
                    232:  * return: NULL error or !=NULL tagPIOPID structure
                    233:  */
                    234: struct tagPIOPID *
                    235: pio_pgetpid(FILE * __restrict iop)
                    236: {
                    237:        struct tagPIOPID *p;
                    238: 
                    239:        if (!iop)
                    240:                return NULL;
                    241: 
                    242:        THREAD_LOCK();
                    243:        SLIST_FOREACH(p, &pio_pidlist, next)
                    244:                if (p->fp == iop)
                    245:                        break;
                    246:        THREAD_UNLOCK();
                    247: 
                    248:        return p;
                    249: }
1.1.2.4   misho     250: 
                    251: /*
                    252:  * pio_pchkpid() - Check exit status of child programs
                    253:  *
                    254:  * @pids = return tagPIOPID structures of exited programs, 
                    255:  *             if !=NULL must call array_Destroy()
                    256:  * return: -1 error or >-1 exited programs
                    257:  */
                    258: int
                    259: pio_pchkpid(array_t ** __restrict pids)
                    260: {
                    261:        register int ret = 0;
                    262:        struct tagPIOPID *p;
                    263:        array_t *pa;
                    264: 
                    265:        if (pids) {
                    266:                if (!(pa = array_Init(0)))
                    267:                        return -1;
                    268:                else
                    269:                        *pids = pa;
                    270:        }
                    271: 
                    272:        THREAD_LOCK();
                    273:        SLIST_FOREACH(p, &pio_pidlist, next)
                    274:                if (p->fp && waitpid(p->pid, &p->stat, WNOHANG) > 0) {
                    275:                        if (pids)
                    276:                                array_Push(pa, p, 0);
                    277:                        ret++;
                    278:                }
                    279:        THREAD_UNLOCK();
                    280: 
                    281:        return ret;
                    282: }

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