From d577cee5be162a9c88c9454f4d798c1a1a5a0ef4 Mon Sep 17 00:00:00 2001 From: Jean-Francois Brousseau Date: Thu, 29 Jul 2004 19:03:34 +0000 Subject: New protocol API commented out, not ready yet --- usr.bin/cvs/proto.c | 621 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 620 insertions(+), 1 deletion(-) (limited to 'usr.bin/cvs/proto.c') 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 * 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 , with optional arguments + * contained in , 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 , with optional arguments + * contained in , 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 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 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 bytes from the buffer 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 bytes from the buffer 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 . 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 to the server. The argument 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 (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 -- cgit v1.2.3