version 1.11, 2004/07/29 18:49:08 |
version 1.12, 2004/07/29 19:03:33 |
|
|
|
|
return (0); |
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 |