[BACK]Return to common.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / rdist

Annotation of src/usr.bin/rdist/common.c, Revision 1.41

1.41    ! jmc         1: /*     $OpenBSD: common.c,v 1.40 2019/06/28 13:35:03 deraadt Exp $     */
1.3       deraadt     2:
1.1       dm          3: /*
                      4:  * Copyright (c) 1983 Regents of the University of California.
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
1.21      millert    15:  * 3. Neither the name of the University nor the names of its contributors
1.1       dm         16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  */
                     31:
1.36      guenther   32: #include <sys/stat.h>
                     33: #include <sys/time.h>
                     34: #include <sys/wait.h>
                     35:
                     36: #include <errno.h>
                     37: #include <fcntl.h>
                     38: #include <grp.h>
                     39: #include <limits.h>
                     40: #include <paths.h>
                     41: #include <stdarg.h>
                     42: #include <stdio.h>
                     43: #include <stdlib.h>
                     44: #include <string.h>
                     45: #include <unistd.h>
                     46:
1.20      millert    47: #include "defs.h"
1.1       dm         48:
                     49: /*
                     50:  * Things common to both the client and server.
                     51:  */
                     52:
                     53: /*
                     54:  * Variables common to both client and server
                     55:  */
1.34      deraadt    56: char                   host[HOST_NAME_MAX+1];  /* Name of this host */
1.29      guenther   57: uid_t                  userid = (uid_t)-1;     /* User's UID */
                     58: gid_t                  groupid = (gid_t)-1;    /* User's GID */
1.39      millert    59: gid_t                  gidset[NGROUPS_MAX];    /* User's GID list */
                     60: int                    gidsetlen = 0;          /* Number of GIDS in list */
1.1       dm         61: char                  *homedir = NULL;         /* User's $HOME */
                     62: char                  *locuser = NULL;         /* Local User's name */
                     63: int                    isserver = FALSE;       /* We're the server */
                     64: int                    amchild = 0;            /* This PID is a child */
                     65: int                    do_fork = 1;            /* Fork child process */
                     66: char                  *currenthost = NULL;     /* Current client hostname */
                     67: char                  *progname = NULL;        /* Name of this program */
                     68: int                    rem_r = -1;             /* Client file descriptor */
                     69: int                    rem_w = -1;             /* Client file descriptor */
1.20      millert    70: volatile sig_atomic_t  contimedout = FALSE;    /* Connection timed out */
1.1       dm         71: int                    rtimeout = RTIMEOUT;    /* Response time out */
                     72: jmp_buf                        finish_jmpbuf;          /* Finish() jmp buffer */
1.2       dm         73: int                    setjmp_ok = FALSE;      /* setjmp()/longjmp() status */
1.1       dm         74: char                 **realargv;               /* Real main() argv */
                     75: int                    realargc;               /* Real main() argc */
                     76: opt_t                  options = 0;            /* Global install options */
1.20      millert    77: char                   defowner[64] = "bin";   /* Default owner */
                     78: char                   defgroup[64] = "bin";   /* Default group */
                     79:
                     80: static int sendcmdmsg(int, char *, size_t);
1.24      krw        81: static ssize_t remread(int, u_char *, size_t);
1.20      millert    82: static int remmore(void);
1.1       dm         83:
                     84: /*
                     85:  * Front end to write() that handles partial write() requests.
                     86:  */
1.31      guenther   87: ssize_t
                     88: xwrite(int fd, void *buf, size_t len)
1.1       dm         89: {
1.31      guenther   90:        size_t nleft = len;
                     91:        ssize_t nwritten;
1.12      mpech      92:        char *ptr = buf;
1.1       dm         93:
                     94:        while (nleft > 0) {
                     95:                if ((nwritten = write(fd, ptr, nleft)) <= 0) {
                     96:                        return nwritten;
                     97:                }
                     98:                nleft -= nwritten;
                     99:                ptr += nwritten;
                    100:        }
                    101:
                    102:        return len;
                    103: }
                    104:
                    105: /*
                    106:  * Do run-time initialization
                    107:  */
1.20      millert   108: int
                    109: init(int argc, char **argv, char **envp)
1.1       dm        110: {
1.38      millert   111:        struct passwd *pw;
1.12      mpech     112:        int i;
1.1       dm        113:
                    114:        /*
                    115:         * Save a copy of our argc and argv before setargs() overwrites them
                    116:         */
                    117:        realargc = argc;
1.37      mmcc      118:        realargv = xmalloc(sizeof(char *) * (argc+1));
1.1       dm        119:        for (i = 0; i < argc; i++)
1.8       millert   120:                realargv[i] = xstrdup(argv[i]);
1.1       dm        121:
                    122:        pw = getpwuid(userid = getuid());
                    123:        if (pw == NULL) {
1.16      deraadt   124:                error("Your user id (%u) is not known to this system.",
1.1       dm        125:                      getuid());
                    126:                return(-1);
                    127:        }
                    128:
1.20      millert   129:        debugmsg(DM_MISC, "UserID = %u pwname = '%s' home = '%s'\n",
1.1       dm        130:                 userid, pw->pw_name, pw->pw_dir);
1.8       millert   131:        homedir = xstrdup(pw->pw_dir);
                    132:        locuser = xstrdup(pw->pw_name);
1.1       dm        133:        groupid = pw->pw_gid;
1.39      millert   134:        gidsetlen = getgroups(NGROUPS_MAX, gidset);
1.1       dm        135:        gethostname(host, sizeof(host));
1.20      millert   136: #if 0
1.1       dm        137:        if ((cp = strchr(host, '.')) != NULL)
                    138:                *cp = CNULL;
1.20      millert   139: #endif
1.1       dm        140:
                    141:        /*
                    142:         * If we're not root, disable paranoid ownership checks
                    143:         * since normal users cannot chown() files.
                    144:         */
                    145:        if (!isserver && userid != 0) {
                    146:                FLAG_ON(options, DO_NOCHKOWNER);
                    147:                FLAG_ON(options, DO_NOCHKGROUP);
                    148:        }
                    149:
                    150:        return(0);
                    151: }
                    152:
                    153: /*
                    154:  * Finish things up before ending.
                    155:  */
1.20      millert   156: void
                    157: finish(void)
1.1       dm        158: {
                    159:        debugmsg(DM_CALL,
                    160:                 "finish() called: do_fork = %d amchild = %d isserver = %d",
                    161:                 do_fork, amchild, isserver);
1.20      millert   162:        cleanup(0);
1.1       dm        163:
                    164:        /*
                    165:         * There's no valid finish_jmpbuf for the rdist master parent.
                    166:         */
                    167:        if (!do_fork || amchild || isserver) {
1.2       dm        168:
                    169:                if (!setjmp_ok) {
                    170: #ifdef DEBUG_SETJMP
1.41    ! jmc       171:                        error("attempting longjmp() without target");
1.2       dm        172:                        abort();
                    173: #else
                    174:                        exit(1);
                    175: #endif
                    176:                }
                    177:
1.1       dm        178:                longjmp(finish_jmpbuf, 1);
                    179:                /*NOTREACHED*/
                    180:                error("Unexpected failure of longjmp() in finish()");
                    181:                exit(2);
                    182:        } else
                    183:                exit(1);
                    184: }
                    185:
                    186: /*
                    187:  * Handle lost connections
                    188:  */
1.20      millert   189: void
                    190: lostconn(void)
1.1       dm        191: {
                    192:        /* Prevent looping */
                    193:        (void) signal(SIGPIPE, SIG_IGN);
                    194:
                    195:        rem_r = rem_w = -1;     /* Ensure we don't try to send to server */
                    196:        checkhostname();
                    197:        error("Lost connection to %s",
                    198:              (currenthost) ? currenthost : "(unknown)");
                    199:
                    200:        finish();
                    201: }
                    202:
                    203: /*
                    204:  * General signal handler
                    205:  */
1.20      millert   206: void
                    207: sighandler(int sig)
