1 /* $OpenBSD: client.c,v 1.56 2007/01/28 02:04:45 joris Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "includes.h" 19 20 #include "cvs.h" 21 #include "log.h" 22 #include "diff.h" 23 #include "remote.h" 24 25 struct cvs_req cvs_requests[] = { 26 /* this is what our client will use, the server should support it */ 27 { "Root", 1, cvs_server_root, REQ_NEEDED }, 28 { "Valid-responses", 1, cvs_server_validresp, REQ_NEEDED }, 29 { "valid-requests", 1, cvs_server_validreq, REQ_NEEDED }, 30 { "Directory", 0, cvs_server_directory, REQ_NEEDED }, 31 { "Static-directory", 0, cvs_server_static_directory, REQ_NEEDED }, 32 { "Sticky", 0, cvs_server_sticky, REQ_NEEDED }, 33 { "Entry", 0, cvs_server_entry, REQ_NEEDED }, 34 { "Modified", 0, cvs_server_modified, REQ_NEEDED }, 35 { "UseUnchanged", 0, cvs_server_useunchanged, REQ_NEEDED }, 36 { "Unchanged", 0, cvs_server_unchanged, REQ_NEEDED }, 37 { "Questionable", 0, cvs_server_questionable, REQ_NEEDED }, 38 { "Argument", 0, cvs_server_argument, REQ_NEEDED }, 39 { "Argumentx", 0, cvs_server_argumentx, REQ_NEEDED }, 40 { "Global_option", 0, cvs_server_globalopt, REQ_NEEDED }, 41 { "Set", 0, cvs_server_set, REQ_NEEDED }, 42 43 /* 44 * used to tell the server what is going on in our 45 * working copy, unsupported until we are told otherwise 46 */ 47 { "Max-dotdot", 0, NULL, 0 }, 48 { "Checkin-prog", 0, NULL, 0 }, 49 { "Update-prog", 0, NULL, 0 }, 50 { "Kopt", 0, NULL, 0 }, 51 { "Checkin-time", 0, NULL, 0 }, 52 { "Is-modified", 0, NULL, 0 }, 53 { "Notify", 0, NULL, 0 }, 54 { "Case", 0, NULL, 0 }, 55 { "Gzip-stream", 0, NULL, 0 }, 56 { "wrapper-sendme-rcsOptions", 0, NULL, 0 }, 57 { "Kerberos-encrypt", 0, NULL, 0 }, 58 { "Gssapi-encrypt", 0, NULL, 0 }, 59 { "Gssapi-authenticate", 0, NULL, 0 }, 60 { "expand-modules", 0, NULL, 0 }, 61 62 /* commands that might be supported */ 63 { "ci", 0, cvs_server_commit, 0 }, 64 { "co", 0, cvs_server_checkout, 0 }, 65 { "update", 0, cvs_server_update, 0 }, 66 { "diff", 0, cvs_server_diff, 0 }, 67 { "log", 0, cvs_server_log, 0 }, 68 { "rlog", 0, NULL, 0 }, 69 { "add", 0, cvs_server_add, 0 }, 70 { "remove", 0, cvs_server_remove, 0 }, 71 { "update-patches", 0, cvs_server_update_patches, 0 }, 72 { "gzip-file-contents", 0, NULL, 0 }, 73 { "status", 0, cvs_server_status, 0 }, 74 { "rdiff", 0, NULL, 0 }, 75 { "tag", 0, cvs_server_tag, 0 }, 76 { "rtag", 0, NULL, 0 }, 77 { "import", 0, cvs_server_import, 0 }, 78 { "admin", 0, cvs_server_admin, 0 }, 79 { "export", 0, NULL, 0 }, 80 { "history", 0, NULL, 0 }, 81 { "release", 0, NULL, 0 }, 82 { "watch-on", 0, NULL, 0 }, 83 { "watch-off", 0, NULL, 0 }, 84 { "watch-add", 0, NULL, 0 }, 85 { "watch-remove", 0, NULL, 0 }, 86 { "watchers", 0, NULL, 0 }, 87 { "editors", 0, NULL, 0 }, 88 { "init", 0, cvs_server_init, 0 }, 89 { "annotate", 0, cvs_server_annotate, 0 }, 90 { "rannotate", 0, NULL, 0 }, 91 { "noop", 0, NULL, 0 }, 92 { "version", 0, cvs_server_version, 0 }, 93 { "", -1, NULL, 0 } 94 }; 95 96 static void client_check_directory(char *); 97 static char *client_get_supported_responses(void); 98 static char *lastdir = NULL; 99 static int end_of_response = 0; 100 101 static void cvs_client_initlog(void); 102 103 /* 104 * File descriptors for protocol logging when the CVS_CLIENT_LOG environment 105 * variable is set. 106 */ 107 static int cvs_client_logon = 0; 108 int cvs_client_inlog_fd = -1; 109 int cvs_client_outlog_fd = -1; 110 111 112 int server_response = SERVER_OK; 113 114 static char * 115 client_get_supported_responses(void) 116 { 117 BUF *bp; 118 char *d; 119 int i, first; 120 121 first = 0; 122 bp = cvs_buf_alloc(512, BUF_AUTOEXT); 123 for (i = 0; cvs_responses[i].supported != -1; i++) { 124 if (cvs_responses[i].hdlr == NULL) 125 continue; 126 127 if (first != 0) 128 cvs_buf_append(bp, " ", 1); 129 else 130 first++; 131 cvs_buf_append(bp, cvs_responses[i].name, 132 strlen(cvs_responses[i].name)); 133 } 134 135 cvs_buf_putc(bp, '\0'); 136 d = cvs_buf_release(bp); 137 return (d); 138 } 139 140 static void 141 client_check_directory(char *data) 142 { 143 int l; 144 CVSENTRIES *entlist; 145 char entry[CVS_ENT_MAXLINELEN], *parent, *base; 146 147 STRIP_SLASH(data); 148 149 cvs_mkpath(data); 150 151 if ((base = basename(data)) == NULL) 152 fatal("client_check_directory: overflow"); 153 154 if ((parent = dirname(data)) == NULL) 155 fatal("client_check_directory: overflow"); 156 157 if (!strcmp(parent, ".")) 158 return; 159 160 l = snprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", base); 161 if (l == -1 || l >= CVS_ENT_MAXLINELEN) 162 fatal("client_check_directory: overflow"); 163 164 entlist = cvs_ent_open(parent); 165 cvs_ent_add(entlist, entry); 166 cvs_ent_close(entlist, ENT_SYNC); 167 } 168 169 void 170 cvs_client_connect_to_server(void) 171 { 172 struct cvs_var *vp; 173 char *cmd, *argv[9], *resp; 174 int ifd[2], ofd[2], argc; 175 176 if (cvs_server_active == 1) 177 fatal("cvs_client_connect: I was already connected to server"); 178 179 switch (current_cvsroot->cr_method) { 180 case CVS_METHOD_PSERVER: 181 case CVS_METHOD_KSERVER: 182 case CVS_METHOD_GSERVER: 183 case CVS_METHOD_FORK: 184 case CVS_METHOD_EXT: 185 fatal("the specified connection method is not supported"); 186 default: 187 break; 188 } 189 190 if (pipe(ifd) == -1) 191 fatal("cvs_client_connect: %s", strerror(errno)); 192 if (pipe(ofd) == -1) 193 fatal("cvs_client_connect: %s", strerror(errno)); 194 195 switch (fork()) { 196 case -1: 197 fatal("cvs_client_connect: fork failed: %s", strerror(errno)); 198 case 0: 199 if (dup2(ifd[0], STDIN_FILENO) == -1) 200 fatal("cvs_client_connect: %s", strerror(errno)); 201 if (dup2(ofd[1], STDOUT_FILENO) == -1) 202 fatal("cvs_client_connect: %s", strerror(errno)); 203 204 close(ifd[1]); 205 close(ofd[0]); 206 207 if ((cmd = getenv("CVS_SERVER")) == NULL) 208 cmd = CVS_SERVER_DEFAULT; 209 210 argc = 0; 211 argv[argc++] = cvs_rsh; 212 213 if (current_cvsroot->cr_user != NULL) { 214 argv[argc++] = "-l"; 215 argv[argc++] = current_cvsroot->cr_user; 216 } 217 218 argv[argc++] = current_cvsroot->cr_host; 219 argv[argc++] = cmd; 220 argv[argc++] = "server"; 221 argv[argc] = NULL; 222 223 cvs_log(LP_TRACE, "connecting to server %s", 224 current_cvsroot->cr_host); 225 226 execvp(argv[0], argv); 227 fatal("cvs_client_connect: failed to execute cvs server"); 228 default: 229 break; 230 } 231 232 close(ifd[0]); 233 close(ofd[1]); 234 235 if ((current_cvsroot->cr_srvin = fdopen(ifd[1], "w")) == NULL) 236 fatal("cvs_client_connect: %s", strerror(errno)); 237 if ((current_cvsroot->cr_srvout = fdopen(ofd[0], "r")) == NULL) 238 fatal("cvs_client_connect: %s", strerror(errno)); 239 240 setvbuf(current_cvsroot->cr_srvin, NULL,_IOLBF, 0); 241 setvbuf(current_cvsroot->cr_srvout, NULL, _IOLBF, 0); 242 243 cvs_client_initlog(); 244 245 if (cvs_cmdop != CVS_OP_INIT) 246 cvs_client_send_request("Root %s", current_cvsroot->cr_dir); 247 248 resp = client_get_supported_responses(); 249 cvs_client_send_request("Valid-responses %s", resp); 250 xfree(resp); 251 252 cvs_client_send_request("valid-requests"); 253 cvs_client_get_responses(); 254 255 cvs_client_send_request("UseUnchanged"); 256 257 if (cvs_nolog == 1) 258 cvs_client_send_request("Global_option -l"); 259 260 if (cvs_noexec == 1) 261 cvs_client_send_request("Global_option -n"); 262 263 if (verbosity == 0) 264 cvs_client_send_request("Global_option -Q"); 265 266 /* Be quiet. This is the default in OpenCVS. */ 267 cvs_client_send_request("Global_option -q"); 268 269 if (cvs_readonly == 1) 270 cvs_client_send_request("Global_option -r"); 271 272 if (cvs_trace == 1) 273 cvs_client_send_request("Global_option -t"); 274 275 if (verbosity == 2) 276 cvs_client_send_request("Global_option -V"); 277 278 /* XXX: If 'Set' is supported? */ 279 TAILQ_FOREACH(vp, &cvs_variables, cv_link) 280 cvs_client_send_request("Set %s=%s", vp->cv_name, vp->cv_val); 281 } 282 283 void 284 cvs_client_send_request(char *fmt, ...) 285 { 286 va_list ap; 287 char *data, *s; 288 struct cvs_req *req; 289 290 va_start(ap, fmt); 291 vasprintf(&data, fmt, ap); 292 va_end(ap); 293 294 if ((s = strchr(data, ' ')) != NULL) 295 *s = '\0'; 296 297 req = cvs_remote_get_request_info(data); 298 if (req == NULL) 299 fatal("'%s' is an unknown request", data); 300 301 if (req->supported != 1) 302 fatal("remote cvs server does not support '%s'", data); 303 304 if (s != NULL) 305 *s = ' '; 306 307 cvs_log(LP_TRACE, "%s", data); 308 309 if (cvs_client_inlog_fd != -1) { 310 BUF *bp; 311 312 bp = cvs_buf_alloc(strlen(data), BUF_AUTOEXT); 313 314 if (cvs_buf_append(bp, data, strlen(data)) < 0) 315 fatal("cvs_client_send_request: cvs_buf_append"); 316 317 cvs_buf_putc(bp, '\n'); 318 319 if (cvs_buf_write_fd(bp, cvs_client_inlog_fd) < 0) 320 fatal("cvs_client_send_request: cvs_buf_write_fd"); 321 322 cvs_buf_free(bp); 323 } 324 325 cvs_remote_output(data); 326 xfree(data); 327 } 328 329 void 330 cvs_client_read_response(void) 331 { 332 char *cmd, *data; 333 struct cvs_resp *resp; 334 335 cmd = cvs_remote_input(); 336 if ((data = strchr(cmd, ' ')) != NULL) 337 (*data++) = '\0'; 338 339 resp = cvs_remote_get_response_info(cmd); 340 if (resp == NULL) 341 fatal("response '%s' is not supported by our client", cmd); 342 343 if (resp->hdlr == NULL) 344 fatal("opencvs client does not support '%s'", cmd); 345 346 (*resp->hdlr)(data); 347 348 xfree(cmd); 349 } 350 351 void 352 cvs_client_get_responses(void) 353 { 354 while (end_of_response != 1) 355 cvs_client_read_response(); 356 357 end_of_response = 0; 358 } 359 360 void 361 cvs_client_senddir(const char *dir) 362 { 363 struct stat st; 364 int nb; 365 char *d, *date, fpath[MAXPATHLEN], repo[MAXPATHLEN], *tag; 366 367 d = NULL; 368 369 if (lastdir != NULL && !strcmp(dir, lastdir)) 370 return; 371 372 cvs_get_repository_path(dir, repo, MAXPATHLEN); 373 374 cvs_client_send_request("Directory %s\n%s", dir, repo); 375 376 if (cvs_path_cat(dir, CVS_PATH_STATICENTRIES, fpath, MAXPATHLEN) >= 377 MAXPATHLEN) 378 fatal("cvs_client_senddir: truncation"); 379 380 if (stat(fpath, &st) == 0 && (st.st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) 381 cvs_client_send_request("Static-directory"); 382 383 d = xstrdup(dir); 384 cvs_parse_tagfile(d, &tag, &date, &nb); 385 386 if (tag != NULL || date != NULL) { 387 char buf[128]; 388 389 if (tag != NULL && nb != NULL) { 390 if (strlcpy(buf, "N", sizeof(buf)) >= sizeof(buf)) 391 fatal("cvs_client_senddir: truncation"); 392 } else if (tag != NULL) { 393 if (strlcpy(buf, "T", sizeof(buf)) >= sizeof(buf)) 394 fatal("cvs_client_senddir: truncation"); 395 } else { 396 if (strlcpy(buf, "D", sizeof(buf)) >= sizeof(buf)) 397 fatal("cvs_client_senddir: truncation"); 398 } 399 400 if (strlcat(buf, tag ? tag : date, sizeof(buf)) >= sizeof(buf)) 401 fatal("cvs_client_senddir: truncation"); 402 403 cvs_client_send_request("Sticky %s", buf); 404 405 if (tag != NULL) 406 xfree(tag); 407 if (date != NULL) 408 xfree(date); 409 } 410 if (d != NULL) 411 xfree(d); 412 413 if (lastdir != NULL) 414 xfree(lastdir); 415 lastdir = xstrdup(dir); 416 } 417 418 void 419 cvs_client_sendfile(struct cvs_file *cf) 420 { 421 int l; 422 size_t len; 423 char rev[16], timebuf[64], sticky[32]; 424 425 if (cf->file_type != CVS_FILE) 426 return; 427 428 cvs_client_senddir(cf->file_wd); 429 cvs_remote_classify_file(cf); 430 431 if (cf->file_type == CVS_DIR) 432 return; 433 434 if (cf->file_ent != NULL) { 435 if (cf->file_status == FILE_ADDED) { 436 len = strlcpy(rev, "0", sizeof(rev)); 437 if (len >= sizeof(rev)) 438 fatal("cvs_client_sendfile: truncation"); 439 440 len = strlcpy(timebuf, "Initial ", sizeof(timebuf)); 441 if (len >= sizeof(timebuf)) 442 fatal("cvs_client_sendfile: truncation"); 443 444 len = strlcat(timebuf, cf->file_name, sizeof(timebuf)); 445 if (len >= sizeof(timebuf)) 446 fatal("cvs_client_sendfile: truncation"); 447 } else { 448 rcsnum_tostr(cf->file_ent->ce_rev, rev, sizeof(rev)); 449 ctime_r(&cf->file_ent->ce_mtime, timebuf); 450 } 451 452 if (cf->file_ent->ce_conflict == NULL) { 453 if (timebuf[strlen(timebuf) - 1] == '\n') 454 timebuf[strlen(timebuf) - 1] = '\0'; 455 } else { 456 len = strlcpy(timebuf, cf->file_ent->ce_conflict, 457 sizeof(timebuf)); 458 if (len >= sizeof(timebuf)) 459 fatal("cvs_client_sendfile: truncation"); 460 len = strlcat(timebuf, "+", sizeof(timebuf)); 461 if (len >= sizeof(timebuf)) 462 fatal("cvs_client_sendfile: truncation"); 463 } 464 465 sticky[0] = '\0'; 466 if (cf->file_ent->ce_tag != NULL) { 467 l = snprintf(sticky, sizeof(sticky), "T%s", 468 cf->file_ent->ce_tag); 469 if (l == -1 || l >= (int)sizeof(sticky)) 470 fatal("cvs_client_sendfile: overflow"); 471 } 472 473 cvs_client_send_request("Entry /%s/%s%s/%s/%s/%s", 474 cf->file_name, (cf->file_status == FILE_REMOVED) ? "-" : "", 475 rev, timebuf, cf->file_ent->ce_opts ? 476 cf->file_ent->ce_opts : "", sticky); 477 } 478 479 switch (cf->file_status) { 480 case FILE_UNKNOWN: 481 if (cf->fd != -1) 482 cvs_client_send_request("Questionable %s", 483 cf->file_name); 484 break; 485 case FILE_ADDED: 486 case FILE_MODIFIED: 487 cvs_client_send_request("Modified %s", cf->file_name); 488 cvs_remote_send_file(cf->file_path); 489 break; 490 case FILE_UPTODATE: 491 cvs_client_send_request("Unchanged %s", cf->file_name); 492 break; 493 } 494 } 495 496 void 497 cvs_client_send_files(char **argv, int argc) 498 { 499 int i; 500 501 for (i = 0; i < argc; i++) 502 cvs_client_send_request("Argument %s", argv[i]); 503 } 504 505 void 506 cvs_client_ok(char *data) 507 { 508 end_of_response = 1; 509 server_response = SERVER_OK; 510 } 511 512 void 513 cvs_client_error(char *data) 514 { 515 end_of_response = 1; 516 server_response = SERVER_ERROR; 517 } 518 519 void 520 cvs_client_validreq(char *data) 521 { 522 int i; 523 char *sp, *ep; 524 struct cvs_req *req; 525 526 sp = data; 527 do { 528 if ((ep = strchr(sp, ' ')) != NULL) 529 *ep = '\0'; 530 531 req = cvs_remote_get_request_info(sp); 532 if (req != NULL) 533 req->supported = 1; 534 535 if (ep != NULL) 536 sp = ep + 1; 537 } while (ep != NULL); 538 539 for (i = 0; cvs_requests[i].supported != -1; i++) { 540 req = &cvs_requests[i]; 541 if ((req->flags & REQ_NEEDED) && 542 req->supported != 1) { 543 fatal("server does not support required '%s'", 544 req->name); 545 } 546 } 547 } 548 549 void 550 cvs_client_e(char *data) 551 { 552 cvs_printf("%s\n", data); 553 } 554 555 void 556 cvs_client_m(char *data) 557 { 558 cvs_printf("%s\n", data); 559 } 560 561 void 562 cvs_client_checkedin(char *data) 563 { 564 int l; 565 CVSENTRIES *entlist; 566 struct cvs_ent *ent, *newent; 567 char *dir, *e, entry[CVS_ENT_MAXLINELEN], rev[16], timebuf[64]; 568 char sticky[16]; 569 570 dir = cvs_remote_input(); 571 e = cvs_remote_input(); 572 xfree(dir); 573 574 entlist = cvs_ent_open(data); 575 newent = cvs_ent_parse(e); 576 ent = cvs_ent_get(entlist, newent->ce_name); 577 xfree(e); 578 579 rcsnum_tostr(newent->ce_rev, rev, sizeof(rev)); 580 ctime_r(&ent->ce_mtime, timebuf); 581 if (timebuf[strlen(timebuf) - 1] == '\n') 582 timebuf[strlen(timebuf) - 1] = '\0'; 583 584 sticky[0] = '\0'; 585 if (ent->ce_tag != NULL) { 586 l = snprintf(sticky, sizeof(sticky), "T%s", ent->ce_tag); 587 if (l == -1 || l >= (int)sizeof(sticky)) 588 fatal("cvs_client_checkedin: overflow"); 589 } 590 591 l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s%s/%s/%s/%s", 592 newent->ce_name, (newent->ce_status == CVS_ENT_REMOVED) ? "-" : "", 593 rev, timebuf, ent->ce_opts ? ent->ce_opts : "", sticky); 594 if (l == -1 || l >= CVS_ENT_MAXLINELEN) 595 fatal("cvs_client_checkedin: overflow"); 596 597 cvs_ent_free(ent); 598 cvs_ent_free(newent); 599 cvs_ent_add(entlist, entry); 600 cvs_ent_close(entlist, ENT_SYNC); 601 } 602 603 void 604 cvs_client_updated(char *data) 605 { 606 int l, fd; 607 time_t now; 608 mode_t fmode, mask; 609 size_t flen; 610 CVSENTRIES *ent; 611 struct cvs_ent *e; 612 const char *errstr; 613 struct timeval tv[2]; 614 char timebuf[32], repo[MAXPATHLEN], *rpath, entry[CVS_ENT_MAXLINELEN]; 615 char *en, *mode, revbuf[32], *len, *fpath, *wdir; 616 617 client_check_directory(data); 618 619 rpath = cvs_remote_input(); 620 en = cvs_remote_input(); 621 mode = cvs_remote_input(); 622 len = cvs_remote_input(); 623 624 cvs_get_repository_path(".", repo, MAXPATHLEN); 625 626 STRIP_SLASH(repo); 627 628 if (strlen(repo) + 1 > strlen(rpath)) 629 fatal("received a repository path that is too short"); 630 631 fpath = rpath + strlen(repo) + 1; 632 if ((wdir = dirname(fpath)) == NULL) 633 fatal("cvs_client_updated: dirname: %s", strerror(errno)); 634 635 flen = strtonum(len, 0, INT_MAX, &errstr); 636 if (errstr != NULL) 637 fatal("cvs_client_updated: %s: %s", len, errstr); 638 xfree(len); 639 640 cvs_strtomode(mode, &fmode); 641 xfree(mode); 642 mask = umask(0); 643 umask(mask); 644 fmode &= ~mask; 645 646 time(&now); 647 asctime_r(gmtime(&now), timebuf); 648 if (timebuf[strlen(timebuf) - 1] == '\n') 649 timebuf[strlen(timebuf) - 1] = '\0'; 650 651 e = cvs_ent_parse(en); 652 xfree(en); 653 rcsnum_tostr(e->ce_rev, revbuf, sizeof(revbuf)); 654 l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s/%s/", e->ce_name, 655 revbuf, timebuf, e->ce_opts ? e->ce_opts : ""); 656 if (l == -1 || l >= CVS_ENT_MAXLINELEN) 657 fatal("cvs_client_updated: overflow"); 658 659 cvs_ent_free(e); 660 ent = cvs_ent_open(wdir); 661 cvs_ent_add(ent, entry); 662 cvs_ent_close(ent, ENT_SYNC); 663 664 if ((fd = open(fpath, O_CREAT | O_WRONLY | O_TRUNC)) == -1) 665 fatal("cvs_client_updated: open: %s: %s", 666 fpath, strerror(errno)); 667 668 cvs_remote_receive_file(fd, flen); 669 670 tv[0].tv_sec = now; 671 tv[0].tv_usec = 0; 672 tv[1] = tv[0]; 673 674 if (futimes(fd, tv) == -1) 675 fatal("cvs_client_updated: futimes: %s", strerror(errno)); 676 677 if (fchmod(fd, fmode) == -1) 678 fatal("cvs_client_updated: fchmod: %s", strerror(errno)); 679 680 (void)close(fd); 681 682 xfree(rpath); 683 } 684 685 void 686 cvs_client_merged(char *data) 687 { 688 int fd; 689 time_t now; 690 mode_t fmode; 691 size_t flen; 692 CVSENTRIES *ent; 693 const char *errstr; 694 struct timeval tv[2]; 695 char timebuf[32], *repo, *rpath, *entry, *mode; 696 char *len, *fpath, *wdir; 697 698 client_check_directory(data); 699 700 rpath = cvs_remote_input(); 701 entry = cvs_remote_input(); 702 mode = cvs_remote_input(); 703 len = cvs_remote_input(); 704 705 repo = xmalloc(MAXPATHLEN); 706 cvs_get_repository_path(".", repo, MAXPATHLEN); 707 708 STRIP_SLASH(repo); 709 710 if (strlen(repo) + 1 > strlen(rpath)) 711 fatal("received a repository path that is too short"); 712 713 fpath = rpath + strlen(repo) + 1; 714 if ((wdir = dirname(fpath)) == NULL) 715 fatal("cvs_client_merged: dirname: %s", strerror(errno)); 716 xfree(repo); 717 718 flen = strtonum(len, 0, INT_MAX, &errstr); 719 if (errstr != NULL) 720 fatal("cvs_client_merged: %s: %s", len, errstr); 721 xfree(len); 722 723 cvs_strtomode(mode, &fmode); 724 xfree(mode); 725 726 time(&now); 727 asctime_r(gmtime(&now), timebuf); 728 if (timebuf[strlen(timebuf) - 1] == '\n') 729 timebuf[strlen(timebuf) - 1] = '\0'; 730 731 ent = cvs_ent_open(wdir); 732 cvs_ent_add(ent, entry); 733 cvs_ent_close(ent, ENT_SYNC); 734 735 if ((fd = open(fpath, O_CREAT | O_WRONLY | O_TRUNC)) == -1) 736 fatal("cvs_client_merged: open: %s: %s", 737 fpath, strerror(errno)); 738 739 cvs_remote_receive_file(fd, flen); 740 741 tv[0].tv_sec = now; 742 tv[0].tv_usec = 0; 743 tv[1] = tv[0]; 744 745 if (futimes(fd, tv) == -1) 746 fatal("cvs_client_merged: futimes: %s", strerror(errno)); 747 748 if (fchmod(fd, fmode) == -1) 749 fatal("cvs_client_merged: fchmod: %s", strerror(errno)); 750 751 (void)close(fd); 752 753 xfree(rpath); 754 } 755 756 void 757 cvs_client_removed(char *data) 758 { 759 char *dir; 760 761 dir = cvs_remote_input(); 762 xfree(dir); 763 } 764 765 void 766 cvs_client_remove_entry(char *data) 767 { 768 CVSENTRIES *entlist; 769 char *filename, *rpath; 770 771 rpath = cvs_remote_input(); 772 if ((filename = strrchr(rpath, '/')) == NULL) 773 fatal("bad rpath in cvs_client_remove_entry: %s", rpath); 774 *filename++; 775 776 entlist = cvs_ent_open(data); 777 cvs_ent_remove(entlist, filename); 778 cvs_ent_close(entlist, ENT_SYNC); 779 780 xfree(rpath); 781 } 782 783 void 784 cvs_client_set_static_directory(char *data) 785 { 786 FILE *fp; 787 char *dir, fpath[MAXPATHLEN]; 788 789 if (cvs_cmdop == CVS_OP_EXPORT) 790 return; 791 792 STRIP_SLASH(data); 793 794 dir = cvs_remote_input(); 795 xfree(dir); 796 797 if (cvs_path_cat(data, CVS_PATH_STATICENTRIES, fpath, MAXPATHLEN) >= 798 MAXPATHLEN) 799 fatal("cvs_client_set_static_directory: truncation"); 800 801 if ((fp = fopen(fpath, "w+")) == NULL) { 802 cvs_log(LP_ERRNO, "%s", fpath); 803 return; 804 } 805 (void)fclose(fp); 806 } 807 808 void 809 cvs_client_clear_static_directory(char *data) 810 { 811 char *dir, fpath[MAXPATHLEN]; 812 813 if (cvs_cmdop == CVS_OP_EXPORT) 814 return; 815 816 STRIP_SLASH(data); 817 818 dir = cvs_remote_input(); 819 xfree(dir); 820 821 if (cvs_path_cat(data, CVS_PATH_STATICENTRIES, fpath, MAXPATHLEN) >= 822 MAXPATHLEN) 823 fatal("cvs_client_clear_static_directory: truncation"); 824 825 (void)cvs_unlink(fpath); 826 } 827 828 void 829 cvs_client_set_sticky(char *data) 830 { 831 FILE *fp; 832 char *dir, *tag, tagpath[MAXPATHLEN]; 833 834 if (cvs_cmdop == CVS_OP_EXPORT) 835 return; 836 837 STRIP_SLASH(data); 838 839 dir = cvs_remote_input(); 840 xfree(dir); 841 tag = cvs_remote_input(); 842 843 if (cvs_path_cat(data, CVS_PATH_TAG, tagpath, MAXPATHLEN) >= MAXPATHLEN) 844 fatal("cvs_client_clear_sticky: truncation"); 845 846 if ((fp = fopen(tagpath, "w+")) == NULL) { 847 cvs_log(LP_ERRNO, "%s", tagpath); 848 goto out; 849 } 850 851 (void)fprintf(fp, "%s\n", tag); 852 (void)fclose(fp); 853 out: 854 xfree(tag); 855 } 856 857 void 858 cvs_client_clear_sticky(char *data) 859 { 860 char *dir, tagpath[MAXPATHLEN]; 861 862 if (cvs_cmdop == CVS_OP_EXPORT) 863 return; 864 865 STRIP_SLASH(data); 866 867 dir = cvs_remote_input(); 868 xfree(dir); 869 870 if (cvs_path_cat(data, CVS_PATH_TAG, tagpath, MAXPATHLEN) >= MAXPATHLEN) 871 fatal("cvs_client_clear_sticky: truncation"); 872 873 (void)cvs_unlink(tagpath); 874 } 875 876 877 /* 878 * cvs_client_initlog() 879 * 880 * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is 881 * set. In this case, the variable's value is used as a path to which the 882 * appropriate suffix is added (".in" for client input and ".out" for server 883 * output). 884 */ 885 static void 886 cvs_client_initlog(void) 887 { 888 int l; 889 u_int i; 890 char *env, *envdup, buf[MAXPATHLEN], fpath[MAXPATHLEN]; 891 char rpath[MAXPATHLEN], *s; 892 struct stat st; 893 time_t now; 894 struct passwd *pwd; 895 896 /* avoid doing it more than once */ 897 if (cvs_client_logon) 898 return; 899 900 if ((env = getenv("CVS_CLIENT_LOG")) == NULL) 901 return; 902 903 envdup = xstrdup(env); 904 if ((s = strchr(envdup, '%')) != NULL) 905 *s = '\0'; 906 907 if (strlcpy(buf, env, sizeof(buf)) >= sizeof(buf)) 908 fatal("cvs_client_initlog: truncation"); 909 910 if (strlcpy(rpath, envdup, sizeof(rpath)) >= sizeof(rpath)) 911 fatal("cvs_client_initlog: truncation"); 912 913 xfree(envdup); 914 915 s = buf; 916 while ((s = strchr(s, '%')) != NULL) { 917 s++; 918 switch (*s) { 919 case 'c': 920 if (strlcpy(fpath, cvs_command, sizeof(fpath)) >= 921 sizeof(fpath)) 922 fatal("cvs_client_initlog: truncation"); 923 break; 924 case 'd': 925 time(&now); 926 if (strlcpy(fpath, ctime(&now), sizeof(fpath)) >= 927 sizeof(fpath)) 928 fatal("cvs_client_initlog: truncation"); 929 break; 930 case 'p': 931 snprintf(fpath, sizeof(fpath), "%d", getpid()); 932 break; 933 case 'u': 934 if ((pwd = getpwuid(getuid())) != NULL) { 935 if (strlcpy(fpath, pwd->pw_name, 936 sizeof(fpath)) >= sizeof(fpath)) 937 fatal("cvs_client_initlog: truncation"); 938 } else { 939 fpath[0] = '\0'; 940 } 941 endpwent(); 942 break; 943 default: 944 fpath[0] = '\0'; 945 break; 946 } 947 948 if (fpath[0] != '\0') { 949 if (strlcat(rpath, "-", sizeof(rpath)) >= sizeof(rpath)) 950 fatal("cvs_client_initlog: truncation"); 951 952 if (strlcat(rpath, fpath, sizeof(rpath)) 953 >= sizeof(rpath)) 954 fatal("cvs_client_initlog: truncation"); 955 } 956 } 957 958 for (i = 0; i < UINT_MAX; i++) { 959 l = snprintf(fpath, sizeof(fpath), "%s-%d.in", rpath, i); 960 if (l == -1 || l >= (int)sizeof(fpath)) 961 fatal("cvs_client_initlog: overflow"); 962 963 if (stat(fpath, &st) != -1) 964 continue; 965 966 if (errno != ENOENT) 967 fatal("cvs_client_initlog() stat failed '%s'", 968 strerror(errno)); 969 970 break; 971 } 972 973 if ((cvs_client_inlog_fd = open(fpath, 974 O_RDWR | O_CREAT | O_TRUNC, 0644)) == NULL) { 975 fatal("cvs_client_initlog: open `%s': %s", 976 fpath, strerror(errno)); 977 } 978 979 for (i = 0; i < UINT_MAX; i++) { 980 l = snprintf(fpath, sizeof(fpath), "%s-%d.out", rpath, i); 981 if (l == -1 || l >= (int)sizeof(fpath)) 982 fatal("cvs_client_initlog: overflow"); 983 984 if (stat(fpath, &st) != -1) 985 continue; 986 987 if (errno != ENOENT) 988 fatal("cvs_client_initlog() stat failed '%s'", 989 strerror(errno)); 990 991 break; 992 } 993 994 if ((cvs_client_outlog_fd = open(fpath, 995 O_RDWR | O_CREAT | O_TRUNC, 0644)) == NULL) { 996 fatal("cvs_client_initlog: open `%s': %s", 997 fpath, strerror(errno)); 998 } 999 1000 cvs_client_logon = 1; 1001 } 1002