/* May return 1 to mean try again. This happens after a successful certificate signing request so that it connects again straight away with the new key/certificate. Returns 2 if there were restore/verify warnings. Returns 3 if timer conditions were not met. */ static int do_client(struct config *conf, enum action act, int vss_restore, int json) { int ret=0; int rfd=-1; int resume=0; SSL *ssl=NULL; BIO *sbio=NULL; char buf[256]=""; SSL_CTX *ctx=NULL; struct cntr cntr; struct cntr p1cntr; char *incexc=NULL; char *server_version=NULL; const char *phase1str="backupphase1"; reset_filecounter(&p1cntr, time(NULL)); reset_filecounter(&cntr, time(NULL)); setup_signals_client(); // settimers(0, 100); logp("begin client\n"); if(act!=ACTION_ESTIMATE) { ssl_load_globals(); if(!(ctx=ssl_initialise_ctx(conf))) { logp("error initialising ssl ctx\n"); ret=-1; goto end; } SSL_CTX_set_session_id_context(ctx, (const unsigned char *)&s_server_session_id_context, sizeof(s_server_session_id_context)); if((rfd=init_client_socket(conf->server, conf->port))<0) { ret=-1; goto end; } if(!(ssl=SSL_new(ctx)) || !(sbio=BIO_new_socket(rfd, BIO_NOCLOSE))) { ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); logp("Problem joining SSL to the socket: %s\n", buf); ret=-1; goto end; } SSL_set_bio(ssl, sbio, sbio); if(SSL_connect(ssl)<=0) { ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); logp("SSL connect error: %s\n", buf); ret=-1; goto end; } } if((ret=async_init(rfd, ssl, conf, act==ACTION_ESTIMATE))) goto end; // Set quality of service bits on backup packets. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) set_bulk_packets(); if(act!=ACTION_ESTIMATE) { char cmd=0; size_t len=0; char *feat=NULL; int ca_ret=0; if((ret=authorise_client(conf, &server_version, &p1cntr))) goto end; if(server_version) { logp("Server version: %s\n", server_version); // Servers before 1.3.2 did not tell us their versions. // 1.3.2 and above can do the automatic CA stuff that // follows. if((ca_ret=ca_client_setup(conf, &p1cntr))<0) { // Error logp("Error with certificate signing request\n"); ret=-1; goto end; } else if(ca_ret>0) { // Certificate signed successfully. // Everything is OK, but we will reconnect now, in // order to use the new keys/certificates. ret=1; goto end; } } set_non_blocking(rfd); if((ret=ssl_check_cert(ssl, conf))) { logp("check cert failed\n"); goto end; } if((ret=async_write_str(CMD_GEN, "extra_comms_begin"))) { logp("Problem requesting extra_comms_begin\n"); goto end; } // Servers greater than 1.3.0 will list the extra_comms // features they support. else if((ret=async_read(&cmd, &feat, &len))) { logp("Problem reading response to extra_comms_begin\n"); goto end; } else if(cmd!=CMD_GEN) { logp("Unexpected command from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat); ret=-1; goto end; } else if(strncmp(feat, "extra_comms_begin ok", strlen("extra_comms_begin ok"))) { logp("Unexpected response from server when reading response to extra_comms_begin: %c:%s\n", cmd, feat); ret=-1; goto end; } // Can add extra bits here. The first extra bit is the // autoupgrade stuff. if(server_supports_autoupgrade(feat)) { if(conf->autoupgrade_dir && conf->autoupgrade_os && (ret=autoupgrade_client(conf, &p1cntr))) goto end; } // :srestore: means that the server wants to do a restore. if(server_supports(feat, ":srestore:")) { if(conf->server_can_restore) { logp("Server is initiating a restore\n"); if(incexc) { free(incexc); incexc=NULL; } if((ret=incexc_recv_client_restore(&incexc, conf, &p1cntr))) goto end; if(incexc) { if((ret=parse_incexcs_buf(conf, incexc))) goto end; act=ACTION_RESTORE; log_restore_settings(conf, 1); } } else { logp("Server wants to initiate a restore\n"); logp("Client configuration says no\n"); if(async_write_str(CMD_GEN, "srestore not ok")) { ret=-1; goto end; } } } if(conf->orig_client) { char str[512]=""; snprintf(str, sizeof(str), "orig_client=%s", conf->orig_client); if(!server_supports(feat, ":orig_client:")) { logp("Server does not support switching client.\n"); ret=-1; goto end; } if((ret=async_write_str(CMD_GEN, str)) || (ret=async_read_expect(CMD_GEN, "orig_client ok"))) { logp("Problem requesting %s\n", str); ret=-1; goto end; } logp("Switched to client %s\n", conf->orig_client); } // :sincexc: is for the server giving the client the // incexc config. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED) { if(!incexc && server_supports(feat, ":sincexc:")) { logp("Server is setting includes/excludes.\n"); if(incexc) { free(incexc); incexc=NULL; } if((ret=incexc_recv_client(&incexc, conf, &p1cntr))) goto end; if(incexc && (ret=parse_incexcs_buf(conf, incexc))) goto end; } } if(server_supports(feat, ":counters:")) { if(async_write_str(CMD_GEN, "countersok")) goto end; conf->send_client_counters=1; } // :incexc: is for the client sending the server the // incexc config so that it better knows what to do on // resume. if(server_supports(feat, ":incexc:") && (ret=incexc_send_client(conf, &p1cntr))) goto end; if((ret=async_write_str(CMD_GEN, "extra_comms_end")) || (ret=async_read_expect(CMD_GEN, "extra_comms_end ok"))) { logp("Problem requesting extra_comms_end\n"); goto end; } if(feat) free(feat); } rfd=-1; switch(act) { case ACTION_BACKUP_TIMED: phase1str="backupphase1timed"; case ACTION_BACKUP: { // Set bulk packets quality of service flags on backup. if(incexc) { logp("Server is overriding the configuration\n"); logp("with the following settings:\n"); if(log_incexcs_buf(incexc)) { ret=-1; goto end; } } if(!conf->sdcount) { logp("Found no include paths!\n"); ret=-1; goto end; } if(!(ret=maybe_check_timer(phase1str, conf, &resume))) { if(conf->backup_script_pre) { int a=0; const char *args[12]; args[a++]=conf->backup_script_pre; args[a++]="pre"; args[a++]="reserved2"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->backup_script_pre_arg, conf->bprecount, &p1cntr, 1, 1)) ret=-1; } if(!ret && do_backup_client(conf, resume, 0, &p1cntr, &cntr)) ret=-1; if((conf->backup_script_post_run_on_fail || !ret) && conf->backup_script_post) { int a=0; const char *args[12]; args[a++]=conf->backup_script_post; args[a++]="post"; // Tell post script whether the restore // failed. args[a++]=ret?"1":"0"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->backup_script_post_arg, conf->bpostcount, &cntr, 1, 1)) ret=-1; } } if(ret<0) logp("error in backup\n"); else if(ret>0) { // Timer script said no. // Have a distinct return value to // differentiate between other cases // (ssl reconnection and restore/verify // warnings). ret=3; } else logp("backup finished ok\n"); break; } case ACTION_RESTORE: case ACTION_VERIFY: { if(conf->restore_script_pre) { int a=0; const char *args[12]; args[a++]=conf->restore_script_pre; args[a++]="pre"; args[a++]="reserved2"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->restore_script_pre_arg, conf->rprecount, &cntr, 1, 1)) ret=-1; } if(!ret && do_restore_client(conf, act, vss_restore, &p1cntr, &cntr)) ret=-1; if((conf->restore_script_post_run_on_fail || !ret) && conf->restore_script_post) { int a=0; const char *args[12]; args[a++]=conf->restore_script_pre; args[a++]="post"; // Tell post script whether the restore // failed. args[a++]=ret?"1":"0"; args[a++]="reserved3"; args[a++]="reserved4"; args[a++]="reserved5"; args[a++]=NULL; if(run_script(args, conf->restore_script_post_arg, conf->rpostcount, &cntr, 1, 1)) ret=-1; } // Return non-zero if there were warnings, // so that the test script can easily check. if(p1cntr.warning+cntr.warning) ret=2; break; } case ACTION_ESTIMATE: if(!ret) ret=do_backup_client(conf, 0, 1, &p1cntr, &cntr); break; case ACTION_DELETE: if(!ret) ret=do_delete_client(conf); break; case ACTION_LIST: case ACTION_LONG_LIST: default: ret=do_list_client(conf, act, json); break; } end: close_fd(&rfd); async_free(); if(act!=ACTION_ESTIMATE) ssl_destroy_ctx(ctx); if(incexc) free(incexc); if(server_version) free(server_version); //logp("end client\n"); return ret; }
static int run_server(struct conf **confs, const char *conffile, int *rfds, int *sfds) { int i=0; int ret=-1; SSL_CTX *ctx=NULL; int found_normal_child=0; struct asfd *asfd=NULL; struct asfd *scfd=NULL; struct async *mainas=NULL; const char *port=get_string(confs[OPT_PORT]); const char *address=get_string(confs[OPT_ADDRESS]); const char *status_port=get_string(confs[OPT_STATUS_PORT]); const char *status_address=get_string(confs[OPT_STATUS_ADDRESS]); if(!(ctx=ssl_initialise_ctx(confs))) { logp("error initialising ssl ctx\n"); goto end; } if((ssl_load_dh_params(ctx, confs))) { logp("error loading dh params\n"); goto end; } if(init_listen_socket(address, port, rfds) || init_listen_socket(status_address, status_port, sfds)) goto end; if(!(mainas=async_alloc()) || mainas->init(mainas, 0)) goto end; for(i=0; i<LISTEN_SOCKETS && rfds[i]!=-1; i++) { struct asfd *newfd; if(!(newfd=setup_asfd(mainas, "main server socket", &rfds[i]))) goto end; newfd->fdtype=ASFD_FD_SERVER_LISTEN_MAIN; } for(i=0; i<LISTEN_SOCKETS && sfds[i]!=-1; i++) { struct asfd *newfd; if(!(newfd=setup_asfd(mainas, "main server status socket", &sfds[i]))) goto end; newfd->fdtype=ASFD_FD_SERVER_LISTEN_STATUS; } while(!hupreload) { switch(mainas->read_write(mainas)) { case 0: for(asfd=mainas->asfd; asfd; asfd=asfd->next) { if(asfd->new_client) { // Incoming client. asfd->new_client=0; if(process_incoming_client(asfd, ctx, conffile, confs)) goto end; if(!get_int(confs[OPT_FORK])) { gentleshutdown++; ret=1; goto end; } continue; } } break; default: int removed=0; // Maybe one of the fds had a problem. // Find and remove it and carry on if possible. for(asfd=mainas->asfd; asfd; ) { struct asfd *a; if(!asfd->want_to_remove) { asfd=asfd->next; continue; } mainas->asfd_remove(mainas, asfd); logp("%s: disconnected fd %d\n", asfd->desc, asfd->fd); a=asfd->next; asfd_free(&asfd); asfd=a; removed++; } if(removed) break; // If we got here, there was no fd to remove. // It is a fatal error. goto end; } for(asfd=mainas->asfd; asfd; asfd=asfd->next) { if(asfd->fdtype!=ASFD_FD_SERVER_PIPE_READ || !asfd->rbuf->buf) continue; // One of the child processes is giving us information. // Try to append it to any of the status child pipes. for(scfd=mainas->asfd; scfd; scfd=scfd->next) { if(scfd->fdtype!=ASFD_FD_SERVER_PIPE_WRITE) continue; switch(scfd->append_all_to_write_buffer(scfd, asfd->rbuf)) { case APPEND_OK: case APPEND_BLOCKED: break; default: goto end; } } // Free the information, even if we did not manage // to append it. That should be OK, more will be along // soon. iobuf_free_content(asfd->rbuf); } chld_check_for_exiting(mainas); // Leave if we had a SIGUSR1 and there are no children running. if(gentleshutdown) { if(!gentleshutdown_logged) { logp("got SIGUSR2 gentle reload signal\n"); logp("will shut down once children have exited\n"); gentleshutdown_logged++; } // FIX THIS: // found_normal_child=chld_add_fd_to_normal_sets(confs, &fsr, &fse, &mfd); else if(!found_normal_child) { logp("all children have exited - shutting down\n"); break; } } } if(hupreload) logp("got SIGHUP reload signal\n"); ret=0; end: async_asfd_free_all(&mainas); if(ctx) ssl_destroy_ctx(ctx); return ret; }
static enum cliret do_client(struct conf **confs, enum action action, int vss_restore) { enum cliret ret=CLIENT_OK; int rfd=-1; SSL *ssl=NULL; SSL_CTX *ctx=NULL; struct cntr *cntr=NULL; char *incexc=NULL; enum action act=action; struct async *as=NULL; struct asfd *asfd=NULL; // as->settimers(0, 100); // logp("begin client\n"); // logp("action %d\n", action); // Status monitor forks a child process instead of connecting to // the server directly. if(action==ACTION_STATUS || action==ACTION_STATUS_SNAPSHOT) { #ifdef HAVE_WIN32 logp("Status mode not implemented on Windows.\n"); goto error; #endif if(status_client_ncurses_init(act) || status_client_ncurses(confs)) ret=CLIENT_ERROR; goto end; } if(!(cntr=cntr_alloc()) || cntr_init(cntr, get_string(confs[OPT_CNAME]))) goto error; set_cntr(confs[OPT_CNTR], cntr); if(act!=ACTION_ESTIMATE && ssl_setup(&rfd, &ssl, &ctx, action, confs)) goto could_not_connect; if(!(as=async_alloc()) || !(asfd=asfd_alloc()) || as->init(as, act==ACTION_ESTIMATE) || asfd->init(asfd, "main socket", as, rfd, ssl, ASFD_STREAM_STANDARD, confs)) goto end; as->asfd_add(as, asfd); // Set quality of service bits on backup packets. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED || act==ACTION_TIMER_CHECK) as->asfd->set_bulk_packets(as->asfd); if(act!=ACTION_ESTIMATE) { if((ret=initial_comms(as, &act, &incexc, confs))) goto end; } rfd=-1; switch(act) { case ACTION_BACKUP: ret=backup_wrapper(asfd, act, "backupphase1", incexc, confs); break; case ACTION_BACKUP_TIMED: ret=backup_wrapper(asfd, act, "backupphase1timed", incexc, confs); break; case ACTION_TIMER_CHECK: ret=backup_wrapper(asfd, act, "backupphase1timedcheck", incexc, confs); break; case ACTION_RESTORE: case ACTION_VERIFY: ret=restore_wrapper(asfd, act, vss_restore, confs); break; case ACTION_ESTIMATE: if(do_backup_client(asfd, confs, act, 0)) goto error; break; case ACTION_DELETE: if(do_delete_client(asfd, confs)) goto error; break; case ACTION_MONITOR: if(do_monitor_client(asfd, confs)) goto error; break; case ACTION_DIFF: case ACTION_DIFF_LONG: /* if(!strcmp(get_string(confs[OPT_BACKUP2]), "n")) // Do a phase1 scan and diff that. ret=backup_wrapper(asfd, act, "backupphase1diff", incexc, confs); else */ // Diff two backups that already exist. // Fall through, the list code is all we need // for simple diffs on the client side. case ACTION_LIST: case ACTION_LIST_LONG: default: if(do_list_client(asfd, act, confs)) goto error; break; } if(asfd_flush_asio(asfd)) ret=CLIENT_ERROR; goto end; error: ret=CLIENT_ERROR; goto end; could_not_connect: ret=CLIENT_COULD_NOT_CONNECT; end: close_fd(&rfd); async_free(&as); asfd_free(&asfd); if(ctx) ssl_destroy_ctx(ctx); free_w(&incexc); set_cntr(confs[OPT_CNTR], NULL); cntr_free(&cntr); //logp("end client\n"); return ret; }
static enum cliret do_client(struct conf *conf, enum action action, int vss_restore, int json) { enum cliret ret=CLIENT_OK; int rfd=-1; int resume=0; SSL *ssl=NULL; SSL_CTX *ctx=NULL; struct cntr *cntr=NULL; char *incexc=NULL; long name_max=0; enum action act=action; struct async *as=NULL; struct asfd *asfd=NULL; // as->settimers(0, 100); logp("begin client\n"); if(!(cntr=cntr_alloc()) || cntr_init(cntr, conf->cname)) goto error; conf->cntr=cntr; if(act!=ACTION_ESTIMATE && ssl_setup(&rfd, &ssl, &ctx, conf)) goto error; if(!(as=async_alloc()) || !(asfd=asfd_alloc()) || as->init(as, act==ACTION_ESTIMATE) || asfd->init(asfd, as, rfd, ssl, conf)) goto end; as->add_asfd(as, asfd); // Set quality of service bits on backup packets. if(act==ACTION_BACKUP || act==ACTION_BACKUP_TIMED || act==ACTION_TIMER_CHECK) as->asfd->set_bulk_packets(as->asfd); if(act!=ACTION_ESTIMATE) { if((ret=initial_comms(as, &act, &incexc, &name_max, conf))) goto end; } rfd=-1; switch(act) { case ACTION_BACKUP: ret=backup_wrapper(asfd, act, "backupphase1", incexc, resume, name_max, conf); break; case ACTION_BACKUP_TIMED: ret=backup_wrapper(asfd, act, "backupphase1timed", incexc, resume, name_max, conf); break; case ACTION_TIMER_CHECK: ret=backup_wrapper(asfd, act, "backupphase1timedcheck", incexc, resume, name_max, conf); break; case ACTION_RESTORE: case ACTION_VERIFY: ret=restore_wrapper(asfd, act, vss_restore, conf); break; case ACTION_ESTIMATE: if(do_backup_client(asfd, conf, act, name_max, 0)) goto error; break; case ACTION_DELETE: if(do_delete_client(asfd, conf)) goto error; break; case ACTION_LIST: case ACTION_LONG_LIST: default: if(do_list_client(asfd, conf, act, json)) goto error; break; } goto end; error: ret=CLIENT_ERROR; end: close_fd(&rfd); async_free(&as); asfd_free(&asfd); if(ctx) ssl_destroy_ctx(ctx); if(incexc) free(incexc); conf->cntr=NULL; if(cntr) cntr_free(&cntr); //logp("end client\n"); return ret; }
static int run_server(struct conf *conf, const char *conffile, int *rfd, const char *oldport, const char *oldstatusport) { int ret=0; SSL_CTX *ctx=NULL; int found_normal_child=0; if(!(ctx=ssl_initialise_ctx(conf))) { logp("error initialising ssl ctx\n"); return 1; } if((ssl_load_dh_params(ctx, conf))) { logp("error loading dh params\n"); return 1; } if(!oldport || strcmp(oldport, conf->port)) { close_fd(rfd); if((*rfd=init_listen_socket(conf->port, 1))<0) return 1; } if(conf->status_port && (!oldstatusport || strcmp(oldstatusport, conf->status_port))) { close_fd(&sfd); if((sfd=init_listen_socket(conf->status_port, 0))<0) return 1; } while(!hupreload) { int mfd=-1; fd_set fsr; fd_set fsw; fd_set fse; struct timeval tval; if(sigchld) { chld_check_for_exiting(); sigchld=0; } FD_ZERO(&fsr); FD_ZERO(&fse); tval.tv_sec=1; tval.tv_usec=0; add_fd_to_sets(*rfd, &fsr, NULL, &fse, &mfd); if(sfd>=0) add_fd_to_sets(sfd, &fsr, NULL, &fse, &mfd); // Add read fds of normal children. found_normal_child=chld_add_fd_to_normal_sets(conf, &fsr, &fse, &mfd); // Leave if we had a SIGUSR1 and there are no children // running. if(gentleshutdown) { if(!gentleshutdown_logged) { logp("got SIGUSR2 gentle reload signal\n"); logp("will shut down once children have exited\n"); gentleshutdown_logged++; } else if(!found_normal_child) { logp("all children have exited - shutting down\n"); break; } } if(select(mfd+1, &fsr, NULL, &fse, &tval)<0) { if(errno!=EAGAIN && errno!=EINTR) { logp("select error in normal part of %s: %s\n", __func__, strerror(errno)); ret=1; break; } } if(FD_ISSET(*rfd, &fse)) { // Happens when a client exits. //logp("error on listening socket.\n"); if(!conf->forking) { gentleshutdown++; break; } continue; } if((sfd>=0 && FD_ISSET(sfd, &fse))) { // Happens when a client exits. //logp("error on status socket.\n"); if(!conf->forking) { gentleshutdown++; break; } continue; } if(FD_ISSET(*rfd, &fsr)) { // A normal client is incoming. if(process_incoming_client(*rfd, conf, ctx, conffile, 0 /* not a status client */)) { ret=1; break; } if(!conf->forking) { gentleshutdown++; break; } } if(sfd>=0 && FD_ISSET(sfd, &fsr)) { // A status client is incoming. //printf("status client?\n"); if(process_incoming_client(sfd, conf, ctx, conffile, 1 /* a status client */)) { ret=1; break; } if(!conf->forking) { gentleshutdown++; break; } } if(chld_fd_isset_normal(conf, &fsr, &fse)) { ret=1; break; } // Have a separate select for writing to status server children mfd=-1; FD_ZERO(&fsw); FD_ZERO(&fse); if(!chld_add_fd_to_status_sets(conf, &fsw, &fse, &mfd)) { // Did not find any status server children. // No need to do the select. continue; } // Do not hang around - doing the status stuff is a lower // priority thing than dealing with normal clients. tval.tv_sec=0; tval.tv_usec=500; //printf("try status server\n"); if(select(mfd+1, NULL, &fsw, &fse, &tval)<0) { if(errno!=EAGAIN && errno!=EINTR) { logp("select error in status part of %s: %s\n", __func__, strerror(errno)); ret=1; break; } } if(chld_fd_isset_status(conf, &fsw, &fse)) { ret=1; break; } } if(hupreload) logp("got SIGHUP reload signal\n"); ssl_destroy_ctx(ctx); return ret; }