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

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

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