summaryrefslogtreecommitdiff
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
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
-rw-r--r--usr.bin/cvs/conf.y (renamed from usr.bin/cvs/aclparse.y)514
-rw-r--r--usr.bin/cvs/cvsd.c216
-rw-r--r--usr.bin/cvs/cvsd.h54
-rw-r--r--usr.bin/cvs/cvsd/Makefile12
4 files changed, 584 insertions, 212 deletions
diff --git a/usr.bin/cvs/aclparse.y b/usr.bin/cvs/conf.y
index 8fe67693731..9c932d912e0 100644
--- a/usr.bin/cvs/aclparse.y
+++ b/usr.bin/cvs/conf.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: aclparse.y,v 1.1 2004/07/13 22:02:40 jfb Exp $ */
+/* $OpenBSD: conf.y,v 1.1 2004/07/25 03:29:34 jfb Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -25,6 +25,9 @@
*
*/
%{
+/*
+ * Configuration parser for the CVS daemon
+ */
#include <sys/types.h>
#include <sys/queue.h>
@@ -40,6 +43,7 @@
#include "log.h"
#include "event.h"
+#define CVS_ACL_MAXRULES 256
#define CVS_ACL_DENY 0
#define CVS_ACL_ALLOW 1
@@ -48,6 +52,14 @@
#define CVS_ACL_QUICKOPT 0x02
+struct conf_macro {
+ char *cm_name;
+ char *cm_val;
+
+ SIMPLEQ_ENTRY(conf_macro) cm_list;
+};
+
+
struct acl_user {
uid_t au_uid;
SLIST_ENTRY(acl_user) au_list;
@@ -55,6 +67,7 @@ struct acl_user {
struct acl_rule {
+ u_int8_t ar_id;
u_int8_t ar_act;
u_int8_t ar_opts;
u_int8_t ar_op;
@@ -62,16 +75,20 @@ struct acl_rule {
char *ar_tag;
SLIST_HEAD(, acl_user) ar_users;
+ TAILQ_ENTRY(acl_rule) ar_list;
};
+
typedef struct {
union {
- u_int32_t num;
+ u_int64_t num;
char *string;
+ struct acl_rule *rule;
struct acl_user *user_list;
+ struct cvsd_addr *addr;
} v;
int lineno;
@@ -85,170 +102,103 @@ int lgetc (FILE *);
int yyerror (const char *, ...);
int yylex (void);
+int yyparse (void);
int lookup (const char *);
int kw_cmp (const void *, const void *);
+int cvs_conf_setmacro (char *, char *);
+const char* cvs_conf_getmacro (const char *);
+
+int cvs_acl_addrule (struct acl_rule *);
u_int cvs_acl_matchuid (struct acl_rule *, uid_t);
u_int cvs_acl_matchtag (const char *, const char *);
u_int cvs_acl_matchpath (const char *, const char *);
-static char *acl_file;
-static FILE *acl_fin;
-static int acl_lineno = 1;
-static int acl_errors = 0;
-static struct acl_rule *acl_rules;
-static u_int acl_nrules = 0;
-static u_int acl_defact = CVS_ACL_DENY;
-
-
-static u_int acl_opts = 0;
-
-
-/*
- * cvs_acl_eval()
- *
- * Evaluate a thingamajimmie against the currently loaded ACL ruleset.
- * Returns CVS_ACL_ALLOW if the operation is permitted, CVS_ACL_DENY otherwise.
- */
-
-u_int
-cvs_acl_eval(struct cvs_op *op)
-{
- u_int i, res;
- struct acl_rule *arp;
-
- /* deny by default */
- res = acl_defact;
-
- for (i = 0; i < acl_nrules; i++) {
- arp = &(acl_rules[i]);
-
- if ((op->co_op != arp->ar_op) ||
- !cvs_acl_matchuid(arp, op->co_uid) ||
- !cvs_acl_matchtag(op->co_tag, arp->ar_tag) ||
- !cvs_acl_matchpath(op->co_path, arp->ar_path))
- continue;
-
- res = arp->ar_act;
-
- if (arp->ar_opts & CVS_ACL_LOGOPT)
- cvs_log(LP_WARN, "act=%u, path=%s, tag=%s, uid=%u",
- op->co_op, op->co_path, op->co_tag, op->co_uid);
- if (arp->ar_opts & CVS_ACL_QUICKOPT)
- break;
- }
-
- return (res);
-}
-
-
-/*
- * cvs_acl_matchuid()
- *
- * Check if an ACL rule has a UID matching <uid>. If no user is specified
- * for a given rule, any UID will match.
- * Returns 1 if this is the case, 0 otherwise.
- */
-
-u_int
-cvs_acl_matchuid(struct acl_rule *rule, uid_t uid)
-{
- struct acl_user *aup;
-
- if (SLIST_EMPTY(&(rule->ar_users)))
- return (1);
-
- SLIST_FOREACH(aup, &(rule->ar_users), au_list)
- if (aup->au_uid == uid)
- return (1);
- return (0);
-}
-
-
-/*
- * cvs_acl_matchtag()
- *
- * Returns 1 if this is the case, 0 otherwise.
- */
-
-u_int
-cvs_acl_matchtag(const char *tag1, const char *tag2)
-{
- if ((tag1 == NULL) && (tag2 == NULL)) /* HEAD */
- return (1);
-
- if ((tag1 != NULL) && (tag2 != NULL) &&
- (strcmp(tag1, tag2) == 0))
- return (1);
-
- return (0);
-}
-
+static const char *conf_file;
+static FILE *conf_fin;
+static int conf_lineno = 1;
+static int conf_errors = 0;
-/*
- * cvs_acl_matchpath()
- *
- * Check if the path <op_path> is a subpath of <acl_path>.
- * Returns 1 if this is the case, 0 otherwise.
- */
+static SIMPLEQ_HEAD(, conf_macro) conf_macros;
-u_int
-cvs_acl_matchpath(const char *op_path, const char *acl_path)
-{
- size_t len;
- char rop_path[MAXPATHLEN];
-
- /* if the ACL path is NULL, apply on all paths */
- if (acl_path == NULL)
- return (1);
-
- if (realpath(op_path, rop_path) == NULL) {
- cvs_log(LP_ERRNO, "failed to convert `%s' to a real path",
- op_path);
- return (0);
- }
-
- printf("comparing `%s' to `%s'\n", rop_path, acl_path);
- len = strlen(rop_path);
-
- if (strncmp(rop_path, acl_path, len) == 0)
- return (1);
+/* ACL rules */
+static TAILQ_HEAD(, acl_rule) acl_rules;
+static u_int acl_nrules = 0;
+static u_int acl_defact = CVS_ACL_DENY;
- return (0);
-}
%}
+%token LISTEN CVSROOT MINCHILD MAXCHILD REQSOCK
%token ALLOW DENY LOG QUICK ON TAG FROM
-%token ADD CHECKOUT COMMIT DIFF HISTORY UPDATE
+%token ANY ADD CHECKOUT COMMIT DIFF HISTORY UPDATE
%token <v.string> STRING
-%type <v.num> action options operation
+%type <v.num> action number options operation
+%type <v.rule> aclrule
+%type <v.addr> address
%type <v.userlist>
%type <v.string> pathspec tagspec
%%
-ruleset : /* empty */
- | ruleset '\n'
- | ruleset rule '\n'
- | ruleset error '\n' { acl_errors++; }
+conf : /* empty */
+ | conf '\n'
+ | conf cfline '\n'
+ | conf error '\n' { conf_errors++; }
+ ;
+
+cfline : macro_assign
+ | directive
+ | aclrule
+ ;
+
+macro_assign : STRING '=' STRING
+ {
+ if (cvs_conf_setmacro($1, $3) < 0) {
+ free($1);
+ free($3);
+ YYERROR;
+ }
+ }
+ ;
+
+directive : LISTEN address { cvsd_set(CVSD_SET_ADDR, $2); }
+ | CVSROOT STRING
+ {
+ cvsd_set(CVSD_SET_ROOT, $2);
+ free($2);
+ }
+ | MINCHILD number { cvsd_set(CVSD_SET_CHMIN, $2); }
+ | MAXCHILD number { cvsd_set(CVSD_SET_CHMAX, $2); }
+ | REQSOCK STRING
+ {
+ cvsd_set(CVSD_SET_SOCK, $2);
+ free($2);
+ }
+ ;
+
+address : STRING
+ {
+ struct cvsd_addr *adp;
+
+ adp = (struct cvsd_addr *)malloc(sizeof(*adp));
+ if (adp == NULL) {
+ YYERROR;
+ }
+
+ $$ = adp;
+ }
;
-rule : action options operation pathspec tagspec userspec
+aclrule : action options operation pathspec tagspec userspec
{
- void *tmp;
struct acl_rule *arp;
- tmp = realloc(acl_rules,
- (acl_nrules + 1) * sizeof(struct acl_rule));
- if (tmp == NULL) {
- cvs_log(LP_ERRNO, "failed to grow ACL ruleset");
+ arp = (struct acl_rule *)malloc(sizeof(*arp));
+ if (arp == NULL) {
free($4);
free($5);
YYERROR;
}
- acl_rules = (struct acl_rule *)tmp;
- arp = &(acl_rules[acl_nrules++]);
-
arp->ar_act = $1;
arp->ar_opts = $2;
arp->ar_op = $3;
@@ -256,6 +206,7 @@ rule : action options operation pathspec tagspec userspec
arp->ar_path = $4;
arp->ar_tag = $5;
+ $$ = arp;
}
;
@@ -270,6 +221,7 @@ options : /* empty */ { $$ = 0; }
;
operation : ADD { $$ = CVS_OP_ADD; }
+ | ANY { $$ = CVS_OP_ANY; }
| COMMIT { $$ = CVS_OP_COMMIT; }
| TAG { $$ = CVS_OP_TAG; }
;
@@ -315,32 +267,60 @@ user : STRING
aup->au_uid = pw->pw_uid;
}
;
+
+number : STRING
+ {
+ char *ep;
+ long res;
+ res = strtol($1, &ep, 0);
+ if ((res == LONG_MIN) || (res == LONG_MAX)) {
+ yyerror("%sflow while converting number `%s'",
+ res == LONG_MIN ? "under" : "over", $1);
+ free($1);
+ YYERROR;
+ }
+ else if (*ep != '\0') {
+ yyerror("invalid number `%s'", $1);
+ free($1);
+ YYERROR;
+ }
+
+ $$ = (u_int64_t)res;
+ free($1);
+ }
+ ;
+
%%
-struct acl_kw {
+struct conf_kw {
char *kw_str;
u_int kw_id;
};
-static const struct acl_kw keywords[] = {
- { "add", ADD },
- { "allow", ALLOW },
- { "commit", COMMIT },
- { "deny", DENY },
- { "from", FROM },
- { "log", LOG },
- { "on", ON },
- { "quick", QUICK },
- { "tag", TAG },
+static const struct conf_kw keywords[] = {
+ { "add", ADD },
+ { "allow", ALLOW },
+ { "any", ANY },
+ { "commit", COMMIT },
+ { "cvsroot", CVSROOT },
+ { "deny", DENY },
+ { "from", FROM },
+ { "listen", LISTEN },
+ { "log", LOG },
+ { "on", ON },
+ { "quick", QUICK },
+ { "reqsock", REQSOCK },
+ { "tag", TAG },
+
};
int
kw_cmp(const void *k, const void *e)
{
- return (strcmp(k, ((const struct acl_kw *)e)->kw_str));
+ return (strcmp(k, ((const struct conf_kw *)e)->kw_str));
}
@@ -348,7 +328,7 @@ int
lookup(const char *tstr)
{
int type;
- const struct acl_kw *kwp;
+ const struct conf_kw *kwp;
kwp = bsearch(tstr, keywords, sizeof(keywords)/sizeof(keywords[0]),
sizeof(keywords[0]), kw_cmp);
@@ -384,27 +364,27 @@ lgetc(FILE *f)
int
yylex(void)
{
- int tok, c;
+ int c;
char buf[1024], *bp, *ep;
bp = buf;
ep = buf + sizeof(buf) - 1;
- yylval.lineno = acl_lineno;
+ yylval.lineno = conf_lineno;
/* skip whitespace */
- while ((c = lgetc(acl_fin)) == ' ')
+ while ((c = lgetc(conf_fin)) == ' ')
;
if (c == '#') {
do {
- c = lgetc(acl_fin);
+ c = lgetc(conf_fin);
} while ((c != '\n') && (c != EOF));
}
else if (c == EOF)
c = 0;
else if (c == '\n')
- acl_lineno++;
+ conf_lineno++;
else if (c != ',') {
do {
*bp++ = c;
@@ -413,11 +393,11 @@ yylex(void)
return (-1);
}
- c = lgetc(acl_fin);
+ c = lgetc(conf_fin);
if (c == EOF)
break;
} while ((c != EOF) && (c != ' ') && (c != '\n'));
- ungetc(c, acl_fin);
+ ungetc(c, conf_fin);
*bp = '\0';
c = lookup(buf);
if (c == STRING) {
@@ -443,7 +423,7 @@ yyerror(const char *fmt, ...)
va_start(vap, fmt);
- if (asprintf(&nfmt, "%s:%d: %s", acl_file, yylval.lineno, fmt) == -1) {
+ if (asprintf(&nfmt, "%s:%d: %s", conf_file, yylval.lineno, fmt) == -1) {
cvs_log(LP_ERRNO, "failed to allocate message buffer");
return (-1);
}
@@ -457,30 +437,222 @@ yyerror(const char *fmt, ...)
/*
- * cvs_acl_parse()
+ * cvs_conf_setmacro()
*
- * Parse the contents of the ACL file <file>.
+ * Add an entry in the macro list for the macro whose name is <macro> and
+ * whose value is <val>.
+ * Returns 0 on success, or -1 on failure.
*/
int
-cvs_acl_parse(const char *file)
+cvs_conf_setmacro(char *macro, char *val)
{
- acl_file = strdup(file);
- if (acl_file == NULL) {
- cvs_log(LP_ERRNO, "failed to copy ACL file path");
+ struct conf_macro *cmp;
+
+ cmp = (struct conf_macro *)malloc(sizeof(*cmp));
+ if (cmp == NULL) {
+ cvs_log(LP_ERRNO, "failed to allocate macro");
return (-1);
}
- acl_fin = fopen(file, "r");
- if (acl_fin == NULL) {
- cvs_log(LP_ERRNO, "failed to open ACL file `%s'", file);
+ /* these strings were already dup'ed by the lexer */
+ printf("macro(%s) = `%s'\n", macro, val);
+ cmp->cm_name = macro;
+ cmp->cm_val = val;
+
+ SIMPLEQ_INSERT_TAIL(&conf_macros, cmp, cm_list);
+
+ return (0);
+}
+
+
+/*
+ * cvs_conf_getmacro()
+ *
+ * Get a macro <macro>'s associated value. Returns the value string on
+ * success, or NULL if no such macro exists.
+ */
+
+const char*
+cvs_conf_getmacro(const char *macro)
+{
+ struct conf_macro *cmp;
+
+ SIMPLEQ_FOREACH(cmp, &conf_macros, cm_list)
+ if (strcmp(cmp->cm_name, macro) == 0)
+ return (cmp->cm_val);
+
+ return (NULL);
+}
+
+
+/*
+ * cvs_conf_read()
+ *
+ * Parse the contents of the configuration file <conf>.
+ */
+
+int
+cvs_conf_read(const char *conf)
+{
+ struct conf_macro *cmp;
+
+ SIMPLEQ_INIT(&conf_macros);
+ TAILQ_INIT(&acl_rules);
+ acl_nrules = 0;
+
+ conf_file = conf;
+ conf_fin = fopen(conf, "r");
+ if (conf_fin == NULL) {
+ cvs_log(LP_ERRNO, "failed to open configuration `%s'", conf);
return (-1);
}
if (yyparse() != 0)
- acl_lineno = -1;
+ conf_lineno = -1;
- (void)fclose(acl_fin);
+ (void)fclose(conf_fin);
+
+ /* we can get rid of macros now */
+ while ((cmp = SIMPLEQ_FIRST(&conf_macros)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&conf_macros, cm_list);
+ free(cmp->cm_name);
+ free(cmp->cm_val);
+ free(cmp);
+ }
- return (acl_lineno);
+ cvs_log(LP_INFO, "config %s parsed successfully", conf);
+
+ return (conf_lineno);
+}
+
+
+/*
+ * cvs_acl_addrule()
+ *
+ * Add a rule to the currently loaded ACL rules.
+ */
+
+int
+cvs_acl_addrule(struct acl_rule *rule)
+{
+ if (acl_nrules == CVS_ACL_MAXRULES) {
+ cvs_log(LP_ERR, "failed to add ACL rule: Ruleset full");
+ return (-1);
+ }
+
+ TAILQ_INSERT_TAIL(&acl_rules, rule, ar_list);
+ return (0);
+}
+
+
+/*
+ * cvs_acl_eval()
+ *
+ * Evaluate a thingamajimmie against the currently loaded ACL ruleset.
+ * Returns CVS_ACL_ALLOW if the operation is permitted, CVS_ACL_DENY otherwise.
+ */
+
+u_int
+cvs_acl_eval(struct cvs_op *op)
+{
+ u_int res;
+ struct acl_rule *arp;
+
+ /* deny by default */
+ res = acl_defact;
+
+ TAILQ_FOREACH(arp, &acl_rules, ar_list) {
+ if (((op->co_op != CVS_OP_ANY) && (op->co_op != arp->ar_op)) ||
+ !cvs_acl_matchuid(arp, op->co_uid) ||
+ !cvs_acl_matchtag(op->co_tag, arp->ar_tag) ||
+ !cvs_acl_matchpath(op->co_path, arp->ar_path))
+ continue;
+
+ res = arp->ar_act;
+
+ if (arp->ar_opts & CVS_ACL_LOGOPT)
+ cvs_log(LP_WARN, "act=%u, path=%s, tag=%s, uid=%u",
+ op->co_op, op->co_path, op->co_tag, op->co_uid);
+ if (arp->ar_opts & CVS_ACL_QUICKOPT)
+ break;
+ }
+
+ return (res);
+}
+
+
+/*
+ * cvs_acl_matchuid()
+ *
+ * Check if an ACL rule has a UID matching <uid>. If no user is specified
+ * for a given rule, any UID will match.
+ * Returns 1 if this is the case, 0 otherwise.
+ */
+
+u_int
+cvs_acl_matchuid(struct acl_rule *rule, uid_t uid)
+{
+ struct acl_user *aup;
+
+ if (SLIST_EMPTY(&(rule->ar_users)))
+ return (1);
+
+ SLIST_FOREACH(aup, &(rule->ar_users), au_list)
+ if (aup->au_uid == uid)
+ return (1);
+ return (0);
+}
+
+
+/*
+ * cvs_acl_matchtag()
+ *
+ * Returns 1 if this is the case, 0 otherwise.
+ */
+
+u_int
+cvs_acl_matchtag(const char *tag1, const char *tag2)
+{
+ if ((tag1 == NULL) && (tag2 == NULL)) /* HEAD */
+ return (1);
+
+ if ((tag1 != NULL) && (tag2 != NULL) &&
+ (strcmp(tag1, tag2) == 0))
+ return (1);
+
+ return (0);
+}
+
+
+/*
+ * cvs_acl_matchpath()
+ *
+ * Check if the path <op_path> is a subpath of <acl_path>.
+ * Returns 1 if this is the case, 0 otherwise.
+ */
+
+u_int
+cvs_acl_matchpath(const char *op_path, const char *acl_path)
+{
+ size_t len;
+ char rop_path[MAXPATHLEN];
+
+ /* if the ACL path is NULL, apply on all paths */
+ if (acl_path == NULL)
+ return (1);
+
+ if (realpath(op_path, rop_path) == NULL) {
+ cvs_log(LP_ERRNO, "failed to convert `%s' to a real path",
+ op_path);
+ return (0);
+ }
+
+ printf("comparing `%s' to `%s'\n", rop_path, acl_path);
+ len = strlen(rop_path);
+
+ if (strncmp(rop_path, acl_path, len) == 0)
+ return (1);
+
+ return (0);
}
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);
+}
diff --git a/usr.bin/cvs/cvsd.h b/usr.bin/cvs/cvsd.h
index e35dc8d9634..0fdfcd586a4 100644
--- a/usr.bin/cvs/cvsd.h
+++ b/usr.bin/cvs/cvsd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cvsd.h,v 1.1 2004/07/13 22:02:40 jfb Exp $ */
+/* $OpenBSD: cvsd.h,v 1.2 2004/07/25 03:29:35 jfb Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -30,6 +30,9 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
#include <pwd.h>
#include <signal.h>
@@ -38,6 +41,7 @@
#define CVSD_USER "_cvsd"
#define CVSD_GROUP "_cvsd"
+#define CVSD_CONF "/etc/cvsd.conf"
#define CVSD_CHILD_DEFMIN 3
#define CVSD_CHILD_DEFMAX 5
@@ -51,12 +55,16 @@
/* requests */
#define CVSD_MSG_GETUID 1
#define CVSD_MSG_GETUNAME 2
-#define CVSD_MSG_PASSFD 3 /* server passes client file descriptor */
-#define CVSD_MSG_SETIDLE 4 /* client has no further processing to do */
+#define CVSD_MSG_GETGID 3
+#define CVSD_MSG_GETGNAME 4
+#define CVSD_MSG_PASSFD 5 /* server passes client file descriptor */
+#define CVSD_MSG_SETIDLE 6 /* client has no further processing to do */
/* replies */
#define CVSD_MSG_UID 128
#define CVSD_MSG_UNAME 129
+#define CVSD_MSG_GID 130
+#define CVSD_MSG_GNAME 131
#define CVSD_MSG_SHUTDOWN 253
#define CVSD_MSG_OK 254
@@ -65,6 +73,19 @@
#define CVSD_MSG_MAXLEN 256
+#define CVSD_SET_ROOT 1
+#define CVSD_SET_CHMIN 2
+#define CVSD_SET_CHMAX 3
+#define CVSD_SET_ADDR 4
+#define CVSD_SET_SOCK 5
+
+
+#define CVSD_ST_UNKNOWN 0
+#define CVSD_ST_IDLE 1
+#define CVSD_ST_BUSY 2
+
+
+
/* message structure to pass data between the parent and the chrooted child */
struct cvsd_msg {
u_int8_t cm_type;
@@ -75,13 +96,21 @@ struct cvsd_msg {
struct cvsd_child {
pid_t ch_pid;
int ch_sock;
+ u_int ch_state;
TAILQ_ENTRY(cvsd_child) ch_list;
};
+struct cvsd_addr {
+ sa_family_t ca_fam;
+ union {
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ } ca_addr;
+};
+
-extern int foreground;
extern volatile sig_atomic_t running;
extern volatile sig_atomic_t restart;
@@ -89,15 +118,20 @@ extern volatile sig_atomic_t restart;
+int cvsd_set (int, ...);
+int cvsd_checkperms (const char *);
+int cvsd_child_fork (struct cvsd_child **);
+struct cvsd_child* cvsd_child_get (void);
+int cvsd_child_reap (struct cvsd_child *);
-
-int cvsd_checkperms (const char *);
-int cvsd_forkchild (void);
+/* from fdpass.c */
+int cvsd_sendfd (int, int);
+int cvsd_recvfd (int);
-/* from aclparse.y */
-int cvs_acl_parse (const char *);
-u_int cvs_acl_eval (struct cvs_op *);
+/* from conf.y */
+int cvs_conf_read (const char *);
+u_int cvs_acl_eval (struct cvs_op *);
/* from msg.c */
int cvsd_sendmsg (int, u_int, const void *, size_t);
diff --git a/usr.bin/cvs/cvsd/Makefile b/usr.bin/cvs/cvsd/Makefile
index ad2e5ec891c..e69ad67b321 100644
--- a/usr.bin/cvs/cvsd/Makefile
+++ b/usr.bin/cvs/cvsd/Makefile
@@ -1,11 +1,15 @@
-# $Id: Makefile,v 1.1 2004/07/13 22:02:40 jfb Exp $
+# $Id: Makefile,v 1.2 2004/07/25 03:29:36 jfb Exp $
.PATH: ${.CURDIR}/..
-BINDIR=/usr/sbin
PROG=cvsd
-MAN=cvsd.8
+SRCS= cvsd.c conf.y log.c msg.c sock.c
+
+BINDIR=/usr/sbin
+MAN=cvsd.8 cvsd.conf.5
-SRCS= cvsd.c aclparse.y log.c msg.c sock.c
+CFLAGS+= -Wall
+CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
+CFLAGS+= -Wsign-compare
.include <bsd.prog.mk>