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