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

Annotation of src/usr.bin/cvs/proto.c, Revision 1.12

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:  * CVS client/server protocol
                     28:  * ==========================
                     29:  *
                     30:  * The following code implements the CVS client/server protocol, which is
                     31:  * documented at the following URL:
                     32:  *     http://www.loria.fr/~molli/cvs/doc/cvsclient_toc.html
                     33:  *
                     34:  * The protocol is split up into two parts; the first part is the client side
                     35:  * of things and is composed of all the response handlers, which are all named
                     36:  * with a prefix of "cvs_resp_".  The second part is the set of request
                     37:  * handlers used by the server.  These handlers process the request and
                     38:  * generate the appropriate response to send back.  The prefix for request
                     39:  * handlers is "cvs_req_".
                     40:  *
                     41:  */
                     42:
                     43: #include <sys/types.h>
                     44: #include <sys/stat.h>
                     45:
                     46: #include <fcntl.h>
                     47: #include <stdio.h>
                     48: #include <errno.h>
                     49: #include <stdlib.h>
                     50: #include <unistd.h>
                     51: #include <signal.h>
                     52: #include <string.h>
                     53: #include <sysexits.h>
                     54: #ifdef CVS_ZLIB
                     55: #include <zlib.h>
                     56: #endif
                     57:
                     58: #include "buf.h"
                     59: #include "cvs.h"
                     60: #include "log.h"
                     61:
                     62:
1.5       jfb        63: #define CVS_MTSTK_MAXDEPTH   16
                     64:
1.11      jfb        65: /* request flags */
                     66: #define CVS_REQF_RESP    0x01
                     67:
                     68:
1.5       jfb        69:
1.1       jfb        70:
                     71: extern int   verbosity;
                     72: extern int   cvs_compress;
                     73: extern char *cvs_rsh;
                     74: extern int   cvs_trace;
                     75: extern int   cvs_nolog;
                     76: extern int   cvs_readonly;
                     77:
                     78:
                     79:
                     80: static int  cvs_resp_validreq  (int, char *);
                     81: static int  cvs_resp_cksum     (int, char *);
1.4       jfb        82: static int  cvs_resp_modtime   (int, char *);
1.1       jfb        83: static int  cvs_resp_m         (int, char *);
                     84: static int  cvs_resp_ok        (int, char *);
                     85: static int  cvs_resp_error     (int, char *);
                     86: static int  cvs_resp_statdir   (int, char *);
1.5       jfb        87: static int  cvs_resp_sticky    (int, char *);
1.1       jfb        88: static int  cvs_resp_newentry  (int, char *);
                     89: static int  cvs_resp_updated   (int, char *);
                     90: static int  cvs_resp_removed   (int, char *);
                     91: static int  cvs_resp_mode      (int, char *);
1.5       jfb        92: static int  cvs_resp_modxpand  (int, char *);
1.1       jfb        93:
                     94:
1.7       jfb        95: static const char *cvs_months[] = {
1.9       jfb        96:        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                     97:        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1.7       jfb        98: };
                     99:
                    100:
                    101:
1.1       jfb       102: struct cvs_req {
                    103:        int      req_id;
                    104:        char     req_str[32];
                    105:        u_int    req_flags;
                    106:        int    (*req_hdlr)(int, char *);
                    107: } cvs_requests[] = {
                    108:        { CVS_REQ_DIRECTORY,     "Directory",         0,  NULL },
                    109:        { CVS_REQ_MAXDOTDOT,     "Max-dotdot",        0,  NULL },
                    110:        { CVS_REQ_STATICDIR,     "Static-directory",  0,  NULL },
                    111:        { CVS_REQ_STICKY,        "Sticky",            0,  NULL },
                    112:        { CVS_REQ_ENTRY,         "Entry",             0,  NULL },
                    113:        { CVS_REQ_ENTRYEXTRA,    "EntryExtra",        0,  NULL },
                    114:        { CVS_REQ_CHECKINTIME,   "Checkin-time",      0,  NULL },
                    115:        { CVS_REQ_MODIFIED,      "Modified",          0,  NULL },
                    116:        { CVS_REQ_ISMODIFIED,    "Is-modified",       0,  NULL },
                    117:        { CVS_REQ_UNCHANGED,     "Unchanged",         0,  NULL },
                    118:        { CVS_REQ_USEUNCHANGED,  "UseUnchanged",      0,  NULL },
                    119:        { CVS_REQ_NOTIFY,        "Notify",            0,  NULL },
                    120:        { CVS_REQ_NOTIFYUSER,    "NotifyUser",        0,  NULL },
                    121:        { CVS_REQ_QUESTIONABLE,  "Questionable",      0,  NULL },
                    122:        { CVS_REQ_CASE,          "Case",              0,  NULL },
                    123:        { CVS_REQ_UTF8,          "Utf8",              0,  NULL },
                    124:        { CVS_REQ_ARGUMENT,      "Argument",          0,  NULL },
                    125:        { CVS_REQ_ARGUMENTX,     "Argumentx",         0,  NULL },
                    126:        { CVS_REQ_GLOBALOPT,     "Global_option",     0,  NULL },
                    127:        { CVS_REQ_GZIPSTREAM,    "Gzip-stream",       0,  NULL },
                    128:        { CVS_REQ_READCVSRC2,    "read-cvsrc2",       0,  NULL },
                    129:        { CVS_REQ_READWRAP,      "read-cvswrappers",  0,  NULL },
                    130:        { CVS_REQ_READIGNORE,    "read-cvsignore",    0,  NULL },
                    131:        { CVS_REQ_ERRIFREADER,   "Error-If-Reader",   0,  NULL },
                    132:        { CVS_REQ_VALIDRCSOPT,   "Valid-RcsOptions",  0,  NULL },
                    133:        { CVS_REQ_SET,           "Set",               0,  NULL },
1.11      jfb       134:        { CVS_REQ_XPANDMOD,      "expand-modules",    CVS_REQF_RESP,  NULL },
1.1       jfb       135:        { CVS_REQ_LOG,           "log",               0,  NULL },
1.11      jfb       136:        { CVS_REQ_CO,            "co",                CVS_REQF_RESP,  NULL },
                    137:        { CVS_REQ_EXPORT,        "export",            CVS_REQF_RESP,  NULL },
1.1       jfb       138:        { CVS_REQ_RANNOTATE,     "rannotate",         0,  NULL },
                    139:        { CVS_REQ_RDIFF,         "rdiff",             0,  NULL },
                    140:        { CVS_REQ_RLOG,          "rlog",              0,  NULL },
1.11      jfb       141:        { CVS_REQ_RTAG,          "rtag",              CVS_REQF_RESP,  NULL },
                    142:        { CVS_REQ_INIT,          "init",              CVS_REQF_RESP,  NULL },
                    143:        { CVS_REQ_STATUS,        "status",            CVS_REQF_RESP,  NULL },
                    144:        { CVS_REQ_UPDATE,        "update",            CVS_REQF_RESP,  NULL },
1.1       jfb       145:        { CVS_REQ_HISTORY,       "history",           0,  NULL },
1.11      jfb       146:        { CVS_REQ_IMPORT,        "import",            CVS_REQF_RESP,  NULL },
                    147:        { CVS_REQ_ADD,           "add",               CVS_REQF_RESP,  NULL },
                    148:        { CVS_REQ_REMOVE,        "remove",            CVS_REQF_RESP,  NULL },
                    149:        { CVS_REQ_RELEASE,       "release",           CVS_REQF_RESP,  NULL },
1.1       jfb       150:        { CVS_REQ_ROOT,          "Root",              0,  NULL },
1.11      jfb       151:        { CVS_REQ_VALIDRESP,     "Valid-responses",   CVS_REQF_RESP,  NULL },
                    152:        { CVS_REQ_VALIDREQ,      "valid-requests",    CVS_REQF_RESP,  NULL },
                    153:        { CVS_REQ_VERSION,       "version",           CVS_REQF_RESP,  NULL },
                    154:        { CVS_REQ_NOOP,          "noop",              CVS_REQF_RESP,  NULL },
                    155:        { CVS_REQ_DIFF,          "diff",              CVS_REQF_RESP,  NULL },
1.1       jfb       156: };
                    157:
                    158:
                    159: struct cvs_resp {
                    160:        u_int  resp_id;
                    161:        char   resp_str[32];
                    162:        int  (*resp_hdlr)(int, char *);
                    163: } cvs_responses[] = {
                    164:        { CVS_RESP_OK,         "ok",                     cvs_resp_ok       },
                    165:        { CVS_RESP_ERROR,      "error",                  cvs_resp_error    },
                    166:        { CVS_RESP_VALIDREQ,   "Valid-requests",         cvs_resp_validreq },
                    167:        { CVS_RESP_M,          "M",                      cvs_resp_m        },
                    168:        { CVS_RESP_MBINARY,    "Mbinary",                cvs_resp_m        },
                    169:        { CVS_RESP_MT,         "MT",                     cvs_resp_m        },
                    170:        { CVS_RESP_E,          "E",                      cvs_resp_m        },
                    171:        { CVS_RESP_F,          "F",                      cvs_resp_m        },
                    172:        { CVS_RESP_CREATED,    "Created",                cvs_resp_updated  },
                    173:        { CVS_RESP_UPDATED,    "Updated",                cvs_resp_updated  },
                    174:        { CVS_RESP_UPDEXIST,   "Update-existing",        cvs_resp_updated  },
                    175:        { CVS_RESP_REMOVED,    "Removed",                cvs_resp_removed  },
                    176:        { CVS_RESP_MERGED,     "Merged",                 NULL              },
                    177:        { CVS_RESP_CKSUM,      "Checksum",               cvs_resp_cksum    },
                    178:        { CVS_RESP_CLRSTATDIR, "Clear-static-directory", cvs_resp_statdir  },
                    179:        { CVS_RESP_SETSTATDIR, "Set-static-directory",   cvs_resp_statdir  },
                    180:        { CVS_RESP_NEWENTRY,   "New-entry",              cvs_resp_newentry },
                    181:        { CVS_RESP_CHECKEDIN,  "Checked-in",             cvs_resp_newentry },
                    182:        { CVS_RESP_MODE,       "Mode",                   cvs_resp_mode     },
1.4       jfb       183:        { CVS_RESP_MODTIME,    "Mod-time",               cvs_resp_modtime  },
1.5       jfb       184:        { CVS_RESP_MODXPAND,   "Module-expansion",       cvs_resp_modxpand },
                    185:        { CVS_RESP_SETSTICKY,  "Set-sticky",             cvs_resp_sticky   },
                    186:        { CVS_RESP_CLRSTICKY,  "Clear-sticky",           cvs_resp_sticky   },
1.1       jfb       187: };
                    188:
                    189:
