Annotation of src/usr.bin/cvs/server.c, Revision 1.58
1.58 ! ray 1: /* $OpenBSD: server.c,v 1.57 2007/05/25 22:27:02 ray Exp $ */
1.1 jfb 2: /*
1.29 joris 3: * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
1.1 jfb 4: *
1.29 joris 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.
1.1 jfb 8: *
1.29 joris 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.
1.1 jfb 16: */
17:
1.55 otto 18: #include <sys/stat.h>
19:
20: #include <errno.h>
21: #include <fcntl.h>
22: #include <libgen.h>
23: #include <stdlib.h>
24: #include <string.h>
25: #include <unistd.h>
1.1 jfb 26:
27: #include "cvs.h"
1.29 joris 28: #include "remote.h"
29:
30: struct cvs_resp cvs_responses[] = {
31: /* this is what our server uses, the client should support it */
32: { "Valid-requests", 1, cvs_client_validreq, RESP_NEEDED },
33: { "ok", 0, cvs_client_ok, RESP_NEEDED},
34: { "error", 0, cvs_client_error, RESP_NEEDED },
35: { "E", 0, cvs_client_e, RESP_NEEDED },
36: { "M", 0, cvs_client_m, RESP_NEEDED },
37: { "Checked-in", 0, cvs_client_checkedin, RESP_NEEDED },
38: { "Updated", 0, cvs_client_updated, RESP_NEEDED },
39: { "Merged", 0, cvs_client_merged, RESP_NEEDED },
40: { "Removed", 0, cvs_client_removed, RESP_NEEDED },
41: { "Remove-entry", 0, cvs_client_remove_entry, RESP_NEEDED },
1.46 xsa 42: { "Set-static-directory", 0, cvs_client_set_static_directory, RESP_NEEDED },
1.45 xsa 43: { "Clear-static-directory", 0, cvs_client_clear_static_directory, RESP_NEEDED },
44: { "Set-sticky", 0, cvs_client_set_sticky, RESP_NEEDED },
45: { "Clear-sticky", 0, cvs_client_clear_sticky, RESP_NEEDED },
1.29 joris 46:
47: /* unsupported responses until told otherwise */
48: { "New-entry", 0, NULL, 0 },
49: { "Created", 0, NULL, 0 },
50: { "Update-existing", 0, NULL, 0 },
51: { "Rcs-diff", 0, NULL, 0 },
52: { "Patched", 0, NULL, 0 },
53: { "Mode", 0, NULL, 0 },
54: { "Mod-time", 0, NULL, 0 },
55: { "Checksum", 0, NULL, 0 },
56: { "Copy-file", 0, NULL, 0 },
57: { "Template", 0, NULL, 0 },
58: { "Set-checkin-prog", 0, NULL, 0 },
59: { "Set-update-prog", 0, NULL, 0 },
60: { "Notified", 0, NULL, 0 },
61: { "Module-expansion", 0, NULL, 0 },
62: { "Wrapper-rcsOption", 0, NULL, 0 },
63: { "Mbinary", 0, NULL, 0 },
64: { "F", 0, NULL, 0 },
65: { "MT", 0, NULL, 0 },
66: { "", -1, NULL, 0 }
67: };
1.1 jfb 68:
1.29 joris 69: int cvs_server(int, char **);
70: char *cvs_server_path = NULL;
1.1 jfb 71:
1.29 joris 72: static char *server_currentdir = NULL;
73: static char *server_argv[CVS_CMD_MAXARG];
74: static int server_argc = 1;
1.8 joris 75:
1.17 jfb 76: struct cvs_cmd cvs_cmd_server = {
1.29 joris 77: CVS_OP_SERVER, 0, "server", { "", "" },
78: "server mode",
1.17 jfb 79: NULL,
1.29 joris 80: NULL,
81: NULL,
82: cvs_server
1.17 jfb 83: };
1.1 jfb 84:
1.14 joris 85:
1.1 jfb 86: int
87: cvs_server(int argc, char **argv)
88: {
1.29 joris 89: char *cmd, *data;
90: struct cvs_req *req;
91:
92: server_argv[0] = xstrdup("server");
93:
1.58 ! ray 94: (void)xasprintf(&cvs_server_path, "%s/cvs-serv%d", cvs_tmpdir,
! 95: getpid());
1.29 joris 96:
97: if (mkdir(cvs_server_path, 0700) == -1)
98: fatal("failed to create temporary server directory: %s, %s",
99: cvs_server_path, strerror(errno));
100:
101: if (chdir(cvs_server_path) == -1)
102: fatal("failed to change directory to '%s'", cvs_server_path);
103:
104: for (;;) {
105: cmd = cvs_remote_input();
106:
107: if ((data = strchr(cmd, ' ')) != NULL)
108: (*data++) = '\0';
109:
110: req = cvs_remote_get_request_info(cmd);
111: if (req == NULL)
112: fatal("request '%s' is not supported by our server",
113: cmd);
114:
115: if (req->hdlr == NULL)
116: fatal("opencvs server does not support '%s'", cmd);
117:
118: (*req->hdlr)(data);
119: xfree(cmd);
1.14 joris 120: }
121:
1.29 joris 122: return (0);
123: }
124:
125: void
126: cvs_server_send_response(char *fmt, ...)
127: {
128: va_list ap;
129: char *data, *s;
130: struct cvs_resp *resp;
131:
132: va_start(ap, fmt);
1.56 ray 133: if (vasprintf(&data, fmt, ap) == -1)
134: fatal("vasprintf: %s", strerror(errno));
1.29 joris 135: va_end(ap);
136:
137: if ((s = strchr(data, ' ')) != NULL)
138: *s = '\0';
139:
140: resp = cvs_remote_get_response_info(data);
141: if (resp == NULL)
142: fatal("'%s' is an unknown response", data);
143:
144: if (resp->supported != 1)
145: fatal("remote cvs client does not support '%s'", data);
1.14 joris 146:
1.29 joris 147: if (s != NULL)
148: *s = ' ';
1.14 joris 149:
1.30 joris 150: cvs_log(LP_TRACE, "%s", data);
1.29 joris 151: cvs_remote_output(data);
152: xfree(data);
153: }
154:
155: void
156: cvs_server_root(char *data)
157: {
158: fatal("duplicate Root request from client, violates the protocol");
159: }
160:
161: void
162: cvs_server_validresp(char *data)
163: {
164: int i;
165: char *sp, *ep;
166: struct cvs_resp *resp;
167:
1.57 ray 168: if ((sp = data) == NULL)
169: fatal("Missing argument for Valid-responses");
170:
1.29 joris 171: do {
172: if ((ep = strchr(sp, ' ')) != NULL)
173: *ep = '\0';
174:
175: resp = cvs_remote_get_response_info(sp);
176: if (resp != NULL)
177: resp->supported = 1;
178:
179: if (ep != NULL)
180: sp = ep + 1;
181: } while (ep != NULL);
182:
183: for (i = 0; cvs_responses[i].supported != -1; i++) {
184: resp = &cvs_responses[i];
185: if ((resp->flags & RESP_NEEDED) &&
186: resp->supported != 1) {
187: fatal("client does not support required '%s'",
188: resp->name);
1.1 jfb 189: }
1.29 joris 190: }
191: }
1.1 jfb 192:
1.29 joris 193: void
194: cvs_server_validreq(char *data)
195: {
196: BUF *bp;
197: char *d;
198: int i, first;
199:
200: first = 0;
201: bp = cvs_buf_alloc(512, BUF_AUTOEXT);
202: for (i = 0; cvs_requests[i].supported != -1; i++) {
203: if (cvs_requests[i].hdlr == NULL)
1.5 jfb 204: continue;
1.1 jfb 205:
1.29 joris 206: if (first != 0)
207: cvs_buf_append(bp, " ", 1);
208: else
209: first++;
210:
211: cvs_buf_append(bp, cvs_requests[i].name,
212: strlen(cvs_requests[i].name));
213: }
214:
215: cvs_buf_putc(bp, '\0');
216: d = cvs_buf_release(bp);
217:
218: cvs_server_send_response("Valid-requests %s", d);
219: cvs_server_send_response("ok");
220: xfree(d);
1.42 xsa 221: }
222:
223: void
1.43 xsa 224: cvs_server_static_directory(char *data)
225: {
226: FILE *fp;
1.51 otto 227: char fpath[MAXPATHLEN];
1.43 xsa 228:
1.54 xsa 229: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
230: server_currentdir, CVS_PATH_STATICENTRIES);
1.43 xsa 231:
232: if ((fp = fopen(fpath, "w+")) == NULL) {
233: cvs_log(LP_ERRNO, "%s", fpath);
1.51 otto 234: return;
1.43 xsa 235: }
236: (void)fclose(fp);
237: }
238:
239: void
1.42 xsa 240: cvs_server_sticky(char *data)
241: {
242: FILE *fp;
1.51 otto 243: char tagpath[MAXPATHLEN];
1.42 xsa 244:
1.57 ray 245: if (data == NULL)
246: fatal("Missing argument for Sticky");
247:
1.54 xsa 248: (void)xsnprintf(tagpath, MAXPATHLEN, "%s/%s",
249: server_currentdir, CVS_PATH_TAG);
1.42 xsa 250:
1.43 xsa 251: if ((fp = fopen(tagpath, "w+")) == NULL) {
1.42 xsa 252: cvs_log(LP_ERRNO, "%s", tagpath);
1.51 otto 253: return;
1.42 xsa 254: }
255:
256: (void)fprintf(fp, "%s\n", data);
257: (void)fclose(fp);
1.29 joris 258: }
259:
260: void
261: cvs_server_globalopt(char *data)
262: {
1.57 ray 263: if (data == NULL)
264: fatal("Missing argument for Global_option");
265:
1.38 xsa 266: if (!strcmp(data, "-l"))
267: cvs_nolog = 1;
1.29 joris 268:
269: if (!strcmp(data, "-n"))
270: cvs_noexec = 1;
1.38 xsa 271:
272: if (!strcmp(data, "-Q"))
273: verbosity = 0;
274:
275: if (!strcmp(data, "-r"))
276: cvs_readonly = 1;
277:
278: if (!strcmp(data, "-t"))
279: cvs_trace = 1;
1.29 joris 280:
281: if (!strcmp(data, "-V"))
282: verbosity = 2;
1.39 xsa 283: }
284:
285: void
286: cvs_server_set(char *data)
287: {
288: char *ep;
289:
1.57 ray 290: if (data == NULL)
291: fatal("Missing argument for Set");
292:
1.39 xsa 293: ep = strchr(data, '=');
294: if (ep == NULL)
295: fatal("no = in variable assignment");
296:
297: *(ep++) = '\0';
298: if (cvs_var_set(data, ep) < 0)
299: fatal("cvs_server_set: cvs_var_set failed");
1.29 joris 300: }
1.1 jfb 301:
1.29 joris 302: void
303: cvs_server_directory(char *data)
304: {
305: CVSENTRIES *entlist;
1.51 otto 306: char *dir, *repo, *parent, entry[CVS_ENT_MAXLINELEN], *dirn, *p;
1.29 joris 307:
308: dir = cvs_remote_input();
1.49 joris 309: STRIP_SLASH(dir);
310:
311: if (strlen(dir) < strlen(current_cvsroot->cr_dir))
1.29 joris 312: fatal("cvs_server_directory: bad Directory request");
313:
1.49 joris 314: repo = dir + strlen(current_cvsroot->cr_dir);
315:
316: /*
317: * This is somewhat required for checkout, as the
318: * directory request will be:
319: *
320: * Directory .
321: * /path/to/cvs/root
322: */
323: if (repo[0] == '\0')
324: p = xstrdup(".");
325: else
326: p = xstrdup(repo + 1);
327:
328: cvs_mkpath(p);
1.29 joris 329:
1.49 joris 330: if ((dirn = basename(p)) == NULL)
1.29 joris 331: fatal("cvs_server_directory: %s", strerror(errno));
332:
1.49 joris 333: if ((parent = dirname(p)) == NULL)
1.29 joris 334: fatal("cvs_server_directory: %s", strerror(errno));
335:
336: if (strcmp(parent, ".")) {
337: entlist = cvs_ent_open(parent);
1.53 xsa 338: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", dirn);
1.29 joris 339:
340: cvs_ent_add(entlist, entry);
341: cvs_ent_close(entlist, ENT_SYNC);
1.1 jfb 342: }
343:
1.29 joris 344: if (server_currentdir != NULL)
345: xfree(server_currentdir);
1.49 joris 346: server_currentdir = xstrdup(p);
1.29 joris 347:
1.49 joris 348: xfree(p);
1.29 joris 349: xfree(dir);
350: }
351:
352: void
353: cvs_server_entry(char *data)
354: {
355: CVSENTRIES *entlist;
356:
1.57 ray 357: if (data == NULL)
358: fatal("Missing argument for Entry");
359:
1.29 joris 360: entlist = cvs_ent_open(server_currentdir);
361: cvs_ent_add(entlist, data);
362: cvs_ent_close(entlist, ENT_SYNC);
363: }
364:
365: void
366: cvs_server_modified(char *data)
367: {
1.41 xsa 368: int fd;
1.29 joris 369: size_t flen;
370: mode_t fmode;
371: const char *errstr;
1.51 otto 372: char *mode, *len, fpath[MAXPATHLEN];
1.29 joris 373:
1.57 ray 374: if (data == NULL)
375: fatal("Missing argument for Modified");
376:
1.29 joris 377: mode = cvs_remote_input();
378: len = cvs_remote_input();
379:
380: cvs_strtomode(mode, &fmode);
381: xfree(mode);
382:
383: flen = strtonum(len, 0, INT_MAX, &errstr);
384: if (errstr != NULL)
385: fatal("cvs_server_modified: %s", errstr);
386: xfree(len);
387:
1.54 xsa 388: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
1.29 joris 389:
390: if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1)
391: fatal("cvs_server_modified: %s: %s", fpath, strerror(errno));
392:
1.48 joris 393: cvs_remote_receive_file(fd, flen);
1.29 joris 394:
395: if (fchmod(fd, 0600) == -1)
396: fatal("cvs_server_modified: failed to set file mode");
397:
398: (void)close(fd);
399: }
400:
401: void
402: cvs_server_useunchanged(char *data)
403: {
404: }
405:
406: void
407: cvs_server_unchanged(char *data)
408: {
1.41 xsa 409: int fd;
1.51 otto 410: char fpath[MAXPATHLEN];
1.29 joris 411: CVSENTRIES *entlist;
412: struct cvs_ent *ent;
413: struct timeval tv[2];
414:
1.57 ray 415: if (data == NULL)
416: fatal("Missing argument for Unchanged");
417:
1.54 xsa 418: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
1.29 joris 419:
420: if ((fd = open(fpath, O_RDWR | O_CREAT | O_TRUNC)) == -1)
421: fatal("cvs_server_unchanged: %s: %s", fpath, strerror(errno));
422:
423: entlist = cvs_ent_open(server_currentdir);
424: ent = cvs_ent_get(entlist, data);
425: if (ent == NULL)
426: fatal("received Unchanged request for non-existing file");
427: cvs_ent_close(entlist, ENT_NOSYNC);
428:
1.52 joris 429: tv[0].tv_sec = ent->ce_mtime;
1.29 joris 430: tv[0].tv_usec = 0;
431: tv[1] = tv[0];
432: if (futimes(fd, tv) == -1)
433: fatal("cvs_server_unchanged: failed to set modified time");
434:
435: if (fchmod(fd, 0600) == -1)
436: fatal("cvs_server_unchanged: failed to set mode");
437:
438: cvs_ent_free(ent);
439: (void)close(fd);
440: }
441:
442: void
443: cvs_server_questionable(char *data)
444: {
445: }
446:
447: void
448: cvs_server_argument(char *data)
449: {
450:
451: if (server_argc > CVS_CMD_MAXARG)
452: fatal("cvs_server_argument: too many arguments sent");
1.57 ray 453:
454: if (data == NULL)
455: fatal("Missing argument for Argument");
1.29 joris 456:
457: server_argv[server_argc++] = xstrdup(data);
1.37 xsa 458: }
459:
460: void
461: cvs_server_argumentx(char *data)
462: {
1.44 xsa 463: }
464:
465: void
466: cvs_server_update_patches(char *data)
467: {
468: /*
469: * This does not actually do anything.
470: * It is used to tell that the server is able to
471: * generate patches when given an `update' request.
472: * The client must issue the -u argument to `update'
473: * to receive patches.
474: */
1.29 joris 475: }
476:
477: void
1.31 xsa 478: cvs_server_add(char *data)
479: {
480: if (chdir(server_currentdir) == -1)
481: fatal("cvs_server_add: %s", strerror(errno));
482:
483: cvs_cmdop = CVS_OP_ADD;
484: cvs_add(server_argc, server_argv);
1.50 joris 485: cvs_server_send_response("ok");
486: }
487:
488: void
489: cvs_server_import(char *data)
490: {
491: if (chdir(server_currentdir) == -1)
492: fatal("cvs_server_import: %s", strerror(errno));
493:
494: cvs_cmdop = CVS_OP_IMPORT;
495: cvs_import(server_argc, server_argv);
1.31 xsa 496: cvs_server_send_response("ok");
497: }
1.35 xsa 498:
499: void
500: cvs_server_admin(char *data)
501: {
502: if (chdir(server_currentdir) == -1)
503: fatal("cvs_server_admin: %s", strerror(errno));
504:
505: cvs_cmdop = CVS_OP_ADMIN;
506: cvs_admin(server_argc, server_argv);
507: cvs_server_send_response("ok");
508: }
509:
1.40 xsa 510: void
511: cvs_server_annotate(char *data)
512: {
513: if (chdir(server_currentdir) == -1)
514: fatal("cvs_server_annotate: %s", strerror(errno));
515:
516: cvs_cmdop = CVS_OP_ANNOTATE;
517: cvs_annotate(server_argc, server_argv);
518: cvs_server_send_response("ok");
519: }
1.31 xsa 520:
521: void
1.29 joris 522: cvs_server_commit(char *data)
523: {
524: if (chdir(server_currentdir) == -1)
525: fatal("cvs_server_commit: %s", strerror(errno));
526:
527: cvs_cmdop = CVS_OP_COMMIT;
528: cvs_commit(server_argc, server_argv);
1.49 joris 529: cvs_server_send_response("ok");
530: }
531:
532: void
533: cvs_server_checkout(char *data)
534: { if (chdir(server_currentdir) == -1)
535: fatal("cvs_server_checkout: %s", strerror(errno));
536:
537: cvs_cmdop = CVS_OP_CHECKOUT;
538: cvs_checkout(server_argc, server_argv);
1.29 joris 539: cvs_server_send_response("ok");
540: }
541:
542: void
543: cvs_server_diff(char *data)
544: {
545: if (chdir(server_currentdir) == -1)
546: fatal("cvs_server_diff: %s", strerror(errno));
547:
548: cvs_cmdop = CVS_OP_DIFF;
549: cvs_diff(server_argc, server_argv);
1.34 xsa 550: cvs_server_send_response("ok");
551: }
552:
553: void
554: cvs_server_init(char *data)
555: {
556: if (chdir(server_currentdir) == -1)
557: fatal("cvs_server_init: %s", strerror(errno));
558:
559: cvs_cmdop = CVS_OP_INIT;
560: cvs_init(server_argc, server_argv);
1.31 xsa 561: cvs_server_send_response("ok");
562: }
563:
564: void
565: cvs_server_remove(char *data)
566: {
567: if (chdir(server_currentdir) == -1)
568: fatal("cvs_server_remove: %s", strerror(errno));
569:
570: cvs_cmdop = CVS_OP_REMOVE;
571: cvs_remove(server_argc, server_argv);
1.29 joris 572: cvs_server_send_response("ok");
573: }
574:
575: void
576: cvs_server_status(char *data)
577: {
578: if (chdir(server_currentdir) == -1)
579: fatal("cvs_server_status: %s", strerror(errno));
580:
581: cvs_cmdop = CVS_OP_STATUS;
582: cvs_status(server_argc, server_argv);
583: cvs_server_send_response("ok");
584: }
585:
586: void
587: cvs_server_log(char *data)
588: {
589: if (chdir(server_currentdir) == -1)
590: fatal("cvs_server_log: %s", strerror(errno));
591:
592: cvs_cmdop = CVS_OP_LOG;
1.32 xsa 593: cvs_getlog(server_argc, server_argv);
594: cvs_server_send_response("ok");
595: }
596:
597: void
598: cvs_server_tag(char *data)
599: {
600: if (chdir(server_currentdir) == -1)
601: fatal("cvs_server_tag: %s", strerror(errno));
602:
603: cvs_cmdop = CVS_OP_TAG;
1.33 xsa 604: cvs_tag(server_argc, server_argv);
1.29 joris 605: cvs_server_send_response("ok");
606: }
607:
608: void
609: cvs_server_update(char *data)
610: {
611: if (chdir(server_currentdir) == -1)
612: fatal("cvs_server_update: %s", strerror(errno));
1.14 joris 613:
1.29 joris 614: cvs_cmdop = CVS_OP_UPDATE;
615: cvs_update(server_argc, server_argv);
1.36 xsa 616: cvs_server_send_response("ok");
617: }
618:
619: void
620: cvs_server_version(char *data)
621: {
622: cvs_cmdop = CVS_OP_VERSION;
623: cvs_version(server_argc, server_argv);
1.29 joris 624: cvs_server_send_response("ok");
1.47 joris 625: }
626:
627: void
628: cvs_server_update_entry(const char *resp, struct cvs_file *cf)
629: {
1.58 ! ray 630: char *p;
1.47 joris 631:
632: if ((p = strrchr(cf->file_rpath, ',')) != NULL)
633: *p = '\0';
634:
1.58 ! ray 635: cvs_server_send_response("%s %s/", resp, cf->file_wd);
1.47 joris 636: cvs_remote_output(cf->file_rpath);
637:
638: if (p != NULL)
639: *p = ',';
1.1 jfb 640: }