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