jcs's openbsd hax
openbsd
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
32struct 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
73int cvs_server(int, char **);
74char *cvs_server_path = NULL;
75
76static char *server_currentdir = NULL;
77static char **server_argv;
78static int server_argc = 1;
79
80extern int disable_fast_checkout;
81
82struct 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
92int
93cvs_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
147void
148cvs_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
165void
166cvs_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
182void
183cvs_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
214void
215cvs_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
243void
244cvs_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
259void
260cvs_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
280void
281cvs_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
305void
306cvs_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
322void
323cvs_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
380void
381cvs_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
392void
393cvs_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
431void
432cvs_server_useunchanged(char *data)
433{
434}
435
436void
437cvs_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
471void
472cvs_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
490void
491cvs_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
502void
503cvs_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
519void
520cvs_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
531void
532cvs_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
543void
544cvs_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
555void
556cvs_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
567void
568cvs_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
579void
580cvs_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
591void
592cvs_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
603void
604cvs_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
615void
616cvs_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
627void
628cvs_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
639void
640cvs_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
651void
652cvs_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
669void
670cvs_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
681void
682cvs_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
693void
694cvs_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
705void
706cvs_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
717void
718cvs_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
729void
730cvs_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
741void
742cvs_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
753void
754cvs_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
765void
766cvs_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
774void
775cvs_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
793void
794cvs_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
807void
808cvs_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
820void
821cvs_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}