summaryrefslogtreecommitdiff
path: root/usr.bin/cvs/proto.c
diff options
context:
space:
mode:
authorJean-Francois Brousseau <jfb@cvs.openbsd.org>2004-07-29 19:03:34 +0000
committerJean-Francois Brousseau <jfb@cvs.openbsd.org>2004-07-29 19:03:34 +0000
commitd577cee5be162a9c88c9454f4d798c1a1a5a0ef4 (patch)
treeec023dc540f8e942839b7df3cbb89121d9232305 /usr.bin/cvs/proto.c
parentd6fed7993cc63d0709c2d7c782cb753a72b41567 (diff)
New protocol API commented out, not ready yet
Diffstat (limited to 'usr.bin/cvs/proto.c')
-rw-r--r--usr.bin/cvs/proto.c621
1 files changed, 620 insertions, 1 deletions
diff --git a/usr.bin/cvs/proto.c b/usr.bin/cvs/proto.c
index 2584459747a..dc8f3defbd5 100644
--- a/usr.bin/cvs/proto.c
+++ b/usr.bin/cvs/proto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: proto.c,v 1.11 2004/07/29 18:49:08 jfb Exp $ */
+/* $OpenBSD: proto.c,v 1.12 2004/07/29 19:03:33 jfb Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -984,3 +984,622 @@ cvs_recvfile(const char *path)
return (0);
}
+
+
+#ifdef notyet
+/*
+ * cvs_sendreq()
+ *
+ * Send a request to the server of type <rid>, with optional arguments
+ * contained in <arg>, which should not be terminated by a newline.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+int
+cvs_sendreq(struct cvs_root *root, u_int rid, const char *arg)
+{
+ int ret;
+ size_t len;
+ struct cvs_req *reqp;
+
+ if (root->cr_srvin == NULL) {
+ cvs_log(LP_ERR, "cannot send request: Not connected");
+ return (-1);
+ }
+
+ reqp = cvs_req_getbyid(rid);
+ if (reqp == NULL) {
+ cvs_log(LP_ERR, "unsupported request type %u", rid);
+ return (-1);
+ }
+
+ snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n",
+ reqp->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg);
+
+ if (cvs_server_inlog != NULL)
+ fputs(cvs_client_buf, cvs_server_inlog);
+
+ ret = fputs(cvs_client_buf, root->cr_srvin);
+ if (ret == EOF) {
+ cvs_log(LP_ERRNO, "failed to send request to server");
+ return (-1);
+ }
+
+ if (reqp->req_flags & CVS_REQF_RESP)
+ ret = cvs_getresp(root);
+
+ return (0);
+}
+
+
+/*
+ * cvs_getresp()
+ *
+ * Get a response from the server. This call will actually read and handle
+ * responses from the server until one of the response handlers returns
+ * non-zero (either an error occured or the end of the response was reached).
+ * Returns the number of handled commands on success, or -1 on failure.
+ */
+
+int
+cvs_getresp(struct cvs_root *root)
+{
+ int nbcmd;
+
+ nbcmd = 0;
+
+ do {
+ /* wait for incoming data */
+ if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
+ root->cr_srvout) == NULL) {
+ if (feof(root->cr_srvout))
+ return (0);
+ cvs_log(LP_ERRNO,
+ "failed to read response from server");
+ return (-1);
+ }
+
+ if (cvs_server_outlog != NULL)
+ fputs(cvs_client_buf, cvs_server_outlog);
+
+ if ((len = strlen(cvs_client_buf)) != 0) {
+ if (cvs_client_buf[len - 1] != '\n') {
+ /* truncated line */
+ }
+ else
+ cvs_client_buf[--len] = '\0';
+ }
+
+ ret = cvs_resp_handle(cvs_client_buf);
+ nbcmd++;
+ } while (ret == 0);
+
+ if (ret > 0)
+ ret = nbcmd;
+ return (ret);
+}
+
+
+/*
+ * cvs_sendresp()
+ *
+ * Send a response to the client of type <rid>, with optional arguments
+ * contained in <arg>, which should not be terminated by a newline.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+int
+cvs_sendresp(u_int rid, const char *arg)
+{
+ int ret;
+ size_t len;
+ const char *resp;
+
+ resp = cvs_resp_getbyid(rid);
+ if (reqp == NULL) {
+ cvs_log(LP_ERR, "unsupported response type %u", rid);
+ return (-1);
+ }
+
+ snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n", resp,
+ (arg == NULL) ? "" : arg);
+
+ ret = fputs(resp, stdout);
+ if (ret == EOF) {
+ cvs_log(LP_ERRNO, "failed to send response to client");
+ }
+ else {
+ if (arg != NULL)
+ ret = fprintf(stdout, " %s", arg);
+ putc('\n', stdout);
+ }
+ return (0);
+}
+
+
+/*
+ * cvs_getreq()
+ *
+ * Get a request from the client.
+ */
+
+int
+cvs_getreq(void)
+{
+ int nbcmd;
+
+ nbcmd = 0;
+
+ do {
+ /* wait for incoming data */
+ if (fgets(cvs_client_buf, sizeof(cvs_client_buf),
+ stdin) == NULL) {
+ if (feof(stdin))
+ return (0);
+ cvs_log(LP_ERRNO,
+ "failed to read request from client");
+ return (-1);
+ }
+
+ if ((len = strlen(cvs_client_buf)) != 0) {
+ if (cvs_client_buf[len - 1] != '\n') {
+ /* truncated line */
+ }
+ else
+ cvs_client_buf[--len] = '\0';
+ }
+
+ ret = cvs_resp_handle(cvs_client_buf);
+ } while (ret == 0);
+}
+
+
+/*
+ * cvs_connect()
+ *
+ * Open a client connection to the cvs server whose address is given in
+ * the <root> variable. The method used to connect depends on the
+ * setting of the CVS_RSH variable.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+int
+cvs_connect(struct cvsroot *root)
+{
+ int i, argc, infd[2], outfd[2];
+ pid_t pid;
+ char *argv[16], *cvs_server_cmd;
+
+ if (pipe(infd) == -1) {
+ cvs_log(LP_ERRNO,
+ "failed to create input pipe for client connection");
+ return (-1);
+ }
+
+ if (pipe(outfd) == -1) {
+ cvs_log(LP_ERRNO,
+ "failed to create output pipe for client connection");
+ (void)close(infd[0]);
+ (void)close(infd[1]);
+ return (-1);
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ cvs_log(LP_ERRNO, "failed to fork for cvs server connection");
+ return (-1);
+ }
+ if (pid == 0) {
+ if ((dup2(infd[0], STDIN_FILENO) == -1) ||
+ (dup2(outfd[1], STDOUT_FILENO) == -1)) {
+ cvs_log(LP_ERRNO,
+ "failed to setup standard streams for cvs server");
+ return (-1);
+ }
+ (void)close(infd[1]);
+ (void)close(outfd[0]);
+
+ argc = 0;
+ argv[argc++] = cvs_rsh;
+
+ if (root->cr_user != NULL) {
+ argv[argc++] = "-l";
+ argv[argc++] = root->cr_user;
+ }
+
+
+ cvs_server_cmd = getenv("CVS_SERVER");
+ if (cvs_server_cmd == NULL)
+ cvs_server_cmd = "cvs";
+
+ argv[argc++] = root->cr_host;
+ argv[argc++] = cvs_server_cmd;
+ argv[argc++] = "server";
+ argv[argc] = NULL;
+
+ for (i = 0; i < argc; i++)
+ printf("argv[%d] = `%s'\n", i, argv[i]);
+
+ execvp(argv[0], argv);
+ cvs_log(LP_ERRNO, "failed to exec");
+ exit(EX_OSERR);
+ }
+
+ /* we are the parent */
+ cvs_server_infd = infd[1];
+ cvs_server_outfd = outfd[0];
+
+ cvs_server_in = fdopen(cvs_server_infd, "w");
+ if (cvs_server_in == NULL) {
+ cvs_log(LP_ERRNO, "failed to create pipe stream");
+ return (-1);
+ }
+
+ cvs_server_out = fdopen(cvs_server_outfd, "r");
+ if (cvs_server_out == NULL) {
+ cvs_log(LP_ERRNO, "failed to create pipe stream");
+ return (-1);
+ }
+ root->cr_srvin = cvs_server_in;
+ root->cr_srvout = cvs_server_out;
+
+ /* make the streams line-buffered */
+ setvbuf(cvs_server_in, NULL, _IOLBF, 0);
+ setvbuf(cvs_server_out, NULL, _IOLBF, 0);
+
+ (void)close(infd[0]);
+ (void)close(outfd[1]);
+
+ cvs_client_initlog();
+
+ cvs_client_sendinfo();
+
+#ifdef CVS_ZLIB
+ /* if compression was requested, initialize it */
+#endif
+
+ return (0);
+}
+
+
+/*
+ * cvs_disconnect()
+ *
+ * Disconnect from the cvs server.
+ */
+
+void
+cvs_disconnect(struct cvsroot *root)
+{
+ cvs_log(LP_DEBUG, "closing client connection");
+ if (root->cr_srvin != NULL) {
+ (void)fclose(root->cr_srvin);
+ root->cr_srvin = NULL;
+ }
+ if (root->cr_srvout != NULL) {
+ (void)fclose(root->cr_srvout);
+ root->cr_srvout = NULL;
+ }
+
+ if (cvs_server_inlog != NULL)
+ fclose(cvs_server_inlog);
+ if (cvs_server_outlog != NULL)
+ fclose(cvs_server_outlog);
+}
+
+
+/*
+ * cvs_sendln()
+ *
+ * Send a single line <line> string to the server. The line is sent as is,
+ * without any modifications.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+int
+cvs_sendln(struct cvsroot *root, const char *line)
+{
+ int nl;
+ size_t len;
+
+ nl = 0;
+ len = strlen(line);
+
+ if ((len > 0) && (line[len - 1] != '\n'))
+ nl = 1;
+
+ if (cvs_server_inlog != NULL) {
+ fputs(line, cvs_server_inlog);
+ if (nl)
+ fputc('\n', cvs_server_inlog);
+ }
+ fputs(line, root->cr_srvin);
+ if (nl)
+ fputc('\n', root->cr_srvin);
+
+ return (0);
+}
+
+
+/*
+ * cvs_client_sendraw()
+ *
+ * Send the first <len> bytes from the buffer <src> to the server.
+ */
+
+int
+cvs_client_sendraw(const void *src, size_t len)
+{
+ if (cvs_server_inlog != NULL)
+ fwrite(src, sizeof(char), len, cvs_server_inlog);
+ if (fwrite(src, sizeof(char), len, cvs_server_in) < len) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * cvs_client_recvraw()
+ *
+ * Receive the first <len> bytes from the buffer <src> to the server.
+ */
+
+ssize_t
+cvs_client_recvraw(void *dst, size_t len)
+{
+ size_t ret;
+
+ ret = fread(dst, sizeof(char), len, cvs_server_out);
+ if (ret == 0)
+ return (-1);
+ if (cvs_server_outlog != NULL)
+ fwrite(dst, sizeof(char), len, cvs_server_outlog);
+ return (ssize_t)ret;
+}
+
+
+/*
+ * cvs_client_getln()
+ *
+ * Get a line from the server's output and store it in <lbuf>. The terminating
+ * newline character is stripped from the result.
+ */
+
+int
+cvs_client_getln(char *lbuf, size_t len)
+{
+ size_t rlen;
+
+ if (fgets(lbuf, len, cvs_server_out) == NULL) {
+ if (ferror(cvs_server_out)) {
+ cvs_log(LP_ERRNO, "failed to read line from server");
+ return (-1);
+ }
+
+ if (feof(cvs_server_out))
+ *lbuf = '\0';
+ }
+
+ if (cvs_server_outlog != NULL)
+ fputs(lbuf, cvs_server_outlog);
+
+ rlen = strlen(lbuf);
+ if ((rlen > 0) && (lbuf[rlen - 1] == '\n'))
+ lbuf[--rlen] = '\0';
+
+ return (0);
+}
+
+
+/*
+ * cvs_client_sendinfo()
+ *
+ * Initialize the connection status by first requesting the list of
+ * supported requests from the server. Then, we send the CVSROOT variable
+ * with the `Root' request.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+static int
+cvs_client_sendinfo(void)
+{
+ char *vresp;
+ /*
+ * First, send the server the list of valid responses, then ask
+ * for valid requests
+ */
+
+ vresp = cvs_resp_getvalid();
+ if (vresp == NULL) {
+ cvs_log(LP_ERR, "can't generate list of valid responses");
+ return (-1);
+ }
+
+ if (cvs_client_sendreq(CVS_REQ_VALIDRESP, vresp, 0) < 0) {
+ }
+ free(vresp);
+
+ if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) {
+ cvs_log(LP_ERR, "failed to get valid requests from server");
+ return (-1);
+ }
+
+ /* now share our global options with the server */
+ if (verbosity == 1)
+ cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0);
+ else if (verbosity == 0)
+ cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0);
+
+ if (cvs_nolog)
+ cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0);
+ if (cvs_readonly)
+ cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0);
+ if (cvs_trace)
+ cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0);
+
+ /* now send the CVSROOT to the server */
+ if (cvs_client_sendreq(CVS_REQ_ROOT, cvs_root->cr_dir, 0) < 0)
+ return (-1);
+
+ /* not sure why, but we have to send this */
+ if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0)
+ return (-1);
+
+ return (0);
+}
+
+
+/*
+ * cvs_client_senddir()
+ *
+ * Send a `Directory' request along with the 2 paths that follow it.
+ */
+
+int
+cvs_client_senddir(const char *dir)
+{
+ char repo[MAXPATHLEN], buf[MAXPATHLEN];
+
+ if (cvs_readrepo(dir, repo, sizeof(repo)) < 0) {
+ repo[0] = '\0';
+ strlcpy(buf, cvs_root->cr_dir, sizeof(buf));
+ }
+ else {
+ snprintf(buf, sizeof(buf), "%s/%s", cvs_root->cr_dir, repo);
+ }
+
+ if ((cvs_client_sendreq(CVS_REQ_DIRECTORY, dir, 0) < 0) ||
+ (cvs_client_sendln(buf) < 0))
+ return (-1);
+
+ return (0);
+}
+
+
+/*
+ * cvs_client_sendarg()
+ *
+ * Send the argument <arg> to the server. The argument <append> is used to
+ * determine if the argument should be simply appended to the last argument
+ * sent or if it should be created as a new argument (0).
+ */
+
+int
+cvs_client_sendarg(const char *arg, int append)
+{
+ return cvs_client_sendreq(((append == 0) ?
+ CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg, 0);
+}
+
+
+/*
+ * cvs_client_sendentry()
+ *
+ * Send an `Entry' request to the server along with the mandatory fields from
+ * the CVS entry <ent> (which are the name and revision).
+ */
+
+int
+cvs_client_sendentry(const struct cvs_ent *ent)
+{
+ char ebuf[128], numbuf[64];
+
+ snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name,
+ rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf)));
+
+ return cvs_client_sendreq(CVS_REQ_ENTRY, ebuf, 0);
+}
+
+
+/*
+ * cvs_client_initlog()
+ *
+ * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is
+ * set. In this case, the variable's value is used as a path to which the
+ * appropriate suffix is added (".in" for server input and ".out" for server
+ * output.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+static int
+cvs_client_initlog(void)
+{
+ char *env, fpath[MAXPATHLEN];
+
+ env = getenv("CVS_CLIENT_LOG");
+ if (env == NULL)
+ return (0);
+
+ strlcpy(fpath, env, sizeof(fpath));
+ strlcat(fpath, ".in", sizeof(fpath));
+ cvs_server_inlog = fopen(fpath, "w");
+ if (cvs_server_inlog == NULL) {
+ cvs_log(LP_ERRNO, "failed to open server input log `%s'",
+ fpath);
+ return (-1);
+ }
+
+ strlcpy(fpath, env, sizeof(fpath));
+ strlcat(fpath, ".out", sizeof(fpath));
+ cvs_server_outlog = fopen(fpath, "w");
+ if (cvs_server_outlog == NULL) {
+ cvs_log(LP_ERRNO, "failed to open server output log `%s'",
+ fpath);
+ return (-1);
+ }
+
+ /* make the streams line-buffered */
+ setvbuf(cvs_server_inlog, NULL, _IOLBF, 0);
+ setvbuf(cvs_server_outlog, NULL, _IOLBF, 0);
+
+ return (0);
+}
+
+
+/*
+ * cvs_client_sendfile()
+ *
+ */
+
+int
+cvs_client_sendfile(const char *path)
+{
+ int fd;
+ ssize_t ret;
+ char buf[4096];
+ struct stat st;
+
+ if (stat(path, &st) == -1) {
+ cvs_log(LP_ERRNO, "failed to stat `%s'", path);
+ return (-1);
+ }
+
+ fd = open(path, O_RDONLY, 0);
+ if (fd == -1) {
+ return (-1);
+ }
+
+ if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0)
+ return (-1);
+
+ cvs_client_sendln(buf);
+ snprintf(buf, sizeof(buf), "%lld\n", st.st_size);
+ cvs_client_sendln(buf);
+
+ while ((ret = read(fd, buf, sizeof(buf))) != 0) {
+ if (ret == -1) {
+ cvs_log(LP_ERRNO, "failed to read file `%s'", path);
+ return (-1);
+ }
+
+ cvs_client_sendraw(buf, (size_t)ret);
+
+ }
+
+ (void)close(fd);
+
+ return (0);
+}
+#endif