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