1.5       jfb       190: static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH];
                    191: static u_int cvs_mtstk_depth = 0;
                    192:
1.8       jfb       193: static time_t cvs_modtime = 0;
1.7       jfb       194:
1.5       jfb       195:
1.1       jfb       196: #define CVS_NBREQ   (sizeof(cvs_requests)/sizeof(cvs_requests[0]))
                    197: #define CVS_NBRESP  (sizeof(cvs_responses)/sizeof(cvs_responses[0]))
                    198:
                    199: /* mask of requets supported by server */
                    200: static u_char  cvs_server_validreq[CVS_REQ_MAX + 1];
                    201:
1.5       jfb       202: /*
                    203:  * Local and remote directory used by the `Directory' request.
                    204:  */
                    205: char  cvs_ldir[MAXPATHLEN];
                    206: char  cvs_rdir[MAXPATHLEN];
                    207:
                    208: char *cvs_fcksum = NULL;
                    209:
                    210: mode_t  cvs_lastmode = 0;
                    211:
1.1       jfb       212:
                    213: /*
                    214:  * cvs_req_getbyid()
                    215:  *
                    216:  */
                    217:
                    218: const char*
                    219: cvs_req_getbyid(int reqid)
                    220: {
                    221:        u_int i;
                    222:
                    223:        for (i = 0; i < CVS_NBREQ; i++)
                    224:                if (cvs_requests[i].req_id == reqid)
                    225:                        return (cvs_requests[i].req_str);
                    226:        return (NULL);
                    227: }
                    228:
                    229:
                    230: /*
                    231:  * cvs_req_getbyname()
                    232:  */
                    233:
                    234: int
                    235: cvs_req_getbyname(const char *rname)
                    236: {
                    237:        u_int i;
                    238:
                    239:        for (i = 0; i < CVS_NBREQ; i++)
                    240:                if (strcmp(cvs_requests[i].req_str, rname) == 0)
                    241:                        return (cvs_requests[i].req_id);
                    242:
                    243:        return (-1);
                    244: }
                    245:
                    246:
                    247: /*
                    248:  * cvs_req_getvalid()
                    249:  *
                    250:  * Build a space-separated list of all the requests that this protocol
                    251:  * implementation supports.
                    252:  */
                    253:
                    254: char*
                    255: cvs_req_getvalid(void)
                    256: {
                    257:        u_int i;
                    258:        size_t len;
                    259:        char *vrstr;
                    260:        BUF *buf;
                    261:
                    262:        buf = cvs_buf_alloc(512, BUF_AUTOEXT);
                    263:        if (buf == NULL)
                    264:                return (NULL);
                    265:
                    266:        cvs_buf_set(buf, cvs_requests[0].req_str,
                    267:            strlen(cvs_requests[0].req_str), 0);
                    268:
                    269:        for (i = 1; i < CVS_NBREQ; i++) {
                    270:                if ((cvs_buf_putc(buf, ' ') < 0) ||
                    271:                    (cvs_buf_append(buf, cvs_requests[i].req_str,
                    272:                    strlen(cvs_requests[i].req_str)) < 0)) {
                    273:                        cvs_buf_free(buf);
                    274:                        return (NULL);
                    275:                }
                    276:        }
                    277:
                    278:        /* NUL-terminate */
                    279:        if (cvs_buf_putc(buf, '\0') < 0) {
                    280:                cvs_buf_free(buf);
                    281:                return (NULL);
                    282:        }
                    283:
                    284:        len = cvs_buf_size(buf);
                    285:        vrstr = (char *)malloc(len);
                    286:
                    287:        cvs_buf_copy(buf, 0, vrstr, len);
                    288:
                    289:        cvs_buf_free(buf);
                    290:
                    291:        return (vrstr);
                    292: }
                    293:
                    294:
                    295: /*
                    296:  * cvs_req_handle()
                    297:  *
                    298:  * Generic request handler dispatcher.
                    299:  */
                    300:
                    301: int
                    302: cvs_req_handle(char *line)
                    303: {
                    304:        return (0);
                    305: }
                    306:
                    307:
                    308: /*
                    309:  * cvs_resp_getbyid()
                    310:  *
                    311:  */
                    312:
                    313: const char*
                    314: cvs_resp_getbyid(int respid)
                    315: {
                    316:        u_int i;
                    317:
                    318:        for (i = 0; i < CVS_NBREQ; i++)
                    319:                if (cvs_responses[i].resp_id == respid)
                    320:                        return (cvs_responses[i].resp_str);
                    321:        return (NULL);
                    322: }
                    323:
                    324:
                    325: /*
                    326:  * cvs_resp_getbyname()
                    327:  */
                    328:
                    329: int
                    330: cvs_resp_getbyname(const char *rname)
                    331: {
                    332:        u_int i;
                    333:
                    334:        for (i = 0; i < CVS_NBREQ; i++)
                    335:                if (strcmp(cvs_responses[i].resp_str, rname) == 0)
                    336:                        return (cvs_responses[i].resp_id);
                    337:
                    338:        return (-1);
                    339: }
                    340:
                    341:
                    342: /*
                    343:  * cvs_resp_getvalid()
                    344:  *
                    345:  * Build a space-separated list of all the responses that this protocol
                    346:  * implementation supports.
                    347:  */
                    348:
                    349: char*
                    350: cvs_resp_getvalid(void)
                    351: {
                    352:        u_int i;
                    353:        size_t len;
                    354:        char *vrstr;
                    355:        BUF *buf;
                    356:
                    357:        buf = cvs_buf_alloc(512, BUF_AUTOEXT);
                    358:        if (buf == NULL)
                    359:                return (NULL);
                    360:
                    361:        cvs_buf_set(buf, cvs_responses[0].resp_str,
                    362:            strlen(cvs_responses[0].resp_str), 0);
                    363:
                    364:        for (i = 1; i < CVS_NBRESP; i++) {
                    365:                if ((cvs_buf_putc(buf, ' ') < 0) ||
                    366:                    (cvs_buf_append(buf, cvs_responses[i].resp_str,
                    367:                    strlen(cvs_responses[i].resp_str)) < 0)) {
                    368:                        cvs_buf_free(buf);
                    369:                        return (NULL);
                    370:                }
                    371:        }
                    372:
                    373:        /* NUL-terminate */
                    374:        if (cvs_buf_putc(buf, '\0') < 0) {
                    375:                cvs_buf_free(buf);
                    376:                return (NULL);
                    377:        }
                    378:
                    379:        len = cvs_buf_size(buf);
                    380:        vrstr = (char *)malloc(len);
                    381:
                    382:        cvs_buf_copy(buf, 0, vrstr, len);
                    383:        cvs_buf_free(buf);
                    384:
                    385:        return (vrstr);
                    386: }
                    387:
                    388:
                    389: /*
                    390:  * cvs_resp_handle()
                    391:  *
                    392:  * Generic response handler dispatcher.  The handler expects the first line
                    393:  * of the command as single argument.
                    394:  * Returns the return value of the command on success, or -1 on failure.
                    395:  */
                    396:
                    397: int
                    398: cvs_resp_handle(char *line)
                    399: {
                    400:        u_int i;
                    401:        size_t len;
                    402:        char *cp, *cmd;
                    403:
                    404:        cmd = line;
                    405:
                    406:        cp = strchr(cmd, ' ');
                    407:        if (cp != NULL)
                    408:                *(cp++) = '\0';
                    409:
                    410:        for (i = 0; i < CVS_NBRESP; i++) {
                    411:                if (strcmp(cvs_responses[i].resp_str, cmd) == 0)
                    412:                        return (*cvs_responses[i].resp_hdlr)
                    413:                            (cvs_responses[i].resp_id, cp);
                    414:        }
                    415:
                    416:        /* unhandled */
                    417:        return (-1);
                    418: }
                    419:
                    420:
                    421: /*
                    422:  * cvs_resp_validreq()
                    423:  *
                    424:  * Handler for the `Valid-requests' response.  The list of valid requests is
                    425:  * split on spaces and each request's entry in the valid request array is set
                    426:  * to 1 to indicate the validity.
                    427:  * Returns 0 on success, or -1 on failure.
                    428:  */
                    429:
                    430: static int
                    431: cvs_resp_validreq(int type, char *line)
                    432: {
                    433:        int i;
                    434:        char *sp, *ep;
                    435:
                    436:        /* parse the requests */
                    437:        sp = line;
                    438:        do {
                    439:                ep = strchr(sp, ' ');
                    440:                if (ep != NULL)
                    441:                        *ep = '\0';
                    442:
                    443:                i = cvs_req_getbyname(sp);
                    444:                if (i != -1)
                    445:                        cvs_server_validreq[i] = 1;
                    446:
                    447:                if (ep != NULL)
                    448:                        sp = ep + 1;
                    449:        } while (ep != NULL);
                    450:
                    451:        return (0);
                    452: }
                    453:
                    454:
                    455: /*
                    456:  * cvs_resp_m()
                    457:  *
1.4       jfb       458:  * Handler for the `M', 'MT', `F' and `E' responses.
1.1       jfb       459:  */
                    460:
                    461: static int
                    462: cvs_resp_m(int type, char *line)
                    463: {
1.5       jfb       464:        char *cp;
1.1       jfb       465:        FILE *stream;
                    466:
                    467:        stream = NULL;
                    468:
                    469:        switch (type) {
                    470:        case CVS_RESP_F:
                    471:                fflush(stderr);
                    472:                return (0);
                    473:        case CVS_RESP_M:
                    474:                stream = stdout;
                    475:                break;
                    476:        case CVS_RESP_E:
                    477:                stream = stderr;
                    478:                break;
                    479:        case CVS_RESP_MT:
1.5       jfb       480:                if (*line == '+') {
                    481:                        if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) {
                    482:                                cvs_log(LP_ERR,
                    483:                                    "MT scope stack has reached max depth");
                    484:                                return (-1);
                    485:                        }
1.6       jfb       486:                        cvs_mt_stack[cvs_mtstk_depth] = strdup(line + 1);
                    487:                        if (cvs_mt_stack[cvs_mtstk_depth] == NULL)
1.5       jfb       488:                                return (-1);
1.6       jfb       489:                        cvs_mtstk_depth++;
1.5       jfb       490:                }
                    491:                else if (*line == '-') {
                    492:                        if (cvs_mtstk_depth == 0) {
                    493:                                cvs_log(LP_ERR, "MT scope stack underflow");
                    494:                                return (-1);
                    495:                        }
1.6       jfb       496:                        else if (strcmp(line + 1,
                    497:                            cvs_mt_stack[cvs_mtstk_depth - 1]) != 0) {
1.5       jfb       498:                                cvs_log(LP_ERR, "mismatch in MT scope stack");
                    499:                                return (-1);
                    500:                        }
                    501:                        free(cvs_mt_stack[cvs_mtstk_depth--]);
                    502:                }
                    503:                else {
                    504:                        if (strcmp(line, "newline") == 0)
                    505:                                putc('\n', stdout);
                    506:                        else if (strncmp(line, "fname ", 6) == 0)
                    507:                                printf("%s", line + 6);
                    508:                        else {
                    509:                                /* assume text */
                    510:                                cp = strchr(line, ' ');
                    511:                                if (cp != NULL)
                    512:                                        printf("%s", cp + 1);
                    513:                        }
                    514:                }
                    515:
1.6       jfb       516:                return (0);
1.1       jfb       517:        case CVS_RESP_MBINARY:
1.5       jfb       518:                cvs_log(LP_WARN, "Mbinary not supported in client yet");
1.1       jfb       519:                break;
                    520:        }
                    521:
                    522:        fputs(line, stream);
                    523:        fputc('\n', stream);
                    524:
                    525:        return (0);
                    526: }
                    527:
                    528:
                    529: /*
                    530:  * cvs_resp_ok()
                    531:  *
                    532:  * Handler for the `ok' response.  This handler's job is to
                    533:  */
                    534:
                    535: static int
                    536: cvs_resp_ok(int type, char *line)
                    537: {
                    538:        return (1);
                    539: }
                    540:
                    541:
                    542: /*
                    543:  * cvs_resp_error()
                    544:  *
                    545:  * Handler for the `error' response.  This handler's job is to
                    546:  */
                    547:
                    548: static int
                    549: cvs_resp_error(int type, char *line)
                    550: {
                    551:        return (1);
                    552: }
                    553:
                    554:
                    555: /*
                    556:  * cvs_resp_statdir()
                    557:  *
                    558:  * Handler for the `Clear-static-directory' and `Set-static-directory'
                    559:  * responses.
                    560:  */
                    561:
                    562: static int
                    563: cvs_resp_statdir(int type, char *line)
                    564: {
                    565:        int fd;
1.6       jfb       566:        char rpath[MAXPATHLEN], statpath[MAXPATHLEN];
                    567:
                    568:        cvs_client_getln(rpath, sizeof(rpath));
1.1       jfb       569:
                    570:        snprintf(statpath, sizeof(statpath), "%s/%s", line,
                    571:            CVS_PATH_STATICENTRIES);
                    572:
                    573:        if ((type == CVS_RESP_CLRSTATDIR) &&
1.5       jfb       574:            (unlink(statpath) == -1) && (errno != ENOENT)) {
1.1       jfb       575:                cvs_log(LP_ERRNO, "failed to unlink %s file",
                    576:                    CVS_PATH_STATICENTRIES);
                    577:                return (-1);
                    578:        }
                    579:        else if (type == CVS_RESP_SETSTATDIR) {
1.5       jfb       580:                fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0400);
1.1       jfb       581:                if (fd == -1) {
                    582:                        cvs_log(LP_ERRNO, "failed to create %s file",
                    583:                            CVS_PATH_STATICENTRIES);
                    584:                        return (-1);
                    585:                }
                    586:                (void)close(fd);
1.5       jfb       587:
                    588:        }
                    589:
                    590:        return (0);
                    591: }
                    592:
                    593: /*
                    594:  * cvs_resp_sticky()
                    595:  *
                    596:  * Handler for the `Clear-sticky' and `Set-sticky' responses.
                    597:  */
                    598:
                    599: static int
                    600: cvs_resp_sticky(int type, char *line)
                    601: {
1.9       jfb       602:        size_t len;
1.6       jfb       603:        char rpath[MAXPATHLEN];
                    604:        struct stat st;
1.7       jfb       605:        CVSFILE *cf;
1.6       jfb       606:
1.9       jfb       607:        /* remove trailing slash */
                    608:        len = strlen(line);
                    609:        if ((len > 0) && (line[len - 1] == '/'))
                    610:                line[--len] = '\0';
                    611:
1.6       jfb       612:        /* get the remote path */
                    613:        cvs_client_getln(rpath, sizeof(rpath));
                    614:
                    615:        /* if the directory doesn't exist, create it */
                    616:        if (stat(line, &st) == -1) {
1.7       jfb       617:                /* attempt to create it */
1.6       jfb       618:                if (errno != ENOENT) {
                    619:                        cvs_log(LP_ERRNO, "failed to stat %s", line);
                    620:                }
1.7       jfb       621:                else {
                    622:                        cf = cvs_file_create(line, DT_DIR, 0755);
                    623:                        if (cf == NULL)
                    624:                                return (-1);
                    625:                        cf->cf_ddat->cd_repo = strdup(line);
                    626:                        cf->cf_ddat->cd_root = cvs_root;
                    627:                        cvs_mkadmin(cf, 0755);
                    628:
                    629:                        cf->cf_ddat->cd_root = NULL;
                    630:                        cvs_file_free(cf);
1.6       jfb       631:                }
                    632:        }
                    633:
1.5       jfb       634:        if (type == CVS_RESP_CLRSTICKY) {
                    635:        }
                    636:        else if (type == CVS_RESP_SETSTICKY) {
1.1       jfb       637:        }
                    638:
                    639:        return (0);
                    640: }
                    641:
                    642:
                    643: /*
                    644:  * cvs_resp_newentry()
                    645:  *
                    646:  * Handler for the `New-entry' response and `Checked-in' responses.
                    647:  */
                    648:
                    649: static int
                    650: cvs_resp_newentry(int type, char *line)
                    651: {
                    652:        char entbuf[128], path[MAXPATHLEN];
                    653:        CVSENTRIES *entfile;
                    654:
1.2       jfb       655:        snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line);
1.1       jfb       656:
                    657:        /* get the remote path */
                    658:        cvs_client_getln(entbuf, sizeof(entbuf));
                    659:
                    660:        /* get the new Entries line */
                    661:        if (cvs_client_getln(entbuf, sizeof(entbuf)) < 0)
                    662:                return (-1);
                    663:
