1 /* $OpenBSD: server.c,v 1.105 2017/08/28 19:33:20 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/types.h> 19 #include <sys/stat.h> 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <libgen.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "cvs.h" 30 #include "remote.h" 31 32 struct cvs_resp cvs_responses[] = { 33 /* this is what our server uses, the client should support it */ 34 { "Valid-requests", 1, cvs_client_validreq, RESP_NEEDED }, 35 { "ok", 0, cvs_client_ok, RESP_NEEDED}, 36 { "error", 0, cvs_client_error, RESP_NEEDED }, 37 { "E", 0, cvs_client_e, RESP_NEEDED }, 38 { "M", 0, cvs_client_m, RESP_NEEDED }, 39 { "Checked-in", 0, cvs_client_checkedin, RESP_NEEDED }, 40 { "Updated", 0, cvs_client_updated, RESP_NEEDED }, 41 { "Merged", 0, cvs_client_merged, RESP_NEEDED }, 42 { "Removed", 0, cvs_client_removed, RESP_NEEDED }, 43 { "Remove-entry", 0, cvs_client_remove_entry, 0 }, 44 { "Set-static-directory", 0, 45 cvs_client_set_static_directory, 0 }, 46 { "Clear-static-directory", 0, 47 cvs_client_clear_static_directory, 0 }, 48 { "Set-sticky", 0, cvs_client_set_sticky, 0 }, 49 { "Clear-sticky", 0, cvs_client_clear_sticky, 0 }, 50 51 /* unsupported responses until told otherwise */ 52 { "New-entry", 0, NULL, 0 }, 53 { "Created", 0, NULL, 0 }, 54 { "Update-existing", 0, NULL, 0 }, 55 { "Rcs-diff", 0, NULL, 0 }, 56 { "Patched", 0, NULL, 0 }, 57 { "Mode", 0, NULL, 0 }, 58 { "Mod-time", 0, NULL, 0 }, 59 { "Checksum", 0, NULL, 0 }, 60 { "Copy-file", 0, NULL, 0 }, 61 { "Template", 0, NULL, 0 }, 62 { "Set-checkin-prog", 0, NULL, 0 }, 63 { "Set-update-prog", 0, NULL, 0 }, 64 { "Notified", 0, NULL, 0 }, 65 { "Module-expansion", 0, NULL, 0 }, 66 { "Wrapper-rcsOption", 0, NULL, 0 }, 67 { "Mbinary", 0, NULL, 0 }, 68 { "F", 0, NULL, 0 }, 69 { "MT", 0, NULL, 0 }, 70 { "", -1, NULL, 0 } 71 }; 72 73 int cvs_server(int, char **); 74 char *cvs_server_path = NULL; 75 76 static char *server_currentdir = NULL; 77 static char **server_argv; 78 static int server_argc = 1; 79 80 extern int disable_fast_checkout; 81 82 struct cvs_cmd cvs_cmd_server = { 83 CVS_OP_SERVER, CVS_USE_WDIR, "server", { "", "" }, 84 "server mode", 85 NULL, 86 NULL, 87 NULL, 88 cvs_server 89 }; 90 91 92 int 93 cvs_server(int argc, char **argv) 94 { 95 char *cmd, *data; 96 struct cvs_req *req; 97 98 if (argc > 1) 99 fatal("server does not take any extra arguments"); 100 101 /* Be on server-side very verbose per default. */ 102 verbosity = 2; 103 104 setvbuf(stdin, NULL, _IOLBF, 0); 105 setvbuf(stdout, NULL, _IOLBF, 0); 106 107 cvs_server_active = 1; 108 109 server_argv = xcalloc(server_argc + 1, sizeof(*server_argv)); 110 server_argv[0] = xstrdup("server"); 111 112 (void)xasprintf(&cvs_server_path, "%s/cvs-serv%d", cvs_tmpdir, 113 getpid()); 114 115 if (mkdir(cvs_server_path, 0700) == -1) 116 fatal("failed to create temporary server directory: %s, %s", 117 cvs_server_path, strerror(errno)); 118 119 if (chdir(cvs_server_path) == -1) 120 fatal("failed to change directory to '%s'", cvs_server_path); 121 122 for (;;) { 123 cmd = cvs_remote_input(); 124 125 if ((data = strchr(cmd, ' ')) != NULL) 126 (*data++) = '\0'; 127 128 req = cvs_remote_get_request_info(cmd); 129 if (req == NULL) 130 fatal("request '%s' is not supported by our server", 131 cmd); 132 133 if (req->hdlr == NULL) 134 fatal("opencvs server does not support '%s'", cmd); 135 136 if ((req->flags & REQ_NEEDDIR) && (server_currentdir == NULL)) 137 fatal("`%s' needs a directory to be sent with " 138 "the `Directory` request first", cmd); 139 140 (*req->hdlr)(data); 141 free(cmd); 142 } 143 144 return (0); 145 } 146 147 void 148 cvs_server_send_response(char *fmt, ...) 149 { 150 int i; 151 va_list ap; 152 char *data; 153 154 va_start(ap, fmt); 155 i = vasprintf(&data, fmt, ap); 156 va_end(ap); 157 if (i == -1) 158 fatal("cvs_server_send_response: could not allocate memory"); 159 160 cvs_log(LP_TRACE, "%s", data); 161 cvs_remote_output(data); 162 free(data); 163 } 164 165 void 166 cvs_server_root(char *data) 167 { 168 if (data == NULL) 169 fatal("Missing argument for Root"); 170 171 if (current_cvsroot != NULL) 172 return; 173 174 if (data[0] != '/' || (current_cvsroot = cvsroot_get(data)) == NULL) 175 fatal("Invalid Root specified!"); 176 177 cvs_parse_configfile(); 178 cvs_parse_modules(); 179 umask(cvs_umask); 180 } 181 182 void 183 cvs_server_validresp(char *data) 184 { 185 int i; 186 char *sp, *ep; 187 struct cvs_resp *resp; 188 189 if ((sp = data) == NULL) 190 fatal("Missing argument for Valid-responses"); 191 192 do { 193 if ((ep = strchr(sp, ' ')) != NULL) 194 *ep = '\0'; 195 196 resp = cvs_remote_get_response_info(sp); 197 if (resp != NULL) 198 resp->supported = 1; 199 200 if (ep != NULL) 201 sp = ep + 1; 202 } while (ep != NULL); 203 204 for (i = 0; cvs_responses[i].supported != -1; i++) { 205 resp = &cvs_responses[i]; 206 if ((resp->flags & RESP_NEEDED) && 207 resp->supported != 1) { 208 fatal("client does not support required '%s'", 209 resp->name); 210 } 211 } 212 } 213 214 void 215 cvs_server_validreq(char *data) 216 { 217 BUF *bp; 218 char *d; 219 int i, first; 220 221 first = 0; 222 bp = buf_alloc(512); 223 for (i = 0; cvs_requests[i].supported != -1; i++) { 224 if (cvs_requests[i].hdlr == NULL) 225 continue; 226 227 if (first != 0) 228 buf_putc(bp, ' '); 229 else 230 first++; 231 232 buf_puts(bp, cvs_requests[i].name); 233 } 234 235 buf_putc(bp, '\0'); 236 d = buf_release(bp); 237 238 cvs_server_send_response("Valid-requests %s", d); 239 cvs_server_send_response("ok"); 240 free(d); 241 } 242 243 void 244 cvs_server_static_directory(char *data) 245 { 246 FILE *fp; 247 char fpath[PATH_MAX]; 248 249 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", 250 server_currentdir, CVS_PATH_STATICENTRIES); 251 252 if ((fp = fopen(fpath, "w+")) == NULL) { 253 cvs_log(LP_ERRNO, "%s", fpath); 254 return; 255 } 256 (void)fclose(fp); 257 } 258 259 void 260 cvs_server_sticky(char *data) 261 { 262 FILE *fp; 263 char tagpath[PATH_MAX]; 264 265 if (data == NULL) 266 fatal("Missing argument for Sticky"); 267 268 (void)xsnprintf(tagpath, PATH_MAX, "%s/%s", 269 server_currentdir, CVS_PATH_TAG); 270 271 if ((fp = fopen(tagpath, "w+")) == NULL) { 272 cvs_log(LP_ERRNO, "%s", tagpath); 273 return; 274 } 275 276 (void)fprintf(fp, "%s\n", data); 277 (void)fclose(fp); 278 } 279 280 void 281 cvs_server_globalopt(char *data) 282 { 283 if (data == NULL) 284 fatal("Missing argument for Global_option"); 285 286 if (!strcmp(data, "-l")) 287 cvs_nolog = 1; 288 289 if (!strcmp(data, "-n")) 290 cvs_noexec = 1; 291 292 if (!strcmp(data, "-Q")) 293 verbosity = 0; 294 295 if (!strcmp(data, "-q")) 296 verbosity = 1; 297 298 if (!strcmp(data, "-r")) 299 cvs_readonly = 1; 300 301 if (!strcmp(data, "-t")) 302 cvs_trace = 1; 303 } 304 305 void 306 cvs_server_set(char *data) 307 { 308 char *ep; 309 310 if (data == NULL) 311 fatal("Missing argument for Set"); 312 313 ep = strchr(data, '='); 314 if (ep == NULL) 315 fatal("no = in variable assignment"); 316 317 *(ep++) = '\0'; 318 if (cvs_var_set(data, ep) < 0) 319 fatal("cvs_server_set: cvs_var_set failed"); 320 } 321 322 void 323 cvs_server_directory(char *data) 324 { 325 CVSENTRIES *entlist; 326 char *dir, *repo, *parent, *entry, *dirn, *p; 327 328 if (current_cvsroot == NULL) 329 fatal("No Root specified for Directory"); 330 331 dir = cvs_remote_input(); 332 STRIP_SLASH(dir); 333 334 if (strlen(dir) < strlen(current_cvsroot->cr_dir)) 335 fatal("cvs_server_directory: bad Directory request"); 336 337 repo = dir + strlen(current_cvsroot->cr_dir); 338 339 /* 340 * This is somewhat required for checkout, as the 341 * directory request will be: 342 * 343 * Directory . 344 * /path/to/cvs/root 345 */ 346 if (repo[0] == '\0') 347 p = xstrdup("."); 348 else 349 p = xstrdup(repo + 1); 350 351 cvs_mkpath(p, NULL); 352 353 if ((dirn = basename(p)) == NULL) 354 fatal("cvs_server_directory: %s", strerror(errno)); 355 356 if ((parent = dirname(p)) == NULL) 357 fatal("cvs_server_directory: %s", strerror(errno)); 358 359 if (strcmp(parent, ".")) { 360 entry = xmalloc(CVS_ENT_MAXLINELEN); 361 cvs_ent_line_str(dirn, NULL, NULL, NULL, NULL, 1, 0, 362 entry, CVS_ENT_MAXLINELEN); 363 364 entlist = cvs_ent_open(parent); 365 cvs_ent_add(entlist, entry); 366 free(entry); 367 } 368 369 free(server_currentdir); 370 server_currentdir = p; 371 372 free(dir); 373 } 374 375 void 376 cvs_server_entry(char *data) 377 { 378 CVSENTRIES *entlist; 379 380 if (data == NULL) 381 fatal("Missing argument for Entry"); 382 383 entlist = cvs_ent_open(server_currentdir); 384 cvs_ent_add(entlist, data); 385 } 386 387 void 388 cvs_server_modified(char *data) 389 { 390 int fd; 391 size_t flen; 392 mode_t fmode; 393 const char *errstr; 394 char *mode, *len, fpath[PATH_MAX]; 395 396 if (data == NULL) 397 fatal("Missing argument for Modified"); 398 399 /* sorry, we have to use TMP_DIR */ 400 disable_fast_checkout = 1; 401 402 mode = cvs_remote_input(); 403 len = cvs_remote_input(); 404 405 cvs_strtomode(mode, &fmode); 406 free(mode); 407 408 flen = strtonum(len, 0, INT_MAX, &errstr); 409 if (errstr != NULL) 410 fatal("cvs_server_modified: %s", errstr); 411 free(len); 412 413 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", server_currentdir, data); 414 415 if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1) 416 fatal("cvs_server_modified: %s: %s", fpath, strerror(errno)); 417 418 cvs_remote_receive_file(fd, flen); 419 420 if (fchmod(fd, 0600) == -1) 421 fatal("cvs_server_modified: failed to set file mode"); 422 423 (void)close(fd); 424 } 425 426 void 427 cvs_server_useunchanged(char *data) 428 { 429 } 430 431 void 432 cvs_server_unchanged(char *data) 433 { 434 char fpath[PATH_MAX]; 435 CVSENTRIES *entlist; 436 struct cvs_ent *ent; 437 char sticky[CVS_ENT_MAXLINELEN]; 438 char rev[CVS_REV_BUFSZ], entry[CVS_ENT_MAXLINELEN]; 439 440 if (data == NULL) 441 fatal("Missing argument for Unchanged"); 442 443 /* sorry, we have to use TMP_DIR */ 444 disable_fast_checkout = 1; 445 446 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", server_currentdir, data); 447 448 entlist = cvs_ent_open(server_currentdir); 449 ent = cvs_ent_get(entlist, data); 450 if (ent == NULL) 451 fatal("received Unchanged request for non-existing file"); 452 453 sticky[0] = '\0'; 454 if (ent->ce_tag != NULL) 455 (void)xsnprintf(sticky, sizeof(sticky), "T%s", ent->ce_tag); 456 457 rcsnum_tostr(ent->ce_rev, rev, sizeof(rev)); 458 (void)xsnprintf(entry, sizeof(entry), "/%s/%s/%s/%s/%s", 459 ent->ce_name, rev, CVS_SERVER_UNCHANGED, ent->ce_opts ? 460 ent->ce_opts : "", sticky); 461 462 cvs_ent_free(ent); 463 cvs_ent_add(entlist, entry); 464 } 465 466 void 467 cvs_server_questionable(char *data) 468 { 469 CVSENTRIES *entlist; 470 char entry[CVS_ENT_MAXLINELEN]; 471 472 if (data == NULL) 473 fatal("Questionable request with no data attached"); 474 475 (void)xsnprintf(entry, sizeof(entry), "/%s/%c///", data, 476 CVS_SERVER_QUESTIONABLE); 477 478 entlist = cvs_ent_open(server_currentdir); 479 cvs_ent_add(entlist, entry); 480 481 /* sorry, we have to use TMP_DIR */ 482 disable_fast_checkout = 1; 483 } 484 485 void 486 cvs_server_argument(char *data) 487 { 488 if (data == NULL) 489 fatal("Missing argument for Argument"); 490 491 server_argv = xreallocarray(server_argv, server_argc + 2, 492 sizeof(*server_argv)); 493 server_argv[server_argc] = xstrdup(data); 494 server_argv[++server_argc] = NULL; 495 } 496 497 void 498 cvs_server_argumentx(char *data) 499 { 500 int idx; 501 size_t len; 502 503 if (server_argc == 1) 504 fatal("Protocol Error: ArgumentX without previous argument"); 505 506 idx = server_argc - 1; 507 508 len = strlen(server_argv[idx]) + strlen(data) + 2; 509 server_argv[idx] = xreallocarray(server_argv[idx], len, sizeof(char)); 510 strlcat(server_argv[idx], "\n", len); 511 strlcat(server_argv[idx], data, len); 512 } 513 514 void 515 cvs_server_update_patches(char *data) 516 { 517 /* 518 * This does not actually do anything. 519 * It is used to tell that the server is able to 520 * generate patches when given an `update' request. 521 * The client must issue the -u argument to `update' 522 * to receive patches. 523 */ 524 } 525 526 void 527 cvs_server_add(char *data) 528 { 529 if (chdir(server_currentdir) == -1) 530 fatal("cvs_server_add: %s", strerror(errno)); 531 532 cvs_cmdop = CVS_OP_ADD; 533 cmdp->cmd_flags = cvs_cmd_add.cmd_flags; 534 cvs_add(server_argc, server_argv); 535 cvs_server_send_response("ok"); 536 } 537 538 void 539 cvs_server_import(char *data) 540 { 541 if (chdir(server_currentdir) == -1) 542 fatal("cvs_server_import: %s", strerror(errno)); 543 544 cvs_cmdop = CVS_OP_IMPORT; 545 cmdp->cmd_flags = cvs_cmd_import.cmd_flags; 546 cvs_import(server_argc, server_argv); 547 cvs_server_send_response("ok"); 548 } 549 550 void 551 cvs_server_admin(char *data) 552 { 553 if (chdir(server_currentdir) == -1) 554 fatal("cvs_server_admin: %s", strerror(errno)); 555 556 cvs_cmdop = CVS_OP_ADMIN; 557 cmdp->cmd_flags = cvs_cmd_admin.cmd_flags; 558 cvs_admin(server_argc, server_argv); 559 cvs_server_send_response("ok"); 560 } 561 562 void 563 cvs_server_annotate(char *data) 564 { 565 if (chdir(server_currentdir) == -1) 566 fatal("cvs_server_annotate: %s", strerror(errno)); 567 568 cvs_cmdop = CVS_OP_ANNOTATE; 569 cmdp->cmd_flags = cvs_cmd_annotate.cmd_flags; 570 cvs_annotate(server_argc, server_argv); 571 cvs_server_send_response("ok"); 572 } 573 574 void 575 cvs_server_rannotate(char *data) 576 { 577 if (chdir(server_currentdir) == -1) 578 fatal("cvs_server_rannotate: %s", strerror(errno)); 579 580 cvs_cmdop = CVS_OP_RANNOTATE; 581 cmdp->cmd_flags = cvs_cmd_rannotate.cmd_flags; 582 cvs_annotate(server_argc, server_argv); 583 cvs_server_send_response("ok"); 584 } 585 586 void 587 cvs_server_commit(char *data) 588 { 589 if (chdir(server_currentdir) == -1) 590 fatal("cvs_server_commit: %s", strerror(errno)); 591 592 cvs_cmdop = CVS_OP_COMMIT; 593 cmdp->cmd_flags = cvs_cmd_commit.cmd_flags; 594 cvs_commit(server_argc, server_argv); 595 cvs_server_send_response("ok"); 596 } 597 598 void 599 cvs_server_checkout(char *data) 600 { 601 if (chdir(server_currentdir) == -1) 602 fatal("cvs_server_checkout: %s", strerror(errno)); 603 604 cvs_cmdop = CVS_OP_CHECKOUT; 605 cmdp->cmd_flags = cvs_cmd_checkout.cmd_flags; 606 cvs_checkout(server_argc, server_argv); 607 cvs_server_send_response("ok"); 608 } 609 610 void 611 cvs_server_diff(char *data) 612 { 613 if (chdir(server_currentdir) == -1) 614 fatal("cvs_server_diff: %s", strerror(errno)); 615 616 cvs_cmdop = CVS_OP_DIFF; 617 cmdp->cmd_flags = cvs_cmd_diff.cmd_flags; 618 cvs_diff(server_argc, server_argv); 619 cvs_server_send_response("ok"); 620 } 621 622 void 623 cvs_server_rdiff(char *data) 624 { 625 if (chdir(server_currentdir) == -1) 626 fatal("cvs_server_rdiff: %s", strerror(errno)); 627 628 cvs_cmdop = CVS_OP_RDIFF; 629 cmdp->cmd_flags = cvs_cmd_rdiff.cmd_flags; 630 cvs_diff(server_argc, server_argv); 631 cvs_server_send_response("ok"); 632 } 633 634 void 635 cvs_server_export(char *data) 636 { 637 if (chdir(server_currentdir) == -1) 638 fatal("cvs_server_export: %s", strerror(errno)); 639 640 cvs_cmdop = CVS_OP_EXPORT; 641 cmdp->cmd_flags = cvs_cmd_export.cmd_flags; 642 cvs_export(server_argc, server_argv); 643 cvs_server_send_response("ok"); 644 } 645 646 void 647 cvs_server_init(char *data) 648 { 649 if (data == NULL) 650 fatal("Missing argument for init"); 651 652 if (current_cvsroot != NULL) 653 fatal("Root in combination with init is not supported"); 654 655 if ((current_cvsroot = cvsroot_get(data)) == NULL) 656 fatal("Invalid argument for init"); 657 658 cvs_cmdop = CVS_OP_INIT; 659 cmdp->cmd_flags = cvs_cmd_init.cmd_flags; 660 cvs_init(server_argc, server_argv); 661 cvs_server_send_response("ok"); 662 } 663 664 void 665 cvs_server_release(char *data) 666 { 667 if (chdir(server_currentdir) == -1) 668 fatal("cvs_server_release: %s", strerror(errno)); 669 670 cvs_cmdop = CVS_OP_RELEASE; 671 cmdp->cmd_flags = cvs_cmd_release.cmd_flags; 672 cvs_release(server_argc, server_argv); 673 cvs_server_send_response("ok"); 674 } 675 676 void 677 cvs_server_remove(char *data) 678 { 679 if (chdir(server_currentdir) == -1) 680 fatal("cvs_server_remove: %s", strerror(errno)); 681 682 cvs_cmdop = CVS_OP_REMOVE; 683 cmdp->cmd_flags = cvs_cmd_remove.cmd_flags; 684 cvs_remove(server_argc, server_argv); 685 cvs_server_send_response("ok"); 686 } 687 688 void 689 cvs_server_status(char *data) 690 { 691 if (chdir(server_currentdir) == -1) 692 fatal("cvs_server_status: %s", strerror(errno)); 693 694 cvs_cmdop = CVS_OP_STATUS; 695 cmdp->cmd_flags = cvs_cmd_status.cmd_flags; 696 cvs_status(server_argc, server_argv); 697 cvs_server_send_response("ok"); 698 } 699 700 void 701 cvs_server_log(char *data) 702 { 703 if (chdir(server_currentdir) == -1) 704 fatal("cvs_server_log: %s", strerror(errno)); 705 706 cvs_cmdop = CVS_OP_LOG; 707 cmdp->cmd_flags = cvs_cmd_log.cmd_flags; 708 cvs_getlog(server_argc, server_argv); 709 cvs_server_send_response("ok"); 710 } 711 712 void 713 cvs_server_rlog(char *data) 714 { 715 if (chdir(current_cvsroot->cr_dir) == -1) 716 fatal("cvs_server_rlog: %s", strerror(errno)); 717 718 cvs_cmdop = CVS_OP_RLOG; 719 cmdp->cmd_flags = cvs_cmd_rlog.cmd_flags; 720 cvs_getlog(server_argc, server_argv); 721 cvs_server_send_response("ok"); 722 } 723 724 void 725 cvs_server_tag(char *data) 726 { 727 if (chdir(server_currentdir) == -1) 728 fatal("cvs_server_tag: %s", strerror(errno)); 729 730 cvs_cmdop = CVS_OP_TAG; 731 cmdp->cmd_flags = cvs_cmd_tag.cmd_flags; 732 cvs_tag(server_argc, server_argv); 733 cvs_server_send_response("ok"); 734 } 735 736 void 737 cvs_server_rtag(char *data) 738 { 739 if (chdir(current_cvsroot->cr_dir) == -1) 740 fatal("cvs_server_rtag: %s", strerror(errno)); 741 742 cvs_cmdop = CVS_OP_RTAG; 743 cmdp->cmd_flags = cvs_cmd_rtag.cmd_flags; 744 cvs_tag(server_argc, server_argv); 745 cvs_server_send_response("ok"); 746 } 747 748 void 749 cvs_server_update(char *data) 750 { 751 if (chdir(server_currentdir) == -1) 752 fatal("cvs_server_update: %s", strerror(errno)); 753 754 cvs_cmdop = CVS_OP_UPDATE; 755 cmdp->cmd_flags = cvs_cmd_update.cmd_flags; 756 cvs_update(server_argc, server_argv); 757 cvs_server_send_response("ok"); 758 } 759 760 void 761 cvs_server_version(char *data) 762 { 763 cvs_cmdop = CVS_OP_VERSION; 764 cmdp->cmd_flags = cvs_cmd_version.cmd_flags; 765 cvs_version(server_argc, server_argv); 766 cvs_server_send_response("ok"); 767 } 768 769 void 770 cvs_server_update_entry(const char *resp, struct cvs_file *cf) 771 { 772 char *p; 773 char repo[PATH_MAX], fpath[PATH_MAX]; 774 775 if ((p = strrchr(cf->file_rpath, ',')) != NULL) 776 *p = '\0'; 777 778 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 779 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", repo, cf->file_name); 780 781 cvs_server_send_response("%s %s/", resp, cf->file_wd); 782 cvs_remote_output(fpath); 783 784 if (p != NULL) 785 *p = ','; 786 } 787 788 void 789 cvs_server_set_sticky(const char *dir, char *tag) 790 { 791 char fpath[PATH_MAX]; 792 char repo[PATH_MAX]; 793 794 cvs_get_repository_path(dir, repo, PATH_MAX); 795 (void)xsnprintf(fpath, PATH_MAX, "%s/", repo); 796 797 cvs_server_send_response("Set-sticky %s/", dir); 798 cvs_remote_output(fpath); 799 cvs_remote_output(tag); 800 } 801 802 void 803 cvs_server_clear_sticky(char *dir) 804 { 805 char fpath[PATH_MAX]; 806 char repo[PATH_MAX]; 807 808 cvs_get_repository_path(dir, repo, PATH_MAX); 809 (void)xsnprintf(fpath, PATH_MAX, "%s/", repo); 810 811 cvs_server_send_response("Clear-sticky %s//", dir); 812 cvs_remote_output(fpath); 813 } 814 815 void 816 cvs_server_exp_modules(char *module) 817 { 818 struct module_checkout *mo; 819 struct cvs_filelist *fl; 820 821 if (server_argc != 2) 822 fatal("expand-modules with no arguments"); 823 824 mo = cvs_module_lookup(server_argv[1]); 825 826 RB_FOREACH(fl, cvs_flisthead, &(mo->mc_modules)) 827 cvs_server_send_response("Module-expansion %s", fl->file_path); 828 cvs_server_send_response("ok"); 829 830 server_argc--; 831 free(server_argv[1]); 832 server_argv[1] = NULL; 833 } 834