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

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

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