static int vss_opts_changed(struct sdirs *sdirs, struct conf **cconfs, const char *incexc) { int ret=-1; struct conf **oldconfs; struct conf **newconfs; if(!(oldconfs=confs_alloc()) || !(newconfs=confs_alloc())) goto end; confs_init(oldconfs); confs_init(newconfs); // Figure out the old config, which is in the incexc file left // in the current backup directory on the server. if(conf_parse_incexcs_path(oldconfs, sdirs->cincexc)) { // Assume that the file did not exist, and therefore // the old split_vss setting is 0. set_int(oldconfs[OPT_SPLIT_VSS], 0); set_int(oldconfs[OPT_STRIP_VSS], 0); } // Figure out the new config, which is either in the incexc file from // the client, or in the cconf on the server. if(incexc) { if(conf_parse_incexcs_buf(newconfs, incexc)) { // Should probably not got here. set_int(newconfs[OPT_SPLIT_VSS], 0); set_int(newconfs[OPT_STRIP_VSS], 0); } } else { set_int(newconfs[OPT_SPLIT_VSS], get_int(cconfs[OPT_SPLIT_VSS])); set_int(newconfs[OPT_STRIP_VSS], get_int(cconfs[OPT_STRIP_VSS])); } if(get_int(newconfs[OPT_SPLIT_VSS])!=get_int(oldconfs[OPT_SPLIT_VSS])) { logp("split_vss=%d (changed since last backup)\n", get_int(newconfs[OPT_SPLIT_VSS])); ret=1; goto end; } if(get_int(newconfs[OPT_STRIP_VSS])!=get_int(oldconfs[OPT_STRIP_VSS])) { logp("strip_vss=%d (changed since last backup)\n", get_int(newconfs[OPT_STRIP_VSS])); ret=1; goto end; } ret=0; end: if(ret==1) logp("All files will be treated as new\n"); confs_free(&oldconfs); confs_free(&newconfs); return ret; }
// 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; }
static ya_result config_zone_section_init(config_data *config) { ya_result return_code; tmp_zone_idx++; /* store the previously configured zone, if any */ config_zone_section_register(config); /* make a new zone section ready */ tmp_zones = zone_alloc(); if(FAIL(return_code = confs_init(zone_tab, tmp_zones))) { zone_free(tmp_zones); tmp_zones = NULL; osformatln(termerr, "config: zone: configuration initialize (zone): %r", return_code); } return return_code; }
int reload(struct conf **confs, const char *conffile, bool firsttime) { if(!firsttime) logp("Reloading config\n"); if(confs_init(confs)) return -1; if(conf_load_global_only(conffile, confs)) return -1; umask(get_mode_t(confs[OPT_UMASK])); // This will turn on syslogging which could not be turned on before // conf_load. log_fzp_set(NULL, confs); #ifndef HAVE_WIN32 if(get_e_burp_mode(confs[OPT_BURP_MODE])==BURP_MODE_SERVER) setup_signals(); #endif // Do not try to change user or group after the first time. if(firsttime && chuser_and_or_chgrp( get_string(confs[OPT_USER]), get_string(confs[OPT_GROUP]))) return -1; return 0; }
// The return code of this is the return code of the standalone process. int champ_chooser_server_standalone(struct conf **globalcs) { int ret=1; struct sdirs *sdirs=NULL; struct conf **cconfs=NULL; const char *orig_client=get_string(globalcs[OPT_ORIG_CLIENT]); if(!(cconfs=confs_alloc())) goto end; confs_init(cconfs); // We need to be given a client name and load the relevant server side // clientconfdir file, because various settings may be overridden // there. if(set_string(cconfs[OPT_CNAME], orig_client) || conf_load_clientconfdir(globalcs, cconfs) || !(sdirs=sdirs_alloc()) || sdirs_init_from_confs(sdirs, cconfs) || champ_chooser_server(sdirs, cconfs, 0 /* resume */)) goto end; ret=0; end: confs_free(&cconfs); sdirs_free(&sdirs); 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; }
END_TEST static void check_restore_clients(struct cstat *cstat, struct conf **parentconf, const char *restore_clients, int permitted) { struct conf **cconfs=NULL; fail_unless((cconfs=confs_alloc())!=NULL); fail_unless(!confs_init(cconfs)); fail_unless(!set_string(cconfs[OPT_CNAME], "cli1")); build_clientconfdir_file("cli1", restore_clients); fail_unless(!conf_load_clientconfdir(parentconf, cconfs)); fail_unless(!set_string(parentconf[OPT_CNAME], "cli2")); fail_unless(cstat_permitted(cstat, parentconf, cconfs)==permitted); confs_free(&cconfs); }
END_TEST static struct cstat *test_cstat_remove_setup(struct conf ***globalcs, const char *cnames[]) { struct cstat *clist=NULL; clean(); fail_unless((*globalcs=confs_alloc())!=NULL); fail_unless(!confs_init(*globalcs)); build_file(GLOBAL_CONF, MIN_SERVER_CONF); fail_unless(!conf_load_global_only(GLOBAL_CONF, *globalcs)); build_clientconfdir_files(cnames); fail_unless(!cstat_get_client_names(&clist, CLIENTCONFDIR)); assert_cstat_list(clist, cnames); return clist; }
END_TEST START_TEST(test_cstat_add_out_of_order) { struct cstat *clist=NULL; struct conf **globalcs; const char *cnames31204[] = {"cli3", "cli1", "cli2", "cli0", "cli4", NULL}; const char *cnames01234[] = {"cli0", "cli1", "cli2", "cli3", "cli4", NULL}; clean(); fail_unless((globalcs=confs_alloc())!=NULL); fail_unless(!confs_init(globalcs)); build_file(GLOBAL_CONF, MIN_SERVER_CONF); fail_unless(!conf_load_global_only(GLOBAL_CONF, globalcs)); build_clientconfdir_files(cnames31204); fail_unless(!cstat_get_client_names(&clist, CLIENTCONFDIR)); assert_cstat_list(clist, cnames01234); test_cstat_remove_teardown(&globalcs, &clist); }
static int run_test_confs(struct conf **confs, const char *client) { int ret=-1; struct conf **cconfs=NULL; if(!client) { confs_dump(confs, 0); ret=0; goto end; } if(!(cconfs=confs_alloc())) goto end; confs_init(cconfs); if(set_string(cconfs[OPT_CNAME], client) || set_string(cconfs[OPT_PEER_VERSION], VERSION) || conf_load_clientconfdir(confs, cconfs)) goto end; confs_dump(cconfs, CONF_FLAG_CC_OVERRIDE|CONF_FLAG_INCEXC); end: confs_free(&cconfs); return ret; }
static int conf_init_save_cname_and_version(struct conf **cconfs) { int ret=-1; char *cname=NULL; char *cversion=NULL; char *orig_cname=get_string(cconfs[OPT_CNAME]); char *orig_cversion=get_string(cconfs[OPT_PEER_VERSION]); if((orig_cname && !(cname=strdup_w(orig_cname, __func__))) || (orig_cversion && !(cversion=strdup_w(orig_cversion, __func__)))) goto end; set_string(cconfs[OPT_CNAME], NULL); set_string(cconfs[OPT_PEER_VERSION], NULL); if(confs_init(cconfs)) goto end; set_string(cconfs[OPT_CNAME], cname); set_string(cconfs[OPT_PEER_VERSION], cversion); ret=0; end: free_w(&cname); free_w(&cversion); return ret; }
int run_bedup(int argc, char *argv[]) { int i=1; int ret=0; int option=0; int nonburp=0; unsigned int maxlinks=DEF_MAX_LINKS; char *groups=NULL; char ext[16]=""; int givenconfigfile=0; const char *configfile=NULL; configfile=get_config_path(); snprintf(ext, sizeof(ext), ".bedup.%d", getpid()); while((option=getopt(argc, argv, "c:dg:hlm:nvV?"))!=-1) { switch(option) { case 'c': configfile=optarg; givenconfigfile=1; break; case 'd': deletedups=1; break; case 'g': groups=optarg; break; case 'l': makelinks=1; break; case 'm': maxlinks=atoi(optarg); break; case 'n': nonburp=1; break; case 'V': printf("%s-%s\n", prog, VERSION); return 0; case 'v': verbose=1; break; case 'h': case '?': return usage(); } } if(nonburp && givenconfigfile) { logp("-n and -c options are mutually exclusive\n"); return 1; } if(nonburp && groups) { logp("-n and -g options are mutually exclusive\n"); return 1; } if(!nonburp && maxlinks!=DEF_MAX_LINKS) { logp("-m option is specified via the configuration file in burp mode (max_hardlinks=)\n"); return 1; } if(deletedups && makelinks) { logp("-d and -l options are mutually exclusive\n"); return 1; } if(deletedups && !nonburp) { logp("-d option requires -n option\n"); return 1; } if(optind>=argc) { if(nonburp) { logp("No directories found after options\n"); return 1; } } else { if(!nonburp) { logp("Do not specify extra arguments.\n"); return 1; } } if(maxlinks<2) { logp("The argument to -m needs to be greater than 1.\n"); return 1; } if(nonburp) { // Read directories from command line. for(i=optind; i<argc; i++) { // Strip trailing slashes, for tidiness. if(argv[i][strlen(argv[i])-1]=='/') argv[i][strlen(argv[i])-1]='\0'; if(process_dir("", argv[i], ext, maxlinks, 0 /* not burp mode */, 0 /* level */)) { ret=1; break; } } } else { struct conf **globalcs=NULL; struct strlist *grouplist=NULL; struct lock *globallock=NULL; if(groups) { char *tok=NULL; if((tok=strtok(groups, ",\n"))) { do { if(strlist_add(&grouplist, tok, 1)) { log_out_of_memory(__func__); return -1; } } while((tok=strtok(NULL, ",\n"))); } if(!grouplist) { logp("unable to read list of groups\n"); return -1; } } // Read directories from config files, and get locks. if(!(globalcs=confs_alloc())) return -1; if(confs_init(globalcs)) return -1; if(conf_load_global_only(configfile, globalcs)) return 1; if(get_e_burp_mode(globalcs[OPT_BURP_MODE])!=BURP_MODE_SERVER) { logp("%s is not a server config file\n", configfile); confs_free(&globalcs); return 1; } logp("Dedup clients from %s\n", get_string(globalcs[OPT_CLIENTCONFDIR])); maxlinks=get_int(globalcs[OPT_MAX_HARDLINKS]); if(grouplist) { struct strlist *g=NULL; logp("in dedup groups:\n"); for(g=grouplist; g; g=g->next) logp("%s\n", g->path); } else { char *lockpath=NULL; // Only get the global lock when doing a global run. // If you are doing individual groups, you are likely // to want to do many different dedup jobs and a // global lock would get in the way. if(!(lockpath=prepend( get_string(globalcs[OPT_LOCKFILE]), ".bedup", "")) || !(globallock=lock_alloc_and_init(lockpath))) return 1; lock_get(globallock); if(globallock->status!=GET_LOCK_GOT) { logp("Could not get lock %s (%d)\n", lockpath, globallock->status); free_w(&lockpath); return 1; } logp("Got %s\n", lockpath); } ret=iterate_over_clients(globalcs, grouplist, ext, maxlinks); confs_free(&globalcs); lock_release(globallock); lock_free(&globallock); strlists_free(&grouplist); } if(!nonburp) { logp("%d client storages scanned\n", ccount); } logp("%llu duplicate %s found\n", count, count==1?"file":"files"); logp("%llu bytes %s%s\n", savedbytes, (makelinks || deletedups)?"saved":"saveable", bytes_to_human(savedbytes)); return ret; }
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]); } }
static int run_child(int *cfd, SSL_CTX *ctx, struct sockaddr_storage *addr, int status_wfd, int status_rfd, const char *conffile, int forking) { int ret=-1; int ca_ret=0; SSL *ssl=NULL; BIO *sbio=NULL; struct conf **confs=NULL; struct conf **cconfs=NULL; struct cntr *cntr=NULL; struct async *as=NULL; const char *cname=NULL; struct asfd *asfd=NULL; int is_status_server=0; if(!(confs=confs_alloc()) || !(cconfs=confs_alloc())) goto end; set_peer_env_vars(addr); // Reload global config, in case things have changed. This means that // the server does not need to be restarted for most conf changes. confs_init(confs); confs_init(cconfs); if(conf_load_global_only(conffile, confs)) goto end; // Hack to keep forking turned off if it was specified as off on the // command line. if(!forking) set_int(confs[OPT_FORK], 0); if(!(sbio=BIO_new_socket(*cfd, BIO_NOCLOSE)) || !(ssl=SSL_new(ctx))) { logp("There was a problem joining ssl to the socket\n"); goto end; } SSL_set_bio(ssl, sbio, sbio); /* Do not try to check peer certificate straight away. Clients can send a certificate signing request when they have no certificate. */ SSL_set_verify(ssl, SSL_VERIFY_PEER /* | SSL_VERIFY_FAIL_IF_NO_PEER_CERT */, 0); if(ssl_do_accept(ssl)) goto end; if(!(as=async_alloc()) || as->init(as, 0) || !(asfd=setup_asfd_ssl(as, "main socket", cfd, ssl))) goto end; asfd->set_timeout(asfd, get_int(confs[OPT_NETWORK_TIMEOUT])); asfd->ratelimit=get_float(confs[OPT_RATELIMIT]); if(authorise_server(as->asfd, confs, cconfs) || !(cname=get_string(cconfs[OPT_CNAME])) || !*cname) { // Add an annoying delay in case they are tempted to // try repeatedly. log_and_send(as->asfd, "unable to authorise on server"); sleep(1); goto end; } if(!get_int(cconfs[OPT_ENABLED])) { log_and_send(as->asfd, "client not enabled on server"); sleep(1); goto end; } // Set up counters. Have to wait until here to get cname. if(!(cntr=cntr_alloc()) || cntr_init(cntr, cname)) goto end; set_cntr(confs[OPT_CNTR], cntr); set_cntr(cconfs[OPT_CNTR], cntr); /* At this point, the client might want to get a new certificate signed. Clients on 1.3.2 or newer can do this. */ if((ca_ret=ca_server_maybe_sign_client_cert(as->asfd, confs, cconfs))<0) { logp("Error signing client certificate request for %s\n", cname); goto end; } else if(ca_ret>0) { // Certificate signed and sent back. // Everything is OK, but we will close this instance // so that the client can start again with a new // connection and its new certificates. logp("Signed and returned client certificate request for %s\n", cname); ret=0; goto end; } /* Now it is time to check the certificate. */ if(ssl_check_cert(ssl, confs, cconfs)) { log_and_send(as->asfd, "check cert failed on server"); goto end; } if(status_rfd>=0) { is_status_server=1; if(!setup_asfd(as, "status server parent socket", &status_rfd)) goto end; } ret=child(as, is_status_server, status_wfd, confs, cconfs); end: *cfd=-1; if(as && asfd_flush_asio(as->asfd)) ret=-1; async_asfd_free_all(&as); // This closes cfd for us. logp("exit child\n"); if(cntr) cntr_free(&cntr); if(confs) { set_cntr(confs[OPT_CNTR], NULL); confs_free(&confs); } if(cconfs) { set_cntr(cconfs[OPT_CNTR], NULL); confs_free(&cconfs); } return ret; }