File: [local] / src / usr.bin / cvs / server.c (download)
Revision 1.100, Fri Jul 23 21:46:05 2010 UTC (13 years, 10 months ago) by ray
Branch: MAIN
CVS Tags: OPENBSD_5_6_BASE, OPENBSD_5_6, OPENBSD_5_5_BASE, OPENBSD_5_5, OPENBSD_5_4_BASE, OPENBSD_5_4, OPENBSD_5_3_BASE, OPENBSD_5_3, OPENBSD_5_2_BASE, OPENBSD_5_2, OPENBSD_5_1_BASE, OPENBSD_5_1, OPENBSD_5_0_BASE, OPENBSD_5_0, OPENBSD_4_9_BASE, OPENBSD_4_9, OPENBSD_4_8_BASE, OPENBSD_4_8 Changes since 1.99: +6 -6 lines
Reduce variable/function name and whitespace differences between
cvs/rcs.
OK xsa zinovik
|
/* $OpenBSD: server.c,v 1.100 2010/07/23 21:46:05 ray Exp $ */
/*
* Copyright (c) 2006 Joris Vink <joris@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "cvs.h"
#include "remote.h"
struct cvs_resp cvs_responses[] = {
/* this is what our server uses, the client should support it */
{ "Valid-requests", 1, cvs_client_validreq, RESP_NEEDED },
{ "ok", 0, cvs_client_ok, RESP_NEEDED},
{ "error", 0, cvs_client_error, RESP_NEEDED },
{ "E", 0, cvs_client_e, RESP_NEEDED },
{ "M", 0, cvs_client_m, RESP_NEEDED },
{ "Checked-in", 0, cvs_client_checkedin, RESP_NEEDED },
{ "Updated", 0, cvs_client_updated, RESP_NEEDED },
{ "Merged", 0, cvs_client_merged, RESP_NEEDED },
{ "Removed", 0, cvs_client_removed, RESP_NEEDED },
{ "Remove-entry", 0, cvs_client_remove_entry, 0 },
{ "Set-static-directory", 0,
cvs_client_set_static_directory, 0 },
{ "Clear-static-directory", 0,
cvs_client_clear_static_directory, 0 },
{ "Set-sticky", 0, cvs_client_set_sticky, 0 },
{ "Clear-sticky", 0, cvs_client_clear_sticky, 0 },
/* unsupported responses until told otherwise */
{ "New-entry", 0, NULL, 0 },
{ "Created", 0, NULL, 0 },
{ "Update-existing", 0, NULL, 0 },
{ "Rcs-diff", 0, NULL, 0 },
{ "Patched", 0, NULL, 0 },
{ "Mode", 0, NULL, 0 },
{ "Mod-time", 0, NULL, 0 },
{ "Checksum", 0, NULL, 0 },
{ "Copy-file", 0, NULL, 0 },
{ "Template", 0, NULL, 0 },
{ "Set-checkin-prog", 0, NULL, 0 },
{ "Set-update-prog", 0, NULL, 0 },
{ "Notified", 0, NULL, 0 },
{ "Module-expansion", 0, NULL, 0 },
{ "Wrapper-rcsOption", 0, NULL, 0 },
{ "Mbinary", 0, NULL, 0 },
{ "F", 0, NULL, 0 },
{ "MT", 0, NULL, 0 },
{ "", -1, NULL, 0 }
};
int cvs_server(int, char **);
char *cvs_server_path = NULL;
static char *server_currentdir = NULL;
static char **server_argv;
static int server_argc = 1;
extern int disable_fast_checkout;
struct cvs_cmd cvs_cmd_server = {
CVS_OP_SERVER, CVS_USE_WDIR, "server", { "", "" },
"server mode",
NULL,
NULL,
NULL,
cvs_server
};
int
cvs_server(int argc, char **argv)
{
char *cmd, *data;
struct cvs_req *req;
if (argc > 1)
fatal("server does not take any extra arguments");
/* Be on server-side very verbose per default. */
verbosity = 2;
setvbuf(stdin, NULL, _IOLBF, 0);
setvbuf(stdout, NULL, _IOLBF, 0);
cvs_server_active = 1;
server_argv = xcalloc(server_argc + 1, sizeof(*server_argv));
server_argv[0] = xstrdup("server");
(void)xasprintf(&cvs_server_path, "%s/cvs-serv%d", cvs_tmpdir,
getpid());
if (mkdir(cvs_server_path, 0700) == -1)
fatal("failed to create temporary server directory: %s, %s",
cvs_server_path, strerror(errno));
if (chdir(cvs_server_path) == -1)
fatal("failed to change directory to '%s'", cvs_server_path);
for (;;) {
cmd = cvs_remote_input();
if ((data = strchr(cmd, ' ')) != NULL)
(*data++) = '\0';
req = cvs_remote_get_request_info(cmd);
if (req == NULL)
fatal("request '%s' is not supported by our server",
cmd);
if (req->hdlr == NULL)
fatal("opencvs server does not support '%s'", cmd);
if ((req->flags & REQ_NEEDDIR) && (server_currentdir == NULL))
fatal("`%s' needs a directory to be sent with "
"the `Directory` request first", cmd);
(*req->hdlr)(data);
xfree(cmd);
}
return (0);
}
void
cvs_server_send_response(char *fmt, ...)
{
int i;
va_list ap;
char *data;
va_start(ap, fmt);
i = vasprintf(&data, fmt, ap);
va_end(ap);
if (i == -1)
fatal("cvs_server_send_response: could not allocate memory");
cvs_log(LP_TRACE, "%s", data);
cvs_remote_output(data);
xfree(data);
}
void
cvs_server_root(char *data)
{
if (data == NULL)
fatal("Missing argument for Root");
if (current_cvsroot != NULL)
return;
if (data[0] != '/' || (current_cvsroot = cvsroot_get(data)) == NULL)
fatal("Invalid Root specified!");
cvs_parse_configfile();
cvs_parse_modules();
umask(cvs_umask);
}
void
cvs_server_validresp(char *data)
{
int i;
char *sp, *ep;
struct cvs_resp *resp;
if ((sp = data) == NULL)
fatal("Missing argument for Valid-responses");
do {
if ((ep = strchr(sp, ' ')) != NULL)
*ep = '\0';
resp = cvs_remote_get_response_info(sp);
if (resp != NULL)
resp->supported = 1;
if (ep != NULL)
sp = ep + 1;
} while (ep != NULL);
for (i = 0; cvs_responses[i].supported != -1; i++) {
resp = &cvs_responses[i];
if ((resp->flags & RESP_NEEDED) &&
resp->supported != 1) {
fatal("client does not support required '%s'",
resp->name);
}
}
}
void
cvs_server_validreq(char *data)
{
BUF *bp;
char *d;
int i, first;
first = 0;
bp = buf_alloc(512);
for (i = 0; cvs_requests[i].supported != -1; i++) {
if (cvs_requests[i].hdlr == NULL)
continue;
if (first != 0)
buf_putc(bp, ' ');
else
first++;
buf_puts(bp, cvs_requests[i].name);
}
buf_putc(bp, '\0');
d = buf_release(bp);
cvs_server_send_response("Valid-requests %s", d);
cvs_server_send_response("ok");
xfree(d);
}
void
cvs_server_static_directory(char *data)
{
FILE *fp;
char fpath[MAXPATHLEN];
(void)xsnprintf(fpath, MAXPATHLEN, "%s/%s",
server_currentdir, CVS_PATH_STATICENTRIES);
if ((fp = fopen(fpath, "w+")) == NULL) {
cvs_log(LP_ERRNO, "%s", fpath);
return;
}
(void)fclose(fp);
}
void
cvs_server_sticky(char *data)
{
FILE *fp;
char tagpath[MAXPATHLEN];
if (data == NULL)
fatal("Missing argument for Sticky");
(void)xsnprintf(tagpath, MAXPATHLEN, "%s/%s",
server_currentdir, CVS_PATH_TAG);
if ((fp = fopen(tagpath, "w+")) == NULL) {
cvs_log(LP_ERRNO, "%s", tagpath);
return;
}
(void)fprintf(fp, "%s\n", data);
(void)fclose(fp);
}
void
cvs_server_globalopt(char *data)
{
if (data == NULL)
fatal("Missing argument for Global_option");
if (!strcmp(data, "-l"))
cvs_nolog = 1;
if (!strcmp(data, "-n"))
cvs_noexec = 1;
if (!strcmp(data, "-Q"))
verbosity = 0;
if (!strcmp(data, "-q"))
verbosity = 1;
if (!strcmp(data, "-r"))
cvs_readonly = 1;
if (!strcmp(data, "-t"))
cvs_trace = 1;
}
void
cvs_server_set(char *data)
{
char *ep;
if (data == NULL)
fatal("Missing argument for Set");
ep = strchr(data, '=');
if (ep == NULL)
fatal("no = in variable assignment");
*(ep++) = '\0';
if (cvs_var_set(data, ep) < 0)
fatal("cvs_server_set: cvs_var_set failed");
}
void
cvs_server_directory(char *data)
{
CVSENTRIES *entlist;
char *dir, *repo, *parent, *entry, *dirn, *p;
if (current_cvsroot == NULL)
fatal("No Root specified for Directory");
dir = cvs_remote_input();
STRIP_SLASH(dir);
if (strlen(dir) < strlen(current_cvsroot->cr_dir))
fatal("cvs_server_directory: bad Directory request");
repo = dir + strlen(current_cvsroot->cr_dir);
/*
* This is somewhat required for checkout, as the
* directory request will be:
*
* Directory .
* /path/to/cvs/root
*/
if (repo[0] == '\0')
p = xstrdup(".");
else
p = xstrdup(repo + 1);
cvs_mkpath(p, NULL);
if ((dirn = basename(p)) == NULL)
fatal("cvs_server_directory: %s", strerror(errno));
if ((parent = dirname(p)) == NULL)
fatal("cvs_server_directory: %s", strerror(errno));
if (strcmp(parent, ".")) {
entry = xmalloc(CVS_ENT_MAXLINELEN);
cvs_ent_line_str(dirn, NULL, NULL, NULL, NULL, 1, 0,
entry, CVS_ENT_MAXLINELEN);
entlist = cvs_ent_open(parent);
cvs_ent_add(entlist, entry);
xfree(entry);
}
if (server_currentdir != NULL)
xfree(server_currentdir);
server_currentdir = p;
xfree(dir);
}
void
cvs_server_entry(char *data)
{
CVSENTRIES *entlist;
if (data == NULL)
fatal("Missing argument for Entry");
entlist = cvs_ent_open(server_currentdir);
cvs_ent_add(entlist, data);
}
void
cvs_server_modified(char *data)
{
int fd;
size_t flen;
mode_t fmode;
const char *errstr;
char *mode, *len, fpath[MAXPATHLEN];
if (data == NULL)
fatal("Missing argument for Modified");
/* sorry, we have to use TMP_DIR */
disable_fast_checkout = 1;
mode = cvs_remote_input();
len = cvs_remote_input();
cvs_strtomode(mode, &fmode);
xfree(mode);
flen = strtonum(len, 0, INT_MAX, &errstr);
if (errstr != NULL)
fatal("cvs_server_modified: %s", errstr);
xfree(len);
(void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
if ((fd = open(fpath, O_WRONLY | O_CREAT | O_TRUNC)) == -1)
fatal("cvs_server_modified: %s: %s", fpath, strerror(errno));
cvs_remote_receive_file(fd, flen);
if (fchmod(fd, 0600) == -1)
fatal("cvs_server_modified: failed to set file mode");
(void)close(fd);
}
void
cvs_server_useunchanged(char *data)
{
}
void
cvs_server_unchanged(char *data)
{
char fpath[MAXPATHLEN];
CVSENTRIES *entlist;
struct cvs_ent *ent;
char sticky[CVS_ENT_MAXLINELEN];
char rev[CVS_REV_BUFSZ], entry[CVS_ENT_MAXLINELEN];
if (data == NULL)
fatal("Missing argument for Unchanged");
/* sorry, we have to use TMP_DIR */
disable_fast_checkout = 1;
(void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", server_currentdir, data);
entlist = cvs_ent_open(server_currentdir);
ent = cvs_ent_get(entlist, data);
if (ent == NULL)
fatal("received Unchanged request for non-existing file");
sticky[0] = '\0';
if (ent->ce_tag != NULL)
(void)xsnprintf(sticky, sizeof(sticky), "T%s", ent->ce_tag);
rcsnum_tostr(ent->ce_rev, rev, sizeof(rev));
(void)xsnprintf(entry, sizeof(entry), "/%s/%s/%s/%s/%s",
ent->ce_name, rev, CVS_SERVER_UNCHANGED, ent->ce_opts ?
ent->ce_opts : "", sticky);
cvs_ent_free(ent);
cvs_ent_add(entlist, entry);
}
void
cvs_server_questionable(char *data)
{
CVSENTRIES *entlist;
char entry[CVS_ENT_MAXLINELEN];
if (data == NULL)
fatal("Questionable request with no data attached");
(void)xsnprintf(entry, sizeof(entry), "/%s/%c///", data,
CVS_SERVER_QUESTIONABLE);
entlist = cvs_ent_open(server_currentdir);
cvs_ent_add(entlist, entry);
/* sorry, we have to use TMP_DIR */
disable_fast_checkout = 1;
}
void
cvs_server_argument(char *data)
{
if (data == NULL)
fatal("Missing argument for Argument");
server_argv = xrealloc(server_argv, server_argc + 2,
sizeof(*server_argv));
server_argv[server_argc] = xstrdup(data);
server_argv[++server_argc] = NULL;
}
void
cvs_server_argumentx(char *data)
{
int idx;
size_t len;
if (server_argc == 1)
fatal("Protocol Error: ArgumentX without previous argument");
idx = server_argc - 1;
len = strlen(server_argv[idx]) + strlen(data) + 2;
server_argv[idx] = xrealloc(server_argv[idx], len, sizeof(char));
strlcat(server_argv[idx], "\n", len);
strlcat(server_argv[idx], data, len);
}
void
cvs_server_update_patches(char *data)
{
/*
* This does not actually do anything.
* It is used to tell that the server is able to
* generate patches when given an `update' request.
* The client must issue the -u argument to `update'
* to receive patches.
*/
}
void
cvs_server_add(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_add: %s", strerror(errno));
cvs_cmdop = CVS_OP_ADD;
cmdp->cmd_flags = cvs_cmd_add.cmd_flags;
cvs_add(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_import(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_import: %s", strerror(errno));
cvs_cmdop = CVS_OP_IMPORT;
cmdp->cmd_flags = cvs_cmd_import.cmd_flags;
cvs_import(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_admin(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_admin: %s", strerror(errno));
cvs_cmdop = CVS_OP_ADMIN;
cmdp->cmd_flags = cvs_cmd_admin.cmd_flags;
cvs_admin(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_annotate(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_annotate: %s", strerror(errno));
cvs_cmdop = CVS_OP_ANNOTATE;
cmdp->cmd_flags = cvs_cmd_annotate.cmd_flags;
cvs_annotate(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_rannotate(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_rannotate: %s", strerror(errno));
cvs_cmdop = CVS_OP_RANNOTATE;
cmdp->cmd_flags = cvs_cmd_rannotate.cmd_flags;
cvs_annotate(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_commit(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_commit: %s", strerror(errno));
cvs_cmdop = CVS_OP_COMMIT;
cmdp->cmd_flags = cvs_cmd_commit.cmd_flags;
cvs_commit(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_checkout(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_checkout: %s", strerror(errno));
cvs_cmdop = CVS_OP_CHECKOUT;
cmdp->cmd_flags = cvs_cmd_checkout.cmd_flags;
cvs_checkout(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_diff(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_diff: %s", strerror(errno));
cvs_cmdop = CVS_OP_DIFF;
cmdp->cmd_flags = cvs_cmd_diff.cmd_flags;
cvs_diff(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_rdiff(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_rdiff: %s", strerror(errno));
cvs_cmdop = CVS_OP_RDIFF;
cmdp->cmd_flags = cvs_cmd_rdiff.cmd_flags;
cvs_diff(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_export(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_export: %s", strerror(errno));
cvs_cmdop = CVS_OP_EXPORT;
cmdp->cmd_flags = cvs_cmd_export.cmd_flags;
cvs_export(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_init(char *data)
{
if (data == NULL)
fatal("Missing argument for init");
if (current_cvsroot != NULL)
fatal("Root in combination with init is not supported");
if ((current_cvsroot = cvsroot_get(data)) == NULL)
fatal("Invalid argument for init");
cvs_cmdop = CVS_OP_INIT;
cmdp->cmd_flags = cvs_cmd_init.cmd_flags;
cvs_init(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_release(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_release: %s", strerror(errno));
cvs_cmdop = CVS_OP_RELEASE;
cmdp->cmd_flags = cvs_cmd_release.cmd_flags;
cvs_release(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_remove(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_remove: %s", strerror(errno));
cvs_cmdop = CVS_OP_REMOVE;
cmdp->cmd_flags = cvs_cmd_remove.cmd_flags;
cvs_remove(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_status(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_status: %s", strerror(errno));
cvs_cmdop = CVS_OP_STATUS;
cmdp->cmd_flags = cvs_cmd_status.cmd_flags;
cvs_status(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_log(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_log: %s", strerror(errno));
cvs_cmdop = CVS_OP_LOG;
cmdp->cmd_flags = cvs_cmd_log.cmd_flags;
cvs_getlog(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_rlog(char *data)
{
if (chdir(current_cvsroot->cr_dir) == -1)
fatal("cvs_server_rlog: %s", strerror(errno));
cvs_cmdop = CVS_OP_RLOG;
cmdp->cmd_flags = cvs_cmd_rlog.cmd_flags;
cvs_getlog(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_tag(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_tag: %s", strerror(errno));
cvs_cmdop = CVS_OP_TAG;
cmdp->cmd_flags = cvs_cmd_tag.cmd_flags;
cvs_tag(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_rtag(char *data)
{
if (chdir(current_cvsroot->cr_dir) == -1)
fatal("cvs_server_rtag: %s", strerror(errno));
cvs_cmdop = CVS_OP_RTAG;
cmdp->cmd_flags = cvs_cmd_rtag.cmd_flags;
cvs_tag(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_update(char *data)
{
if (chdir(server_currentdir) == -1)
fatal("cvs_server_update: %s", strerror(errno));
cvs_cmdop = CVS_OP_UPDATE;
cmdp->cmd_flags = cvs_cmd_update.cmd_flags;
cvs_update(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_version(char *data)
{
cvs_cmdop = CVS_OP_VERSION;
cmdp->cmd_flags = cvs_cmd_version.cmd_flags;
cvs_version(server_argc, server_argv);
cvs_server_send_response("ok");
}
void
cvs_server_update_entry(const char *resp, struct cvs_file *cf)
{
char *p;
char repo[MAXPATHLEN], fpath[MAXPATHLEN];
if ((p = strrchr(cf->file_rpath, ',')) != NULL)
*p = '\0';
cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN);
(void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", repo, cf->file_name);
cvs_server_send_response("%s %s/", resp, cf->file_wd);
cvs_remote_output(fpath);
if (p != NULL)
*p = ',';
}
void
cvs_server_set_sticky(const char *dir, const char *tag)
{
char fpath[MAXPATHLEN];
char repo[MAXPATHLEN];
cvs_get_repository_path(dir, repo, MAXPATHLEN);
(void)xsnprintf(fpath, MAXPATHLEN, "%s/", repo);
cvs_server_send_response("Set-sticky %s/", dir);
cvs_remote_output(fpath);
cvs_remote_output(tag);
}
void
cvs_server_clear_sticky(char *dir)
{
char fpath[MAXPATHLEN];
char repo[MAXPATHLEN];
cvs_get_repository_path(dir, repo, MAXPATHLEN);
(void)xsnprintf(fpath, MAXPATHLEN, "%s/", repo);
cvs_server_send_response("Clear-sticky %s//", dir);
cvs_remote_output(fpath);
}
void
cvs_server_exp_modules(char *module)
{
struct module_checkout *mo;
struct cvs_filelist *fl;
if (server_argc != 2)
fatal("expand-modules with no arguments");
mo = cvs_module_lookup(server_argv[1]);
RB_FOREACH(fl, cvs_flisthead, &(mo->mc_modules))
cvs_server_send_response("Module-expansion %s", fl->file_path);
cvs_server_send_response("ok");
server_argc--;
xfree(server_argv[1]);
server_argv[1] = NULL;
}