1.2       jfb       664:        entfile = cvs_ent_open(path, O_WRONLY);
1.1       jfb       665:        if (entfile == NULL)
                    666:                return (-1);
1.3       jfb       667:        cvs_ent_addln(entfile, entbuf);
1.1       jfb       668:        cvs_ent_close(entfile);
                    669:
                    670:        return (0);
                    671: }
                    672:
                    673:
                    674: /*
                    675:  * cvs_resp_cksum()
                    676:  *
                    677:  * Handler for the `Checksum' response.  We store the checksum received for
                    678:  * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>.
                    679:  * Upon next file reception, the handler checks to see if there is a stored
                    680:  * checksum.
                    681:  * The file handler must make sure that the checksums match and free the
                    682:  * checksum buffer once it's done to indicate there is no further checksum.
                    683:  */
                    684:
                    685: static int
                    686: cvs_resp_cksum(int type, char *line)
                    687: {
                    688:        if (cvs_fcksum != NULL) {
                    689:                cvs_log(LP_WARN, "unused checksum");
                    690:                free(cvs_fcksum);
                    691:        }
                    692:
                    693:        cvs_fcksum = strdup(line);
                    694:        if (cvs_fcksum == NULL) {
                    695:                cvs_log(LP_ERRNO, "failed to copy checksum string");
                    696:                return (-1);
                    697:        }
                    698:
1.4       jfb       699:        return (0);
                    700: }
                    701:
                    702:
                    703: /*
                    704:  * cvs_resp_modtime()
                    705:  *
                    706:  * Handler for the `Mod-time' file update modifying response.  The timestamp
                    707:  * given is used to set the last modification time on the next file that
                    708:  * will be received.
                    709:  */
                    710:
                    711: static int
                    712: cvs_resp_modtime(int type, char *line)
                    713: {
1.8       jfb       714:        int i;
                    715:        long off;
                    716:        char sign, mon[8], gmt[8], hr[4], min[4], *ep;
                    717:        struct tm cvs_tm;
                    718:
                    719:        memset(&cvs_tm, 0, sizeof(cvs_tm));
1.9       jfb       720:        sscanf(line, "%d %3s %d %2d:%2d:%2d %5s", &cvs_tm.tm_mday, mon,
1.8       jfb       721:            &cvs_tm.tm_year, &cvs_tm.tm_hour, &cvs_tm.tm_min,
                    722:            &cvs_tm.tm_sec, gmt);
                    723:        cvs_tm.tm_year -= 1900;
1.9       jfb       724:        cvs_tm.tm_isdst = -1;
1.8       jfb       725:
                    726:        if (*gmt == '-') {
1.9       jfb       727:                sscanf(gmt, "%c%2s%2s", &sign, hr, min);
1.8       jfb       728:                cvs_tm.tm_gmtoff = strtol(hr, &ep, 10);
                    729:                if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
                    730:                    (cvs_tm.tm_gmtoff == LONG_MAX) ||
                    731:                    (*ep != '\0')) {
                    732:                        cvs_log(LP_ERR,
                    733:                            "parse error in GMT hours specification `%s'", hr);
                    734:                        cvs_tm.tm_gmtoff = 0;
                    735:                }
                    736:                else {
                    737:                        /* get seconds */
                    738:                        cvs_tm.tm_gmtoff *= 3600;
1.7       jfb       739:
1.8       jfb       740:                        /* add the minutes */
                    741:                        off = strtol(min, &ep, 10);
                    742:                        if ((cvs_tm.tm_gmtoff == LONG_MIN) ||
                    743:                            (cvs_tm.tm_gmtoff == LONG_MAX) ||
                    744:                            (*ep != '\0')) {
                    745:                                cvs_log(LP_ERR,
                    746:                                    "parse error in GMT minutes "
                    747:                                    "specification `%s'", min);
                    748:                        }
                    749:                        else
                    750:                                cvs_tm.tm_gmtoff += off * 60;
                    751:                }
                    752:        }
                    753:        if (sign == '-')
                    754:                cvs_tm.tm_gmtoff = -cvs_tm.tm_gmtoff;
