[BACK]Return to privsep-root.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / external / bsd / dhcpcd / dist / src

Annotation of src/external/bsd/dhcpcd/dist/src/privsep-root.c, Revision 1.1.1.1.2.2

1.1.1.1.2.2! martin      1: /* SPDX-License-Identifier: BSD-2-Clause */
        !             2: /*
        !             3:  * Privilege Separation for dhcpcd, privileged actioneer
        !             4:  * Copyright (c) 2006-2020 Roy Marples <roy@marples.name>
        !             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.
        !            15:  *
        !            16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
        !            17:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            18:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            19:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
        !            20:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            21:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            22:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            23:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            24:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            25:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            26:  * SUCH DAMAGE.
        !            27:  */
        !            28:
        !            29: #include <sys/ioctl.h>
        !            30: #include <sys/socket.h>
        !            31: #include <sys/stat.h>
        !            32: #include <sys/time.h>
        !            33: #include <sys/types.h>
        !            34: #include <sys/wait.h>
        !            35:
        !            36: #include <assert.h>
        !            37: #include <errno.h>
        !            38: #include <fcntl.h>
        !            39: #include <pwd.h>
        !            40: #include <signal.h>
        !            41: #include <stdlib.h>
        !            42: #include <string.h>
        !            43: #include <unistd.h>
        !            44:
        !            45: #include "common.h"
        !            46: #include "dhcpcd.h"
        !            47: #include "eloop.h"
        !            48: #include "if.h"
        !            49: #include "logerr.h"
        !            50: #include "privsep.h"
        !            51: #include "script.h"
        !            52:
        !            53: __CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long));
        !            54:
        !            55: struct psr_error
        !            56: {
        !            57:        ssize_t psr_result;
        !            58:        int psr_errno;
        !            59:        char psr_pad[sizeof(ssize_t) - sizeof(int)];
        !            60: };
        !            61:
        !            62: struct psr_ctx {
        !            63:        struct dhcpcd_ctx *psr_ctx;
        !            64:        struct psr_error psr_error;
        !            65: };
        !            66:
        !            67: static void
        !            68: ps_root_readerrorsig(__unused int sig, void *arg)
        !            69: {
        !            70:        struct dhcpcd_ctx *ctx = arg;
        !            71:
        !            72:        eloop_exit(ctx->ps_eloop, EXIT_FAILURE);
        !            73: }
        !            74:
        !            75: static void
        !            76: ps_root_readerrorcb(void *arg)
        !            77: {
        !            78:        struct psr_ctx *psr_ctx = arg;
        !            79:        struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
        !            80:        struct psr_error *psr_error = &psr_ctx->psr_error;
        !            81:        ssize_t len;
        !            82:        int exit_code = EXIT_FAILURE;
        !            83:
        !            84:        len = read(ctx->ps_root_fd, psr_error, sizeof(*psr_error));
        !            85:        if (len == 0 || len == -1) {
        !            86:                logerr(__func__);
        !            87:                psr_error->psr_result = -1;
        !            88:                psr_error->psr_errno = errno;
        !            89:        } else if ((size_t)len < sizeof(*psr_error)) {
        !            90:                logerrx("%s: psr_error truncated", __func__);
        !            91:                psr_error->psr_result = -1;
        !            92:                psr_error->psr_errno = EINVAL;
        !            93:        } else
        !            94:                exit_code = EXIT_SUCCESS;
        !            95:
        !            96:        eloop_exit(ctx->ps_eloop, exit_code);
        !            97: }
        !            98:
        !            99: ssize_t
        !           100: ps_root_readerror(struct dhcpcd_ctx *ctx)
        !           101: {
        !           102:        struct psr_ctx psr_ctx = { .psr_ctx = ctx };
        !           103:
        !           104:        if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
        !           105:            ps_root_readerrorcb, &psr_ctx) == -1)
        !           106:        {
        !           107:                logerr(__func__);
        !           108:                return -1;
        !           109:        }
        !           110:
        !           111:        eloop_start(ctx->ps_eloop, &ctx->sigset);
        !           112:
        !           113:        errno = psr_ctx.psr_error.psr_errno;
        !           114:        return psr_ctx.psr_error.psr_result;
        !           115: }
        !           116:
        !           117: static ssize_t
        !           118: ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result)
        !           119: {
        !           120:        struct psr_error psr = {
        !           121:                .psr_result = result,
        !           122:                .psr_errno = errno,
        !           123:        };
        !           124:
        !           125: #ifdef PRIVSEP_DEBUG
        !           126:        logdebugx("%s: result %zd errno %d", __func__, result, errno);
        !           127: #endif
        !           128:
        !           129:        return write(ctx->ps_root_fd, &psr, sizeof(psr));
        !           130: }
        !           131:
        !           132: static ssize_t
        !           133: ps_root_doioctl(unsigned long req, void *data, size_t len)
        !           134: {
        !           135:        int s, err;
        !           136:
        !           137:        s = socket(PF_INET, SOCK_DGRAM, 0);
        !           138:        if (s != -1)
        !           139: #ifdef IOCTL_REQUEST_TYPE
        !           140:        {
        !           141:                ioctl_request_t reqt;
        !           142:
        !           143:                memcpy(&reqt, &req, sizeof(reqt));
        !           144:                err = ioctl(s, reqt, data, len);
        !           145:        }
        !           146: #else
        !           147:                err = ioctl(s, req, data, len);
        !           148: #endif
        !           149:        else
        !           150:                err = -1;
        !           151:        if (s != -1)
        !           152:                close(s);
        !           153:        return err;
        !           154: }
        !           155:
        !           156: static ssize_t
        !           157: ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
        !           158: {
        !           159:        const char *envbuf = data;
        !           160:        char * const argv[] = { UNCONST(data), NULL };
        !           161:        pid_t pid;
        !           162:        int status;
        !           163:
        !           164: #ifdef PRIVSEP_DEBUG
        !           165:        logdebugx("%s: IN %zu", __func__, len);
        !           166: #endif
        !           167:
        !           168:        if (len == 0)
        !           169:                return 0;
        !           170:
        !           171:        /* Script is the first one, find the environment buffer. */
        !           172:        while (*envbuf != '\0') {
        !           173:                if (len == 0)
        !           174:                        return EINVAL;
        !           175:                envbuf++;
        !           176:                len--;
        !           177:        }
        !           178:
        !           179:        if (len != 0) {
        !           180:                envbuf++;
        !           181:                len--;
        !           182:        }
        !           183:
        !           184: #ifdef PRIVSEP_DEBUG
        !           185:        logdebugx("%s: run script: %s", __func__, argv[0]);
        !           186: #endif
        !           187:
        !           188:        if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)
        !           189:                return -1;
        !           190:
        !           191:        pid = script_exec(ctx, argv, ctx->script_env);
        !           192:        if (pid == -1)
        !           193:                return -1;
        !           194:        /* Wait for the script to finish */
        !           195:        while (waitpid(pid, &status, 0) == -1) {
        !           196:                if (errno != EINTR) {
        !           197:                        logerr(__func__);
        !           198:                        status = 0;
        !           199:                        break;
        !           200:                }
        !           201:        }
        !           202:        return status;
        !           203: }
        !           204:
        !           205: #if defined(__linux__) && !defined(st_mtimespec)
        !           206: #define        st_atimespec  st_atim
        !           207: #define        st_mtimespec  st_mtim
        !           208: #endif
        !           209: ssize_t
        !           210: ps_root_docopychroot(struct dhcpcd_ctx *ctx, const char *file)
        !           211: {
        !           212:
        !           213:        char path[PATH_MAX], buf[BUFSIZ], *slash;
        !           214:        struct stat from_sb, to_sb;
        !           215:        int from_fd, to_fd;
        !           216:        ssize_t rcount, wcount, total;
        !           217: #if defined(BSD) || defined(__linux__)
        !           218:        struct timespec ts[2];
        !           219: #else
        !           220:        struct timeval tv[2];
        !           221: #endif
        !           222:
        !           223:        if (snprintf(path, sizeof(path), "%s/%s",
        !           224:            ctx->ps_user->pw_dir, file) == -1)
        !           225:                return -1;
        !           226:        if (stat(file, &from_sb) == -1)
        !           227:                return -1;
        !           228:        if (stat(path, &to_sb) == 0) {
        !           229: #if defined(BSD) || defined(__linux__)
        !           230:                if (from_sb.st_mtimespec.tv_sec == to_sb.st_mtimespec.tv_sec &&
        !           231:                    from_sb.st_mtimespec.tv_nsec == to_sb.st_mtimespec.tv_nsec)
        !           232:                        return 0;
        !           233: #else
        !           234:                if (from_sb.st_mtime == to_sb.st_mtime)
        !           235:                        return 0;
        !           236: #endif
        !           237:        } else {
        !           238:                /* Ensure directory exists */
        !           239:                slash = strrchr(path, '/');
        !           240:                if (slash != NULL) {
        !           241:                        *slash = '\0';
        !           242:                        ps_mkdir(path);
        !           243:                        *slash = '/';
        !           244:                }
        !           245:        }
        !           246:
        !           247:        if (unlink(path) == -1 && errno != ENOENT)
        !           248:                return -1;
        !           249:        if ((from_fd = open(file, O_RDONLY, 0)) == -1)
        !           250:                return -1;
        !           251:        if ((to_fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0555)) == -1)
        !           252:                return -1;
        !           253:
        !           254:        total = 0;
        !           255:        while ((rcount = read(from_fd, buf, sizeof(buf))) > 0) {
        !           256:                wcount = write(to_fd, buf, (size_t)rcount);
        !           257:                if (wcount != rcount) {
        !           258:                        total = -1;
        !           259:                        break;
        !           260:                }
        !           261:                total += wcount;
        !           262:        }
        !           263:
        !           264: #if defined(BSD) || defined(__linux__)
        !           265:        ts[0] = from_sb.st_atimespec;
        !           266:        ts[1] = from_sb.st_mtimespec;
        !           267:        if (futimens(to_fd, ts) == -1)
        !           268:                total = -1;
        !           269: #else
        !           270:        tv[0].tv_sec = from_sb.st_atime;
        !           271:        tv[0].tv_usec = 0;
        !           272:        tv[1].tv_sec = from_sb.st_mtime;
        !           273:        tv[1].tv_usec = 0;
        !           274:        if (futimes(to_fd, tv) == -1)
        !           275:                total = -1;
        !           276: #endif
        !           277:        close(from_fd);
        !           278:        close(to_fd);
        !           279:
        !           280:        return total;
        !           281: }
        !           282:
        !           283: static ssize_t
        !           284: ps_root_dofileop(struct dhcpcd_ctx *ctx, void *data, size_t len, uint8_t op)
        !           285: {
        !           286:        char *path = data;
        !           287:        size_t plen;
        !           288:
        !           289:        if (len < sizeof(plen)) {
        !           290:                errno = EINVAL;
        !           291:                return -1;
        !           292:        }
        !           293:
        !           294:        memcpy(&plen, path, sizeof(plen));
        !           295:        path += sizeof(plen);
        !           296:        if (sizeof(plen) + plen > len) {
        !           297:                errno = EINVAL;
        !           298:                return -1;
        !           299:        }
        !           300:
        !           301:        switch(op) {
        !           302:        case PS_COPY:
        !           303:                return ps_root_docopychroot(ctx, path);
        !           304:        case PS_UNLINK:
        !           305:                return (ssize_t)unlink(path);
        !           306:        default:
        !           307:                errno = ENOTSUP;
        !           308:                return -1;
        !           309:        }
        !           310: }
        !           311:
        !           312: static ssize_t
        !           313: ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
        !           314: {
        !           315:        struct dhcpcd_ctx *ctx = arg;
        !           316:        uint8_t cmd;
        !           317:        struct ps_process *psp;
        !           318:        struct iovec *iov = msg->msg_iov;
        !           319:        void *data = iov->iov_base;
        !           320:        size_t len = iov->iov_len;
        !           321:        ssize_t err;
        !           322:
        !           323:        cmd = (uint8_t)(psm->ps_cmd & ~(PS_START | PS_STOP | PS_DELETE));
        !           324:        psp = ps_findprocess(ctx, &psm->ps_id);
        !           325:
        !           326: #ifdef PRIVSEP_DEBUG
        !           327:        logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
        !           328: #endif
        !           329:
        !           330:        if ((!(psm->ps_cmd & PS_START) || cmd == PS_BPF_ARP_ADDR) &&
        !           331:            psp != NULL)
        !           332:        {
        !           333:                if (psm->ps_cmd & PS_STOP) {
        !           334:                        int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
        !           335:
        !           336:                        ps_freeprocess(psp);
        !           337:                        return ret;
        !           338:                }
        !           339:                return ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
        !           340:        }
        !           341:
        !           342:        if (psm->ps_cmd & (PS_STOP | PS_DELETE) && psp == NULL)
        !           343:                return 0;
        !           344:
        !           345:        /* All these should just be PS_START */
        !           346:        switch (cmd) {
        !           347: #ifdef INET
        !           348: #ifdef ARP
        !           349:        case PS_BPF_ARP:        /* FALLTHROUGH */
        !           350: #endif
        !           351:        case PS_BPF_BOOTP:
        !           352:                return ps_bpf_cmd(ctx, psm, msg);
        !           353: #endif
        !           354: #ifdef INET
        !           355:        case PS_BOOTP:
        !           356:                return ps_inet_cmd(ctx, psm, msg);
        !           357: #endif
        !           358: #ifdef INET6
        !           359: #ifdef DHCP6
        !           360:        case PS_DHCP6:  /* FALLTHROUGH */
        !           361: #endif
        !           362:        case PS_ND:
        !           363:                return ps_inet_cmd(ctx, psm, msg);
        !           364: #endif
        !           365:        default:
        !           366:                break;
        !           367:        }
        !           368:
        !           369:        assert(msg->msg_iovlen == 1);
        !           370:
        !           371:        /* Reset errno */
        !           372:        errno = 0;
        !           373:
        !           374:        switch (psm->ps_cmd) {
        !           375:        case PS_IOCTL:
        !           376:                err = ps_root_doioctl(psm->ps_flags, data, len);
        !           377:                break;
        !           378:        case PS_SCRIPT:
        !           379:                err = ps_root_run_script(ctx, data, len);
        !           380:                break;
        !           381:        case PS_COPY:   /* FALLTHROUGH */
        !           382:        case PS_UNLINK:
        !           383:                err = ps_root_dofileop(ctx, data, len, psm->ps_cmd);
        !           384:                break;
        !           385:        default:
        !           386:                err = ps_root_os(psm, msg);
        !           387:                break;
        !           388:        }
        !           389:
        !           390:        return ps_root_writeerror(ctx, err);
        !           391: }
        !           392:
        !           393: /* Receive from state engine, do an action. */
        !           394: static void
        !           395: ps_root_recvmsg(void *arg)
        !           396: {
        !           397:        struct dhcpcd_ctx *ctx = arg;
        !           398:
        !           399:        if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1 &&
        !           400:            errno != ECONNRESET)
        !           401:                logerr(__func__);
        !           402: }
        !           403:
        !           404: static int
        !           405: ps_root_startcb(void *arg)
        !           406: {
        !           407:        struct dhcpcd_ctx *ctx = arg;
        !           408:
        !           409:        setproctitle("[privileged actioneer]");
        !           410:        ctx->ps_root_pid = getpid();
        !           411:        return 0;
        !           412: }
        !           413:
        !           414: static void
        !           415: ps_root_signalcb(int sig, void *arg)
        !           416: {
        !           417:        struct dhcpcd_ctx *ctx = arg;
        !           418:
        !           419:        /* Ignore SIGINT, respect PS_STOP command or SIGTERM. */
        !           420:        if (sig == SIGINT)
        !           421:                return;
        !           422:
        !           423:        logerrx("process %d unexpectedly terminating on signal %d",
        !           424:            getpid(), sig);
        !           425:        if (ctx->ps_root_pid == getpid()) {
        !           426:                shutdown(ctx->ps_root_fd, SHUT_RDWR);
        !           427:                shutdown(ctx->ps_data_fd, SHUT_RDWR);
        !           428:        }
        !           429:        eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
        !           430: }
        !           431:
        !           432: static ssize_t
        !           433: ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
        !           434: {
        !           435:        struct dhcpcd_ctx *ctx = arg;
        !           436:        ssize_t err;
        !           437:
        !           438:        err = ps_bpf_dispatch(ctx, psm, msg);
        !           439:        if (err == -1 && errno == ENOTSUP)
        !           440:                err = ps_inet_dispatch(ctx, psm, msg);
        !           441:        return err;
        !           442: }
        !           443:
        !           444: static void
        !           445: ps_root_dispatch(void *arg)
        !           446: {
        !           447:        struct dhcpcd_ctx *ctx = arg;
        !           448:
        !           449:        if (ps_recvpsmsg(ctx, ctx->ps_data_fd, ps_root_dispatchcb, ctx) == -1)
        !           450:                logerr(__func__);
        !           451: }
        !           452:
        !           453: pid_t
        !           454: ps_root_start(struct dhcpcd_ctx *ctx)
        !           455: {
        !           456:        int fd[2];
        !           457:        pid_t pid;
        !           458:
        !           459: #define        SOCK_CXNB       SOCK_CLOEXEC | SOCK_NONBLOCK
        !           460:        if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1)
        !           461:                return -1;
        !           462:
        !           463:        pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
        !           464:            ps_root_recvmsg, NULL, ctx,
        !           465:            ps_root_startcb, ps_root_signalcb, 0);
        !           466:
        !           467:        if (pid == 0) {
        !           468:                ctx->ps_data_fd = fd[1];
        !           469:                close(fd[0]);
        !           470:                return 0;
        !           471:        } else if (pid == -1)
        !           472:                return -1;
        !           473:
        !           474:        ctx->ps_data_fd = fd[0];
        !           475:        close(fd[1]);
        !           476:        if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
        !           477:            ps_root_dispatch, ctx) == -1)
        !           478:                logerr(__func__);
        !           479:
        !           480:        if ((ctx->ps_eloop = eloop_new()) == NULL) {
        !           481:                logerr(__func__);
        !           482:                return -1;
        !           483:        }
        !           484:
        !           485:        if (eloop_signal_set_cb(ctx->ps_eloop,
        !           486:            dhcpcd_signals, dhcpcd_signals_len,
        !           487:            ps_root_readerrorsig, ctx) == -1)
        !           488:        {
        !           489:                logerr(__func__);
        !           490:                return -1;
        !           491:        }
        !           492:        return pid;
        !           493: }
        !           494:
        !           495: int
        !           496: ps_root_stop(struct dhcpcd_ctx *ctx)
        !           497: {
        !           498:
        !           499:        return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd);
        !           500: }
        !           501:
        !           502: ssize_t
        !           503: ps_root_script(const struct interface *ifp, const void *data, size_t len)
        !           504: {
        !           505:        char buf[PS_BUFLEN], *p = buf;
        !           506:        size_t blen = PS_BUFLEN, slen = strlen(ifp->options->script) + 1;
        !           507:
        !           508: #ifdef PRIVSEP_DEBUG
        !           509:        logdebugx("%s: sending script: %zu %s len %zu",
        !           510:            __func__, slen, ifp->options->script, len);
        !           511: #endif
        !           512:
        !           513:        if (slen > blen) {
        !           514:                errno = ENOBUFS;
        !           515:                return -1;
        !           516:        }
        !           517:        memcpy(p, ifp->options->script, slen);
        !           518:        p += slen;
        !           519:        blen -= slen;
        !           520:
        !           521:        if (len > blen) {
        !           522:                errno = ENOBUFS;
        !           523:                return -1;
        !           524:        }
        !           525:        memcpy(p, data, len);
        !           526:
        !           527: #ifdef PRIVSEP_DEBUG
        !           528:        logdebugx("%s: sending script data: %zu", __func__, slen + len);
        !           529: #endif
        !           530:
        !           531:        if (ps_sendcmd(ifp->ctx, ifp->ctx->ps_root_fd, PS_SCRIPT, 0,
        !           532:            buf, slen + len) == -1)
        !           533:                return -1;
        !           534:
        !           535:        return ps_root_readerror(ifp->ctx);
        !           536: }
        !           537:
        !           538: ssize_t
        !           539: ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
        !           540:     size_t len)
        !           541: {
        !           542: #ifdef IOCTL_REQUEST_TYPE
        !           543:        unsigned long ulreq = 0;
        !           544:
        !           545:        memcpy(&ulreq, &req, sizeof(req));
        !           546:        if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1)
        !           547:                return -1;
        !           548: #else
        !           549:        if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1)
        !           550:                return -1;
        !           551: #endif
        !           552:        return ps_root_readerror(ctx);
        !           553: }
        !           554:
        !           555: static ssize_t
        !           556: ps_root_fileop(struct dhcpcd_ctx *ctx, const char *path, uint8_t op)
        !           557: {
        !           558:        char buf[PATH_MAX], *p = buf;
        !           559:        size_t plen = strlen(path) + 1;
        !           560:        size_t len = sizeof(plen) + plen;
        !           561:
        !           562:        if (len > sizeof(buf)) {
        !           563:                errno = ENOBUFS;
        !           564:                return -1;
        !           565:        }
        !           566:
        !           567:        memcpy(p, &plen, sizeof(plen));
        !           568:        p += sizeof(plen);
        !           569:        memcpy(p, path, plen);
        !           570:
        !           571:        if (ps_sendcmd(ctx, ctx->ps_root_fd, op, 0, buf, len) == -1)
        !           572:                return -1;
        !           573:        return ps_root_readerror(ctx);
        !           574: }
        !           575:
        !           576:
        !           577: ssize_t
        !           578: ps_root_copychroot(struct dhcpcd_ctx *ctx, const char *path)
        !           579: {
        !           580:
        !           581:        return ps_root_fileop(ctx, path, PS_COPY);
        !           582: }
        !           583:
        !           584: ssize_t
        !           585: ps_root_unlink(struct dhcpcd_ctx *ctx, const char *path)
        !           586: {
        !           587:
        !           588:        return ps_root_fileop(ctx, path, PS_UNLINK);
        !           589: }

CVSweb <webmaster@jp.NetBSD.org>