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

Annotation of src/usr.bin/cvs/client.c, Revision 1.1

1.1     ! jfb         1: /*     $OpenBSD$       */
        !             2: /*
        !             3:  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
        !             4:  * All rights reserved.
        !             5:  *
        !             6:  * Redistribution and use in source and binary forms, with or without
        !             7:  * modification, are permitted provided that the following conditions
        !             8:  * are met:
        !             9:  *
        !            10:  * 1. Redistributions of source code must retain the above copyright
        !            11:  *    notice, this list of conditions and the following disclaimer.
        !            12:  * 2. The name of the author may not be used to endorse or promote products
        !            13:  *    derived from this software without specific prior written permission.
        !            14:  *
        !            15:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
        !            16:  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
        !            17:  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
        !            18:  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
        !            19:  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
        !            20:  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
        !            21:  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
        !            22:  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
        !            23:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
        !            24:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
        !            25:  */
        !            26:
        !            27: #include <sys/types.h>
        !            28: #include <sys/stat.h>
        !            29:
        !            30: #include <fcntl.h>
        !            31: #include <stdio.h>
        !            32: #include <errno.h>
        !            33: #include <stdlib.h>
        !            34: #include <unistd.h>
        !            35: #include <signal.h>
        !            36: #include <string.h>
        !            37: #include <sysexits.h>
        !            38: #ifdef CVS_ZLIB
        !            39: #include <zlib.h>
        !            40: #endif
        !            41:
        !            42: #include "cvs.h"
        !            43: #include "log.h"
        !            44:
        !            45:
        !            46:
        !            47: extern int   verbosity;
        !            48: extern int   cvs_compress;
        !            49: extern char *cvs_rsh;
        !            50: extern int   cvs_trace;
        !            51: extern int   cvs_nolog;
        !            52: extern int   cvs_readonly;
        !            53:
        !            54: extern struct cvsroot *cvs_root;
        !            55:
        !            56:
        !            57:
        !            58:
        !            59: static int  cvs_client_sendinfo (void);
        !            60: static int  cvs_client_initlog  (void);
        !            61:
        !            62:
        !            63:
        !            64: static int    cvs_server_infd = -1;
        !            65: static int    cvs_server_outfd = -1;
        !            66: static FILE  *cvs_server_in;
        !            67: static FILE  *cvs_server_out;
        !            68:
        !            69: /* protocol log files, if the CVS_CLIENT_LOG environment variable is used */
        !            70: static FILE  *cvs_server_inlog;
        !            71: static FILE  *cvs_server_outlog;
        !            72:
        !            73: static char   cvs_client_buf[4096];
        !            74:
        !            75: /* last directory sent with `Directory' */
        !            76: static char   cvs_lastdir[MAXPATHLEN] = "";
        !            77:
        !            78:
        !            79:
        !            80: /*
        !            81:  * cvs_client_connect()
        !            82:  *
        !            83:  * Open a client connection to the cvs server whose address is given in
        !            84:  * the global <cvs_root> variable.  The method used to connect depends on the
        !            85:  * setting of the CVS_RSH variable.
        !            86:  */
        !            87:
        !            88: int
        !            89: cvs_client_connect(void)
        !            90: {
        !            91:        int argc, infd[2], outfd[2];
        !            92:        pid_t pid;
        !            93:        char *argv[16], *cvs_server_cmd;
        !            94:
        !            95:        if (pipe(infd) == -1) {
        !            96:                cvs_log(LP_ERRNO,
        !            97:                    "failed to create input pipe for client connection");
        !            98:                return (-1);
        !            99:        }
        !           100:
        !           101:        if (pipe(outfd) == -1) {
        !           102:                cvs_log(LP_ERRNO,
        !           103:                    "failed to create output pipe for client connection");
        !           104:                (void)close(infd[0]);
        !           105:                (void)close(infd[1]);
        !           106:                return (-1);
        !           107:        }
        !           108:
        !           109:        pid = fork();
        !           110:        if (pid == -1) {
        !           111:                cvs_log(LP_ERRNO, "failed to fork for cvs server connection");
        !           112:                return (-1);
        !           113:        }
        !           114:        if (pid == 0) {
        !           115:                if ((dup2(infd[0], STDIN_FILENO) == -1) ||
        !           116:                    (dup2(outfd[1], STDOUT_FILENO) == -1)) {
        !           117:                        cvs_log(LP_ERRNO,
        !           118:                            "failed to setup standard streams for cvs server");
        !           119:                        return (-1);
        !           120:                }
        !           121:                (void)close(infd[1]);
        !           122:                (void)close(outfd[0]);
        !           123:
        !           124:                argc = 0;
        !           125:                argv[argc++] = cvs_rsh;
        !           126:
        !           127:                if (cvs_root->cr_user != NULL) {
        !           128:                        argv[argc++] = "-l";
        !           129:                        argv[argc++] = cvs_root->cr_user;
        !           130:                }
        !           131:
        !           132:
        !           133:                cvs_server_cmd = getenv("CVS_SERVER");
        !           134:                if (cvs_server_cmd == NULL)
        !           135:                        cvs_server_cmd = "cvs";
        !           136:
        !           137:                argv[argc++] = cvs_root->cr_host;
        !           138:                argv[argc++] = cvs_server_cmd;
        !           139:                argv[argc++] = "server";
        !           140:                argv[argc] = NULL;
        !           141:
        !           142:                execvp(argv[0], argv);
        !           143:                cvs_log(LP_ERRNO, "failed to exec");
        !           144:                exit(EX_OSERR);
        !           145:        }
        !           146:
        !           147:        /* we are the parent */
        !           148:        cvs_server_infd = infd[1];
        !           149:        cvs_server_outfd = outfd[0];
        !           150:
        !           151:        cvs_server_in = fdopen(cvs_server_infd, "w");
        !           152:        if (cvs_server_in == NULL) {
        !           153:                cvs_log(LP_ERRNO, "failed to create pipe stream");
        !           154:                return (-1);
        !           155:        }
        !           156:
        !           157:        cvs_server_out = fdopen(cvs_server_outfd, "r");
        !           158:        if (cvs_server_out == NULL) {
        !           159:                cvs_log(LP_ERRNO, "failed to create pipe stream");
        !           160:                return (-1);
        !           161:        }
        !           162:
        !           163:        /* make the streams line-buffered */
        !           164:        setvbuf(cvs_server_in, NULL, _IOLBF, 0);
        !           165:        setvbuf(cvs_server_out, NULL, _IOLBF, 0);
        !           166:
        !           167:        (void)close(infd[0]);
        !           168:        (void)close(outfd[1]);
        !           169:
        !           170:        cvs_client_initlog();
        !           171:
        !           172:        cvs_client_sendinfo();
        !           173:
        !           174: #ifdef CVS_ZLIB
        !           175:        /* if compression was requested, initialize it */
        !           176: #endif
        !           177:
        !           178:        return (0);
        !           179: }
        !           180:
        !           181:
        !           182: /*
        !           183:  * cvs_client_disconnect()
        !           184:  *
        !           185:  * Disconnect from the cvs server.
        !           186:  */
        !           187:
        !           188: void
        !           189: cvs_client_disconnect(void)
        !           190: {
        !           191:        cvs_log(LP_DEBUG, "closing client connection");
        !           192:        (void)fclose(cvs_server_in);
        !           193:        (void)fclose(cvs_server_out);
        !           194:        cvs_server_in = NULL;
        !           195:        cvs_server_out = NULL;
        !           196:        cvs_server_infd = -1;
        !           197:        cvs_server_outfd = -1;
        !           198:
        !           199:        if (cvs_server_inlog != NULL)
        !           200:                fclose(cvs_server_inlog);
        !           201:        if (cvs_server_outlog != NULL)
        !           202:                fclose(cvs_server_outlog);
        !           203: }
        !           204:
        !           205:
        !           206: /*
        !           207:  * cvs_client_sendreq()
        !           208:  *
        !           209:  * Send a request to the server of type <rid>, with optional arguments
        !           210:  * contained in <arg>, which should not be terminated by a newline.
        !           211:  * The <resp> argument is 0 if no response is expected, or any other value if
        !           212:  * a response is expected.
        !           213:  * Returns 0 on success, or -1 on failure.
        !           214:  */
        !           215:
        !           216: int
        !           217: cvs_client_sendreq(u_int rid, const char *arg, int resp)
        !           218: {
        !           219:        int ret;
        !           220:        size_t len;
        !           221:        char *rbp;
        !           222:        const char *reqp;
        !           223:
        !           224:        if (cvs_server_in == NULL) {
        !           225:                cvs_log(LP_ERR, "cannot send request: Not connected");
        !           226:                return (-1);
        !           227:        }
        !           228:
        !           229:        reqp = cvs_req_getbyid(rid);
        !           230:        if (reqp == NULL) {
        !           231:                cvs_log(LP_ERR, "unsupported request type %u", rid);
        !           232:                return (-1);
        !           233:        }
        !           234:
        !           235:        snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n", reqp,
        !           236:            (arg == NULL) ? "" : arg);
        !           237:
        !           238:        rbp = cvs_client_buf;
        !           239:
        !           240:        if (cvs_server_inlog != NULL)
        !           241:                fputs(cvs_client_buf, cvs_server_inlog);
        !           242:
        !           243:        ret = fputs(cvs_client_buf, cvs_server_in);
        !           244:        if (ret == EOF) {
        !           245:                cvs_log(LP_ERRNO, "failed to send request to server");
        !           246:                return (-1);
        !           247:        }
        !           248:
        !           249:        if (resp) {
        !           250:                do {
        !           251:                        /* wait for incoming data */
        !           252:                        if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
        !           253:                            cvs_server_out) == NULL) {
        !           254:                                if (feof(cvs_server_out))
        !           255:                                        return (0);
        !           256:                                cvs_log(LP_ERRNO,
        !           257:                                    "failed to read response from server");
        !           258:                                return (-1);
        !           259:                        }
        !           260:
        !           261:                        if (cvs_server_outlog != NULL)
        !           262:                                fputs(cvs_client_buf, cvs_server_outlog);
        !           263:
        !           264:                        if ((len = strlen(cvs_client_buf)) != 0) {
        !           265:                                if (cvs_client_buf[len - 1] != '\n') {
        !           266:                                        /* truncated line */
        !           267:                                }
        !           268:                                else
        !           269:                                        cvs_client_buf[--len] = '\0';
        !           270:                        }
        !           271:
        !           272:                        ret = cvs_resp_handle(cvs_client_buf);
        !           273:                } while (ret == 0);
        !           274:        }
        !           275:
        !           276:        return (0);
        !           277: }
        !           278:
        !           279:
        !           280: /*
        !           281:  * cvs_client_sendln()
        !           282:  *
        !           283:  * Send a single line <line> string to the server.  The line is sent as is,
        !           284:  * without any modifications.
        !           285:  * Returns 0 on success, or -1 on failure.
        !           286:  */
        !           287:
        !           288: int
        !           289: cvs_client_sendln(const char *line)
        !           290: {
        !           291:        int nl;
        !           292:        size_t len;
        !           293:
        !           294:        nl = 0;
        !           295:        len = strlen(line);
        !           296:
        !           297:        if ((len > 0) && (line[len - 1] != '\n'))
        !           298:                nl = 1;
        !           299:
        !           300:        if (cvs_server_inlog != NULL) {
        !           301:                fputs(line, cvs_server_inlog);
        !           302:                if (nl)
        !           303:                        fputc('\n', cvs_server_inlog);
        !           304:        }
        !           305:        fputs(line, cvs_server_in);
        !           306:        if (nl)
        !           307:                fputc('\n', cvs_server_in);
        !           308:
        !           309:        return (0);
        !           310: }
        !           311:
        !           312:
        !           313: /*
        !           314:  * cvs_client_sendraw()
        !           315:  *
        !           316:  * Send the first <len> bytes from the buffer <src> to the server.
        !           317:  */
        !           318:
        !           319: int
        !           320: cvs_client_sendraw(const void *src, size_t len)
        !           321: {
        !           322:        if (cvs_server_inlog != NULL)
        !           323:                fwrite(src, sizeof(char), len, cvs_server_inlog);
        !           324:        if (fwrite(src, sizeof(char), len, cvs_server_in) < len) {
        !           325:                return (-1);
        !           326:        }
        !           327:
        !           328:        return (0);
        !           329: }
        !           330:
        !           331:
        !           332: /*
        !           333:  * cvs_client_recvraw()
        !           334:  *
        !           335:  * Receive the first <len> bytes from the buffer <src> to the server.
        !           336:  */
        !           337:
        !           338: ssize_t
        !           339: cvs_client_recvraw(void *dst, size_t len)
        !           340: {
        !           341:        size_t ret;
        !           342:
        !           343:        ret = fread(dst, sizeof(char), len, cvs_server_out);
        !           344:        if (ret == 0)
        !           345:                return (-1);
        !           346:        if (cvs_server_outlog != NULL)
        !           347:                fwrite(dst, sizeof(char), len, cvs_server_outlog);
        !           348:        return (ssize_t)ret;
        !           349: }
        !           350:
        !           351:
        !           352: /*
        !           353:  * cvs_client_getln()
        !           354:  *
        !           355:  * Get a line from the server's output and store it in <lbuf>.  The terminating
        !           356:  * newline character is stripped from the result.
        !           357:  */
        !           358:
        !           359: int
        !           360: cvs_client_getln(char *lbuf, size_t len)
        !           361: {
        !           362:        size_t rlen;
        !           363:
        !           364:        if (fgets(lbuf, len, cvs_server_out) == NULL) {
        !           365:                if (ferror(cvs_server_out)) {
        !           366:                        cvs_log(LP_ERRNO, "failed to read line from server");
        !           367:                        return (-1);
        !           368:                }
        !           369:
        !           370:                if (feof(cvs_server_out))
        !           371:                        *lbuf = '\0';
        !           372:        }
        !           373:
        !           374:        if (cvs_server_outlog != NULL)
        !           375:                fputs(lbuf, cvs_server_outlog);
        !           376:
        !           377:        rlen = strlen(lbuf);
        !           378:        if ((rlen > 0) && (lbuf[rlen - 1] == '\n'))
        !           379:                lbuf[--rlen] = '\0';
        !           380:
        !           381:        return (0);
        !           382: }
        !           383:
        !           384:
        !           385: /*
        !           386:  * cvs_client_sendinfo()
        !           387:  *
        !           388:  * Initialize the connection status by first requesting the list of
        !           389:  * supported requests from the server.  Then, we send the CVSROOT variable
        !           390:  * with the `Root' request.
        !           391:  * Returns 0 on success, or -1 on failure.
        !           392:  */
        !           393:
        !           394: static int
        !           395: cvs_client_sendinfo(void)
        !           396: {
        !           397:        char *vresp;
        !           398:        /*
        !           399:         * First, send the server the list of valid responses, then ask
        !           400:         * for valid requests
        !           401:         */
        !           402:
        !           403:        vresp = cvs_resp_getvalid();
        !           404:        if (vresp == NULL) {
        !           405:                cvs_log(LP_ERR, "can't generate list of valid responses");
        !           406:                return (-1);
        !           407:        }
        !           408:
        !           409:        if (cvs_client_sendreq(CVS_REQ_VALIDRESP, vresp, 0) < 0) {
        !           410:        }
        !           411:        free(vresp);
        !           412:
        !           413:        if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
        !           414:                cvs_log(LP_ERR, "failed to get valid requests from server");
        !           415:                return (-1);
        !           416:        }
        !           417:
        !           418:        /* now share our global options with the server */
        !           419:        if (verbosity == 1)
        !           420:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
        !           421:        else if (verbosity == 0)
        !           422:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
        !           423:
        !           424:        if (cvs_nolog)
        !           425:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
        !           426:        if (cvs_readonly)
        !           427:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
        !           428:        if (cvs_trace)
        !           429:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
        !           430:
        !           431:        /* now send the CVSROOT to the server */
        !           432:        if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
        !           433:                return (-1);
        !           434:
        !           435:        /* not sure why, but we have to send this */
        !           436:        if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
        !           437:                return (-1);
        !           438:
        !           439:        return (0);
        !           440: }
        !           441:
        !           442:
        !           443: /*
        !           444:  * cvs_client_senddir()
        !           445:  *
        !           446:  * Send a `Directory' request along with the 2 paths that follow it.
        !           447:  */
        !           448:
        !           449: int
        !           450: cvs_client_senddir(const char *dir)
        !           451: {
        !           452:        char repo[MAXPATHLEN], buf[MAXPATHLEN];
        !           453:
        !           454:        /* don't bother sending if it's the same as the last Directory sent */
        !           455:        if (strcmp(dir, cvs_lastdir) == 0)
        !           456:                return (0);
        !           457:
        !           458:        if (cvs_readrepo(dir, repo, sizeof(repo)) < 0)
        !           459:                return (-1);
        !           460:
        !           461:        snprintf(buf, sizeof(buf), "%s/%s", cvs_root->cr_dir, repo);
        !           462:
        !           463:        if ((cvs_client_sendreq(CVS_REQ_DIRECTORY, dir, 0) < 0) ||
        !           464:            (cvs_client_sendln(buf) < 0))
        !           465:                return (-1);
        !           466:        strlcpy(cvs_lastdir, dir, sizeof(cvs_lastdir));
        !           467:
        !           468:        return (0);
        !           469: }
        !           470:
        !           471:
        !           472: /*
        !           473:  * cvs_client_sendarg()
        !           474:  *
        !           475:  * Send the argument <arg> to the server.  The argument <append> is used to
        !           476:  * determine if the argument should be simply appended to the last argument
        !           477:  * sent or if it should be created as a new argument (0).
        !           478:  */
        !           479:
        !           480: int
        !           481: cvs_client_sendarg(const char *arg, int append)
        !           482: {
        !           483:        return cvs_client_sendreq(((append == 0) ?
        !           484:            CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg, 0);
        !           485: }
        !           486:
        !           487:
        !           488: /*
        !           489:  * cvs_client_sendentry()
        !           490:  *
        !           491:  * Send an `Entry' request to the server along with the mandatory fields from
        !           492:  * the CVS entry <ent> (which are the name and revision).
        !           493:  */
        !           494:
        !           495: int
        !           496: cvs_client_sendentry(const struct cvs_ent *ent)
        !           497: {
        !           498:        char ebuf[128], numbuf[64];
        !           499:
        !           500:        snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name,
        !           501:            rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf)));
        !           502:
        !           503:        return cvs_client_sendreq(CVS_REQ_ENTRY, ebuf, 0);
        !           504: }
        !           505:
        !           506:
        !           507: /*
        !           508:  * cvs_client_initlog()
        !           509:  *
        !           510:  * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
        !           511:  * set.  In this case, the variable's value is used as a path to which the
        !           512:  * appropriate suffix is added (".in" for server input and ".out" for server
        !           513:  * output.
        !           514:  * Returns 0 on success, or -1 on failure.
        !           515:  */
        !           516:
        !           517: static int
        !           518: cvs_client_initlog(void)
        !           519: {
        !           520:        char *env, fpath[MAXPATHLEN];
        !           521:
        !           522:        env = getenv("CVS_CLIENT_LOG");
        !           523:        if (env == NULL)
        !           524:                return (0);
        !           525:
        !           526:        strlcpy(fpath, env, sizeof(fpath));
        !           527:        strlcat(fpath, ".in", sizeof(fpath));
        !           528:        cvs_server_inlog = fopen(fpath, "w");
        !           529:        if (cvs_server_inlog == NULL) {
        !           530:                cvs_log(LP_ERRNO, "failed to open server input log `%s'",
        !           531:                    fpath);
        !           532:                return (-1);
        !           533:        }
        !           534:
        !           535:        strlcpy(fpath, env, sizeof(fpath));
        !           536:        strlcat(fpath, ".out", sizeof(fpath));
        !           537:        cvs_server_outlog = fopen(fpath, "w");
        !           538:        if (cvs_server_outlog == NULL) {
        !           539:                cvs_log(LP_ERRNO, "failed to open server output log `%s'",
        !           540:                    fpath);
        !           541:                return (-1);
        !           542:        }
        !           543:
        !           544:        /* make the streams line-buffered */
        !           545:        setvbuf(cvs_server_inlog, NULL, _IOLBF, 0);
        !           546:        setvbuf(cvs_server_outlog, NULL, _IOLBF, 0);
        !           547:
        !           548:        return (0);
        !           549: }
        !           550:
        !           551:
        !           552: /*
        !           553:  * cvs_client_sendfile()
        !           554:  *
        !           555:  */
        !           556:
        !           557: int
        !           558: cvs_client_sendfile(const char *path)
        !           559: {
        !           560:        int fd;
        !           561:        ssize_t ret;
        !           562:        char buf[4096];
        !           563:        struct stat st;
        !           564:
        !           565:        if (stat(path, &st) == -1) {
        !           566:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
        !           567:                return (-1);
        !           568:        }
        !           569:
        !           570:        fd = open(path, O_RDONLY, 0);
        !           571:        if (fd == -1) {
        !           572:                return (-1);
        !           573:        }
        !           574:
        !           575:        if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
        !           576:                return (-1);
        !           577:
        !           578:        cvs_client_sendln(buf);
        !           579:        snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
        !           580:        cvs_client_sendln(buf);
        !           581:
        !           582:        while ((ret = read(fd, buf, sizeof(buf))) != 0) {
        !           583:                if (ret == -1) {
        !           584:                        cvs_log(LP_ERRNO, "failed to read file `%s'", path);
        !           585:                        return (-1);
        !           586:                }
        !           587:
        !           588:                cvs_client_sendraw(buf, (size_t)ret);
        !           589:
        !           590:        }
        !           591:
        !           592:        (void)close(fd);
        !           593:
        !           594:        return (0);
        !           595: }