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