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