/* * This gets the client name from which the backup was made */ static int get_client_name(UAContext *ua, RESTORE_CTX *rx) { int i; CLIENT_DBR cr; /* * If no client name specified yet, get it now */ if (!rx->ClientName) { /* * Try command line argument */ i = find_arg_with_value(ua, NT_("client")); if (i < 0) { i = find_arg_with_value(ua, NT_("backupclient")); } if (i >= 0) { if (!is_name_valid(ua->argv[i], &ua->errmsg)) { ua->error_msg("%s argument: %s", ua->argk[i], ua->errmsg); return 0; } rx->ClientName = bstrdup(ua->argv[i]); return 1; } memset(&cr, 0, sizeof(cr)); if (!get_client_dbr(ua, &cr)) { return 0; } rx->ClientName = bstrdup(cr.Name); } return 1; }
/* * .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4 */ bool dot_bvfs_restore_cmd(UAContext *ua, const char *cmd) { DBId_t pathid = 0; int limit = 2000, offset = 0, i; char *path = NULL, *jobid = NULL; char *empty = (char *)""; char *fileid, *dirid, *hardlink; fileid = dirid = hardlink = empty; if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &limit, &offset)) { ua->error_msg("Can't find jobid, pathid or path argument\n"); return false; /* not enough param */ } Bvfs fs(ua->jcr, ua->db); fs.set_jobids(jobid); if ((i = find_arg_with_value(ua, "fileid")) >= 0) { fileid = ua->argv[i]; } if ((i = find_arg_with_value(ua, "dirid")) >= 0) { dirid = ua->argv[i]; } if ((i = find_arg_with_value(ua, "hardlink")) >= 0) { hardlink = ua->argv[i]; } if (fs.compute_restore_list(fileid, dirid, hardlink, path)) { ua->send_msg("OK\n"); } else { ua->error_msg("Can't create restore list\n"); } return true; }
/* * Can use an argument to filter on JobType * .jobs [type=B] */ bool dot_jobs_cmd(UAContext *ua, const char *cmd) { int pos; JOBRES *job; uint32_t type = 0; if ((pos = find_arg_with_value(ua, "type")) >= 0) { type = ua->argv[pos][0]; } LockRes(); ua->send->array_start("jobs"); foreach_res(job, R_JOB) { if (!type || type == job->JobType) { if (acl_access_ok(ua, Job_ACL, job->name())) { ua->send->object_start(); ua->send->object_key_value("name", job->name(), "%s\n"); ua->send->object_end(); } } } ua->send->array_end("jobs"); UnlockRes(); return true; }
/* .bvfs_cleanup path=b2XXXXX */ static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd) { int i; if ((i = find_arg_with_value(ua, "path")) >= 0) { open_client_db(ua); Bvfs fs(ua->jcr, ua->db); fs.drop_restore_list(ua->argv[i]); } return true; }
/* * This gets the client name from which the backup was made */ static bool get_client_name(UAContext *ua, RESTORE_CTX *rx) { int i; CLIENT_DBR cr; memset(&cr, 0, sizeof(cr)); /* * If no client name specified yet, get it now */ if (!rx->ClientName) { /* * Try command line argument */ i = find_arg_with_value(ua, NT_("client")); if (i < 0) { i = find_arg_with_value(ua, NT_("backupclient")); } if (i >= 0) { if (!is_name_valid(ua->argv[i], ua->errmsg)) { ua->error_msg("%s argument: %s", ua->argk[i], ua->errmsg); return false; } bstrncpy(cr.Name, ua->argv[i], sizeof(cr.Name)); if (!db_get_client_record(ua->jcr, ua->db, &cr)) { ua->error_msg("invalid %s argument: %s\n", ua->argk[i], ua->argv[i]); return false; } rx->ClientName = bstrdup(ua->argv[i]); return true; } if (!get_client_dbr(ua, &cr)) { return false; } rx->ClientName = bstrdup(cr.Name); } return true; }
/* * Returns: 1 if Volume purged * 0 if Volume not purged */ bool purge_jobs_from_volume(UAContext *ua, MEDIA_DBR *mr, bool force) { POOL_MEM query(PM_MESSAGE); db_list_ctx lst; char *jobids=NULL; int i; bool purged = false; bool status; status = bstrcmp(mr->VolStatus, "Append") || bstrcmp(mr->VolStatus, "Full") || bstrcmp(mr->VolStatus, "Used") || bstrcmp(mr->VolStatus, "Error"); if (!status) { ua->error_msg(_("\nVolume \"%s\" has VolStatus \"%s\" and cannot be purged.\n" "The VolStatus must be: Append, Full, Used, or Error to be purged.\n"), mr->VolumeName, mr->VolStatus); return 0; } /* * Check if he wants to purge a single jobid */ i = find_arg_with_value(ua, "jobid"); if (i >= 0 && is_a_number_list(ua->argv[i])) { jobids = ua->argv[i]; } else { /* * Purge ALL JobIds */ if (!db_get_volume_jobids(ua->jcr, ua->db, mr, &lst)) { ua->error_msg("%s", db_strerror(ua->db)); Dmsg0(050, "Count failed\n"); goto bail_out; } jobids = lst.list; } if (*jobids) { purge_jobs_from_catalog(ua, jobids); } ua->info_msg(_("%d File%s on Volume \"%s\" purged from catalog.\n"), lst.count, lst.count<=1?"":"s", mr->VolumeName); purged = is_volume_purged(ua, mr, force); bail_out: return purged; }
/* * Update long term statistics */ static bool update_stats(UAContext *ua) { int i = find_arg_with_value(ua, NT_("days")); utime_t since=0; if (i >= 0) { since = atoi(ua->argv[i]) * 24*60*60; } int nb = db_update_stats(ua->jcr, ua->db, since); ua->info_msg(_("Updating %i job(s).\n"), nb); return true; }
/* * This is where we pick up a client name to restore to. */ static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx) { /* Start with same name as backup client */ bstrncpy(rx.RestoreClientName, rx.ClientName, sizeof(rx.RestoreClientName)); /* try command line argument */ int i = find_arg_with_value(ua, NT_("restoreclient")); if (i >= 0) { if (!is_name_valid(ua->argv[i], &ua->errmsg)) { ua->error_msg("%s argument: %s", ua->argk[i], ua->errmsg); return 0; } bstrncpy(rx.RestoreClientName, ua->argv[i], sizeof(rx.RestoreClientName)); return 1; } return 1; }
/* * Can use an argument to filter on JobType * .jobs [type=B] */ static bool jobscmd(UAContext *ua, const char *cmd) { JOBRES *job; uint32_t type = 0; int pos; if ((pos = find_arg_with_value(ua, "type")) >= 0) { type = ua->argv[pos][0]; } LockRes(); foreach_res(job, R_JOB) { if (!type || type == job->JobType) { if (acl_access_ok(ua, Job_ACL, job->name())) { ua->send_msg("%s\n", job->name()); } } } UnlockRes(); return true; }
static void make_unique_restore_filename(UAContext *ua, POOL_MEM &fname) { JCR *jcr = ua->jcr; int i = find_arg_with_value(ua, "bootstrap"); if (i >= 0) { Mmsg(fname, "%s", ua->argv[i]); jcr->unlink_bsr = false; } else { P(mutex); uniq++; V(mutex); Mmsg(fname, "%s/%s.restore.%u.bsr", working_directory, my_name, uniq); jcr->unlink_bsr = true; } if (jcr->RestoreBootstrap) { free(jcr->RestoreBootstrap); } jcr->RestoreBootstrap = bstrdup(fname.c_str()); }
bool dot_bvfs_update_cmd(UAContext *ua, const char *cmd) { int pos; if (!open_client_db(ua, true)) { return 1; } pos = find_arg_with_value(ua, "jobid"); if (pos != -1 && is_a_number_list(ua->argv[pos])) { if (!bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos])) { ua->error_msg("ERROR: BVFS reported a problem for %s\n", ua->argv[pos]); } } else { /* update cache for all jobids */ bvfs_update_cache(ua->jcr, ua->db); } return true; }
/* * .bvfs_lsfiles jobid=1,2,3,4 pathid=10 * .bvfs_lsfiles jobid=1,2,3,4 path=/ */ bool dot_bvfs_lsfiles_cmd(UAContext *ua, const char *cmd) { DBId_t pathid = 0; int limit = 2000, offset = 0; char *path = NULL, *jobid = NULL; char *pattern = NULL; int i; if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &limit, &offset)) { ua->error_msg("Can't find jobid, pathid or path argument\n"); return false; /* not enough param */ } if ((i = find_arg_with_value(ua, "pattern")) >= 0) { pattern = ua->argv[i]; } if (!ua->guid) { ua->guid = new_guid_list(); } Bvfs fs(ua->jcr, ua->db); fs.set_jobids(jobid); fs.set_handler(bvfs_result_handler, ua); fs.set_limit(limit); if (pattern) { fs.set_pattern(pattern); } if (pathid) { fs.ch_dir(pathid); } else { fs.ch_dir(path); } fs.set_offset(offset); ua->send->array_start("files"); fs.ls_files(); ua->send->array_end("files"); return true; }
/* * This is where we pick up a client name to restore to. */ static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx) { int i; /* * Try command line argument */ i = find_arg_with_value(ua, NT_("restoreclient")); if (i >= 0) { if (!is_name_valid(ua->argv[i], &ua->errmsg)) { ua->error_msg("%s argument: %s", ua->argk[i], ua->errmsg); return 0; } rx.RestoreClientName = bstrdup(ua->argv[i]); return 1; } rx.RestoreClientName = bstrdup(rx.ClientName); return 1; }
static inline bool configure_export(UAContext *ua) { bool result = false; int i; i = find_arg_with_value(ua, NT_("client")); if (i < 0) { configure_export_usage(ua); return false; } if (!ua->GetClientResWithName(ua->argv[i])) { configure_export_usage(ua); return false; } ua->send->object_start("configure"); result = configure_create_fd_resource(ua, ua->argv[i]); ua->send->object_end("configure"); return result; }
/* * This is where we pick up a client name to restore to. */ static bool get_restore_client_name(UAContext *ua, RESTORE_CTX &rx) { int i; /* * Try command line argument */ i = find_arg_with_value(ua, NT_("restoreclient")); if (i >= 0) { if (!is_name_valid(ua->argv[i], ua->errmsg)) { ua->error_msg("%s argument: %s", ua->argk[i], ua->errmsg); return false; } if (!GetClientResWithName(ua->argv[i])) { ua->error_msg("invalid %s argument: %s\n", ua->argk[i], ua->argv[i]); return false; } rx.RestoreClientName = bstrdup(ua->argv[i]); return true; } rx.RestoreClientName = bstrdup(rx.ClientName); return true; }
/* * .bvfs_get_jobids jobid=1 * -> returns needed jobids to restore * .bvfs_get_jobids jobid=1 all * -> returns needed jobids to restore with all filesets a JobId=1 time * .bvfs_get_jobids ujobid=JobName * -> returns needed jobids to restore */ static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd) { JOB_DBR jr; db_list_ctx jobids, tempids; int pos; char ed1[50]; POOL_MEM query; dbid_list ids; /* Store all FileSetIds for this client */ if (!open_client_db(ua, true)) { return true; } memset(&jr, 0, sizeof(jr)); if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) { bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job)); } if ((pos = find_arg_with_value(ua, "jobid")) >= 0) { jr.JobId = str_to_int64(ua->argv[pos]); } if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), ua->cmd, db_strerror(ua->db)); return true; } /* When in level base, we don't rely on any Full/Incr/Diff */ if (jr.JobLevel == L_BASE) { ua->send_msg("%s\n", edit_int64(jr.JobId, ed1)); return true; } /* If we have the "all" option, we do a search on all defined fileset * for this client */ if (find_arg(ua, "all") > 0) { edit_int64(jr.ClientId, ed1); Mmsg(query, uar_sel_filesetid, ed1); db_get_query_dbids(ua->jcr, ua->db, query, ids); } else { ids.num_ids = 1; ids.DBId[0] = jr.FileSetId; } jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ /* Foreach different FileSet, we build a restore jobid list */ for (int i=0; i < ids.num_ids; i++) { jr.FileSetId = ids.DBId[i]; if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids)) { return true; } jobids.add(tempids); } ua->send_msg("%s\n", jobids.list); return true; }
/* * Update a Job record -- allows you to change the * date fields in a Job record. This helps when * providing migration from other vendors. */ static bool update_job(UAContext *ua) { int i; char ed1[50], ed2[50]; POOL_MEM cmd(PM_MESSAGE); JOB_DBR jr; CLIENT_DBR cr; utime_t StartTime; char *client_name = NULL; char *start_time = NULL; const char *kw[] = { NT_("starttime"), /* 0 */ NT_("client"), /* 1 */ NULL }; Dmsg1(200, "cmd=%s\n", ua->cmd); i = find_arg_with_value(ua, NT_("jobid")); if (i < 0) { ua->error_msg(_("Expect JobId keyword, not found.\n")); return false; } memset(&jr, 0, sizeof(jr)); memset(&cr, 0, sizeof(cr)); jr.JobId = str_to_int64(ua->argv[i]); if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg("%s", db_strerror(ua->db)); return false; } for (i=0; kw[i]; i++) { int j; if ((j=find_arg_with_value(ua, kw[i])) >= 0) { switch (i) { case 0: /* start time */ start_time = ua->argv[j]; break; case 1: /* Client name */ client_name = ua->argv[j]; break; } } } if (!client_name && !start_time) { ua->error_msg(_("Neither Client nor StartTime specified.\n")); return 0; } if (client_name) { if (!get_client_dbr(ua, &cr)) { return false; } jr.ClientId = cr.ClientId; } if (start_time) { utime_t delta_start; StartTime = str_to_utime(start_time); if (StartTime == 0) { ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]); return false; } delta_start = StartTime - jr.StartTime; Dmsg3(200, "ST=%lld jr.ST=%lld delta=%lld\n", StartTime, (utime_t)jr.StartTime, delta_start); jr.StartTime = (time_t)StartTime; jr.SchedTime += (time_t)delta_start; jr.EndTime += (time_t)delta_start; jr.JobTDate += delta_start; /* Convert to DB times */ bstrutime(jr.cStartTime, sizeof(jr.cStartTime), jr.StartTime); bstrutime(jr.cSchedTime, sizeof(jr.cSchedTime), jr.SchedTime); bstrutime(jr.cEndTime, sizeof(jr.cEndTime), jr.EndTime); } Mmsg(cmd, "UPDATE Job SET ClientId=%s,StartTime='%s',SchedTime='%s'," "EndTime='%s',JobTDate=%s WHERE JobId=%s", edit_int64(jr.ClientId, ed1), jr.cStartTime, jr.cSchedTime, jr.cEndTime, edit_uint64(jr.JobTDate, ed1), edit_int64(jr.JobId, ed2)); if (!db_sql_query(ua->db, cmd.c_str(), NULL, NULL)) { ua->error_msg("%s", db_strerror(ua->db)); return false; } return true; }
/* * Update Slots corresponding to Volumes in autochanger */ void update_slots(UAContext *ua) { USTORE store; vol_list_t *vl, *vol_list = NULL; MEDIA_DBR mr; char *slot_list; bool scan; int max_slots; int drive; int Enabled = 1; bool have_enabled; int i; if (!open_client_db(ua)) { return; } store.store = get_storage_resource(ua, true/*arg is storage*/); if (!store.store) { return; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); drive = get_storage_drive(ua, store.store); scan = find_arg(ua, NT_("scan")) >= 0; if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) { Enabled = get_enabled(ua, ua->argv[i]); if (Enabled < 0) { return; } have_enabled = true; } else { have_enabled = false; } max_slots = get_num_slots_from_SD(ua); Dmsg1(100, "max_slots=%d\n", max_slots); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(max_slots+1); if (!get_user_slot_list(ua, slot_list, max_slots)) { free(slot_list); return; } vol_list = get_vol_list_from_SD(ua, scan); if (!vol_list) { ua->warning_msg(_("No Volumes found to label, or no barcodes.\n")); goto bail_out; } /* First zap out any InChanger with StorageId=0 */ db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0", NULL, NULL); /* Walk through the list updating the media records */ for (vl=vol_list; vl; vl=vl->next) { if (vl->Slot > max_slots) { ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots); continue; } /* Check if user wants us to look at this slot */ if (!slot_list[vl->Slot]) { Dmsg1(100, "Skipping slot=%d\n", vl->Slot); continue; } /* If scanning, we read the label rather than the barcode */ if (scan) { if (vl->VolName) { free(vl->VolName); vl->VolName = NULL; } vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive); Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot); } slot_list[vl->Slot] = 0; /* clear Slot */ memset(&mr, 0, sizeof(mr)); mr.Slot = vl->Slot; mr.InChanger = 1; mr.StorageId = store.store->StorageId; /* Set InChanger to zero for this Slot */ db_lock(ua->db); db_make_inchanger_unique(ua->jcr, ua->db, &mr); db_unlock(ua->db); if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot); ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot); continue; } memset(&mr, 0, sizeof(mr)); bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); db_lock(ua->db); if (db_get_media_record(ua->jcr, ua->db, &mr)) { if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) { mr.Slot = vl->Slot; mr.InChanger = 1; mr.StorageId = store.store->StorageId; if (have_enabled) { mr.Enabled = Enabled; } if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg("%s", db_strerror(ua->db)); } else { ua->info_msg(_( "Catalog record for Volume \"%s\" updated to reference slot %d.\n"), mr.VolumeName, mr.Slot); } } else { ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"), mr.VolumeName); } db_unlock(ua->db); continue; } else { ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"), mr.VolumeName, vl->Slot); } db_unlock(ua->db); } memset(&mr, 0, sizeof(mr)); mr.InChanger = 1; mr.StorageId = store.store->StorageId; db_lock(ua->db); for (int i=1; i <= max_slots; i++) { if (slot_list[i]) { mr.Slot = i; /* Set InChanger to zero for this Slot */ db_make_inchanger_unique(ua->jcr, ua->db, &mr); } } db_unlock(ua->db); bail_out: free_vol_list(vol_list); free(slot_list); close_sd_bsock(ua); return; }
/* * Update a media record -- allows you to change the * Volume status. E.g. if you want Bacula to stop * writing on the volume, set it to anything other * than Append. */ static int update_volume(UAContext *ua) { MEDIA_DBR mr; POOL *pool; POOL_DBR pr; POOLMEM *query; POOL_MEM ret; char buf[1000]; char ed1[130]; bool done = false; int i; const char *kw[] = { NT_("VolStatus"), /* 0 */ NT_("VolRetention"), /* 1 */ NT_("VolUse"), /* 2 */ NT_("MaxVolJobs"), /* 3 */ NT_("MaxVolFiles"), /* 4 */ NT_("MaxVolBytes"), /* 5 */ NT_("Recycle"), /* 6 */ NT_("InChanger"), /* 7 */ NT_("Slot"), /* 8 */ NT_("Pool"), /* 9 */ NT_("FromPool"), /* 10 */ NT_("AllFromPool"), /* 11 !!! see below !!! */ NT_("Enabled"), /* 12 */ NT_("RecyclePool"), /* 13 */ NT_("ActionOnPurge"), /* 14 */ NULL }; #define AllFromPool 11 /* keep this updated with above */ for (i=0; kw[i]; i++) { int j; POOL_DBR pr; if ((j=find_arg_with_value(ua, kw[i])) > 0) { /* If all from pool don't select a media record */ if (i != AllFromPool && !select_media_dbr(ua, &mr)) { return 0; } switch (i) { case 0: update_volstatus(ua, ua->argv[j], &mr); break; case 1: update_volretention(ua, ua->argv[j], &mr); break; case 2: update_voluseduration(ua, ua->argv[j], &mr); break; case 3: update_volmaxjobs(ua, ua->argv[j], &mr); break; case 4: update_volmaxfiles(ua, ua->argv[j], &mr); break; case 5: update_volmaxbytes(ua, ua->argv[j], &mr); break; case 6: update_volrecycle(ua, ua->argv[j], &mr); break; case 7: update_volinchanger(ua, ua->argv[j], &mr); break; case 8: update_volslot(ua, ua->argv[j], &mr); break; case 9: memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); break; } update_vol_pool(ua, ua->argv[j], &mr, &pr); break; case 10: update_vol_from_pool(ua, &mr); return 1; case 11: update_all_vols_from_pool(ua, ua->argv[j]); return 1; case 12: update_volenabled(ua, ua->argv[j], &mr); break; case 13: update_vol_recyclepool(ua, ua->argv[j], &mr); break; case 14: update_vol_actiononpurge(ua, ua->argv[j], &mr); break; } done = true; } } /* Allow user to simply update all volumes */ if (find_arg(ua, NT_("fromallpools")) > 0) { update_all_vols(ua); return 1; } for ( ; !done; ) { start_prompt(ua, _("Parameters to modify:\n")); add_prompt(ua, _("Volume Status")); /* 0 */ add_prompt(ua, _("Volume Retention Period")); /* 1 */ add_prompt(ua, _("Volume Use Duration")); /* 2 */ add_prompt(ua, _("Maximum Volume Jobs")); /* 3 */ add_prompt(ua, _("Maximum Volume Files")); /* 4 */ add_prompt(ua, _("Maximum Volume Bytes")); /* 5 */ add_prompt(ua, _("Recycle Flag")); /* 6 */ add_prompt(ua, _("Slot")); /* 7 */ add_prompt(ua, _("InChanger Flag")); /* 8 */ add_prompt(ua, _("Volume Files")); /* 9 */ add_prompt(ua, _("Pool")); /* 10 */ add_prompt(ua, _("Volume from Pool")); /* 11 */ add_prompt(ua, _("All Volumes from Pool")); /* 12 */ add_prompt(ua, _("All Volumes from all Pools")); /* 13 */ add_prompt(ua, _("Enabled")), /* 14 */ add_prompt(ua, _("RecyclePool")), /* 15 */ add_prompt(ua, _("Action On Purge")), /* 16 */ add_prompt(ua, _("Done")); /* 17 */ i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0); /* For All Volumes, All Volumes from Pool, and Done, we don't need * a Volume record */ if ( i != 12 && i != 13 && i != 17) { if (!select_media_dbr(ua, &mr)) { /* Get Volume record */ return 0; } ua->info_msg(_("Updating Volume \"%s\"\n"), mr.VolumeName); } switch (i) { case 0: /* Volume Status */ /* Modify Volume Status */ ua->info_msg(_("Current Volume status is: %s\n"), mr.VolStatus); start_prompt(ua, _("Possible Values are:\n")); add_prompt(ua, NT_("Append")); add_prompt(ua, NT_("Archive")); add_prompt(ua, NT_("Disabled")); add_prompt(ua, NT_("Full")); add_prompt(ua, NT_("Used")); add_prompt(ua, NT_("Cleaning")); if (strcmp(mr.VolStatus, NT_("Purged")) == 0) { add_prompt(ua, NT_("Recycle")); } add_prompt(ua, NT_("Read-Only")); if (do_prompt(ua, "", _("Choose new Volume Status"), ua->cmd, sizeof(mr.VolStatus)) < 0) { return 1; } update_volstatus(ua, ua->cmd, &mr); break; case 1: /* Retention */ ua->info_msg(_("Current retention period is: %s\n"), edit_utime(mr.VolRetention, ed1, sizeof(ed1))); if (!get_cmd(ua, _("Enter Volume Retention period: "))) { return 0; } update_volretention(ua, ua->cmd, &mr); break; case 2: /* Use Duration */ ua->info_msg(_("Current use duration is: %s\n"), edit_utime(mr.VolUseDuration, ed1, sizeof(ed1))); if (!get_cmd(ua, _("Enter Volume Use Duration: "))) { return 0; } update_voluseduration(ua, ua->cmd, &mr); break; case 3: /* Max Jobs */ ua->info_msg(_("Current max jobs is: %u\n"), mr.MaxVolJobs); if (!get_pint(ua, _("Enter new Maximum Jobs: "))) { return 0; } update_volmaxjobs(ua, ua->cmd, &mr); break; case 4: /* Max Files */ ua->info_msg(_("Current max files is: %u\n"), mr.MaxVolFiles); if (!get_pint(ua, _("Enter new Maximum Files: "))) { return 0; } update_volmaxfiles(ua, ua->cmd, &mr); break; case 5: /* Max Bytes */ ua->info_msg(_("Current value is: %s\n"), edit_uint64(mr.MaxVolBytes, ed1)); if (!get_cmd(ua, _("Enter new Maximum Bytes: "))) { return 0; } update_volmaxbytes(ua, ua->cmd, &mr); break; case 6: /* Recycle */ ua->info_msg(_("Current recycle flag is: %s\n"), mr.Recycle==1?_("yes"):_("no")); if (!get_yesno(ua, _("Enter new Recycle status: "))) { return 0; } update_volrecycle(ua, ua->cmd, &mr); break; case 7: /* Slot */ ua->info_msg(_("Current Slot is: %d\n"), mr.Slot); if (!get_pint(ua, _("Enter new Slot: "))) { return 0; } update_volslot(ua, ua->cmd, &mr); break; case 8: /* InChanger */ ua->info_msg(_("Current InChanger flag is: %d\n"), mr.InChanger); bsnprintf(buf, sizeof(buf), _("Set InChanger flag for Volume \"%s\": yes/no: "), mr.VolumeName); if (!get_yesno(ua, buf)) { return 0; } mr.InChanger = ua->pint32_val; /* * Make sure to use db_update... rather than doing this directly, * so that any Slot is handled correctly. */ if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg(_("Error updating media record Slot: ERR=%s"), db_strerror(ua->db)); } else { ua->info_msg(_("New InChanger flag is: %d\n"), mr.InChanger); } break; case 9: /* Volume Files */ int32_t VolFiles; ua->warning_msg(_("Warning changing Volume Files can result\n" "in loss of data on your Volume\n\n")); ua->info_msg(_("Current Volume Files is: %u\n"), mr.VolFiles); if (!get_pint(ua, _("Enter new number of Files for Volume: "))) { return 0; } VolFiles = ua->pint32_val; if (VolFiles != (int)(mr.VolFiles + 1)) { ua->warning_msg(_("Normally, you should only increase Volume Files by one!\n")); if (!get_yesno(ua, _("Increase Volume Files? (yes/no): ")) || ua->pint32_val == 0) { break; } } query = get_pool_memory(PM_MESSAGE); Mmsg(query, "UPDATE Media SET VolFiles=%u WHERE MediaId=%s", VolFiles, edit_int64(mr.MediaId, ed1)); if (!db_sql_query(ua->db, query, NULL, NULL)) { ua->error_msg("%s", db_strerror(ua->db)); } else { ua->info_msg(_("New Volume Files is: %u\n"), VolFiles); } free_pool_memory(query); break; case 10: /* Volume's Pool */ memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.PoolId; if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->error_msg("%s", db_strerror(ua->db)); return 0; } ua->info_msg(_("Current Pool is: %s\n"), pr.Name); if (!get_cmd(ua, _("Enter new Pool name: "))) { return 0; } update_vol_pool(ua, ua->cmd, &mr, &pr); return 1; case 11: update_vol_from_pool(ua, &mr); return 1; case 12: pool = select_pool_resource(ua); if (pool) { update_all_vols_from_pool(ua, pool->name()); } return 1; case 13: update_all_vols(ua); return 1; case 14: ua->info_msg(_("Current Enabled is: %d\n"), mr.Enabled); if (!get_cmd(ua, _("Enter new Enabled: "))) { return 0; } if (strcasecmp(ua->cmd, "yes") == 0 || strcasecmp(ua->cmd, "true") == 0) { mr.Enabled = 1; } else if (strcasecmp(ua->cmd, "no") == 0 || strcasecmp(ua->cmd, "false") == 0) { mr.Enabled = 0; } else if (strcasecmp(ua->cmd, "archived") == 0) { mr.Enabled = 2; } else { mr.Enabled = atoi(ua->cmd); } update_volenabled(ua, ua->cmd, &mr); break; case 15: memset(&pr, 0, sizeof(POOL_DBR)); pr.PoolId = mr.RecyclePoolId; if (db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->info_msg(_("Current RecyclePool is: %s\n"), pr.Name); } else { ua->info_msg(_("No current RecyclePool\n")); } if (!select_pool_dbr(ua, &pr, NT_("recyclepool"))) { return 0; } update_vol_recyclepool(ua, pr.Name, &mr); return 1; case 16: pm_strcpy(ret, ""); ua->info_msg(_("Current ActionOnPurge is: %s\n"), action_on_purge_to_string(mr.ActionOnPurge, ret)); if (!get_cmd(ua, _("Enter new ActionOnPurge (one of: Truncate, None): "))) { return 0; } update_vol_actiononpurge(ua, ua->cmd, &mr); break; default: /* Done or error */ ua->info_msg(_("Selection terminated.\n")); return 1; } } return 1; }
static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) { POOLMEM *VolumeName; int jobid, n; int i, j; JOB_DBR jr; POOL_DBR pr; MEDIA_DBR mr; if (!open_client_db(ua)) return 1; memset(&jr, 0, sizeof(jr)); memset(&pr, 0, sizeof(pr)); Dmsg1(20, "list: %s\n", cmd); if (!ua->db) { ua->error_msg(_("Hey! DB is NULL\n")); } /* Apply any limit */ j = find_arg_with_value(ua, NT_("limit")); if (j >= 0) { jr.limit = atoi(ua->argv[j]); } /* Scan arguments looking for things to do */ for (i=1; i<ua->argc; i++) { /* List JOBS */ if (bstrcasecmp(ua->argk[i], NT_("jobs"))) { db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); /* List JOBTOTALS */ } else if (bstrcasecmp(ua->argk[i], NT_("jobtotals"))) { db_list_job_totals(ua->jcr, ua->db, &jr, prtit, ua); /* List JOBID=nn */ } else if (bstrcasecmp(ua->argk[i], NT_("jobid"))) { if (ua->argv[i]) { jobid = str_to_int64(ua->argv[i]); if (jobid > 0) { jr.JobId = jobid; db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); } } /* List JOB=xxx */ } else if ((bstrcasecmp(ua->argk[i], NT_("job")) || bstrcasecmp(ua->argk[i], NT_("jobname"))) && ua->argv[i]) { bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH); jr.JobId = 0; db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); /* List UJOBID=xxx */ } else if (bstrcasecmp(ua->argk[i], NT_("ujobid")) && ua->argv[i]) { bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH); jr.JobId = 0; db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist); /* List Base files */ } else if (bstrcasecmp(ua->argk[i], NT_("basefiles"))) { /* TODO: cleanup this block */ for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } if (jobid > 0) { db_list_base_files_for_job(ua->jcr, ua->db, jobid, prtit, ua); } } /* List FILES */ } else if (bstrcasecmp(ua->argk[i], NT_("files"))) { for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } if (jobid > 0) { db_list_files_for_job(ua->jcr, ua->db, jobid, prtit, ua); } } /* List JOBMEDIA */ } else if (bstrcasecmp(ua->argk[i], NT_("jobmedia"))) { bool done = false; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } db_list_jobmedia_records(ua->jcr, ua->db, jobid, prtit, ua, llist); done = true; } if (!done) { /* List for all jobs (jobid=0) */ db_list_jobmedia_records(ua->jcr, ua->db, 0, prtit, ua, llist); } /* List JOBLOG */ } else if (bstrcasecmp(ua->argk[i], NT_("joblog"))) { bool done = false; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } db_list_joblog_records(ua->jcr, ua->db, jobid, prtit, ua, llist); done = true; } if (!done) { /* List for all jobs (jobid=0) */ db_list_joblog_records(ua->jcr, ua->db, 0, prtit, ua, llist); } /* List POOLS */ } else if (bstrcasecmp(ua->argk[i], NT_("pool")) || bstrcasecmp(ua->argk[i], NT_("pools"))) { POOL_DBR pr; memset(&pr, 0, sizeof(pr)); if (ua->argv[i]) { bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name)); } db_list_pool_records(ua->jcr, ua->db, &pr, prtit, ua, llist); } else if (bstrcasecmp(ua->argk[i], NT_("clients"))) { db_list_client_records(ua->jcr, ua->db, prtit, ua, llist); /* List MEDIA or VOLUMES */ } else if (bstrcasecmp(ua->argk[i], NT_("media")) || bstrcasecmp(ua->argk[i], NT_("volume")) || bstrcasecmp(ua->argk[i], NT_("volumes"))) { bool done = false; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("ujobid")) && ua->argv[j]) { bstrncpy(jr.Job, ua->argv[j], MAX_NAME_LENGTH); jr.JobId = 0; db_get_job_record(ua->jcr, ua->db, &jr); jobid = jr.JobId; } else if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { jobid = str_to_int64(ua->argv[j]); } else { continue; } VolumeName = get_pool_memory(PM_FNAME); n = db_get_job_volume_names(ua->jcr, ua->db, jobid, &VolumeName); ua->send_msg(_("Jobid %d used %d Volume(s): %s\n"), jobid, n, VolumeName); free_pool_memory(VolumeName); done = true; } /* if no job or jobid keyword found, then we list all media */ if (!done) { int num_pools; uint32_t *ids; /* List a specific volume? */ if (ua->argv[i]) { bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName)); db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist); return 1; } /* Is a specific pool wanted? */ for (i=1; i<ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("pool"))) { if (!get_pool_dbr(ua, &pr)) { ua->error_msg(_("No Pool specified.\n")); return 1; } mr.PoolId = pr.PoolId; db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist); return 1; } } /* List Volumes in all pools */ if (!db_get_pool_ids(ua->jcr, ua->db, &num_pools, &ids)) { ua->error_msg(_("Error obtaining pool ids. ERR=%s\n"), db_strerror(ua->db)); return 1; } if (num_pools <= 0) { return 1; } for (i=0; i < num_pools; i++) { pr.PoolId = ids[i]; if (db_get_pool_record(ua->jcr, ua->db, &pr)) { ua->send_msg(_("Pool: %s\n"), pr.Name); } mr.PoolId = ids[i]; db_list_media_records(ua->jcr, ua->db, &mr, prtit, ua, llist); } free(ids); return 1; } /* List next volume */ } else if (bstrcasecmp(ua->argk[i], NT_("nextvol")) || bstrcasecmp(ua->argk[i], NT_("nextvolume"))) { n = 1; j = find_arg_with_value(ua, NT_("days")); if (j >= 0) { n = atoi(ua->argv[j]); if ((n < 0) || (n > 50)) { ua->warning_msg(_("Ignoring invalid value for days. Max is 50.\n")); n = 1; } } list_nextvol(ua, n); } else if (bstrcasecmp(ua->argk[i], NT_("copies"))) { char *jobids = NULL; uint32_t limit=0; for (j=i+1; j<ua->argc; j++) { if (bstrcasecmp(ua->argk[j], NT_("jobid")) && ua->argv[j]) { if (is_a_number_list(ua->argv[j])) { jobids = ua->argv[j]; } } else if (bstrcasecmp(ua->argk[j], NT_("limit")) && ua->argv[j]) { limit = atoi(ua->argv[j]); } } db_list_copies_records(ua->jcr,ua->db,limit,jobids,prtit,ua,llist); } else if (bstrcasecmp(ua->argk[i], NT_("limit")) || bstrcasecmp(ua->argk[i], NT_("days"))) { /* Ignore it */ } else { ua->error_msg(_("Unknown list keyword: %s\n"), NPRT(ua->argk[i])); } } return 1; }
/* * Restore files */ bool restore_cmd(UAContext *ua, const char *cmd) { RESTORE_CTX rx; /* restore context */ POOL_MEM buf; JOBRES *job; int i; JCR *jcr = ua->jcr; char *escaped_bsr_name = NULL; char *escaped_where_name = NULL; char *strip_prefix, *add_prefix, *add_suffix, *regexp; strip_prefix = add_prefix = add_suffix = regexp = NULL; memset(&rx, 0, sizeof(rx)); rx.path = get_pool_memory(PM_FNAME); rx.fname = get_pool_memory(PM_FNAME); rx.JobIds = get_pool_memory(PM_FNAME); rx.JobIds[0] = 0; rx.BaseJobIds = get_pool_memory(PM_FNAME); rx.query = get_pool_memory(PM_FNAME); rx.bsr = new_bsr(); i = find_arg_with_value(ua, "comment"); if (i >= 0) { rx.comment = ua->argv[i]; if (!is_comment_legal(ua, rx.comment)) { goto bail_out; } } i = find_arg_with_value(ua, "backupformat"); if (i >= 0) { rx.backup_format = ua->argv[i]; } i = find_arg_with_value(ua, "where"); if (i >= 0) { rx.where = ua->argv[i]; } i = find_arg_with_value(ua, "replace"); if (i >= 0) { rx.replace = ua->argv[i]; } i = find_arg_with_value(ua, "pluginoptions"); if (i >= 0) { rx.plugin_options = ua->argv[i]; } i = find_arg_with_value(ua, "strip_prefix"); if (i >= 0) { strip_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_prefix"); if (i >= 0) { add_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_suffix"); if (i >= 0) { add_suffix = ua->argv[i]; } i = find_arg_with_value(ua, "regexwhere"); if (i >= 0) { rx.RegexWhere = ua->argv[i]; } if (strip_prefix || add_suffix || add_prefix) { int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix); regexp = (char *)bmalloc(len * sizeof(char)); bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix); rx.RegexWhere = regexp; } /* TODO: add acl for regexwhere ? */ if (rx.RegexWhere) { if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere, true)) { ua->error_msg(_("\"RegexWhere\" specification not authorized.\n")); goto bail_out; } } if (rx.where) { if (!acl_access_ok(ua, Where_ACL, rx.where, true)) { ua->error_msg(_("\"where\" specification not authorized.\n")); goto bail_out; } } if (!open_client_db(ua, true)) { goto bail_out; } /* Ensure there is at least one Restore Job */ LockRes(); foreach_res(job, R_JOB) { if (job->JobType == JT_RESTORE) { if (!rx.restore_job) { rx.restore_job = job; } rx.restore_jobs++; } } UnlockRes(); if (!rx.restore_jobs) { ua->error_msg(_( "No Restore Job Resource found in bareos-dir.conf.\n" "You must create at least one before running this command.\n")); goto bail_out; } /* * Request user to select JobIds or files by various different methods * last 20 jobs, where File saved, most recent backup, ... * In the end, a list of files are pumped into * add_findex() */ switch (user_select_jobids_or_files(ua, &rx)) { case 0: /* error */ goto bail_out; case 1: /* selected by jobid */ get_and_display_basejobs(ua, &rx); if (!build_directory_tree(ua, &rx)) { ua->send_msg(_("Restore not done.\n")); goto bail_out; } break; case 2: /* selected by filename, no tree needed */ break; } if (rx.bsr->JobId) { char ed1[50]; if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */ ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n")); goto bail_out; } if (!(rx.selected_files = write_bsr_file(ua, rx))) { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } display_bsr_info(ua, rx); /* display vols needed, etc */ if (rx.selected_files==1) { ua->info_msg(_("\n1 file selected to be restored.\n\n")); } else { ua->info_msg(_("\n%s files selected to be restored.\n\n"), edit_uint64_with_commas(rx.selected_files, ed1)); } } else { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } if (rx.restore_jobs == 1) { job = rx.restore_job; } else { job = get_restore_job(ua); } if (!job) { goto bail_out; } if (!get_client_name(ua, &rx)) { goto bail_out; } if (!rx.ClientName) { ua->error_msg(_("No Client resource found!\n")); goto bail_out; } if (!get_restore_client_name(ua, rx)) { goto bail_out; } escaped_bsr_name = escape_filename(jcr->RestoreBootstrap); Mmsg(ua->cmd, "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\"" " bootstrap=\"%s\" files=%u catalog=\"%s\"", job->name(), rx.ClientName, rx.RestoreClientName, rx.store?rx.store->name():"", escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name()); /* * Build run command */ if (rx.backup_format) { Mmsg(buf, " backupformat=%s", rx.backup_format); pm_strcat(ua->cmd, buf); } pm_strcpy(buf, ""); if (rx.RegexWhere) { escaped_where_name = escape_filename(rx.RegexWhere); Mmsg(buf, " regexwhere=\"%s\"", escaped_where_name ? escaped_where_name : rx.RegexWhere); } else if (rx.where) { escaped_where_name = escape_filename(rx.where); Mmsg(buf," where=\"%s\"", escaped_where_name ? escaped_where_name : rx.where); } pm_strcat(ua->cmd, buf); if (rx.replace) { Mmsg(buf, " replace=%s", rx.replace); pm_strcat(ua->cmd, buf); } if (rx.plugin_options) { Mmsg(buf, " pluginoptions=%s", rx.plugin_options); pm_strcat(ua->cmd, buf); } if (rx.comment) { Mmsg(buf, " comment=\"%s\"", rx.comment); pm_strcat(ua->cmd, buf); } if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } if (find_arg(ua, NT_("yes")) > 0) { pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */ } Dmsg1(200, "Submitting: %s\n", ua->cmd); /* * Transfer jobids to jcr to for picking up restore objects */ jcr->JobIds = rx.JobIds; rx.JobIds = NULL; parse_ua_args(ua); run_cmd(ua, ua->cmd); free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return true; bail_out: if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return false; }
/* * Prune Directory meta data records from the database. */ static bool prune_directory(UAContext *ua, CLIENTRES *client) { int i, len; CLIENT_DBR cr; char *prune_topdir = NULL; POOL_MEM query(PM_MESSAGE), temp(PM_MESSAGE); bool recursive = false; bool retval = false; /* * See if a client was selected. */ if (!client) { if (!get_yesno(ua, _("No client restriction given really remove " "directory for all clients (yes/no): ")) || ua->pint32_val == 0) { if (!(client = get_client_resource(ua))) { return false; } } } /* * See if we need to recursively remove all directories under a certain path. */ recursive = find_arg(ua, NT_("recursive")) >= 0; /* * Get the directory to prune. */ i = find_arg_with_value(ua, NT_("directory")); if (i >= 0) { pm_strcpy(temp, ua->argv[i]); } else { if (recursive) { if (!get_cmd(ua, _("Please enter the full path prefix to remove: "), false)) { return false; } } else { if (!get_cmd(ua, _("Please enter the full path to remove: "), false)) { return false; } } pm_strcpy(temp, ua->cmd); } /* * See if the directory ends in a / and escape it for usage in a database query. */ len = strlen(temp.c_str()); if (*(temp.c_str() + len - 1) != '/') { pm_strcat(temp, "/"); len++; } prune_topdir = (char *)malloc(len * 2 + 1); db_escape_string(ua->jcr, ua->db, prune_topdir, temp.c_str(), len); /* * Remove all files in particular directory. */ if (recursive) { Mmsg(query, "DELETE FROM file WHERE pathid IN (" "SELECT pathid FROM path " "WHERE path LIKE '%s%%'" ")", prune_topdir); } else { Mmsg(query, "DELETE FROM file WHERE pathid IN (" "SELECT pathid FROM path " "WHERE path LIKE '%s'" ")", prune_topdir); } if (client) { char ed1[50]; memset(&cr, 0, sizeof(cr)); bstrncpy(cr.Name, client->name(), sizeof(cr.Name)); if (!db_create_client_record(ua->jcr, ua->db, &cr)) { goto bail_out; } Mmsg(temp, " AND JobId IN (" "SELECT JobId FROM Job " "WHERE ClientId=%s" ")", edit_int64(cr.ClientId, ed1)); pm_strcat(query, temp.c_str()); } db_lock(ua->db); db_sql_query(ua->db, query.c_str()); db_unlock(ua->db); /* * If we removed the entries from the file table without limiting it to a * certain client we created orphaned path entries as no one is referencing * them anymore. */ if (!client) { if (!get_yesno(ua, _("Cleanup orphaned path records (yes/no):")) || ua->pint32_val == 0) { retval = true; goto bail_out; } if (recursive) { Mmsg(query, "DELETE FROM path " "WHERE path LIKE '%s%%'", prune_topdir); } else { Mmsg(query, "DELETE FROM path " "WHERE path LIKE '%s'", prune_topdir); } db_lock(ua->db); db_sql_query(ua->db, query.c_str()); db_unlock(ua->db); } retval = true; bail_out: if (prune_topdir) { free(prune_topdir); } return retval; }
/* * Prune records from database * * prune files client=xxx [pool=yyy] * prune jobs client=xxx [pool=yyy] * prune volume=xxx * prune stats * prune directory=xxx [client=xxx] [recursive] */ int prune_cmd(UAContext *ua, const char *cmd) { CLIENTRES *client; POOLRES *pool; POOL_DBR pr; MEDIA_DBR mr; utime_t retention; int kw; static const char *keywords[] = { NT_("Files"), NT_("Jobs"), NT_("Volume"), NT_("Stats"), NT_("Directory"), NULL }; if (!open_client_db(ua, true)) { return false; } /* * First search args */ kw = find_arg_keyword(ua, keywords); if (kw < 0 || kw > 4) { /* * No args, so ask user */ kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords); } switch (kw) { case 0: /* prune files */ if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, NT_("pool")) >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* * Pool File Retention takes precedence over client File Retention */ if (pool && pool->FileRetention > 0) { if (!confirm_retention(ua, &pool->FileRetention, "File")) { return false; } } else if (!confirm_retention(ua, &client->FileRetention, "File")) { return false; } prune_files(ua, client, pool); return true; case 1: { /* prune jobs */ int i; char jobtype[MAX_NAME_LENGTH]; if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, NT_("pool")) >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* * Ask what jobtype to prune. */ if ((i = find_arg_with_value(ua, NT_("jobtype"))) >= 0) { bstrncpy(jobtype, ua->argv[i], sizeof(jobtype)); } else { start_prompt(ua, _("Jobtype to prune:\n")); for (i = 0; jobtypes[i].type_name; i++) { add_prompt(ua, jobtypes[i].type_name); } if (do_prompt(ua, _("JobType"), _("Select Job Type"), jobtype, sizeof(jobtype)) < 0) { return true; } } for (i = 0; jobtypes[i].type_name; i++) { if (bstrcasecmp(jobtypes[i].type_name, jobtype)) { break; } } if (!jobtypes[i].type_name) { ua->warning_msg(_("Illegal jobtype %s.\n"), jobtype); return false; } /* * Pool Job Retention takes precedence over client Job Retention */ if (pool && pool->JobRetention > 0) { if (!confirm_retention(ua, &pool->JobRetention, "Job")) { return false; } } else if (!confirm_retention(ua, &client->JobRetention, "Job")) { return false; } if (jobtypes[i].type_name) { return prune_jobs(ua, client, pool, jobtypes[i].job_type); } return false; } case 2: /* prune volume */ if (!select_pool_and_media_dbr(ua, &pr, &mr)) { return false; } if (mr.Enabled == 2) { ua->error_msg(_("Cannot prune Volume \"%s\" because it is archived.\n"), mr.VolumeName); return false; } if (!confirm_retention(ua, &mr.VolRetention, "Volume")) { return false; } return prune_volume(ua, &mr); case 3: /* prune stats */ if (!me->stats_retention) { return false; } retention = me->stats_retention; if (!confirm_retention(ua, &retention, "Statistics")) { return false; } return prune_stats(ua, retention); case 4: /* prune directory */ if (find_arg_with_value(ua, NT_("client")) >= 0) { if (!(client = get_client_resource(ua))) { return false; } } else { client = NULL; } return prune_directory(ua, client); default: break; } return true; }
static bool get_user_slot_list(UAContext *ua, char *slot_list, int num_slots) { int i; const char *msg; /* slots are numbered 1 to num_slots */ for (int i=0; i <= num_slots; i++) { slot_list[i] = 0; } i = find_arg_with_value(ua, "slots"); if (i > 0) { /* scan slot list in ua->argv[i] */ char *p, *e, *h; int beg, end; strip_trailing_junk(ua->argv[i]); for (p=ua->argv[i]; p && *p; p=e) { /* Check for list */ e = strchr(p, ','); if (e) { *e++ = 0; } /* Check for range */ h = strchr(p, '-'); /* range? */ if (h == p) { msg = _("Negative numbers not permitted\n"); goto bail_out; } if (h) { *h++ = 0; if (!is_an_integer(h)) { msg = _("Range end is not integer.\n"); goto bail_out; } skip_spaces(&p); if (!is_an_integer(p)) { msg = _("Range start is not an integer.\n"); goto bail_out; } beg = atoi(p); end = atoi(h); if (end < beg) { msg = _("Range end not bigger than start.\n"); goto bail_out; } } else { skip_spaces(&p); if (!is_an_integer(p)) { msg = _("Input value is not an integer.\n"); goto bail_out; } beg = end = atoi(p); } if (beg <= 0 || end <= 0) { msg = _("Values must be be greater than zero.\n"); goto bail_out; } if (end > num_slots) { msg = _("Slot too large.\n"); goto bail_out; } for (i=beg; i<=end; i++) { slot_list[i] = 1; /* Turn on specified range */ } } } else { /* Turn everything on */ for (i=1; i <= num_slots; i++) { slot_list[i] = 1; } } Dmsg0(100, "Slots turned on:\n"); for (i=1; i <= num_slots; i++) { if (slot_list[i]) { Dmsg1(100, "%d\n", i); } } return true; bail_out: return false; }
/* * .bvfs_get_jobids jobid=1 * -> returns needed jobids to restore * .bvfs_get_jobids jobid=1 all * -> returns needed jobids to restore with all filesets a JobId=1 time * .bvfs_get_jobids ujobid=JobName * -> returns needed jobids to restore */ bool dot_bvfs_get_jobids_cmd(UAContext *ua, const char *cmd) { JOB_DBR jr; db_list_ctx jobids, tempids; int pos; char ed1[50]; POOL_MEM query; dbid_list ids; /* Store all FileSetIds for this client */ if (!open_client_db(ua, true)) { return true; } memset(&jr, 0, sizeof(jr)); if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) { bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job)); } else if ((pos = find_arg_with_value(ua, "jobid")) >= 0) { jr.JobId = str_to_int64(ua->argv[pos]); } else { ua->error_msg(_("Can't find ujobid or jobid argument\n")); return false; } if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), ua->argv[pos], db_strerror(ua->db)); return false; } /* * When in level base, we don't rely on any Full/Incr/Diff */ if (jr.JobLevel == L_BASE) { jobids.add(edit_int64(jr.JobId, ed1)); } else { /* * If we have the "all" option, we do a search on all defined fileset for this client */ if (find_arg(ua, "all") > 0) { edit_int64(jr.ClientId, ed1); Mmsg(query, uar_sel_filesetid, ed1); db_get_query_dbids(ua->jcr, ua->db, query, ids); } else { ids.num_ids = 1; ids.DBId[0] = jr.FileSetId; } jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ /* * Foreach different FileSet, we build a restore jobid list */ for (int i = 0; i < ids.num_ids; i++) { jr.FileSetId = ids.DBId[i]; if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids)) { return true; } jobids.add(tempids); } } switch (ua->api) { case API_MODE_JSON: { char *cur_id, *bp; ua->send->array_start("jobids"); cur_id = jobids.list; while (cur_id && strlen(cur_id)) { bp = strchr(cur_id, ','); if (bp) { *bp++ = '\0'; } ua->send->object_start(); ua->send->object_key_value("id", cur_id, "%s\n"); ua->send->object_end(); cur_id = bp; } ua->send->array_end("jobids"); break; } default: ua->send_msg("%s\n", jobids.list); break; } return true; }
/* * Update Slots corresponding to Volumes in autochanger */ static void update_slots(UAContext *ua) { USTORERES store; vol_list_t *vl; dlist *vol_list = NULL; MEDIA_DBR mr; char *slot_list; bool scan; int max_slots; int drive = -1; int Enabled = 1; bool have_enabled; int i; if (!open_client_db(ua)) { return; } store.store = get_storage_resource(ua, true, true); if (!store.store) { return; } pm_strcpy(store.store_source, _("command line")); set_wstorage(ua->jcr, &store); scan = find_arg(ua, NT_("scan")) >= 0; if (scan) { drive = get_storage_drive(ua, store.store); } if ((i=find_arg_with_value(ua, NT_("Enabled"))) >= 0) { Enabled = get_enabled(ua, ua->argv[i]); if (Enabled < 0) { return; } have_enabled = true; } else { have_enabled = false; } max_slots = get_num_slots_from_SD(ua); Dmsg1(100, "max_slots=%d\n", max_slots); if (max_slots <= 0) { ua->warning_msg(_("No slots in changer to scan.\n")); return; } slot_list = (char *)malloc(nbytes_for_bits(max_slots)); clear_all_bits(max_slots, slot_list); if (!get_user_slot_list(ua, slot_list, "slots", max_slots)) { free(slot_list); return; } vol_list = get_vol_list_from_SD(ua, store.store, false, scan); if (!vol_list) { ua->warning_msg(_("No Volumes found to update, or no barcodes.\n")); goto bail_out; } /* * First zap out any InChanger with StorageId=0 */ db_sql_query(ua->db, "UPDATE Media SET InChanger=0 WHERE StorageId=0"); /* * Walk through the list updating the media records */ memset(&mr, 0, sizeof(mr)); foreach_dlist(vl, vol_list) { if (vl->Slot > max_slots) { ua->warning_msg(_("Slot %d greater than max %d ignored.\n"), vl->Slot, max_slots); continue; } /* * Check if user wants us to look at this slot */ if (!bit_is_set(vl->Slot - 1, slot_list)) { Dmsg1(100, "Skipping slot=%d\n", vl->Slot); continue; } /* * If scanning, we read the label rather than the barcode */ if (scan) { if (vl->VolName) { free(vl->VolName); vl->VolName = NULL; } vl->VolName = get_volume_name_from_SD(ua, vl->Slot, drive); Dmsg2(100, "Got Vol=%s from SD for Slot=%d\n", vl->VolName, vl->Slot); } clear_bit(vl->Slot - 1, slot_list); /* clear Slot */ set_storageid_in_mr(store.store, &mr); mr.Slot = vl->Slot; mr.InChanger = 1; mr.MediaId = 0; /* Get by VolumeName */ if (vl->VolName) { bstrncpy(mr.VolumeName, vl->VolName, sizeof(mr.VolumeName)); } else { mr.VolumeName[0] = 0; } set_storageid_in_mr(store.store, &mr); Dmsg4(100, "Before make unique: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); db_lock(ua->db); /* * Set InChanger to zero for this Slot */ db_make_inchanger_unique(ua->jcr, ua->db, &mr); db_unlock(ua->db); Dmsg4(100, "After make unique: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); if (!vl->VolName) { Dmsg1(100, "No VolName for Slot=%d setting InChanger to zero.\n", vl->Slot); ua->info_msg(_("No VolName for Slot=%d InChanger set to zero.\n"), vl->Slot); continue; } db_lock(ua->db); Dmsg4(100, "Before get MR: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); if (db_get_media_record(ua->jcr, ua->db, &mr)) { Dmsg4(100, "After get MR: Vol=%s slot=%d inchanger=%d sid=%d\n", mr.VolumeName, mr.Slot, mr.InChanger, mr.StorageId); /* * If Slot, Inchanger, and StorageId have changed, update the Media record */ if (mr.Slot != vl->Slot || !mr.InChanger || mr.StorageId != store.store->StorageId) { mr.Slot = vl->Slot; mr.InChanger = 1; if (have_enabled) { mr.Enabled = Enabled; } set_storageid_in_mr(store.store, &mr); if (!db_update_media_record(ua->jcr, ua->db, &mr)) { ua->error_msg("%s", db_strerror(ua->db)); } else { ua->info_msg(_("Catalog record for Volume \"%s\" updated to reference slot %d.\n"), mr.VolumeName, mr.Slot); } } else { ua->info_msg(_("Catalog record for Volume \"%s\" is up to date.\n"), mr.VolumeName); } } else { ua->warning_msg(_("Volume \"%s\" not found in catalog. Slot=%d InChanger set to zero.\n"), mr.VolumeName, vl->Slot); } db_unlock(ua->db); } memset(&mr, 0, sizeof(mr)); mr.InChanger = 1; set_storageid_in_mr(store.store, &mr); /* * Any slot not visited gets it Inchanger flag reset. */ db_lock(ua->db); for (i = 1; i <= max_slots; i++) { if (bit_is_set(i - 1, slot_list)) { /* * Set InChanger to zero for this Slot */ mr.Slot = i; db_make_inchanger_unique(ua->jcr, ua->db, &mr); } } db_unlock(ua->db); bail_out: if (vol_list) { free_vol_list(vol_list); } free(slot_list); close_sd_bsock(ua); return; }
/* * 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 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; }
/* * Prune records from database * * prune files (from) client=xxx [pool=yyy] * prune jobs (from) client=xxx [pool=yyy] * prune volume=xxx * prune stats */ int prunecmd(UAContext *ua, const char *cmd) { CLIENTRES *client; POOLRES *pool; POOL_DBR pr; MEDIA_DBR mr; utime_t retention; int kw; static const char *keywords[] = { NT_("Files"), NT_("Jobs"), NT_("Volume"), NT_("Stats"), NULL}; if (!open_client_db(ua)) { return false; } /* First search args */ kw = find_arg_keyword(ua, keywords); if (kw < 0 || kw > 3) { /* no args, so ask user */ kw = do_keyword_prompt(ua, _("Choose item to prune"), keywords); } switch (kw) { case 0: /* prune files */ if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, "pool") >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* Pool File Retention takes precedence over client File Retention */ if (pool && pool->FileRetention > 0) { if (!confirm_retention(ua, &pool->FileRetention, "File")) { return false; } } else if (!confirm_retention(ua, &client->FileRetention, "File")) { return false; } prune_files(ua, client, pool); return true; case 1: /* prune jobs */ if (!(client = get_client_resource(ua))) { return false; } if (find_arg_with_value(ua, "pool") >= 0) { pool = get_pool_resource(ua); } else { pool = NULL; } /* Pool Job Retention takes precedence over client Job Retention */ if (pool && pool->JobRetention > 0) { if (!confirm_retention(ua, &pool->JobRetention, "Job")) { return false; } } else if (!confirm_retention(ua, &client->JobRetention, "Job")) { return false; } /* ****FIXME**** allow user to select JobType */ prune_jobs(ua, client, pool, JT_BACKUP); return 1; case 2: /* prune volume */ if (!select_pool_and_media_dbr(ua, &pr, &mr)) { return false; } if (mr.Enabled == 2) { ua->error_msg(_("Cannot prune Volume \"%s\" because it is archived.\n"), mr.VolumeName); return false; } if (!confirm_retention(ua, &mr.VolRetention, "Volume")) { return false; } prune_volume(ua, &mr); return true; case 3: /* prune stats */ if (!director->stats_retention) { return false; } retention = director->stats_retention; if (!confirm_retention(ua, &retention, "Statistics")) { return false; } prune_stats(ua, retention); return true; default: break; } return true; }
static bool list_nextvol(UAContext *ua, int ndays) { JOBRES *job; JCR *jcr; USTORERES store; RUNRES *run; utime_t runtime; bool found = false; MEDIA_DBR mr; POOL_DBR pr; int i = find_arg_with_value(ua, "job"); if (i <= 0) { if ((job = select_job_resource(ua)) == NULL) { return false; } } else { job = (JOBRES *)GetResWithName(R_JOB, ua->argv[i]); if (!job) { Jmsg(ua->jcr, M_ERROR, 0, _("%s is not a job name.\n"), ua->argv[i]); if ((job = select_job_resource(ua)) == NULL) { return false; } } } jcr = new_jcr(sizeof(JCR), dird_free_jcr); for (run=NULL; (run = find_next_run(run, job, runtime, ndays)); ) { if (!complete_jcr_for_job(jcr, job, run->pool)) { found = false; goto get_out; } if (!jcr->jr.PoolId) { ua->error_msg(_("Could not find Pool for Job %s\n"), job->name()); continue; } memset(&pr, 0, sizeof(pr)); pr.PoolId = jcr->jr.PoolId; if (!db_get_pool_record(jcr, jcr->db, &pr)) { bstrncpy(pr.Name, "*UnknownPool*", sizeof(pr.Name)); } mr.PoolId = jcr->jr.PoolId; get_job_storage(&store, job, run); set_storageid_in_mr(store.store, &mr); /* no need to set ScratchPoolId, since we use fnv_no_create_vol */ if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) { ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"), job->name(), pr.Name, level_to_str(run->level)); } else { ua->send_msg( _("The next Volume to be used by Job \"%s\" (Pool=%s, Level=%s) will be %s\n"), job->name(), pr.Name, level_to_str(run->level), mr.VolumeName); found = true; } } get_out: if (jcr->db) { db_sql_close_pooled_connection(jcr, jcr->db); jcr->db = NULL; } free_jcr(jcr); if (!found) { ua->error_msg(_("Could not find next Volume for Job %s.\n"), job->hdr.name); return false; } return true; }