1.1       dm        208: {
1.7       millert   209:        int save_errno = errno;
                    210:
1.26      deraadt   211:        /* XXX signal race */
1.1       dm        212:        debugmsg(DM_CALL, "sighandler() received signal %d\n", sig);
                    213:
                    214:        switch (sig) {
                    215:        case SIGALRM:
                    216:                contimedout = TRUE;
1.26      deraadt   217:                /* XXX signal race */
1.1       dm        218:                checkhostname();
                    219:                error("Response time out");
                    220:                finish();
                    221:                break;
                    222:
                    223:        case SIGPIPE:
1.26      deraadt   224:                /* XXX signal race */
1.1       dm        225:                lostconn();
                    226:                break;
                    227:
                    228:        case SIGFPE:
                    229:                debug = !debug;
                    230:                break;
                    231:
                    232:        case SIGHUP:
                    233:        case SIGINT:
                    234:        case SIGQUIT:
                    235:        case SIGTERM:
1.26      deraadt   236:                /* XXX signal race */
1.1       dm        237:                finish();
                    238:                break;
                    239:
                    240:        default:
1.26      deraadt   241:                /* XXX signal race */
1.1       dm        242:                fatalerr("No signal handler defined for signal %d.", sig);
                    243:        }
1.7       millert   244:        errno = save_errno;
1.1       dm        245: }
                    246:
                    247: /*
                    248:  * Function to actually send the command char and message to the
                    249:  * remote host.
                    250:  */
1.20      millert   251: static int
                    252: sendcmdmsg(int cmd, char *msg, size_t msgsize)
1.1       dm        253: {
                    254:        int len;
                    255:
                    256:        if (rem_w < 0)
                    257:                return(-1);
                    258:
                    259:        /*
                    260:         * All commands except C_NONE should have a newline
                    261:         */
                    262:        if (cmd != C_NONE && !strchr(msg + 1, '\n'))
1.18      millert   263:                (void) strlcat(msg + 1, "\n", msgsize - 1);
1.1       dm        264:
                    265:        if (cmd == C_NONE)
                    266:                len = strlen(msg);
                    267:        else {
                    268:                len = strlen(msg + 1) + 1;
                    269:                msg[0] = cmd;
                    270:        }
                    271:
                    272:        debugmsg(DM_PROTO, ">>> Cmd = %c (\\%3.3o) Msg = \"%.*s\"",
                    273:                 cmd, cmd,
                    274:                 (cmd == C_NONE) ? len-1 : len-2,
                    275:                 (cmd == C_NONE) ? msg : msg + 1);
                    276:
                    277:        return(!(xwrite(rem_w, msg, len) == len));
                    278: }
                    279:
                    280: /*
                    281:  * Send a command message to the remote host.
                    282:  * Called as sendcmd(char cmdchar, char *fmt, arg1, arg2, ...)
1.28      guenther  283:  * The fmt may be NULL, in which case there are no args.
1.1       dm        284:  */
1.20      millert   285: int
1.28      guenther  286: sendcmd(char cmd, const char *fmt, ...)
1.1       dm        287: {
                    288:        static char buf[BUFSIZ];
                    289:        va_list args;
                    290:
                    291:        va_start(args, fmt);
                    292:        if (fmt)
1.18      millert   293:                (void) vsnprintf(buf + (cmd != C_NONE),
1.20      millert   294:                                 sizeof(buf) - (cmd != C_NONE), fmt, args);
1.1       dm        295:        else
                    296:                buf[1] = CNULL;
                    297:        va_end(args);
                    298:
1.18      millert   299:        return(sendcmdmsg(cmd, buf, sizeof(buf)));
1.1       dm        300: }
                    301:
                    302: /*
                    303:  * Internal variables and routines for reading lines from the remote.
                    304:  */
                    305: static u_char rembuf[BUFSIZ];
                    306: static u_char *remptr;
1.24      krw       307: static ssize_t remleft;
1.1       dm        308:
                    309: #define remc() (--remleft < 0 ? remmore() : *remptr++)
                    310:
                    311: /*
                    312:  * Back end to remote read()
                    313:  */
1.24      krw       314: static ssize_t
                    315: remread(int fd, u_char *buf, size_t bufsiz)
