/* * Inititiate the communications with the Director. * He has made a connection to our server. * * Basic tasks done here: * We read Director's initial message and authorize him. * */ int authenticate_director(JCR *jcr) { BSOCK *dir = jcr->dir_bsock; if (!authenticate(R_DIRECTOR, dir, jcr)) { bnet_fsend(dir, "%s", Dir_sorry); Emsg0(M_FATAL, 0, _("Unable to authenticate Director\n")); return 0; } return bnet_fsend(dir, "%s", OK_hello); }
/* * We get the slot list from the Storage daemon. * If scan is set, we return all slots found, * otherwise, we return only slots with valid barcodes (Volume names) */ static vol_list_t *get_vol_list_from_SD(UAContext *ua, bool scan) { STORE *store = ua->jcr->wstore; char dev_name[MAX_NAME_LENGTH]; BSOCK *sd; vol_list_t *vl; vol_list_t *vol_list = NULL; if (!(sd=open_sd_bsock(ua))) { return NULL; } bstrncpy(dev_name, store->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); /* Ask for autochanger list of volumes */ bnet_fsend(sd, NT_("autochanger list %s \n"), dev_name); /* Read and organize list of Volumes */ while (bnet_recv(sd) >= 0) { char *p; int Slot; strip_trailing_junk(sd->msg); /* Check for returned SD messages */ if (sd->msg[0] == '3' && B_ISDIGIT(sd->msg[1]) && B_ISDIGIT(sd->msg[2]) && B_ISDIGIT(sd->msg[3]) && sd->msg[4] == ' ') { ua->send_msg("%s\n", sd->msg); /* pass them on to user */ continue; } /* Validate Slot: if scanning, otherwise Slot:Barcode */ p = strchr(sd->msg, ':'); if (scan && p) { /* Scanning -- require only valid slot */ Slot = atoi(sd->msg); if (Slot <= 0) { p--; *p = ':'; ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg); continue; } } else { /* Not scanning */ if (p && strlen(p) > 1) { *p++ = 0; if (!is_an_integer(sd->msg) || (Slot=atoi(sd->msg)) <= 0) { p--; *p = ':'; ua->error_msg(_("Invalid Slot number: %s\n"), sd->msg); continue; } } else { continue; } if (!is_volume_name_legal(ua, p)) { p--; *p = ':'; ua->error_msg(_("Invalid Volume name: %s\n"), sd->msg); continue; } } /* Add Slot and VolumeName to list */ vl = (vol_list_t *)malloc(sizeof(vol_list_t)); vl->Slot = Slot; if (p) { if (*p == ':') { p++; /* skip separator */ } vl->VolName = bstrdup(p); } else { vl->VolName = NULL; } Dmsg2(100, "Add slot=%d Vol=%s to SD list.\n", vl->Slot, NPRT(vl->VolName)); if (!vol_list) { vl->next = vol_list; vol_list = vl; } else { /* Add new entry to end of list */ for (vol_list_t *tvl=vol_list; tvl; tvl=tvl->next) { if (!tvl->next) { tvl->next = vl; vl->next = NULL; break; } } } } close_sd_bsock(ua); return vol_list; }
/* * Do a verification of the specified files against the Catlaog * * Returns: false on failure * true on success */ bool do_verify(JCR *jcr) { const char *level; BSOCK *fd; int stat; char ed1[100]; JOB_DBR jr; JobId_t verify_jobid = 0; const char *Name; free_wstorage(jcr); /* we don't write */ memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); /* * Find JobId of last job that ran. Note, we do this when * the job actually starts running, not at schedule time, * so that we find the last job that terminated before * this job runs rather than before it is scheduled. This * permits scheduling a Backup and Verify at the same time, * but with the Verify at a lower priority. * * For VERIFY_CATALOG we want the JobId of the last INIT. * For VERIFY_VOLUME_TO_CATALOG, we want the JobId of the * last backup Job. */ if (jcr->get_JobLevel() == L_VERIFY_CATALOG || jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) { memcpy(&jr, &jcr->jr, sizeof(jr)); if (jcr->verify_job && (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG)) { Name = jcr->verify_job->name(); } else { Name = NULL; } Dmsg1(100, "find last jobid for: %s\n", NPRT(Name)); if (!db_find_last_jobid(jcr, jcr->db, Name, &jr)) { if (jcr->get_JobLevel() == L_VERIFY_CATALOG) { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous InitCatalog Job.\n" "Please run a Verify with Level=InitCatalog before\n" "running the current Job.\n")); } else { Jmsg(jcr, M_FATAL, 0, _( "Unable to find JobId of previous Job for this client.\n")); } return false; } verify_jobid = jr.JobId; Dmsg1(100, "Last full jobid=%d\n", verify_jobid); } /* * Now get the job record for the previous backup that interests * us. We use the verify_jobid that we found above. */ if (jcr->get_JobLevel() == L_VERIFY_CATALOG || jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG || jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG) { jcr->previous_jr.JobId = verify_jobid; if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { Jmsg(jcr, M_FATAL, 0, _("Could not get job record for previous Job. ERR=%s"), db_strerror(jcr->db)); return false; } if (!(jcr->previous_jr.JobStatus == JS_Terminated || jcr->previous_jr.JobStatus == JS_Warnings)) { Jmsg(jcr, M_FATAL, 0, _("Last Job %d did not terminate normally. JobStatus=%c\n"), verify_jobid, jcr->previous_jr.JobStatus); return false; } Jmsg(jcr, M_INFO, 0, _("Verifying against JobId=%d Job=%s\n"), jcr->previous_jr.JobId, jcr->previous_jr.Job); } /* * If we are verifying a Volume, we need the Storage * daemon, so open a connection, otherwise, just * create a dummy authorization key (passed to * File daemon but not used). */ if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) { if (!create_restore_bootstrap_file(jcr)) { return false; } } else { jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */ } if (jcr->get_JobLevel() == L_VERIFY_DISK_TO_CATALOG && jcr->verify_job) { jcr->fileset = jcr->verify_job->fileset; } Dmsg2(100, "ClientId=%u JobLevel=%c\n", jcr->previous_jr.ClientId, jcr->get_JobLevel()); if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Verify JobId=%s Level=%s Job=%s\n"), edit_uint64(jcr->JobId, ed1), level_to_str(jcr->get_JobLevel()), jcr->Job); if (jcr->get_JobLevel() == L_VERIFY_VOLUME_TO_CATALOG) { /* * Start conversation with Storage daemon */ set_jcr_job_status(jcr, JS_Blocked); if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) { return false; } /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->rstorage, NULL)) { return false; } if (!jcr->store_bsock->fsend("run")) { return false; } /* * Now start a Storage daemon message thread */ if (!start_storage_daemon_message_thread(jcr)) { return false; } Dmsg0(50, "Storage daemon connection OK\n"); } /* * OK, now connect to the File daemon * and ask him for the files. */ set_jcr_job_status(jcr, JS_Blocked); if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) { goto bail_out; } set_jcr_job_status(jcr, JS_Running); fd = jcr->file_bsock; Dmsg0(30, ">filed: Send include list\n"); if (!send_include_list(jcr)) { goto bail_out; } Dmsg0(30, ">filed: Send exclude list\n"); if (!send_exclude_list(jcr)) { goto bail_out; } /* * Send Level command to File daemon, as well * as the Storage address if appropriate. */ switch (jcr->get_JobLevel()) { case L_VERIFY_INIT: level = "init"; break; case L_VERIFY_CATALOG: level = "catalog"; break; case L_VERIFY_VOLUME_TO_CATALOG: /* * send Storage daemon address to the File daemon */ if (jcr->rstore->SDDport == 0) { jcr->rstore->SDDport = jcr->rstore->SDport; } bnet_fsend(fd, storaddr, jcr->rstore->address, jcr->rstore->SDDport); if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) { goto bail_out; } /* * Send the bootstrap file -- what Volumes/files to restore */ if (!send_bootstrap_file(jcr, fd) || !response(jcr, fd, OKbootstrap, "Bootstrap", DISPLAY_ERROR)) { goto bail_out; } if (!jcr->RestoreBootstrap) { Jmsg0(jcr, M_FATAL, 0, _("Deprecated feature ... use bootstrap.\n")); goto bail_out; } level = "volume"; break; case L_VERIFY_DATA: level = "data"; break; case L_VERIFY_DISK_TO_CATALOG: level="disk_to_catalog"; break; default: Jmsg2(jcr, M_FATAL, 0, _("Unimplemented Verify level %d(%c)\n"), jcr->get_JobLevel(), jcr->get_JobLevel()); goto bail_out; } if (!send_runscripts_commands(jcr)) { goto bail_out; } /* * Send verify command/level to File daemon */ fd->fsend(verifycmd, level); if (!response(jcr, fd, OKverify, "Verify", DISPLAY_ERROR)) { goto bail_out; } /* * Now get data back from File daemon and * compare it to the catalog or store it in the * catalog depending on the run type. */ /* Compare to catalog */ switch (jcr->get_JobLevel()) { case L_VERIFY_CATALOG: Dmsg0(10, "Verify level=catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_VOLUME_TO_CATALOG: Dmsg0(10, "Verify level=volume\n"); get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_DISK_TO_CATALOG: Dmsg0(10, "Verify level=disk_to_catalog\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_compare_to_catalog(jcr, jcr->previous_jr.JobId); break; case L_VERIFY_INIT: /* Build catalog */ Dmsg0(10, "Verify level=init\n"); jcr->sd_msg_thread_done = true; /* no SD msg thread, so it is done */ jcr->SDJobStatus = JS_Terminated; get_attributes_and_put_in_catalog(jcr); db_end_transaction(jcr, jcr->db); /* terminate any open transaction */ db_write_batch_file_records(jcr); break; default: Jmsg1(jcr, M_FATAL, 0, _("Unimplemented verify level %d\n"), jcr->get_JobLevel()); goto bail_out; } stat = wait_for_job_termination(jcr); verify_cleanup(jcr, stat); return true; bail_out: return false; }
/* * Common routine for both label and relabel */ static int do_label(UAContext *ua, const char *cmd, int relabel) { USTORE store; BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; MEDIA_DBR mr, omr; POOL_DBR pr; bool print_reminder = true; bool label_barcodes = false; int ok = FALSE; int i, j; int drive; bool media_record_exists = false; static const char *barcode_keyword[] = { "barcode", "barcodes", NULL}; memset(&pr, 0, sizeof(pr)); if (!open_client_db(ua)) { return 1; } /* Look for one of the barcode keywords */ if (!relabel && (i=find_arg_keyword(ua, barcode_keyword)) >= 0) { /* Now find the keyword in the list */ if ((j = find_arg(ua, barcode_keyword[i])) > 0) { *ua->argk[j] = 0; /* zap barcode keyword */ } label_barcodes = true; } store.store = get_storage_resource(ua, true/*use default*/); if (!store.store) { return 1; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); if (label_barcodes) { label_from_barcodes(ua, drive); return 1; } /* If relabel get name of Volume to relabel */ if (relabel) { /* Check for oldvolume=name */ i = find_arg_with_value(ua, "oldvolume"); if (i >= 0) { memset(&omr, 0, sizeof(omr)); bstrncpy(omr.VolumeName, ua->argv[i], sizeof(omr.VolumeName)); if (db_get_media_record(ua->jcr, ua->db, &omr)) { goto checkVol; } ua->error_msg("%s", db_strerror(ua->db)); } /* No keyword or Vol not found, ask user to select */ if (!select_media_dbr(ua, &omr)) { return 1; } /* Require Volume to be Purged or Recycled */ checkVol: if (strcmp(omr.VolStatus, "Purged") != 0 && strcmp(omr.VolStatus, "Recycle") != 0) { ua->error_msg(_("Volume \"%s\" has VolStatus %s. It must be Purged or Recycled before relabeling.\n"), omr.VolumeName, omr.VolStatus); return 1; } } /* Check for volume=NewVolume */ i = find_arg_with_value(ua, "volume"); if (i >= 0) { pm_strcpy(ua->cmd, ua->argv[i]); goto checkName; } /* Get a new Volume name */ for ( ;; ) { media_record_exists = false; if (!get_cmd(ua, _("Enter new Volume name: "))) { return 1; } checkName: if (!is_volume_name_legal(ua, ua->cmd)) { continue; } memset(&mr, 0, sizeof(mr)); bstrncpy(mr.VolumeName, ua->cmd, sizeof(mr.VolumeName)); /* If VolBytes are zero the Volume is not labeled */ if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.VolBytes != 0) { ua->error_msg(_("Media record for new Volume \"%s\" already exists.\n"), mr.VolumeName); continue; } media_record_exists = true; } break; /* Got it */ } /* If autochanger, request slot */ i = find_arg_with_value(ua, "slot"); if (i >= 0) { mr.Slot = atoi(ua->argv[i]); if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } else if (store.store->autochanger) { if (!get_pint(ua, _("Enter slot (0 or Enter for none): "))) { return 1; } mr.Slot = ua->pint32_val; if (mr.Slot < 0) { mr.Slot = 0; } mr.InChanger = mr.Slot > 0; /* if slot give assume in changer */ } mr.StorageId = store.store->StorageId; bstrncpy(mr.MediaType, store.store->media_type, sizeof(mr.MediaType)); /* Must select Pool if not already done */ if (pr.PoolId == 0) { memset(&pr, 0, sizeof(pr)); if (!select_pool_dbr(ua, &pr)) { return 1; } } ok = send_label_request(ua, &mr, &omr, &pr, relabel, media_record_exists, drive); if (ok) { sd = ua->jcr->store_bsock; if (relabel) { /* Delete the old media record */ if (!db_delete_media_record(ua->jcr, ua->db, &omr)) { ua->error_msg(_("Delete of Volume \"%s\" failed. ERR=%s"), omr.VolumeName, db_strerror(ua->db)); } else { ua->info_msg(_("Old volume \"%s\" deleted from catalog.\n"), omr.VolumeName); /* Update the number of Volumes in the pool */ pr.NumVols--; if (!db_update_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } } if (ua->automount) { bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name)); ua->info_msg(_("Requesting to mount %s ...\n"), dev_name); bash_spaces(dev_name); bnet_fsend(sd, "mount %s drive=%d", dev_name, drive); unbash_spaces(dev_name); while (bnet_recv(sd) >= 0) { ua->send_msg("%s", sd->msg); /* Here we can get * 3001 OK mount. Device=xxx or * 3001 Mounted Volume vvvv * 3002 Device "DVD-Writer" (/dev/hdc) is mounted. * 3906 is cannot mount non-tape * So for those, no need to print a reminder */ if (strncmp(sd->msg, "3001 ", 5) == 0 || strncmp(sd->msg, "3002 ", 5) == 0 || strncmp(sd->msg, "3906 ", 5) == 0) { print_reminder = false; } } } } if (print_reminder) { ua->info_msg(_("Do not forget to mount the drive!!!\n")); } close_sd_bsock(ua); return 1; }
/* * Do a backup of the specified FileSet * * Returns: false on failure * true on success */ bool do_backup(JCR *jcr) { int stat; int tls_need = BNET_TLS_NONE; BSOCK *fd; STORE *store; char ed1[100]; if (jcr->get_JobLevel() == L_VIRTUAL_FULL) { return do_vbackup(jcr); } /* Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Backup JobId %s, Job=%s\n"), edit_uint64(jcr->JobId, ed1), jcr->Job); set_jcr_job_status(jcr, JS_Running); Dmsg2(100, "JobId=%d JobLevel=%c\n", jcr->jr.JobId, jcr->jr.JobLevel); if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* * Open a message channel connection with the Storage * daemon. This is to let him know that our client * will be contacting him for a backup session. * */ Dmsg0(110, "Open connection with storage daemon\n"); set_jcr_job_status(jcr, JS_WaitSD); /* * Start conversation with Storage daemon */ if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) { return false; } /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, NULL, jcr->wstorage)) { return false; } /* * Start the job prior to starting the message thread below * to avoid two threads from using the BSOCK structure at * the same time. */ if (!bnet_fsend(jcr->store_bsock, "run")) { return false; } /* * Now start a Storage daemon message thread. Note, * this thread is used to provide the catalog services * for the backup job, including inserting the attributes * into the catalog. See catalog_update() in catreq.c */ if (!start_storage_daemon_message_thread(jcr)) { return false; } Dmsg0(150, "Storage daemon connection OK\n"); set_jcr_job_status(jcr, JS_WaitFD); if (!connect_to_file_daemon(jcr, 10, FDConnectTimeout, 1)) { goto bail_out; } set_jcr_job_status(jcr, JS_Running); fd = jcr->file_bsock; if (!send_include_list(jcr)) { goto bail_out; } if (!send_exclude_list(jcr)) { goto bail_out; } if (!send_level_command(jcr)) { goto bail_out; } /* * send Storage daemon address to the File daemon */ store = jcr->wstore; if (store->SDDport == 0) { store->SDDport = store->SDport; } /* TLS Requirement */ if (store->tls_enable) { if (store->tls_require) { tls_need = BNET_TLS_REQUIRED; } else { tls_need = BNET_TLS_OK; } } fd->fsend(storaddr, store->address, store->SDDport, tls_need); if (!response(jcr, fd, OKstore, "Storage", DISPLAY_ERROR)) { goto bail_out; } if (!send_runscripts_commands(jcr)) { goto bail_out; } /* * We re-update the job start record so that the start * time is set after the run before job. This avoids * that any files created by the run before job will * be saved twice. They will be backed up in the current * job, but not in the next one unless they are changed. * Without this, they will be backed up in this job and * in the next job run because in that case, their date * is after the start of this run. */ jcr->start_time = time(NULL); jcr->jr.StartTime = jcr->start_time; if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); } /* * If backup is in accurate mode, we send the list of * all files to FD. */ if (!send_accurate_current_files(jcr)) { goto bail_out; } /* Send backup command */ fd->fsend(backupcmd); if (!response(jcr, fd, OKbackup, "backup", DISPLAY_ERROR)) { goto bail_out; } /* Pickup Job termination data */ stat = wait_for_job_termination(jcr); db_write_batch_file_records(jcr); /* used by bulk batch file insert */ if (stat == JS_Terminated) { backup_cleanup(jcr, stat); return true; } return false; /* Come here only after starting SD thread */ bail_out: set_jcr_job_status(jcr, JS_ErrorTerminated); Dmsg1(400, "wait for sd. use=%d\n", jcr->use_count()); /* Cancel SD */ wait_for_job_termination(jcr, FDConnectTimeout); Dmsg1(400, "after wait for sd. use=%d\n", jcr->use_count()); return false; }