summaryrefslogtreecommitdiff
path: root/usr.bin/cvs/server.c
diff options
context:
space:
mode:
authorJoris Vink <joris@cvs.openbsd.org>2006-07-07 17:37:18 +0000
committerJoris Vink <joris@cvs.openbsd.org>2006-07-07 17:37:18 +0000
commit3b8d87766176c8c22119cca9b60143e0b8b842d3 (patch)
treea75735ef3dd068f9476605fc1b104c93f293d539 /usr.bin/cvs/server.c
parent0bc2e30232a85b2aea92a0a6a147bb29e7f9088e (diff)
first part of opencvs remote, fairly useable on existing trees
although i advise against using it on real development trees for now. only a few commands work right so far: - commit - diff - status - log - update (partially working) if you feel like testing remote and run into bugs feel free to contact me, and please include a full trace (-t).
Diffstat (limited to 'usr.bin/cvs/server.c')
-rw-r--r--usr.bin/cvs/server.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/usr.bin/cvs/server.c b/usr.bin/cvs/server.c
new file mode 100644
index 00000000000..b4ce702a00e
--- /dev/null
+++ b/usr.bin/cvs/server.c
@@ -0,0 +1,455 @@
+/* $OpenBSD: server.c,v 1.29 2006/07/07 17:37:17 joris Exp $ */
+/*
+ * Copyright (c) 2006 Joris Vink <joris@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+
+#include "cvs.h"
+#include "log.h"
+#include "diff.h"
+#include "remote.h"
+
+struct cvs_resp cvs_responses[] = {
+ /* this is what our server uses, the client should support it */
+ { "Valid-requests", 1, cvs_client_validreq, RESP_NEEDED },
+ { "ok", 0, cvs_client_ok, RESP_NEEDED},
+ { "error", 0, cvs_client_error, RESP_NEEDED },
+ { "E", 0, cvs_client_e, RESP_NEEDED },
+ { "M", 0, cvs_client_m, RESP_NEEDED },
+ { "Checked-in", 0, cvs_client_checkedin, RESP_NEEDED },
+ { "Updated", 0, cvs_client_updated, RESP_NEEDED },
+ { "Merged", 0, cvs_client_merged, RESP_NEEDED },
+ { "Removed", 0, cvs_client_removed, RESP_NEEDED },
+ { "Remove-entry", 0, cvs_client_remove_entry, RESP_NEEDED },
+
+ /* unsupported responses until told otherwise */
+ { "New-entry", 0, NULL, 0 },
+ { "Created", 0, NULL, 0 },
+ { "Update-existing", 0, NULL, 0 },
+ { "Rcs-diff", 0, NULL, 0 },
+ { "Patched", 0, NULL, 0 },
+ { "Mode", 0, NULL, 0 },
+ { "Mod-time", 0, NULL, 0 },
+ { "Checksum", 0, NULL, 0 },
+ { "Copy-file", 0, NULL, 0 },
+ { "Set-static-directory", 0, NULL, 0 },
+ { "Clear-static-directory", 0, NULL, 0 },
+ { "Set-sticky", 0, NULL, 0 },
+ { "Clear-sticky", 0, NULL, 0 },
+ { "Template", 0, NULL, 0 },
+ { "Set-checkin-prog", 0, NULL, 0 },
+ { "Set-update-prog", 0, NULL, 0 },
+ { "Notified", 0, NULL, 0 },
+ { "Module-expansion", 0, NULL, 0 },
+ { "Wrapper-rcsOption", 0, NULL, 0 },
+ { "Mbinary", 0, NULL, 0 },
+ { "F", 0, NULL, 0 },
+ { "MT", 0, NULL, 0 },
+ { "", -1, NULL, 0 }
+};
+
+int cvs_server(int, char **);
+char *cvs_server_path = NULL;
+
+static char *server_currentdir = NULL;
+static char *server_argv[CVS_CMD_MAXARG];
+static int server_argc = 1;
+
+struct cvs_cmd cvs_cmd_server = {
+ CVS_OP_SERVER, 0, "server", { "", "" },
+ "server mode",
+ NULL,
+ NULL,
+ NULL,
+ cvs_server
+};
+
+
+int
+cvs_server(int argc, char **argv)
+{
+ int l;
+ char *cmd, *data;
+ struct cvs_req *req;
+
+ server_argv[0] = xstrdup("server");
+
+ cvs_server_path = xmalloc(MAXPATHLEN);
+ l = snprintf(cvs_server_path, MAXPATHLEN, "%s/cvs-serv%d",
+ cvs_tmpdir, getpid());
+ if (l == -1 || l >= MAXPATHLEN)
+ fatal("cvs_server: overflow in server path");
+
+ if (mkdir(cvs_server_path, 0700) == -1)
+ fatal("failed to create temporary server directory: %s, %s",
+ cvs_server_path, strerror(errno));
+
+ if (chdir(cvs_server_path) == -1)
+ fatal("failed to change directory to '%s'", cvs_server_path);
+
+ for (;;) {
+ cmd = cvs_remote_input();
+
+ if ((data = strchr(cmd, ' ')) != NULL)
+ (*data++) = '\0';
+
+ req = cvs_remote_get_request_info(cmd);
+ if (req == NULL)
+ fatal("request '%s' is not supported by our server",
+ cmd);
+
+ if (req->hdlr == NULL)
+ fatal("opencvs server does not support '%s'", cmd);
+
+ (*req->hdlr)(data);
+ xfree(cmd);
+ }
+
+ return (0);
+}
+
+void
+cvs_server_send_response(char *fmt, ...)
+{
+ va_list ap;
+ char *data, *s;
+ struct cvs_resp *resp;
+
+ va_start(ap, fmt);
+ vasprintf(&data, fmt, ap);
+ va_end(ap);
+
+ if ((s = strchr(data, ' ')) != NULL)
+ *s = '\0';
+
+ resp = cvs_remote_get_response_info(data);
+ if (resp == NULL)
+ fatal("'%s' is an unknown response", data);
+
+ if (resp->supported != 1)
+ fatal("remote cvs client does not support '%s'", data);
+
+ if (s != NULL)
+ *s = ' ';
+
+ cvs_remote_output(data);
+ xfree(data);
+}
+
+void
+cvs_server_root(char *data)
+{
+ fatal("duplicate Root request from client, violates the protocol");
+}
+
+void
+cvs_server_validresp(char *data)
+{
+ int i;
+ char *sp, *ep;
+ struct cvs_resp *resp;
+
+ sp = data;
+ do {
+ if ((ep = strchr(sp, ' ')) != NULL)
+ *ep = '\0';
+
+ resp = cvs_remote_get_response_info(sp);
+ if (resp != NULL)
+ resp->supported = 1;
+
+ if (ep != NULL)
+ sp = ep + 1;
+ } while (ep != NULL);
+
+ for (i = 0; cvs_responses[i].supported != -1; i++) {
+ resp = &cvs_responses[i];
+ if ((resp->flags & RESP_NEEDED) &&
+ resp->supported != 1) {
+ fatal("client does not support required '%s'",
+ resp->name);
+ }
+ }
+}
+
+void
+cvs_server_validreq(char *data)
+{
+ BUF *bp;
+ char *d;
+ int i, first;
+
+ first = 0;
+ bp = cvs_buf_alloc(512, BUF_AUTOEXT);
+ for (i = 0; cvs_requests[i].supported != -1; i++) {
+ if (cvs_requests[i].hdlr == NULL)
+ continue;
+
+ if (first != 0)
+ cvs_buf_append(bp, " ", 1);
+ else
+ first++;
+
+ cvs_buf_append(bp, cvs_requests[i].name,
+ strlen(cvs_requests[i].name));
+ }
+
+ cvs_buf_putc(bp, '\0');
+ d = cvs_buf_release(bp);
+
+ cvs_server_send_response("Valid-requests %s", d);
+ cvs_server_send_response("ok");
+ xfree(d);
+}
+
+void
+cvs_server_globalopt(char *data)
+{
+ if (!strcmp(data, "-t"))
+ cvs_trace = 1;
+
+ if (!strcmp(data, "-n"))
+ cvs_noexec = 1;
+
+ if (!strcmp(data, "-V"))
+ verbosity = 2;
+
+ cvs_log(LP_TRACE, "cvs_server_globalopt(%s)", data);
+}
+
+void
+cvs_server_directory(char *data)
+{
+ int l;
+ CVSENTRIES *entlist;
+ char *dir, *repo, *parent, *entry, *dirn;
+
+ cvs_log(LP_TRACE, "cvs_server_directory(%s)", data);
+
+ dir = cvs_remote_input();
+ if (strlen(dir) < strlen(current_cvsroot->cr_dir) + 1)
+ fatal("cvs_server_directory: bad Directory request");
+
+ repo = dir + strlen(current_cvsroot->cr_dir) + 1;
+ cvs_mkpath(repo);
+
+ if ((dirn = basename(repo)) == NULL)
+ fatal("cvs_server_directory: %s", strerror(errno));
+
+ if ((parent = dirname(repo)) == NULL)
+ fatal("cvs_server_directory: %s", strerror(errno));
+
+ if (strcmp(parent, ".")) {
+ entlist = cvs_ent_open(parent);
+ entry = xmalloc(CVS_ENT_MAXLINELEN);
+ l = snprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", dirn);
+ if (l == -1 || l >= CVS_ENT_MAXLINELEN)
+ fatal("cvs_server_directory: overflow");
+
+ cvs_ent_add(entlist, entry);
+ cvs_ent_close(entlist, ENT_SYNC);
+ xfree(entry);
+ }
+
+ if (server_currentdir != NULL)
+ xfree(server_currentdir);
+ server_currentdir = xstrdup(repo);
+
+ xfree(dir);
+}
+
+void
+cvs_server_entry(char *data)
+{
+ CVSENTRIES *entlist;
+
+ cvs_log(LP_TRACE, "cvs_server_entry(%s)", data);
+
+ entlist = cvs_ent_open(server_currentdir);
+ cvs_ent_add(entlist, data);
+ cvs_ent_close(entlist, ENT_SYNC);
+}
+
+void
+cvs_server_modified(char *data)
+{
+ BUF *bp;
+ int fd, l;
+ size_t flen;
+ mode_t fmode;
+ const char *errstr;
+ char *mode, *len, *fpath;
+
+ cvs_log(LP_TRACE, "cvs_server_modified(%s)", data);
+
+ mode = cvs_remote_input();
+ len = cvs_remote_input();
+
+ cvs_strtomode(mode, &fmode);
+ xfree(mode);
+
+ flen = strtonum(len, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ fatal("cvs_server_modified: %s", errstr);
+ xfree(len);
+
+ bp = cvs_remote_receive_file(flen);
+
+ fpath = xmalloc(MAXPATHLEN);
+ l = snprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
+ if (l == -1 || l >= MAXPATHLEN)
+ fatal("cvs_server_modified: overflow");
+
+ if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1)
+ fatal("cvs_server_modified: %s: %s", fpath, strerror(errno));
+
+ if (cvs_buf_write_fd(bp, fd) == -1)
+ fatal("cvs_server_modified: failed to write file '%s'", fpath);
+
+ if (fchmod(fd, 0600) == -1)
+ fatal("cvs_server_modified: failed to set file mode");
+
+ xfree(fpath);
+ (void)close(fd);
+ cvs_buf_free(bp);
+}
+
+void
+cvs_server_useunchanged(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_useunchanged()");
+}
+
+void
+cvs_server_unchanged(char *data)
+{
+ int l, fd;
+ char *fpath;
+ CVSENTRIES *entlist;
+ struct cvs_ent *ent;
+ struct timeval tv[2];
+
+ cvs_log(LP_TRACE, "cvs_server_unchanged(%s)", data);
+
+ fpath = xmalloc(MAXPATHLEN);
+ l = snprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
+ if (l == -1 || l >= MAXPATHLEN)
+ fatal("cvs_server_unchanged: overflow");
+
+ if ((fd = open(fpath, O_RDWR | O_CREAT | O_TRUNC)) == -1)
+ fatal("cvs_server_unchanged: %s: %s", fpath, strerror(errno));
+
+ entlist = cvs_ent_open(server_currentdir);
+ ent = cvs_ent_get(entlist, data);
+ if (ent == NULL)
+ fatal("received Unchanged request for non-existing file");
+ cvs_ent_close(entlist, ENT_NOSYNC);
+
+ tv[0].tv_sec = cvs_hack_time(ent->ce_mtime, 0);
+ tv[0].tv_usec = 0;
+ tv[1] = tv[0];
+ if (futimes(fd, tv) == -1)
+ fatal("cvs_server_unchanged: failed to set modified time");
+
+ if (fchmod(fd, 0600) == -1)
+ fatal("cvs_server_unchanged: failed to set mode");
+
+ cvs_ent_free(ent);
+ xfree(fpath);
+ (void)close(fd);
+}
+
+void
+cvs_server_questionable(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_questionable(%s)", data);
+}
+
+void
+cvs_server_argument(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_argument(%s)", data);
+
+ if (server_argc > CVS_CMD_MAXARG)
+ fatal("cvs_server_argument: too many arguments sent");
+
+ server_argv[server_argc++] = xstrdup(data);
+}
+
+void
+cvs_server_commit(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_commit()");
+
+ if (chdir(server_currentdir) == -1)
+ fatal("cvs_server_commit: %s", strerror(errno));
+
+ cvs_cmdop = CVS_OP_COMMIT;
+ cvs_commit(server_argc, server_argv);
+ cvs_server_send_response("ok");
+}
+
+void
+cvs_server_diff(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_diff()");
+
+ if (chdir(server_currentdir) == -1)
+ fatal("cvs_server_diff: %s", strerror(errno));
+
+ cvs_cmdop = CVS_OP_DIFF;
+ cvs_diff(server_argc, server_argv);
+ cvs_server_send_response("ok");
+}
+
+void
+cvs_server_status(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_status()");
+
+ if (chdir(server_currentdir) == -1)
+ fatal("cvs_server_status: %s", strerror(errno));
+
+ cvs_cmdop = CVS_OP_STATUS;
+ cvs_status(server_argc, server_argv);
+ cvs_server_send_response("ok");
+}
+
+void
+cvs_server_log(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_log()");
+
+ if (chdir(server_currentdir) == -1)
+ fatal("cvs_server_log: %s", strerror(errno));
+
+ cvs_cmdop = CVS_OP_LOG;
+ cvs_getlog(server_argc, server_argv);
+ cvs_server_send_response("ok");
+}
+
+void
+cvs_server_update(char *data)
+{
+ cvs_log(LP_TRACE, "cvs_server_update()");
+
+ if (chdir(server_currentdir) == -1)
+ fatal("cvs_server_update: %s", strerror(errno));
+
+ cvs_cmdop = CVS_OP_UPDATE;
+ cvs_update(server_argc, server_argv);
+ cvs_server_send_response("ok");
+}