/* This file is in the public domain. */ #include #include #include #include #include #include #include extern const char *__progname; #include "vars.h" #include "util.h" #include "repo.h" #include "ctype.h" #include "listen.h" #include "structs.h" #include "linereader.h" #include "config.h" struct config_parse_temp { SECT_OPS *ops; void *sectpriv; const char *filename; LINEREADER *lr; FILE *errf; int elb; void (*err_throw)(void); } ; extern SECT_OPS sectops_repo; extern SECT_OPS sectops_listen; static SECT_OPS *sects[] = { §ops_repo, §ops_listen }; void config_warn(CONFIG *cfg, const char *fmt, ...) { va_list ap; if (! cfg->pt) abort(); va_start(ap,fmt); vfprintf(cfg->pt->errf,fmt,ap); va_end(ap); putc('\n',cfg->pt->errf); } void config_err(CONFIG *cfg, const char *fmt, ...) { va_list ap; if (! cfg->pt) abort(); va_start(ap,fmt); vfprintf(cfg->pt->errf,fmt,ap); va_end(ap); putc('\n',cfg->pt->errf); (*cfg->pt->err_throw)(); } void config_err_post(void (*post)(void), CONFIG *cfg, const char *fmt, ...) { va_list ap; if (! cfg->pt) abort(); va_start(ap,fmt); vfprintf(cfg->pt->errf,fmt,ap); va_end(ap); putc('\n',cfg->pt->errf); (*post)(); (*cfg->pt->err_throw)(); } static void config_err_wrap(FILE *f, void *ptv, const char *buf, int len) { CONFIG_PARSE_TEMP *pt; pt = ptv; for (;len>0;buf++,len--) { if (pt->elb) fprintf(f,"%s: line %d: ",pt->filename,lr_lineno(pt->lr)); putc(*buf,f); pt->elb = (*buf == '\n'); } } static int config_close_current_sect(CONFIG *cfg) { int err; err = 0; if (cfg->pt->ops) { __label__ errjmp; FILE *fsave; void (*saved_err_throw)(void); void err_throw(void) { err = 1; goto errjmp; } void err_wrap(FILE *f, void *ptv, const char *buf, int len) { CONFIG_PARSE_TEMP *pt; pt = ptv; for (;len>0;buf++,len--) { if (pt->elb) fprintf(f,"ending previous section: "); putc(*buf,f); pt->elb = (*buf == '\n'); } } saved_err_throw = cfg->pt->err_throw; cfg->pt->err_throw = &err_throw; fsave = cfg->pt->errf; cfg->pt->errf = stdio_wrap_w(fsave,cfg->pt,&err_wrap,0); (*cfg->pt->ops->done)(cfg->pt->sectpriv); errjmp:; fclose(cfg->pt->errf); cfg->pt->errf = fsave; cfg->pt->err_throw = saved_err_throw; } return(err); } static int config_process(CONFIG *cfg, const char *line) { int i; for (i=0;line[i]&&UCisspace(line[i]);i++) ; switch (line[i]) { case '\0': case '#': return(0); break; } if (cfg->pt->ops) { switch ((*cfg->pt->ops->parse)(cfg->pt->sectpriv,line)) { case SPRV_GOOD: return(0); break; case SPRV_UNKNOWN: break; case SPRV_ERROR: return(1); break; default: abort(); break; } } for (i=(sizeof(sects)/sizeof(sects[0]))-1;i>=0;i--) { void *pp; pp = (*sects[i]->start)(line,cfg); if (pp) { int err; err = config_close_current_sect(cfg); cfg->pt->ops = sects[i]; cfg->pt->sectpriv = pp; if (err) (*cfg->pt->err_throw)(); return(0); } } if (cfg->pt->ops) { config_err(cfg,"not valid in this section and not a valid section start line"); } else { config_err(cfg,"not a valid section start line"); } } static void config_close_parse(CONFIG *cfg) { int err; err = config_close_current_sect(cfg); cfg->pt->ops = 0; cfg->pt->sectpriv = 0; if (err) (*cfg->pt->err_throw)(); } static void config_free(CONFIG *cfg) { if (cfg->pt) abort(); repo_free_chain(cfg->repos); listen_free_chain(cfg->listens); free(cfg); } static void config_parse_done(CONFIG *cfg) { lr_done(cfg->pt->lr); fclose(cfg->pt->errf); free(cfg->pt); cfg->pt = 0; } static int statmatch(const struct stat *a, const struct stat *b) { return( (a->st_dev == b->st_dev) && (a->st_ino == b->st_ino) ); } static int config_switch(CONFIG *old, CONFIG *new) { REPO *oldru; REPO *newru; PAIRLIST *rmatches; int fd; REPO **orp; REPO *or; REPO **nrp; REPO *nr; int errs; PAIRLIST *pl; LISTEN *ol; LISTEN *nl; LEP *olep; LEP *nlep; errs = 0; rmatches = 0; oldru = old ? old->repos : 0; newru = new->repos; nrp = &newru; while <"newr"> ((nr = *nrp)) { fd = open(nr->filename,O_RDWR|O_CREAT,0666); if (fd < 0) { fprintf(stderr,"%s: can't open %s for repo %s: %s\n",__progname,nr->filename,nr->name,strerror(errno)); errs = 1; } else { fstat(fd,&nr->filestat); for (orp=&oldru;(or=*orp);orp=&or->link) { if (statmatch(&nr->filestat,&or->filestat)) { close(fd); *orp = or->link; *nrp = nr->link; pl = malloc(sizeof(PAIRLIST)); pl->a = or; pl->b = nr; pl->link = rmatches; rmatches = pl; continue <"newr">; } } nr->f = fdopen(fd,"r+"); } nrp = &nr->link; } for (ol=old?old->listens:0;ol;ol=ol->link) { for (olep=ol->endpoints;olep;olep=olep->link) { (*olep->ops->pre_transfer)(olep->priv); } } for (nl=new->listens;nl;nl=nl->link) { for <"new"> (nlep=nl->endpoints;nlep;nlep=nlep->link) { for (ol=old?old->listens:0;ol;ol=ol->link) { for (olep=ol->endpoints;olep;olep=olep->link) { if (olep->ops == nlep->ops) (*olep->ops->setup_transfer)(olep->priv,nlep->priv); } } if ((*nlep->ops->openfds)(nlep->priv)) errs = 1; } } if (errs) { while (rmatches) { pl = rmatches; rmatches = pl->link; ((REPO *)pl->a)->link = old->repos; old->repos = pl->a; ((REPO *)pl->b)->link = new->repos; new->repos = pl->b; free(pl); } for (ol=old?old->listens:0;ol;ol=ol->link) { for (olep=ol->endpoints;olep;olep=olep->link) { (*olep->ops->no_transfer)(olep->priv); } } return(1); } while (rmatches) { pl = rmatches; rmatches = pl->link; or = pl->a; nr = pl->b; nr->f = or->f; or->f = 0; free(pl); } for (nl=new->listens;nl;nl=nl->link) { for (nlep=nl->endpoints;nlep;nlep=nlep->link) { (*nlep->ops->do_transfer)(nlep->priv); } } return(0); } int reload_config(void) { __label__ errjmp; CONFIG *cfg; int fd; int err; int errs; void err_throw(void) { goto errjmp; } fd = open(configfile,O_RDONLY,0); if (fd < 0) { fprintf(stderr,"%s: %s: open: %s\n",__progname,configfile,strerror(errno)); return(-1); } cfg = malloc(sizeof(CONFIG)); cfg->repos = 0; cfg->listens = 0; cfg->pt = malloc(sizeof(CONFIG_PARSE_TEMP)); cfg->pt->ops = 0; cfg->pt->sectpriv = 0; cfg->pt->filename = configfile; cfg->pt->lr = lr_setup(fd); cfg->pt->errf = stdio_wrap_w(stderr,cfg->pt,&config_err_wrap,0); cfg->pt->elb = 1; cfg->pt->err_throw = 0; if (fstat(fd,&cfg->confstat) < 0) { fprintf(stderr,"%s: %s: fstat %s\n",__progname,configfile,strerror(errno)); config_parse_done(cfg); free(cfg); close(fd); return(-1); } errs = 0; while <"lines"> (1) { switch (lr_getline(cfg->pt->lr)) { case LRGL_ERROR: errs = 1; break <"lines">; case LRGL_EOF: break <"lines">; case LRGL_LINE: break; default: abort(); break; } cfg->pt->err_throw = &err_throw; err = config_process(cfg,lr_ptr(cfg->pt->lr)); if (0) { errjmp:; err = 1; } fflush(cfg->pt->errf); cfg->pt->err_throw = 0; if (err) errs = 1; if (err < 0) break; } config_close_parse(cfg); config_parse_done(cfg); close(fd); if (errs || config_switch(conf,cfg)) { config_free(cfg); return(-1); } else { if (conf) config_free(conf); conf = cfg; return(0); } }