version 1.8, 2004/07/31 01:15:26 |
version 1.9, 2006/07/07 17:37:17 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> |
* Copyright (c) 2006 Joris Vink <joris@openbsd.org> |
* All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Permission to use, copy, modify, and distribute this software for any |
* modification, are permitted provided that the following conditions |
* purpose with or without fee is hereby granted, provided that the above |
* are met: |
* copyright notice and this permission notice appear in all copies. |
* |
* |
* 1. Redistributions of source code must retain the above copyright |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* notice, this list of conditions and the following disclaimer. |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* 2. The name of the author may not be used to endorse or promote products |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* derived from this software without specific prior written permission. |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
|
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
*/ |
|
|
#include <sys/types.h> |
#include "includes.h" |
#include <sys/stat.h> |
|
|
|
#include <fcntl.h> |
|
#include <stdio.h> |
|
#include <errno.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <signal.h> |
|
#include <string.h> |
|
#include <sysexits.h> |
|
#ifdef CVS_ZLIB |
|
#include <zlib.h> |
|
#endif |
|
|
|
#include "cvs.h" |
#include "cvs.h" |
#include "log.h" |
#include "log.h" |
#include "proto.h" |
#include "diff.h" |
|
#include "remote.h" |
|
|
|
struct cvs_req cvs_requests[] = { |
|
/* this is what our client will use, the server should support it */ |
|
{ "Root", 1, cvs_server_root, REQ_NEEDED }, |
|
{ "Valid-responses", 1, cvs_server_validresp, REQ_NEEDED }, |
|
{ "valid-requests", 1, cvs_server_validreq, REQ_NEEDED }, |
|
{ "Global_option", 0, cvs_server_globalopt, REQ_NEEDED }, |
|
{ "Directory", 0, cvs_server_directory, REQ_NEEDED }, |
|
{ "Entry", 0, cvs_server_entry, REQ_NEEDED }, |
|
{ "Modified", 0, cvs_server_modified, REQ_NEEDED }, |
|
{ "UseUnchanged", 0, cvs_server_useunchanged, REQ_NEEDED }, |
|
{ "Unchanged", 0, cvs_server_unchanged, REQ_NEEDED }, |
|
{ "Questionable", 0, cvs_server_questionable, REQ_NEEDED }, |
|
{ "Argument", 0, cvs_server_argument, REQ_NEEDED }, |
|
|
|
/* |
|
* used to tell the server what is going on in our |
|
* working copy, unsupported until we are told otherwise |
|
*/ |
|
{ "Max-dotdot", 0, NULL, 0 }, |
|
{ "Static-directory", 0, NULL, 0 }, |
|
{ "Sticky", 0, NULL, 0 }, |
|
{ "Checkin-prog", 0, NULL, 0 }, |
|
{ "Update-prog", 0, NULL, 0 }, |
|
{ "Kopt", 0, NULL, 0 }, |
|
{ "Checkin-time", 0, NULL, 0 }, |
|
{ "Is-modified", 0, NULL, 0 }, |
|
{ "Notify", 0, NULL, 0 }, |
|
{ "Case", 0, NULL, 0 }, |
|
{ "Argumentx", 0, NULL, 0 }, |
|
{ "Gzip-stream", 0, NULL, 0 }, |
|
{ "Kerberos-encrypt", 0, NULL, 0 }, |
|
{ "Gssapi-encrypt", 0, NULL, 0 }, |
|
{ "Gssapi-authenticate", 0, NULL, 0 }, |
|
{ "Set", 0, NULL, 0 }, |
|
{ "expand-modules", 0, NULL, 0 }, |
|
|
extern int verbosity; |
/* commands that might be supported */ |
extern int cvs_compress; |
{ "ci", 0, cvs_server_commit, 0 }, |
extern char *cvs_rsh; |
{ "co", 0, NULL, 0 }, |
extern int cvs_trace; |
{ "diff", 0, cvs_server_diff, 0 }, |
extern int cvs_nolog; |
{ "tag", 0, NULL, 0 }, |
extern int cvs_readonly; |
{ "status", 0, cvs_server_status, 0 }, |
|
{ "admin", 0, NULL, 0 }, |
|
{ "history", 0, NULL, 0 }, |
|
{ "watchers", 0, NULL, 0 }, |
|
{ "editors", 0, NULL, 0 }, |
|
{ "annotate", 0, NULL, 0 }, |
|
{ "log", 0, cvs_server_log, 0 }, |
|
{ "export", 0, NULL, 0 }, |
|
{ "rdiff", 0, NULL, 0 }, |
|
{ "rtag", 0, NULL, 0 }, |
|
{ "init", 0, NULL, 0 }, |
|
{ "update", 0, cvs_server_update, 0 }, |
|
{ "import", 0, NULL, 0 }, |
|
{ "add", 0, NULL, 0 }, |
|
{ "remove", 0, NULL, 0 }, |
|
{ "watch-on", 0, NULL, 0 }, |
|
{ "watch-off", 0, NULL, 0 }, |
|
{ "watch-add", 0, NULL, 0 }, |
|
{ "watch-remove", 0, NULL, 0 }, |
|
{ "release", 0, NULL, 0 }, |
|
{ "noop", 0, NULL, 0 }, |
|
{ "update-patches", 0, NULL, 0 }, |
|
{ "gzip-file-contents", 0, NULL, 0 }, |
|
{ "wrapper-sendme-rcsOptions", 0, NULL, 0 }, |
|
{ "", -1, NULL, 0 } |
|
}; |
|
|
|
static char *client_get_supported_responses(void); |
|
static char *lastdir = NULL; |
|
static int end_of_response = 0; |
|
|
static int cvs_client_sendinfo (struct cvsroot *); |
static char * |
static int cvs_client_initlog (void); |
client_get_supported_responses(void) |
|
{ |
|
BUF *bp; |
|
char *d; |
|
int i, first; |
|
|
|
first = 0; |
|
bp = cvs_buf_alloc(512, BUF_AUTOEXT); |
|
for (i = 0; cvs_responses[i].supported != -1; i++) { |
|
if (cvs_responses[i].hdlr == NULL) |
|
continue; |
|
|
|
if (first != 0) |
|
cvs_buf_append(bp, " ", 1); |
|
else |
|
first++; |
|
cvs_buf_append(bp, cvs_responses[i].name, |
|
strlen(cvs_responses[i].name)); |
|
} |
|
|
static int cvs_server_infd = -1; |
cvs_buf_putc(bp, '\0'); |
static int cvs_server_outfd = -1; |
d = cvs_buf_release(bp); |
static FILE *cvs_server_in; |
return (d); |
static FILE *cvs_server_out; |
} |
|
|
/* protocol log files, if the CVS_CLIENT_LOG environment variable is used */ |
void |
static FILE *cvs_server_inlog; |
cvs_client_connect_to_server(void) |
static FILE *cvs_server_outlog; |
|
|
|
static char cvs_client_buf[4096]; |
|
|
|
|
|
|
|
/* |
|
* cvs_client_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_client_connect(struct cvsroot *root) |
|
{ |
{ |
int argc, infd[2], outfd[2]; |
char *cmd, *argv[9], *resp; |
pid_t pid; |
int ifd[2], ofd[2], argc; |
char *argv[16], *cvs_server_cmd; |
|
|
|
if (pipe(infd) == -1) { |
switch (current_cvsroot->cr_method) { |
cvs_log(LP_ERRNO, |
case CVS_METHOD_PSERVER: |
"failed to create input pipe for client connection"); |
case CVS_METHOD_KSERVER: |
return (-1); |
case CVS_METHOD_GSERVER: |
|
case CVS_METHOD_FORK: |
|
case CVS_METHOD_EXT: |
|
fatal("the specified connection method is not support"); |
|
default: |
|
break; |
} |
} |
|
|
if (pipe(outfd) == -1) { |
if (pipe(ifd) == -1) |
cvs_log(LP_ERRNO, |
fatal("cvs_client_connect: %s", strerror(errno)); |
"failed to create output pipe for client connection"); |
if (pipe(ofd) == -1) |
(void)close(infd[0]); |
fatal("cvs_client_connect: %s", strerror(errno)); |
(void)close(infd[1]); |
|
return (-1); |
|
} |
|
|
|
pid = fork(); |
switch (fork()) { |
if (pid == -1) { |
case -1: |
cvs_log(LP_ERRNO, "failed to fork for cvs server connection"); |
fatal("cvs_client_connect: fork failed: %s", strerror(errno)); |
return (-1); |
case 0: |
} |
if (dup2(ifd[0], STDIN_FILENO) == -1) |
if (pid == 0) { |
fatal("cvs_client_connect: %s", strerror(errno)); |
if ((dup2(infd[0], STDIN_FILENO) == -1) || |
if (dup2(ofd[1], STDOUT_FILENO) == -1) |
(dup2(outfd[1], STDOUT_FILENO) == -1)) { |
fatal("cvs_client_connect: %s", strerror(errno)); |
cvs_log(LP_ERRNO, |
|
"failed to setup standard streams for cvs server"); |
|
return (-1); |
|
} |
|
(void)close(infd[1]); |
|
(void)close(outfd[0]); |
|
|
|
|
close(ifd[1]); |
|
close(ofd[0]); |
|
|
|
if ((cmd = getenv("CVS_SERVER")) == NULL) |
|
cmd = CVS_SERVER_DEFAULT; |
|
|
argc = 0; |
argc = 0; |
argv[argc++] = cvs_rsh; |
argv[argc++] = cvs_rsh; |
|
|
if (root->cr_user != NULL) { |
if (current_cvsroot->cr_user != NULL) { |
argv[argc++] = "-l"; |
argv[argc++] = "-l"; |
argv[argc++] = root->cr_user; |
argv[argc++] = current_cvsroot->cr_user; |
} |
} |
|
|
|
argv[argc++] = current_cvsroot->cr_host; |
cvs_server_cmd = getenv("CVS_SERVER"); |
argv[argc++] = cmd; |
if (cvs_server_cmd == NULL) |
|
cvs_server_cmd = "cvs"; |
|
|
|
argv[argc++] = root->cr_host; |
|
argv[argc++] = cvs_server_cmd; |
|
argv[argc++] = "server"; |
argv[argc++] = "server"; |
argv[argc] = NULL; |
argv[argc] = NULL; |
|
|
|
cvs_log(LP_TRACE, "connecting to server %s", |
|
current_cvsroot->cr_host); |
|
|
execvp(argv[0], argv); |
execvp(argv[0], argv); |
cvs_log(LP_ERRNO, "failed to exec"); |
fatal("cvs_client_connect: failed to execute cvs server"); |
exit(EX_OSERR); |
default: |
|
break; |
} |
} |
|
|
/* we are the parent */ |
close(ifd[0]); |
cvs_server_infd = infd[1]; |
close(ofd[1]); |
cvs_server_outfd = outfd[0]; |
|
|
|
cvs_server_in = fdopen(cvs_server_infd, "w"); |
if ((current_cvsroot->cr_srvin = fdopen(ifd[1], "w")) == NULL) |
if (cvs_server_in == NULL) { |
fatal("cvs_client_connect: %s", strerror(errno)); |
cvs_log(LP_ERRNO, "failed to create pipe stream"); |
if ((current_cvsroot->cr_srvout = fdopen(ofd[0], "r")) == NULL) |
return (-1); |
fatal("cvs_client_connect: %s", strerror(errno)); |
} |
|
|
|
cvs_server_out = fdopen(cvs_server_outfd, "r"); |
setvbuf(current_cvsroot->cr_srvin, NULL,_IOLBF, 0); |
if (cvs_server_out == NULL) { |
setvbuf(current_cvsroot->cr_srvout, NULL, _IOLBF, 0); |
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 */ |
cvs_client_send_request("Root %s", current_cvsroot->cr_dir); |
setvbuf(cvs_server_in, NULL, _IOLBF, 0); |
|
setvbuf(cvs_server_out, NULL, _IOLBF, 0); |
|
|
|
(void)close(infd[0]); |
resp = client_get_supported_responses(); |
(void)close(outfd[1]); |
cvs_client_send_request("Valid-responses %s", resp); |
|
xfree(resp); |
|
|
cvs_client_initlog(); |
cvs_client_send_request("valid-requests"); |
|
cvs_client_get_responses(); |
|
|
cvs_client_sendinfo(root); |
if (cvs_trace) |
|
cvs_client_send_request("Global_option -t"); |
|
|
#ifdef CVS_ZLIB |
if (verbosity == 2) |
/* if compression was requested, initialize it */ |
cvs_client_send_request("Global_option -V"); |
#endif |
|
|
|
return (0); |
cvs_client_send_request("UseUnchanged"); |
} |
} |
|
|
|
|
/* |
|
* cvs_client_disconnect() |
|
* |
|
* Disconnect from the cvs server. |
|
*/ |
|
|
|
void |
void |
cvs_client_disconnect(struct cvsroot *root) |
cvs_client_send_request(char *fmt, ...) |
{ |
{ |
cvs_log(LP_DEBUG, "closing client connection"); |
va_list ap; |
(void)fclose(cvs_server_in); |
char *data, *s; |
(void)fclose(cvs_server_out); |
struct cvs_req *req; |
cvs_server_in = NULL; |
|
cvs_server_out = NULL; |
|
cvs_server_infd = -1; |
|
cvs_server_outfd = -1; |
|
|
|
if (cvs_server_inlog != NULL) |
va_start(ap, fmt); |
fclose(cvs_server_inlog); |
vasprintf(&data, fmt, ap); |
if (cvs_server_outlog != NULL) |
va_end(ap); |
fclose(cvs_server_outlog); |
|
} |
|
|
|
|
if ((s = strchr(data, ' ')) != NULL) |
|
*s = '\0'; |
|
|
/* |
req = cvs_remote_get_request_info(data); |
* cvs_client_sendln() |
if (req == NULL) |
* |
fatal("'%s' is an unknown request", data); |
* 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 |
if (req->supported != 1) |
cvs_client_sendln(const char *line) |
fatal("remote cvs server does not support '%s'", data); |
|
|
|
if (s != NULL) |
|
*s = ' '; |
|
|
|
cvs_remote_output(data); |
|
xfree(data); |
|
} |
|
|
|
void |
|
cvs_client_read_response(void) |
{ |
{ |
int nl; |
char *cmd, *data; |
size_t len; |
struct cvs_resp *resp; |
|
|
nl = 0; |
cmd = cvs_remote_input(); |
len = strlen(line); |
if ((data = strchr(cmd, ' ')) != NULL) |
|
(*data++) = '\0'; |
|
|
if ((len > 0) && (line[len - 1] != '\n')) |
resp = cvs_remote_get_response_info(cmd); |
nl = 1; |
if (resp == NULL) |
|
fatal("response '%s' is not supported by our client", cmd); |
|
|
if (cvs_server_inlog != NULL) { |
if (resp->hdlr == NULL) |
fputs(line, cvs_server_inlog); |
fatal("opencvs client does not support '%s'", cmd); |
if (nl) |
|
fputc('\n', cvs_server_inlog); |
|
} |
|
fputs(line, cvs_server_in); |
|
if (nl) |
|
fputc('\n', cvs_server_in); |
|
|
|
return (0); |
(*resp->hdlr)(data); |
|
xfree(cmd); |
} |
} |
|
|
|
void |
|
cvs_client_get_responses(void) |
|
{ |
|
while (end_of_response != 1) |
|
cvs_client_read_response(); |
|
|
/* |
end_of_response = 0; |
* cvs_client_sendraw() |
} |
* |
|
* Send the first <len> bytes from the buffer <src> to the server. |
|
*/ |
|
|
|
int |
void |
cvs_client_sendraw(const void *src, size_t len) |
cvs_client_senddir(const char *dir) |
{ |
{ |
if (cvs_server_inlog != NULL) |
char *repo; |
fwrite(src, sizeof(char), len, cvs_server_inlog); |
|
if (fwrite(src, sizeof(char), len, cvs_server_in) < len) { |
|
return (-1); |
|
} |
|
|
|
return (0); |
cvs_log(LP_TRACE, "cvs_client_senddir(%s)", dir); |
} |
|
|
|
|
if (lastdir != NULL && !strcmp(dir, lastdir)) |
|
return; |
|
|
/* |
cvs_client_send_request("Directory %s", dir); |
* cvs_client_recvraw() |
|
* |
|
* Receive the first <len> bytes from the buffer <src> to the server. |
|
*/ |
|
|
|
ssize_t |
repo = xmalloc(MAXPATHLEN); |
cvs_client_recvraw(void *dst, size_t len) |
cvs_get_repository_path(dir, repo, MAXPATHLEN); |
{ |
cvs_remote_output(repo); |
size_t ret; |
xfree(repo); |
|
|
ret = fread(dst, sizeof(char), len, cvs_server_out); |
if (lastdir != NULL) |
if (ret == 0) |
xfree(lastdir); |
return (-1); |
lastdir = xstrdup(dir); |
if (cvs_server_outlog != NULL) |
|
fwrite(dst, sizeof(char), len, cvs_server_outlog); |
|
return (ssize_t)ret; |
|
} |
} |
|
|
|
void |
|
cvs_client_sendfile(struct cvs_file *cf) |
|
{ |
|
int l; |
|
size_t len; |
|
char rev[16], timebuf[64], sticky[32]; |
|
|
/* |
if (cf->file_type != CVS_FILE) |
* cvs_client_getln() |
return; |
* |
|
* 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_senddir(cf->file_wd); |
cvs_client_getln(char *lbuf, size_t len) |
|
{ |
|
size_t rlen; |
|
|
|
if (fgets(lbuf, len, cvs_server_out) == NULL) { |
cvs_log(LP_TRACE, "cvs_client_sendfile(%s)", cf->file_path); |
if (ferror(cvs_server_out)) { |
cvs_remote_classify_file(cf); |
cvs_log(LP_ERRNO, "failed to read line from server"); |
|
return (-1); |
if (cf->file_ent != NULL) { |
|
if (cf->file_status == FILE_ADDED) { |
|
len = strlcpy(rev, "0", sizeof(rev)); |
|
if (len >= sizeof(rev)) |
|
fatal("cvs_client_sendfile: truncation"); |
|
|
|
len = strlcpy(timebuf, "Initial ", sizeof(timebuf)); |
|
if (len >= sizeof(timebuf)) |
|
fatal("cvs_client_sendfile: truncation"); |
|
|
|
len = strlcat(timebuf, cf->file_name, sizeof(timebuf)); |
|
if (len >= sizeof(timebuf)) |
|
fatal("cvs_client_sendfile: truncation"); |
|
} else { |
|
rcsnum_tostr(cf->file_ent->ce_rev, rev, sizeof(rev)); |
|
ctime_r(&cf->file_ent->ce_mtime, timebuf); |
} |
} |
|
|
if (feof(cvs_server_out)) |
if (cf->file_ent->ce_conflict == NULL) { |
*lbuf = '\0'; |
if (timebuf[strlen(timebuf) - 1] == '\n') |
|
timebuf[strlen(timebuf) - 1] = '\0'; |
|
} else { |
|
len = strlcpy(timebuf, cf->file_ent->ce_conflict, |
|
sizeof(timebuf)); |
|
if (len >= sizeof(timebuf)) |
|
fatal("cvs_client_sendfile: truncation"); |
|
} |
|
|
|
sticky[0] = '\0'; |
|
if (cf->file_ent->ce_tag != NULL) { |
|
l = snprintf(sticky, sizeof(sticky), "T%s", |
|
cf->file_ent->ce_tag); |
|
if (l == -1 || l >= (int)sizeof(sticky)) |
|
fatal("cvs_client_sendfile: overflow"); |
|
} |
|
|
|
cvs_client_send_request("Entry /%s/%s%s/%s/%s/", |
|
cf->file_name, (cf->file_status == FILE_REMOVED) ? "-" : "", |
|
rev, timebuf, sticky); |
} |
} |
|
|
if (cvs_server_outlog != NULL) |
switch (cf->file_status) { |
fputs(lbuf, cvs_server_outlog); |
case FILE_UNKNOWN: |
|
if (cf->fd != -1) |
|
cvs_client_send_request("Questionable %s", |
|
cf->file_name); |
|
break; |
|
case FILE_ADDED: |
|
case FILE_MODIFIED: |
|
cvs_client_send_request("Modified %s", cf->file_name); |
|
cvs_remote_send_file(cf->file_path); |
|
break; |
|
case FILE_UPTODATE: |
|
cvs_client_send_request("Unchanged %s", cf->file_name); |
|
break; |
|
} |
|
} |
|
|
rlen = strlen(lbuf); |
void |
if ((rlen > 0) && (lbuf[rlen - 1] == '\n')) |
cvs_client_send_files(char **argv, int argc) |
lbuf[--rlen] = '\0'; |
{ |
|
int i; |
|
|
return (0); |
for (i = 0; i < argc; i++) |
|
cvs_client_send_request("Argument %s", argv[i]); |
} |
} |
|
|
|
void |
|
cvs_client_ok(char *data) |
|
{ |
|
cvs_log(LP_TRACE, "cvs_client_ok()"); |
|
end_of_response = 1; |
|
} |
|
|
/* |
void |
* cvs_client_sendinfo() |
cvs_client_error(char *data) |
* |
{ |
* Initialize the connection status by first requesting the list of |
cvs_log(LP_TRACE, "cvs_client_error()"); |
* supported requests from the server. Then, we send the CVSROOT variable |
end_of_response = 1; |
* with the `Root' request. |
} |
* Returns 0 on success, or -1 on failure. |
|
*/ |
|
|
|
static int |
void |
cvs_client_sendinfo(struct cvsroot *root) |
cvs_client_validreq(char *data) |
{ |
{ |
char *vresp; |
int i; |
/* |
char *sp, *ep; |
* First, send the server the list of valid responses, then ask |
struct cvs_req *req; |
* for valid requests |
|
*/ |
|
|
|
vresp = cvs_resp_getvalid(); |
sp = data; |
if (vresp == NULL) { |
do { |
cvs_log(LP_ERR, "can't generate list of valid responses"); |
if ((ep = strchr(sp, ' ')) != NULL) |
return (-1); |
*ep = '\0'; |
} |
|
|
|
if (cvs_client_sendreq(CVS_REQ_VALIDRESP, vresp, 0) < 0) { |
req = cvs_remote_get_request_info(sp); |
} |
if (req != NULL) |
free(vresp); |
req->supported = 1; |
|
|
if (cvs_client_sendreq(CVS_REQ_VALIDREQ, NULL, 1) < 0) { |
if (ep != NULL) |
cvs_log(LP_ERR, "failed to get valid requests from server"); |
sp = ep + 1; |
return (-1); |
} while (ep != NULL); |
|
|
|
for (i = 0; cvs_requests[i].supported != -1; i++) { |
|
req = &cvs_requests[i]; |
|
if ((req->flags & REQ_NEEDED) && |
|
req->supported != 1) { |
|
fatal("server does not support required '%s'", |
|
req->name); |
|
} |
} |
} |
|
} |
|
|
/* not sure why, but we have to send this */ |
void |
if (cvs_client_sendreq(CVS_REQ_USEUNCHANGED, NULL, 0) < 0) |
cvs_client_e(char *data) |
return (-1); |
{ |
|
cvs_printf("%s\n", data); |
|
} |
|
|
/* now share our global options with the server */ |
void |
if (verbosity == 1) |
cvs_client_m(char *data) |
cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-q", 0); |
{ |
else if (verbosity == 0) |
cvs_printf("%s\n", data); |
cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-Q", 0); |
} |
|
|
if (cvs_nolog) |
void |
cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-l", 0); |
cvs_client_checkedin(char *data) |
if (cvs_readonly) |
{ |
cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-r", 0); |
int l; |
if (cvs_trace) |
CVSENTRIES *entlist; |
cvs_client_sendreq(CVS_REQ_GLOBALOPT, "-t", 0); |
struct cvs_ent *ent, *newent; |
|
char *dir, *entry, rev[16], timebuf[64], sticky[16]; |
|
|
/* now send the CVSROOT to the server */ |
cvs_log(LP_TRACE, "cvs_client_checkedin(%s)", data); |
if (cvs_client_sendreq(CVS_REQ_ROOT, root->cr_dir, 0) < 0) |
|
return (-1); |
|
|
|
return (0); |
dir = cvs_remote_input(); |
} |
entry = cvs_remote_input(); |
|
xfree(dir); |
|
|
|
entlist = cvs_ent_open(data); |
|
newent = cvs_ent_parse(entry); |
|
ent = cvs_ent_get(entlist, newent->ce_name); |
|
xfree(entry); |
|
|
/* |
entry = xmalloc(CVS_ENT_MAXLINELEN); |
* cvs_client_senddir() |
|
* |
|
* Send a `Directory' request along with the 2 paths that follow it. |
|
*/ |
|
|
|
int |
rcsnum_tostr(newent->ce_rev, rev, sizeof(rev)); |
cvs_client_senddir(const char *dir) |
ctime_r(&ent->ce_mtime, timebuf); |
{ |
if (timebuf[strlen(timebuf) - 1] == '\n') |
char repo[MAXPATHLEN], buf[MAXPATHLEN]; |
timebuf[strlen(timebuf) - 1] = '\0'; |
|
|
if (cvs_readrepo(dir, repo, sizeof(repo)) < 0) { |
sticky[0] = '\0'; |
repo[0] = '\0'; |
if (ent->ce_tag != NULL) { |
strlcpy(buf, cvs_root->cr_dir, sizeof(buf)); |
l = snprintf(sticky, sizeof(sticky), "T%s", ent->ce_tag); |
|
if (l == -1 || l >= (int)sizeof(sticky)) |
|
fatal("cvs_client_checkedin: overflow"); |
} |
} |
else { |
|
snprintf(buf, sizeof(buf), "%s/%s", cvs_root->cr_dir, repo); |
|
} |
|
|
|
if ((cvs_client_sendreq(CVS_REQ_DIRECTORY, dir, 0) < 0) || |
l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//%s/", |
(cvs_client_sendln(buf) < 0)) |
newent->ce_name, rev, timebuf, sticky); |
return (-1); |
if (l == -1 || l >= CVS_ENT_MAXLINELEN) |
|
fatal("cvs_client_checkedin: overflow"); |
|
|
return (0); |
cvs_ent_free(ent); |
|
cvs_ent_free(newent); |
|
cvs_ent_add(entlist, entry); |
|
cvs_ent_close(entlist, ENT_SYNC); |
|
|
|
xfree(entry); |
} |
} |
|
|
|
void |
|
cvs_client_updated(char *data) |
|
{ |
|
BUF *bp; |
|
int l, fd; |
|
time_t now; |
|
mode_t fmode; |
|
size_t flen; |
|
CVSENTRIES *ent; |
|
struct cvs_ent *e; |
|
const char *errstr; |
|
struct timeval tv[2]; |
|
char timebuf[32], *repo, *rpath, *entry, *mode; |
|
char revbuf[32], *len, *fpath, *wdir; |
|
|
/* |
cvs_log(LP_TRACE, "cvs_client_updated(%s)", data); |
* 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 |
rpath = cvs_remote_input(); |
cvs_client_sendarg(const char *arg, int append) |
entry = cvs_remote_input(); |
{ |
mode = cvs_remote_input(); |
return cvs_client_sendreq(((append == 0) ? |
len = cvs_remote_input(); |
CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg, 0); |
|
} |
|
|
|
|
repo = xmalloc(MAXPATHLEN); |
|
cvs_get_repository_path(".", repo, MAXPATHLEN); |
|
|
/* |
if (strlen(repo) + 1 > strlen(rpath)) |
* cvs_client_sendentry() |
fatal("received a repository path that is too short"); |
* |
|
* Send an `Entry' request to the server along with the mandatory fields from |
|
* the CVS entry <ent> (which are the name and revision). |
|
*/ |
|
|
|
int |
fpath = rpath + strlen(repo) + 1; |
cvs_client_sendentry(const struct cvs_ent *ent) |
if ((wdir = dirname(fpath)) == NULL) |
{ |
fatal("cvs_client_updated: dirname: %s", strerror(errno)); |
char ebuf[128], numbuf[64]; |
xfree(repo); |
|
|
snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name, |
flen = strtonum(len, 0, INT_MAX, &errstr); |
rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf))); |
if (errstr != NULL) |
|
fatal("cvs_client_updated: %s: %s", len, errstr); |
|
xfree(len); |
|
|
return cvs_client_sendreq(CVS_REQ_ENTRY, ebuf, 0); |
cvs_strtomode(mode, &fmode); |
} |
xfree(mode); |
|
|
|
time(&now); |
|
now = cvs_hack_time(now, 0); |
|
ctime_r(&now, timebuf); |
|
if (timebuf[strlen(timebuf) - 1] == '\n') |
|
timebuf[strlen(timebuf) - 1] = '\0'; |
|
|
/* |
e = cvs_ent_parse(entry); |
* cvs_client_initlog() |
xfree(entry); |
* |
rcsnum_tostr(e->ce_rev, revbuf, sizeof(revbuf)); |
* Initialize protocol logging if the CVS_CLIENT_LOG environment variable is |
entry = xmalloc(CVS_ENT_MAXLINELEN); |
* set. In this case, the variable's value is used as a path to which the |
l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//", e->ce_name, |
* appropriate suffix is added (".in" for server input and ".out" for server |
revbuf, timebuf); |
* output. |
if (l == -1 || l >= CVS_ENT_MAXLINELEN) |
* Returns 0 on success, or -1 on failure. |
fatal("cvs_client_updated: overflow"); |
*/ |
|
|
|
static int |
cvs_ent_free(e); |
cvs_client_initlog(void) |
ent = cvs_ent_open(wdir); |
{ |
cvs_ent_add(ent, entry); |
char *env, fpath[MAXPATHLEN]; |
cvs_ent_close(ent, ENT_SYNC); |
|
xfree(entry); |
|
|
env = getenv("CVS_CLIENT_LOG"); |
bp = cvs_remote_receive_file(flen); |
if (env == NULL) |
if ((fd = open(fpath, O_CREAT | O_WRONLY | O_TRUNC)) == -1) |
return (0); |
fatal("cvs_client_updated: open: %s: %s", |
|
fpath, strerror(errno)); |
|
|
strlcpy(fpath, env, sizeof(fpath)); |
if (cvs_buf_write_fd(bp, fd) == -1) |
strlcat(fpath, ".in", sizeof(fpath)); |
fatal("cvs_client_updated: cvs_buf_write_fd failed for %s", |
cvs_server_inlog = fopen(fpath, "w"); |
|
if (cvs_server_inlog == NULL) { |
|
cvs_log(LP_ERRNO, "failed to open server input log `%s'", |
|
fpath); |
fpath); |
return (-1); |
|
} |
|
|
|
strlcpy(fpath, env, sizeof(fpath)); |
cvs_buf_free(bp); |
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 */ |
now = cvs_hack_time(now, 0); |
setvbuf(cvs_server_inlog, NULL, _IOLBF, 0); |
tv[0].tv_sec = now; |
setvbuf(cvs_server_outlog, NULL, _IOLBF, 0); |
tv[0].tv_usec = 0; |
|
tv[1] = tv[0]; |
|
|
return (0); |
if (futimes(fd, tv) == -1) |
|
fatal("cvs_client_updated: futimes: %s", strerror(errno)); |
|
|
|
if (fchmod(fd, fmode) == -1) |
|
fatal("cvs_client_updated: fchmod: %s", strerror(errno)); |
|
|
|
(void)close(fd); |
|
|
|
xfree(rpath); |
|
} |
|
|
|
void |
|
cvs_client_merged(char *data) |
|
{ |
|
cvs_log(LP_TRACE, "cvs_client_merged(%s)", data); |
|
} |
|
|
|
void |
|
cvs_client_removed(char *data) |
|
{ |
|
cvs_log(LP_TRACE, "cvs_client_removed(%s)", data); |
|
} |
|
|
|
void |
|
cvs_client_remove_entry(char *data) |
|
{ |
|
char *dir; |
|
|
|
cvs_log(LP_TRACE, "cvs_client_remove_entry(%s)", data); |
|
|
|
dir = cvs_remote_input(); |
|
xfree(dir); |
} |
} |