]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
Patch to add MySQL ssl access
[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_dir_setdebug(UAContext *ua, int64_t level, int trace_flag, char *options, int64_t tags)
918 {
919    debug_level = level;
920    debug_level_tags = tags;
921    set_trace(trace_flag);
922    set_debug_flags(options);
923 }
924
925 static void do_storage_setdebug(UAContext *ua, STORE *store,
926                int64_t level, int trace_flag, int hangup, int blowup,
927                char *options, char *tags)
928 {
929    BSOCK *sd;
930    USTORE lstore;
931
932    lstore.store = store;
933    pm_strcpy(lstore.store_source, _("unknown source"));
934    set_wstorage(ua->jcr, &lstore);
935    /* Try connecting for up to 15 seconds */
936    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
937       store->name(), store->address, store->SDport);
938    if (!connect_to_storage_daemon(ua->jcr, 1, 15, 0)) {
939       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
940       return;
941    }
942    Dmsg0(120, _("Connected to storage daemon\n"));
943    sd = ua->jcr->store_bsock;
944    sd->fsend("setdebug=%ld trace=%ld hangup=%ld blowup=%ld options=%s tags=%s\n",
945              (int32_t)level, trace_flag, hangup, blowup, options, NPRTB(tags));
946    if (sd->recv() >= 0) {
947       ua->send_msg("%s", sd->msg);
948    }
949    sd->signal(BNET_TERMINATE);
950    free_bsock(ua->jcr->store_bsock);
951    return;
952 }
953
954 /*
955  * For the client, we have the following values that can be set
956  *  level = debug level
957  *  trace = send debug output to a file
958  *  options = various options for debug or specific FD behavior
959  *  hangup = how many records to send to FD before hanging up
960  *    obviously this is most useful for testing restarting
961  *    failed jobs.
962  *  blowup = how many records to send to FD before blowing up the FD.
963  */
964 static void do_client_setdebug(UAContext *ua, CLIENT *client,
965                int64_t level, int trace, int hangup, int blowup,
966                char *options, char *tags)
967 {
968    CLIENT *old_client;
969    BSOCK *fd;
970
971    /* Connect to File daemon */
972
973    old_client = ua->jcr->client;
974    ua->jcr->client = client;
975    /* Try to connect for 15 seconds */
976    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
977       client->name(), client->address, client->FDport);
978    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
979       ua->error_msg(_("Failed to connect to Client.\n"));
980       ua->jcr->client = old_client;
981       return;
982    }
983    Dmsg0(120, "Connected to file daemon\n");
984
985    fd = ua->jcr->file_bsock;
986    if (ua->jcr->FDVersion <= 10) {
987       fd->fsend("setdebug=%ld trace=%d hangup=%d\n",
988                 (int32_t)level, trace, hangup);
989    } else {
990       fd->fsend("setdebug=%ld trace=%d hangup=%d blowup=%d options=%s tags=%s\n",
991                 (int32_t)level, trace, hangup, blowup, options, NPRTB(tags));
992    }
993    if (fd->recv() >= 0) {
994       ua->send_msg("%s", fd->msg);
995    }
996    fd->signal(BNET_TERMINATE);
997    free_bsock(ua->jcr->file_bsock);
998    ua->jcr->client = old_client;
999    return;
1000 }
1001
1002
1003 static void do_all_setdebug(UAContext *ua, int64_t level,
1004                int trace_flag, int hangup, int blowup,
1005                char *options, char *tags)
1006 {
1007    STORE *store, **unique_store;
1008    CLIENT *client, **unique_client;
1009    int i, j, found;
1010    int64_t t=0;
1011
1012    /* Director */
1013    debug_parse_tags(tags, &t);
1014    do_dir_setdebug(ua, level, trace_flag, options, t);
1015
1016    /* Count Storage items */
1017    LockRes();
1018    store = NULL;
1019    i = 0;
1020    foreach_res(store, R_STORAGE) {
1021       i++;
1022    }
1023    unique_store = (STORE **) malloc(i * sizeof(STORE));
1024    /* Find Unique Storage address/port */
1025    store = (STORE *)GetNextRes(R_STORAGE, NULL);
1026    i = 0;
1027    unique_store[i++] = store;
1028    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
1029       found = 0;
1030       for (j=0; j<i; j++) {
1031          if (strcmp(unique_store[j]->address, store->address) == 0 &&
1032              unique_store[j]->SDport == store->SDport) {
1033             found = 1;
1034             break;
1035          }
1036       }
1037       if (!found) {
1038          unique_store[i++] = store;
1039          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
1040       }
1041    }
1042    UnlockRes();
1043
1044    /* Call each unique Storage daemon */
1045    for (j=0; j<i; j++) {
1046       do_storage_setdebug(ua, unique_store[j], level, trace_flag,
1047          hangup, blowup, options, tags);
1048    }
1049    free(unique_store);
1050
1051    /* Count Client items */
1052    LockRes();
1053    client = NULL;
1054    i = 0;
1055    foreach_res(client, R_CLIENT) {
1056       i++;
1057    }
1058    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
1059    /* Find Unique Client address/port */
1060    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
1061    i = 0;
1062    unique_client[i++] = client;
1063    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
1064       found = 0;
1065       for (j=0; j<i; j++) {
1066          if (strcmp(unique_client[j]->address, client->address) == 0 &&
1067              unique_client[j]->FDport == client->FDport) {
1068             found = 1;
1069             break;
1070          }
1071       }
1072       if (!found) {
1073          unique_client[i++] = client;
1074          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
1075       }
1076    }
1077    UnlockRes();
1078
1079    /* Call each unique File daemon */
1080    for (j=0; j<i; j++) {
1081       do_client_setdebug(ua, unique_client[j], level, trace_flag,
1082          hangup, blowup, options, tags);
1083    }
1084    free(unique_client);
1085 }
1086
1087 /*
1088  * setdebug level=nn all trace=1/0
1089  */
1090 static int setdebug_cmd(UAContext *ua, const char *cmd)
1091 {
1092    STORE *store;
1093    CLIENT *client;
1094    int64_t level=0, tags=0;
1095    int trace_flag = -1;
1096    int hangup = -1;
1097    int blowup = -1;
1098    int i;
1099    char *tags_str=NULL;
1100    char options[60];
1101
1102    Dmsg1(120, "setdebug:%s:\n", cmd);
1103
1104    *options = 0;
1105    i = find_arg_with_value(ua, "options");
1106    if (i >= 0) {
1107       bstrncpy(options, ua->argv[i], sizeof(options) - 1);
1108    }
1109    level = -1;
1110    i = find_arg_with_value(ua, "level");
1111    if (i >= 0) {
1112       level = str_to_int64(ua->argv[i]);
1113    }
1114    if (level < 0) {
1115       if (!get_pint(ua, _("Enter new debug level: "))) {
1116          return 1;
1117       }
1118       level = ua->pint32_val;
1119    }
1120
1121    /* Better to send the tag string instead of tweaking the level
1122     * in case where we extend the tag or change the representation
1123     */
1124    i = find_arg_with_value(ua, "tags");
1125    if (i > 0) {
1126       tags_str = ua->argv[i];
1127       if (!debug_parse_tags(tags_str, &tags)) {
1128          ua->error_msg(_("Incorrect tags found on command line %s\n"), tags_str);
1129          return 1;
1130       }
1131    }
1132
1133    /* Look for trace flag. -1 => not change */
1134    i = find_arg_with_value(ua, "trace");
1135    if (i >= 0) {
1136       trace_flag = atoi(ua->argv[i]);
1137       if (trace_flag > 0) {
1138          trace_flag = 1;
1139       }
1140    }
1141
1142    /* Look for hangup (debug only) flag. -1 => not change */
1143    i = find_arg_with_value(ua, "hangup");
1144    if (i >= 0) {
1145       hangup = atoi(ua->argv[i]);
1146    }
1147
1148    /* Look for blowup (debug only) flag. -1 => not change */
1149    i = find_arg_with_value(ua, "blowup");
1150    if (i >= 0) {
1151       blowup = atoi(ua->argv[i]);
1152    }
1153
1154    /* General debug? */
1155    for (i=1; i<ua->argc; i++) {
1156       if (strcasecmp(ua->argk[i], "all") == 0) {
1157          do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1158          return 1;
1159       }
1160       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1161           strcasecmp(ua->argk[i], "director") == 0) {
1162          do_dir_setdebug(ua, level, trace_flag, options, tags);
1163          return 1;
1164       }
1165       if (strcasecmp(ua->argk[i], "client") == 0 ||
1166           strcasecmp(ua->argk[i], "fd") == 0) {
1167          client = NULL;
1168          if (ua->argv[i]) {
1169             client = GetClientResWithName(ua->argv[i]);
1170             if (client) {
1171                do_client_setdebug(ua, client, level, trace_flag,
1172                   hangup, blowup, options, tags_str);
1173                return 1;
1174             }
1175          }
1176          client = select_client_resource(ua);
1177          if (client) {
1178             do_client_setdebug(ua, client, level, trace_flag,
1179                hangup, blowup, options, tags_str);
1180             return 1;
1181          }
1182       }
1183
1184       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1185           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1186           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1187          store = NULL;
1188          if (ua->argv[i]) {
1189             store = GetStoreResWithName(ua->argv[i]);
1190             if (store) {
1191                do_storage_setdebug(ua, store, level, trace_flag,
1192                   hangup, blowup, options, tags_str);
1193                return 1;
1194             }
1195          }
1196          store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1197          if (store) {
1198             do_storage_setdebug(ua, store, level, trace_flag,
1199                hangup, blowup, options, tags_str);
1200             return 1;
1201          }
1202       }
1203    }
1204    /*
1205     * We didn't find an appropriate keyword above, so
1206     * prompt the user.
1207     */
1208    start_prompt(ua, _("Available daemons are: \n"));
1209    add_prompt(ua, _("Director"));
1210    add_prompt(ua, _("Storage"));
1211    add_prompt(ua, _("Client"));
1212    add_prompt(ua, _("All"));
1213    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1214    case 0:                         /* Director */
1215       do_dir_setdebug(ua, level, trace_flag, options, tags);
1216       break;
1217    case 1:
1218       store = get_storage_resource(ua, false/*no default*/, true/*unique*/);
1219       if (store) {
1220          do_storage_setdebug(ua, store, level, trace_flag, hangup, blowup,
1221             options, tags_str);
1222       }
1223       break;
1224    case 2:
1225       client = select_client_resource(ua);
1226       if (client) {
1227          do_client_setdebug(ua, client, level, trace_flag, hangup, blowup,
1228             options, tags_str);
1229       }
1230       break;
1231    case 3:
1232       do_all_setdebug(ua, level, trace_flag, hangup, blowup, options, tags_str);
1233       break;
1234    default:
1235       break;
1236    }
1237    return 1;
1238 }
1239
1240 /*
1241  * Turn debug tracing to file on/off
1242  */
1243 static int trace_cmd(UAContext *ua, const char *cmd)
1244 {
1245    char *onoff;
1246
1247    if (ua->argc != 2) {
1248       if (!get_cmd(ua, _("Turn on or off? "))) {
1249          return 1;
1250       }
1251       onoff = ua->cmd;
1252    } else {
1253       onoff = ua->argk[1];
1254    }
1255
1256    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1257    return 1;
1258 }
1259
1260 static int var_cmd(UAContext *ua, const char *cmd)
1261 {
1262    POOLMEM *val = get_pool_memory(PM_FNAME);
1263    char *var;
1264
1265    if (!open_client_db(ua)) {
1266       return 1;
1267    }
1268    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1269       var++;
1270    }
1271    while (*var == ' ') {                 /* skip spaces */
1272       var++;
1273    }
1274    Dmsg1(100, "Var=%s:\n", var);
1275    variable_expansion(ua->jcr, var, &val);
1276    ua->send_msg("%s\n", val);
1277    free_pool_memory(val);
1278    return 1;
1279 }
1280
1281 static int estimate_cmd(UAContext *ua, const char *cmd)
1282 {
1283    JOB *job = NULL;
1284    CLIENT *client = NULL;
1285    FILESET *fileset = NULL;
1286    int listing = 0;
1287    char since[MAXSTRING];
1288    JCR *jcr = ua->jcr;
1289    int accurate=-1;
1290
1291    jcr->setJobLevel(L_FULL);
1292    for (int i=1; i<ua->argc; i++) {
1293       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1294           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1295          if (ua->argv[i]) {
1296             client = GetClientResWithName(ua->argv[i]);
1297             if (!client) {
1298                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1299                return 1;
1300             }
1301             if (!acl_access_ok(ua, Client_ACL, client->name())) {
1302                ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
1303                return 1;
1304             }
1305             continue;
1306          } else {
1307             ua->error_msg(_("Client name missing.\n"));
1308             return 1;
1309          }
1310       }
1311       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1312          if (ua->argv[i]) {
1313             job = GetJobResWithName(ua->argv[i]);
1314             if (!job) {
1315                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1316                return 1;
1317             }
1318             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1319                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1320                return 1;
1321             }
1322             continue;
1323          } else {
1324             ua->error_msg(_("Job name missing.\n"));
1325             return 1;
1326          }
1327
1328       }
1329       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1330          if (ua->argv[i]) {
1331             fileset = GetFileSetResWithName(ua->argv[i]);
1332             if (!fileset) {
1333                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1334                return 1;
1335             }
1336             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1337                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1338                return 1;
1339             }
1340             continue;
1341          } else {
1342             ua->error_msg(_("Fileset name missing.\n"));
1343             return 1;
1344          }
1345       }
1346       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1347          listing = 1;
1348          continue;
1349       }
1350       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1351          if (ua->argv[i]) {
1352             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1353                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1354                return 1;
1355             }
1356             continue;
1357          } else {
1358             ua->error_msg(_("Level value missing.\n")); 
1359             return 1; 
1360          }
1361       }
1362       if (strcasecmp(ua->argk[i], NT_("accurate")) == 0) {
1363          if (ua->argv[i]) {
1364             if (!is_yesno(ua->argv[i], &accurate)) {
1365                ua->error_msg(_("Invalid value for accurate. "
1366                                "It must be yes or no.\n"));
1367                return 1;
1368             }
1369             continue;
1370          } else {
1371             ua->error_msg(_("Accurate value missing.\n"));
1372             return 1; 
1373          }
1374       }
1375    }
1376    if (!job && !(client && fileset)) {
1377       if (!(job = select_job_resource(ua))) {
1378          return 1;
1379       }
1380    }
1381    if (!job) {
1382       job = GetJobResWithName(ua->argk[1]);
1383       if (!job) {
1384          ua->error_msg(_("No job specified.\n"));
1385          return 1;
1386       }
1387       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1388          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1389          return 1;
1390       }
1391    }
1392    if (!client) {
1393       client = job->client;
1394    }
1395    if (!fileset) {
1396       fileset = job->fileset;
1397    }
1398    jcr->client = client;
1399    jcr->fileset = fileset;
1400    close_db(ua);
1401    if (job->pool->catalog) {
1402       ua->catalog = job->pool->catalog;
1403    } else {
1404       ua->catalog = client->catalog;
1405    }
1406
1407    if (!open_db(ua)) {
1408       return 1;
1409    }
1410
1411    jcr->job = job;
1412    jcr->setJobType(JT_BACKUP);
1413    jcr->start_time = time(NULL);
1414    init_jcr_job_record(jcr);
1415
1416    if (!get_or_create_client_record(jcr)) {
1417       return 1;
1418    }
1419    if (!get_or_create_fileset_record(jcr)) {
1420       return 1;
1421    }
1422
1423    get_level_since_time(ua->jcr, since, sizeof(since));
1424
1425    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1426       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1427    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1428       ua->error_msg(_("Failed to connect to Client.\n"));
1429       return 1;
1430    }
1431
1432    /* The level string change if accurate mode is enabled */
1433    if (accurate >= 0) {
1434       jcr->accurate = accurate;
1435    } else {
1436       jcr->accurate = job->accurate;
1437    }
1438
1439    if (!send_level_command(jcr)) {
1440       goto bail_out;
1441    }
1442
1443    if (!send_include_list(jcr)) {
1444       ua->error_msg(_("Error sending include list.\n"));
1445       goto bail_out;
1446    }
1447
1448    if (!send_exclude_list(jcr)) {
1449       ua->error_msg(_("Error sending exclude list.\n"));
1450       goto bail_out;
1451    }
1452
1453    /*
1454     * If the job is in accurate mode, we send the list of
1455     * all files to FD.
1456     */
1457    Dmsg1(40, "estimate accurate=%d\n", jcr->accurate);
1458    if (!send_accurate_current_files(jcr)) {
1459       goto bail_out;
1460    }
1461
1462    jcr->file_bsock->fsend("estimate listing=%d\n", listing);
1463    while (jcr->file_bsock->recv() >= 0) {
1464       ua->send_msg("%s", jcr->file_bsock->msg);
1465    }
1466
1467 bail_out:
1468    if (jcr->file_bsock) {
1469       jcr->file_bsock->signal(BNET_TERMINATE);
1470       free_bsock(ua->jcr->file_bsock);
1471    }
1472    return 1;
1473 }
1474
1475 /*
1476  * print time
1477  */
1478 static int time_cmd(UAContext *ua, const char *cmd)
1479 {
1480    char sdt[50];
1481    time_t ttime = time(NULL);
1482    struct tm tm;
1483    (void)localtime_r(&ttime, &tm);
1484    strftime(sdt, sizeof(sdt), "%a %d-%b-%Y %H:%M:%S", &tm);
1485    ua->send_msg("%s\n", sdt);
1486    return 1;
1487 }
1488
1489 /*
1490  * reload the conf file
1491  */
1492 extern "C" void reload_config(int sig);
1493
1494 static int reload_cmd(UAContext *ua, const char *cmd)
1495 {
1496    reload_config(1);
1497    return 1;
1498 }
1499
1500 /*
1501  * Delete Pool records (should purge Media with it).
1502  *
1503  *  delete pool=<pool-name>
1504  *  delete volume pool=<pool-name> volume=<name>
1505  *  delete jobid=xxx
1506  */
1507 static int delete_cmd(UAContext *ua, const char *cmd)
1508 {
1509    static const char *keywords[] = {
1510       NT_("volume"),
1511       NT_("pool"),
1512       NT_("jobid"),
1513       NT_("snapshot"),
1514       NULL};
1515
1516    /* Deleting large jobs can take time! */
1517    if (!open_new_client_db(ua)) {
1518       return 1;
1519    }
1520
1521    switch (find_arg_keyword(ua, keywords)) {
1522    case 0:
1523       delete_volume(ua);
1524       return 1;
1525    case 1:
1526       delete_pool(ua);
1527       return 1;
1528    case 2:
1529       int i;
1530       while ((i=find_arg(ua, "jobid")) > 0) {
1531          delete_job(ua);
1532          *ua->argk[i] = 0;         /* zap keyword already visited */
1533       }
1534       return 1;
1535    case 3:
1536       delete_snapshot(ua);
1537       return 1;
1538    default:
1539       break;
1540    }
1541
1542    ua->warning_msg(_(
1543 "In general it is not a good idea to delete either a\n"
1544 "Pool or a Volume since they may contain data.\n\n"));
1545
1546    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1547    case 0:
1548       delete_volume(ua);
1549       break;
1550    case 1:
1551       delete_pool(ua);
1552       break;
1553    case 2:
1554       delete_job(ua);
1555       return 1;
1556    case 3:
1557       delete_snapshot(ua);
1558       return 1;
1559    default:
1560       ua->warning_msg(_("Nothing done.\n"));
1561       break;
1562    }
1563    return 1;
1564 }
1565
1566 /*
1567  * delete_job has been modified to parse JobID lists like the
1568  * following:
1569  * delete JobID=3,4,6,7-11,14
1570  *
1571  * Thanks to Phil Stracchino for the above addition.
1572  */
1573 static void delete_job(UAContext *ua)
1574 {
1575    int JobId;               /* not JobId_t because it's unsigned and not compatible with sellist */
1576    char buf[256];
1577    sellist sl;
1578
1579    int i = find_arg_with_value(ua, NT_("jobid"));
1580    if (i >= 0) {
1581       if (!sl.set_string(ua->argv[i], true)) {
1582          ua->warning_msg("%s", sl.get_errmsg());
1583          return;
1584       } 
1585  
1586       if (sl.size() > 25 && (find_arg(ua, "yes") < 0)) {
1587          bsnprintf(buf, sizeof(buf),
1588                    _("Are you sure you want to delete %d JobIds ? (yes/no): "), sl.size());
1589          if (!get_yesno(ua, buf)) {
1590             return;
1591          } 
1592       }
1593
1594       foreach_sellist(JobId, &sl) {
1595          do_job_delete(ua, JobId);
1596       }
1597
1598    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1599       return;
1600
1601    } else {
1602       JobId = ua->int64_val;
1603       do_job_delete(ua, JobId);
1604    }
1605 }
1606
1607 /*
1608  * do_job_delete now performs the actual delete operation atomically
1609  */
1610 static void do_job_delete(UAContext *ua, JobId_t JobId)
1611 {
1612    char ed1[50];
1613
1614    edit_int64(JobId, ed1);
1615    purge_jobs_from_catalog(ua, ed1);
1616    ua->send_msg(_("JobId=%s and associated records deleted from the catalog.\n"), ed1);
1617 }
1618
1619 /*
1620  * Delete media records from database -- dangerous
1621  */
1622 static int delete_volume(UAContext *ua)
1623 {
1624    MEDIA_DBR mr;
1625    char buf[1000];
1626    db_list_ctx lst;
1627
1628    if (!select_media_dbr(ua, &mr)) {
1629       return 1;
1630    }
1631    ua->warning_msg(_("\nThis command will delete volume %s\n"
1632       "and all Jobs saved on that volume from the Catalog\n"),
1633       mr.VolumeName);
1634
1635    if (find_arg(ua, "yes") >= 0) {
1636       ua->pint32_val = 1; /* Have "yes" on command line already" */
1637    } else {
1638       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1639          mr.VolumeName);
1640       if (!get_yesno(ua, buf)) {
1641          return 1;
1642       }
1643    }
1644    if (!ua->pint32_val) {
1645       return 1;
1646    }
1647
1648    /* If not purged, do it */
1649    if (strcmp(mr.VolStatus, "Purged") != 0) {
1650       if (!db_get_volume_jobids(ua->jcr, ua->db, &mr, &lst)) {
1651          ua->error_msg(_("Can't list jobs on this volume\n"));
1652          return 1;
1653       }
1654       if (lst.count) {
1655          purge_jobs_from_catalog(ua, lst.list);
1656       }
1657    }
1658
1659    db_delete_media_record(ua->jcr, ua->db, &mr);
1660    return 1;
1661 }
1662
1663 /*
1664  * Delete a pool record from the database -- dangerous
1665  */
1666 static int delete_pool(UAContext *ua)
1667 {
1668    POOL_DBR  pr;
1669    char buf[200];
1670
1671    memset(&pr, 0, sizeof(pr));
1672
1673    if (!get_pool_dbr(ua, &pr)) {
1674       return 1;
1675    }
1676    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1677       pr.Name);
1678    if (!get_yesno(ua, buf)) {
1679       return 1;
1680    }
1681    if (ua->pint32_val) {
1682       db_delete_pool_record(ua->jcr, ua->db, &pr);
1683    }
1684    return 1;
1685 }
1686
1687 int memory_cmd(UAContext *ua, const char *cmd)
1688 {
1689    garbage_collect_memory();
1690    list_dir_status_header(ua);
1691    sm_dump(false, true);
1692    return 1;
1693 }
1694
1695 static void do_storage_cmd(UAContext *ua, const char *command)
1696 {
1697    USTORE store;
1698    BSOCK *sd;
1699    JCR *jcr = ua->jcr;
1700    char dev_name[MAX_NAME_LENGTH];
1701    int drive, i;
1702    int slot;
1703
1704    if (!open_client_db(ua)) {
1705       return;
1706    }
1707    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1708
1709    store.store = get_storage_resource(ua, true/*arg is storage*/);
1710    if (!store.store) {
1711       return;
1712    }
1713    pm_strcpy(store.store_source, _("unknown source"));
1714    set_wstorage(jcr, &store);
1715    drive = get_storage_drive(ua, store.store);
1716    slot = get_storage_slot(ua, store.store);
1717
1718    /* Users may set a device name directly on the command line */
1719    if ((i = find_arg_with_value(ua, "device")) > 0) {
1720       POOLMEM *errmsg = get_pool_memory(PM_NAME);
1721       if (!is_name_valid(ua->argv[i], &errmsg)) {
1722          ua->error_msg(_("Invalid device name. %s"), errmsg);
1723          free_pool_memory(errmsg);
1724          return;
1725       }
1726       free_pool_memory(errmsg);
1727       bstrncpy(dev_name, ua->argv[i], sizeof(dev_name));
1728
1729    } else {                     /* We take the default device name */
1730       bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1731    }
1732
1733    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1734       store.store->media_type, store.store->dev_name(), drive);
1735    Dmsg4(120, "Cmd: %s %s drive=%d slot=%d\n", command, dev_name, drive, slot);
1736
1737    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1738       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1739       return;
1740    }
1741    sd = jcr->store_bsock;
1742    bash_spaces(dev_name);
1743    sd->fsend("%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1744    while (sd->recv() >= 0) {
1745       ua->send_msg("%s", sd->msg);
1746    }
1747    sd->signal(BNET_TERMINATE);
1748    free_bsock(ua->jcr->store_bsock);
1749 }
1750
1751 /*
1752  * mount [storage=<name>] [drive=nn] [slot=mm]
1753  */
1754 static int mount_cmd(UAContext *ua, const char *cmd)
1755 {
1756    do_storage_cmd(ua, "mount")  ;          /* mount */
1757    return 1;
1758 }
1759
1760
1761 /*
1762  * unmount [storage=<name>] [drive=nn]
1763  */
1764 static int unmount_cmd(UAContext *ua, const char *cmd)
1765 {
1766    do_storage_cmd(ua, "unmount");          /* unmount */
1767    return 1;
1768 }
1769
1770
1771 /*
1772  * release [storage=<name>] [drive=nn]
1773  */
1774 static int release_cmd(UAContext *ua, const char *cmd)
1775 {
1776    do_storage_cmd(ua, "release");          /* release */
1777    return 1;
1778 }
1779
1780
1781 /*
1782  * Switch databases
1783  *   use catalog=<name>
1784  */
1785 static int use_cmd(UAContext *ua, const char *cmd)
1786 {
1787    CAT *oldcatalog, *catalog;
1788
1789
1790    close_db(ua);                      /* close any previously open db */
1791    oldcatalog = ua->catalog;
1792
1793    if (!(catalog = get_catalog_resource(ua))) {
1794       ua->catalog = oldcatalog;
1795    } else {
1796       ua->catalog = catalog;
1797    }
1798    if (open_db(ua)) {
1799       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1800          ua->catalog->name(), ua->catalog->db_name);
1801    }
1802    return 1;
1803 }
1804
1805 int quit_cmd(UAContext *ua, const char *cmd)
1806 {
1807    ua->quit = true;
1808    return 1;
1809 }
1810
1811 /* Handler to get job status */
1812 static int status_handler(void *ctx, int num_fields, char **row)
1813 {
1814    char *val = (char *)ctx;
1815
1816    if (row[0]) {
1817       *val = row[0][0];
1818    } else {
1819       *val = '?';               /* Unknown by default */
1820    }
1821
1822    return 0;
1823 }
1824
1825 /*
1826  * Wait until no job is running
1827  */
1828 int wait_cmd(UAContext *ua, const char *cmd)
1829 {
1830    JCR *jcr;
1831    int i;
1832    time_t stop_time = 0;
1833
1834    /*
1835     * no args
1836     * Wait until no job is running
1837     */
1838    if (ua->argc == 1) {
1839       bmicrosleep(0, 200000);            /* let job actually start */
1840       for (bool running=true; running; ) {
1841          running = false;
1842          foreach_jcr(jcr) {
1843             if (jcr->JobId != 0) {
1844                running = true;
1845                break;
1846             }
1847          }
1848          endeach_jcr(jcr);
1849
1850          if (running) {
1851             bmicrosleep(1, 0);
1852          }
1853       }
1854       return 1;
1855    }
1856
1857    i = find_arg_with_value(ua, NT_("timeout"));
1858    if (i > 0 && ua->argv[i]) {
1859       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1860    }
1861
1862    /* we have jobid, jobname or ujobid argument */
1863
1864    uint32_t jobid = 0 ;
1865
1866    if (!open_client_db(ua)) {
1867       ua->error_msg(_("ERR: Can't open db\n")) ;
1868       return 1;
1869    }
1870
1871    for (int i=1; i<ua->argc; i++) {
1872       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1873          if (!ua->argv[i]) {
1874             break;
1875          }
1876          jobid = str_to_int64(ua->argv[i]);
1877          break;
1878       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1879                  strcasecmp(ua->argk[i], "job") == 0) {
1880          if (!ua->argv[i]) {
1881             break;
1882          }
1883          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1884          if (jcr) {
1885             jobid = jcr->JobId ;
1886             free_jcr(jcr);
1887          }
1888          break;
1889       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1890          if (!ua->argv[i]) {
1891             break;
1892          }
1893          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1894          if (jcr) {
1895             jobid = jcr->JobId ;
1896             free_jcr(jcr);
1897          }
1898          break;
1899       /* Wait for a mount request */
1900       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1901          for (bool waiting=false; !waiting; ) {
1902             foreach_jcr(jcr) {
1903                if (jcr->JobId != 0 &&
1904                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1905                   waiting = true;
1906                   break;
1907                }
1908             }
1909             endeach_jcr(jcr);
1910             if (waiting) {
1911                break;
1912             }
1913             if (stop_time && (time(NULL) >= stop_time)) {
1914                ua->warning_msg(_("Wait on mount timed out\n"));
1915                return 1;
1916             }
1917             bmicrosleep(1, 0);
1918          }
1919          return 1;
1920       }
1921    }
1922
1923    if (jobid == 0) {
1924       ua->error_msg(_("ERR: Job was not found\n"));
1925       return 1 ;
1926    }
1927
1928    /*
1929     * We wait the end of a specific job
1930     */
1931
1932    bmicrosleep(0, 200000);            /* let job actually start */
1933    for (bool running=true; running; ) {
1934       running = false;
1935
1936       jcr=get_jcr_by_id(jobid) ;
1937
1938       if (jcr) {
1939          running = true ;
1940          free_jcr(jcr);
1941       }
1942
1943       if (running) {
1944          bmicrosleep(1, 0);
1945       }
1946    }
1947
1948    /*
1949     * We have to get JobStatus
1950     */
1951
1952    int status ;
1953    char jobstatus = '?';        /* Unknown by default */
1954    char buf[256] ;
1955
1956    bsnprintf(buf, sizeof(buf),
1957              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1958
1959
1960    db_sql_query(ua->db, buf, status_handler, (void *)&jobstatus);
1961
1962    switch (jobstatus) {
1963    case JS_Error:
1964       status = 1 ;         /* Warning */
1965       break;
1966
1967    case JS_Incomplete:
1968    case JS_FatalError:
1969    case JS_ErrorTerminated:
1970    case JS_Canceled:
1971       status = 2 ;         /* Critical */
1972       break;
1973
1974    case JS_Warnings:
1975    case JS_Terminated:
1976       status = 0 ;         /* Ok */
1977       break;
1978
1979    default:
1980       status = 3 ;         /* Unknown */
1981       break;
1982    }
1983
1984    ua->send_msg("JobId=%i\n", jobid) ;
1985    ua->send_msg("JobStatus=%s (%c)\n",
1986                 job_status_to_str(jobstatus, 0),
1987                 jobstatus) ;
1988
1989    if (ua->gui || ua->api) {
1990       ua->send_msg("ExitStatus=%i\n", status) ;
1991    }
1992
1993    return 1;
1994 }
1995
1996
1997 static int help_cmd(UAContext *ua, const char *cmd)
1998 {
1999    int i;
2000    ua->send_msg(_("  Command       Description\n  =======       ===========\n"));
2001    for (i=0; i<comsize; i++) {
2002       if (ua->argc == 2) {
2003          if (!strcasecmp(ua->argk[1], commands[i].key)) {
2004             ua->send_msg(_("  %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key,
2005                          commands[i].help, commands[i].usage);
2006             break;
2007          }
2008       } else {
2009          ua->send_msg(_("  %-13s %s\n"), commands[i].key, commands[i].help);
2010       }
2011    }
2012    if (i == comsize && ua->argc == 2) {
2013       ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]);
2014    }
2015    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
2016    return 1;
2017 }
2018
2019 int qhelp_cmd(UAContext *ua, const char *cmd)
2020 {
2021    int i,j;
2022    /* Want to display only commands */
2023    j = find_arg(ua, NT_("all"));
2024    if (j >= 0) {
2025       for (i=0; i<comsize; i++) {
2026          ua->send_msg("%s\n", commands[i].key);
2027       }
2028       return 1;
2029    }
2030    /* Want to display a specific help section */
2031    j = find_arg_with_value(ua, NT_("item"));
2032    if (j >= 0 && ua->argk[j]) {
2033       for (i=0; i<comsize; i++) {
2034          if (bstrcmp(commands[i].key, ua->argv[j])) {
2035             ua->send_msg("%s\n", commands[i].usage);
2036             break;
2037          }
2038       }
2039       return 1;
2040    }
2041    /* Want to display everything */
2042    for (i=0; i<comsize; i++) {
2043       ua->send_msg("%s %s -- %s\n", commands[i].key, commands[i].help, commands[i].usage);
2044    }
2045    return 1;
2046 }
2047
2048 #if 1
2049 static int version_cmd(UAContext *ua, const char *cmd)
2050 {
2051    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
2052                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
2053    return 1;
2054 }
2055 #else
2056 /*
2057  *  Test code -- turned on only for debug testing
2058  */
2059 static int version_cmd(UAContext *ua, const char *cmd)
2060 {
2061    dbid_list ids;
2062    POOL_MEM query(PM_MESSAGE);
2063    open_db(ua);
2064    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
2065    db_get_query_dbids(ua->jcr, ua->db, query, ids);
2066    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
2067    for (int i=0; i < ids.num_ids; i++) {
2068       ua->send_msg("id=%d\n", ids.DBId[i]);
2069    }
2070    close_db(ua);
2071    return 1;
2072 }
2073 #endif
2074
2075 /*
2076  * This call uses open_client_db() and force a
2077  * new dedicated connection to the catalog
2078  */
2079 bool open_new_client_db(UAContext *ua)
2080 {
2081    bool ret;
2082
2083    /* Force a new dedicated connection */
2084    ua->force_mult_db_connections = true;
2085    ret = open_client_db(ua);
2086    ua->force_mult_db_connections = false;
2087
2088    return ret;
2089 }
2090
2091 /*
2092  * This call explicitly checks for a catalog=xxx and
2093  *  if given, opens that catalog.  It also checks for
2094  *  client=xxx and if found, opens the catalog
2095  *  corresponding to that client. If we still don't
2096  *  have a catalog, look for a Job keyword and get the
2097  *  catalog from its client record.
2098  */
2099 bool open_client_db(UAContext *ua)
2100 {
2101    int i;
2102    CAT *catalog;
2103    CLIENT *client;
2104    JOB *job;
2105
2106    /* Try for catalog keyword */
2107    i = find_arg_with_value(ua, NT_("catalog"));
2108    if (i >= 0) {
2109       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
2110          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
2111          return false;
2112       }
2113       catalog = GetCatalogResWithName(ua->argv[i]);
2114       if (catalog) {
2115          if (ua->catalog && ua->catalog != catalog) {
2116             close_db(ua);
2117          }
2118          ua->catalog = catalog;
2119          return open_db(ua);
2120       }
2121    }
2122
2123    /* Try for client keyword */
2124    i = find_arg_with_value(ua, NT_("client"));
2125    if (i >= 0) {
2126       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
2127          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
2128          return false;
2129       }
2130       client = GetClientResWithName(ua->argv[i]);
2131       if (client) {
2132          catalog = client->catalog;
2133          if (ua->catalog && ua->catalog != catalog) {
2134             close_db(ua);
2135          }
2136          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2137             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2138             return false;
2139          }
2140          ua->catalog = catalog;
2141          return open_db(ua);
2142       }
2143    }
2144
2145    /* Try for Job keyword */
2146    i = find_arg_with_value(ua, NT_("job"));
2147    if (i >= 0) {
2148       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
2149          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
2150          return false;
2151       }
2152       job = GetJobResWithName(ua->argv[i]);
2153       if (job) {
2154          catalog = job->client->catalog;
2155          if (ua->catalog && ua->catalog != catalog) {
2156             close_db(ua);
2157          }
2158          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
2159             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
2160             return false;
2161          }
2162          ua->catalog = catalog;
2163          return open_db(ua);
2164       }
2165    }
2166
2167    return open_db(ua);
2168 }
2169
2170
2171 /*
2172  * Open the catalog database.
2173  */
2174 bool open_db(UAContext *ua)
2175 {
2176    bool mult_db_conn;
2177
2178    /* The force_mult_db_connections is telling us if we modify the
2179     * private or the shared link
2180     */
2181    if (ua->force_mult_db_connections) {
2182       ua->db = ua->private_db;
2183
2184    } else {
2185       ua->db = ua->shared_db;
2186    }
2187
2188    if (ua->db) {
2189       return true;
2190    }
2191
2192    if (!ua->catalog) {
2193       ua->catalog = get_catalog_resource(ua);
2194       if (!ua->catalog) {
2195          ua->error_msg( _("Could not find a Catalog resource\n"));
2196          return false;
2197       }
2198    }
2199
2200    /* Some modules like bvfs need their own catalog connection */
2201    mult_db_conn = ua->catalog->mult_db_connections;
2202    if (ua->force_mult_db_connections) {
2203       mult_db_conn = true;
2204    }
2205
2206    ua->jcr->catalog = ua->catalog;
2207
2208    Dmsg0(100, "UA Open database\n");
2209    ua->db = db_init_database(ua->jcr, ua->catalog->db_driver,
2210                              ua->catalog->db_name,
2211                              ua->catalog->db_user,
2212                              ua->catalog->db_password, ua->catalog->db_address,
2213                              ua->catalog->db_port, ua->catalog->db_socket,
2214                              ua->catalog->db_ssl_key, ua->catalog->db_ssl_cert,
2215                              ua->catalog->db_ssl_ca, ua->catalog->db_ssl_capath,
2216                              ua->catalog->db_ssl_cipher,
2217                              mult_db_conn, ua->catalog->disable_batch_insert); 
2218    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
2219       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
2220                  ua->catalog->db_name);
2221       if (ua->db) {
2222          ua->error_msg("%s", db_strerror(ua->db));
2223       }
2224       close_db(ua);
2225       return false;
2226    }
2227    ua->jcr->db = ua->db;
2228
2229    /* Depending on the type of connection, we set the right variable */
2230    if (ua->force_mult_db_connections) {
2231       ua->private_db = ua->db;
2232
2233    } else {
2234       ua->shared_db = ua->db;
2235    }
2236
2237    if (!ua->api) {
2238       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
2239    }
2240    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
2241    return true;
2242 }
2243
2244 void close_db(UAContext *ua)
2245 {
2246    if (ua->jcr) {
2247       ua->jcr->db = NULL;
2248    }
2249
2250    if (ua->shared_db) {
2251       db_close_database(ua->jcr, ua->shared_db);
2252       ua->shared_db = NULL;
2253    }
2254
2255    if (ua->private_db) {
2256       db_close_database(ua->jcr, ua->private_db);
2257       ua->private_db = NULL;
2258    }
2259
2260    ua->db = NULL;
2261 }