version 1.12, 2004/07/29 19:03:33 |
version 1.13, 2004/07/30 01:49:24 |
|
|
#include "buf.h" |
#include "buf.h" |
#include "cvs.h" |
#include "cvs.h" |
#include "log.h" |
#include "log.h" |
|
#include "proto.h" |
|
|
|
|
#define CVS_MTSTK_MAXDEPTH 16 |
#define CVS_MTSTK_MAXDEPTH 16 |
|
|
|
|
|
|
|
|
static int cvs_resp_validreq (int, char *); |
static int cvs_resp_validreq (struct cvsroot *, int, char *); |
static int cvs_resp_cksum (int, char *); |
static int cvs_resp_cksum (struct cvsroot *, int, char *); |
static int cvs_resp_modtime (int, char *); |
static int cvs_resp_modtime (struct cvsroot *, int, char *); |
static int cvs_resp_m (int, char *); |
static int cvs_resp_m (struct cvsroot *, int, char *); |
static int cvs_resp_ok (int, char *); |
static int cvs_resp_ok (struct cvsroot *, int, char *); |
static int cvs_resp_error (int, char *); |
static int cvs_resp_error (struct cvsroot *, int, char *); |
static int cvs_resp_statdir (int, char *); |
static int cvs_resp_statdir (struct cvsroot *, int, char *); |
static int cvs_resp_sticky (int, char *); |
static int cvs_resp_sticky (struct cvsroot *, int, char *); |
static int cvs_resp_newentry (int, char *); |
static int cvs_resp_newentry (struct cvsroot *, int, char *); |
static int cvs_resp_updated (int, char *); |
static int cvs_resp_updated (struct cvsroot *, int, char *); |
static int cvs_resp_removed (int, char *); |
static int cvs_resp_removed (struct cvsroot *, int, char *); |
static int cvs_resp_mode (int, char *); |
static int cvs_resp_mode (struct cvsroot *, int, char *); |
static int cvs_resp_modxpand (int, char *); |
static int cvs_resp_modxpand (struct cvsroot *, int, char *); |
|
|
|
static int cvs_initlog (void); |
|
|
static const char *cvs_months[] = { |
static const char *cvs_months[] = { |
"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
|
|
int req_id; |
int req_id; |
char req_str[32]; |
char req_str[32]; |
u_int req_flags; |
u_int req_flags; |
int (*req_hdlr)(int, char *); |
int (*req_hdlr)(int, char *); |
} cvs_requests[] = { |
} cvs_requests[] = { |
{ CVS_REQ_DIRECTORY, "Directory", 0, NULL }, |
{ CVS_REQ_DIRECTORY, "Directory", 0, NULL }, |
{ CVS_REQ_MAXDOTDOT, "Max-dotdot", 0, NULL }, |
{ CVS_REQ_MAXDOTDOT, "Max-dotdot", 0, NULL }, |
|
|
{ CVS_REQ_REMOVE, "remove", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_REMOVE, "remove", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_RELEASE, "release", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_RELEASE, "release", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_ROOT, "Root", 0, NULL }, |
{ CVS_REQ_ROOT, "Root", 0, NULL }, |
{ CVS_REQ_VALIDRESP, "Valid-responses", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_VALIDRESP, "Valid-responses", 0, NULL }, |
{ CVS_REQ_VALIDREQ, "valid-requests", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_VALIDREQ, "valid-requests", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_VERSION, "version", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_VERSION, "version", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_NOOP, "noop", CVS_REQF_RESP, NULL }, |
{ CVS_REQ_NOOP, "noop", CVS_REQF_RESP, NULL }, |
|
|
struct cvs_resp { |
struct cvs_resp { |
u_int resp_id; |
u_int resp_id; |
char resp_str[32]; |
char resp_str[32]; |
int (*resp_hdlr)(int, char *); |
int (*resp_hdlr)(struct cvsroot *, int, char *); |
} cvs_responses[] = { |
} cvs_responses[] = { |
{ CVS_RESP_OK, "ok", cvs_resp_ok }, |
{ CVS_RESP_OK, "ok", cvs_resp_ok }, |
{ CVS_RESP_ERROR, "error", cvs_resp_error }, |
{ CVS_RESP_ERROR, "error", cvs_resp_error }, |
|
|
|
|
mode_t cvs_lastmode = 0; |
mode_t cvs_lastmode = 0; |
|
|
|
static char cvs_proto_buf[4096]; |
|
|
/* |
/* |
|
* Output files for protocol logging when the CVS_CLIENT_LOG enviroment |
|
* variable is set. |
|
*/ |
|
static int cvs_server_logon = 0; |
|
static FILE *cvs_server_inlog = NULL; |
|
static FILE *cvs_server_outlog = NULL; |
|
|
|
|
|
/* |
|
* 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 argc, infd[2], outfd[2]; |
|
pid_t pid; |
|
char *argv[16], *cvs_server_cmd, *vresp; |
|
|
|
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; |
|
|
|
execvp(argv[0], argv); |
|
cvs_log(LP_ERRNO, "failed to exec"); |
|
exit(EX_OSERR); |
|
} |
|
|
|
/* we are the parent */ |
|
(void)close(infd[0]); |
|
(void)close(outfd[1]); |
|
|
|
root->cr_srvin = fdopen(infd[1], "w"); |
|
if (root->cr_srvin == NULL) { |
|
cvs_log(LP_ERRNO, "failed to create pipe stream"); |
|
return (-1); |
|
} |
|
|
|
root->cr_srvout = fdopen(outfd[0], "r"); |
|
if (root->cr_srvout == NULL) { |
|
cvs_log(LP_ERRNO, "failed to create pipe stream"); |
|
return (-1); |
|
} |
|
|
|
/* make the streams line-buffered */ |
|
(void)setvbuf(root->cr_srvin, NULL, _IOLBF, 0); |
|
(void)setvbuf(root->cr_srvout, NULL, _IOLBF, 0); |
|
|
|
cvs_initlog(); |
|
|
|
/* |
|
* 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_sendreq(root, CVS_REQ_VALIDRESP, vresp) < 0) { |
|
} |
|
free(vresp); |
|
|
|
if (cvs_sendreq(root, CVS_REQ_VALIDREQ, NULL) < 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_sendreq(root, CVS_REQ_GLOBALOPT, "-q"); |
|
else if (verbosity == 0) |
|
cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-Q"); |
|
|
|
if (cvs_nolog) |
|
cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-l"); |
|
if (cvs_readonly) |
|
cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-r"); |
|
if (cvs_trace) |
|
cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-t"); |
|
|
|
/* now send the CVSROOT to the server */ |
|
if (cvs_sendreq(root, CVS_REQ_ROOT, root->cr_dir) < 0) |
|
return (-1); |
|
|
|
/* not sure why, but we have to send this */ |
|
if (cvs_sendreq(root, CVS_REQ_USEUNCHANGED, NULL) < 0) |
|
return (-1); |
|
|
|
#ifdef CVS_ZLIB |
|
/* if compression was requested, initialize it */ |
|
#endif |
|
|
|
cvs_log(LP_DEBUG, "connected to %s", root->cr_host); |
|
|
|
return (0); |
|
} |
|
|
|
|
|
/* |
|
* cvs_disconnect() |
|
* |
|
* Disconnect from the cvs server. |
|
*/ |
|
|
|
void |
|
cvs_disconnect(struct cvsroot *root) |
|
{ |
|
cvs_log(LP_DEBUG, "closing connection to %s", root->cr_host); |
|
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; |
|
} |
|
} |
|
|
|
|
|
/* |
* cvs_req_getbyid() |
* cvs_req_getbyid() |
* |
* |
*/ |
*/ |
|
|
const char* |
struct cvs_req* |
cvs_req_getbyid(int reqid) |
cvs_req_getbyid(int reqid) |
{ |
{ |
u_int i; |
u_int i; |
|
|
for (i = 0; i < CVS_NBREQ; i++) |
for (i = 0; i < CVS_NBREQ; i++) |
if (cvs_requests[i].req_id == reqid) |
if (cvs_requests[i].req_id == reqid) |
return (cvs_requests[i].req_str); |
return &(cvs_requests[i]); |
return (NULL); |
return (NULL); |
} |
} |
|
|
|
|
* cvs_req_getbyname() |
* cvs_req_getbyname() |
*/ |
*/ |
|
|
int |
struct cvs_req* |
cvs_req_getbyname(const char *rname) |
cvs_req_getbyname(const char *rname) |
{ |
{ |
u_int i; |
u_int i; |
|
|
for (i = 0; i < CVS_NBREQ; i++) |
for (i = 0; i < CVS_NBREQ; i++) |
if (strcmp(cvs_requests[i].req_str, rname) == 0) |
if (strcmp(cvs_requests[i].req_str, rname) == 0) |
return (cvs_requests[i].req_id); |
return &(cvs_requests[i]); |
|
|
return (-1); |
return (NULL); |
} |
} |
|
|
|
|
|
|
* |
* |
*/ |
*/ |
|
|
const char* |
struct cvs_resp* |
cvs_resp_getbyid(int respid) |
cvs_resp_getbyid(int respid) |
{ |
{ |
u_int i; |
u_int i; |
|
|
for (i = 0; i < CVS_NBREQ; i++) |
for (i = 0; i < CVS_NBREQ; i++) |
if (cvs_responses[i].resp_id == respid) |
if (cvs_responses[i].resp_id == (u_int)respid) |
return (cvs_responses[i].resp_str); |
return &(cvs_responses[i]); |
return (NULL); |
return (NULL); |
} |
} |
|
|
|
|
* cvs_resp_getbyname() |
* cvs_resp_getbyname() |
*/ |
*/ |
|
|
int |
struct cvs_resp* |
cvs_resp_getbyname(const char *rname) |
cvs_resp_getbyname(const char *rname) |
{ |
{ |
u_int i; |
u_int i; |
|
|
for (i = 0; i < CVS_NBREQ; i++) |
for (i = 0; i < CVS_NBREQ; i++) |
if (strcmp(cvs_responses[i].resp_str, rname) == 0) |
if (strcmp(cvs_responses[i].resp_str, rname) == 0) |
return (cvs_responses[i].resp_id); |
return &(cvs_responses[i]); |
|
|
return (-1); |
return (NULL); |
} |
} |
|
|
|
|
|
|
*/ |
*/ |
|
|
int |
int |
cvs_resp_handle(char *line) |
cvs_resp_handle(struct cvsroot *root, char *line) |
{ |
{ |
u_int i; |
u_int i; |
size_t len; |
|
char *cp, *cmd; |
char *cp, *cmd; |
|
|
cmd = line; |
cmd = line; |
|
|
for (i = 0; i < CVS_NBRESP; i++) { |
for (i = 0; i < CVS_NBRESP; i++) { |
if (strcmp(cvs_responses[i].resp_str, cmd) == 0) |
if (strcmp(cvs_responses[i].resp_str, cmd) == 0) |
return (*cvs_responses[i].resp_hdlr) |
return (*cvs_responses[i].resp_hdlr) |
(cvs_responses[i].resp_id, cp); |
(root, cvs_responses[i].resp_id, cp); |
} |
} |
|
|
/* unhandled */ |
/* unhandled */ |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_validreq(int type, char *line) |
cvs_resp_validreq(struct cvsroot *root, int type, char *line) |
{ |
{ |
int i; |
|
char *sp, *ep; |
char *sp, *ep; |
|
struct cvs_req *req; |
|
|
/* parse the requests */ |
/* parse the requests */ |
sp = line; |
sp = line; |
|
|
if (ep != NULL) |
if (ep != NULL) |
*ep = '\0'; |
*ep = '\0'; |
|
|
i = cvs_req_getbyname(sp); |
req = cvs_req_getbyname(sp); |
if (i != -1) |
if (req != NULL) |
cvs_server_validreq[i] = 1; |
cvs_server_validreq[req->req_id] = 1; |
|
|
if (ep != NULL) |
if (ep != NULL) |
sp = ep + 1; |
sp = ep + 1; |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_m(int type, char *line) |
cvs_resp_m(struct cvsroot *root, int type, char *line) |
{ |
{ |
char *cp; |
char *cp; |
FILE *stream; |
FILE *stream; |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_ok(int type, char *line) |
cvs_resp_ok(struct cvsroot *root, int type, char *line) |
{ |
{ |
return (1); |
return (1); |
} |
} |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_error(int type, char *line) |
cvs_resp_error(struct cvsroot *root, int type, char *line) |
{ |
{ |
return (1); |
return (1); |
} |
} |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_statdir(int type, char *line) |
cvs_resp_statdir(struct cvsroot *root, int type, char *line) |
{ |
{ |
int fd; |
int fd; |
char rpath[MAXPATHLEN], statpath[MAXPATHLEN]; |
char rpath[MAXPATHLEN], statpath[MAXPATHLEN]; |
|
|
cvs_client_getln(rpath, sizeof(rpath)); |
cvs_getln(root, rpath, sizeof(rpath)); |
|
|
snprintf(statpath, sizeof(statpath), "%s/%s", line, |
snprintf(statpath, sizeof(statpath), "%s/%s", line, |
CVS_PATH_STATICENTRIES); |
CVS_PATH_STATICENTRIES); |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_sticky(int type, char *line) |
cvs_resp_sticky(struct cvsroot *root, int type, char *line) |
{ |
{ |
size_t len; |
size_t len; |
char rpath[MAXPATHLEN]; |
char rpath[MAXPATHLEN]; |
|
|
line[--len] = '\0'; |
line[--len] = '\0'; |
|
|
/* get the remote path */ |
/* get the remote path */ |
cvs_client_getln(rpath, sizeof(rpath)); |
cvs_getln(root, rpath, sizeof(rpath)); |
|
|
/* if the directory doesn't exist, create it */ |
/* if the directory doesn't exist, create it */ |
if (stat(line, &st) == -1) { |
if (stat(line, &st) == -1) { |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_newentry(int type, char *line) |
cvs_resp_newentry(struct cvsroot *root, int type, char *line) |
{ |
{ |
char entbuf[128], path[MAXPATHLEN]; |
char entbuf[128], path[MAXPATHLEN]; |
CVSENTRIES *entfile; |
CVSENTRIES *entfile; |
|
|
snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line); |
snprintf(path, sizeof(path), "%s/" CVS_PATH_ENTRIES, line); |
|
|
/* get the remote path */ |
/* get the remote path */ |
cvs_client_getln(entbuf, sizeof(entbuf)); |
cvs_getln(root, entbuf, sizeof(entbuf)); |
|
|
/* get the new Entries line */ |
/* get the new Entries line */ |
if (cvs_client_getln(entbuf, sizeof(entbuf)) < 0) |
if (cvs_getln(root, entbuf, sizeof(entbuf)) < 0) |
return (-1); |
return (-1); |
|
|
entfile = cvs_ent_open(path, O_WRONLY); |
entfile = cvs_ent_open(path, O_WRONLY); |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_cksum(int type, char *line) |
cvs_resp_cksum(struct cvsroot *root, int type, char *line) |
{ |
{ |
if (cvs_fcksum != NULL) { |
if (cvs_fcksum != NULL) { |
cvs_log(LP_WARN, "unused checksum"); |
cvs_log(LP_WARN, "unused checksum"); |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_modtime(int type, char *line) |
cvs_resp_modtime(struct cvsroot *root, int type, char *line) |
{ |
{ |
int i; |
int i; |
long off; |
long off; |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_updated(int type, char *line) |
cvs_resp_updated(struct cvsroot *root, int type, char *line) |
{ |
{ |
size_t len; |
size_t len; |
char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN]; |
char tbuf[32], path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN]; |
CVSENTRIES *ef; |
CVSENTRIES *ef; |
struct cvs_ent *ep; |
struct cvs_ent *ep; |
|
|
|
ep = NULL; |
|
|
if (type == CVS_RESP_CREATED) { |
if (type == CVS_RESP_CREATED) { |
/* read the remote path of the file */ |
/* read the remote path of the file */ |
cvs_client_getln(path, sizeof(path)); |
cvs_getln(root, path, sizeof(path)); |
|
|
/* read the new entry */ |
/* read the new entry */ |
cvs_client_getln(path, sizeof(path)); |
cvs_getln(root, path, sizeof(path)); |
ep = cvs_ent_parse(path); |
ep = cvs_ent_parse(path); |
if (ep == NULL) |
if (ep == NULL) |
return (-1); |
return (-1); |
|
|
} |
} |
|
|
snprintf(path, sizeof(path), "%s%s", line, ep->ce_name); |
snprintf(path, sizeof(path), "%s%s", line, ep->ce_name); |
if (cvs_recvfile(path) < 0) { |
if (cvs_recvfile(root, path) < 0) { |
return (-1); |
return (-1); |
} |
} |
|
|
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_removed(int type, char *line) |
cvs_resp_removed(struct cvsroot *root, int type, char *line) |
{ |
{ |
return (0); |
return (0); |
} |
} |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_mode(int type, char *line) |
cvs_resp_mode(struct cvsroot *root, int type, char *line) |
{ |
{ |
if (cvs_strtomode(line, &cvs_lastmode) < 0) { |
if (cvs_strtomode(line, &cvs_lastmode) < 0) { |
return (-1); |
return (-1); |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_resp_modxpand(int type, char *line) |
cvs_resp_modxpand(struct cvsroot *root, int type, char *line) |
{ |
{ |
return (0); |
return (0); |
} |
} |
|
|
*/ |
*/ |
|
|
int |
int |
cvs_sendfile(const char *path) |
cvs_sendfile(struct cvsroot *root, const char *path) |
{ |
{ |
int fd; |
int fd; |
ssize_t ret; |
ssize_t ret; |
|
|
if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0) |
if (cvs_modetostr(st.st_mode, buf, sizeof(buf)) < 0) |
return (-1); |
return (-1); |
|
|
cvs_client_sendln(buf); |
cvs_sendln(root, buf); |
snprintf(buf, sizeof(buf), "%lld\n", st.st_size); |
snprintf(buf, sizeof(buf), "%lld\n", st.st_size); |
cvs_client_sendln(buf); |
cvs_sendln(root, buf); |
|
|
while ((ret = read(fd, buf, sizeof(buf))) != 0) { |
while ((ret = read(fd, buf, sizeof(buf))) != 0) { |
if (ret == -1) { |
if (ret == -1) { |
|
|
return (-1); |
return (-1); |
} |
} |
|
|
cvs_client_sendraw(buf, (size_t)ret); |
cvs_sendraw(root, buf, (size_t)ret); |
|
|
} |
} |
|
|
|
|
*/ |
*/ |
|
|
int |
int |
cvs_recvfile(const char *path) |
cvs_recvfile(struct cvsroot *root, const char *path) |
{ |
{ |
int fd; |
int fd; |
mode_t mode; |
mode_t mode; |
|
|
off_t fsz, cnt; |
off_t fsz, cnt; |
char buf[4096], *ep; |
char buf[4096], *ep; |
|
|
if ((cvs_client_getln(buf, sizeof(buf)) < 0) || |
if ((cvs_getln(root, buf, sizeof(buf)) < 0) || |
(cvs_strtomode(buf, &mode) < 0)) { |
(cvs_strtomode(buf, &mode) < 0)) { |
return (-1); |
return (-1); |
} |
} |
|
|
cvs_client_getln(buf, sizeof(buf)); |
cvs_getln(root, buf, sizeof(buf)); |
|
|
fsz = (off_t)strtol(buf, &ep, 10); |
fsz = (off_t)strtol(buf, &ep, 10); |
if (*ep != '\0') { |
if (*ep != '\0') { |
|
|
len = MIN(sizeof(buf), (size_t)(fsz - cnt)); |
len = MIN(sizeof(buf), (size_t)(fsz - cnt)); |
if (len == 0) |
if (len == 0) |
break; |
break; |
ret = cvs_client_recvraw(buf, len); |
ret = cvs_recvraw(root, buf, len); |
if (ret == -1) { |
if (ret == -1) { |
(void)close(fd); |
(void)close(fd); |
(void)unlink(path); |
(void)unlink(path); |
|
|
} |
} |
|
|
|
|
#ifdef notyet |
|
/* |
/* |
* cvs_sendreq() |
* cvs_sendreq() |
* |
* |
|
|
*/ |
*/ |
|
|
int |
int |
cvs_sendreq(struct cvs_root *root, u_int rid, const char *arg) |
cvs_sendreq(struct cvsroot *root, u_int rid, const char *arg) |
{ |
{ |
int ret; |
int ret; |
size_t len; |
struct cvs_req *req; |
struct cvs_req *reqp; |
|
|
|
if (root->cr_srvin == NULL) { |
if (root->cr_srvin == NULL) { |
cvs_log(LP_ERR, "cannot send request: Not connected"); |
cvs_log(LP_ERR, "cannot send request: Not connected"); |
return (-1); |
return (-1); |
} |
} |
|
|
reqp = cvs_req_getbyid(rid); |
req = cvs_req_getbyid(rid); |
if (reqp == NULL) { |
if (req == NULL) { |
cvs_log(LP_ERR, "unsupported request type %u", rid); |
cvs_log(LP_ERR, "unsupported request type %u", rid); |
return (-1); |
return (-1); |
} |
} |
|
|
snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n", |
snprintf(cvs_proto_buf, sizeof(cvs_proto_buf), "%s%s%s\n", |
reqp->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg); |
req->req_str, (arg == NULL) ? "" : " ", (arg == NULL) ? "" : arg); |
|
|
if (cvs_server_inlog != NULL) |
if (cvs_server_inlog != NULL) |
fputs(cvs_client_buf, cvs_server_inlog); |
fputs(cvs_proto_buf, cvs_server_inlog); |
|
|
ret = fputs(cvs_client_buf, root->cr_srvin); |
ret = fputs(cvs_proto_buf, root->cr_srvin); |
if (ret == EOF) { |
if (ret == EOF) { |
cvs_log(LP_ERRNO, "failed to send request to server"); |
cvs_log(LP_ERRNO, "failed to send request to server"); |
return (-1); |
return (-1); |
} |
} |
|
|
if (reqp->req_flags & CVS_REQF_RESP) |
if (req->req_flags & CVS_REQF_RESP) |
ret = cvs_getresp(root); |
ret = cvs_getresp(root); |
|
|
return (0); |
return (ret); |
} |
} |
|
|
|
|
|
|
*/ |
*/ |
|
|
int |
int |
cvs_getresp(struct cvs_root *root) |
cvs_getresp(struct cvsroot *root) |
{ |
{ |
int nbcmd; |
int nbcmd, ret; |
|
size_t len; |
|
|
nbcmd = 0; |
nbcmd = 0; |
|
|
do { |
do { |
/* wait for incoming data */ |
/* wait for incoming data */ |
if (fgets(cvs_client_buf, sizeof(cvs_client_buf), |
if (fgets(cvs_proto_buf, sizeof(cvs_proto_buf), |
root->cr_srvout) == NULL) { |
root->cr_srvout) == NULL) { |
if (feof(root->cr_srvout)) |
if (feof(root->cr_srvout)) |
return (0); |
return (0); |
|
|
} |
} |
|
|
if (cvs_server_outlog != NULL) |
if (cvs_server_outlog != NULL) |
fputs(cvs_client_buf, cvs_server_outlog); |
fputs(cvs_proto_buf, cvs_server_outlog); |
|
|
if ((len = strlen(cvs_client_buf)) != 0) { |
if ((len = strlen(cvs_proto_buf)) != 0) { |
if (cvs_client_buf[len - 1] != '\n') { |
if (cvs_proto_buf[len - 1] != '\n') { |
/* truncated line */ |
/* truncated line */ |
} |
} |
else |
else |
cvs_client_buf[--len] = '\0'; |
cvs_proto_buf[--len] = '\0'; |
} |
} |
|
|
ret = cvs_resp_handle(cvs_client_buf); |
ret = cvs_resp_handle(root, cvs_proto_buf); |
nbcmd++; |
nbcmd++; |
} while (ret == 0); |
} while (ret == 0); |
|
|
|
|
|
|
|
|
/* |
/* |
|
* cvs_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_getln(struct cvsroot *root, char *lbuf, size_t len) |
|
{ |
|
size_t rlen; |
|
|
|
if (fgets(lbuf, len, root->cr_srvout) == NULL) { |
|
if (ferror(root->cr_srvout)) { |
|
cvs_log(LP_ERRNO, "failed to read line from server"); |
|
return (-1); |
|
} |
|
|
|
if (feof(root->cr_srvout)) |
|
*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); |
|
} |
|
|
|
#ifdef notyet |
|
/* |
* cvs_sendresp() |
* cvs_sendresp() |
* |
* |
* Send a response to the client of type <rid>, with optional arguments |
* Send a response to the client of type <rid>, with optional arguments |
|
|
const char *resp; |
const char *resp; |
|
|
resp = cvs_resp_getbyid(rid); |
resp = cvs_resp_getbyid(rid); |
if (reqp == NULL) { |
if (resp == NULL) { |
cvs_log(LP_ERR, "unsupported response type %u", rid); |
cvs_log(LP_ERR, "unsupported response type %u", rid); |
return (-1); |
return (-1); |
} |
} |
|
|
snprintf(cvs_client_buf, sizeof(cvs_client_buf), "%s %s\n", resp, |
snprintf(cvs_proto_buf, sizeof(cvs_proto_buf), "%s %s\n", resp, |
(arg == NULL) ? "" : arg); |
(arg == NULL) ? "" : arg); |
|
|
ret = fputs(resp, stdout); |
ret = fputs(resp, stdout); |
|
|
|
|
do { |
do { |
/* wait for incoming data */ |
/* wait for incoming data */ |
if (fgets(cvs_client_buf, sizeof(cvs_client_buf), |
if (fgets(cvs_proto_buf, sizeof(cvs_proto_buf), |
stdin) == NULL) { |
stdin) == NULL) { |
if (feof(stdin)) |
if (feof(stdin)) |
return (0); |
return (0); |
|
|
return (-1); |
return (-1); |
} |
} |
|
|
if ((len = strlen(cvs_client_buf)) != 0) { |
if ((len = strlen(cvs_proto_buf)) != 0) { |
if (cvs_client_buf[len - 1] != '\n') { |
if (cvs_proto_buf[len - 1] != '\n') { |
/* truncated line */ |
/* truncated line */ |
} |
} |
else |
else |
cvs_client_buf[--len] = '\0'; |
cvs_proto_buf[--len] = '\0'; |
} |
} |
|
|
ret = cvs_resp_handle(cvs_client_buf); |
ret = cvs_resp_handle(cvs_proto_buf); |
} while (ret == 0); |
} 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 |
#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() |
* cvs_sendln() |
* |
* |
* Send a single line <line> string to the server. The line is sent as is, |
* Send a single line <line> string to the server. The line is sent as is, |
|
|
|
|
|
|
/* |
/* |
* cvs_client_sendraw() |
* cvs_sendraw() |
* |
* |
* Send the first <len> bytes from the buffer <src> to the server. |
* Send the first <len> bytes from the buffer <src> to the server. |
*/ |
*/ |
|
|
int |
int |
cvs_client_sendraw(const void *src, size_t len) |
cvs_sendraw(struct cvsroot *root, const void *src, size_t len) |
{ |
{ |
if (cvs_server_inlog != NULL) |
if (cvs_server_inlog != NULL) |
fwrite(src, sizeof(char), len, cvs_server_inlog); |
fwrite(src, sizeof(char), len, cvs_server_inlog); |
if (fwrite(src, sizeof(char), len, cvs_server_in) < len) { |
if (fwrite(src, sizeof(char), len, root->cr_srvin) < len) { |
return (-1); |
return (-1); |
} |
} |
|
|
|
|
|
|
|
|
/* |
/* |
* cvs_client_recvraw() |
* cvs_recvraw() |
* |
* |
* Receive the first <len> bytes from the buffer <src> to the server. |
* Receive the first <len> bytes from the buffer <src> to the server. |
*/ |
*/ |
|
|
ssize_t |
ssize_t |
cvs_client_recvraw(void *dst, size_t len) |
cvs_recvraw(struct cvsroot *root, void *dst, size_t len) |
{ |
{ |
size_t ret; |
size_t ret; |
|
|
ret = fread(dst, sizeof(char), len, cvs_server_out); |
ret = fread(dst, sizeof(char), len, root->cr_srvout); |
if (ret == 0) |
if (ret == 0) |
return (-1); |
return (-1); |
if (cvs_server_outlog != NULL) |
if (cvs_server_outlog != NULL) |
|
|
|
|
|
|
/* |
/* |
* cvs_client_getln() |
* cvs_senddir() |
* |
* |
* 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. |
* Send a `Directory' request along with the 2 paths that follow it. |
*/ |
*/ |
|
|
int |
int |
cvs_client_senddir(const char *dir) |
cvs_senddir(struct cvsroot *root, CVSFILE *dir) |
{ |
{ |
char repo[MAXPATHLEN], buf[MAXPATHLEN]; |
char buf[MAXPATHLEN]; |
|
|
if (cvs_readrepo(dir, repo, sizeof(repo)) < 0) { |
if (dir->cf_ddat->cd_repo == NULL) |
repo[0] = '\0'; |
strlcpy(buf, root->cr_dir, sizeof(buf)); |
strlcpy(buf, cvs_root->cr_dir, sizeof(buf)); |
else |
} |
snprintf(buf, sizeof(buf), "%s/%s", root->cr_dir, |
else { |
dir->cf_ddat->cd_repo); |
snprintf(buf, sizeof(buf), "%s/%s", cvs_root->cr_dir, repo); |
|
} |
|
|
|
if ((cvs_client_sendreq(CVS_REQ_DIRECTORY, dir, 0) < 0) || |
if ((cvs_sendreq(root, CVS_REQ_DIRECTORY, dir->cf_path) < 0) || |
(cvs_client_sendln(buf) < 0)) |
(cvs_sendln(root, buf) < 0)) |
return (-1); |
return (-1); |
|
|
return (0); |
return (0); |
|
|
|
|
|
|
/* |
/* |
* cvs_client_sendarg() |
* cvs_sendarg() |
* |
* |
* Send the argument <arg> to the server. The argument <append> is used to |
* 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 |
* determine if the argument should be simply appended to the last argument |
|
|
*/ |
*/ |
|
|
int |
int |
cvs_client_sendarg(const char *arg, int append) |
cvs_sendarg(struct cvsroot *root, const char *arg, int append) |
{ |
{ |
return cvs_client_sendreq(((append == 0) ? |
return cvs_sendreq(root, ((append == 0) ? |
CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg, 0); |
CVS_REQ_ARGUMENT : CVS_REQ_ARGUMENTX), arg); |
} |
} |
|
|
|
|
/* |
/* |
* cvs_client_sendentry() |
* cvs_sendentry() |
* |
* |
* Send an `Entry' request to the server along with the mandatory fields from |
* Send an `Entry' request to the server along with the mandatory fields from |
* the CVS entry <ent> (which are the name and revision). |
* the CVS entry <ent> (which are the name and revision). |
*/ |
*/ |
|
|
int |
int |
cvs_client_sendentry(const struct cvs_ent *ent) |
cvs_sendentry(struct cvsroot *root, const struct cvs_ent *ent) |
{ |
{ |
char ebuf[128], numbuf[64]; |
char ebuf[128], numbuf[64]; |
|
|
snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name, |
snprintf(ebuf, sizeof(ebuf), "/%s/%s///", ent->ce_name, |
rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf))); |
rcsnum_tostr(ent->ce_rev, numbuf, sizeof(numbuf))); |
|
|
return cvs_client_sendreq(CVS_REQ_ENTRY, ebuf, 0); |
return cvs_sendreq(root, CVS_REQ_ENTRY, ebuf); |
} |
} |
|
|
|
|
/* |
/* |
* cvs_client_initlog() |
* cvs_initlog() |
* |
* |
* Initialize protocol logging if the CVS_CLIENT_LOG environment variable is |
* 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 |
* set. In this case, the variable's value is used as a path to which the |
|
|
*/ |
*/ |
|
|
static int |
static int |
cvs_client_initlog(void) |
cvs_initlog(void) |
{ |
{ |
char *env, fpath[MAXPATHLEN]; |
char *env, fpath[MAXPATHLEN]; |
|
|
|
/* avoid doing it more than once */ |
|
if (cvs_server_logon) |
|
return (0); |
|
|
env = getenv("CVS_CLIENT_LOG"); |
env = getenv("CVS_CLIENT_LOG"); |
if (env == NULL) |
if (env == NULL) |
return (0); |
return (0); |
|
|
setvbuf(cvs_server_inlog, NULL, _IOLBF, 0); |
setvbuf(cvs_server_inlog, NULL, _IOLBF, 0); |
setvbuf(cvs_server_outlog, NULL, _IOLBF, 0); |
setvbuf(cvs_server_outlog, NULL, _IOLBF, 0); |
|
|
return (0); |
cvs_server_logon = 1; |
} |
|
|
|
|
|
/* |
|
* 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); |
return (0); |
} |
} |
#endif |
|