/* * Called here to send the appropriate commands to the SD * to do truncate on purge. */ static void do_truncate_on_purge(UAContext *ua, MEDIA_DBR *mr, char *pool, char *storage, int drive, BSOCK *sd) { bool ok=false; uint64_t VolBytes = 0; /* TODO: Return if not mr->Recyle ? */ if (!mr->Recycle) { return; } /* Do it only if action on purge = truncate is set */ if (!(mr->ActionOnPurge & ON_PURGE_TRUNCATE)) { return; } /* * Send the command to truncate the volume after purge. If this feature * is disabled for the specific device, this will be a no-op. */ /* Protect us from spaces */ bash_spaces(mr->VolumeName); bash_spaces(mr->MediaType); bash_spaces(pool); bash_spaces(storage); /* Do it by relabeling the Volume, which truncates it */ sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s " "MediaType=%s Slot=%d drive=%d\n", storage, mr->VolumeName, mr->VolumeName, pool, mr->MediaType, mr->Slot, drive); unbash_spaces(mr->VolumeName); unbash_spaces(mr->MediaType); unbash_spaces(pool); unbash_spaces(storage); /* Send relabel command, and check for valid response */ while (sd->recv() >= 0) { ua->send_msg("%s", sd->msg); if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu ", &VolBytes) == 1) { ok = true; } } if (ok) { mr->VolBytes = VolBytes; mr->VolFiles = 0; set_storageid_in_mr(NULL, mr); if (!db_update_media_record(ua->jcr, ua->db, mr)) { ua->error_msg(_("Can't update volume size in the catalog\n")); } ua->send_msg(_("The volume \"%s\" has been truncated\n"), mr->VolumeName); } else { ua->warning_msg(_("Unable to truncate volume \"%s\"\n"), mr->VolumeName); } }
/* * Query Device command from Director * Sends Storage Daemon's information on the device to the * caller (presumably the Director). * This command always returns "true" so that the line is * not closed on an error. * */ bool query_cmd(JCR *jcr) { POOL_MEM dev_name, VolumeName, MediaType, ChangerName; BSOCK *dir = jcr->dir_bsock; DEVRES *device; AUTOCHANGER *changer; bool ok; Dmsg1(100, "Query_cmd: %s", dir->msg); ok = sscanf(dir->msg, query_device, dev_name.c_str()) == 1; Dmsg1(100, "<dird: %s", dir->msg); if (ok) { unbash_spaces(dev_name); foreach_res(device, R_DEVICE) { /* Find resource, and make sure we were able to open it */ if (bstrcmp(dev_name.c_str(), device->name())) { if (!device->dev) { device->dev = init_dev(jcr, device); } if (!device->dev) { break; } ok = dir_update_device(jcr, device->dev); if (ok) { ok = dir->fsend(OK_query); } else { dir->fsend(NO_query); } return ok; } } foreach_res(changer, R_AUTOCHANGER) { /*Find resource, and make sure we were able to open it */ if (bstrcmp(dev_name.c_str(), changer->name())) { if (!changer->device || changer->device->size() == 0) { continue; /* no devices */ } ok = dir_update_changer(jcr, changer); if (ok) { ok = dir->fsend(OK_query); } else { dir->fsend(NO_query); } return ok; } } /* If we get here, the device/autochanger was not found */ unbash_spaces(dir->msg); pm_strcpy(jcr->errmsg, dir->msg); dir->fsend(NO_device, dev_name.c_str()); Dmsg1(100, ">dird: %s", dir->msg); } else {
static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr) { int status; char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50]; jcr->MediaId = mr->MediaId; pm_strcpy(jcr->VolumeName, mr->VolumeName); bash_spaces(mr->VolumeName); status = sd->fsend(OK_media, mr->VolumeName, mr->VolJobs, mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1), mr->VolMounts, mr->VolErrors, mr->VolWrites, edit_uint64(mr->MaxVolBytes, ed2), edit_uint64(mr->VolCapacityBytes, ed3), mr->VolStatus, mr->Slot, mr->MaxVolJobs, mr->MaxVolFiles, mr->InChanger, edit_int64(mr->VolReadTime, ed4), edit_int64(mr->VolWriteTime, ed5), mr->EndFile, mr->EndBlock, mr->LabelType, edit_uint64(mr->MediaId, ed6), mr->EncrKey, mr->MinBlocksize, mr->MaxBlocksize); unbash_spaces(mr->VolumeName); Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg); return status; }
/** * Get Volume info for a specific volume from the Director's Database * * Returns: true on success (Director guarantees that Pool and MediaType * are correct and VolStatus==Append or * VolStatus==Recycle) * false on failure * * Volume information returned in dcr->VolCatInfo */ bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing) { JCR *jcr = dcr->jcr; BSOCK *dir = jcr->dir_bsock; P(vol_info_mutex); dcr->setVolCatName(dcr->VolumeName); bash_spaces(dcr->getVolCatName()); dir->fsend(Get_Vol_Info, jcr->Job, dcr->getVolCatName(), writing==GET_VOL_INFO_FOR_WRITE?1:0); Dmsg1(dbglvl, ">dird %s", dir->msg); unbash_spaces(dcr->getVolCatName()); bool ok = do_get_volume_info(dcr); V(vol_info_mutex); return ok; }
/** * Get Volume info for a specific volume from the Director's Database * * Returns: true on success (Director guarantees that Pool and MediaType * are correct and VolStatus==Append or * VolStatus==Recycle) * false on failure * * Volume information returned in dcr->VolCatInfo */ bool SD_DCR::dir_get_volume_info(enum get_vol_info_rw writing) { bool ok; BSOCK *dir = jcr->dir_bsock; P(vol_info_mutex); setVolCatName(VolumeName); bash_spaces(getVolCatName()); dir->fsend(Get_Vol_Info, jcr->Job, getVolCatName(), (writing == GET_VOL_INFO_FOR_WRITE) ? 1 : 0); Dmsg1(dbglvl, ">dird %s", dir->msg); unbash_spaces(getVolCatName()); ok = do_get_volume_info(this); V(vol_info_mutex); return ok; }
static void build_restore_command(JCR *jcr, POOL_MEM &ret) { char replace, *where, *cmd; char empty = '\0'; /* * Build the restore command */ if (jcr->replace != 0) { replace = jcr->replace; } else if (jcr->res.job->replace != 0) { replace = jcr->res.job->replace; } else { replace = REPLACE_ALWAYS; /* always replace */ } if (jcr->RegexWhere) { where = jcr->RegexWhere; /* override */ cmd = restorecmdR; } else if (jcr->res.job->RegexWhere) { where = jcr->res.job->RegexWhere; /* no override take from job */ cmd = restorecmdR; } else if (jcr->where) { where = jcr->where; /* override */ cmd = restorecmd; } else if (jcr->res.job->RestoreWhere) { where = jcr->res.job->RestoreWhere; /* no override take from job */ cmd = restorecmd; } else { /* nothing was specified */ where = ∅ /* use default */ cmd = restorecmd; } jcr->prefix_links = jcr->res.job->PrefixLinks; bash_spaces(where); Mmsg(ret, cmd, replace, jcr->prefix_links, where); unbash_spaces(where); }
/* * See who is connecting and lookup the authentication information. * First make him prove his identity and then prove our identity to the Remote daemon. */ static inline bool two_way_authenticate(int rcode, BSOCK *bs, JCR* jcr) { POOLMEM *dirname = get_pool_memory(PM_MESSAGE); DIRRES *director = NULL; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool compatible = true; /* Want md5 compatible DIR */ bool auth_success = false; alist *verify_list = NULL; btimer_t *tid = NULL; if (rcode != R_DIRECTOR) { Dmsg1(dbglvl, "I only authenticate directors, not %d\n", rcode); Jmsg1(jcr, M_FATAL, 0, _("I only authenticate directors, not %d\n"), rcode); goto auth_fatal; } if (bs->msglen < 25 || bs->msglen > 500) { Dmsg2(dbglvl, "Bad Hello command from Director at %s. Len=%d.\n", bs->who(), bs->msglen); char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s. Len=%d.\n"), who, bs->msglen); goto auth_fatal; } dirname = check_pool_memory_size(dirname, bs->msglen); if (sscanf(bs->msg, "Hello Director %s calling", dirname) != 1) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; bs->msg[100] = 0; Dmsg2(dbglvl, "Bad Hello command from Director at %s: %s\n", bs->who(), bs->msg); Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s: %s\n"), who, bs->msg); goto auth_fatal; } unbash_spaces(dirname); foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, dirname)) break; } if (!director) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Jmsg2(jcr, M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"), dirname, who); goto auth_fatal; } if (have_tls) { /* * TLS Requirement */ if (director->tls_enable) { if (director->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (director->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (director->tls_verify_peer) { verify_list = director->tls_allowed_cns; } } /* * Timeout Hello after 10 min */ tid = start_bsock_timer(bs, AUTH_TIMEOUT); /* * Sanity check. */ ASSERT(director->password.encoding == p_encoding_md5); /* * Challenge the director */ auth_success = cram_md5_challenge(bs, director->password.value, tls_local_need, compatible); if (job_canceled(jcr)) { auth_success = false; goto auth_fatal; /* quick exit */ } if (auth_success) { auth_success = cram_md5_respond(bs, director->password.value, &tls_remote_need, &compatible); if (!auth_success) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Dmsg1(dbglvl, "cram_get_auth failed for %s\n", who); } } else { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Dmsg1(dbglvl, "cram_auth failed for %s\n", who); } if (!auth_success) { Emsg1(M_FATAL, 0, _("Incorrect password given by Director at %s.\n"), bs->who()); goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* * Engage TLS! Full Speed Ahead! */ if (!bnet_tls_server(director->tls_ctx, bs, verify_list)) { Jmsg0(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_fatal; } if (director->tls_authenticate) { /* authentication only? */ bs->free_tls(); /* shutodown tls */ } } auth_fatal: if (tid) { stop_bsock_timer(tid); tid = NULL; } free_pool_memory(dirname); jcr->director = director; /* * Single thread all failures to avoid DOS */ if (!auth_success) { P(mutex); bmicrosleep(6, 0); V(mutex); } return auth_success; }
/** * Get info on the next appendable volume in the Director's database * * Returns: true on success dcr->VolumeName is volume * reserve_volume() called on Volume name * false on failure dcr->VolumeName[0] == 0 * also sets dcr->found_in_use if at least one * in use volume was found. * * Volume information returned in dcr * */ bool dir_find_next_appendable_volume(DCR *dcr) { JCR *jcr = dcr->jcr; BSOCK *dir = jcr->dir_bsock; bool rtn; char lastVolume[MAX_NAME_LENGTH]; Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n", dcr->is_reserved(), dcr->VolumeName); /* * Try the twenty oldest or most available volumes. Note, * the most available could already be mounted on another * drive, so we continue looking for a not in use Volume. */ lock_volumes(); P(vol_info_mutex); dcr->clear_found_in_use(); lastVolume[0] = 0; for (int vol_index=1; vol_index < 20; vol_index++) { bash_spaces(dcr->media_type); bash_spaces(dcr->pool_name); dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type); unbash_spaces(dcr->media_type); unbash_spaces(dcr->pool_name); Dmsg1(dbglvl, ">dird %s", dir->msg); if (do_get_volume_info(dcr)) { /* Give up if we get the same volume name twice */ if (lastVolume[0] && bstrcmp(lastVolume, dcr->VolumeName)) { Dmsg1(dbglvl, "Got same vol = %s\n", lastVolume); break; } bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume)); if (dcr->can_i_write_volume()) { Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", dcr->VolumeName); if (reserve_volume(dcr, dcr->VolumeName) == NULL) { Dmsg2(dbglvl, "Could not reserve volume %s on %s\n", dcr->VolumeName, dcr->dev->print_name()); continue; } Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n", dcr->VolumeName); rtn = true; goto get_out; } else { Dmsg1(dbglvl, "Volume %s is in use.\n", dcr->VolumeName); /* If volume is not usable, it is in use by someone else */ dcr->set_found_in_use(); continue; } } Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index, dcr->dev->print_name()); break; } rtn = false; dcr->VolumeName[0] = 0; get_out: V(vol_info_mutex); unlock_volumes(); return rtn; }
/* * 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; }
/** * Common routine for: * dir_get_volume_info() * and * dir_find_next_appendable_volume() * * NOTE!!! All calls to this routine must be protected by * locking vol_info_mutex before calling it so that * we don't have one thread modifying the parameters * and another reading them. * * Returns: true on success and vol info in dcr->VolCatInfo * false on failure */ static bool do_get_volume_info(DCR *dcr) { JCR *jcr = dcr->jcr; BSOCK *dir = jcr->dir_bsock; VOLUME_CAT_INFO vol; int n; int32_t InChanger; dcr->setVolCatInfo(false); if (dir->recv() <= 0) { Dmsg0(dbglvl, "getvolname error bnet_recv\n"); Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n")); return false; } memset(&vol, 0, sizeof(vol)); Dmsg1(dbglvl, "<dird %s", dir->msg); n = sscanf(dir->msg, OK_media, vol.VolCatName, &vol.VolCatJobs, &vol.VolCatFiles, &vol.VolCatBlocks, &vol.VolCatBytes, &vol.VolCatMounts, &vol.VolCatErrors, &vol.VolCatWrites, &vol.VolCatMaxBytes, &vol.VolCatCapacityBytes, vol.VolCatStatus, &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles, &InChanger, &vol.VolReadTime, &vol.VolWriteTime, &vol.EndFile, &vol.EndBlock, &vol.LabelType, &vol.VolMediaId, vol.VolEncrKey, &vol.VolMinBlocksize, &vol.VolMaxBlocksize); if (n != 24) { Dmsg3(dbglvl, "Bad response from Dir fields=%d, len=%d: %s", n, dir->msglen, dir->msg); Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg); return false; } vol.InChanger = InChanger; /* bool in structure */ vol.is_valid = true; unbash_spaces(vol.VolCatName); bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName)); dcr->VolCatInfo = vol; /* structure assignment */ /* * If we received a new crypto key update the cache and write out the new cache on a change. */ if (*vol.VolEncrKey) { if (update_crypto_cache(vol.VolCatName, vol.VolEncrKey)) { write_crypto_cache(me->working_directory, "bareos-sd", get_first_port_host_order(me->SDaddrs)); } } Dmsg4(dbglvl, "do_get_volume_info return true slot=%d Volume=%s, " "VolminBlocksize=%u VolMaxBlocksize=%u\n", vol.Slot, vol.VolCatName, vol.VolMinBlocksize, vol.VolMaxBlocksize); Dmsg2(dbglvl, "setting dcr->VolMinBlocksize(%u) to vol.VolMinBlocksize(%u)\n", dcr->VolMinBlocksize, vol.VolMinBlocksize); Dmsg2(dbglvl, "setting dcr->VolMaxBlocksize(%u) to vol.VolMaxBlocksize(%u)\n", dcr->VolMaxBlocksize, vol.VolMaxBlocksize); /* * Assign the volcatinfo to the dcr. */ dcr->VolMinBlocksize = vol.VolMinBlocksize; dcr->VolMaxBlocksize = vol.VolMaxBlocksize; return true; }
/* * Common routine for both label and relabel */ static int do_label(UAContext *ua, const char *cmd, bool relabel) { USTORERES store; BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; MEDIA_DBR mr, omr; POOL_DBR pr; bool print_reminder = true; bool label_barcodes = false; bool label_encrypt = false; int ok = FALSE; int i, j; int drive; bool media_record_exists = false; static const char *barcode_keywords[] = { "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_keywords)) >= 0) { /* * Now find the keyword in the list */ if ((j = find_arg(ua, barcode_keywords[i])) > 0) { *ua->argk[j] = 0; /* zap barcode keyword */ } label_barcodes = true; } /* * Look for the encrypt keyword */ if ((i = find_arg(ua, "encrypt")) > 0) { *ua->argk[i] = 0; /* zap encrypt keyword */ label_encrypt = true; } store.store = get_storage_resource(ua, true, label_barcodes); if (!store.store) { return 1; } switch (store.store->Protocol) { case APT_NDMPV2: case APT_NDMPV3: case APT_NDMPV4: /* * See if the user selected a NDMP storage device but its * handled by a native Bareos storage daemon e.g. we have * a paired_storage pointer. */ if (store.store->paired_storage) { store.store = store.store->paired_storage; } else { ua->warning_msg(_("Storage has non-native protocol.\n")); return 1; } break; default: break; } 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, label_encrypt); 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) { 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 (!bstrcmp(omr.VolStatus, "Purged") && !bstrcmp(omr.VolStatus, "Recycle")) { 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; } /* * Search by Media name so set VolumeName and clear MediaId. */ mr.MediaId = 0; 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 */ } set_storageid_in_mr(store.store, &mr); 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; } } /* * See if we need to generate a new passphrase for hardware encryption. */ if (label_encrypt) { ua->info_msg(_("Generating new hardware encryption key\n")); if (!generate_new_encryption_key(ua, &mr)) { 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); sd->fsend("mount %s drive=%d", dev_name, drive); unbash_spaces(dev_name); /* * We use bget_dirmsg here and not bnet_recv because as part of * the mount request the stored can request catalog information for * any plugin who listens to the bsdEventLabelVerified event. * As we don't want to loose any non protocol data e.g. errors * without a 3xxx prefix we set the allow_any_message of * bget_dirmsg to true and as such is behaves like a normal * bnet_recv for any non protocol messages. */ while (bget_dirmsg(sd, true) >= 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 (bstrncmp(sd->msg, "3001 ", 5) || bstrncmp(sd->msg, "3002 ", 5) || bstrncmp(sd->msg, "3906 ", 5)) { print_reminder = false; } } } } if (print_reminder) { ua->info_msg(_("Do not forget to mount the drive!!!\n")); } close_sd_bsock(ua); return 1; }
int authenticate_user_agent(UAContext *uac) { char name[MAX_NAME_LENGTH]; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool tls_authenticate; int compatible = true; CONRES *cons = NULL; BSOCK *ua = uac->UA_sock; bool auth_success = false; TLS_CONTEXT *tls_ctx = NULL; alist *verify_list = NULL; if (ua->msglen < 16 || ua->msglen >= MAX_NAME_LENGTH + 15) { Emsg4(M_ERROR, 0, _("UA Hello from %s:%s:%d is invalid. Len=%d\n"), ua->who(), ua->host(), ua->port(), ua->msglen); return 0; } if (sscanf(ua->msg, "Hello %127s calling\n", name) != 1) { ua->msg[100] = 0; /* terminate string */ Emsg4(M_ERROR, 0, _("UA Hello from %s:%s:%d is invalid. Got: %s\n"), ua->who(), ua->host(), ua->port(), ua->msg); return 0; } name[sizeof(name)-1] = 0; /* terminate name */ if (bstrcmp(name, "*UserAgent*")) { /* default console */ /* TLS Requirement */ if (director->tls_enable) { if (director->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } tls_authenticate = director->tls_authenticate; if (tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (director->tls_verify_peer) { verify_list = director->tls_allowed_cns; } auth_success = cram_md5_challenge(ua, director->password, tls_local_need, compatible) && cram_md5_respond(ua, director->password, &tls_remote_need, &compatible); } else { unbash_spaces(name); cons = (CONRES *)GetResWithName(R_CONSOLE, name); if (cons) { /* TLS Requirement */ if (cons->tls_enable) { if (cons->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } tls_authenticate = cons->tls_authenticate; if (tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (cons->tls_verify_peer) { verify_list = cons->tls_allowed_cns; } auth_success = cram_md5_challenge(ua, cons->password, tls_local_need, compatible) && cram_md5_respond(ua, cons->password, &tls_remote_need, &compatible); if (auth_success) { uac->cons = cons; /* save console resource pointer */ } } else { auth_success = false; goto auth_done; } } /* Verify that the remote peer is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Emsg0(M_FATAL, 0, _("Authorization problem:" " Remote client did not advertise required TLS support.\n")); auth_success = false; goto auth_done; } /* Verify that we are willing to meet the peer's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Emsg0(M_FATAL, 0, _("Authorization problem:" " Remote client requires TLS.\n")); auth_success = false; goto auth_done; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { if (cons) { tls_ctx = cons->tls_ctx; } else { tls_ctx = director->tls_ctx; } /* Engage TLS! Full Speed Ahead! */ if (!bnet_tls_server(tls_ctx, ua, verify_list)) { Emsg0(M_ERROR, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_done; } if (tls_authenticate) { /* authentication only? */ ua->free_tls(); /* stop tls */ } } /* Authorization Completed */ auth_done: if (!auth_success) { ua->fsend("%s", _(Dir_sorry)); Emsg4(M_ERROR, 0, _("Unable to authenticate console \"%s\" at %s:%s:%d.\n"), name, ua->who(), ua->host(), ua->port()); sleep(5); return 0; } ua->fsend(_("1000 OK: %s Version: %s (%s)\n"), my_name, VERSION, BDATE); return 1; }
/* * Director requests us to start a job * Basic tasks done here: * - We pickup the JobId to be run from the Director. * - We pickup the device, media, and pool from the Director * - Wait for a connection from the File Daemon (FD) * - Accept commands from the FD (i.e. run the job) * - Return when the connection is terminated or * there is an error. */ bool job_cmd(JCR *jcr) { int32_t JobId; char sd_auth_key[200]; char spool_size[30]; char seed[100]; BSOCK *dir = jcr->dir_bsock; POOL_MEM job_name, client_name, job, fileset_name, fileset_md5; int32_t JobType, level, spool_attributes, no_attributes, spool_data; int32_t write_part_after_job, PreferMountedVols; int32_t rerunning; int32_t is_client; int stat; JCR *ojcr; /* * Get JobId and permissions from Director */ Dmsg1(100, "<dird: %s", dir->msg); bstrncpy(spool_size, "0", sizeof(spool_size)); stat = sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(), client_name.c_str(), &JobType, &level, fileset_name.c_str(), &no_attributes, &spool_attributes, fileset_md5.c_str(), &spool_data, &write_part_after_job, &PreferMountedVols, spool_size, &rerunning, &jcr->VolSessionId, &jcr->VolSessionTime, &is_client, &sd_auth_key); if (stat != 19) { pm_strcpy(jcr->errmsg, dir->msg); dir->fsend(BAD_job, stat, jcr->errmsg); Dmsg1(100, ">dird: %s", dir->msg); jcr->setJobStatus(JS_ErrorTerminated); return false; } jcr->rerunning = rerunning; jcr->sd_client = is_client; if (is_client) { jcr->sd_auth_key = bstrdup(sd_auth_key); } Dmsg3(100, "rerunning=%d VolSesId=%d VolSesTime=%d\n", jcr->rerunning, jcr->VolSessionId, jcr->VolSessionTime); /* * Since this job could be rescheduled, we * check to see if we have it already. If so * free the old jcr and use the new one. */ ojcr = get_jcr_by_full_name(job.c_str()); if (ojcr && !ojcr->authenticated) { Dmsg2(100, "Found ojcr=0x%x Job %s\n", (unsigned)(intptr_t)ojcr, job.c_str()); free_jcr(ojcr); } jcr->JobId = JobId; Dmsg2(800, "Start JobId=%d %p\n", JobId, jcr); set_jcr_in_tsd(jcr); /* * If job rescheduled because previous was incomplete, * the Resched flag is set and VolSessionId and VolSessionTime * are given to us (same as restarted job). */ if (!jcr->rerunning) { jcr->VolSessionId = newVolSessionId(); jcr->VolSessionTime = VolSessionTime; } bstrncpy(jcr->Job, job, sizeof(jcr->Job)); unbash_spaces(job_name); jcr->job_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->job_name, job_name); unbash_spaces(client_name); jcr->client_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->client_name, client_name); unbash_spaces(fileset_name); jcr->fileset_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_name, fileset_name); jcr->setJobType(JobType); jcr->setJobLevel(level); jcr->no_attributes = no_attributes; jcr->spool_attributes = spool_attributes; jcr->spool_data = spool_data; jcr->spool_size = str_to_int64(spool_size); jcr->write_part_after_job = write_part_after_job; jcr->fileset_md5 = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_md5, fileset_md5); jcr->PreferMountedVols = PreferMountedVols; jcr->authenticated = false; /* * Pass back an authorization key for the File daemon */ if (jcr->sd_client) { bstrncpy(sd_auth_key, "xxx", 3); } else { bsnprintf(seed, sizeof(seed), "%p%d", jcr, JobId); make_session_key(sd_auth_key, seed, 1); } dir->fsend(OKjob, jcr->VolSessionId, jcr->VolSessionTime, sd_auth_key); Dmsg2(150, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); /* If not client, set key, otherwise it is already set */ if (!jcr->sd_client) { jcr->sd_auth_key = bstrdup(sd_auth_key); memset(sd_auth_key, 0, sizeof(sd_auth_key)); } new_plugins(jcr); /* instantiate the plugins */ generate_daemon_event(jcr, "JobStart"); generate_plugin_event(jcr, bsdEventJobStart, (void *)"JobStart"); return true; }
/* * NOTE! This routine opens the SD socket but leaves it open */ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, POOL_DBR *pr, bool relabel, bool media_record_exists, int drive) { BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; bool ok = false; uint64_t VolBytes = 0; if (!(sd=open_sd_bsock(ua))) { return false; } bstrncpy(dev_name, ua->jcr->res.wstore->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); bash_spaces(mr->VolumeName); bash_spaces(mr->MediaType); bash_spaces(pr->Name); if (relabel) { bash_spaces(omr->VolumeName); sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s " "MediaType=%s Slot=%d drive=%d MinBlocksize=%d MaxBlocksize=%d", dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive, /* * if relabeling, keep blocksize settings */ omr->MinBlocksize, omr->MaxBlocksize); ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"), omr->VolumeName, mr->VolumeName); } else { sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s " "Slot=%d drive=%d MinBlocksize=%d MaxBlocksize=%d", dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive, /* * if labeling, use blocksize defined in pool */ pr->MinBlocksize, pr->MaxBlocksize); ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"), mr->VolumeName, mr->Slot); Dmsg8(100, "label %s VolumeName=%s PoolName=%s MediaType=%s " "Slot=%d drive=%d MinBlocksize=%d MaxBlocksize=%d\n", dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive, pr->MinBlocksize, pr->MaxBlocksize); } /* * We use bget_dirmsg here and not bnet_recv because as part of * the label request the stored can request catalog information for * any plugin who listens to the bsdEventLabelVerified event. * As we don't want to loose any non protocol data e.g. errors * without a 3xxx prefix we set the allow_any_message of * bget_dirmsg to true and as such is behaves like a normal * bnet_recv for any non protocol messages. */ while (bget_dirmsg(sd, true) >= 0) { ua->send_msg("%s", sd->msg); if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu ", &VolBytes) == 1) { ok = true; } } unbash_spaces(mr->VolumeName); unbash_spaces(mr->MediaType); unbash_spaces(pr->Name); mr->LabelDate = time(NULL); mr->set_label_date = true; if (ok) { if (media_record_exists) { /* we update it */ mr->VolBytes = VolBytes; mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */ set_storageid_in_mr(ua->jcr->res.wstore, mr); if (!db_update_media_record(ua->jcr, ua->db, mr)) { ua->error_msg("%s", db_strerror(ua->db)); ok = false; } } else { /* create the media record */ set_pool_dbr_defaults_in_media_dbr(mr, pr); mr->VolBytes = VolBytes; mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */ mr->Enabled = 1; set_storageid_in_mr(ua->jcr->res.wstore, mr); if (db_create_media_record(ua->jcr, ua->db, mr)) { ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"), mr->VolumeName, mr->Slot); /* * Update number of volumes in pool */ pr->NumVols++; if (!db_update_pool_record(ua->jcr, ua->db, pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } else { ua->error_msg("%s", db_strerror(ua->db)); ok = false; } } } else { ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName); } return ok; }
void catalog_request(JCR *jcr, BSOCK *bs) { MEDIA_DBR mr, sdmr; JOBMEDIA_DBR jm; char Job[MAX_NAME_LENGTH]; char pool_name[MAX_NAME_LENGTH]; int index, ok, label, writing; POOLMEM *omsg; POOL_DBR pr; uint32_t Stripe, Copy; uint64_t MediaId; utime_t VolFirstWritten; utime_t VolLastWritten; memset(&sdmr, 0, sizeof(sdmr)); memset(&jm, 0, sizeof(jm)); Dsm_check(100); /* * Request to find next appendable Volume for this Job */ Dmsg1(100, "catreq %s", bs->msg); if (!jcr->db) { omsg = get_memory(bs->msglen+1); pm_strcpy(omsg, bs->msg); bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg); Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg); free_memory(omsg); return; } /* * Find next appendable medium for SD */ if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) { memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, pool_name, sizeof(pr.Name)); unbash_spaces(pr.Name); ok = db_get_pool_record(jcr, jcr->db, &pr); if (ok) { mr.PoolId = pr.PoolId; set_storageid_in_mr(jcr->res.wstore, &mr); mr.ScratchPoolId = pr.ScratchPoolId; ok = find_next_volume_for_append(jcr, &mr, index, fnv_create_vol, fnv_prune); Dmsg3(050, "find_media ok=%d idx=%d vol=%s\n", ok, index, mr.VolumeName); } /* * Send Find Media response to Storage daemon */ if (ok) { send_volume_info_to_storage_daemon(jcr, bs, &mr); } else { bs->fsend(_("1901 No Media.\n")); Dmsg0(500, "1901 No Media.\n"); } /* * Request to find specific Volume information */ } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) { Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName); /* * Find the Volume */ unbash_spaces(mr.VolumeName); if (db_get_media_record(jcr, jcr->db, &mr)) { const char *reason = NULL; /* detailed reason for rejection */ /* * If we are reading, accept any volume (reason == NULL) * If we are writing, check if the Volume is valid * for this job, and do a recycle if necessary */ if (writing) { /* * SD wants to write this Volume, so make * sure it is suitable for this job, i.e. * Pool matches, and it is either Append or Recycle * and Media Type matches and Pool allows any volume. */ if (mr.PoolId != jcr->jr.PoolId) { reason = _("not in Pool"); } else if (!bstrcmp(mr.MediaType, jcr->res.wstore->media_type)) { reason = _("not correct MediaType"); } else { /* * Now try recycling if necessary * reason set non-NULL if we cannot use it */ check_if_volume_valid_or_recyclable(jcr, &mr, &reason); } } if (!reason && mr.Enabled != 1) { reason = _("is not Enabled"); } if (reason == NULL) { /* * Send Find Media response to Storage daemon */ send_volume_info_to_storage_daemon(jcr, bs, &mr); } else { /* Not suitable volume */ bs->fsend(_("1998 Volume \"%s\" catalog status is %s, %s.\n"), mr.VolumeName, mr.VolStatus, reason); } } else { bs->fsend(_("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName); Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName); } /* * Request to update Media record. Comes typically at the end * of a Storage daemon Job Session, when labeling/relabeling a * Volume, or when an EOF mark is written. */ } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName, &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes, &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes, &VolLastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger, &sdmr.VolReadTime, &sdmr.VolWriteTime, &VolFirstWritten) == 18) { db_lock(jcr->db); Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName, mr.VolStatus, sdmr.VolStatus); bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */ unbash_spaces(mr.VolumeName); if (!db_get_media_record(jcr, jcr->db, &mr)) { Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"), mr.VolumeName, db_strerror(jcr->db)); bs->fsend(_("1991 Catalog Request for vol=%s failed: %s"), mr.VolumeName, db_strerror(jcr->db)); goto bail_out; } /* Set first written time if this is first job */ if (mr.FirstWritten == 0) { if (VolFirstWritten == 0) { mr.FirstWritten = jcr->start_time; /* use Job start time as first write */ } else { mr.FirstWritten = VolFirstWritten; } mr.set_first_written = true; } /* If we just labeled the tape set time */ if (label || mr.LabelDate == 0) { mr.LabelDate = jcr->start_time; mr.set_label_date = true; if (mr.InitialWrite == 0) { mr.InitialWrite = jcr->start_time; } Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate); } else { /* * Insanity check for VolFiles get set to a smaller value */ if (sdmr.VolFiles < mr.VolFiles) { Jmsg(jcr, M_FATAL, 0, _("Volume Files at %u being set to %u" " for Volume \"%s\". This is incorrect.\n"), mr.VolFiles, sdmr.VolFiles, mr.VolumeName); bs->fsend(_("1992 Update Media error. VolFiles=%u, CatFiles=%u\n"), sdmr.VolFiles, mr.VolFiles); goto bail_out; } } Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs); /* * Check if the volume has been written by the job, * and update the LastWritten field if needed. */ if (mr.VolBlocks != sdmr.VolBlocks && VolLastWritten != 0) { mr.LastWritten = VolLastWritten; } /* * Update to point to the last device used to write the Volume. * However, do so only if we are writing the tape, i.e. * the number of VolWrites has increased. */ if (jcr->res.wstore && sdmr.VolWrites > mr.VolWrites) { Dmsg2(050, "Update StorageId old=%d new=%d\n", mr.StorageId, jcr->res.wstore->StorageId); /* Update StorageId after write */ set_storageid_in_mr(jcr->res.wstore, &mr); } else { /* Nothing written, reset same StorageId */ set_storageid_in_mr(NULL, &mr); } /* Copy updated values to original media record */ mr.VolJobs = sdmr.VolJobs; mr.VolFiles = sdmr.VolFiles; mr.VolBlocks = sdmr.VolBlocks; mr.VolBytes = sdmr.VolBytes; mr.VolMounts = sdmr.VolMounts; mr.VolErrors = sdmr.VolErrors; mr.VolWrites = sdmr.VolWrites; mr.Slot = sdmr.Slot; mr.InChanger = sdmr.InChanger; bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus)); mr.VolReadTime = sdmr.VolReadTime; mr.VolWriteTime = sdmr.VolWriteTime; Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName); /* * Update the database, then before sending the response to the * SD, check if the Volume has expired. */ if (!db_update_media_record(jcr, jcr->db, &mr)) { Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"), db_strerror(jcr->db)); bs->fsend(_("1993 Update Media error\n")); Dmsg0(400, "send error\n"); } else { (void)has_volume_expired(jcr, &mr); send_volume_info_to_storage_daemon(jcr, bs, &mr); } bail_out: db_unlock(jcr->db); Dmsg1(400, ">CatReq response: %s", bs->msg); Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr); return; /* * Request to create a JobMedia record */ } else if (sscanf(bs->msg, Create_job_media, &Job, &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile, &jm.StartBlock, &jm.EndBlock, &Copy, &Stripe, &MediaId) == 10) { if (jcr->mig_jcr) { jm.JobId = jcr->mig_jcr->JobId; } else { jm.JobId = jcr->JobId; } jm.MediaId = MediaId; Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n", jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex); if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) { Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"), db_strerror(jcr->db)); bs->fsend(_("1992 Create JobMedia error\n")); } else { Dmsg0(400, "JobMedia record created\n"); bs->fsend(OK_create); } } else { omsg = get_memory(bs->msglen+1); pm_strcpy(omsg, bs->msg); bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg); Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg); free_memory(omsg); } Dmsg1(400, ">CatReq response: %s", bs->msg); Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr); return; }
/* * NOTE! This routine opens the SD socket but leaves it open */ static bool send_label_request(UAContext *ua, MEDIA_DBR *mr, MEDIA_DBR *omr, POOL_DBR *pr, int relabel, bool media_record_exists, int drive) { BSOCK *sd; char dev_name[MAX_NAME_LENGTH]; bool ok = false; bool is_dvd = false; uint64_t VolBytes = 0; if (!(sd=open_sd_bsock(ua))) { return false; } bstrncpy(dev_name, ua->jcr->wstore->dev_name(), sizeof(dev_name)); bash_spaces(dev_name); bash_spaces(mr->VolumeName); bash_spaces(mr->MediaType); bash_spaces(pr->Name); if (relabel) { bash_spaces(omr->VolumeName); sd->fsend("relabel %s OldName=%s NewName=%s PoolName=%s " "MediaType=%s Slot=%d drive=%d", dev_name, omr->VolumeName, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive); ua->send_msg(_("Sending relabel command from \"%s\" to \"%s\" ...\n"), omr->VolumeName, mr->VolumeName); } else { sd->fsend("label %s VolumeName=%s PoolName=%s MediaType=%s " "Slot=%d drive=%d", dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive); ua->send_msg(_("Sending label command for Volume \"%s\" Slot %d ...\n"), mr->VolumeName, mr->Slot); Dmsg6(100, "label %s VolumeName=%s PoolName=%s MediaType=%s Slot=%d drive=%d\n", dev_name, mr->VolumeName, pr->Name, mr->MediaType, mr->Slot, drive); } while (sd->recv() >= 0) { int dvd; ua->send_msg("%s", sd->msg); if (sscanf(sd->msg, "3000 OK label. VolBytes=%llu DVD=%d ", &VolBytes, &dvd) == 2) { is_dvd = dvd; ok = true; } } unbash_spaces(mr->VolumeName); unbash_spaces(mr->MediaType); unbash_spaces(pr->Name); mr->LabelDate = time(NULL); mr->set_label_date = true; if (is_dvd) { /* We know that a freshly labelled DVD has 1 VolParts */ /* This does not apply to auto-labelled DVDs. */ mr->VolParts = 1; } if (ok) { if (media_record_exists) { /* we update it */ mr->VolBytes = VolBytes; mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */ mr->StorageId = ua->jcr->wstore->StorageId; if (!db_update_media_record(ua->jcr, ua->db, mr)) { ua->error_msg("%s", db_strerror(ua->db)); ok = false; } } else { /* create the media record */ set_pool_dbr_defaults_in_media_dbr(mr, pr); mr->VolBytes = VolBytes; mr->InChanger = mr->Slot > 0; /* if slot give assume in changer */ mr->StorageId = ua->jcr->wstore->StorageId; mr->Enabled = 1; if (db_create_media_record(ua->jcr, ua->db, mr)) { ua->info_msg(_("Catalog record for Volume \"%s\", Slot %d successfully created.\n"), mr->VolumeName, mr->Slot); /* Update number of volumes in pool */ pr->NumVols++; if (!db_update_pool_record(ua->jcr, ua->db, pr)) { ua->error_msg("%s", db_strerror(ua->db)); } } else { ua->error_msg("%s", db_strerror(ua->db)); ok = false; } } } else { ua->error_msg(_("Label command failed for Volume %s.\n"), mr->VolumeName); } return ok; }
/* * See who is connecting and lookup the authentication information. * First make him prove his identity and then prove our identity to the Remote daemon. */ static inline bool two_way_authenticate(int rcode, BSOCK *bs, JCR* jcr) { POOLMEM *dirname; DIRRES *director = NULL; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool compatible = true; /* require md5 compatible DIR */ bool auth_success = false; alist *verify_list = NULL; if (rcode != R_DIRECTOR) { Dmsg1(dbglvl, "I only authenticate Directors, not %d\n", rcode); Jmsg1(jcr, M_FATAL, 0, _("I only authenticate Directors, not %d\n"), rcode); return 0; } /* * Sanity check. */ if (bs->msglen < 25 || bs->msglen > 500) { Dmsg2(dbglvl, "Bad Hello command from Director at %s. Len=%d.\n", bs->who(), bs->msglen); Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s. Len=%d.\n"), bs->who(), bs->msglen); return 0; } dirname = get_pool_memory(PM_MESSAGE); dirname = check_pool_memory_size(dirname, bs->msglen); if (sscanf(bs->msg, "Hello Director %127s calling", dirname) != 1) { bs->msg[100] = 0; Dmsg2(dbglvl, "Bad Hello command from Director at %s: %s\n", bs->who(), bs->msg); Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s: %s\n"), bs->who(), bs->msg); return 0; } director = NULL; unbash_spaces(dirname); foreach_res(director, rcode) { if (bstrcmp(director->hdr.name, dirname)) { break; } } if (!director) { Dmsg2(dbglvl, "Connection from unknown Director %s at %s rejected.\n", dirname, bs->who()); Jmsg(jcr, M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n" "Please see %s for help.\n"), dirname, bs->who(), MANUAL_AUTH_URL); free_pool_memory(dirname); return 0; } /* * TLS Requirement */ if (director->tls_enable) { if (director->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (director->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (director->tls_verify_peer) { verify_list = director->tls_allowed_cns; } ASSERT(director->password.encoding == p_encoding_md5); /* * Timeout Hello after 10 mins */ btimer_t *tid = start_bsock_timer(bs, AUTH_TIMEOUT); auth_success = cram_md5_challenge(bs, director->password.value, tls_local_need, compatible); if (auth_success) { auth_success = cram_md5_respond(bs, director->password.value, &tls_remote_need, &compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_get_auth failed with %s\n", bs->who()); } } else { Dmsg1(dbglvl, "cram_auth failed with %s\n", bs->who()); } if (!auth_success) { Jmsg(jcr, M_FATAL, 0, _("Incorrect password given by Director.\n" "Please see %s for help.\n"), MANUAL_AUTH_URL); auth_success = false; goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* * Engage TLS! Full Speed Ahead! */ if (!bnet_tls_server(director->tls_ctx, bs, verify_list)) { Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed with DIR at \"%s:%d\"\n"), bs->host(), bs->port()); auth_success = false; goto auth_fatal; } if (director->tls_authenticate) { /* authenticate with tls only? */ bs->free_tls(); /* yes, shut it down */ } } auth_fatal: stop_bsock_timer(tid); free_pool_memory(dirname); jcr->director = director; return auth_success; }
/* * Get a message * Call appropriate processing routine * If it is not a Jmsg or a ReqCat message, * return it to the caller. * * This routine is called to get the next message from * another daemon. If the message is in canonical message * format and the type is known, it will be dispatched * to the appropriate handler. If the message is * in any other format, it will be returned. * * E.g. any message beginning with a digit will be passed * through to the caller. * All other messages are expected begin with some identifier * -- for the moment only the first character is checked, but * at a later time, the whole identifier (e.g. Jmsg, CatReq, ...) * could be checked. This is followed by Job=Jobname <user-defined> * info. The identifier is used to dispatch the message to the right * place (Job message, catalog request, ...). The Job is used to lookup * the JCR so that the action is performed on the correct jcr, and * the rest of the message is up to the user. Note, DevUpd uses * *System* for the Job name, and hence no JCR is obtained. This * is a *rare* case where a jcr is not really needed. * */ int bget_dirmsg(BSOCK *bs, bool allow_any_message) { int32_t n = BNET_TERMINATE; char Job[MAX_NAME_LENGTH]; char MsgType[20]; int type; utime_t mtime; /* message time */ JCR *jcr = bs->jcr(); char *msg; for ( ; !bs->is_stop() && !bs->is_timed_out(); ) { n = bs->recv(); Dmsg2(200, "bget_dirmsg %d: %s\n", n, bs->msg); if (bs->is_stop() || bs->is_timed_out()) { return n; /* error or terminate */ } if (n == BNET_SIGNAL) { /* handle signal */ /* BNET_SIGNAL (-1) return from bnet_recv() => network signal */ switch (bs->msglen) { case BNET_EOD: /* end of data */ return n; case BNET_EOD_POLL: bs->fsend(OK_msg);/* send response */ return n; /* end of data */ case BNET_TERMINATE: bs->set_terminated(); return n; case BNET_POLL: bs->fsend(OK_msg); /* send response */ break; case BNET_HEARTBEAT: // encode_time(time(NULL), Job); // Dmsg1(100, "%s got heartbeat.\n", Job); break; case BNET_HB_RESPONSE: break; case BNET_STATUS: /* *****FIXME***** Implement more completely */ bs->fsend("Status OK\n"); bs->signal(BNET_EOD); break; case BNET_BTIME: /* send BAREOS time */ char ed1[50]; bs->fsend("btime %s\n", edit_uint64(get_current_btime(),ed1)); break; default: Jmsg1(jcr, M_WARNING, 0, _("bget_dirmsg: unknown bnet signal %d\n"), bs->msglen); return n; } continue; } /* * Handle normal data */ if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */ return n; /* yes, return it */ } /* * If we get here, it must be a request. Either * a message to dispatch, or a catalog request. * Try to fulfill it. */ if (sscanf(bs->msg, "%020s Job=%127s ", MsgType, Job) != 2) { /* * If the special flag allow_any_message is given ignore * the error and just return it as normal data. */ if (allow_any_message) { return n; } else { Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg); continue; } } /* * Skip past "Jmsg Job=nnn" */ if (!(msg=find_msg_start(bs->msg))) { Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg); continue; } /* * Here we are expecting a message of the following format: * Jmsg Job=nnn type=nnn level=nnn Message-string * Note, level should really be mtime, but that changes * the protocol. */ if (bs->msg[0] == 'J') { /* Job message */ if (sscanf(bs->msg, "Jmsg Job=%127s type=%d level=%lld", Job, &type, &mtime) != 3) { Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg); continue; } Dmsg1(900, "Got msg: %s\n", bs->msg); skip_spaces(&msg); skip_nonspaces(&msg); /* skip type=nnn */ skip_spaces(&msg); skip_nonspaces(&msg); /* skip level=nnn */ if (*msg == ' ') { msg++; /* skip leading space */ } Dmsg1(900, "Dispatch msg: %s", msg); dispatch_message(jcr, type, mtime, msg); continue; } /* * Here we expact a CatReq message * CatReq Job=nn Catalog-Request-Message */ if (bs->msg[0] == 'C') { /* Catalog request */ Dmsg2(900, "Catalog req jcr 0x%x: %s", jcr, bs->msg); catalog_request(jcr, bs); continue; } if (bs->msg[0] == 'U') { /* SD sending attributes */ Dmsg2(900, "Catalog upd jcr 0x%x: %s", jcr, bs->msg); catalog_update(jcr, bs); continue; } if (bs->msg[0] == 'B') { /* SD sending file spool attributes */ Dmsg2(100, "Blast attributes jcr 0x%x: %s", jcr, bs->msg); char filename[256]; if (sscanf(bs->msg, "BlastAttr Job=%127s File=%255s", Job, filename) != 2) { Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg); continue; } unbash_spaces(filename); if (despool_attributes_from_file(jcr, filename)) { bs->fsend("1000 OK BlastAttr\n"); } else { bs->fsend("1990 ERROR BlastAttr\n"); } continue; } if (bs->msg[0] == 'M') { /* Mount request */ Dmsg1(900, "Mount req: %s", bs->msg); mount_request(jcr, bs, msg); continue; } if (bs->msg[0] == 'S') { /* Status change */ int JobStatus; char Job[MAX_NAME_LENGTH]; if (sscanf(bs->msg, Job_status, &Job, &JobStatus) == 2) { set_jcr_sd_job_status(jcr, JobStatus); /* current status */ } else { Jmsg1(jcr, M_ERROR, 0, _("Malformed message: %s\n"), bs->msg); } continue; } #ifdef needed /* No JCR for Device Updates! */ if (bs->msg[0] = 'D') { /* Device update */ DEVICE *dev; POOL_MEM dev_name, changer_name, media_type, volume_name; int dev_open, dev_append, dev_read, dev_labeled; int dev_offline, dev_autochanger, dev_autoselect; int dev_num_writers, dev_max_writers, dev_reserved; uint64_t dev_read_time, dev_write_time, dev_write_bytes, dev_read_bytes; uint64_t dev_PoolId; Dmsg1(100, "<stored: %s", bs->msg); if (sscanf(bs->msg, Device_update, &Job, dev_name.c_str(), &dev_append, &dev_read, &dev_num_writers, &dev_open, &dev_labeled, &dev_offline, &dev_reserved, &dev_max_writers, &dev_autoselect, &dev_autochanger, changer_name.c_str(), media_type.c_str(), volume_name.c_str(), &dev_read_time, &dev_write_time, &dev_read_bytes, &dev_write_bytes) != 19) { Emsg1(M_ERROR, 0, _("Malformed message: %s\n"), bs->msg); } else { unbash_spaces(dev_name); dev = (DEVICE *)GetResWithName(R_DEVICE, dev_name.c_str()); if (!dev) { continue; } unbash_spaces(changer_name); unbash_spaces(media_type); unbash_spaces(volume_name); bstrncpy(dev->ChangerName, changer_name.c_str(), sizeof(dev->ChangerName)); bstrncpy(dev->MediaType, media_type.c_str(), sizeof(dev->MediaType)); bstrncpy(dev->VolumeName, volume_name.c_str(), sizeof(dev->VolumeName)); /* Note, these are copied because they are boolean rather than * integer. */ dev->open = dev_open; dev->append = dev_append; dev->read = dev_read; dev->labeled = dev_labeled; dev->offline = dev_offline; dev->autoselect = dev_autoselect; dev->autochanger = dev_autochanger > 0; dev->num_drives = dev_autochanger; /* does double duty */ dev->PoolId = dev_PoolId; dev->num_writers = dev_num_writers; dev->max_writers = dev_max_writers; dev->reserved = dev_reserved; dev->found = true; dev->DevReadTime = dev_read_time; /* TODO : have to update database */ dev->DevWriteTime = dev_write_time; dev->DevReadBytes = dev_read_bytes; dev->DevWriteBytes = dev_write_bytes; } continue; } #endif return n; } return n; }
/* * Director requests us to start a job * Basic tasks done here: * - We pickup the JobId to be run from the Director. * - We pickup the device, media, and pool from the Director * - Wait for a connection from the File Daemon (FD) * - Accept commands from the FD (i.e. run the job) * - Return when the connection is terminated or * there is an error. */ bool job_cmd(JCR *jcr) { int32_t JobId; char auth_key[MAX_NAME_LENGTH]; char seed[MAX_NAME_LENGTH]; char spool_size[MAX_NAME_LENGTH]; BSOCK *dir = jcr->dir_bsock; POOL_MEM job_name, client_name, job, fileset_name, fileset_md5, backup_format; int32_t JobType, level, spool_attributes, no_attributes, spool_data; int32_t PreferMountedVols, rerunning, protocol, dumplevel; int status; uint64_t quota = 0; JCR *ojcr; /* * Get JobId and permissions from Director */ Dmsg1(100, "<dird: %s", dir->msg); bstrncpy(spool_size, "0", sizeof(spool_size)); status = sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(), client_name.c_str(), &JobType, &level, fileset_name.c_str(), &no_attributes, &spool_attributes, fileset_md5.c_str(), &spool_data, &PreferMountedVols, spool_size, &rerunning, &jcr->VolSessionId, &jcr->VolSessionTime, "a, &protocol, backup_format.c_str(), &dumplevel); if (status != 20) { pm_strcpy(jcr->errmsg, dir->msg); dir->fsend(BAD_job, status, jcr->errmsg); Dmsg1(100, ">dird: %s", dir->msg); jcr->setJobStatus(JS_ErrorTerminated); return false; } jcr->rerunning = (rerunning) ? true : false; jcr->setJobProtocol(protocol); Dmsg4(100, "rerunning=%d VolSesId=%d VolSesTime=%d Protocol=%d\n", jcr->rerunning, jcr->VolSessionId, jcr->VolSessionTime, jcr->getJobProtocol()); /* * Since this job could be rescheduled, we * check to see if we have it already. If so * free the old jcr and use the new one. */ ojcr = get_jcr_by_full_name(job.c_str()); if (ojcr && !ojcr->authenticated) { Dmsg2(100, "Found ojcr=0x%x Job %s\n", (unsigned)(intptr_t)ojcr, job.c_str()); free_jcr(ojcr); } jcr->JobId = JobId; Dmsg2(800, "Start JobId=%d %p\n", JobId, jcr); /* * If job rescheduled because previous was incomplete, * the Resched flag is set and VolSessionId and VolSessionTime * are given to us (same as restarted job). */ if (!jcr->rerunning) { jcr->VolSessionId = newVolSessionId(); jcr->VolSessionTime = VolSessionTime; } bstrncpy(jcr->Job, job, sizeof(jcr->Job)); unbash_spaces(job_name); jcr->job_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->job_name, job_name); unbash_spaces(client_name); jcr->client_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->client_name, client_name); unbash_spaces(fileset_name); jcr->fileset_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_name, fileset_name); jcr->setJobType(JobType); jcr->setJobLevel(level); jcr->no_attributes = no_attributes; jcr->spool_attributes = spool_attributes; jcr->spool_data = spool_data; jcr->spool_size = str_to_int64(spool_size); jcr->fileset_md5 = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_md5, fileset_md5); jcr->PreferMountedVols = PreferMountedVols; jcr->RemainingQuota = quota; unbash_spaces(backup_format); jcr->backup_format = get_pool_memory(PM_NAME); pm_strcpy(jcr->backup_format, backup_format); jcr->DumpLevel = dumplevel; jcr->authenticated = false; Dmsg1(50, "Quota set as %llu\n", quota); /* * Pass back an authorization key for the File daemon */ bsnprintf(seed, sizeof(seed), "%p%d", jcr, JobId); make_session_key(auth_key, seed, 1); dir->fsend(OK_job, jcr->VolSessionId, jcr->VolSessionTime, auth_key); Dmsg2(50, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); jcr->sd_auth_key = bstrdup(auth_key); memset(auth_key, 0, sizeof(auth_key)); dispatch_new_plugin_options(jcr); generate_plugin_event(jcr, bsdEventJobStart, (void *)"JobStart"); return true; }
/** * Get info on the next appendable volume in the Director's database * * Returns: true on success dcr->VolumeName is volume * reserve_volume() called on Volume name * false on failure dcr->VolumeName[0] == 0 * also sets dcr->found_in_use if at least one * in use volume was found. * * Volume information returned in dcr */ bool SD_DCR::dir_find_next_appendable_volume() { bool retval; BSOCK *dir = jcr->dir_bsock; POOL_MEM unwanted_volumes(PM_MESSAGE); Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n", is_reserved(), VolumeName); /* * Try the twenty oldest or most available volumes. Note, * the most available could already be mounted on another * drive, so we continue looking for a not in use Volume. */ lock_volumes(); P(vol_info_mutex); clear_found_in_use(); pm_strcpy(unwanted_volumes, ""); for (int vol_index = 1; vol_index < 20; vol_index++) { bash_spaces(media_type); bash_spaces(pool_name); bash_spaces(unwanted_volumes.c_str()); dir->fsend(Find_media, jcr->Job, vol_index, pool_name, media_type, unwanted_volumes.c_str()); unbash_spaces(media_type); unbash_spaces(pool_name); unbash_spaces(unwanted_volumes.c_str()); Dmsg1(dbglvl, ">dird %s", dir->msg); if (do_get_volume_info(this)) { if (vol_index == 1) { pm_strcpy(unwanted_volumes, VolumeName); } else { pm_strcat(unwanted_volumes, ","); pm_strcat(unwanted_volumes, VolumeName); } if (can_i_write_volume()) { Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", VolumeName); if (reserve_volume(this, VolumeName) == NULL) { Dmsg2(dbglvl, "Could not reserve volume %s on %s\n", VolumeName, dev->print_name()); continue; } Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n", VolumeName); retval = true; goto get_out; } else { Dmsg1(dbglvl, "Volume %s is in use.\n", VolumeName); /* * If volume is not usable, it is in use by someone else */ set_found_in_use(); continue; } } Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index, dev->print_name()); break; } retval = false; VolumeName[0] = 0; get_out: V(vol_info_mutex); unlock_volumes(); return retval; }
bool run_cmd(JCR *jcr) { struct timeval tv; struct timezone tz; struct timespec timeout; int errstat = 0; BSOCK *cl; int fd_version = 0; int sd_version = 0; char job_name[500]; int i; int stat; Dsm_check(200); Dmsg1(200, "Run_cmd: %s\n", jcr->dir_bsock->msg); /* If we do not need the FD, we are doing a virtual backup. */ if (jcr->no_client_used()) { do_vbackup(jcr); return false; } jcr->sendJobStatus(JS_WaitFD); /* wait for FD to connect */ Dmsg2(050, "sd_calls_client=%d sd_client=%d\n", jcr->sd_calls_client, jcr->sd_client); if (jcr->sd_calls_client) { /* We connected to Client, so finish work */ cl = jcr->file_bsock; if (!cl) { Jmsg0(jcr, M_FATAL, 0, _("Client socket not open. Could not connect to Client.\n")); Dmsg0(050, "Client socket not open. Could not connect to Client.\n"); return false; } /* Get response to Hello command sent earlier */ Dmsg0(050, "Read Hello command from Client\n"); for (i=0; i<60; i++) { stat = cl->recv(); if (stat <= 0) { bmicrosleep(1, 0); } else { break; } } if (stat <= 0) { berrno be; Jmsg1(jcr, M_FATAL, 0, _("Recv request to Client failed. ERR=%s\n"), be.bstrerror()); Dmsg1(050, _("Recv request to Client failed. ERR=%s\n"), be.bstrerror()); return false; } Dmsg1(050, "Got from FD: %s\n", cl->msg); if (sscanf(cl->msg, "Hello Bacula SD: Start Job %127s %d %d", job_name, &fd_version, &sd_version) != 3) { Jmsg1(jcr, M_FATAL, 0, _("Bad Hello from Client: %s.\n"), cl->msg); Dmsg1(050, _("Bad Hello from Client: %s.\n"), cl->msg); return false; } unbash_spaces(job_name); jcr->FDVersion = fd_version; jcr->SDVersion = sd_version; Dmsg1(050, "FDVersion=%d\n", fd_version); /* * Authenticate the File daemon */ Dmsg0(050, "=== Authenticate FD\n"); if (jcr->authenticated || !authenticate_filed(jcr)) { Dmsg1(050, "Authentication failed Job %s\n", jcr->Job); Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n")); } else { jcr->authenticated = true; } } else if (!jcr->sd_client) { /* We wait to receive connection from Client */ gettimeofday(&tv, &tz); timeout.tv_nsec = tv.tv_usec * 1000; timeout.tv_sec = tv.tv_sec + me->client_wait; Dmsg3(050, "%s waiting %d sec for FD to contact SD key=%s\n", jcr->Job, (int)(timeout.tv_sec-time(NULL)), jcr->sd_auth_key); Dmsg3(800, "=== Block Job=%s jid=%d %p\n", jcr->Job, jcr->JobId, jcr); /* * Wait for the File daemon to contact us to start the Job, * when he does, we will be released, unless the 30 minutes * expires. */ P(mutex); while ( !jcr->authenticated && !job_canceled(jcr) ) { errstat = pthread_cond_timedwait(&jcr->job_start_wait, &mutex, &timeout); if (errstat == ETIMEDOUT || errstat == EINVAL || errstat == EPERM) { break; } Dmsg1(800, "=== Auth cond errstat=%d\n", errstat); } Dmsg4(050, "=== Auth=%d jid=%d canceled=%d errstat=%d\n", jcr->JobId, jcr->authenticated, job_canceled(jcr), errstat); V(mutex); Dmsg2(800, "Auth fail or cancel for jid=%d %p\n", jcr->JobId, jcr); } memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); if (jcr->authenticated && !job_canceled(jcr)) { Dmsg2(800, "Running jid=%d %p\n", jcr->JobId, jcr); run_job(jcr); /* Run the job */ } Dmsg2(800, "Done jid=%d %p\n", jcr->JobId, jcr); return false; }