1.1       dm        316: {
                    317:        return(read(fd, (char *)buf, bufsiz));
                    318: }
                    319:
1.20      millert   320: static int
                    321: remmore(void)
1.1       dm        322: {
                    323:        (void) signal(SIGALRM, sighandler);
                    324:        (void) alarm(rtimeout);
                    325:
                    326:        remleft = remread(rem_r, rembuf, sizeof(rembuf));
                    327:
                    328:        (void) alarm(0);
                    329:
                    330:        if (remleft < 0)
                    331:                return (-2);    /* error */
                    332:        if (remleft == 0)
                    333:                return (-1);    /* EOF */
                    334:        remptr = rembuf;
                    335:        remleft--;
                    336:        return (*remptr++);
                    337: }
                    338:
                    339: /*
                    340:  * Read an input line from the remote.  Return the number of bytes
                    341:  * stored (equivalent to strlen(p)).  If `cleanup' is set, EOF at
                    342:  * the beginning of a line is returned as EOF (-1); other EOFs, or
                    343:  * errors, call cleanup() or lostconn().  In other words, unless
                    344:  * the third argument is nonzero, this routine never returns failure.
                    345:  */
1.20      millert   346: int
                    347: remline(u_char *buffer, int space, int doclean)
1.1       dm        348: {
1.12      mpech     349:        int c, left = space;
                    350:        u_char *p = buffer;
1.1       dm        351:
                    352:        if (rem_r < 0) {
                    353:                error("Cannot read remote input: Remote descriptor not open.");
                    354:                return(-1);
                    355:        }
                    356:
                    357:        while (left > 0) {
                    358:                if ((c = remc()) < -1) {        /* error */
                    359:                        if (doclean) {
                    360:                                finish();
                    361:                                /*NOTREACHED*/
                    362:                        }
                    363:                        lostconn();
                    364:                        /*NOTREACHED*/
                    365:                }
                    366:                if (c == -1) {                  /* got EOF */
                    367:                        if (doclean) {
                    368:                                if (left == space)
                    369:                                        return (-1);/* signal proper EOF */
                    370:                                finish();       /* improper EOF */
                    371:                                /*NOTREACHED*/
                    372:                        }
                    373:                        lostconn();
                    374:                        /*NOTREACHED*/
                    375:                }
                    376:                if (c == '\n') {
                    377:                        *p = CNULL;
                    378:
                    379:                        if (debug) {
                    380:                                static char mbuf[BUFSIZ];
                    381:
1.20      millert   382:                                (void) snprintf(mbuf, sizeof(mbuf),
1.1       dm        383:                                        "<<< Cmd = %c (\\%3.3o) Msg = \"%s\"",
                    384:                                               buffer[0], buffer[0],
                    385:                                               buffer + 1);
                    386:
                    387:                                debugmsg(DM_PROTO, "%s", mbuf);
                    388:                        }
                    389:
                    390:                        return (space - left);
                    391:                }
                    392:                *p++ = c;
                    393:                left--;
                    394:        }
                    395:
                    396:        /* this will probably blow the entire session */
                    397:        error("remote input line too long");
                    398:        p[-1] = CNULL;          /* truncate */
                    399:        return (space);
                    400: }
                    401:
                    402: /*
                    403:  * Non-line-oriented remote read.
                    404:  */
1.24      krw       405: ssize_t
                    406: readrem(char *p, ssize_t space)
1.1       dm        407: {
                    408:        if (remleft <= 0) {
                    409:                /*
                    410:                 * Set remote time out alarm.
                    411:                 */
                    412:                (void) signal(SIGALRM, sighandler);
                    413:                (void) alarm(rtimeout);
                    414:
                    415:                remleft = remread(rem_r, rembuf, sizeof(rembuf));
                    416:
                    417:                (void) alarm(0);
                    418:                remptr = rembuf;
                    419:        }
                    420:
                    421:        if (remleft <= 0)
                    422:                return (remleft);
                    423:        if (remleft < space)
                    424:                space = remleft;
                    425:
1.20      millert   426:        memcpy(p, remptr, space);
1.1       dm        427:
                    428:        remptr += space;
                    429:        remleft -= space;
                    430:
                    431:        return (space);
                    432: }
                    433:
                    434: /*
                    435:  * Get the user name for the uid.
                    436:  */
1.20      millert   437: char *
1.29      guenther  438: getusername(uid_t uid, char *file, opt_t opts)
1.1       dm        439: {
                    440:        static char buf[100];
1.29      guenther  441:        static uid_t lastuid = (uid_t)-1;
1.39      millert   442:        const char *name;
1.1       dm        443:
                    444:        /*
                    445:         * The value of opts may have changed so we always
                    446:         * do the opts check.
                    447:         */
                    448:        if (IS_ON(opts, DO_NUMCHKOWNER)) {
1.20      millert   449:                (void) snprintf(buf, sizeof(buf), ":%u", uid);
1.1       dm        450:                return(buf);
                    451:        }
                    452:
                    453:        /*
1.39      millert   454:         * Try to avoid passwd lookup.
1.1       dm        455:         */
1.6       millert   456:        if (lastuid == uid && buf[0] != '\0' && buf[0] != ':')
1.1       dm        457:                return(buf);
                    458:
                    459:        lastuid = uid;
                    460:
1.39      millert   461:        if ((name = user_from_uid(uid, 1)) == NULL) {
1.20      millert   462:                if (IS_ON(opts, DO_DEFOWNER) && !isserver)
                    463:                        (void) strlcpy(buf, defowner, sizeof(buf));
                    464:                else {
                    465:                        message(MT_WARNING,
                    466:                                "%s: No password entry for uid %u", file, uid);
                    467:                        (void) snprintf(buf, sizeof(buf), ":%u", uid);
                    468:                }
                    469:        } else {
1.39      millert   470:                (void) strlcpy(buf, name, sizeof(buf));
1.20      millert   471:        }
1.1       dm        472:
                    473:        return(buf);
                    474: }
                    475:
                    476: /*
                    477:  * Get the group name for the gid.
                    478:  */
1.20      millert   479: char *
1.29      guenther  480: getgroupname(gid_t gid, char *file, opt_t opts)
1.1       dm        481: {
                    482:        static char buf[100];
1.29      guenther  483:        static gid_t lastgid = (gid_t)-1;
1.39      millert   484:        const char *name;
1.1       dm        485:
                    486:        /*
                    487:         * The value of opts may have changed so we always
                    488:         * do the opts check.
                    489:         */
                    490:        if (IS_ON(opts, DO_NUMCHKGROUP)) {
1.20      millert   491:                (void) snprintf(buf, sizeof(buf), ":%u", gid);
1.1       dm        492:                return(buf);
                    493:        }
                    494:
                    495:        /*
1.39      millert   496:         * Try to avoid group lookup.
1.1       dm        497:         */
1.6       millert   498:        if (lastgid == gid && buf[0] != '\0' && buf[0] != ':')
1.1       dm        499:                return(buf);
                    500:
                    501:        lastgid = gid;
                    502:
1.39      millert   503:        if ((name = group_from_gid(gid, 1)) == NULL) {
1.20      millert   504:                if (IS_ON(opts, DO_DEFGROUP) && !isserver)
                    505:                        (void) strlcpy(buf, defgroup, sizeof(buf));
                    506:                else {
                    507:                        message(MT_WARNING, "%s: No name for group %u",
                    508:                                file, gid);
                    509:                        (void) snprintf(buf, sizeof(buf), ":%u", gid);
                    510:                }
1.1       dm        511:        } else
1.39      millert   512:                (void) strlcpy(buf, name, sizeof(buf));
1.1       dm        513:
                    514:        return(buf);
                    515: }
                    516:
                    517: /*
                    518:  * Read a response from the remote host.
                    519:  */
1.20      millert   520: int
                    521: response(void)
1.1       dm        522: {
                    523:        static u_char resp[BUFSIZ];
                    524:        u_char *s;
                    525:        int n;
                    526:
                    527:        debugmsg(DM_CALL, "response() start\n");
                    528:
                    529:        n = remline(s = resp, sizeof(resp), 0);
                    530:
                    531:        n--;
                    532:        switch (*s++) {
                    533:         case C_ACK:
                    534:                debugmsg(DM_PROTO, "received ACK\n");
                    535:                return(0);
                    536:        case C_LOGMSG:
                    537:                if (n > 0) {
                    538:                        message(MT_CHANGE, "%s", s);
                    539:                        return(1);
                    540:                }
                    541:                debugmsg(DM_PROTO, "received EMPTY logmsg\n");
                    542:                return(0);
                    543:        case C_NOTEMSG:
                    544:                if (s)
                    545:                        message(MT_NOTICE, "%s", s);
                    546:                return(response());
                    547:
                    548:        default:
                    549:                s--;
                    550:                n++;
                    551:                /* fall into... */
                    552:
                    553:        case C_ERRMSG:  /* Normal error message */
                    554:                if (s)
                    555:                        message(MT_NERROR, "%s", s);
                    556:                return(-1);
                    557:
                    558:        case C_FERRMSG: /* Fatal error message */
                    559:                if (s)
                    560:                        message(MT_FERROR, "%s", s);
                    561:                finish();
1.20      millert   562:                return(-1);
1.1       dm        563:        }
                    564:        /*NOTREACHED*/
                    565: }
                    566:
                    567: /*
                    568:  * This should be in expand.c but the other routines call other modules
                    569:  * that we don't want to load in.
                    570:  *
                    571:  * Expand file names beginning with `~' into the
                    572:  * user's home directory path name. Return a pointer in buf to the
                    573:  * part corresponding to `file'.
                    574:  */
1.20      millert   575: char *
                    576: exptilde(char *ebuf, char *file, size_t ebufsize)
1.1       dm        577: {
1.38      millert   578:        struct passwd *pw;
1.18      millert   579:        char *pw_dir, *rest;
1.39      millert   580:        static char lastuser[_PW_NAME_LEN + 1];
                    581:        static char lastdir[PATH_MAX];
1.18      millert   582:        size_t len;
1.1       dm        583:
                    584:        if (*file != '~') {
1.18      millert   585: notilde:
                    586:                (void) strlcpy(ebuf, file, ebufsize);
1.1       dm        587:                return(ebuf);
                    588:        }
1.38      millert   589:        pw_dir = homedir;
1.1       dm        590:        if (*++file == CNULL) {
1.18      millert   591:                rest = NULL;
1.1       dm        592:        } else if (*file == '/') {
1.18      millert   593:                rest = file;
1.1       dm        594:        } else {
1.18      millert   595:                rest = file;
                    596:                while (*rest && *rest != '/')
                    597:                        rest++;
                    598:                if (*rest == '/')
                    599:                        *rest = CNULL;
1.1       dm        600:                else
1.18      millert   601:                        rest = NULL;
1.38      millert   602:                if (strcmp(locuser, file) != 0) {
1.39      millert   603:                        if (strcmp(lastuser, file) != 0) {
                    604:                                if ((pw = getpwnam(file)) == NULL) {
                    605:                                        error("%s: unknown user name", file);
                    606:                                        if (rest != NULL)
                    607:                                                *rest = '/';
                    608:                                        return(NULL);
                    609:                                }
                    610:                                strlcpy(lastuser, pw->pw_name, sizeof(lastuser));
                    611:                                strlcpy(lastdir, pw->pw_dir, sizeof(lastdir));
1.1       dm        612:                        }
1.39      millert   613:                        pw_dir = lastdir;
1.1       dm        614:                }
1.18      millert   615:                if (rest != NULL)
                    616:                        *rest = '/';
                    617:        }
                    618:        if ((len = strlcpy(ebuf, pw_dir, ebufsize)) >= ebufsize)
                    619:                goto notilde;
                    620:        pw_dir = ebuf + len;
                    621:        if (rest != NULL) {
                    622:                pw_dir++;
                    623:                if ((len = strlcat(ebuf, rest, ebufsize)) >= ebufsize)
                    624:                        goto notilde;
1.1       dm        625:        }
1.18      millert   626:        return(pw_dir);
1.1       dm        627: }
                    628:
                    629:
                    630:
                    631: /*
                    632:  * Set access and modify times of a given file
                    633:  */
1.20      millert   634: int
                    635: setfiletime(char *file, time_t atime, time_t mtime)
1.1       dm        636: {
                    637:        struct timeval tv[2];
                    638:
                    639:        if (atime != 0 && mtime != 0) {
                    640:                tv[0].tv_sec = atime;
                    641:                tv[1].tv_sec = mtime;
1.33      guenther  642:                tv[0].tv_usec = tv[1].tv_usec = 0;
                    643:                return (utimes(file, tv));
1.1       dm        644:        } else  /* Set to current time */
1.33      guenther  645:                return (utimes(file, NULL));
1.1       dm        646: }
                    647:
                    648: /*
                    649:  * Get version info
                    650:  */
1.20      millert   651: char *
                    652: getversion(void)
1.1       dm        653: {
                    654:        static char buff[BUFSIZ];
                    655:
1.20      millert   656:        (void) snprintf(buff, sizeof(buff),
1.1       dm        657:        "Version %s.%d (%s) - Protocol Version %d, Release %s, Patch level %d",
                    658:                       DISTVERSION, PATCHLEVEL, DISTSTATUS,
                    659:                       VERSION, DISTVERSION, PATCHLEVEL);
                    660:
                    661:        return(buff);
                    662: }
                    663:
                    664: /*
                    665:  * Execute a shell command to handle special cases.
                    666:  * This is now common to both server and client
                    667:  */
1.20      millert   668: void
                    669: runcommand(char *cmd)
1.1       dm        670: {
1.20      millert   671:        ssize_t nread;
                    672:        pid_t pid, wpid;
1.12      mpech     673:        char *cp, *s;
1.1       dm        674:        char sbuf[BUFSIZ], buf[BUFSIZ];
1.20      millert   675:        int fd[2], status;
                    676:
1.40      deraadt   677:        if (pipe(fd) == -1) {
1.1       dm        678:                error("pipe of %s failed: %s", cmd, SYSERR);
                    679:                return;
                    680:        }
                    681:
                    682:        if ((pid = fork()) == 0) {
                    683:                /*
                    684:                 * Return everything the shell commands print.
                    685:                 */
                    686:                (void) close(0);
                    687:                (void) close(1);
                    688:                (void) close(2);
                    689:                (void) open(_PATH_DEVNULL, O_RDONLY);
                    690:                (void) dup(fd[PIPE_WRITE]);
                    691:                (void) dup(fd[PIPE_WRITE]);
                    692:                (void) close(fd[PIPE_READ]);
                    693:                (void) close(fd[PIPE_WRITE]);
1.11      deraadt   694:                (void) execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
1.1       dm        695:                _exit(127);
                    696:        }
                    697:        (void) close(fd[PIPE_WRITE]);
                    698:        s = sbuf;
                    699:        *s++ = C_LOGMSG;
1.20      millert   700:        while ((nread = read(fd[PIPE_READ], buf, sizeof(buf))) > 0) {
1.1       dm        701:                cp = buf;
                    702:                do {
                    703:                        *s++ = *cp++;
                    704:                        if (cp[-1] != '\n') {
                    705:                                if (s < (char *) &sbuf[sizeof(sbuf)-1])
                    706:                                        continue;
                    707:                                *s++ = '\n';
                    708:                        }
                    709:                        /*
                    710:                         * Throw away blank lines.
                    711:                         */
                    712:                        if (s == &sbuf[2]) {
                    713:                                s--;
                    714:                                continue;
                    715:                        }
                    716:                        if (isserver)
                    717:                                (void) xwrite(rem_w, sbuf, s - sbuf);
                    718:                        else {
                    719:                                *s = CNULL;
                    720:                                message(MT_INFO, "%s", sbuf+1);
                    721:                        }
                    722:                        s = &sbuf[1];
1.20      millert   723:                } while (--nread);
1.1       dm        724:        }
                    725:        if (s > (char *) &sbuf[1]) {
                    726:                *s++ = '\n';
                    727:                if (isserver)
                    728:                        (void) xwrite(rem_w, sbuf, s - sbuf);
                    729:                else {
                    730:                        *s = CNULL;
                    731:                        message(MT_INFO, "%s", sbuf+1);
                    732:                }
                    733:        }
1.20      millert   734:        while ((wpid = wait(&status)) != pid && wpid != -1)
1.1       dm        735:                ;
1.20      millert   736:        if (wpid == -1)
1.1       dm        737:                status = -1;
                    738:        (void) close(fd[PIPE_READ]);
                    739:        if (status)
                    740:                error("shell returned %d", status);
                    741:        else if (isserver)
                    742:                ack();
                    743: }
                    744:
                    745: /*
                    746:  * Malloc with error checking
                    747:  */
