/* * Select a Storage resource from prompt list */ STORERES *select_storage_resource(UAContext *ua, bool autochanger_only) { STORERES *store; char name[MAX_NAME_LENGTH]; if (autochanger_only) { start_prompt(ua, _("The defined Autochanger Storage resources are:\n")); } else { start_prompt(ua, _("The defined Storage resources are:\n")); } LockRes(); foreach_res(store, R_STORAGE) { if (acl_access_ok(ua, Storage_ACL, store->name())) { if (autochanger_only && !store->autochanger) { continue; } else { add_prompt(ua, store->name()); } } } UnlockRes(); if (do_prompt(ua, _("Storage"), _("Select Storage resource"), name, sizeof(name)) < 0) { return NULL; } store = (STORERES *)GetResWithName(R_STORAGE, name); return store; }
static void display_vol_info(UAContext *ua, RESTORE_CTX &rx, JobId_t JobId) { int i; RBSR *bsr; char online; POOL_MEM volmsg(PM_MESSAGE); char Device[MAX_NAME_LENGTH]; for (bsr = rx.bsr; bsr; bsr = bsr->next) { if (JobId && JobId != bsr->JobId) { continue; } for (i = 0; i < bsr->VolCount; i++) { if (bsr->VolParams[i].VolumeName[0]) { if (!get_storage_device(Device, bsr->VolParams[i].Storage)) { Device[0] = 0; } if (bsr->VolParams[i].InChanger && bsr->VolParams[i].Slot) { online = '*'; } else { online = ' '; } Mmsg(volmsg, "%c%-25s %-25s %-25s", online, bsr->VolParams[i].VolumeName, bsr->VolParams[i].Storage, Device); add_prompt(ua, volmsg.c_str()); } } } }
/* * Given a list of keywords, prompt the user to choose one. * * Returns: -1 on failure * index into list (base 0) on success */ int do_keyword_prompt(UAContext *ua, const char *msg, const char **list) { start_prompt(ua, _("You have the following choices:\n")); for (int i = 0; list[i]; i++) { add_prompt(ua, list[i]); } return do_prompt(ua, "", msg, NULL, 0); }
/* * Get a catalog resource from prompt list */ CATRES *get_catalog_resource(UAContext *ua) { CATRES *catalog = NULL; char name[MAX_NAME_LENGTH]; for (int i = 1; i < ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("catalog")) && ua->argv[i]) { if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) { catalog = (CATRES *)GetResWithName(R_CATALOG, ua->argv[i]); break; } } } if (ua->gui && !catalog) { LockRes(); catalog = (CATRES *)GetNextRes(R_CATALOG, NULL); UnlockRes(); if (!catalog) { ua->error_msg(_("Could not find a Catalog resource\n")); return NULL; } else if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) { ua->error_msg(_("You must specify a \"use <catalog-name>\" command before continuing.\n")); return NULL; } return catalog; } if (!catalog) { start_prompt(ua, _("The defined Catalog resources are:\n")); LockRes(); foreach_res(catalog, R_CATALOG) { if (acl_access_ok(ua, Catalog_ACL, catalog->name())) { add_prompt(ua, catalog->name()); } } UnlockRes(); if (do_prompt(ua, _("Catalog"), _("Select Catalog resource"), name, sizeof(name)) < 0) { return NULL; } catalog = (CATRES *)GetResWithName(R_CATALOG, name); }
/* * Select a FileSet resource from prompt list */ FILESETRES *select_fileset_resource(UAContext *ua) { FILESETRES *fs; char name[MAX_NAME_LENGTH]; start_prompt(ua, _("The defined FileSet resources are:\n")); LockRes(); foreach_res(fs, R_FILESET) { if (acl_access_ok(ua, FileSet_ACL, fs->name())) { add_prompt(ua, fs->name()); } } UnlockRes(); if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name)) < 0) { return NULL; } fs = (FILESETRES *)GetResWithName(R_FILESET, name); return fs; }
/* * The first step in the restore process is for the user to * select a list of JobIds from which he will subsequently * select which files are to be restored. * * Returns: 2 if filename list made * 1 if jobid list made * 0 on error */ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) { char *p; char date[MAX_TIME_LENGTH]; bool have_date = false; /* Include current second if using current time */ utime_t now = time(NULL) + 1; JobId_t JobId; JOB_DBR jr = { (JobId_t)-1 }; bool done = false; int i, j; const char *list[] = { _("List last 20 Jobs run"), _("List Jobs where a given File is saved"), _("Enter list of comma separated JobIds to select"), _("Enter SQL list command"), _("Select the most recent backup for a client"), _("Select backup for a client before a specified time"), _("Enter a list of files to restore"), _("Enter a list of files to restore before a specified time"), _("Find the JobIds of the most recent backup for a client"), _("Find the JobIds for a backup for a client before a specified time"), _("Enter a list of directories to restore for found JobIds"), _("Select full restore to a specified Job date"), _("Cancel"), NULL }; const char *kw[] = { /* * These keywords are handled in a for loop */ "jobid", /* 0 */ "current", /* 1 */ "before", /* 2 */ "file", /* 3 */ "directory", /* 4 */ "select", /* 5 */ "pool", /* 6 */ "all", /* 7 */ /* * The keyword below are handled by individual arg lookups */ "client", /* 8 */ "storage", /* 9 */ "fileset", /* 10 */ "where", /* 11 */ "yes", /* 12 */ "bootstrap", /* 13 */ "done", /* 14 */ "strip_prefix", /* 15 */ "add_prefix", /* 16 */ "add_suffix", /* 17 */ "regexwhere", /* 18 */ "restoreclient", /* 19 */ "copies", /* 20 */ "comment", /* 21 */ "restorejob", /* 22 */ "replace", /* 23 */ "pluginoptions", /* 24 */ NULL }; rx->JobIds[0] = 0; for (i = 1; i<ua->argc; i++) { /* loop through arguments */ bool found_kw = false; for (j = 0; kw[j]; j++) { /* loop through keywords */ if (bstrcasecmp(kw[j], ua->argk[i])) { found_kw = true; break; } } if (!found_kw) { ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]); return 0; } /* Found keyword in kw[] list, process it */ switch (j) { case 0: /* jobid */ if (!has_value(ua, i)) { return 0; } if (*rx->JobIds != 0) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->argv[i]); done = true; break; case 1: /* current */ /* * Note, we add one second here just to include any job * that may have finished within the current second, * which happens a lot in scripting small jobs. */ bstrutime(date, sizeof(date), now); have_date = true; break; case 2: /* before */ if (have_date || !has_value(ua, i)) { return 0; } if (str_to_utime(ua->argv[i]) == 0) { ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]); return 0; } bstrncpy(date, ua->argv[i], sizeof(date)); have_date = true; break; case 3: /* file */ case 4: /* dir */ if (!has_value(ua, i)) { return 0; } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } pm_strcpy(ua->cmd, ua->argv[i]); insert_one_file_or_dir(ua, rx, date, j==4); return 2; case 5: /* select */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = true; break; case 6: /* pool specified */ if (!has_value(ua, i)) { return 0; } rx->pool = (POOLRES *)GetResWithName(R_POOL, ua->argv[i]); if (!rx->pool) { ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]); return 0; } if (!acl_access_ok(ua, Pool_ACL, ua->argv[i], true)) { rx->pool = NULL; ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]); return 0; } break; case 7: /* all specified */ rx->all = true; break; default: /* * All keywords 7 or greater are ignored or handled by a select prompt */ break; } } if (!done) { ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n" "to be restored. You will be presented several methods\n" "of specifying the JobIds. Then you will be allowed to\n" "select which files from those JobIds are to be restored.\n\n")); } /* If choice not already made above, prompt */ for ( ; !done; ) { char *fname; int len; bool gui_save; db_list_ctx jobids; start_prompt(ua, _("To select the JobIds, you have the following choices:\n")); for (int i=0; list[i]; i++) { add_prompt(ua, list[i]); } done = true; switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) { case -1: /* error or cancel */ return 0; case 0: /* list last 20 Jobs run */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), true)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, ua->send, HORZ_LIST, true); ua->jcr->gui = gui_save; done = false; break; case 1: /* list where a file is saved */ if (!get_client_name(ua, rx)) { return 0; } if (!get_cmd(ua, _("Enter Filename (no path):"))) { return 0; } len = strlen(ua->cmd); fname = (char *)malloc(len * 2 + 1); db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len); Mmsg(rx->query, uar_file[db_get_type_index(ua->db)], rx->ClientName, fname); free(fname); gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, rx->query, ua->send, HORZ_LIST, true); ua->jcr->gui = gui_save; done = false; break; case 2: /* enter a list of JobIds */ if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } pm_strcpy(rx->JobIds, ua->cmd); break; case 3: /* Enter an SQL list command */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), true)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } if (!get_cmd(ua, _("Enter SQL list command: "))) { return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, ua->cmd, ua->send, HORZ_LIST, true); ua->jcr->gui = gui_save; done = false; break; case 4: /* Select the most recent backups */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 5: /* select backup at specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 6: /* Enter files */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 7: /* enter files backed up before specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 8: /* Find JobIds for current backup */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 9: /* Find JobIds for give date */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 10: /* Enter directories */ if (*rx->JobIds != 0) { ua->send_msg(_("You have already selected the following JobIds: %s\n"), rx->JobIds); } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { if (*rx->JobIds != 0 && *ua->cmd) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->cmd); } if (*rx->JobIds == 0 || *rx->JobIds == '.') { *rx->JobIds = 0; return 0; /* nothing entered, return */ } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter full directory names or start the name\n" "with a < to indicate it is a filename containing a list\n" "of directories and terminate them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter directory name: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } /* Add trailing slash to end of directory names */ if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) { strcat(ua->cmd, "/"); } insert_one_file_or_dir(ua, rx, date, true); } return 2; case 11: /* Choose a jobid and select jobs */ if (!get_cmd(ua, _("Enter JobId to get the state to restore: ")) || !is_an_integer(ua->cmd)) { return 0; } memset(&jr, 0, sizeof(jr)); jr.JobId = str_to_int64(ua->cmd); 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 0; } ua->send_msg(_("Selecting jobs to build the Full state at %s\n"), jr.cStartTime); jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) { return 0; } pm_strcpy(rx->JobIds, jobids.list); Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds); break; case 12: /* Cancel or quit */ return 0; } } memset(&jr, 0, sizeof(jr)); POOLMEM *JobIds = get_pool_memory(PM_FNAME); *JobIds = 0; rx->TotalFiles = 0; /* * Find total number of files to be restored, and filter the JobId * list to contain only ones permitted by the ACL conditions. */ for (p=rx->JobIds; ; ) { char ed1[50]; int status = get_next_jobid_from_list(&p, &JobId); if (status < 0) { ua->error_msg(_("Invalid JobId in list.\n")); free_pool_memory(JobIds); return 0; } if (status == 0) { break; } if (jr.JobId == JobId) { continue; /* duplicate of last JobId */ } memset(&jr, 0, sizeof(jr)); jr.JobId = JobId; if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), edit_int64(JobId, ed1), db_strerror(ua->db)); free_pool_memory(JobIds); return 0; } if (!acl_access_ok(ua, Job_ACL, jr.Name, true)) { ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"), edit_int64(JobId, ed1), jr.Name); continue; } if (*JobIds != 0) { pm_strcat(JobIds, ","); } pm_strcat(JobIds, edit_int64(JobId, ed1)); rx->TotalFiles += jr.JobFiles; } free_pool_memory(rx->JobIds); rx->JobIds = JobIds; /* Set ACL filtered list */ if (*rx->JobIds == 0) { ua->warning_msg(_("No Jobs selected.\n")); return 0; } if (strchr(rx->JobIds,',')) { ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds); } else { ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds); } return true; }
/* * Update a Pool Record in the database. * It is always updated from the Resource record. * * update pool=<pool-name> * updates pool from Pool resource * update media pool=<pool-name> volume=<volume-name> * changes pool info for volume * update slots [scan=...] * updates autochanger slots * update stats [days=...] * updates long term statistics */ int update_cmd(UAContext *ua, const char *cmd) { static const char *kw[] = { NT_("media"), /* 0 */ NT_("volume"), /* 1 */ NT_("pool"), /* 2 */ NT_("slots"), /* 3 */ NT_("slot"), /* 4 */ NT_("jobid"), /* 5 */ NT_("stats"), /* 6 */ NULL}; if (!open_client_db(ua)) { return 1; } switch (find_arg_keyword(ua, kw)) { case 0: case 1: update_volume(ua); return 1; case 2: update_pool(ua); return 1; case 3: case 4: update_slots(ua); return 1; case 5: update_job(ua); return 1; case 6: update_stats(ua); return 1; default: break; } start_prompt(ua, _("Update choice:\n")); add_prompt(ua, _("Volume parameters")); add_prompt(ua, _("Pool from resource")); add_prompt(ua, _("Slots from autochanger")); add_prompt(ua, _("Long term statistics")); switch (do_prompt(ua, _("item"), _("Choose catalog item to update"), NULL, 0)) { case 0: update_volume(ua); break; case 1: update_pool(ua); break; case 2: update_slots(ua); break; case 3: update_stats(ua); break; default: break; } return 1; }
/* * 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; }
/* * Called to set up the rlogin connection. * * Returns an error message, or NULL on success. * * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static const char *rlogin_init(void *frontend_handle, void **backend_handle, Conf *conf, char *host, int port, char **realhost, int nodelay, int keepalive) { static const struct plug_function_table fn_table = { rlogin_log, rlogin_closing, rlogin_receive, rlogin_sent, NULL }; SockAddr addr; const char *err; Rlogin rlogin; char *ruser; int addressfamily; char *loghost; rlogin = snew(struct rlogin_tag); rlogin->fn = &fn_table; rlogin->s = NULL; rlogin->closed_on_socket_error = FALSE; rlogin->frontend = frontend_handle; rlogin->term_width = conf_get_int(conf, CONF_width); rlogin->term_height = conf_get_int(conf, CONF_height); rlogin->firstbyte = 1; rlogin->cansize = 0; rlogin->prompt = NULL; rlogin->conf = conf_copy(conf); *backend_handle = rlogin; addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ { char *buf; buf = dupprintf("Looking up host \"%s\"%s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(rlogin->frontend, buf); sfree(buf); } addr = name_lookup(host, port, realhost, conf, addressfamily); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return err; } if (port < 0) port = 513; /* default rlogin port */ /* * Open socket. */ rlogin->s = new_connection(addr, *realhost, port, 1, 0, nodelay, keepalive, (Plug) rlogin, conf); if ((err = sk_socket_error(rlogin->s)) != NULL) return err; loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = strrchr(*realhost, ':'); if (colon) { /* * FIXME: if we ever update this aspect of ssh.c for * IPv6 literal management, this should change in line * with it. */ *colon++ = '\0'; } } /* * Send local username, remote username, terminal type and * terminal speed - unless we don't have the remote username yet, * in which case we prompt for it and may end up deferring doing * anything else until the local prompt mechanism returns. */ if ((ruser = get_remote_username(conf)) != NULL) { rlogin_startup(rlogin, ruser); sfree(ruser); } else { int ret; rlogin->prompt = new_prompts(rlogin->frontend); rlogin->prompt->to_server = TRUE; rlogin->prompt->name = dupstr("Rlogin login name"); add_prompt(rlogin->prompt, dupstr("rlogin username: "), TRUE); ret = get_userpass_input(rlogin->prompt, NULL, 0); if (ret >= 0) { rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); } } return NULL; }
/* * 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; }
/* * .die (seg fault) * .dump (sm_dump) * .exit (no arg => .quit) */ bool dot_admin_cmds(UAContext *ua, const char *cmd) { pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; STORERES *store = NULL; CLIENTRES *client = NULL; bool dir=false; bool do_deadlock=false; const char *remote_cmd; int i; JCR *jcr = NULL; int a; if (strncmp(ua->argk[0], ".die", 4) == 0) { if (find_arg(ua, "deadlock") > 0) { do_deadlock = true; remote_cmd = ".die deadlock"; } else { remote_cmd = ".die"; } } else if (strncmp(ua->argk[0], ".dump", 5) == 0) { remote_cmd = "sm_dump"; } else if (strncmp(ua->argk[0], ".exit", 5) == 0) { remote_cmd = "exit"; } else { ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]); return true; } /* General debug? */ for (i = 1; i < ua->argc; i++) { if (bstrcasecmp(ua->argk[i], "dir") || bstrcasecmp(ua->argk[i], "director")) { dir = true; } if (bstrcasecmp(ua->argk[i], "client") || bstrcasecmp(ua->argk[i], "fd")) { client = NULL; if (ua->argv[i]) { client = (CLIENTRES *)GetResWithName(R_CLIENT, ua->argv[i]); } if (!client) { client = select_client_resource(ua); } } if (bstrcasecmp(ua->argk[i], NT_("store")) || bstrcasecmp(ua->argk[i], NT_("storage")) || bstrcasecmp(ua->argk[i], NT_("sd"))) { store = NULL; if (ua->argv[i]) { store = (STORERES *)GetResWithName(R_STORAGE, ua->argv[i]); } if (!store) { store = get_storage_resource(ua); } } } if (!dir && !store && !client) { /* * We didn't find an appropriate keyword above, so * prompt the user. */ start_prompt(ua, _("Available daemons are: \n")); add_prompt(ua, _("Director")); add_prompt(ua, _("Storage")); add_prompt(ua, _("Client")); switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) { case 0: /* Director */ dir=true; break; case 1: store = get_storage_resource(ua); break; case 2: client = select_client_resource(ua); break; default: break; } } if (store) { switch (store->Protocol) { case APT_NDMPV2: case APT_NDMPV3: case APT_NDMPV4: ua->warning_msg(_("Storage has non-native protocol.\n")); break; default: do_storage_cmd(ua, store, remote_cmd); break; } } if (client) { do_client_cmd(ua, client, remote_cmd); } if (dir) { if (strncmp(remote_cmd, ".die", 4) == 0) { if (do_deadlock) { ua->send_msg(_("The Director will generate a deadlock.\n")); P(mutex); P(mutex); } ua->send_msg(_("The Director will segment fault.\n")); a = jcr->JobId; /* ref NULL pointer */ jcr->JobId = 1000; /* another ref NULL pointer */ jcr->JobId = a; } else if (strncmp(remote_cmd, ".dump", 5) == 0) { sm_dump(false, true); } else if (strncmp(remote_cmd, ".exit", 5) == 0) { quit_cmd(ua, cmd); } } return true; }
/* * Read a file containing SQL queries and prompt * the user to select which one. * * File format: * # => comment * :prompt for query * *prompt for subst %1 * *prompt for subst %2 * ... * SQL statement possibly terminated by ; * :next query prompt */ bool query_cmd(UAContext *ua, const char *cmd) { FILE *fd = NULL; POOLMEM *query = get_pool_memory(PM_MESSAGE); char line[1000]; int i, item, len; char *prompt[9]; int nprompt = 0; char *query_file = me->query_file; if (!open_client_db(ua, true)) { goto bail_out; } if ((fd=fopen(query_file, "rb")) == NULL) { berrno be; ua->error_msg(_("Could not open %s: ERR=%s\n"), query_file, be.bstrerror()); goto bail_out; } start_prompt(ua, _("Available queries:\n")); while (fgets(line, sizeof(line), fd) != NULL) { if (line[0] == ':') { strip_trailing_junk(line); add_prompt(ua, line+1); } } if ((item=do_prompt(ua, "", _("Choose a query"), NULL, 0)) < 0) { goto bail_out; } rewind(fd); i = -1; while (fgets(line, sizeof(line), fd) != NULL) { if (line[0] == ':') { i++; } if (i == item) { break; } } if (i != item) { ua->error_msg(_("Could not find query.\n")); goto bail_out; } query[0] = 0; for (i=0; i<9; i++) { prompt[i] = NULL; } while (fgets(line, sizeof(line), fd) != NULL) { if (line[0] == '#') { continue; } if (line[0] == ':') { break; } strip_trailing_junk(line); len = strlen(line); if (line[0] == '*') { /* prompt */ if (nprompt >= 9) { ua->error_msg(_("Too many prompts in query, max is 9.\n")); } else { line[len++] = ' '; line[len] = 0; prompt[nprompt++] = bstrdup(line+1); continue; } } if (*query != 0) { pm_strcat(query, " "); } pm_strcat(query, line); if (line[len-1] != ';') { continue; } line[len-1] = 0; /* zap ; */ if (query[0] != 0) { query = substitute_prompts(ua, query, prompt, nprompt); Dmsg1(100, "Query2=%s\n", query); if (query[0] == '!') { db_list_sql_query(ua->jcr, ua->db, query+1, ua->send, VERT_LIST, false); } else if (!db_list_sql_query(ua->jcr, ua->db, query, ua->send, HORZ_LIST, true)) { ua->send_msg("%s\n", query); } query[0] = 0; } } /* end while */ if (query[0] != 0) { query = substitute_prompts(ua, query, prompt, nprompt); Dmsg1(100, "Query2=%s\n", query); if (query[0] == '!') { db_list_sql_query(ua->jcr, ua->db, query+1, ua->send, VERT_LIST, false); } else if (!db_list_sql_query(ua->jcr, ua->db, query, ua->send, HORZ_LIST, true)) { ua->error_msg("%s\n", query); } } bail_out: if (fd) { fclose(fd); } free_pool_memory(query); for (i=0; i<nprompt; i++) { free(prompt[i]); } return true; }