1.7       jfb       755:
1.8       jfb       756:        for (i = 0; i < (int)(sizeof(cvs_months)/sizeof(cvs_months[0])); i++) {
                    757:                if (strcmp(cvs_months[i], mon) == 0) {
                    758:                        cvs_tm.tm_mon = i;
                    759:                        break;
                    760:                }
1.7       jfb       761:        }
                    762:
1.8       jfb       763:        cvs_modtime = mktime(&cvs_tm);
1.1       jfb       764:        return (0);
                    765: }
                    766:
                    767:
                    768: /*
                    769:  * cvs_resp_updated()
                    770:  *
1.6       jfb       771:  * Handler for the `Updated' and `Created' responses.
1.1       jfb       772:  */
                    773:
                    774: static int
                    775: cvs_resp_updated(int type, char *line)
                    776: {
1.7       jfb       777:        size_t len;
                    778:        char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN];
                    779:        CVSENTRIES *ef;
1.6       jfb       780:        struct cvs_ent *ep;
1.1       jfb       781:
                    782:        if (type == CVS_RESP_CREATED) {
1.6       jfb       783:                /* read the remote path of the file */
                    784:                cvs_client_getln(path, sizeof(path));
                    785:
                    786:                /* read the new entry */
                    787:                cvs_client_getln(path, sizeof(path));
                    788:                ep = cvs_ent_parse(path);
                    789:                if (ep == NULL)
                    790:                        return (-1);
1.7       jfb       791:
                    792:                /* set the timestamp as the last one received from Mod-time */
1.8       jfb       793:                ep->ce_timestamp = ctime_r(&cvs_modtime, tbuf);
1.7       jfb       794:                len = strlen(tbuf);
                    795:                if ((len > 0) && (tbuf[len - 1] == '\n'))
                    796:                        tbuf[--len] = '\0';
                    797:
                    798:                ef = cvs_ent_open(line, O_WRONLY);
                    799:                if (ef == NULL)
                    800:                        return (-1);
                    801:
                    802:                cvs_ent_add(ef, ep);
                    803:                cvs_ent_close(ef);
1.1       jfb       804:        }
                    805:        else if (type == CVS_RESP_UPDEXIST) {
                    806:        }
                    807:        else if (type == CVS_RESP_UPDATED) {
                    808:        }
                    809:
