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