1.27      guenther  748: void *
1.20      millert   749: xmalloc(size_t amt)
1.1       dm        750: {
1.27      guenther  751:        void *ptr;
1.1       dm        752:
1.27      guenther  753:        if ((ptr = malloc(amt)) == NULL)
1.25      krw       754:                fatalerr("Cannot malloc %zu bytes of memory.", amt);
1.1       dm        755:
1.27      guenther  756:        return (ptr);
1.1       dm        757: }
                    758:
                    759: /*
                    760:  * realloc with error checking
                    761:  */
1.27      guenther  762: void *
                    763: xrealloc(void *baseptr, size_t amt)
1.1       dm        764: {
1.27      guenther  765:        void *new;
1.1       dm        766:
1.27      guenther  767:        if ((new = realloc(baseptr, amt)) == NULL)
1.25      krw       768:                fatalerr("Cannot realloc %zu bytes of memory.", amt);
1.1       dm        769:
1.27      guenther  770:        return (new);
1.1       dm        771: }
                    772:
                    773: /*
                    774:  * calloc with error checking
                    775:  */
1.27      guenther  776: void *
1.20      millert   777: xcalloc(size_t num, size_t esize)
1.1       dm        778: {
1.27      guenther  779:        void *ptr;
1.1       dm        780:
1.27      guenther  781:        if ((ptr = calloc(num, esize)) == NULL)
1.25      krw       782:                fatalerr("Cannot calloc %zu * %zu = %zu bytes of memory.",
1.1       dm        783:                      num, esize, num * esize);
                    784:
1.27      guenther  785:        return (ptr);
1.8       millert   786: }
                    787:
                    788: /*
                    789:  * Strdup with error checking
                    790:  */
1.20      millert   791: char *
                    792: xstrdup(const char *str)
1.8       millert   793: {
1.20      millert   794:        size_t len = strlen(str) + 1;
1.27      guenther  795:        char *nstr = xmalloc(len);
1.8       millert   796:
1.27      guenther  797:        return (memcpy(nstr, str, len));
1.1       dm        798: }
                    799:
                    800: /*
                    801:  * Private version of basename()
                    802:  */
1.20      millert   803: char *
                    804: xbasename(char *path)
1.1       dm        805: {
1.12      mpech     806:        char *cp;
1.1       dm        807:
1.20      millert   808:        if ((cp = strrchr(path, '/')) != NULL)
1.1       dm        809:                return(cp+1);
                    810:        else
                    811:                return(path);
                    812: }
                    813:
                    814: /*
1.10      provos    815:  * Take a colon (':') separated path to a file and
1.1       dm        816:  * search until a component of that path is found and
                    817:  * return the found file name.
                    818:  */
1.20      millert   819: char *
                    820: searchpath(char *path)
1.1       dm        821: {
1.12      mpech     822:        char *file;
1.19      millert   823:        char *space;
                    824:        int found;
1.1       dm        825:        struct stat statbuf;
                    826:
1.19      millert   827:        for (found = 0; !found && (file = strsep(&path, ":")) != NULL; ) {
                    828:                if ((space = strchr(file, ' ')) != NULL)
                    829:                        *space = CNULL;
                    830:                found = stat(file, &statbuf) == 0;
                    831:                if (space)
                    832:                        *space = ' ';           /* Put back what we zapped */
1.1       dm        833:        }
1.19      millert   834:        return (file);
1.1       dm        835: }