diff options
author | Jean-Francois Brousseau <jfb@cvs.openbsd.org> | 2004-07-25 03:29:37 +0000 |
---|---|---|
committer | Jean-Francois Brousseau <jfb@cvs.openbsd.org> | 2004-07-25 03:29:37 +0000 |
commit | be169be1824b7a9864243a1100db89997b425636 (patch) | |
tree | 8f691c8397c668dedba865aa251102a8ac81b502 | |
parent | 45ed71a892d6d296211def9e68018a8e3deccc89 (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.c | 216 | ||||
-rw-r--r-- | usr.bin/cvs/cvsd.h | 54 | ||||
-rw-r--r-- | usr.bin/cvs/cvsd/Makefile | 12 |
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> |