xref: /openbsd/usr.bin/cvs/server.c (revision 431378d1)
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
cvs_server(int argc,char ** argv)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
cvs_server_send_response(char * fmt,...)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
cvs_server_root(char * data)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
cvs_server_validresp(char * data)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
cvs_server_validreq(char * data)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
cvs_server_static_directory(char * data)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
cvs_server_sticky(char * data)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
cvs_server_globalopt(char * data)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
cvs_server_set(char * data)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
cvs_server_directory(char * data)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
cvs_server_entry(char * data)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
cvs_server_modified(char * data)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
cvs_server_useunchanged(char * data)432 cvs_server_useunchanged(char *data)
433 {
434 }
435 
436 void
cvs_server_unchanged(char * data)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
cvs_server_questionable(char * data)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
cvs_server_argument(char * data)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
cvs_server_argumentx(char * data)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
cvs_server_update_patches(char * data)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
cvs_server_add(char * data)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
cvs_server_import(char * data)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
cvs_server_admin(char * data)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
cvs_server_annotate(char * data)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
cvs_server_rannotate(char * data)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
cvs_server_commit(char * data)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
cvs_server_checkout(char * data)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
cvs_server_diff(char * data)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
cvs_server_rdiff(char * data)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
cvs_server_export(char * data)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
cvs_server_init(char * data)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
cvs_server_release(char * data)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
cvs_server_remove(char * data)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
cvs_server_status(char * data)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
cvs_server_log(char * data)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
cvs_server_rlog(char * data)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
cvs_server_tag(char * data)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
cvs_server_rtag(char * data)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
cvs_server_update(char * data)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
cvs_server_version(char * data)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
cvs_server_update_entry(const char * resp,struct cvs_file * cf)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
cvs_server_set_sticky(const char * dir,char * tag)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
cvs_server_clear_sticky(char * dir)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
cvs_server_exp_modules(char * module)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