int extra_comms(struct async *as, struct conf **confs, enum action *action, char **incexc) { int ret=-1; char *feat=NULL; struct asfd *asfd; struct iobuf *rbuf; const char *orig_client=get_string(confs[OPT_ORIG_CLIENT]); asfd=as->asfd; rbuf=asfd->rbuf; if(asfd->write_str(asfd, 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. if(asfd->read(asfd)) { logp("Problem reading response to extra_comms_begin\n"); goto end; } feat=rbuf->buf; if(rbuf->cmd!=CMD_GEN || strncmp_w(feat, "extra_comms_begin ok")) { iobuf_log_unexpected(rbuf, __func__); goto end; } logp("%s\n", feat); iobuf_init(rbuf); // Can add extra bits here. The first extra bit is the // autoupgrade stuff. if(server_supports_autoupgrade(feat) && get_string(confs[OPT_AUTOUPGRADE_DIR]) && get_string(confs[OPT_AUTOUPGRADE_OS]) && autoupgrade_client(as, confs)) goto end; // :srestore: means that the server wants to do a restore. if(server_supports(feat, ":srestore:")) { if(get_int(confs[OPT_SERVER_CAN_RESTORE])) { logp("Server is initiating a restore\n"); if(incexc_recv_client_restore(asfd, incexc, confs)) goto end; if(*incexc) { if(conf_parse_incexcs_buf(confs, *incexc)) goto end; *action=ACTION_RESTORE; log_restore_settings(confs, 1); } } else { logp("Server wants to initiate a restore\n"); logp("Client configuration says no\n"); if(asfd->write_str(asfd, CMD_GEN, "srestore not ok")) goto end; } } if(orig_client) { char str[512]=""; snprintf(str, sizeof(str), "orig_client=%s", orig_client); if(!server_supports(feat, ":orig_client:")) { logp("Server does not support switching client.\n"); goto end; } if(asfd->write_str(asfd, CMD_GEN, str) || asfd->read_expect(asfd, CMD_GEN, "orig_client ok")) { logp("Problem requesting %s\n", str); goto end; } logp("Switched to client %s\n", orig_client); } // :sincexc: is for the server giving the client the // incexc config. if(*action==ACTION_BACKUP || *action==ACTION_BACKUP_TIMED || *action==ACTION_TIMER_CHECK) { if(!*incexc && server_supports(feat, ":sincexc:")) { logp("Server is setting includes/excludes.\n"); if(incexc_recv_client(asfd, incexc, confs)) goto end; if(*incexc && conf_parse_incexcs_buf(confs, *incexc)) goto end; } } if(server_supports(feat, ":counters:")) { if(asfd->write_str(asfd, CMD_GEN, "countersok")) goto end; set_int(confs[OPT_SEND_CLIENT_CNTR], 1); } // :incexc: is for the client sending the server the // incexc conf so that it better knows what to do on // resume. if(server_supports(feat, ":incexc:") && incexc_send_client(asfd, confs)) goto end; if(server_supports(feat, ":uname:")) { const char *clientos=NULL; #ifdef HAVE_WIN32 #ifdef _WIN64 clientos="Windows 64bit"; #else clientos="Windows 32bit"; #endif #else struct utsname utsname; if(!uname(&utsname)) clientos=(const char *)utsname.sysname; #endif if(clientos) { char msg[128]=""; snprintf(msg, sizeof(msg), "uname=%s", clientos); if(asfd->write_str(asfd, CMD_GEN, msg)) goto end; } } if(server_supports(feat, ":csetproto:")) { char msg[128]=""; // Use protocol2 if no choice has been made on client side. if(get_e_protocol(confs[OPT_PROTOCOL])==PROTO_AUTO) { logp("Server has protocol=0 (auto)\n"); set_e_protocol(confs[OPT_PROTOCOL], PROTO_2); } // Send choice to server. snprintf(msg, sizeof(msg), "protocol=%d", get_e_protocol(confs[OPT_PROTOCOL])); if(asfd->write_str(asfd, CMD_GEN, msg)) goto end; logp("Using protocol=%d\n", get_e_protocol(confs[OPT_PROTOCOL])); } else if(server_supports(feat, ":forceproto=1:")) { logp("Server is forcing protocol 1\n"); if(get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_AUTO && get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_1) { logp("But client has set protocol=%d!\n", get_e_protocol(confs[OPT_PROTOCOL])); goto end; } set_e_protocol(confs[OPT_PROTOCOL], PROTO_1); } else if(server_supports(feat, ":forceproto=2:")) { logp("Server is forcing protocol 2\n"); if(get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_AUTO && get_e_protocol(confs[OPT_PROTOCOL])!=PROTO_2) { logp("But client has set protocol=%d!\n", get_e_protocol(confs[OPT_PROTOCOL])); goto end; } set_e_protocol(confs[OPT_PROTOCOL], PROTO_2); } if(server_supports(feat, ":msg:")) { set_int(confs[OPT_MESSAGE], 1); if(asfd->write_str(asfd, CMD_GEN, "msg")) goto end; } #ifndef RS_DEFAULT_STRONG_LEN if(server_supports(feat, ":rshash=blake2:")) { set_e_rshash(confs[OPT_RSHASH], RSHASH_BLAKE2); // Send choice to server. if(asfd->write_str(asfd, CMD_GEN, "rshash=blake2")) goto end; } else #endif set_e_rshash(confs[OPT_RSHASH], RSHASH_MD4); if(asfd->write_str(asfd, CMD_GEN, "extra_comms_end") || asfd->read_expect(asfd, CMD_GEN, "extra_comms_end ok")) { logp("Problem requesting extra_comms_end\n"); goto end; } ret=0; end: return ret; }
/* 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; }
// a = length of struct bu array // i = position to restore from static int restore_manifest(struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, regex_t *regex, int srestore, enum action act, const char *client, char **dir_for_notify, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf) { int ret=0; gzFile zp=NULL; char *manifest=NULL; char *datadir=NULL; FILE *logfp=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. char status=STATUS_RESTORING; if(act==ACTION_RESTORE) status=STATUS_RESTORING; else if(act==ACTION_VERIFY) status=STATUS_VERIFYING; if( (act==ACTION_RESTORE && !(logpath=prepend_s(arr[i].path, "restorelog", strlen("restorelog")))) || (act==ACTION_RESTORE && !(logpathz=prepend_s(arr[i].path, "restorelog.gz", strlen("restorelog.gz")))) || (act==ACTION_VERIFY && !(logpath=prepend_s(arr[i].path, "verifylog", strlen("verifylog")))) || (act==ACTION_VERIFY && !(logpathz=prepend_s(arr[i].path, "verifylog.gz", strlen("verifylog.gz")))) || !(manifest=prepend_s(arr[i].path, "manifest.gz", strlen("manifest.gz")))) { log_and_send_oom(__FUNCTION__); ret=-1; } else if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp, cconf)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(msg); ret=-1; } *dir_for_notify=strdup(arr[i].path); log_restore_settings(cconf, srestore); // First, do a pass through the manifest to set up the counters. // This is the equivalent of a phase1 scan during backup. if(!ret && !(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { int ars=0; int quit=0; struct sbuf sb; init_sbuf(&sb); while(!quit) { if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if((!srestore || check_srestore(cconf, sb.path)) && check_regex(regex, sb.path)) { do_filecounter(p1cntr, sb.cmd, 0); if(sb.endfile) do_filecounter_bytes(p1cntr, strtoull(sb.endfile, NULL, 10)); /* if(sb.cmd==CMD_FILE || sb.cmd==CMD_ENC_FILE || sb.cmd==CMD_METADATA || sb.cmd==CMD_ENC_METADATA || sb.cmd==CMD_VSS || sb.cmd==CMD_ENC_VSS || sb.cmd==CMD_VSS_T || sb.cmd==CMD_ENC_VSS_T || sb.cmd==CMD_EFS_FILE) do_filecounter_bytes(p1cntr, (unsigned long long) sb.statp.st_size); */ } } free_sbuf(&sb); } free_sbuf(&sb); gzclose_fp(&zp); } if(cconf->send_client_counters) { if(send_counters(client, p1cntr, cntr)) { ret=-1; } } // Now, do the actual restore. if(!ret && !(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { char cmd; int s=0; int quit=0; size_t len=0; struct sbuf sb; // For out-of-sequence directory restoring so that the // timestamps come out right: int scount=0; struct sbuf **sblist=NULL; init_sbuf(&sb); while(!quit) { int ars=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) { logp("read quick error\n"); ret=-1; quit++; break; } if(buf) { //logp("got read quick\n"); if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; continue; } else if(cmd==CMD_INTERRUPT) { // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. free(buf); buf=NULL; continue; } else { logp("unexpected cmd from client: %c:%s\n", cmd, buf); free(buf); buf=NULL; ret=-1; quit++; break; } } if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if((!srestore || check_srestore(cconf, sb.path)) && check_regex(regex, sb.path) && restore_ent(client, &sb, &sblist, &scount, arr, a, i, tmppath1, tmppath2, act, status, cconf, cntr, p1cntr)) { ret=-1; quit++; } } free_sbuf(&sb); } gzclose_fp(&zp); // Restore any directories that are left in the list. if(!ret) for(s=scount-1; s>=0; s--) { if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } } free_sbufs(sblist, scount); if(!ret) ret=do_restore_end(act, cntr); //print_endcounter(cntr); print_filecounters(p1cntr, cntr, act); reset_filecounter(p1cntr, time(NULL)); reset_filecounter(cntr, time(NULL)); } set_logfp(NULL, cconf); compress_file(logpath, logpathz, cconf); if(manifest) free(manifest); if(datadir) free(datadir); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }
static int restore_manifest(struct asfd *asfd, struct bu *bu, regex_t *regex, int srestore, enum action act, struct sdirs *sdirs, char **dir_for_notify, struct conf **cconfs) { int ret=-1; char *manifest=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. enum cntr_status cntr_status=CNTR_STATUS_RESTORING; if(act==ACTION_RESTORE) cntr_status=CNTR_STATUS_RESTORING; else if(act==ACTION_VERIFY) cntr_status=CNTR_STATUS_VERIFYING; if((act==ACTION_RESTORE && get_logpaths(bu, "restorelog", &logpath, &logpathz)) || (act==ACTION_VERIFY && get_logpaths(bu, "verifylog", &logpath, &logpathz)) || !(manifest=prepend_s(bu->path, get_protocol(cconfs)==PROTO_1? "manifest.gz":"manifest"))) { log_and_send_oom(asfd, __func__); goto end; } if(log_fzp_set(logpath, cconfs)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(asfd, msg); goto end; } *dir_for_notify=strdup_w(bu->path, __func__); log_restore_settings(cconfs, srestore); // First, do a pass through the manifest to set up cntr. // This is the equivalent of a phase1 scan during backup. if(setup_cntr(asfd, manifest, regex, srestore, act, cntr_status, cconfs)) goto end; if(get_int(cconfs[OPT_SEND_CLIENT_CNTR]) && cntr_send(get_cntr(cconfs))) goto end; // Now, do the actual restore. ret=actual_restore(asfd, bu, manifest, regex, srestore, act, sdirs, cntr_status, cconfs); end: log_fzp_set(NULL, cconfs); compress_file(logpath, logpathz, get_int(cconfs[OPT_COMPRESSION])); if(manifest) free(manifest); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }