// FIX THIS: need to unit test this. static int do_conf_switch_to_orig_client(struct conf **globalcs, struct conf **cconfs, const char *orig_client, const char *buf) { int ret=-1; int loadrc; struct conf **sconfs=NULL; if(!(sconfs=confs_alloc()) || confs_init(sconfs)) goto end; if(set_string(sconfs[OPT_CNAME], orig_client)) goto end; logp("Client wants to switch to client: %s\n", get_string(sconfs[OPT_CNAME])); // Allow unit testing using a buffer. #ifdef UTEST if(buf) loadrc=conf_load_overrides_buf(globalcs, sconfs, buf); else #endif loadrc=conf_load_clientconfdir(globalcs, sconfs); if(loadrc) { logp("Could not load alternate config: %s", get_string(sconfs[OPT_CNAME])); goto end; } set_int(sconfs[OPT_SEND_CLIENT_CNTR], get_int(cconfs[OPT_SEND_CLIENT_CNTR])); if(!restore_client_allowed(cconfs, sconfs)) goto end; if(set_string(sconfs[OPT_RESTORE_PATH], get_string(cconfs[OPT_RESTORE_PATH]))) goto end; if(set_string(cconfs[OPT_RESTORE_PATH], NULL)) goto end; set_cntr(sconfs[OPT_CNTR], get_cntr(cconfs[OPT_CNTR])); set_cntr(cconfs[OPT_CNTR], NULL); confs_free_content(cconfs); confs_init(cconfs); confs_memcpy(cconfs, sconfs); confs_null(sconfs); if(set_string(cconfs[OPT_RESTORE_CLIENT], get_string(cconfs[OPT_CNAME]))) goto end; if(set_string(cconfs[OPT_ORIG_CLIENT], get_string(cconfs[OPT_CNAME]))) goto end; logp("Switched to client %s\n", get_string(cconfs[OPT_CNAME])); ret=0; end: confs_free(&sconfs); return ret; }
// FIX THIS: need to unit test this. int conf_switch_to_orig_client(struct conf **globalcs, struct conf **cconfs, const char *orig_client) { int ret=-1; struct conf **sconfs=NULL; if(!(sconfs=confs_alloc()) || confs_init(sconfs)) goto end; if(set_string(sconfs[OPT_CNAME], orig_client)) goto end; logp("Client wants to switch to client: %s\n", get_string(sconfs[OPT_CNAME])); if(conf_load_clientconfdir(globalcs, sconfs)) { logp("Could not load alternate config: %s", get_string(sconfs[OPT_CNAME])); goto end; } set_int(sconfs[OPT_SEND_CLIENT_CNTR], get_int(cconfs[OPT_SEND_CLIENT_CNTR])); if(!restore_client_allowed(cconfs, sconfs)) goto end; if(set_string(sconfs[OPT_RESTORE_PATH], get_string(cconfs[OPT_RESTORE_PATH]))) goto end; if(set_string(cconfs[OPT_RESTORE_PATH], NULL)) goto end; set_cntr(sconfs[OPT_CNTR], get_cntr(cconfs)); set_cntr(cconfs[OPT_CNTR], NULL); confs_free_content(cconfs); confs_init(cconfs); confs_memcpy(cconfs, sconfs); confs_null(sconfs); if(set_string(cconfs[OPT_RESTORE_CLIENT], get_string(cconfs[OPT_CNAME]))) goto end; if(set_string(cconfs[OPT_ORIG_CLIENT], get_string(cconfs[OPT_CNAME]))) goto end; logp("Switched to client %s\n", get_string(cconfs[OPT_CNAME])); ret=0; end: confs_free(&sconfs); return ret; }
static #endif int cstat_reload_from_client_confs(struct cstat **clist, struct conf **globalcs, struct conf **cconfs) { struct cstat *c; struct stat statp; static time_t global_mtime=0; time_t global_mtime_new=0; const char *globalconffile; int reloaded=0; globalconffile=get_string(globalcs[OPT_CONFFILE]); if(stat(globalconffile, &statp) || !S_ISREG(statp.st_mode)) { logp("Could not stat main conf file %s: %s\n", globalconffile, strerror(errno)); return -1; } global_mtime_new=statp.st_mtime; // FIX THIS: If '. included' conf files have changed, this code will // not detect them. I guess that conf.c should make a list of them. while(1) { for(c=*clist; c; c=c->next) { // Look at the client conf files to see if they have // changed, and reload bits and pieces if they have. if(!c->conffile) continue; if(stat(c->conffile, &statp) || !S_ISREG(statp.st_mode)) { cstat_remove(clist, &c); break; // Go to the beginning of the list. } if(statp.st_mtime==c->conf_mtime && global_mtime_new==global_mtime) { // The conf files have not changed - no need to // do anything. continue; } c->conf_mtime=statp.st_mtime; confs_free_content(cconfs); if(set_string(cconfs[OPT_CNAME], c->name)) return -1; if(conf_load_clientconfdir(globalcs, cconfs)) { // If the file has junk in it, we will keep // trying to reload it after removal. // So, just deny permission to view it. c->permitted=0; continue; } if(set_cstat_from_conf(c, globalcs, cconfs)) return -1; reloaded++; } // Only stop if the end of the list was not reached. if(!c) break; } if(global_mtime!=global_mtime_new) global_mtime=global_mtime_new; return reloaded; }
static int iterate_over_clients(struct conf **globalcs, struct strlist *grouplist, const char *ext, unsigned int maxlinks) { int ret=0; DIR *dirp=NULL; struct conf **cconfs=NULL; struct dirent *dirinfo=NULL; const char *globalclientconfdir=get_string(globalcs[OPT_CLIENTCONFDIR]); signal(SIGABRT, &sighandler); signal(SIGTERM, &sighandler); signal(SIGINT, &sighandler); if(!(cconfs=confs_alloc())) return -1; if(confs_init(cconfs)) return -1; if(!(dirp=opendir(globalclientconfdir))) { logp("Could not opendir '%s': %s\n", globalclientconfdir, strerror(errno)); return 0; } while((dirinfo=readdir(dirp))) { char *lockfile=NULL; char *lockfilebase=NULL; char *client_lockdir=NULL; struct lock *lock=NULL; if(dirinfo->d_ino==0 // looks_like...() also avoids '.' and '..'. || looks_like_tmp_or_hidden_file(dirinfo->d_name) || !is_regular_file(globalclientconfdir, dirinfo->d_name)) continue; confs_free_content(cconfs); if(confs_init(cconfs)) return -1; if(set_string(cconfs[OPT_CNAME], dirinfo->d_name)) return -1; if(conf_load_clientconfdir(globalcs, cconfs)) { logp("could not load config for client %s\n", dirinfo->d_name); return 0; } if(grouplist) { const char *dedup_group= get_string(cconfs[OPT_DEDUP_GROUP]); if(!dedup_group || !in_group(grouplist, dedup_group)) continue; } if(!(client_lockdir=get_string(cconfs[OPT_CLIENT_LOCKDIR]))) client_lockdir=get_string(cconfs[OPT_DIRECTORY]); if(!(lockfilebase=prepend(client_lockdir, dirinfo->d_name, "/")) || !(lockfile=prepend(lockfilebase, BEDUP_LOCKFILE_NAME, "/"))) { free_w(&lockfilebase); free_w(&lockfile); ret=-1; break; } free_w(&lockfilebase); if(!(lock=lock_alloc_and_init(lockfile))) { ret=-1; break; } lock_get(lock); free_w(&lockfile); if(lock->status!=GET_LOCK_GOT) { logp("Could not get %s\n", lock->path); continue; } logp("Got %s\n", lock->path); // Remember that we got that lock. lock_add_to_list(&locklist, lock); if(process_dir(get_string(cconfs[OPT_DIRECTORY]), dirinfo->d_name, ext, maxlinks, 1 /* burp mode */, 0 /* level */)) { ret=-1; break; } ccount++; } closedir(dirp); locks_release_and_free(&locklist); confs_free(&cconfs); return ret; }
static int process_incoming_client(struct asfd *asfd, SSL_CTX *ctx, const char *conffile, struct conf **confs) { int cfd=-1; pid_t childpid; int pipe_rfd[2]; int pipe_wfd[2]; socklen_t client_length=0; struct sockaddr_storage client_name; enum asfd_fdtype fdtype=asfd->fdtype; int forking=get_int(confs[OPT_FORK]); client_length=sizeof(client_name); if((cfd=accept(asfd->fd, (struct sockaddr *)&client_name, &client_length))==-1) { // Look out, accept will get interrupted by SIGCHLDs. if(errno==EINTR) return 0; logp("accept failed on %s (%d) in %s: %s\n", asfd->desc, asfd->fd, __func__, strerror(errno)); return -1; } reuseaddr(cfd); if(log_peer_address(&client_name)) return -1; if(!forking) return run_child(&cfd, ctx, &client_name, -1, -1, conffile, forking); if(chld_check_counts(confs, asfd)) { logp("Closing new connection.\n"); close_fd(&cfd); return 0; } if(pipe(pipe_rfd)<0 || pipe(pipe_wfd)<0) { logp("pipe failed: %s", strerror(errno)); close_fd(&cfd); return -1; } switch((childpid=fork())) { case -1: logp("fork failed: %s\n", strerror(errno)); return -1; case 0: { // Child. int p; int ret; struct sigaction sa; struct async *as=asfd->as; async_asfd_free_all(&as); // Close unnecessary file descriptors. // Go up to FD_SETSIZE and hope for the best. // FIX THIS: Now that async_asfd_free_all() is doing // everything, double check whether this is needed. for(p=3; p<(int)FD_SETSIZE; p++) { if(p!=pipe_rfd[1] && p!=pipe_wfd[0] && p!=cfd) close(p); } // Set SIGCHLD back to default, so that I // can get sensible returns from waitpid. memset(&sa, 0, sizeof(sa)); sa.sa_handler=SIG_DFL; sigaction(SIGCHLD, &sa, NULL); close(pipe_rfd[0]); // close read end close(pipe_wfd[1]); // close write end confs_free_content(confs); confs_init(confs); ret=run_child(&cfd, ctx, &client_name, pipe_rfd[1], fdtype==ASFD_FD_SERVER_LISTEN_STATUS?pipe_wfd[0]:-1, conffile, forking); close(pipe_rfd[1]); close(pipe_wfd[0]); close_fd(&cfd); exit(ret); } default: // Parent. close(pipe_rfd[1]); // close write end close(pipe_wfd[0]); // close read end close_fd(&cfd); return setup_parent_child_pipes(asfd, childpid, &pipe_rfd[0], &pipe_wfd[1]); } }