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