1 /* $OpenBSD: server.c,v 1.106 2020/10/19 19:51:20 naddy 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 char parentbuf[PATH_MAX], dirnbuf[PATH_MAX]; 328 329 if (current_cvsroot == NULL) 330 fatal("No Root specified for Directory"); 331 332 dir = cvs_remote_input(); 333 STRIP_SLASH(dir); 334 335 if (strlen(dir) < strlen(current_cvsroot->cr_dir)) 336 fatal("cvs_server_directory: bad Directory request"); 337 338 repo = dir + strlen(current_cvsroot->cr_dir); 339 340 /* 341 * This is somewhat required for checkout, as the 342 * directory request will be: 343 * 344 * Directory . 345 * /path/to/cvs/root 346 */ 347 if (repo[0] == '\0') 348 p = xstrdup("."); 349 else 350 p = xstrdup(repo + 1); 351 352 cvs_mkpath(p, NULL); 353 354 if (strlcpy(dirnbuf, p, sizeof(dirnbuf)) >= sizeof(dirnbuf)) 355 fatal("cvs_server_directory: truncation"); 356 if ((dirn = basename(dirnbuf)) == NULL) 357 fatal("cvs_server_directory: %s", strerror(errno)); 358 359 if (strlcpy(parentbuf, p, sizeof(parentbuf)) >= sizeof(parentbuf)) 360 fatal("cvs_server_directory: truncation"); 361 if ((parent = dirname(parentbuf)) == NULL) 362 fatal("cvs_server_directory: %s", strerror(errno)); 363 364 if (strcmp(parent, ".")) { 365 entry = xmalloc(CVS_ENT_MAXLINELEN); 366 cvs_ent_line_str(dirn, NULL, NULL, NULL, NULL, 1, 0, 367 entry, CVS_ENT_MAXLINELEN); 368 369 entlist = cvs_ent_open(parent); 370 cvs_ent_add(entlist, entry); 371 free(entry); 372 } 373 374 free(server_currentdir); 375 server_currentdir = p; 376 377 free(dir); 378 } 379 380 void 381 cvs_server_entry(char *data) 382 { 383 CVSENTRIES *entlist; 384 385 if (data == NULL) 386 fatal("Missing argument for Entry"); 387 388 entlist = cvs_ent_open(server_currentdir); 389 cvs_ent_add(entlist, data); 390 } 391 392 void 393 cvs_server_modified(char *data) 394 { 395 int fd; 396 size_t flen; 397 mode_t fmode; 398 const char *errstr; 399 char *mode, *len, fpath[PATH_MAX]; 400 401 if (data == NULL) 402 fatal("Missing argument for Modified"); 403 404 /* sorry, we have to use TMP_DIR */ 405 disable_fast_checkout = 1; 406 407 mode = cvs_remote_input(); 408 len = cvs_remote_input(); 409 410 cvs_strtomode(mode, &fmode); 411 free(mode); 412 413 flen = strtonum(len, 0, INT_MAX, &errstr); 414 if (errstr != NULL) 415 fatal("cvs_server_modified: %s", errstr); 416 free(len); 417 418 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", server_currentdir, data); 419 420 if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1) 421 fatal("cvs_server_modified: %s: %s", fpath, strerror(errno)); 422 423 cvs_remote_receive_file(fd, flen); 424 425 if (fchmod(fd, 0600) == -1) 426 fatal("cvs_server_modified: failed to set file mode"); 427 428 (void)close(fd); 429 } 430 431 void 432 cvs_server_useunchanged(char *data) 433 { 434 } 435 436 void 437 cvs_server_unchanged(char *data) 438 { 439 char fpath[PATH_MAX]; 440 CVSENTRIES *entlist; 441 struct cvs_ent *ent; 442 char sticky[CVS_ENT_MAXLINELEN]; 443 char rev[CVS_REV_BUFSZ], entry[CVS_ENT_MAXLINELEN]; 444 445 if (data == NULL) 446 fatal("Missing argument for Unchanged"); 447 448 /* sorry, we have to use TMP_DIR */ 449 disable_fast_checkout = 1; 450 451 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", server_currentdir, data); 452 453 entlist = cvs_ent_open(server_currentdir); 454 ent = cvs_ent_get(entlist, data); 455 if (ent == NULL) 456 fatal("received Unchanged request for non-existing file"); 457 458 sticky[0] = '\0'; 459 if (ent->ce_tag != NULL) 460 (void)xsnprintf(sticky, sizeof(sticky), "T%s", ent->ce_tag); 461 462 rcsnum_tostr(ent->ce_rev, rev, sizeof(rev)); 463 (void)xsnprintf(entry, sizeof(entry), "/%s/%s/%s/%s/%s", 464 ent->ce_name, rev, CVS_SERVER_UNCHANGED, ent->ce_opts ? 465 ent->ce_opts : "", sticky); 466 467 cvs_ent_free(ent); 468 cvs_ent_add(entlist, entry); 469 } 470 471 void 472 cvs_server_questionable(char *data) 473 { 474 CVSENTRIES *entlist; 475 char entry[CVS_ENT_MAXLINELEN]; 476 477 if (data == NULL) 478 fatal("Questionable request with no data attached"); 479 480 (void)xsnprintf(entry, sizeof(entry), "/%s/%c///", data, 481 CVS_SERVER_QUESTIONABLE); 482 483 entlist = cvs_ent_open(server_currentdir); 484 cvs_ent_add(entlist, entry); 485 486 /* sorry, we have to use TMP_DIR */ 487 disable_fast_checkout = 1; 488 } 489 490 void 491 cvs_server_argument(char *data) 492 { 493 if (data == NULL) 494 fatal("Missing argument for Argument"); 495 496 server_argv = xreallocarray(server_argv, server_argc + 2, 497 sizeof(*server_argv)); 498 server_argv[server_argc] = xstrdup(data); 499 server_argv[++server_argc] = NULL; 500 } 501 502 void 503 cvs_server_argumentx(char *data) 504 { 505 int idx; 506 size_t len; 507 508 if (server_argc == 1) 509 fatal("Protocol Error: ArgumentX without previous argument"); 510 511 idx = server_argc - 1; 512 513 len = strlen(server_argv[idx]) + strlen(data) + 2; 514 server_argv[idx] = xreallocarray(server_argv[idx], len, sizeof(char)); 515 strlcat(server_argv[idx], "\n", len); 516 strlcat(server_argv[idx], data, len); 517 } 518 519 void 520 cvs_server_update_patches(char *data) 521 { 522 /* 523 * This does not actually do anything. 524 * It is used to tell that the server is able to 525 * generate patches when given an `update' request. 526 * The client must issue the -u argument to `update' 527 * to receive patches. 528 */ 529 } 530 531 void 532 cvs_server_add(char *data) 533 { 534 if (chdir(server_currentdir) == -1) 535 fatal("cvs_server_add: %s", strerror(errno)); 536 537 cvs_cmdop = CVS_OP_ADD; 538 cmdp->cmd_flags = cvs_cmd_add.cmd_flags; 539 cvs_add(server_argc, server_argv); 540 cvs_server_send_response("ok"); 541 } 542 543 void 544 cvs_server_import(char *data) 545 { 546 if (chdir(server_currentdir) == -1) 547 fatal("cvs_server_import: %s", strerror(errno)); 548 549 cvs_cmdop = CVS_OP_IMPORT; 550 cmdp->cmd_flags = cvs_cmd_import.cmd_flags; 551 cvs_import(server_argc, server_argv); 552 cvs_server_send_response("ok"); 553 } 554 555 void 556 cvs_server_admin(char *data) 557 { 558 if (chdir(server_currentdir) == -1) 559 fatal("cvs_server_admin: %s", strerror(errno)); 560 561 cvs_cmdop = CVS_OP_ADMIN; 562 cmdp->cmd_flags = cvs_cmd_admin.cmd_flags; 563 cvs_admin(server_argc, server_argv); 564 cvs_server_send_response("ok"); 565 } 566 567 void 568 cvs_server_annotate(char *data) 569 { 570 if (chdir(server_currentdir) == -1) 571 fatal("cvs_server_annotate: %s", strerror(errno)); 572 573 cvs_cmdop = CVS_OP_ANNOTATE; 574 cmdp->cmd_flags = cvs_cmd_annotate.cmd_flags; 575 cvs_annotate(server_argc, server_argv); 576 cvs_server_send_response("ok"); 577 } 578 579 void 580 cvs_server_rannotate(char *data) 581 { 582 if (chdir(server_currentdir) == -1) 583 fatal("cvs_server_rannotate: %s", strerror(errno)); 584 585 cvs_cmdop = CVS_OP_RANNOTATE; 586 cmdp->cmd_flags = cvs_cmd_rannotate.cmd_flags; 587 cvs_annotate(server_argc, server_argv); 588 cvs_server_send_response("ok"); 589 } 590 591 void 592 cvs_server_commit(char *data) 593 { 594 if (chdir(server_currentdir) == -1) 595 fatal("cvs_server_commit: %s", strerror(errno)); 596 597 cvs_cmdop = CVS_OP_COMMIT; 598 cmdp->cmd_flags = cvs_cmd_commit.cmd_flags; 599 cvs_commit(server_argc, server_argv); 600 cvs_server_send_response("ok"); 601 } 602 603 void 604 cvs_server_checkout(char *data) 605 { 606 if (chdir(server_currentdir) == -1) 607 fatal("cvs_server_checkout: %s", strerror(errno)); 608 609 cvs_cmdop = CVS_OP_CHECKOUT; 610 cmdp->cmd_flags = cvs_cmd_checkout.cmd_flags; 611 cvs_checkout(server_argc, server_argv); 612 cvs_server_send_response("ok"); 613 } 614 615 void 616 cvs_server_diff(char *data) 617 { 618 if (chdir(server_currentdir) == -1) 619 fatal("cvs_server_diff: %s", strerror(errno)); 620 621 cvs_cmdop = CVS_OP_DIFF; 622 cmdp->cmd_flags = cvs_cmd_diff.cmd_flags; 623 cvs_diff(server_argc, server_argv); 624 cvs_server_send_response("ok"); 625 } 626 627 void 628 cvs_server_rdiff(char *data) 629 { 630 if (chdir(server_currentdir) == -1) 631 fatal("cvs_server_rdiff: %s", strerror(errno)); 632 633 cvs_cmdop = CVS_OP_RDIFF; 634 cmdp->cmd_flags = cvs_cmd_rdiff.cmd_flags; 635 cvs_diff(server_argc, server_argv); 636 cvs_server_send_response("ok"); 637 } 638 639 void 640 cvs_server_export(char *data) 641 { 642 if (chdir(server_currentdir) == -1) 643 fatal("cvs_server_export: %s", strerror(errno)); 644 645 cvs_cmdop = CVS_OP_EXPORT; 646 cmdp->cmd_flags = cvs_cmd_export.cmd_flags; 647 cvs_export(server_argc, server_argv); 648 cvs_server_send_response("ok"); 649 } 650 651 void 652 cvs_server_init(char *data) 653 { 654 if (data == NULL) 655 fatal("Missing argument for init"); 656 657 if (current_cvsroot != NULL) 658 fatal("Root in combination with init is not supported"); 659 660 if ((current_cvsroot = cvsroot_get(data)) == NULL) 661 fatal("Invalid argument for init"); 662 663 cvs_cmdop = CVS_OP_INIT; 664 cmdp->cmd_flags = cvs_cmd_init.cmd_flags; 665 cvs_init(server_argc, server_argv); 666 cvs_server_send_response("ok"); 667 } 668 669 void 670 cvs_server_release(char *data) 671 { 672 if (chdir(server_currentdir) == -1) 673 fatal("cvs_server_release: %s", strerror(errno)); 674 675 cvs_cmdop = CVS_OP_RELEASE; 676 cmdp->cmd_flags = cvs_cmd_release.cmd_flags; 677 cvs_release(server_argc, server_argv); 678 cvs_server_send_response("ok"); 679 } 680 681 void 682 cvs_server_remove(char *data) 683 { 684 if (chdir(server_currentdir) == -1) 685 fatal("cvs_server_remove: %s", strerror(errno)); 686 687 cvs_cmdop = CVS_OP_REMOVE; 688 cmdp->cmd_flags = cvs_cmd_remove.cmd_flags; 689 cvs_remove(server_argc, server_argv); 690 cvs_server_send_response("ok"); 691 } 692 693 void 694 cvs_server_status(char *data) 695 { 696 if (chdir(server_currentdir) == -1) 697 fatal("cvs_server_status: %s", strerror(errno)); 698 699 cvs_cmdop = CVS_OP_STATUS; 700 cmdp->cmd_flags = cvs_cmd_status.cmd_flags; 701 cvs_status(server_argc, server_argv); 702 cvs_server_send_response("ok"); 703 } 704 705 void 706 cvs_server_log(char *data) 707 { 708 if (chdir(server_currentdir) == -1) 709 fatal("cvs_server_log: %s", strerror(errno)); 710 711 cvs_cmdop = CVS_OP_LOG; 712 cmdp->cmd_flags = cvs_cmd_log.cmd_flags; 713 cvs_getlog(server_argc, server_argv); 714 cvs_server_send_response("ok"); 715 } 716 717 void 718 cvs_server_rlog(char *data) 719 { 720 if (chdir(current_cvsroot->cr_dir) == -1) 721 fatal("cvs_server_rlog: %s", strerror(errno)); 722 723 cvs_cmdop = CVS_OP_RLOG; 724 cmdp->cmd_flags = cvs_cmd_rlog.cmd_flags; 725 cvs_getlog(server_argc, server_argv); 726 cvs_server_send_response("ok"); 727 } 728 729 void 730 cvs_server_tag(char *data) 731 { 732 if (chdir(server_currentdir) == -1) 733 fatal("cvs_server_tag: %s", strerror(errno)); 734 735 cvs_cmdop = CVS_OP_TAG; 736 cmdp->cmd_flags = cvs_cmd_tag.cmd_flags; 737 cvs_tag(server_argc, server_argv); 738 cvs_server_send_response("ok"); 739 } 740 741 void 742 cvs_server_rtag(char *data) 743 { 744 if (chdir(current_cvsroot->cr_dir) == -1) 745 fatal("cvs_server_rtag: %s", strerror(errno)); 746 747 cvs_cmdop = CVS_OP_RTAG; 748 cmdp->cmd_flags = cvs_cmd_rtag.cmd_flags; 749 cvs_tag(server_argc, server_argv); 750 cvs_server_send_response("ok"); 751 } 752 753 void 754 cvs_server_update(char *data) 755 { 756 if (chdir(server_currentdir) == -1) 757 fatal("cvs_server_update: %s", strerror(errno)); 758 759 cvs_cmdop = CVS_OP_UPDATE; 760 cmdp->cmd_flags = cvs_cmd_update.cmd_flags; 761 cvs_update(server_argc, server_argv); 762 cvs_server_send_response("ok"); 763 } 764 765 void 766 cvs_server_version(char *data) 767 { 768 cvs_cmdop = CVS_OP_VERSION; 769 cmdp->cmd_flags = cvs_cmd_version.cmd_flags; 770 cvs_version(server_argc, server_argv); 771 cvs_server_send_response("ok"); 772 } 773 774 void 775 cvs_server_update_entry(const char *resp, struct cvs_file *cf) 776 { 777 char *p; 778 char repo[PATH_MAX], fpath[PATH_MAX]; 779 780 if ((p = strrchr(cf->file_rpath, ',')) != NULL) 781 *p = '\0'; 782 783 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 784 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", repo, cf->file_name); 785 786 cvs_server_send_response("%s %s/", resp, cf->file_wd); 787 cvs_remote_output(fpath); 788 789 if (p != NULL) 790 *p = ','; 791 } 792 793 void 794 cvs_server_set_sticky(const char *dir, char *tag) 795 { 796 char fpath[PATH_MAX]; 797 char repo[PATH_MAX]; 798 799 cvs_get_repository_path(dir, repo, PATH_MAX); 800 (void)xsnprintf(fpath, PATH_MAX, "%s/", repo); 801 802 cvs_server_send_response("Set-sticky %s/", dir); 803 cvs_remote_output(fpath); 804 cvs_remote_output(tag); 805 } 806 807 void 808 cvs_server_clear_sticky(char *dir) 809 { 810 char fpath[PATH_MAX]; 811 char repo[PATH_MAX]; 812 813 cvs_get_repository_path(dir, repo, PATH_MAX); 814 (void)xsnprintf(fpath, PATH_MAX, "%s/", repo); 815 816 cvs_server_send_response("Clear-sticky %s//", dir); 817 cvs_remote_output(fpath); 818 } 819 820 void 821 cvs_server_exp_modules(char *module) 822 { 823 struct module_checkout *mo; 824 struct cvs_filelist *fl; 825 826 if (server_argc != 2) 827 fatal("expand-modules with no arguments"); 828 829 mo = cvs_module_lookup(server_argv[1]); 830 831 RB_FOREACH(fl, cvs_flisthead, &(mo->mc_modules)) 832 cvs_server_send_response("Module-expansion %s", fl->file_path); 833 cvs_server_send_response("ok"); 834 835 server_argc--; 836 free(server_argv[1]); 837 server_argv[1] = NULL; 838 } 839