Annotation of src/usr.bin/cvs/server.c, Revision 1.62
1.62 ! niallo 1: /* $OpenBSD: server.c,v 1.61 2007/06/06 00:38:37 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;
1.59 ray 129: char *data;
1.29 joris 130:
131: va_start(ap, fmt);
1.56 ray 132: if (vasprintf(&data, fmt, ap) == -1)
133: fatal("vasprintf: %s", strerror(errno));
1.29 joris 134: va_end(ap);
1.14 joris 135:
1.30 joris 136: cvs_log(LP_TRACE, "%s", data);
1.29 joris 137: cvs_remote_output(data);
138: xfree(data);
139: }
140:
141: void
142: cvs_server_root(char *data)
143: {
144: fatal("duplicate Root request from client, violates the protocol");
145: }
146:
147: void
148: cvs_server_validresp(char *data)
149: {
150: int i;
151: char *sp, *ep;
152: struct cvs_resp *resp;
153:
1.57 ray 154: if ((sp = data) == NULL)
155: fatal("Missing argument for Valid-responses");
156:
1.29 joris 157: do {
158: if ((ep = strchr(sp, ' ')) != NULL)
159: *ep = '\0';
160:
161: resp = cvs_remote_get_response_info(sp);
162: if (resp != NULL)
163: resp->supported = 1;
164:
165: if (ep != NULL)
166: sp = ep + 1;
167: } while (ep != NULL);
168:
169: for (i = 0; cvs_responses[i].supported != -1; i++) {
170: resp = &cvs_responses[i];
171: if ((resp->flags & RESP_NEEDED) &&
172: resp->supported != 1) {
173: fatal("client does not support required '%s'",
174: resp->name);
1.1 jfb 175: }
1.29 joris 176: }
177: }
1.1 jfb 178:
1.29 joris 179: void
180: cvs_server_validreq(char *data)
181: {
182: BUF *bp;
183: char *d;
184: int i, first;
185:
186: first = 0;
187: bp = cvs_buf_alloc(512, BUF_AUTOEXT);
188: for (i = 0; cvs_requests[i].supported != -1; i++) {
189: if (cvs_requests[i].hdlr == NULL)
1.5 jfb 190: continue;
1.1 jfb 191:
1.29 joris 192: if (first != 0)
193: cvs_buf_append(bp, " ", 1);
194: else
195: first++;
196:
197: cvs_buf_append(bp, cvs_requests[i].name,
198: strlen(cvs_requests[i].name));
199: }
200:
201: cvs_buf_putc(bp, '\0');
202: d = cvs_buf_release(bp);
203:
204: cvs_server_send_response("Valid-requests %s", d);
205: cvs_server_send_response("ok");
206: xfree(d);
1.42 xsa 207: }
208:
209: void
1.43 xsa 210: cvs_server_static_directory(char *data)
211: {
212: FILE *fp;
1.51 otto 213: char fpath[MAXPATHLEN];
1.43 xsa 214:
1.54 xsa 215: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
216: server_currentdir, CVS_PATH_STATICENTRIES);
1.43 xsa 217:
218: if ((fp = fopen(fpath, "w+")) == NULL) {
219: cvs_log(LP_ERRNO, "%s", fpath);
1.51 otto 220: return;
1.43 xsa 221: }
222: (void)fclose(fp);
223: }
224:
225: void
1.42 xsa 226: cvs_server_sticky(char *data)
227: {
228: FILE *fp;
1.51 otto 229: char tagpath[MAXPATHLEN];
1.42 xsa 230:
1.57 ray 231: if (data == NULL)
232: fatal("Missing argument for Sticky");
233:
1.54 xsa 234: (void)xsnprintf(tagpath, MAXPATHLEN, "%s/%s",
235: server_currentdir, CVS_PATH_TAG);
1.42 xsa 236:
1.43 xsa 237: if ((fp = fopen(tagpath, "w+")) == NULL) {
1.42 xsa 238: cvs_log(LP_ERRNO, "%s", tagpath);
1.51 otto 239: return;
1.42 xsa 240: }
241:
242: (void)fprintf(fp, "%s\n", data);
243: (void)fclose(fp);
1.29 joris 244: }
245:
246: void
247: cvs_server_globalopt(char *data)
248: {
1.57 ray 249: if (data == NULL)
250: fatal("Missing argument for Global_option");
251:
1.38 xsa 252: if (!strcmp(data, "-l"))
253: cvs_nolog = 1;
1.29 joris 254:
255: if (!strcmp(data, "-n"))
256: cvs_noexec = 1;
1.38 xsa 257:
258: if (!strcmp(data, "-Q"))
259: verbosity = 0;
260:
261: if (!strcmp(data, "-r"))
262: cvs_readonly = 1;
263:
264: if (!strcmp(data, "-t"))
265: cvs_trace = 1;
1.29 joris 266:
267: if (!strcmp(data, "-V"))
268: verbosity = 2;
1.39 xsa 269: }
270:
271: void
272: cvs_server_set(char *data)
273: {
274: char *ep;
275:
1.57 ray 276: if (data == NULL)
277: fatal("Missing argument for Set");
278:
1.39 xsa 279: ep = strchr(data, '=');
280: if (ep == NULL)
281: fatal("no = in variable assignment");
282:
283: *(ep++) = '\0';
284: if (cvs_var_set(data, ep) < 0)
285: fatal("cvs_server_set: cvs_var_set failed");
1.29 joris 286: }
1.1 jfb 287:
1.29 joris 288: void
289: cvs_server_directory(char *data)
290: {
291: CVSENTRIES *entlist;
1.51 otto 292: char *dir, *repo, *parent, entry[CVS_ENT_MAXLINELEN], *dirn, *p;
1.29 joris 293:
294: dir = cvs_remote_input();
1.49 joris 295: STRIP_SLASH(dir);
296:
297: if (strlen(dir) < strlen(current_cvsroot->cr_dir))
1.29 joris 298: fatal("cvs_server_directory: bad Directory request");
299:
1.49 joris 300: repo = dir + strlen(current_cvsroot->cr_dir);
301:
302: /*
303: * This is somewhat required for checkout, as the
304: * directory request will be:
305: *
306: * Directory .
307: * /path/to/cvs/root
308: */
309: if (repo[0] == '\0')
310: p = xstrdup(".");
311: else
312: p = xstrdup(repo + 1);
313:
314: cvs_mkpath(p);
1.29 joris 315:
1.49 joris 316: if ((dirn = basename(p)) == NULL)
1.29 joris 317: fatal("cvs_server_directory: %s", strerror(errno));
318:
1.49 joris 319: if ((parent = dirname(p)) == NULL)
1.29 joris 320: fatal("cvs_server_directory: %s", strerror(errno));
321:
322: if (strcmp(parent, ".")) {
323: entlist = cvs_ent_open(parent);
1.53 xsa 324: (void)xsnprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", dirn);
1.29 joris 325:
326: cvs_ent_add(entlist, entry);
327: cvs_ent_close(entlist, ENT_SYNC);
1.1 jfb 328: }
329:
1.29 joris 330: if (server_currentdir != NULL)
331: xfree(server_currentdir);
1.61 ray 332: server_currentdir = p;
1.29 joris 333:
334: xfree(dir);
335: }
336:
337: void
338: cvs_server_entry(char *data)
339: {
340: CVSENTRIES *entlist;
341:
1.57 ray 342: if (data == NULL)
343: fatal("Missing argument for Entry");
344:
1.29 joris 345: entlist = cvs_ent_open(server_currentdir);
346: cvs_ent_add(entlist, data);
347: cvs_ent_close(entlist, ENT_SYNC);
348: }
349:
350: void
351: cvs_server_modified(char *data)
352: {
1.41 xsa 353: int fd;
1.29 joris 354: size_t flen;
355: mode_t fmode;
356: const char *errstr;
1.51 otto 357: char *mode, *len, fpath[MAXPATHLEN];
1.29 joris 358:
1.57 ray 359: if (data == NULL)
360: fatal("Missing argument for Modified");
361:
1.29 joris 362: mode = cvs_remote_input();
363: len = cvs_remote_input();
364:
365: cvs_strtomode(mode, &fmode);
366: xfree(mode);
367:
368: flen = strtonum(len, 0, INT_MAX, &errstr);
369: if (errstr != NULL)
370: fatal("cvs_server_modified: %s", errstr);
371: xfree(len);
372:
1.54 xsa 373: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
1.29 joris 374:
375: if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1)
376: fatal("cvs_server_modified: %s: %s", fpath, strerror(errno));
377:
1.48 joris 378: cvs_remote_receive_file(fd, flen);
1.29 joris 379:
380: if (fchmod(fd, 0600) == -1)
381: fatal("cvs_server_modified: failed to set file mode");
382:
383: (void)close(fd);
384: }
385:
386: void
387: cvs_server_useunchanged(char *data)
388: {
389: }
390:
391: void
392: cvs_server_unchanged(char *data)
393: {
1.41 xsa 394: int fd;
1.51 otto 395: char fpath[MAXPATHLEN];
1.29 joris 396: CVSENTRIES *entlist;
397: struct cvs_ent *ent;
398: struct timeval tv[2];
399:
1.57 ray 400: if (data == NULL)
401: fatal("Missing argument for Unchanged");
402:
1.54 xsa 403: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
1.29 joris 404:
405: if ((fd = open(fpath, O_RDWR | O_CREAT | O_TRUNC)) == -1)
406: fatal("cvs_server_unchanged: %s: %s", fpath, strerror(errno));
407:
408: entlist = cvs_ent_open(server_currentdir);
409: ent = cvs_ent_get(entlist, data);
410: if (ent == NULL)
411: fatal("received Unchanged request for non-existing file");
412: cvs_ent_close(entlist, ENT_NOSYNC);
413:
1.52 joris 414: tv[0].tv_sec = ent->ce_mtime;
1.29 joris 415: tv[0].tv_usec = 0;
416: tv[1] = tv[0];
417: if (futimes(fd, tv) == -1)
418: fatal("cvs_server_unchanged: failed to set modified time");
419:
420: if (fchmod(fd, 0600) == -1)
421: fatal("cvs_server_unchanged: failed to set mode");
422:
423: cvs_ent_free(ent);
424: (void)close(fd);
425: }
426:
427: void
428: cvs_server_questionable(char *data)
429: {
430: }
431:
432: void
433: cvs_server_argument(char *data)
434: {
1.60 ray 435: if (server_argc >= CVS_CMD_MAXARG)
1.29 joris 436: fatal("cvs_server_argument: too many arguments sent");
1.57 ray 437:
438: if (data == NULL)
439: fatal("Missing argument for Argument");
1.29 joris 440:
441: server_argv[server_argc++] = xstrdup(data);
1.37 xsa 442: }
443:
444: void
445: cvs_server_argumentx(char *data)
446: {
1.44 xsa 447: }
448:
449: void
450: cvs_server_update_patches(char *data)
451: {
452: /*
453: * This does not actually do anything.
454: * It is used to tell that the server is able to
455: * generate patches when given an `update' request.
456: * The client must issue the -u argument to `update'
457: * to receive patches.
458: */
1.29 joris 459: }
460:
461: void
1.31 xsa 462: cvs_server_add(char *data)
463: {
464: if (chdir(server_currentdir) == -1)
465: fatal("cvs_server_add: %s", strerror(errno));
466:
467: cvs_cmdop = CVS_OP_ADD;
468: cvs_add(server_argc, server_argv);
1.50 joris 469: cvs_server_send_response("ok");
470: }
471:
472: void
473: cvs_server_import(char *data)
474: {
475: if (chdir(server_currentdir) == -1)
476: fatal("cvs_server_import: %s", strerror(errno));
477:
478: cvs_cmdop = CVS_OP_IMPORT;
479: cvs_import(server_argc, server_argv);
1.31 xsa 480: cvs_server_send_response("ok");
481: }
1.35 xsa 482:
483: void
484: cvs_server_admin(char *data)
485: {
486: if (chdir(server_currentdir) == -1)
487: fatal("cvs_server_admin: %s", strerror(errno));
488:
489: cvs_cmdop = CVS_OP_ADMIN;
490: cvs_admin(server_argc, server_argv);
491: cvs_server_send_response("ok");
492: }
493:
1.40 xsa 494: void
495: cvs_server_annotate(char *data)
496: {
497: if (chdir(server_currentdir) == -1)
498: fatal("cvs_server_annotate: %s", strerror(errno));
499:
500: cvs_cmdop = CVS_OP_ANNOTATE;
501: cvs_annotate(server_argc, server_argv);
502: cvs_server_send_response("ok");
503: }
1.31 xsa 504:
505: void
1.29 joris 506: cvs_server_commit(char *data)
507: {
508: if (chdir(server_currentdir) == -1)
509: fatal("cvs_server_commit: %s", strerror(errno));
510:
511: cvs_cmdop = CVS_OP_COMMIT;
512: cvs_commit(server_argc, server_argv);
1.49 joris 513: cvs_server_send_response("ok");
514: }
515:
516: void
517: cvs_server_checkout(char *data)
518: { if (chdir(server_currentdir) == -1)
519: fatal("cvs_server_checkout: %s", strerror(errno));
520:
521: cvs_cmdop = CVS_OP_CHECKOUT;
522: cvs_checkout(server_argc, server_argv);
1.29 joris 523: cvs_server_send_response("ok");
524: }
525:
526: void
527: cvs_server_diff(char *data)
528: {
529: if (chdir(server_currentdir) == -1)
530: fatal("cvs_server_diff: %s", strerror(errno));
531:
532: cvs_cmdop = CVS_OP_DIFF;
533: cvs_diff(server_argc, server_argv);
1.34 xsa 534: cvs_server_send_response("ok");
535: }
536:
537: void
538: cvs_server_init(char *data)
539: {
540: if (chdir(server_currentdir) == -1)
541: fatal("cvs_server_init: %s", strerror(errno));
542:
543: cvs_cmdop = CVS_OP_INIT;
544: cvs_init(server_argc, server_argv);
1.31 xsa 545: cvs_server_send_response("ok");
546: }
547:
548: void
549: cvs_server_remove(char *data)
550: {
551: if (chdir(server_currentdir) == -1)
552: fatal("cvs_server_remove: %s", strerror(errno));
553:
554: cvs_cmdop = CVS_OP_REMOVE;
555: cvs_remove(server_argc, server_argv);
1.29 joris 556: cvs_server_send_response("ok");
557: }
558:
559: void
560: cvs_server_status(char *data)
561: {
562: if (chdir(server_currentdir) == -1)
563: fatal("cvs_server_status: %s", strerror(errno));
564:
565: cvs_cmdop = CVS_OP_STATUS;
566: cvs_status(server_argc, server_argv);
567: cvs_server_send_response("ok");
568: }
569:
570: void
571: cvs_server_log(char *data)
572: {
573: if (chdir(server_currentdir) == -1)
574: fatal("cvs_server_log: %s", strerror(errno));
575:
576: cvs_cmdop = CVS_OP_LOG;
1.62 ! niallo 577: cvs_getlog(server_argc, server_argv);
! 578: cvs_server_send_response("ok");
! 579: }
! 580:
! 581: void
! 582: cvs_server_rlog(char *data)
! 583: {
! 584: char fpath[MAXPATHLEN];
! 585: struct cvsroot *cvsroot;
! 586:
! 587: cvsroot = cvsroot_get(NULL);
! 588:
! 589: (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
! 590: cvsroot->cr_dir, server_currentdir);
! 591:
! 592: if (chdir(fpath) == -1)
! 593: fatal("cvs_server_log: %s", strerror(errno));
! 594:
! 595: cvs_cmdop = CVS_OP_RLOG;
1.32 xsa 596: cvs_getlog(server_argc, server_argv);
597: cvs_server_send_response("ok");
598: }
599:
600: void
601: cvs_server_tag(char *data)
602: {
603: if (chdir(server_currentdir) == -1)
604: fatal("cvs_server_tag: %s", strerror(errno));
605:
606: cvs_cmdop = CVS_OP_TAG;
1.33 xsa 607: cvs_tag(server_argc, server_argv);
1.29 joris 608: cvs_server_send_response("ok");
609: }
610:
611: void
612: cvs_server_update(char *data)
613: {
614: if (chdir(server_currentdir) == -1)
615: fatal("cvs_server_update: %s", strerror(errno));
1.14 joris 616:
1.29 joris 617: cvs_cmdop = CVS_OP_UPDATE;
618: cvs_update(server_argc, server_argv);
1.36 xsa 619: cvs_server_send_response("ok");
620: }
621:
622: void
623: cvs_server_version(char *data)
624: {
625: cvs_cmdop = CVS_OP_VERSION;
626: cvs_version(server_argc, server_argv);
1.29 joris 627: cvs_server_send_response("ok");
1.47 joris 628: }
629:
630: void
631: cvs_server_update_entry(const char *resp, struct cvs_file *cf)
632: {
1.58 ray 633: char *p;
1.47 joris 634:
635: if ((p = strrchr(cf->file_rpath, ',')) != NULL)
636: *p = '\0';
637:
1.58 ray 638: cvs_server_send_response("%s %s/", resp, cf->file_wd);
1.47 joris 639: cvs_remote_output(cf->file_rpath);
640:
641: if (p != NULL)
642: *p = ',';
1.1 jfb 643: }