1.6       jfb       810:        snprintf(path, sizeof(path), "%s%s", line, ep->ce_name);
                    811:        if (cvs_recvfile(path) < 0) {
1.1       jfb       812:                return (-1);
                    813:        }
                    814:
                    815:        /* now see if there is a checksum */
1.6       jfb       816:        if (cvs_fcksum != NULL) {
                    817:                if (cvs_cksum(line, cksum_buf, sizeof(cksum_buf)) < 0) {
                    818:                }
                    819:
                    820:                if (strcmp(cksum_buf, cvs_fcksum) != 0) {
                    821:                        cvs_log(LP_ERR, "checksum error on received file");
                    822:                        (void)unlink(line);
                    823:                }
1.1       jfb       824:
1.6       jfb       825:                free(cvs_fcksum);
                    826:                cvs_fcksum = NULL;
1.1       jfb       827:        }
                    828:
                    829:        return (0);
                    830: }
                    831:
                    832:
                    833: /*
                    834:  * cvs_resp_removed()
                    835:  *
                    836:  * Handler for the `Updated' response.
                    837:  */
                    838:
                    839: static int
                    840: cvs_resp_removed(int type, char *line)
                    841: {
                    842:        return (0);
                    843: }
                    844:
                    845:
                    846: /*
                    847:  * cvs_resp_mode()
                    848:  *
                    849:  * Handler for the `Mode' response.
                    850:  */
                    851:
                    852: static int
                    853: cvs_resp_mode(int type, char *line)
                    854: {
                    855:        if (cvs_strtomode(line, &cvs_lastmode) < 0) {
                    856:                return (-1);
                    857:        }
1.5       jfb       858:        return (0);
                    859: }
                    860:
                    861:
                    862: /*
                    863:  * cvs_resp_modxpand()
                    864:  *
                    865:  * Handler for the `Module-expansion' response.
                    866:  */
                    867:
                    868: static int
                    869: cvs_resp_modxpand(int type, char *line)
                    870: {
1.1       jfb       871:        return (0);
                    872: }
                    873:
                    874:
                    875: /*
                    876:  * cvs_sendfile()
                    877:  *
                    878:  * Send the mode and size of a file followed by the file's contents.
                    879:  * Returns 0 on success, or -1 on failure.
                    880:  */
                    881:
                    882: int
                    883: cvs_sendfile(const char *path)
                    884: {
                    885:        int fd;
                    886:        ssize_t ret;
                    887:        char buf[4096];
                    888:        struct stat st;
                    889:
                    890:        if (stat(path, &st) == -1) {
                    891:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
                    892:                return (-1);
                    893:        }
                    894:
                    895:        fd = open(path, O_RDONLY, 0);
                    896:        if (fd == -1) {
                    897:                return (-1);
                    898:        }
                    899:
                    900:        if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
                    901:                return (-1);
                    902:
                    903:        cvs_client_sendln(buf);
                    904:        snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
                    905:        cvs_client_sendln(buf);
                    906:
                    907:        while ((ret = read(fd, buf, sizeof(buf))) != 0) {
                    908:                if (ret == -1) {
                    909:                        cvs_log(LP_ERRNO, "failed to read file `%s'", path);
                    910:                        return (-1);
                    911:                }
                    912:
                    913:                cvs_client_sendraw(buf, (size_t)ret);
                    914:
                    915:        }
                    916:
                    917:        (void)close(fd);
                    918:
                    919:        return (0);
                    920: }
                    921:
                    922:
                    923: /*
                    924:  * cvs_recvfile()
                    925:  *
                    926:  * Receive the mode and size of a file followed the file's contents and
                    927:  * create or update the file whose path is <path> with the received
                    928:  * information.
                    929:  */
                    930:
                    931: int
                    932: cvs_recvfile(const char *path)
                    933: {
                    934:        int fd;
                    935:        mode_t mode;
                    936:        size_t len;
                    937:        ssize_t ret;
                    938:        off_t fsz, cnt;
                    939:        char buf[4096], *ep;
                    940:
                    941:        if ((cvs_client_getln(buf, sizeof(buf)) < 0) ||
                    942:            (cvs_strtomode(buf, &mode) < 0)) {
                    943:                return (-1);
                    944:        }
                    945:
                    946:        cvs_client_getln(buf, sizeof(buf));
                    947:
                    948:        fsz = (off_t)strtol(buf, &ep, 10);
                    949:        if (*ep != '\0') {
                    950:                cvs_log(LP_ERR, "parse error in file size transmission");
                    951:                return (-1);
                    952:        }
                    953:
1.6       jfb       954:        fd = open(path, O_WRONLY|O_CREAT, mode);
1.1       jfb       955:        if (fd == -1) {
                    956:                cvs_log(LP_ERRNO, "failed to open `%s'", path);
                    957:                return (-1);
                    958:        }
                    959:
                    960:        cnt = 0;
                    961:        do {
                    962:                len = MIN(sizeof(buf), (size_t)(fsz - cnt));
1.6       jfb       963:                if (len == 0)
                    964:                        break;
1.1       jfb       965:                ret = cvs_client_recvraw(buf, len);
                    966:                if (ret == -1) {
                    967:                        (void)close(fd);
                    968:                        (void)unlink(path);
                    969:                        return (-1);
                    970:                }
                    971:
                    972:                if (write(fd, buf, (size_t)ret) == -1) {
                    973:                        cvs_log(LP_ERRNO,
                    974:                            "failed to write contents to file `%s'", path);
                    975:                        (void)close(fd);
                    976:                        (void)unlink(path);
                    977:                        return (-1);
                    978:                }
                    979:
                    980:                cnt += (off_t)ret;
                    981:        } while (cnt < fsz);
                    982:
                    983:        (void)close(fd);
                    984:
                    985:        return (0);
                    986: }
1.12    ! jfb       987:
        !           988:
        !           989: #ifdef notyet
        !           990: /*
        !           991:  * cvs_sendreq()
        !           992:  *
        !           993:  * Send a request to the server of type <rid>, with optional arguments
        !           994:  * contained in <arg>, which should not be terminated by a newline.
        !           995:  * Returns 0 on success, or -1 on failure.
        !           996:  */
        !           997:
        !           998: int
        !           999: cvs_sendreq(struct cvs_root *root, u_int rid, const char *arg)
        !          1000: {
        !          1001:        int ret;
        !          1002:        size_t len;
        !          1003:        struct cvs_req *reqp;
        !          1004:
        !          1005:        if (root->cr_srvin == NULL) {
        !          1006:                cvs_log(LP_ERR, "cannot send request: Not connected");
        !          1007:                return (-1);
        !          1008:        }
        !          1009:
        !          1010:        reqp = cvs_req_getbyid(rid);
        !          1011:        if (reqp == NULL) {
        !          1012:                cvs_log(LP_ERR, "unsupported request type %u", rid);
        !          1013:                return (-1);
        !          1014:        }
        !          1015:
        !          1016:        snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n",
        !          1017:            reqp->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg);
        !          1018:
        !          1019:        if (cvs_server_inlog != NULL)
        !          1020:                fputs(cvs_client_buf, cvs_server_inlog);
        !          1021:
        !          1022:        ret = fputs(cvs_client_buf, root->cr_srvin);
        !          1023:        if (ret == EOF) {
        !          1024:                cvs_log(LP_ERRNO, "failed to send request to server");
        !          1025:                return (-1);
        !          1026:        }
        !          1027:
        !          1028:        if (reqp->req_flags & CVS_REQF_RESP)
        !          1029:                ret = cvs_getresp(root);
        !          1030:
        !          1031:        return (0);
        !          1032: }
        !          1033:
        !          1034:
        !          1035: /*
        !          1036:  * cvs_getresp()
        !          1037:  *
        !          1038:  * Get a response from the server.  This call will actually read and handle
        !          1039:  * responses from the server until one of the response handlers returns
        !          1040:  * non-zero (either an error occured or the end of the response was reached).
        !          1041:  * Returns the number of handled commands on success, or -1 on failure.
        !          1042:  */
        !          1043:
        !          1044: int
        !          1045: cvs_getresp(struct cvs_root *root)
        !          1046: {
        !          1047:        int nbcmd;
        !          1048:
        !          1049:        nbcmd = 0;
        !          1050:
        !          1051:        do {
        !          1052:                /* wait for incoming data */
        !          1053:                if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
        !          1054:                    root->cr_srvout) == NULL) {
        !          1055:                        if (feof(root->cr_srvout))
        !          1056:                                return (0);
        !          1057:                        cvs_log(LP_ERRNO,
        !          1058:                            "failed to read response from server");
        !          1059:                        return (-1);
        !          1060:                }
        !          1061:
        !          1062:                if (cvs_server_outlog != NULL)
        !          1063:                        fputs(cvs_client_buf, cvs_server_outlog);
        !          1064:
        !          1065:                if ((len = strlen(cvs_client_buf)) != 0) {
        !          1066:                        if (cvs_client_buf[len - 1] != '\n') {
        !          1067:                                /* truncated line */
        !          1068:                        }
        !          1069:                        else
        !          1070:                                cvs_client_buf[--len] = '\0';
        !          1071:                }
        !          1072:
        !          1073:                ret = cvs_resp_handle(cvs_client_buf);
        !          1074:                nbcmd++;
        !          1075:        } while (ret == 0);
        !          1076:
        !          1077:        if (ret > 0)
        !          1078:                ret = nbcmd;
        !          1079:        return (ret);
        !          1080: }
        !          1081:
        !          1082:
        !          1083: /*
        !          1084:  * cvs_sendresp()
        !          1085:  *
        !          1086:  * Send a response to the client of type <rid>, with optional arguments
        !          1087:  * contained in <arg>, which should not be terminated by a newline.
        !          1088:  * Returns 0 on success, or -1 on failure.
        !          1089:  */
        !          1090:
        !          1091: int
        !          1092: cvs_sendresp(u_int rid, const char *arg)
        !          1093: {
        !          1094:        int ret;
        !          1095:        size_t len;
        !          1096:        const char *resp;
        !          1097:
        !          1098:        resp = cvs_resp_getbyid(rid);
        !          1099:        if (reqp == NULL) {
        !          1100:                cvs_log(LP_ERR, "unsupported response type %u", rid);
        !          1101:                return (-1);
        !          1102:        }
        !          1103:
        !          1104:        snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n", resp,
        !          1105:            (arg == NULL) ? "" : arg);
        !          1106:
        !          1107:        ret = fputs(resp, stdout);
        !          1108:        if (ret == EOF) {
        !          1109:                cvs_log(LP_ERRNO, "failed to send response to client");
        !          1110:        }
        !          1111:        else {
        !          1112:                if (arg != NULL)
        !          1113:                        ret = fprintf(stdout, " %s", arg);
        !          1114:                putc('\n', stdout);
        !          1115:        }
        !          1116:        return (0);
        !          1117: }
        !          1118:
        !          1119:
        !          1120: /*
        !          1121:  * cvs_getreq()
        !          1122:  *
        !          1123:  * Get a request from the client.
        !          1124:  */
        !          1125:
        !          1126: int
        !          1127: cvs_getreq(void)
        !          1128: {
        !          1129:        int nbcmd;
        !          1130:
        !          1131:        nbcmd = 0;
        !          1132:
        !          1133:        do {
        !          1134:                /* wait for incoming data */
        !          1135:                if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
        !          1136:                    stdin) == NULL) {
        !          1137:                        if (feof(stdin))
        !          1138:                                return (0);
        !          1139:                        cvs_log(LP_ERRNO,
        !          1140:                            "failed to read request from client");
        !          1141:                        return (-1);
        !          1142:                }
        !          1143:
        !          1144:                if ((len = strlen(cvs_client_buf)) != 0) {
        !          1145:                        if (cvs_client_buf[len - 1] != '\n') {
        !          1146:                                /* truncated line */
        !          1147:                        }
        !          1148:                        else
        !          1149:                                cvs_client_buf[--len] = '\0';
        !          1150:                }
        !          1151:
        !          1152:                ret = cvs_resp_handle(cvs_client_buf);
        !          1153:        } while (ret == 0);
        !          1154: }
        !          1155:
        !          1156:
        !          1157: /*
        !          1158:  * cvs_connect()
        !          1159:  *
        !          1160:  * Open a client connection to the cvs server whose address is given in
        !          1161:  * the <root> variable.  The method used to connect depends on the
        !          1162:  * setting of the CVS_RSH variable.
        !          1163:  * Returns 0 on success, or -1 on failure.
        !          1164:  */
        !          1165:
        !          1166: int
        !          1167: cvs_connect(struct cvsroot *root)
        !          1168: {
        !          1169:        int i, argc, infd[2], outfd[2];
        !          1170:        pid_t pid;
        !          1171:        char *argv[16], *cvs_server_cmd;
        !          1172:
        !          1173:        if (pipe(infd) == -1) {
        !          1174:                cvs_log(LP_ERRNO,
        !          1175:                    "failed to create input pipe for client connection");
        !          1176:                return (-1);
        !          1177:        }
        !          1178:
        !          1179:        if (pipe(outfd) == -1) {
        !          1180:                cvs_log(LP_ERRNO,
        !          1181:                    "failed to create output pipe for client connection");
        !          1182:                (void)close(infd[0]);
        !          1183:                (void)close(infd[1]);
        !          1184:                return (-1);
        !          1185:        }
        !          1186:
        !          1187:        pid = fork();
        !          1188:        if (pid == -1) {
        !          1189:                cvs_log(LP_ERRNO, "failed to fork for cvs server connection");
        !          1190:                return (-1);
        !          1191:        }
        !          1192:        if (pid == 0) {
        !          1193:                if ((dup2(infd[0], STDIN_FILENO) == -1) ||
        !          1194:                    (dup2(outfd[1], STDOUT_FILENO) == -1)) {
        !          1195:                        cvs_log(LP_ERRNO,
        !          1196:                            "failed to setup standard streams for cvs server");
        !          1197:                        return (-1);
        !          1198:                }
        !          1199:                (void)close(infd[1]);
        !          1200:                (void)close(outfd[0]);
        !          1201:
        !          1202:                argc = 0;
        !          1203:                argv[argc++] = cvs_rsh;
        !          1204:
        !          1205:                if (root->cr_user != NULL) {
        !          1206:                        argv[argc++] = "-l";
        !          1207:                        argv[argc++] = root->cr_user;
        !          1208:                }
        !          1209:
        !          1210:
        !          1211:                cvs_server_cmd = getenv("CVS_SERVER");
        !          1212:                if (cvs_server_cmd == NULL)
        !          1213:                        cvs_server_cmd = "cvs";
        !          1214:
        !          1215:                argv[argc++] = root->cr_host;
        !          1216:                argv[argc++] = cvs_server_cmd;
        !          1217:                argv[argc++] = "server";
        !          1218:                argv[argc] = NULL;
        !          1219:
        !          1220:                for (i = 0; i < argc; i++)
        !          1221:                        printf("argv[%d] = `%s'\n", i, argv[i]);
        !          1222:
        !          1223:                execvp(argv[0], argv);
        !          1224:                cvs_log(LP_ERRNO, "failed to exec");
        !          1225:                exit(EX_OSERR);
        !          1226:        }
        !          1227:
        !          1228:        /* we are the parent */
        !          1229:        cvs_server_infd = infd[1];
        !          1230:        cvs_server_outfd = outfd[0];
        !          1231:
        !          1232:        cvs_server_in = fdopen(cvs_server_infd, "w");
        !          1233:        if (cvs_server_in == NULL) {
        !          1234:                cvs_log(LP_ERRNO, "failed to create pipe stream");
        !          1235:                return (-1);
        !          1236:        }
        !          1237:
        !          1238:        cvs_server_out = fdopen(cvs_server_outfd, "r");
        !          1239:        if (cvs_server_out == NULL) {
        !          1240:                cvs_log(LP_ERRNO, "failed to create pipe stream");
        !          1241:                return (-1);
        !          1242:        }
        !          1243:        root->cr_srvin = cvs_server_in;
        !          1244:        root->cr_srvout = cvs_server_out;
        !          1245:
        !          1246:        /* make the streams line-buffered */
        !          1247:        setvbuf(cvs_server_in, NULL, _IOLBF, 0);
        !          1248:        setvbuf(cvs_server_out, NULL, _IOLBF, 0);
        !          1249:
        !          1250:        (void)close(infd[0]);
        !          1251:        (void)close(outfd[1]);
        !          1252:
        !          1253:        cvs_client_initlog();
        !          1254:
        !          1255:        cvs_client_sendinfo();
        !          1256:
        !          1257: #ifdef CVS_ZLIB
        !          1258:        /* if compression was requested, initialize it */
        !          1259: #endif
        !          1260:
        !          1261:        return (0);
        !          1262: }
        !          1263:
        !          1264:
        !          1265: /*
        !          1266:  * cvs_disconnect()
        !          1267:  *
        !          1268:  * Disconnect from the cvs server.
        !          1269:  */
        !          1270:
        !          1271: void
        !          1272: cvs_disconnect(struct cvsroot *root)
        !          1273: {
        !          1274:        cvs_log(LP_DEBUG, "closing client connection");
        !          1275:        if (root->cr_srvin != NULL) {
        !          1276:                (void)fclose(root->cr_srvin);
        !          1277:                root->cr_srvin = NULL;
        !          1278:        }
        !          1279:        if (root->cr_srvout != NULL) {
        !          1280:                (void)fclose(root->cr_srvout);
        !          1281:                root->cr_srvout = NULL;
        !          1282:        }
        !          1283:
        !          1284:        if (cvs_server_inlog != NULL)
        !          1285:                fclose(cvs_server_inlog);
        !          1286:        if (cvs_server_outlog != NULL)
        !          1287:                fclose(cvs_server_outlog);
        !          1288: }
        !          1289:
        !          1290:
        !          1291: /*
        !          1292:  * cvs_sendln()
        !          1293:  *
        !          1294:  * Send a single line <line> string to the server.  The line is sent as is,
        !          1295:  * without any modifications.
        !          1296:  * Returns 0 on success, or -1 on failure.
        !          1297:  */
        !          1298:
        !          1299: int
        !          1300: cvs_sendln(struct cvsroot *root, const char *line)
        !          1301: {
        !          1302:        int nl;
        !          1303:        size_t len;
        !          1304:
        !          1305:        nl = 0;
        !          1306:        len = strlen(line);
        !          1307:
        !          1308:        if ((len > 0) && (line[len - 1] != '\n'))
        !          1309:                nl = 1;
        !          1310:
        !          1311:        if (cvs_server_inlog != NULL) {
        !          1312:                fputs(line, cvs_server_inlog);
        !          1313:                if (nl)
        !          1314:                        fputc('\n', cvs_server_inlog);
        !          1315:        }
        !          1316:        fputs(line, root->cr_srvin);
        !          1317:        if (nl)
        !          1318:                fputc('\n', root->cr_srvin);
        !          1319:
        !          1320:        return (0);
        !          1321: }
        !          1322:
        !          1323:
        !          1324: /*
        !          1325:  * cvs_client_sendraw()
        !          1326:  *
        !          1327:  * Send the first <len> bytes from the buffer <src> to the server.
        !          1328:  */
        !          1329:
        !          1330: int
        !          1331: cvs_client_sendraw(const void *src, size_t len)
        !          1332: {
        !          1333:        if (cvs_server_inlog != NULL)
        !          1334:                fwrite(src, sizeof(char), len, cvs_server_inlog);
        !          1335:        if (fwrite(src, sizeof(char), len, cvs_server_in) < len) {
        !          1336:                return (-1);
        !          1337:        }
        !          1338:
        !          1339:        return (0);
        !          1340: }
        !          1341:
        !          1342:
        !          1343: /*
        !          1344:  * cvs_client_recvraw()
        !          1345:  *
        !          1346:  * Receive the first <len> bytes from the buffer <src> to the server.
        !          1347:  */
        !          1348:
        !          1349: ssize_t
        !          1350: cvs_client_recvraw(void *dst, size_t len)
        !          1351: {
        !          1352:        size_t ret;
        !          1353:
        !          1354:        ret = fread(dst, sizeof(char), len, cvs_server_out);
        !          1355:        if (ret == 0)
        !          1356:                return (-1);
        !          1357:        if (cvs_server_outlog != NULL)
        !          1358:                fwrite(dst, sizeof(char), len, cvs_server_outlog);
        !          1359:        return (ssize_t)ret;
        !          1360: }
        !          1361:
        !          1362:
        !          1363: /*
        !          1364:  * cvs_client_getln()
        !          1365:  *
        !          1366:  * Get a line from the server's output and store it in <lbuf>.  The terminating
        !          1367:  * newline character is stripped from the result.
        !          1368:  */
        !          1369:
        !          1370: int
        !          1371: cvs_client_getln(char *lbuf, size_t len)
        !          1372: {
        !          1373:        size_t rlen;
        !          1374:
        !          1375:        if (fgets(lbuf, len, cvs_server_out) == NULL) {
        !          1376:                if (ferror(cvs_server_out)) {
        !          1377:                        cvs_log(LP_ERRNO, "failed to read line from server");
        !          1378:                        return (-1);
        !          1379:                }
        !          1380:
        !          1381:                if (feof(cvs_server_out))
        !          1382:                        *lbuf = '\0';
        !          1383:        }
        !          1384:
        !          1385:        if (cvs_server_outlog != NULL)
        !          1386:                fputs(lbuf, cvs_server_outlog);
        !          1387:
        !          1388:        rlen = strlen(lbuf);
        !          1389:        if ((rlen > 0) && (lbuf[rlen - 1] == '\n'))
        !          1390:                lbuf[--rlen] = '\0';
        !          1391:
        !          1392:        return (0);
        !          1393: }
        !          1394:
        !          1395:
        !          1396: /*
        !          1397:  * cvs_client_sendinfo()
        !          1398:  *
        !          1399:  * Initialize the connection status by first requesting the list of
        !          1400:  * supported requests from the server.  Then, we send the CVSROOT variable
        !          1401:  * with the `Root' request.
        !          1402:  * Returns 0 on success, or -1 on failure.
        !          1403:  */
        !          1404:
        !          1405: static int
        !          1406: cvs_client_sendinfo(void)
        !          1407: {
        !          1408:        char *vresp;
        !          1409:        /*
        !          1410:         * First, send the server the list of valid responses, then ask
        !          1411:         * for valid requests
        !          1412:         */
        !          1413:
        !          1414:        vresp = cvs_resp_getvalid();
        !          1415:        if (vresp == NULL) {
        !          1416:                cvs_log(LP_ERR, "can't generate list of valid responses");
        !          1417:                return (-1);
        !          1418:        }
        !          1419:
        !          1420:        if (cvs_client_sendreq(CVS_REQ_VALIDRESP, vresp, 0) < 0) {
        !          1421:        }
        !          1422:        free(vresp);
        !          1423:
        !          1424:        if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
        !          1425:                cvs_log(LP_ERR, "failed to get valid requests from server");
        !          1426:                return (-1);
        !          1427:        }
        !          1428:
        !          1429:        /* now share our global options with the server */
        !          1430:        if (verbosity == 1)
        !          1431:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
        !          1432:        else if (verbosity == 0)
        !          1433:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
        !          1434:
        !          1435:        if (cvs_nolog)
        !          1436:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
        !          1437:        if (cvs_readonly)
        !          1438:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
        !          1439:        if (cvs_trace)
        !          1440:                cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
        !          1441:
        !          1442:        /* now send the CVSROOT to the server */
        !          1443:        if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
        !          1444:                return (-1);
        !          1445:
        !          1446:        /* not sure why, but we have to send this */
        !          1447:        if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
        !          1448:                return (-1);
        !          1449:
        !          1450:        return (0);
        !          1451: }
        !          1452:
        !          1453:
        !          1454: /*
        !          1455:  * cvs_client_senddir()
        !          1456:  *
        !          1457:  * Send a `Directory' request along with the 2 paths that follow it.
        !          1458:  */
        !          1459:
        !          1460: int
        !          1461: cvs_client_senddir(const char *dir)
        !          1462: {
        !          1463:        char repo[MAXPATHLEN], buf[MAXPATHLEN];
        !          1464:
        !          1465:        if (cvs_readrepo(dir, repo, sizeof(repo)) < 0) {
        !          1466:                repo[0] = '\0';
        !          1467:                strlcpy(buf, cvs_root->cr_dir, sizeof(buf));
        !          1468:        }
        !          1469:        else {
        !          1470:                snprintf(buf, sizeof(buf), "%s/%s", cvs_root->cr_dir, repo);
        !          1471:        }
        !          1472:
        !          1473:        if ((cvs_client_sendreq(CVS_REQ_DIRECTORY, dir, 0) < 0) ||
        !          1474:            (cvs_client_sendln(buf) < 0))
        !          1475:                return (-1);
        !          1476:
        !          1477:        return (0);
        !          1478: }
        !          1479:
        !          1480:
        !          1481: /*
        !          1482:  * cvs_client_sendarg()
        !          1483:  *
        !          1484:  * Send the argument <arg> to the server.  The argument <append> is used to
        !          1485:  * determine if the argument should be simply appended to the last argument
        !          1486:  * sent or if it should be created as a new argument (0).
        !          1487:  */
        !          1488:
        !          1489: int
        !          1490: cvs_client_sendarg(const char *arg, int append)
        !          1491: {
        !          1492:        return cvs_client_sendreq(((append == 0) ?
        !          1493:            CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg, 0);
        !          1494: }
        !          1495:
        !          1496:
        !          1497: /*
        !          1498:  * cvs_client_sendentry()
        !          1499:  *
        !          1500:  * Send an `Entry' request to the server along with the mandatory fields from
        !          1501:  * the CVS entry <ent> (which are the name and revision).
        !          1502:  */
        !          1503:
        !          1504: int
        !          1505: cvs_client_sendentry(const struct cvs_ent *ent)
        !          1506: {
        !          1507:        char ebuf[128], numbuf[64];
        !          1508:
        !          1509:        snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name,
        !          1510:            rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf)));
        !          1511:
        !          1512:        return cvs_client_sendreq(CVS_REQ_ENTRY, ebuf, 0);
        !          1513: }
        !          1514:
        !          1515:
        !          1516: /*
        !          1517:  * cvs_client_initlog()
        !          1518:  *
        !          1519:  * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
        !          1520:  * set.  In this case, the variable's value is used as a path to which the
        !          1521:  * appropriate suffix is added (".in" for server input and ".out" for server
        !          1522:  * output.
        !          1523:  * Returns 0 on success, or -1 on failure.
        !          1524:  */
        !          1525:
        !          1526: static int
        !          1527: cvs_client_initlog(void)
        !          1528: {
        !          1529:        char *env, fpath[MAXPATHLEN];
        !          1530:
        !          1531:        env = getenv("CVS_CLIENT_LOG");
        !          1532:        if (env == NULL)
        !          1533:                return (0);
        !          1534:
        !          1535:        strlcpy(fpath, env, sizeof(fpath));
        !          1536:        strlcat(fpath, ".in", sizeof(fpath));
        !          1537:        cvs_server_inlog = fopen(fpath, "w");
        !          1538:        if (cvs_server_inlog == NULL) {
        !          1539:                cvs_log(LP_ERRNO, "failed to open server input log `%s'",
        !          1540:                    fpath);
        !          1541:                return (-1);
        !          1542:        }
        !          1543:
        !          1544:        strlcpy(fpath, env, sizeof(fpath));
        !          1545:        strlcat(fpath, ".out", sizeof(fpath));
        !          1546:        cvs_server_outlog = fopen(fpath, "w");
        !          1547:        if (cvs_server_outlog == NULL) {
        !          1548:                cvs_log(LP_ERRNO, "failed to open server output log `%s'",
        !          1549:                    fpath);
        !          1550:                return (-1);
        !          1551:        }
        !          1552:
        !          1553:        /* make the streams line-buffered */
        !          1554:        setvbuf(cvs_server_inlog, NULL, _IOLBF, 0);
        !          1555:        setvbuf(cvs_server_outlog, NULL, _IOLBF, 0);
        !          1556:
        !          1557:        return (0);
        !          1558: }
        !          1559:
        !          1560:
        !          1561: /*
        !          1562:  * cvs_client_sendfile()
        !          1563:  *
        !          1564:  */
        !          1565:
        !          1566: int
        !          1567: cvs_client_sendfile(const char *path)
        !          1568: {
        !          1569:        int fd;
        !          1570:        ssize_t ret;
        !          1571:        char buf[4096];
        !          1572:        struct stat st;
        !          1573:
        !          1574:        if (stat(path, &st) == -1) {
        !          1575:                cvs_log(LP_ERRNO, "failed to stat `%s'", path);
        !          1576:                return (-1);
        !          1577:        }
        !          1578:
        !          1579:        fd = open(path, O_RDONLY, 0);
        !          1580:        if (fd == -1) {
        !          1581:                return (-1);
        !          1582:        }
        !          1583:
        !          1584:        if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
        !          1585:                return (-1);
        !          1586:
        !          1587:        cvs_client_sendln(buf);
        !          1588:        snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
        !          1589:        cvs_client_sendln(buf);
        !          1590:
        !          1591:        while ((ret = read(fd, buf, sizeof(buf))) != 0) {
        !          1592:                if (ret == -1) {
        !          1593:                        cvs_log(LP_ERRNO, "failed to read file `%s'", path);
        !          1594:                        return (-1);
        !          1595:                }
        !          1596:
        !          1597:                cvs_client_sendraw(buf, (size_t)ret);
        !          1598:
        !          1599:        }
        !          1600:
        !          1601:        (void)close(fd);
        !          1602:
        !          1603:        return (0);
        !          1604: }
        !          1605: #endif