1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Bacula Director -- User Agent Commands
21  *
22  *     Kern Sibbald, September MM
23  */
24 
25 #include "bacula.h"
26 #include "dird.h"
27 
28 /* Imported subroutines */
29 
30 /* Imported variables */
31 extern jobq_t job_queue;              /* job queue */
32 
33 
34 /* Imported functions */
35 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
36 extern int gui_cmd(UAContext *ua, const char *cmd);
37 extern int label_cmd(UAContext *ua, const char *cmd);
38 extern int list_cmd(UAContext *ua, const char *cmd);
39 extern int llist_cmd(UAContext *ua, const char *cmd);
40 extern int messagescmd(UAContext *ua, const char *cmd);
41 extern int prunecmd(UAContext *ua, const char *cmd);
42 extern int purge_cmd(UAContext *ua, const char *cmd);
43 extern int truncate_cmd(UAContext *ua, const char *cmd);  /* in ua_purge.c */
44 extern int query_cmd(UAContext *ua, const char *cmd);
45 extern int relabel_cmd(UAContext *ua, const char *cmd);
46 extern int restore_cmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int show_cmd(UAContext *ua, const char *cmd);
49 extern int sqlquery_cmd(UAContext *ua, const char *cmd);
50 extern int status_cmd(UAContext *ua, const char *cmd);
51 extern int update_cmd(UAContext *ua, const char *cmd);
52 extern int collect_cmd(UAContext *ua, const char *cmd);
53 
54 /* Forward referenced functions */
55 static int add_cmd(UAContext *ua, const char *cmd);
56 static int automount_cmd(UAContext *ua, const char *cmd);
57 static int cancel_cmd(UAContext *ua, const char *cmd);
58 static int create_cmd(UAContext *ua, const char *cmd);
59 static int delete_cmd(UAContext *ua, const char *cmd);
60 static int disable_cmd(UAContext *ua, const char *cmd);
61 static int enable_cmd(UAContext *ua, const char *cmd);
62 static int estimate_cmd(UAContext *ua, const char *cmd);
63 static int help_cmd(UAContext *ua, const char *cmd);
64 static int memory_cmd(UAContext *ua, const char *cmd);
65 static int mount_cmd(UAContext *ua, const char *cmd);
66 static int release_cmd(UAContext *ua, const char *cmd);
67 static int reload_cmd(UAContext *ua, const char *cmd);
68 static int setdebug_cmd(UAContext *ua, const char *cmd);
69 static int setbwlimit_cmd(UAContext *ua, const char *cmd);
70 static int setip_cmd(UAContext *ua, const char *cmd);
71 static int time_cmd(UAContext *ua, const char *cmd);
72 static int trace_cmd(UAContext *ua, const char *cmd);
73 static int unmount_cmd(UAContext *ua, const char *cmd);
74 static int use_cmd(UAContext *ua, const char *cmd);
75 static int cloud_cmd(UAContext *ua, const char *cmd);
76 static int var_cmd(UAContext *ua, const char *cmd);
77 static int version_cmd(UAContext *ua, const char *cmd);
78 static int wait_cmd(UAContext *ua, const char *cmd);
79 
80 static void do_job_delete(UAContext *ua, JobId_t JobId);
81 static int delete_volume(UAContext *ua);
82 static int delete_pool(UAContext *ua);
83 static void delete_job(UAContext *ua);
84 static int delete_client(UAContext *ua);
85 static void do_storage_cmd(UAContext *ua, const char *command);
86 
87 int qhelp_cmd(UAContext *ua, const char *cmd);
88 int quit_cmd(UAContext *ua, const char *cmd);
89 
90 /* not all in alphabetical order.  New commands are added after existing commands with similar letters
91    to prevent breakage of existing user scripts.  */
92 struct cmdstruct {
93    const char *key;                             /* command */
94    int (*func)(UAContext *ua, const char *cmd); /* handler */
95    const char *help;            /* main purpose */
96    const char *usage;           /* all arguments to build usage */
97    const bool use_in_rs;        /* Can use it in Console RunScript */
98 };
99 static struct cmdstruct commands[] = {                                      /* Can use it in Console RunScript*/
100  { NT_("add"),        add_cmd,     _("Add media to a pool"),   NT_("pool=<pool-name> storage=<storage> jobid=<JobId>"),  false},
101  { NT_("autodisplay"), autodisplay_cmd,_("Autodisplay console messages"), NT_("on | off"),    false},
102  { NT_("automount"),   automount_cmd,  _("Automount after label"),        NT_("on | off"),    false},
103  { NT_("cancel"),     cancel_cmd,    _("Cancel a job"), NT_("jobid=<number-list> | job=<job-name> | ujobid=<unique-jobid> | inactive client=<client-name> storage=<storage-name> | all"), false},
104  { NT_("cloud"),      cloud_cmd,     _("Specific Cloud commands"),
105    NT_("[storage=<storage-name>] [volume=<vol>] [pool=<pool>] [allpools] [allfrompool] [mediatype=<type>] [drive=<number>] [slots=<number] \n"
106        "\tstatus  | prune | list | upload | truncate"), true},
107  { NT_("create"),     create_cmd,    _("Create DB Pool from resource"), NT_("pool=<pool-name>"),                    false},
108  { NT_("delete"),     delete_cmd,    _("Delete volume, pool, client or job"), NT_("volume=<vol-name> | pool=<pool-name> | jobid=<id> | client=<client-name> | snapshot"), true},
109  { NT_("disable"),    disable_cmd,   _("Disable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"),  true},
110  { NT_("enable"),     enable_cmd,    _("Enable a job, attributes batch process"), NT_("job=<name> | client=<name> | schedule=<name> | storage=<name> | batch"),   true},
111  { NT_("estimate"),   estimate_cmd,  _("Performs FileSet estimate, listing gives full listing"),
112    NT_("fileset=<fs> client=<cli> level=<level> accurate=<yes/no> job=<job> listing"), true},
113 
114  { NT_("exit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),         false},
115  { NT_("gui"),        gui_cmd,       _("Non-interactive gui mode"),   NT_("on | off"), false},
116  { NT_("help"),       help_cmd,      _("Print help on specific command"),
117    NT_("add autodisplay automount cancel create delete disable\n\tenable estimate exit gui label list llist"
118        "\n\tmessages memory mount prune purge quit query\n\trestore relabel release reload run status statistics"
119        "\n\tsetbandwidth setdebug setip show sqlquery time trace unmount\n\tumount update use var version wait"
120        "\n\tsnapshot"),         false},
121 
122  { NT_("label"),      label_cmd,     _("Label a tape"), NT_("storage=<storage> volume=<vol> pool=<pool> slot=<slot> drive=<nb> barcodes"), false},
123  { NT_("list"),       list_cmd,      _("List objects from catalog"),
124    NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [limit=<n>]|\n"
125        "\tjobtotals | pools | volume | media <pool=pool-name> | files [type=<deleted|all>] jobid=<nn> | copies jobid=<nn> |\n"
126        "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot | \n"
127        "\tfileindex=<mm> | clients\n"
128       ), false},
129 
130  { NT_("llist"),      llist_cmd,     _("Full or long list like list command"),
131    NT_("jobs [client=<cli>] [jobid=<nn>] [ujobid=<name>] [job=<name>] [joberrors] [jobstatus=<s>] [level=<l>] [jobtype=<t>] [order=<asc/desc>] [limit=<n>]|\n"
132        "\tjobtotals | pools | volume | media <pool=pool-name> | files jobid=<nn> | copies jobid=<nn> |\n"
133        "\tjoblog jobid=<nn> | pluginrestoreconf jobid=<nn> restoreobjectid=<nn> | snapshot |\n"
134        "\tjobid=<nn> fileindex=<mm> | clients\n"), false},
135 
136  { NT_("messages"),   messagescmd,   _("Display pending messages"),   NT_(""),    false},
137  { NT_("memory"),     memory_cmd,    _("Print current memory usage"), NT_(""),    true},
138  { NT_("mount"),      mount_cmd,     _("Mount storage"),
139    NT_("storage=<storage-name> slot=<num> drive=<num> [ device=<device-name> ] [ jobid=<id> | job=<job-name> ]"), false},
140 
141  { NT_("prune"),      prunecmd,      _("Prune expired records from catalog"),
142    NT_("files | jobs | snapshot  [client=<client-name>] | client=<client-name> | \n"
143        "\t[expired] [all | allpools | allfrompool] [pool=<pool>] [mediatype=<type>] volume=<volume-name> [yes]"),
144    true},
145 
146  { NT_("purge"),      purge_cmd,     _("Purge records from catalog"), NT_("files jobs volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
147  { NT_("quit"),       quit_cmd,      _("Terminate Bconsole session"), NT_(""),              false},
148  { NT_("query"),      query_cmd,     _("Query catalog"), NT_("[<query-item-number>]"),      false},
149  { NT_("restore"),    restore_cmd,   _("Restore files"),
150    NT_("where=</path> client=<client> storage=<storage> bootstrap=<file> "
151        "restorejob=<job> restoreclient=<cli> noautoparent"
152        "\n\tcomment=<text> jobid=<jobid> jobuser=<user> jobgroup=<grp> copies done select all"), false},
153 
154  { NT_("relabel"),    relabel_cmd,   _("Relabel a tape"),
155    NT_("storage=<storage-name> oldvolume=<old-volume-name>\n\tvolume=<newvolume-name> pool=<pool>"), false},
156 
157  { NT_("release"),    release_cmd,   _("Release storage"),  NT_("storage=<storage-name> [ device=<device-name> ] "),      false},
158  { NT_("reload"),     reload_cmd,    _("Reload conf file"), NT_(""),                  true},
159  { NT_("run"),        run_cmd,       _("Run a job"),
160    NT_("job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
161        " where=<directory-prefix>\n\twhen=<universal-time-specification> pool=<pool-name>\n\t"
162        " nextpool=<next-pool-name> comment=<text> accurate=<bool> spooldata=<bool> yes"), false},
163 
164  { NT_("restart"),    restart_cmd,   _("Restart a job"),
165    NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
166        "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
167 
168  { NT_("resume"),    restart_cmd,   _("Resume a job"),
169    NT_("incomplete job=<job-name> client=<client-name>\n\tfileset=<FileSet-name> level=<level-keyword>\n\tstorage=<storage-name>"
170        "when=<universal-time-specification>\n\tcomment=<text> spooldata=<bool> jobid=<jobid>"), false},
171 
172  { NT_("status"),     status_cmd,    _("Report status"),
173    NT_("all | network [bytes=<nn-b>] | dir=<dir-name> | director | client=<client-name> |\n"
174        "\tstorage=<storage-name> slots |\n"
175        "\tschedule [job=<job-name>] [client=<cli-name>] [schedule=<sched-name>] [days=<nn>] [limit=<nn>]\n"
176        "\t\t[time=<universal-time-specification>]"), true},
177 
178  { NT_("stop"),       cancel_cmd,    _("Stop a job"), NT_("jobid=<number-list> job=<job-name> ujobid=<unique-jobid> all"), false},
179  { NT_("setdebug"),   setdebug_cmd,  _("Sets debug level"),
180    NT_("level=<nn> tags=<tags> trace=0/1 options=<0tTc> tags=<tags> | client=<client-name> | dir | storage=<storage-name> | all"), true},
181 
182  { NT_("setbandwidth"),   setbwlimit_cmd,  _("Sets bandwidth"),
183    NT_("limit=<speed> client=<client-name> jobid=<number> job=<job-name> ujobid=<unique-jobid>"), true},
184 
185  { NT_("snapshot"),   snapshot_cmd,  _("Handle snapshots"),
186    NT_("[client=<client-name> | job=<job-name> | jobid=<jobid>] [delete | list | listclient | prune | sync | update]"), true},
187 
188  { NT_("setip"),      setip_cmd,     _("Sets new client address -- if authorized"), NT_(""),   false},
189  { NT_("show"),       show_cmd,      _("Show resource records"),
190    NT_("job=<xxx> |  pool=<yyy> | fileset=<aaa> | schedule=<sss> | client=<zzz> | storage=<sss> | disabled | all"), true},
191 
192  { NT_("sqlquery"),   sqlquery_cmd,  _("Use SQL to query catalog"), NT_(""),          false},
193  { NT_("statistics"),    collect_cmd,   _("Display daemon statistics data"), NT_("[ simple | full | json ] all | <metric>\n"
194        "\t[ client=<client-name> | storage=<storage-name> ]"), true},
195  { NT_("time"),       time_cmd,      _("Print current time"),       NT_(""),          true},
196  { NT_("trace"),      trace_cmd,     _("Turn on/off trace to file"), NT_("on | off"), true},
197  { NT_("truncate"),   truncate_cmd,  _("Truncate one or more Volumes"), NT_("volume=<vol> [mediatype=<type> pool=<pool> allpools storage=<st> drive=<num>]"),  true},
198  { NT_("unmount"),    unmount_cmd,   _("Unmount storage"),
199    NT_("storage=<storage-name> [ drive=<num> ] | jobid=<id> | job=<job-name>"), false},
200 
201  { NT_("umount"),     unmount_cmd,   _("Umount - for old-time Unix guys, see unmount"),
202    NT_("storage=<storage-name> [ drive=<num> ] [ device=<dev-name> ]| jobid=<id> | job=<job-name>"), false},
203 
204  { NT_("update"),     update_cmd,    _("Update volume, pool or stats"),
205    NT_("stats\n\tsnapshot\n\tpool=<poolname>\n\tslots storage=<storage> scan"
206        "\n\tvolume=<volname> volstatus=<status> volretention=<time-def> cacheretention=<time-def>"
207        "\n\t pool=<pool> recycle=<yes/no> slot=<number>\n\t inchanger=<yes/no>"
208        "\n\t maxvolbytes=<size> maxvolfiles=<nb> maxvoljobs=<nb>"
209        "\n\t enabled=<yes/no> recyclepool=<pool> actiononpurge=<action>"
210        "\n\t allfrompool=<pool> fromallpools frompool"),true},
211  { NT_("use"),        use_cmd,       _("Use catalog xxx"), NT_("catalog=<catalog>"),     false},
212  { NT_("var"),        var_cmd,       _("Does variable expansion"), NT_(""),  false},
213  { NT_("version"),    version_cmd,   _("Print Director version"),  NT_(""),  true},
214  { NT_("wait"),       wait_cmd,      _("Wait until no jobs are running"),
215    NT_("jobname=<name> | jobid=<nnn> | ujobid=<complete_name>"), false}
216 };
217 
218 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
219 
get_command(int index)220 const char *get_command(int index) {
221    return commands[index].key;
222 }
223 
224 /*
225  * Execute a command from the UA
226  */
do_a_command(UAContext * ua)227 bool do_a_command(UAContext *ua)
228 {
229    int i;
230    int len;
231    bool ok = false;
232    bool found = false;
233 
234    Dmsg1(900, "Command: %s\n", ua->argk[0]);
235    if (ua->argc == 0) {
236       return false;
237    }
238 
239    if (ua->jcr->wstorage) {
240       while (ua->jcr->wstorage->size()) {
241          ua->jcr->wstorage->remove(0);
242       }
243    }
244 
245    len = strlen(ua->argk[0]);
246    for (i=0; i<comsize; i++) {     /* search for command */
247       if (strncasecmp(ua->argk[0],  commands[i].key, len) == 0) {
248          ua->cmd_index = i;
249          /* Check if command permitted, but "quit" is always OK */
250          if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
251              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
252             break;
253          }
254          /* Check if this command is authorized in RunScript */
255          if (ua->runscript && !commands[i].use_in_rs) {
256             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
257             break;
258          }
259          if (ua->api) ua->signal(BNET_CMD_BEGIN);
260          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
261          if (ua->api) ua->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
262          found = ua->UA_sock && ua->UA_sock->is_stop() ? false : true;
263          break;
264       }
265    }
266    if (!found) {
267       ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
268       ok = false;
269    }
270    return ok;
271 }
272 
273 /*
274  * This is a common routine used to stuff the Pool DB record defaults
275  *   into the Media DB record just before creating a media (Volume)
276  *   record.
277  */
set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR * mr,POOL_DBR * pr)278 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
279 {
280    mr->PoolId = pr->PoolId;
281    bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
282    mr->Recycle = pr->Recycle;
283    mr->VolRetention = pr->VolRetention;
284    mr->CacheRetention = pr->CacheRetention;
285    mr->VolUseDuration = pr->VolUseDuration;
286    mr->ActionOnPurge = pr->ActionOnPurge;
287    mr->RecyclePoolId = pr->RecyclePoolId;
288    mr->MaxVolJobs = pr->MaxVolJobs;
289    mr->MaxVolFiles = pr->MaxVolFiles;
290    mr->MaxVolBytes = pr->MaxVolBytes;
291    mr->LabelType = pr->LabelType;
292    mr->Enabled = 1;
293 }
294 
295 
296 /*
297  *  Add Volumes to an existing Pool
298  */
add_cmd(UAContext * ua,const char * cmd)299 static int add_cmd(UAContext *ua, const char *cmd)
300 {
301    POOL_DBR pr;
302    MEDIA_DBR mr;
303    int num, i, max, startnum;
304    char name[MAX_NAME_LENGTH];
305    STORE *store;
306    int Slot = 0, InChanger = 0;
307 
308    ua->send_msg(_(
309 "You probably don't want to be using this command since it\n"
310 "creates database records without labeling the Volumes.\n"
311 "You probably want to use the \"label\" command.\n\n"));
312 
313    if (!open_client_db(ua)) {
314       return 1;
315    }
316 
317    memset(&pr, 0, sizeof(pr));
318 
319    if (!get_pool_dbr(ua, &pr)) {
320       return 1;
321    }
322 
323    Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
324       pr.MaxVols, pr.PoolType);
325 
326    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
327       ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
328       if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
329          return 1;
330       }
331       pr.MaxVols = ua->pint32_val;
332    }
333 
334    /* Get media type */
335    if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
336       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
337    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
338       return 1;
339    }
340 
341    if (pr.MaxVols == 0) {
342       max = 1000;
343    } else {
344       max = pr.MaxVols - pr.NumVols;
345    }
346    for (;;) {
347       char buf[100];
348       bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
349       if (!get_pint(ua, buf)) {
350          return 1;
351       }
352       num = ua->pint32_val;
353       if (num < 0 || num > max) {
354          ua->warning_msg(_("The number must be between 0 and %d\n"), max);
355          continue;
356       }
357       break;
358    }
359 
360    for (;;) {
361       if (num == 0) {
362          if (!get_cmd(ua, _("Enter Volume name: "))) {
363             return 1;
364          }
365       } else {
366          if (!get_cmd(ua, _("Enter base volume name: "))) {
367             return 1;
368          }
369       }
370       /* Don't allow | in Volume name because it is the volume separator character */
371       if (!is_volume_name_legal(ua, ua->cmd)) {
372          continue;
373       }
374       if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
375          ua->warning_msg(_("Volume name too long.\n"));
376          continue;
377       }
378       if (strlen(ua->cmd) == 0) {
379          ua->warning_msg(_("Volume name must be at least one character long.\n"));
380          continue;
381       }
382       break;
383    }
384 
385    bstrncpy(name, ua->cmd, sizeof(name));
386    if (num > 0) {
387       bstrncat(name, "%04d", sizeof(name));
388 
389       for (;;) {
390          if (!get_pint(ua, _("Enter the starting number: "))) {
391             return 1;
392          }
393          startnum = ua->pint32_val;
394          if (startnum < 1) {
395             ua->warning_msg(_("Start number must be greater than zero.\n"));
396             continue;
397          }
398          break;
399       }
400    } else {
401       startnum = 1;
402       num = 1;
403    }
404 
405    if (store && store->autochanger) {
406       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
407          return 1;
408       }
409       Slot = ua->pint32_val;
410       if (!get_yesno(ua, _("InChanger? yes/no: "))) {
411          return 1;
412       }
413       InChanger = ua->pint32_val;
414    }
415 
416    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
417    for (i=startnum; i < num+startnum; i++) {
418       bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
419       mr.Slot = Slot++;
420       mr.InChanger = InChanger;
421       mr.Enabled = 1;
422       set_storageid_in_mr(store, &mr);
423       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
424       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
425          ua->error_msg("%s", db_strerror(ua->db));
426          return 1;
427       }
428 //    if (i == startnum) {
429 //       first_id = mr.PoolId;
430 //    }
431    }
432    pr.NumVols += num;
433    Dmsg0(200, "Update pool record.\n");
434    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
435       ua->warning_msg("%s", db_strerror(ua->db));
436       return 1;
437    }
438    ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
439 
440    return 1;
441 }
442 
443 /*
444  * Turn auto mount on/off
445  *
446  *  automount on
447  *  automount off
448  */
automount_cmd(UAContext * ua,const char * cmd)449 int automount_cmd(UAContext *ua, const char *cmd)
450 {
451    char *onoff;
452 
453    if (ua->argc != 2) {
454       if (!get_cmd(ua, _("Turn on or off? "))) {
455             return 1;
456       }
457       onoff = ua->cmd;
458    } else {
459       onoff = ua->argk[1];
460    }
461 
462    ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
463    return 1;
464 }
465 
466 /*
467  * Cancel/Stop a job -- Stop marks it as Incomplete
468  *   so that it can be restarted.
469  */
cancel_cmd(UAContext * ua,const char * cmd)470 static int cancel_cmd(UAContext *ua, const char *cmd)
471 {
472    JCR    *jcr;
473    bool    ret = true;
474    int     nb;
475    bool    cancel = strcasecmp(commands[ua->cmd_index].key, "cancel") == 0;
476    alist  *jcrs = New(alist(5, not_owned_by_alist));
477 
478    /* If the user explicitely ask, we can send the cancel command to
479     * the FD.
480     */
481    if (find_arg(ua, "inactive") > 0) {
482       ret = cancel_inactive_job(ua);
483       goto bail_out;
484    }
485 
486    nb = select_running_jobs(ua, jcrs, commands[ua->cmd_index].key);
487 
488    foreach_alist(jcr, jcrs) {
489       /* Execute the cancel command only if we don't have an error */
490       if (nb != -1) {
491          ret &= cancel_job(ua, jcr, 60, cancel);
492       }
493       free_jcr(jcr);
494    }
495 
496 bail_out:
497    delete jcrs;
498    return ret;
499 }
500 
501 /*
502  * This is a common routine to create or update a
503  *   Pool DB base record from a Pool Resource. We handle
504  *   the setting of MaxVols and NumVols slightly differently
505  *   depending on if we are creating the Pool or we are
506  *   simply bringing it into agreement with the resource (updage).
507  *
508  * Caution : RecyclePoolId isn't setup in this function.
509  *           You can use set_pooldbr_recyclepoolid();
510  *
511  */
set_pooldbr_from_poolres(POOL_DBR * pr,POOL * pool,e_pool_op op)512 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
513 {
514    bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
515    if (op == POOL_OP_CREATE) {
516       pr->MaxVols = pool->max_volumes;
517       pr->NumVols = 0;
518    } else {          /* update pool */
519       if (pr->MaxVols != pool->max_volumes) {
520          pr->MaxVols = pool->max_volumes;
521       }
522       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
523          pr->MaxVols = pr->NumVols;
524       }
525    }
526    pr->LabelType = pool->LabelType;
527    pr->UseOnce = pool->use_volume_once;
528    pr->UseCatalog = pool->use_catalog;
529    pr->Recycle = pool->Recycle;
530    pr->VolRetention = pool->VolRetention;
531    pr->CacheRetention = pool->CacheRetention;
532    pr->VolUseDuration = pool->VolUseDuration;
533    pr->MaxVolJobs = pool->MaxVolJobs;
534    pr->MaxVolFiles = pool->MaxVolFiles;
535    pr->MaxVolBytes = pool->MaxVolBytes;
536    pr->AutoPrune = pool->AutoPrune;
537    pr->ActionOnPurge = pool->action_on_purge;
538    pr->Recycle = pool->Recycle;
539    if (pool->label_format) {
540       bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
541    } else {
542       bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat));    /* none */
543    }
544 }
545 
546 /* set/update Pool.RecyclePoolId and Pool.ScratchPoolId in Catalog */
update_pool_references(JCR * jcr,BDB * db,POOL * pool)547 int update_pool_references(JCR *jcr, BDB *db, POOL *pool)
548 {
549    POOL_DBR  pr;
550 
551    if (pool->ScratchPool == pool) {
552       Jmsg(NULL, M_WARNING, 0,
553            _("The ScratchPool directive for Pool \"%s\" is incorrect. Using default Scratch pool instead.\n"),
554            pool->name());
555       pool->ScratchPool = NULL;
556    }
557 
558    if (!pool->RecyclePool && !pool->ScratchPool) {
559       return 1;
560    }
561 
562    memset(&pr, 0, sizeof(POOL_DBR));
563    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
564 
565    /* Don't compute NumVols here */
566    if (!db_get_pool_record(jcr, db, &pr)) {
567       return -1;                       /* not exists in database */
568    }
569 
570    set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
571 
572    if (!set_pooldbr_references(jcr, db, &pr, pool)) {
573       return -1;                      /* error */
574    }
575 
576    /* NumVols is updated here */
577    if (!db_update_pool_record(jcr, db, &pr)) {
578       return -1;                      /* error */
579    }
580    return 1;
581 }
582 
583 /* set POOL_DBR.RecyclePoolId and POOL_DBR.ScratchPoolId from Pool resource
584  * works with set_pooldbr_from_poolres
585  */
set_pooldbr_references(JCR * jcr,BDB * db,POOL_DBR * pr,POOL * pool)586 bool set_pooldbr_references(JCR *jcr, BDB *db, POOL_DBR *pr, POOL *pool)
587 {
588    POOL_DBR rpool;
589    bool ret = true;
590 
591    if (pool->RecyclePool) {
592       memset(&rpool, 0, sizeof(POOL_DBR));
593 
594       bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
595       if (db_get_pool_record(jcr, db, &rpool)) {
596         pr->RecyclePoolId = rpool.PoolId;
597       } else {
598         Jmsg(jcr, M_WARNING, 0,
599         _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
600           "Try to update it with 'update pool=%s'\n"),
601         pool->name(), rpool.Name, rpool.Name,pool->name());
602 
603         ret = false;
604       }
605    } else {                    /* no RecyclePool used, set it to 0 */
606       pr->RecyclePoolId = 0;
607    }
608 
609    if (pool->ScratchPool) {
610       memset(&rpool, 0, sizeof(POOL_DBR));
611 
612       bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
613       if (db_get_pool_record(jcr, db, &rpool)) {
614         pr->ScratchPoolId = rpool.PoolId;
615       } else {
616         Jmsg(jcr, M_WARNING, 0,
617         _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
618           "Try to update it with 'update pool=%s'\n"),
619         pool->name(), rpool.Name, rpool.Name,pool->name());
620         ret = false;
621       }
622    } else {                    /* no ScratchPool used, set it to 0 */
623       pr->ScratchPoolId = 0;
624    }
625 
626    return ret;
627 }
628 
629 
630 /*
631  * Create a pool record from a given Pool resource
632  *   Also called from backup.c
633  * Returns: -1  on error
634  *           0  record already exists
635  *           1  record created
636  */
637 
create_pool(JCR * jcr,BDB * db,POOL * pool,e_pool_op op)638 int create_pool(JCR *jcr, BDB *db, POOL *pool, e_pool_op op)
639 {
640    POOL_DBR  pr;
641    memset(&pr, 0, sizeof(POOL_DBR));
642    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
643 
644    if (db_get_pool_record(jcr, db, &pr)) {
645       /* Pool Exists */
646       if (op == POOL_OP_UPDATE) {  /* update request */
647          set_pooldbr_from_poolres(&pr, pool, op);
648          set_pooldbr_references(jcr, db, &pr, pool);
649          db_update_pool_record(jcr, db, &pr);
650       }
651       return 0;                       /* exists */
652    }
653 
654    set_pooldbr_from_poolres(&pr, pool, op);
655    set_pooldbr_references(jcr, db, &pr, pool);
656 
657    if (!db_create_pool_record(jcr, db, &pr)) {
658       return -1;                      /* error */
659    }
660    return 1;
661 }
662 
663 
664 
665 /*
666  * Create a Pool Record in the database.
667  *  It is always created from the Resource record.
668  */
create_cmd(UAContext * ua,const char * cmd)669 static int create_cmd(UAContext *ua, const char *cmd)
670 {
671    POOL *pool;
672 
673    if (!open_client_db(ua)) {
674       return 1;
675    }
676 
677    pool = get_pool_resource(ua);
678    if (!pool) {
679       return 1;
680    }
681 
682    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
683    case 0:
684       ua->error_msg(_("Error: Pool %s already exists.\n"
685                "Use update to change it.\n"), pool->name());
686       break;
687 
688    case -1:
689       ua->error_msg("%s", db_strerror(ua->db));
690       break;
691 
692    default:
693      break;
694    }
695    ua->send_msg(_("Pool %s created.\n"), pool->name());
696    return 1;
697 }
698 
699 
700 extern DIRRES *director;
701 extern char *configfile;
702 
setbwlimit_client(UAContext * ua,CLIENT * client,char * Job,int64_t limit)703 static int setbwlimit_client(UAContext *ua, CLIENT *client, char *Job, int64_t limit)
704 {
705    POOL_MEM buf;
706    CLIENT *old_client;
707    char ed1[50];
708    if (!client) {
709       return 1;
710    }
711 
712    /* Connect to File daemon */
713    old_client = ua->jcr->client;
714    ua->jcr->client = client;
715    ua->jcr->max_bandwidth = limit;
716 
717    /* Try to connect for 15 seconds */
718    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
719                 client->name(), client->address(buf.addr()), client->FDport);
720    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
721       ua->error_msg(_("Failed to connect to Client.\n"));
722       goto bail_out;
723    }
724    Dmsg0(120, "Connected to file daemon\n");
725 
726    if (!send_bwlimit(ua->jcr, Job)) {
727       ua->error_msg(_("Failed to set bandwidth limit to Client.\n"));
728 
729    } else {
730       /* Note, we add 2000 OK that was sent by FD to us to message */
731       ua->info_msg(_("2000 OK Limiting bandwidth to %sB/s %s\n"),
732                    edit_uint64_with_suffix(limit, ed1), *Job?Job:_("on running and future jobs"));
733    }
734 
735    ua->jcr->file_bsock->signal(BNET_TERMINATE);
736    free_bsock(ua->jcr->file_bsock);
737    ua->jcr->max_bandwidth = 0;
738 
739 bail_out:
740    ua->jcr->client = old_client;
741    return 1;
742 }
743 
setbwlimit_cmd(UAContext * ua,const char * cmd)744 static int setbwlimit_cmd(UAContext *ua, const char *cmd)
745 {
746    int action = -1;
747    CLIENT *client = NULL;
748    char Job[MAX_NAME_LENGTH];
749    *Job=0;
750    uint64_t limit = 0;
751    JCR *jcr = NULL;
752    int i;
753 
754    const char *lst_all[] = { "job", "jobid", "jobname", "client", NULL };
755    if (find_arg_keyword(ua, lst_all) < 0) {
756        start_prompt(ua, _("Set Bandwidth choice:\n"));
757        add_prompt(ua, _("Running Job")); /* 0 */
758        add_prompt(ua, _("Running and future Jobs for a Client")); /* 1 */
759        action = do_prompt(ua, "item", _("Choose where to limit the bandwidth"),
760                           NULL, 0);
761        if (action < 0) {
762           return 1;
763        }
764    }
765 
766    i = find_arg_with_value(ua, "limit");
767    if (i >= 0) {
768       if (!speed_to_uint64(ua->argv[i], strlen(ua->argv[i]), &limit)) {
769          ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
770          return 1;
771       }
772    } else {
773       if (!get_cmd(ua, _("Enter new bandwidth limit: "))) {
774          return 1;
775       }
776       if (!speed_to_uint64(ua->cmd, strlen(ua->cmd), &limit)) {
777          ua->error_msg(_("Invalid value for limit parameter. Expecting speed.\n"));
778          return 1;
779       }
780    }
781 
782    const char *lst[] = { "job", "jobid", "jobname", NULL };
783    if (action == 0 || find_arg_keyword(ua, lst) > 0) {
784       alist *jcrs = New(alist(10, not_owned_by_alist));
785       select_running_jobs(ua, jcrs, "limit");
786       foreach_alist(jcr, jcrs) {
787          jcr->max_bandwidth = limit; /* TODO: see for locking (Should be safe)*/
788          bstrncpy(Job, jcr->Job, sizeof(Job));
789          client = jcr->client;
790          setbwlimit_client(ua, client, Job, limit);
791          free_jcr(jcr);
792       }
793 
794    } else {
795       client = get_client_resource(ua, JT_BACKUP_RESTORE);
796       if (client) {
797          setbwlimit_client(ua, client, Job, limit);
798       }
799    }
800    return 1;
801 }
802 
803 /*
804  * Set a new address in a Client resource. We do this only
805  *  if the Console name is the same as the Client name
806  *  and the Console can access the client.
807  */
setip_cmd(UAContext * ua,const char * cmd)808 static int setip_cmd(UAContext *ua, const char *cmd)
809 {
810    CLIENT *client;
811    char addr[1024];
812    if (!ua->cons || !acl_access_client_ok(ua, ua->cons->name(), JT_BACKUP_RESTORE)) {
813       ua->error_msg(_("Unauthorized command from this console.\n"));
814       return 1;
815    }
816    LockRes();
817    client = GetClientResWithName(ua->cons->name());
818 
819    if (!client) {
820       ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
821       goto get_out;
822    }
823    /* MA Bug 6 remove ifdef */
824    sockaddr_to_ascii(&(ua->UA_sock->client_addr),
825                      sizeof(ua->UA_sock->client_addr), addr, sizeof(addr));
826    client->setAddress(addr);
827    ua->send_msg(_("Client \"%s\" address set to %s\n"),
828             client->name(), addr);
829 get_out:
830    UnlockRes();
831    return 1;
832 }
833 
834 /*
835  * Does all sorts of enable/disable commands: batch, scheduler (not implemented)
836  *  job, client, schedule, storage
837  */
do_enable_disable_cmd(UAContext * ua,bool setting)838 static void do_enable_disable_cmd(UAContext *ua, bool setting)
839 {
840    JOB *job = NULL;
841    CLIENT *client = NULL;
842    SCHED *sched = NULL;
843    int i;
844 
845    if (find_arg(ua, NT_("batch")) > 0) {
846       ua->send_msg(_("Job Attributes Insertion %sabled\n"), setting?"en":"dis");
847       db_disable_batch_insert(setting);
848       return;
849    }
850 
851    /*
852     * if (find_arg(ua, NT_("scheduler")) > 0) {
853     *    ua->send_msg(_("Job Scheduler %sabled\n"), setting?"en":"dis");
854     *    return;
855     * }
856     */
857 
858    i = find_arg(ua, NT_("job"));
859    if (i >= 0) {
860       if (ua->argv[i]) {
861          LockRes();
862          job = GetJobResWithName(ua->argv[i]);
863          UnlockRes();
864       } else {
865          job = select_enable_disable_job_resource(ua, setting);
866          if (!job) {
867             return;
868          }
869       }
870    }
871    if (job) {
872       if (!acl_access_ok(ua, Job_ACL, job->name())) {
873          ua->error_msg(_("Unauthorized command from this console.\n"));
874          return;
875       }
876       job->setEnabled(setting);
877       ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
878    }
879 
880    i = find_arg(ua, NT_("client"));
881    if (i >= 0) {
882       if (ua->argv[i]) {
883          LockRes();
884          client = GetClientResWithName(ua->argv[i]);
885          UnlockRes();
886       } else {
887          client = select_enable_disable_client_resource(ua, setting);
888          if (!client) {
889             return;
890          }
891       }
892    }
893    if (client) {
894       if (!acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
895          ua->error_msg(_("Unauthorized command from this console.\n"));
896          return;
897       }
898       client->setEnabled(setting);
899       ua->send_msg(_("Client \"%s\" %sabled\n"), client->name(), setting?"en":"dis");
900    }
901 
902    i = find_arg(ua, NT_("schedule"));
903    if (i >= 0) {
904       if (ua->argv[i]) {
905          LockRes();
906          sched = (SCHED *)GetResWithName(R_SCHEDULE, ua->argv[i]);
907          UnlockRes();
908       } else {
909          sched = select_enable_disable_schedule_resource(ua, setting);
910          if (!sched) {
911             return;
912          }
913       }
914    }
915    if (sched) {
916       if (!acl_access_ok(ua, Schedule_ACL, sched->name())) {
917          ua->error_msg(_("Unauthorized command from this console.\n"));
918          return;
919       }
920       sched->setEnabled(setting);
921       ua->send_msg(_("Schedule \"%s\" %sabled\n"), sched->name(), setting?"en":"dis");
922    }
923 
924    i = find_arg(ua, NT_("storage"));
925    if (i >= 0) {
926       do_storage_cmd(ua, setting?"enable":"disable");
927    }
928 
929    if (i < 0 && !sched && !client && !job) {
930       ua->error_msg(_("You must enter one of the following keywords: job, client, schedule, or storage.\n"));
931    }
932 
933    return;
934 }
935 
enable_cmd(UAContext * ua,const char * cmd)936 static int enable_cmd(UAContext *ua, const char *cmd)
937 {
938    do_enable_disable_cmd(ua, true);
939    return 1;
940 }
941 
disable_cmd(UAContext * ua,const char * cmd)942 static int disable_cmd(UAContext *ua, const char *cmd)
943 {
944    do_enable_disable_cmd(ua, false);
945    return 1;
946 }
947 
do_dir_setdebug(UAContext * ua,int64_t level,int trace_flag,char * options,int64_t tags)948 static void do_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
949 {
950    debug_level = level;
951    debug_level_tags = tags;
952    set_trace(trace_flag);
953    set_debug_flags(options);
954 }
955 
do_storage_setdebug(UAContext * ua,STORE * store,int64_t level,int trace_flag,int hangup,int blowup,char * options,char * tags)956 static void do_storage_setdebug(UAContext *ua, STORE *store,
957                int64_t level, int trace_flag, int hangup, int blowup,
958                char *options, char *tags)
959 {
960    BSOCK *sd;
961    USTORE lstore;
962 
963    lstore.store = store;
964    pm_strcpy(lstore.store_source, _("unknown source"));
965    set_wstorage(ua->jcr, &lstore);
966    /* Try connecting for up to 15 seconds */
967    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
968       store->name(), store->address, store->SDport);
969    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
970       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
971       return;
972    }
973    Dmsg0(120, _("Connected to storage daemon\n"));
974    sd = ua->jcr->store_bsock;
975    sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
976              (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
977    if (sd->recv() >= 0) {
978       ua->send_msg("%s", sd->msg);
979    }
980    sd->signal(BNET_TERMINATE);
981    free_bsock(ua->jcr->store_bsock);
982    return;
983 }
984 
985 /*
986  * For the client, we have the following values that can be set
987  *  level = debug level
988  *  trace = send debug output to a file
989  *  options = various options for debug or specific FD behavior
990  *  hangup = how many records to send to FD before hanging up
991  *    obviously this is most useful for testing restarting
992  *    failed jobs.
993  *  blowup = how many records to send to FD before blowing up the FD.
994  */
do_client_setdebug(UAContext * ua,CLIENT * client,int64_t level,int trace,int hangup,int blowup,char * options,char * tags)995 static void do_client_setdebug(UAContext *ua, CLIENT *client,
996                int64_t level, int trace, int hangup, int blowup,
997                char *options, char *tags)
998 {
999    POOL_MEM buf;
1000    CLIENT *old_client;
1001    BSOCK *fd;
1002 
1003    /* Connect to File daemon */
1004 
1005    old_client = ua->jcr->client;
1006    ua->jcr->client = client;
1007    /* Try to connect for 15 seconds */
1008    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1009                 client->name(), client->address(buf.addr()), client->FDport);
1010 
1011    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1012       ua->error_msg(_("Failed to connect to Client.\n"));
1013       ua->jcr->client = old_client;
1014       return;
1015    }
1016    Dmsg0(120, "Connected to file daemon\n");
1017 
1018    fd = ua->jcr->file_bsock;
1019    if (ua->jcr->FDVersion <= 10) {
1020       fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
1021                 (int32_t)level, trace, hangup);
1022    } else {
1023       fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
1024                 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
1025    }
1026    if (fd->recv() >= 0) {
1027       ua->send_msg("%s", fd->msg);
1028    }
1029    fd->signal(BNET_TERMINATE);
1030    free_bsock(ua->jcr->file_bsock);
1031    ua->jcr->client = old_client;
1032    return;
1033 }
1034 
1035 
do_all_setdebug(UAContext * ua,int64_t level,int trace_flag,int hangup,int blowup,char * options,char * tags)1036 static void do_all_setdebug(UAContext *ua, int64_t level,
1037                int trace_flag, int hangup, int blowup,
1038                char *options, char *tags)
1039 {
1040    STORE *store, **unique_store;
1041    CLIENT *client, **unique_client;
1042    POOL_MEM buf1, buf2;
1043    int i, j, found;
1044    int64_t t=0;
1045 
1046    /* Director */
1047    debug_parse_tags(tags, &t);
1048    do_dir_setdebug(ua, level, trace_flag, options, t);
1049 
1050    /* Count Storage items */
1051    LockRes();
1052    store = NULL;
1053    i = 0;
1054    foreach_res(store, R_STORAGE) {
1055       i++;
1056    }
1057    unique_store = (STORE **) malloc(i * sizeof(STORE));
1058    /* Find Unique Storage address/port */
1059    store = (STORE *)GetNextRes(R_STORAGE, NULL);
1060    i = 0;
1061    unique_store[i++] = store;
1062    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1063       found = 0;
1064       for (j=0; j<i; j++) {
1065          if (strcmp(unique_store[j]->address, store->address) == 0 &&
1066              unique_store[j]->SDport == store->SDport) {
1067             found = 1;
1068             break;
1069          }
1070       }
1071       if (!found) {
1072          unique_store[i++] = store;
1073          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1074       }
1075    }
1076    UnlockRes();
1077 
1078    /* Call each unique Storage daemon */
1079    for (j=0; j<i; j++) {
1080       do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1081          hangup, blowup, options, tags);
1082    }
1083    free(unique_store);
1084 
1085    /* Count Client items */
1086    LockRes();
1087    client = NULL;
1088    i = 0;
1089    foreach_res(client, R_CLIENT) {
1090       i++;
1091    }
1092    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1093    /* Find Unique Client address/port */
1094    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1095    i = 0;
1096    unique_client[i++] = client;
1097    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1098       found = 0;
1099       for (j=0; j<i; j++) {
1100          if (strcmp(unique_client[j]->address(buf1.addr()), client->address(buf2.addr())) == 0 &&
1101              unique_client[j]->FDport == client->FDport) {
1102             found = 1;
1103             break;
1104          }
1105       }
1106       if (!found) {
1107          unique_client[i++] = client;
1108          Dmsg2(140, "Stuffing: %s:%d\n", client->address(buf1.addr()), client->FDport);
1109       }
1110    }
1111    UnlockRes();
1112 
1113    /* Call each unique File daemon */
1114    for (j=0; j<i; j++) {
1115       do_client_setdebug(ua, unique_client[j], level, trace_flag,
1116          hangup, blowup, options, tags);
1117    }
1118    free(unique_client);
1119 }
1120 
1121 /*
1122  * setdebug level=nn all trace=1/0
1123  */
setdebug_cmd(UAContext * ua,const char * cmd)1124 static int setdebug_cmd(UAContext *ua, const char *cmd)
1125 {
1126    STORE *store;
1127    CLIENT *client;
1128    int64_t level=0, tags=0;
1129    int trace_flag = -1;
1130    int hangup = -1;
1131    int blowup = -1;
1132    int i;
1133    char *tags_str=NULL;
1134    char options[60];
1135 
1136    Dmsg1(120, "setdebug:%s:\n", cmd);
1137 
1138    *options = 0;
1139    i = find_arg_with_value(ua, "options");
1140    if (i >= 0) {
1141       bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1142    }
1143    level = -1;
1144    i = find_arg_with_value(ua, "level");
1145    if (i >= 0) {
1146       level = str_to_int64(ua->argv[i]);
1147    }
1148    if (level < 0) {
1149       if (!get_pint(ua, _("Enter new debug level: "))) {
1150          return 1;
1151       }
1152       level = ua->pint32_val;
1153    }
1154 
1155    /* Better to send the tag string instead of tweaking the level
1156     * in case where we extend the tag or change the representation
1157     */
1158    i = find_arg_with_value(ua, "tags");
1159    if (i > 0) {
1160       tags_str = ua->argv[i];
1161       if (!debug_parse_tags(tags_str, &tags)) {
1162          ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1163          return 1;
1164       }
1165    }
1166 
1167    /* Look for trace flag. -1 => not change */
1168    i = find_arg_with_value(ua, "trace");
1169    if (i >= 0) {
1170       trace_flag = atoi(ua->argv[i]);
1171       if (trace_flag > 0) {
1172          trace_flag = 1;
1173       }
1174    }
1175 
1176    /* Look for hangup (debug only) flag. -1 => not change */
1177    i = find_arg_with_value(ua, "hangup");
1178    if (i >= 0) {
1179       hangup = atoi(ua->argv[i]);
1180    }
1181 
1182    /* Look for blowup (debug only) flag. -1 => not change */
1183    i = find_arg_with_value(ua, "blowup");
1184    if (i >= 0) {
1185       blowup = atoi(ua->argv[i]);
1186    }
1187 
1188    /* General debug? */
1189    for (i=1; i<ua->argc; i++) {
1190       if (strcasecmp(ua->argk[i], "all") == 0) {
1191          do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1192          return 1;
1193       }
1194       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1195           strcasecmp(ua->argk[i], "director") == 0) {
1196          do_dir_setdebug(ua, level, trace_flag, options, tags);
1197          return 1;
1198       }
1199       if (strcasecmp(ua->argk[i], "client") == 0 ||
1200           strcasecmp(ua->argk[i], "fd") == 0) {
1201          client = NULL;
1202          if (ua->argv[i]) {
1203             client = GetClientResWithName(ua->argv[i]);
1204             if (client) {
1205                do_client_setdebug(ua, client, level, trace_flag,
1206                   hangup, blowup, options, tags_str);
1207                return 1;
1208             }
1209          }
1210          client = select_client_resource(ua, JT_BACKUP_RESTORE);
1211          if (client) {
1212             do_client_setdebug(ua, client, level, trace_flag,
1213                hangup, blowup, options, tags_str);
1214             return 1;
1215          }
1216       }
1217 
1218       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1219           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1220           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1221          store = NULL;
1222          if (ua->argv[i]) {
1223             store = GetStoreResWithName(ua->argv[i]);
1224             if (store) {
1225                do_storage_setdebug(ua, store, level, trace_flag,
1226                   hangup, blowup, options, tags_str);
1227                return 1;
1228             }
1229          }
1230          store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1231          if (store) {
1232             do_storage_setdebug(ua, store, level, trace_flag,
1233                hangup, blowup, options, tags_str);
1234             return 1;
1235          }
1236       }
1237    }
1238    /*
1239     * We didn't find an appropriate keyword above, so
1240     * prompt the user.
1241     */
1242    start_prompt(ua, _("Available daemons are: \n"));
1243    add_prompt(ua, _("Director"));
1244    add_prompt(ua, _("Storage"));
1245    add_prompt(ua, _("Client"));
1246    add_prompt(ua, _("All"));
1247    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1248    case 0:                         /* Director */
1249       do_dir_setdebug(ua, level, trace_flag, options, tags);
1250       break;
1251    case 1:
1252       store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1253       if (store) {
1254          do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1255             options, tags_str);
1256       }
1257       break;
1258    case 2:
1259       client = select_client_resource(ua, JT_BACKUP_RESTORE);
1260       if (client) {
1261          do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1262             options, tags_str);
1263       }
1264       break;
1265    case 3:
1266       do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1267       break;
1268    default:
1269       break;
1270    }
1271    return 1;
1272 }
1273 
1274 /*
1275  * Turn debug tracing to file on/off
1276  */
trace_cmd(UAContext * ua,const char * cmd)1277 static int trace_cmd(UAContext *ua, const char *cmd)
1278 {
1279    char *onoff;
1280 
1281    if (ua->argc != 2) {
1282       if (!get_cmd(ua, _("Turn on or off? "))) {
1283          return 1;
1284       }
1285       onoff = ua->cmd;
1286    } else {
1287       onoff = ua->argk[1];
1288    }
1289 
1290    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1291    return 1;
1292 }
1293 
var_cmd(UAContext * ua,const char * cmd)1294 static int var_cmd(UAContext *ua, const char *cmd)
1295 {
1296    POOLMEM *val = get_pool_memory(PM_FNAME);
1297    char *var;
1298 
1299    if (!open_client_db(ua)) {
1300       return 1;
1301    }
1302    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1303       var++;
1304    }
1305    while (*var == ' ') {                 /* skip spaces */
1306       var++;
1307    }
1308    Dmsg1(100, "Var=%s:\n", var);
1309    variable_expansion(ua->jcr, var, &val);
1310    ua->send_msg("%s\n", val);
1311    free_pool_memory(val);
1312    return 1;
1313 }
1314 
estimate_cmd(UAContext * ua,const char * cmd)1315 static int estimate_cmd(UAContext *ua, const char *cmd)
1316 {
1317    JOB *job = NULL;
1318    CLIENT *client = NULL;
1319    FILESET *fileset = NULL;
1320    POOL_MEM buf;
1321    int listing = 0;
1322    char since[MAXSTRING];
1323    JCR *jcr = ua->jcr;
1324    int accurate=-1;
1325 
1326    jcr->setJobType(JT_BACKUP);
1327    jcr->start_time = time(NULL);
1328    jcr->setJobLevel(L_FULL);
1329 
1330    for (int i=1; i<ua->argc; i++) {
1331       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1332           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1333          if (ua->argv[i]) {
1334             client = GetClientResWithName(ua->argv[i]);
1335             if (!client) {
1336                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1337                return 1;
1338             }
1339             if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
1340                ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1341                return 1;
1342             }
1343             continue;
1344          } else {
1345             ua->error_msg(_("Client name missing.\n"));
1346             return 1;
1347          }
1348       }
1349       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1350          if (ua->argv[i]) {
1351             job = GetJobResWithName(ua->argv[i]);
1352             if (!job) {
1353                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1354                return 1;
1355             }
1356             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1357                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1358                return 1;
1359             }
1360             continue;
1361          } else {
1362             ua->error_msg(_("Job name missing.\n"));
1363             return 1;
1364          }
1365 
1366       }
1367       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1368          if (ua->argv[i]) {
1369             fileset = GetFileSetResWithName(ua->argv[i]);
1370             if (!fileset) {
1371                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1372                return 1;
1373             }
1374             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1375                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1376                return 1;
1377             }
1378             continue;
1379          } else {
1380             ua->error_msg(_("Fileset name missing.\n"));
1381             return 1;
1382          }
1383       }
1384       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1385          listing = 1;
1386          continue;
1387       }
1388       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1389          if (ua->argv[i]) {
1390             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1391                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1392                return 1;
1393             }
1394             continue;
1395          } else {
1396             ua->error_msg(_("Level value missing.\n"));
1397             return 1;
1398          }
1399       }
1400       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1401          if (ua->argv[i]) {
1402             if (!is_yesno(ua->argv[i], &accurate)) {
1403                ua->error_msg(_("Invalid value for accurate. "
1404                                "It must be yes or no.\n"));
1405                return 1;
1406             }
1407             continue;
1408          } else {
1409             ua->error_msg(_("Accurate value missing.\n"));
1410             return 1;
1411          }
1412       }
1413    }
1414    if (!job && !(client && fileset)) {
1415       if (!(job = select_job_resource(ua))) {
1416          return 1;
1417       }
1418    }
1419    if (!job) {
1420       job = GetJobResWithName(ua->argk[1]);
1421       if (!job) {
1422          ua->error_msg(_("No job specified.\n"));
1423          return 1;
1424       }
1425       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1426          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1427          return 1;
1428       }
1429    }
1430    jcr->job = job;
1431    if (!client) {
1432       client = job->client;
1433    }
1434    if (!fileset) {
1435       fileset = job->fileset;
1436    }
1437    jcr->client = client;
1438    jcr->fileset = fileset;
1439    close_db(ua);
1440    if (job->pool->catalog) {
1441       ua->catalog = job->pool->catalog;
1442    } else {
1443       ua->catalog = client->catalog;
1444    }
1445 
1446    if (!open_db(ua)) {
1447       return 1;
1448    }
1449 
1450    init_jcr_job_record(jcr);
1451 
1452    if (!get_or_create_client_record(jcr)) {
1453       return 1;
1454    }
1455    if (!get_or_create_fileset_record(jcr)) {
1456       return 1;
1457    }
1458 
1459    get_level_since_time(ua->jcr, since, sizeof(since));
1460 
1461    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1462                 jcr->client->name(), jcr->client->address(buf.addr()), jcr->client->FDport);
1463    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1464       ua->error_msg(_("Failed to connect to Client.\n"));
1465       return 1;
1466    }
1467 
1468    /* The level string change if accurate mode is enabled */
1469    if (accurate >= 0) {
1470       jcr->accurate = accurate;
1471    } else {
1472       jcr->accurate = job->accurate;
1473    }
1474 
1475    if (!send_level_command(jcr)) {
1476       goto bail_out;
1477    }
1478 
1479    if (!send_include_list(jcr)) {
1480       ua->error_msg(_("Error sending include list.\n"));
1481       goto bail_out;
1482    }
1483 
1484    if (!send_exclude_list(jcr)) {
1485       ua->error_msg(_("Error sending exclude list.\n"));
1486       goto bail_out;
1487    }
1488 
1489    /*
1490     * If the job is in accurate mode, we send the list of
1491     * all files to FD.
1492     */
1493    Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1494    if (!send_accurate_current_files(jcr)) {
1495       goto bail_out;
1496    }
1497 
1498    jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1499    while (jcr->file_bsock->recv() >= 0) {
1500       ua->send_msg("%s", jcr->file_bsock->msg);
1501    }
1502 
1503 bail_out:
1504    if (jcr->file_bsock) {
1505       jcr->file_bsock->signal(BNET_TERMINATE);
1506       free_bsock(ua->jcr->file_bsock);
1507    }
1508    return 1;
1509 }
1510 
1511 /*
1512  * print time
1513  */
time_cmd(UAContext * ua,const char * cmd)1514 static int time_cmd(UAContext *ua, const char *cmd)
1515 {
1516    char sdt[50];
1517    time_t ttime = time(NULL);
1518    struct tm tm;
1519    (void)localtime_r(&ttime, &tm);
1520    strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1521    ua->send_msg("%s\n", sdt);
1522    return 1;
1523 }
1524 
1525 /*
1526  * reload the conf file
1527  */
1528 extern "C" void reload_config(int sig);
1529 
reload_cmd(UAContext * ua,const char * cmd)1530 static int reload_cmd(UAContext *ua, const char *cmd)
1531 {
1532    reload_config(1);
1533    return 1;
1534 }
1535 
1536 /*
1537  * Delete Pool records (should purge Media with it).
1538  *
1539  *  delete pool=<pool-name>
1540  *  delete volume pool=<pool-name> volume=<name>
1541  *  delete jobid=xxx
1542  */
delete_cmd(UAContext * ua,const char * cmd)1543 static int delete_cmd(UAContext *ua, const char *cmd)
1544 {
1545    static const char *keywords[] = {
1546       NT_("volume"),
1547       NT_("pool"),
1548       NT_("jobid"),
1549       NT_("snapshot"),
1550       NT_("client"),
1551       NULL};
1552 
1553    /* Deleting large jobs can take time! */
1554    if (!open_new_client_db(ua)) {
1555       return 1;
1556    }
1557 
1558    switch (find_arg_keyword(ua, keywords)) {
1559    case 0:
1560       delete_volume(ua);
1561       return 1;
1562    case 1:
1563       delete_pool(ua);
1564       return 1;
1565    case 2:
1566       int i;
1567       while ((i=find_arg(ua, "jobid")) > 0) {
1568          delete_job(ua);
1569          *ua->argk[i] = 0;         /* zap keyword already visited */
1570       }
1571       return 1;
1572    case 3:
1573       delete_snapshot(ua);
1574       return 1;
1575    case 4:
1576       delete_client(ua);
1577       return 1;
1578    default:
1579       break;
1580    }
1581 
1582    ua->warning_msg(_(
1583 "In general it is not a good idea to delete either a\n"
1584 "Pool or a Volume since they may contain data.\n\n"));
1585 
1586    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1587    case 0:
1588       delete_volume(ua);
1589       break;
1590    case 1:
1591       delete_pool(ua);
1592       break;
1593    case 2:
1594       delete_job(ua);
1595       return 1;
1596    case 3:
1597       delete_snapshot(ua);
1598       return 1;
1599    case 4:
1600       delete_client(ua);
1601       return 1;
1602    default:
1603       ua->warning_msg(_("Nothing done.\n"));
1604       break;
1605    }
1606    return 1;
1607 }
1608 
1609 /*
1610  * delete_job has been modified to parse JobID lists like the
1611  * following:
1612  * delete JobID=3,4,6,7-11,14
1613  *
1614  * Thanks to Phil Stracchino for the above addition.
1615  */
delete_job(UAContext * ua)1616 static void delete_job(UAContext *ua)
1617 {
1618    int JobId;               /* not JobId_t because it's unsigned and not compatible with sellist */
1619    char buf[256];
1620    sellist sl;
1621 
1622    int i = find_arg_with_value(ua, NT_("jobid"));
1623    if (i >= 0) {
1624       if (!sl.set_string(ua->argv[i], true)) {
1625          ua->warning_msg("%s", sl.get_errmsg());
1626          return;
1627       }
1628 
1629       if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1630          bsnprintf(buf, sizeof(buf),
1631                    _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1632          if (!get_yesno(ua, buf) || ua->pint32_val==0) {
1633             return;
1634          }
1635       }
1636 
1637       foreach_sellist(JobId, &sl) {
1638          do_job_delete(ua, JobId);
1639       }
1640 
1641    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1642       return;
1643 
1644    } else {
1645       JobId = ua->int64_val;
1646       do_job_delete(ua, JobId);
1647    }
1648 }
1649 
1650 /*
1651  * do_job_delete now performs the actual delete operation atomically
1652  */
do_job_delete(UAContext * ua,JobId_t JobId)1653 static void do_job_delete(UAContext *ua, JobId_t JobId)
1654 {
1655    char ed1[50];
1656 
1657    edit_int64(JobId, ed1);
1658    purge_jobs_from_catalog(ua, ed1);
1659    ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1660 }
1661 
1662 /*
1663  * Delete media records from database -- dangerous
1664  */
delete_volume(UAContext * ua)1665 static int delete_volume(UAContext *ua)
1666 {
1667    MEDIA_DBR mr;
1668    char buf[1000];
1669    db_list_ctx lst;
1670 
1671    if (!select_media_dbr(ua, &mr)) {
1672       return 1;
1673    }
1674    ua->warning_msg(_("\nThis command will delete volume %s\n"
1675       "and all Jobs saved on that volume from the Catalog\n"),
1676       mr.VolumeName);
1677 
1678    if (find_arg(ua, "yes") >= 0) {
1679       ua->pint32_val = 1; /* Have "yes" on command line already" */
1680    } else {
1681       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1682          mr.VolumeName);
1683       if (!get_yesno(ua, buf)) {
1684          return 1;
1685       }
1686    }
1687    if (!ua->pint32_val) {
1688       return 1;
1689    }
1690 
1691    /* If not purged, do it */
1692    if (strcmp(mr.VolStatus, "Purged") != 0) {
1693       if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1694          ua->error_msg(_("Can't list jobs on this volume\n"));
1695          return 1;
1696       }
1697       if (lst.count) {
1698          purge_jobs_from_catalog(ua, lst.list);
1699       }
1700    }
1701 
1702    db_delete_media_record(ua->jcr, ua->db, &mr);
1703    return 1;
1704 }
1705 
1706 /*
1707  * Delete a pool record from the database -- dangerous
1708  * TODO: Check if the resource is still defined?
1709  */
delete_pool(UAContext * ua)1710 static int delete_pool(UAContext *ua)
1711 {
1712    POOL_DBR  pr;
1713    char buf[200];
1714 
1715    memset(&pr, 0, sizeof(pr));
1716 
1717    if (!get_pool_dbr(ua, &pr)) {
1718       return 1;
1719    }
1720    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1721       pr.Name);
1722    if (!get_yesno(ua, buf)) {
1723       return 1;
1724    }
1725    if (ua->pint32_val) {
1726       db_delete_pool_record(ua->jcr, ua->db, &pr);
1727    }
1728    return 1;
1729 }
1730 
1731 /*
1732  * Delete a client record from the database
1733  */
delete_client(UAContext * ua)1734 static int delete_client(UAContext *ua)
1735 {
1736    CLIENT *client;
1737    CLIENT_DBR  cr;
1738    char buf[200];
1739    db_list_ctx lst;
1740 
1741    memset(&cr, 0, sizeof(cr));
1742 
1743    if (!get_client_dbr(ua, &cr, 0)) {
1744       return 1;
1745    }
1746 
1747    client = (CLIENT*) GetResWithName(R_CLIENT, cr.Name);
1748 
1749    if (client) {
1750       ua->error_msg(_("Unable to delete Client \"%s\", the resource is still defined in the configuration.\n"), cr.Name);
1751       return 1;
1752    }
1753 
1754    if (!db_get_client_jobids(ua->jcr, ua->db, &cr, &lst)) {
1755       ua->error_msg(_("Can't list jobs on this client\n"));
1756       return 1;
1757    }
1758 
1759    if (find_arg(ua, "yes") > 0) {
1760       ua->pint32_val = 1;
1761 
1762    } else {
1763       if (lst.count  == 0) {
1764          bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Client \"%s\? (yes/no): "), cr.Name);
1765       } else {
1766          bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Client \"%s\" and purge %d job(s)? (yes/no): "), cr.Name, lst.count);
1767       }
1768       if (!get_yesno(ua, buf)) {
1769          return 1;
1770       }
1771    }
1772 
1773    if (ua->pint32_val) {
1774       if (lst.count) {
1775          ua->send_msg(_("Purging %d job(s).\n"), lst.count);
1776          purge_jobs_from_catalog(ua, lst.list);
1777       }
1778       ua->send_msg(_("Deleting client \"%s\".\n"), cr.Name);
1779       db_delete_client_record(ua->jcr, ua->db, &cr);
1780    }
1781    return 1;
1782 }
1783 
memory_cmd(UAContext * ua,const char * cmd)1784 int memory_cmd(UAContext *ua, const char *cmd)
1785 {
1786    garbage_collect_memory();
1787    list_dir_status_header(ua);
1788    sm_dump(false, true);
1789    return 1;
1790 }
1791 
do_storage_cmd(UAContext * ua,const char * command)1792 static void do_storage_cmd(UAContext *ua, const char *command)
1793 {
1794    USTORE store;
1795    BSOCK *sd;
1796    JCR *jcr = ua->jcr;
1797    char dev_name[MAX_NAME_LENGTH];
1798    int drive, i;
1799    int slot;
1800 
1801    if (!open_client_db(ua)) {
1802       return;
1803    }
1804    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1805 
1806    store.store = get_storage_resource(ua, true/*arg is storage*/);
1807    if (!store.store) {
1808       return;
1809    }
1810    pm_strcpy(store.store_source, _("unknown source"));
1811    set_wstorage(jcr, &store);
1812    drive = get_storage_drive(ua, store.store);
1813    /* For the disable/enable/unmount commands, the slot is not mandatory */
1814    if (strcasecmp(command, "disable") == 0 ||
1815         strcasecmp(command, "enable") == 0 ||
1816         strcasecmp(command, "unmount")  == 0) {
1817       slot = 0;
1818    } else {
1819       slot = get_storage_slot(ua, store.store);
1820    }
1821    /* Users may set a device name directly on the command line */
1822    if ((i = find_arg_with_value(ua, "device")) > 0) {
1823       POOLMEM *errmsg = get_pool_memory(PM_NAME);
1824       if (!is_name_valid(ua->argv[i], &errmsg)) {
1825          ua->error_msg(_("Invalid device name. %s"), errmsg);
1826          free_pool_memory(errmsg);
1827          return;
1828       }
1829       free_pool_memory(errmsg);
1830       bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1831 
1832    } else {                     /* We take the default device name */
1833       bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1834    }
1835 
1836    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1837       store.store->media_type, store.store->dev_name(), drive);
1838    Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1839 
1840    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1841       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1842       return;
1843    }
1844    sd = jcr->store_bsock;
1845    bash_spaces(dev_name);
1846    sd->fsend("%s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1847    while (sd->recv() >= 0) {
1848       ua->send_msg("%s", sd->msg);
1849    }
1850    sd->signal(BNET_TERMINATE);
1851    free_bsock(ua->jcr->store_bsock);
1852 }
1853 
1854 /*
1855  * mount [storage=<name>] [drive=nn] [slot=mm]
1856  */
mount_cmd(UAContext * ua,const char * cmd)1857 static int mount_cmd(UAContext *ua, const char *cmd)
1858 {
1859    do_storage_cmd(ua, "mount")  ;          /* mount */
1860    return 1;
1861 }
1862 
1863 
1864 /*
1865  * unmount [storage=<name>] [drive=nn]
1866  */
unmount_cmd(UAContext * ua,const char * cmd)1867 static int unmount_cmd(UAContext *ua, const char *cmd)
1868 {
1869    do_storage_cmd(ua, "unmount");          /* unmount */
1870    return 1;
1871 }
1872 
1873 
1874 /*
1875  * release [storage=<name>] [drive=nn]
1876  */
release_cmd(UAContext * ua,const char * cmd)1877 static int release_cmd(UAContext *ua, const char *cmd)
1878 {
1879    do_storage_cmd(ua, "release");          /* release */
1880    return 1;
1881 }
1882 
1883 /*
1884  * cloud functions, like to upload cached parts to cloud.
1885  */
cloud_volumes_cmd(UAContext * ua,const char * cmd,const char * mode)1886 int cloud_volumes_cmd(UAContext *ua, const char *cmd, const char *mode)
1887 {
1888    int drive = -1;
1889    int nb = 0;
1890    uint32_t *results = NULL;
1891    MEDIA_DBR mr;
1892    POOL_DBR pr;
1893    BSOCK *sd = NULL;
1894    char storage[MAX_NAME_LENGTH];
1895    const char *action = mode;
1896    memset(&pr, 0, sizeof(pr));
1897 
1898    /*
1899     * Look for all volumes that are enabled and
1900     *  have more the 200 bytes.
1901     */
1902    mr.Enabled = 1;
1903    mr.Recycle = -1;             /* All Recycle status */
1904    if (strcmp("prunecache", mode) == 0) {
1905       mr.CacheRetention = 1;
1906       action = "truncate cache";
1907    }
1908 
1909    if (!scan_storage_cmd(ua, cmd, false, /* fromallpool*/
1910                          &drive, &mr, &pr, NULL, storage,
1911                          &nb, &results))
1912    {
1913       goto bail_out;
1914    }
1915 
1916    if ((sd=open_sd_bsock(ua)) == NULL) {
1917       Dmsg0(100, "Can't open connection to sd\n");
1918       goto bail_out;
1919    }
1920 
1921    /*
1922     * Loop over the candidate Volumes and upload parts
1923     */
1924    for (int i=0; i < nb; i++) {
1925       bool ok=false;
1926       mr.clear();
1927       mr.MediaId = results[i];
1928       if (!db_get_media_record(ua->jcr, ua->db, &mr)) {
1929          goto bail_out;
1930       }
1931 
1932       /* Protect us from spaces */
1933       bash_spaces(mr.VolumeName);
1934       bash_spaces(mr.MediaType);
1935       bash_spaces(pr.Name);
1936       bash_spaces(storage);
1937 
1938       sd->fsend("%s Storage=%s Volume=%s PoolName=%s MediaType=%s "
1939                 "Slot=%d drive=%d CacheRetention=%lld\n",
1940                 action, storage, mr.VolumeName, pr.Name, mr.MediaType,
1941                 mr.Slot, drive, mr.CacheRetention);
1942 
1943       unbash_spaces(mr.VolumeName);
1944       unbash_spaces(mr.MediaType);
1945       unbash_spaces(pr.Name);
1946       unbash_spaces(storage);
1947 
1948       /* Check for valid response */
1949       while (bget_dirmsg(sd) >= 0) {
1950          if (strncmp(sd->msg, "3000 OK truncate cache", 22) == 0) {
1951             ua->send_msg("%s", sd->msg);
1952             ok = true;
1953 
1954          } else if (strncmp(sd->msg, "3000 OK", 7) == 0) {
1955             ua->send_msg(_("The volume \"%s\" has been uploaded\n"), mr.VolumeName);
1956             ok = true;
1957 
1958 
1959          } else if (strncmp(sd->msg, "39", 2) == 0) {
1960             ua->warning_msg("%s", sd->msg);
1961 
1962          } else {
1963             ua->send_msg("%s", sd->msg);
1964          }
1965       }
1966       if (!ok) {
1967          ua->warning_msg(_("Unable to %s for volume \"%s\"\n"), action, mr.VolumeName);
1968       }
1969    }
1970 
1971 bail_out:
1972    close_db(ua);
1973    close_sd_bsock(ua);
1974    ua->jcr->wstore = NULL;
1975    if (results) {
1976       free(results);
1977    }
1978 
1979    return 1;
1980 }
1981 
1982 /* List volumes in the cloud */
1983 /* TODO: Update the code for .api 2 and llist */
cloud_list_cmd(UAContext * ua,const char * cmd)1984 static int cloud_list_cmd(UAContext *ua, const char *cmd)
1985 {
1986    int drive = -1;
1987    int64_t size, mtime;
1988    STORE *store = NULL;
1989    MEDIA_DBR mr;
1990    POOL_DBR pr;
1991    BSOCK *sd = NULL;
1992    char storage[MAX_NAME_LENGTH];
1993    char ed1[50], ed2[50];
1994    bool first=true;
1995    uint32_t maxpart=0, part;
1996    uint64_t maxpart_size=0;
1997    memset((void *)&pr, 0, sizeof(pr));
1998    memset((void *)&mr, 0, sizeof(mr));
1999 
2000    /* Look at arguments */
2001    for (int i=1; i<ua->argc; i++) {
2002       if (strcasecmp(ua->argk[i], NT_("volume")) == 0
2003           && is_name_valid(ua->argv[i], NULL)) {
2004          bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName));
2005 
2006       } else if (strcasecmp(ua->argk[i], NT_("drive")) == 0 && ua->argv[i]) {
2007          drive = atoi(ua->argv[i]);
2008       }
2009    }
2010 
2011    if (!open_client_db(ua)) {
2012       goto bail_out;
2013    }
2014 
2015    /* Choose storage */
2016    ua->jcr->wstore = store = get_storage_resource(ua, false);
2017    if (!store) {
2018       goto bail_out;
2019    }
2020    bstrncpy(storage, store->dev_name(), sizeof(storage));
2021    bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
2022 
2023    if ((sd=open_sd_bsock(ua)) == NULL) {
2024       Dmsg0(100, "Can't open connection to SD\n");
2025       goto bail_out;
2026    }
2027 
2028    /* Protect us from spaces */
2029    bash_spaces(mr.MediaType);
2030    bash_spaces(storage);
2031    bash_spaces(mr.VolumeName);
2032 
2033    sd->fsend("cloudlist Storage=%s Volume=%s MediaType=%s Slot=%d drive=%d\n",
2034              storage, mr.VolumeName,  mr.MediaType, mr.Slot, drive);
2035 
2036    if (mr.VolumeName[0]) {      /* Want to list parts */
2037       const char *output_hformat="| %8d | %12sB | %20s |\n";
2038       uint64_t volsize=0;
2039       /* Check for valid response */
2040       while (sd->recv() >= 0) {
2041          if (sscanf(sd->msg, "part=%d size=%lld mtime=%lld", &part, &size, &mtime) != 3) {
2042             if (sd->msg[0] == '3') {
2043                ua->send_msg("%s", sd->msg);
2044             }
2045             continue;
2046          }
2047          /* Print information */
2048          if (first) {
2049             ua->send_msg(_("+----------+---------------+----------------------+\n"));
2050             ua->send_msg(_("|   Part   |     Size      |   MTime              |\n"));
2051             ua->send_msg(_("+----------+---------------+----------------------+\n"));
2052             first=false;
2053          }
2054          if (part > maxpart) {
2055             maxpart = part;
2056             maxpart_size = size;
2057          }
2058          volsize += size;
2059          ua->send_msg(output_hformat, part, edit_uint64_with_suffix(size, ed1), bstrftimes(ed2, sizeof(ed2), mtime));
2060       }
2061       if (!first) {
2062          ua->send_msg(_("+----------+---------------+----------------------+\n"));
2063       }
2064       /* TODO: See if we fix the catalog record directly */
2065       if (db_get_media_record(ua->jcr, ua->db, &mr)) {
2066          POOL_MEM errmsg, tmpmsg;
2067          if (mr.LastPartBytes != maxpart_size) {
2068             Mmsg(tmpmsg, "Error on volume \"%s\". Catalog LastPartBytes mismatch %lld != %lld\n",
2069                  mr.VolumeName, mr.LastPartBytes, maxpart_size);
2070             pm_strcpy(errmsg, tmpmsg.c_str());
2071          }
2072          if (mr.VolCloudParts != maxpart) {
2073             Mmsg(tmpmsg, "Error on volume \"%s\". Catalog VolCloudParts mismatch %ld != %ld\n",
2074                  mr.VolumeName, mr.VolCloudParts, maxpart);
2075             pm_strcpy(errmsg, tmpmsg.c_str());
2076          }
2077          if (strlen(errmsg.c_str()) > 0) {
2078             ua->error_msg("\n%s", errmsg.c_str());
2079          }
2080       }
2081    } else {                     /* TODO: Get the last part if possible? */
2082       const char *output_hformat="| %18s | %9s | %20s | %20s | %12sB |\n";
2083 
2084       /* Check for valid response */
2085       while (sd->recv() >= 0) {
2086          if (sscanf(sd->msg, "volume=%127s", mr.VolumeName) != 1) {
2087             if (sd->msg[0] == '3') {
2088                ua->send_msg("%s", sd->msg);
2089             }
2090             continue;
2091          }
2092          unbash_spaces(mr.VolumeName);
2093 
2094          mr.MediaId = 0;
2095 
2096          if (mr.VolumeName[0] && db_get_media_record(ua->jcr, ua->db, &mr)) {
2097             memset(&pr, 0, sizeof(POOL_DBR));
2098             pr.PoolId = mr.PoolId;
2099             if (!db_get_pool_record(ua->jcr, ua->db, &pr)) {
2100                strcpy(pr.Name, "?");
2101             }
2102 
2103             if (first) {
2104                ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2105                ua->send_msg(_("|    Volume Name     |   Status  |     Media Type       |       Pool           |    VolBytes   |\n"));
2106                ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2107                first=false;
2108             }
2109             /* Print information */
2110             ua->send_msg(output_hformat, mr.VolumeName, mr.VolStatus, mr.MediaType, pr.Name,
2111                          edit_uint64_with_suffix(mr.VolBytes, ed1));
2112          }
2113       }
2114       if (!first) {
2115          ua->send_msg(_("+--------------------+-----------+----------------------+----------------------+---------------+\n"));
2116       }
2117    }
2118 
2119 bail_out:
2120    close_db(ua);
2121    close_sd_bsock(ua);
2122    ua->jcr->wstore = NULL;
2123    return 1;
2124 }
2125 
2126 /* Ask client to create/prune/delete a snapshot via the command line */
cloud_cmd(UAContext * ua,const char * cmd)2127 static int cloud_cmd(UAContext *ua, const char *cmd)
2128 {
2129    for (int i=0; i<ua->argc; i++) {
2130       if (strcasecmp(ua->argk[i], NT_("upload")) == 0) {
2131          return cloud_volumes_cmd(ua, cmd, "upload");
2132 
2133       } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
2134          return cloud_list_cmd(ua, cmd);
2135 
2136       } else if (strcasecmp(ua->argk[i], NT_("truncate")) == 0) {
2137          return cloud_volumes_cmd(ua, cmd, "truncate cache");
2138 
2139       } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
2140 
2141       } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
2142          return cloud_volumes_cmd(ua, cmd, "prunecache");
2143 
2144       } else {
2145          continue;
2146       }
2147    }
2148 
2149    for ( ;; ) {
2150 
2151       start_prompt(ua, _("Cloud choice: \n"));
2152       add_prompt(ua, _("List Cloud Volumes in the Cloud"));
2153       add_prompt(ua, _("Upload a Volume to the Cloud"));
2154       add_prompt(ua, _("Prune the Cloud Cache"));
2155       add_prompt(ua, _("Truncate a Volume Cache"));
2156       add_prompt(ua, _("Done"));
2157 
2158       switch(do_prompt(ua, "", _("Select action to perform on Cloud"), NULL, 0)) {
2159       case 0:                   /* list cloud */
2160          cloud_list_cmd(ua, cmd);
2161          break;
2162       case 1:                   /* upload */
2163          cloud_volumes_cmd(ua, cmd, "upload");
2164          break;
2165       case 2:                   /* Prune cache */
2166          cloud_volumes_cmd(ua, cmd, "prunecache");
2167          break;
2168       case 3:                   /* Truncate cache */
2169          cloud_volumes_cmd(ua, cmd, "truncate cache");
2170          break;
2171       default:
2172          ua->info_msg(_("Selection terminated.\n"));
2173          return 1;
2174       }
2175    }
2176    return 1;
2177 }
2178 
2179 /*
2180  * Switch databases
2181  *   use catalog=<name>
2182  */
use_cmd(UAContext * ua,const char * cmd)2183 static int use_cmd(UAContext *ua, const char *cmd)
2184 {
2185    CAT *oldcatalog, *catalog;
2186 
2187 
2188    close_db(ua);                      /* close any previously open db */
2189    oldcatalog = ua->catalog;
2190 
2191    if (!(catalog = get_catalog_resource(ua))) {
2192       ua->catalog = oldcatalog;
2193    } else {
2194       ua->catalog = catalog;
2195    }
2196    if (open_db(ua)) {
2197       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
2198          ua->catalog->name(), ua->catalog->db_name);
2199    }
2200    return 1;
2201 }
2202 
quit_cmd(UAContext * ua,const char * cmd)2203 int quit_cmd(UAContext *ua, const char *cmd)
2204 {
2205    ua->quit = true;
2206    return 1;
2207 }
2208 
2209 /* Handler to get job status */
status_handler(void * ctx,int num_fields,char ** row)2210 static int status_handler(void *ctx, int num_fields, char **row)
2211 {
2212    char *val = (char *)ctx;
2213 
2214    if (row[0]) {
2215       *val = row[0][0];
2216    } else {
2217       *val = '?';               /* Unknown by default */
2218    }
2219 
2220    return 0;
2221 }
2222 
2223 /*
2224  * Wait until no job is running
2225  */
wait_cmd(UAContext * ua,const char * cmd)2226 int wait_cmd(UAContext *ua, const char *cmd)
2227 {
2228    JCR *jcr;
2229    int i;
2230    time_t stop_time = 0;
2231 
2232    /*
2233     * no args
2234     * Wait until no job is running
2235     */
2236    if (ua->argc == 1) {
2237       bmicrosleep(0, 200000);            /* let job actually start */
2238       for (bool running=true; running; ) {
2239          running = false;
2240          foreach_jcr(jcr) {
2241             if (!jcr->is_internal_job()) {
2242                running = true;
2243                break;
2244             }
2245          }
2246          endeach_jcr(jcr);
2247 
2248          if (running) {
2249             bmicrosleep(1, 0);
2250          }
2251       }
2252       return 1;
2253    }
2254 
2255    i = find_arg_with_value(ua, NT_("timeout"));
2256    if (i > 0 && ua->argv[i]) {
2257       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
2258    }
2259 
2260    /* we have jobid, jobname or ujobid argument */
2261 
2262    uint32_t jobid = 0 ;
2263 
2264    if (!open_client_db(ua)) {
2265       ua->error_msg(_("ERR: Can't open db\n")) ;
2266       return 1;
2267    }
2268 
2269    for (int i=1; i<ua->argc; i++) {
2270       if (strcasecmp(ua->argk[i], "jobid") == 0) {
2271          if (!ua->argv[i]) {
2272             break;
2273          }
2274          jobid = str_to_int64(ua->argv[i]);
2275          break;
2276       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
2277                  strcasecmp(ua->argk[i], "job") == 0) {
2278          if (!ua->argv[i]) {
2279             break;
2280          }
2281          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
2282          if (jcr) {
2283             jobid = jcr->JobId ;
2284             free_jcr(jcr);
2285          }
2286          break;
2287       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
2288          if (!ua->argv[i]) {
2289             break;
2290          }
2291          jcr=get_jcr_by_full_name(ua->argv[i]) ;
2292          if (jcr) {
2293             jobid = jcr->JobId ;
2294             free_jcr(jcr);
2295          }
2296          break;
2297       /* Wait for a mount request */
2298       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
2299          for (bool waiting=false; !waiting; ) {
2300             foreach_jcr(jcr) {
2301                if (!jcr->is_internal_job() &&
2302                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount ||
2303                     jcr->SDJobStatus == JS_WaitMedia || jcr->SDJobStatus == JS_WaitMount))
2304                {
2305                   waiting = true;
2306                   break;
2307                }
2308             }
2309             endeach_jcr(jcr);
2310             if (waiting) {
2311                break;
2312             }
2313             if (stop_time && (time(NULL) >= stop_time)) {
2314                ua->warning_msg(_("Wait on mount timed out\n"));
2315                return 1;
2316             }
2317             bmicrosleep(1, 0);
2318          }
2319          return 1;
2320       }
2321    }
2322 
2323    if (jobid == 0) {
2324       ua->error_msg(_("ERR: Job was not found\n"));
2325       return 1 ;
2326    }
2327 
2328    /*
2329     * We wait the end of a specific job
2330     */
2331 
2332    bmicrosleep(0, 200000);            /* let job actually start */
2333    for (bool running=true; running; ) {
2334       running = false;
2335 
2336       jcr=get_jcr_by_id(jobid) ;
2337 
2338       if (jcr) {
2339          running = true ;
2340          free_jcr(jcr);
2341       }
2342 
2343       if (running) {
2344          bmicrosleep(1, 0);
2345       }
2346    }
2347 
2348    /*
2349     * We have to get JobStatus
2350     */
2351 
2352    int status ;
2353    char jobstatus = '?';        /* Unknown by default */
2354    char buf[256] ;
2355 
2356    bsnprintf(buf, sizeof(buf),
2357              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
2358 
2359 
2360    db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
2361 
2362    switch (jobstatus) {
2363    case JS_Error:
2364       status = 1 ;         /* Warning */
2365       break;
2366 
2367    case JS_Incomplete:
2368    case JS_FatalError:
2369    case JS_ErrorTerminated:
2370    case JS_Canceled:
2371       status = 2 ;         /* Critical */
2372       break;
2373 
2374    case JS_Warnings:
2375    case JS_Terminated:
2376       status = 0 ;         /* Ok */
2377       break;
2378 
2379    default:
2380       status = 3 ;         /* Unknown */
2381       break;
2382    }
2383 
2384    ua->send_msg("JobId=%i\n", jobid) ;
2385    ua->send_msg("JobStatus=%s (%c)\n",
2386                 job_status_to_str(jobstatus, 0),
2387                 jobstatus) ;
2388 
2389    if (ua->gui || ua->api) {
2390       ua->send_msg("ExitStatus=%i\n", status) ;
2391    }
2392 
2393    return 1;
2394 }
2395 
2396 
help_cmd(UAContext * ua,const char * cmd)2397 static int help_cmd(UAContext *ua, const char *cmd)
2398 {
2399    int i;
2400    ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
2401    for (i=0; i<comsize; i++) {
2402       if (ua->argc == 2) {
2403          if (!strcasecmp(ua->argk[1], commands[i].key)) {
2404             ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2405                          commands[i].help, commands[i].usage);
2406             break;
2407          }
2408       } else {
2409          ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
2410       }
2411    }
2412    if (i == comsize && ua->argc == 2) {
2413       ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2414    }
2415    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2416    return 1;
2417 }
2418 
qhelp_cmd(UAContext * ua,const char * cmd)2419 int qhelp_cmd(UAContext *ua, const char *cmd)
2420 {
2421    int i,j;
2422    /* Want to display only commands */
2423    j = find_arg(ua, NT_("all"));
2424    if (j >= 0) {
2425       for (i=0; i<comsize; i++) {
2426          ua->send_msg("%s\n", commands[i].key);
2427       }
2428       return 1;
2429    }
2430    /* Want to display a specific help section */
2431    j = find_arg_with_value(ua, NT_("item"));
2432    if (j >= 0 && ua->argk[j]) {
2433       for (i=0; i<comsize; i++) {
2434          if (bstrcmp(commands[i].key, ua->argv[j])) {
2435             ua->send_msg("%s\n", commands[i].usage);
2436             break;
2437          }
2438       }
2439       return 1;
2440    }
2441    /* Want to display everything */
2442    for (i=0; i<comsize; i++) {
2443       ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2444    }
2445    return 1;
2446 }
2447 
2448 #if 1
version_cmd(UAContext * ua,const char * cmd)2449 static int version_cmd(UAContext *ua, const char *cmd)
2450 {
2451    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2452                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2453    return 1;
2454 }
2455 #else
2456 /*
2457  *  Test code -- turned on only for debug testing
2458  */
version_cmd(UAContext * ua,const char * cmd)2459 static int version_cmd(UAContext *ua, const char *cmd)
2460 {
2461    dbid_list ids;
2462    POOL_MEM query(PM_MESSAGE);
2463    open_db(ua);
2464    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2465    db_get_query_dbids(ua->jcr, ua->db, query, ids);
2466    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2467    for (int i=0; i < ids.num_ids; i++) {
2468       ua->send_msg("id=%d\n", ids.DBId[i]);
2469    }
2470    close_db(ua);
2471    return 1;
2472 }
2473 #endif
2474 
2475 /*
2476  * This call uses open_client_db() and force a
2477  * new dedicated connection to the catalog
2478  */
open_new_client_db(UAContext * ua)2479 bool open_new_client_db(UAContext *ua)
2480 {
2481    bool ret;
2482 
2483    /* Force a new dedicated connection */
2484    ua->force_mult_db_connections = true;
2485    ret = open_client_db(ua);
2486    ua->force_mult_db_connections = false;
2487 
2488    return ret;
2489 }
2490 
2491 /*
2492  * This call explicitly checks for a catalog=xxx and
2493  *  if given, opens that catalog.  It also checks for
2494  *  client=xxx and if found, opens the catalog
2495  *  corresponding to that client. If we still don't
2496  *  have a catalog, look for a Job keyword and get the
2497  *  catalog from its client record.
2498  */
open_client_db(UAContext * ua)2499 bool open_client_db(UAContext *ua)
2500 {
2501    int i;
2502    CAT *catalog;
2503    CLIENT *client;
2504    JOB *job;
2505 
2506    /* Try for catalog keyword */
2507    i = find_arg_with_value(ua, NT_("catalog"));
2508    if (i >= 0) {
2509       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2510          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2511          return false;
2512       }
2513       catalog = GetCatalogResWithName(ua->argv[i]);
2514       if (catalog) {
2515          if (ua->catalog && ua->catalog != catalog) {
2516             close_db(ua);
2517          }
2518          ua->catalog = catalog;
2519          return open_db(ua);
2520       }
2521    }
2522 
2523    /* Try for client keyword */
2524    i = find_arg_with_value(ua, NT_("client"));
2525    if (i >= 0) {
2526       if (!acl_access_client_ok(ua, ua->argv[i], JT_BACKUP_RESTORE)) {
2527          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2528          return false;
2529       }
2530       client = GetClientResWithName(ua->argv[i]);
2531       if (client) {
2532          catalog = client->catalog;
2533          if (ua->catalog && ua->catalog != catalog) {
2534             close_db(ua);
2535          }
2536          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2537             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2538             return false;
2539          }
2540          ua->catalog = catalog;
2541          return open_db(ua);
2542       }
2543    }
2544 
2545    /* Try for Job keyword */
2546    i = find_arg_with_value(ua, NT_("job"));
2547    if (i >= 0) {
2548       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2549          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2550          return false;
2551       }
2552       job = GetJobResWithName(ua->argv[i]);
2553       if (job) {
2554          catalog = job->client->catalog;
2555          if (ua->catalog && ua->catalog != catalog) {
2556             close_db(ua);
2557          }
2558          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2559             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2560             return false;
2561          }
2562          ua->catalog = catalog;
2563          return open_db(ua);
2564       }
2565    }
2566 
2567    return open_db(ua);
2568 }
2569 
2570 
2571 /*
2572  * Open the catalog database.
2573  */
open_db(UAContext * ua)2574 bool open_db(UAContext *ua)
2575 {
2576    bool mult_db_conn;
2577 
2578    /* With a restricted console, we can't share a SQL connection */
2579    if (ua->cons) {
2580       ua->force_mult_db_connections = true;
2581    }
2582 
2583    /* The force_mult_db_connections is telling us if we modify the
2584     * private or the shared link
2585     */
2586    if (ua->force_mult_db_connections) {
2587       ua->db = ua->private_db;
2588 
2589    } else {
2590       ua->db = ua->shared_db;
2591    }
2592 
2593    if (ua->db) {
2594       return true;
2595    }
2596 
2597    if (!ua->catalog) {
2598       ua->catalog = get_catalog_resource(ua);
2599       if (!ua->catalog) {
2600          ua->error_msg( _("Could not find a Catalog resource\n"));
2601          return false;
2602       }
2603    }
2604 
2605    /* Some modules like bvfs need their own catalog connection */
2606    mult_db_conn = ua->catalog->mult_db_connections;
2607    if (ua->force_mult_db_connections) {
2608       mult_db_conn = true;
2609    }
2610 
2611    ua->jcr->catalog = ua->catalog;
2612 
2613    Dmsg0(100, "UA Open database\n");
2614    ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2615                              ua->catalog->db_name,
2616                              ua->catalog->db_user,
2617                              ua->catalog->db_password, ua->catalog->db_address,
2618                              ua->catalog->db_port, ua->catalog->db_socket,
2619                              ua->catalog->db_ssl_mode, ua->catalog->db_ssl_key,
2620                              ua->catalog->db_ssl_cert, ua->catalog->db_ssl_ca,
2621                              ua->catalog->db_ssl_capath, ua->catalog->db_ssl_cipher,
2622                              mult_db_conn, ua->catalog->disable_batch_insert);
2623    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2624       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2625                  ua->catalog->db_name);
2626       if (ua->db) {
2627          ua->error_msg("%s", db_strerror(ua->db));
2628       }
2629       close_db(ua);
2630       return false;
2631    }
2632    ua->jcr->db = ua->db;
2633 
2634    /* Depending on the type of connection, we set the right variable */
2635    if (ua->force_mult_db_connections) {
2636       ua->private_db = ua->db;
2637 
2638    } else {
2639       ua->shared_db = ua->db;
2640    }
2641    /* With a restricted console, the DB backend should know restrictions about
2642     * Pool, Job, etc...
2643     */
2644    if (ua->cons) {
2645       ua->db->set_acl(ua->jcr, DB_ACL_JOB, ua->cons->ACL_lists[Job_ACL]);
2646       ua->db->set_acl(ua->jcr, DB_ACL_CLIENT, ua->cons->ACL_lists[Client_ACL]);
2647       ua->db->set_acl(ua->jcr, DB_ACL_POOL, ua->cons->ACL_lists[Pool_ACL]);
2648       ua->db->set_acl(ua->jcr, DB_ACL_FILESET, ua->cons->ACL_lists[FileSet_ACL]);
2649 
2650       /* For RestoreClient and BackupClient, we take also in account the Client list */
2651       ua->db->set_acl(ua->jcr, DB_ACL_RCLIENT,
2652                       ua->cons->ACL_lists[Client_ACL],
2653                       ua->cons->ACL_lists[RestoreClient_ACL]);
2654 
2655       ua->db->set_acl(ua->jcr, DB_ACL_BCLIENT,
2656                       ua->cons->ACL_lists[Client_ACL],
2657                       ua->cons->ACL_lists[BackupClient_ACL]);
2658    }
2659    if (!ua->api) {
2660       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2661    }
2662    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2663    return true;
2664 }
2665 
close_db(UAContext * ua)2666 void close_db(UAContext *ua)
2667 {
2668    if (ua->jcr) {
2669       ua->jcr->db = NULL;
2670    }
2671 
2672    if (ua->shared_db) {
2673       db_close_database(ua->jcr, ua->shared_db);
2674       ua->shared_db = NULL;
2675    }
2676 
2677    if (ua->private_db) {
2678       db_close_database(ua->jcr, ua->private_db);
2679       ua->private_db = NULL;
2680    }
2681 
2682    ua->db = NULL;
2683 }
2684