summaryrefslogtreecommitdiff
path: root/usr.bin/cvs/cvsd.c
diff options
context:
space:
mode:
authorJean-Francois Brousseau <jfb@cvs.openbsd.org>2004-07-25 03:29:37 +0000
committerJean-Francois Brousseau <jfb@cvs.openbsd.org>2004-07-25 03:29:37 +0000
commitbe169be1824b7a9864243a1100db89997b425636 (patch)
tree8f691c8397c668dedba865aa251102a8ac81b502 /usr.bin/cvs/cvsd.c
parent45ed71a892d6d296211def9e68018a8e3deccc89 (diff)
* rework on the child API, still needs more functionality
* move the ACL parsing code into the more general conf.y, which handles parsing of the whole configuration file
Diffstat (limited to 'usr.bin/cvs/cvsd.c')
-rw-r--r--usr.bin/cvs/cvsd.c216
1 files changed, 189 insertions, 27 deletions
diff --git a/usr.bin/cvs/cvsd.c b/usr.bin/cvs/cvsd.c
index 647bc22a9e0..2295b11f285 100644
--- a/usr.bin/cvs/cvsd.c
+++ b/usr.bin/cvs/cvsd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cvsd.c,v 1.2 2004/07/16 01:46:16 jfb Exp $ */
+/* $OpenBSD: cvsd.c,v 1.3 2004/07/25 03:29:35 jfb Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -60,6 +60,7 @@ extern char *__progname;
int foreground = 0;
+volatile sig_atomic_t running = 0;
volatile sig_atomic_t restart = 0;
@@ -68,7 +69,7 @@ gid_t cvsd_gid = -1;
char *cvsd_root = NULL;
-char *cvsd_aclfile = NULL;
+char *cvsd_conffile = CVSD_CONF;
static int cvsd_privfd = -1;
@@ -81,6 +82,13 @@ static volatile sig_atomic_t cvsd_chmin = CVSD_CHILD_DEFMIN;
static volatile sig_atomic_t cvsd_chmax = CVSD_CHILD_DEFMAX;
+void usage (void);
+void sighup_handler (int);
+void sigint_handler (int);
+void sigchld_handler (int);
+int cvsd_msghdlr (struct cvsd_child *, int);
+
+
/*
* sighup_handler()
*
@@ -103,7 +111,6 @@ sighup_handler(int signo)
void
sigint_handler(int signo)
{
- cvs_sock_doloop = 0;
}
@@ -132,8 +139,7 @@ void
usage(void)
{
fprintf(stderr,
- "Usage: %s [-dfpv] [-a aclfile] [-c config] [-r root] [-s path]\n"
- "\t-a aclfile\tUse the file <aclfile> for ACL ruleset\n"
+ "Usage: %s [-dfpv] [-c config] [-r root] [-s path]\n"
"\t-d\t\tStart the server in debugging mode (very verbose)\n"
"\t-f\t\tStay in foreground instead of becoming a daemon\n"
"\t-p\t\tPerform permission and ownership check on the repository\n"
@@ -149,7 +155,6 @@ main(int argc, char **argv)
{
u_int i;
int ret, checkrepo;
- uid_t uid;
struct passwd *pwd;
struct group *grp;
@@ -158,10 +163,10 @@ main(int argc, char **argv)
if (cvs_log_init(LD_STD|LD_SYSLOG, LF_PID) < 0)
err(1, "failed to initialize logging mechanism");
- while ((ret = getopt(argc, argv, "a:dfpr:s:v")) != -1) {
+ while ((ret = getopt(argc, argv, "a:c:dfpr:s:v")) != -1) {
switch (ret) {
- case 'a':
- cvsd_aclfile = optarg;
+ case 'c':
+ cvsd_conffile = optarg;
break;
case 'd':
cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG);
@@ -191,8 +196,8 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
- if ((cvsd_aclfile != NULL) && (cvs_acl_parse(cvsd_aclfile) < 0))
- errx(1, "error while parsing the ACL file `%s'", cvsd_aclfile);
+ if (cvs_conf_read(cvsd_conffile) < 0)
+ errx(1, "error parsing configuration file `%s'", cvsd_conffile);
if (cvsd_root == NULL)
errx(1, "no CVS root directory specified");
@@ -243,15 +248,15 @@ main(int argc, char **argv)
}
/* spawn the initial pool of children */
- for (i = 0; i < cvsd_chmin; i++) {
- ret = cvsd_forkchild();
+ for (i = 0; i < (u_int)cvsd_chmin; i++) {
+ ret = cvsd_child_fork(NULL);
if (ret == -1) {
cvs_log(LP_ERR, "failed to spawn child");
exit(EX_OSERR);
}
}
- cvsd_sock_loop();
+ cvsd_parent_loop();
cvs_log(LP_NOTICE, "shutting down");
cvs_log_cleanup();
@@ -394,21 +399,28 @@ cvsd_checkperms(const char *path)
/*
- * cvsd_forkchild()
+ * cvsd_child_fork()
*
* Fork a child process which chroots to the CVS repository's root directory.
* We need to temporarily regain privileges in order to chroot.
- * On success, returns 0 if this is the child process, 1 if this is the
- * parent, or -1 on failure.
+ * If the <chpp> argument is not NULL, a reference to the newly created child
+ * structure will be returned.
+ * On success, returns 0 in the child process context, 1 in the parent's
+ * context, or -1 on failure.
*/
int
-cvsd_forkchild(void)
+cvsd_child_fork(struct cvsd_child **chpp)
{
int svec[2];
pid_t pid;
struct cvsd_child *chp;
+ if (cvsd_chnum == cvsd_chmax) {
+ cvs_log(LP_WARN, "child pool reached limit of processes");
+ return (-1);
+ }
+
if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, svec) == -1) {
cvs_log(LP_ERRNO, "failed to create socket pair");
return (-1);
@@ -468,33 +480,88 @@ cvsd_forkchild(void)
chp->ch_pid = pid;
chp->ch_sock = svec[0];
- (void)close(svec[1]);
+ chp->ch_state = CVSD_ST_IDLE;
+
+ TAILQ_INSERT_TAIL(&cvsd_children, chp, ch_list);
+ if (chpp != NULL)
+ *chpp = chp;
+ (void)close(svec[1]);
return (1);
}
/*
+ * cvsd_child_reap()
+ *
+ * Returns 0 on success, or -1 on failure.
+ */
+
+int
+cvsd_child_reap(struct cvsd_child *ch)
+{
+
+
+ return (0);
+}
+
+
+/*
+ * cvsd_child_get()
+ *
+ * Find a child process in idle state and return a pointer to the child's
+ * structure. If there are no available child processes, a new one will be
+ * created unless the number of children has attained the maximum, in which
+ * case NULL is returned.
+ */
+
+struct cvsd_child*
+cvsd_child_get(void)
+{
+ struct cvsd_child *chp;
+
+ TAILQ_FOREACH(chp, &cvsd_children, ch_list)
+ if (chp->ch_state == CVSD_ST_IDLE)
+ return (chp);
+
+ /* no available child, attempt to fork a new one */
+ chp = NULL;
+ if ((cvsd_chnum < cvsd_chmax) && (cvsd_child_fork(&chp) < 0))
+ return (NULL);
+
+ return (chp);
+}
+
+
+
+/*
* cvsd_parent_loop()
*
* Main loop of the parent cvsd process, which listens on its end of the
- * socket pair for requests from the chrooted child.
+ * local socket for requests from the cvs(1) program and on any outstanding
+ * messages from the children.
*/
static void
cvsd_parent_loop(void)
{
- uid_t uid;
- int timeout, ret;
+ int cfd, timeout, ret;
nfds_t nfds, i;
struct pollfd *pfd;
struct cvsd_child *chp;
nfds = 0;
timeout = INFTIM;
+ pfd = NULL;
for (;;) {
- nfds = cvsd_chnum;
+ if (!running)
+ break;
+
+ if (restart) {
+ /* restart server */
+ }
+ nfds = cvsd_chnum + 1;
pfd = (struct pollfd *)realloc(pfd,
nfds * sizeof(struct pollfd));
if (pfd == NULL) {
@@ -502,14 +569,17 @@ cvsd_parent_loop(void)
return;
}
- i = 0;
+ pfd[0].fd = cvsd_sock;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+ i = 1;
TAILQ_FOREACH(chp, &cvsd_children, ch_list) {
pfd[i].fd = chp->ch_sock;
pfd[i].events = POLLIN;
pfd[i].revents = 0;
i++;
- if (i == nfds)
+ if (i == nfds) /* just a precaution */
break;
}
@@ -519,14 +589,35 @@ cvsd_parent_loop(void)
break;
}
+ if (pfd[0].revents & (POLLERR|POLLNVAL)) {
+ cvs_log(LP_ERR, "poll error on request socket");
+ }
+ else if (pfd[0].revents & POLLIN) {
+ cfd = cvsd_sock_accept(pfd[0].fd);
+ if (cfd == -1)
+ chp = cvsd_child_get();
+ if (chp == NULL) {
+ cvs_log(LP_ALERT,
+ "request queue not implemented");
+ break;
+ }
+
+ if (cvsd_sendmsg(chp->ch_sock, CVSD_MSG_PASSFD,
+ &cfd, sizeof(cfd)) < 0)
+ break;
+
+ /* mark the child as busy */
+ chp->ch_state = CVSD_ST_BUSY;
+ }
+
chp = TAILQ_FIRST(&cvsd_children);
- for (i = 0; i < nfds; i++) {
+ for (i = 1; i < nfds; i++) {
if (pfd[i].revents & (POLLERR|POLLNVAL)) {
cvs_log(LP_ERR,
"poll error on child socket (PID %d)",
chp->ch_pid);
}
- else
+ else if (pfd[i].revents & POLLIN)
cvsd_msghdlr(chp, pfd[i].fd);
chp = TAILQ_NEXT(chp, ch_list);
@@ -545,6 +636,8 @@ static void
cvsd_child_loop(void)
{
int ret, timeout;
+ uid_t uid;
+ gid_t gid;
struct pollfd pfd[1];
pfd[0].fd = cvsd_privfd;
@@ -561,6 +654,10 @@ cvsd_child_loop(void)
}
cvs_log(LP_INFO, "polling");
+ if (pfd[0].revents & (POLLERR|POLLNVAL)) {
+ cvs_log(LP_ERR, "poll error");
+ break;
+ }
}
exit(0);
@@ -569,6 +666,9 @@ cvsd_child_loop(void)
/*
* cvsd_msghdlr()
+ *
+ * Handler for messages received from child processes.
+ * Returns 0 on success, or -1 on failure.
*/
int
@@ -631,6 +731,21 @@ cvsd_msghdlr(struct cvsd_child *child, int fd)
iov[1].iov_base = pw->pw_name;
}
break;
+ case CVSD_MSG_GETGID:
+ rbuf[ret] = '\0';
+ cvs_log(LP_INFO, "getting GID for `%s'", rbuf);
+
+ gr = getgrnam(rbuf);
+ if (gr != NULL) {
+ msg.cm_type = CVSD_MSG_GID;
+ msg.cm_len = sizeof(gid_t);
+ iov[1].iov_len = msg.cm_len;
+ iov[1].iov_base = &(gr->gr_gid);
+ }
+ break;
+ case CVSD_MSG_SETIDLE:
+ child->ch_state = CVSD_ST_IDLE;
+ break;
default:
cvs_log(LP_ERR, "unknown command type %u", msg.cm_type);
return (-1);
@@ -640,3 +755,50 @@ cvsd_msghdlr(struct cvsd_child *child, int fd)
return (ret);
}
+
+
+/*
+ * cvsd_set()
+ *
+ * Generic interface to set some of the parameters of the cvs server.
+ * Returns 0 on success, or -1 on failure.
+ */
+
+int
+cvsd_set(int what, ...)
+{
+ char *str;
+ va_list vap;
+
+ va_start(vap, what);
+
+ switch (what) {
+ case CVSD_SET_ROOT:
+ str = va_arg(vap, char *);
+ cvsd_root = str;
+ break;
+ case CVSD_SET_CHMIN:
+ cvsd_chmin = va_arg(vap, int);
+ /* we should increase the number of children accordingly */
+ break;
+ case CVSD_SET_CHMAX:
+ cvsd_chmax = va_arg(vap, int);
+ /* we should decrease the number of children accordingly */
+ break;
+ case CVSD_SET_ADDR:
+ /* this is more like and add than a set */
+ break;
+ case CVSD_SET_SOCK:
+ cvsd_sock_path = va_arg(vap, char *);
+ if (cvsd_sock_open() < 0)
+ return (-1);
+ break;
+ default:
+ cvs_log(LP_ERR, "invalid field to set");
+ return (-1);
+ }
+
+ va_end(vap);
+
+ return (0);
+}