]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
- Correct typo in Copyright
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2  *
3  *   Bacula Director -- User Agent Commands
4  *
5  *     Kern Sibbald, September MM
6  *
7  *   Version $Id$
8  */
9 /*
10    Copyright (C) 2000-2005 Kern Sibbald
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License
14    version 2 as amended with additional clauses defined in the
15    file LICENSE in the main source directory.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
20    the file LICENSE for additional details.
21
22  */
23
24 #include "bacula.h"
25 #include "dird.h"
26
27 /* Imported subroutines */
28
29 /* Imported variables */
30 extern int r_first;
31 extern int r_last;
32 extern struct s_res resources[];
33 extern char my_name[];
34 extern jobq_t job_queue;              /* job queue */
35
36
37 /* Imported functions */
38 extern int status_cmd(UAContext *ua, const char *cmd);
39 extern int list_cmd(UAContext *ua, const char *cmd);
40 extern int llist_cmd(UAContext *ua, const char *cmd);
41 extern int show_cmd(UAContext *ua, const char *cmd);
42 extern int messagescmd(UAContext *ua, const char *cmd);
43 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
44 extern int gui_cmd(UAContext *ua, const char *cmd);
45 extern int sqlquerycmd(UAContext *ua, const char *cmd);
46 extern int querycmd(UAContext *ua, const char *cmd);
47 extern int retentioncmd(UAContext *ua, const char *cmd);
48 extern int prunecmd(UAContext *ua, const char *cmd);
49 extern int purgecmd(UAContext *ua, const char *cmd);
50 extern int restore_cmd(UAContext *ua, const char *cmd);
51 extern int label_cmd(UAContext *ua, const char *cmd);
52 extern int relabel_cmd(UAContext *ua, const char *cmd);
53 extern int update_cmd(UAContext *ua, const char *cmd);
54
55 /* Forward referenced functions */
56 static int add_cmd(UAContext *ua, const char *cmd);
57 static int create_cmd(UAContext *ua, const char *cmd);
58 static int cancel_cmd(UAContext *ua, const char *cmd);
59 static int setdebug_cmd(UAContext *ua, const char *cmd);
60 static int trace_cmd(UAContext *ua, const char *cmd);
61 static int var_cmd(UAContext *ua, const char *cmd);
62 static int estimate_cmd(UAContext *ua, const char *cmd);
63 static int help_cmd(UAContext *ua, const char *cmd);
64 static int delete_cmd(UAContext *ua, const char *cmd);
65 static int use_cmd(UAContext *ua, const char *cmd);
66 static int unmount_cmd(UAContext *ua, const char *cmd);
67 static int version_cmd(UAContext *ua, const char *cmd);
68 static int automount_cmd(UAContext *ua, const char *cmd);
69 static int time_cmd(UAContext *ua, const char *cmd);
70 static int reload_cmd(UAContext *ua, const char *cmd);
71 static int delete_volume(UAContext *ua);
72 static int delete_pool(UAContext *ua);
73 static void delete_job(UAContext *ua);
74 static int mount_cmd(UAContext *ua, const char *cmd);
75 static int release_cmd(UAContext *ua, const char *cmd);
76 static int wait_cmd(UAContext *ua, const char *cmd);
77 static int setip_cmd(UAContext *ua, const char *cmd);
78 static int python_cmd(UAContext *ua, const char *cmd);
79 static void do_job_delete(UAContext *ua, JobId_t JobId);
80 static void delete_job_id_range(UAContext *ua, char *tok);
81
82 int qhelp_cmd(UAContext *ua, const char *cmd);
83 int quit_cmd(UAContext *ua, const char *cmd);
84
85
86 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
87 static struct cmdstruct commands[] = {
88  { N_("add"),        add_cmd,         _("add media to a pool")},
89  { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
90  { N_("automount"),   automount_cmd,  _("automount [on|off] -- after label")},
91  { N_("cancel"),     cancel_cmd,    _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
92  { N_("create"),     create_cmd,    _("create DB Pool from resource")},
93  { N_("delete"),     delete_cmd,    _("delete [pool=<pool-name> | media volume=<volume-name>]")},
94  { N_("estimate"),   estimate_cmd,  _("performs FileSet estimate, listing gives full listing")},
95  { N_("exit"),       quit_cmd,      _("exit = quit")},
96  { N_("gui"),        gui_cmd,       _("gui [on|off] -- non-interactive gui mode")},
97  { N_("help"),       help_cmd,      _("print this command")},
98  { N_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
99  { N_("label"),      label_cmd,     _("label a tape")},
100  { N_("llist"),      llist_cmd,     _("full or long list like list command")},
101  { N_("messages"),   messagescmd,   _("messages")},
102  { N_("mount"),      mount_cmd,     _("mount <storage-name>")},
103  { N_("prune"),      prunecmd,      _("prune expired records from catalog")},
104  { N_("purge"),      purgecmd,      _("purge records from catalog")},
105  { N_("python"),     python_cmd,    _("python control commands")},
106  { N_("quit"),       quit_cmd,      _("quit")},
107  { N_("query"),      querycmd,      _("query catalog")},
108  { N_("restore"),    restore_cmd,   _("restore files")},
109  { N_("relabel"),    relabel_cmd,   _("relabel a tape")},
110  { N_("release"),    release_cmd,   _("release <storage-name>")},
111  { N_("reload"),     reload_cmd,    _("reload conf file")},
112  { N_("run"),        run_cmd,       _("run <job-name>")},
113  { N_("status"),     status_cmd,    _("status [storage | client]=<name>")},
114  { N_("setdebug"),   setdebug_cmd,  _("sets debug level")},
115  { N_("setip"),      setip_cmd,     _("sets new client address -- if authorized")},
116  { N_("show"),       show_cmd,      _("show (resource records) [jobs | pools | ... | all]")},
117  { N_("sqlquery"),   sqlquerycmd,   _("use SQL to query catalog")},
118  { N_("time"),       time_cmd,      _("print current time")},
119  { N_("trace"),      trace_cmd,     _("turn on/off trace to file")},
120  { N_("unmount"),    unmount_cmd,   _("unmount <storage-name>")},
121  { N_("umount"),     unmount_cmd,   _("umount <storage-name> for old-time Unix guys")},
122  { N_("update"),     update_cmd,    _("update Volume, Pool or slots")},
123  { N_("use"),        use_cmd,       _("use catalog xxx")},
124  { N_("var"),        var_cmd,       _("does variable expansion")},
125  { N_("version"),    version_cmd,   _("print Director version")},
126  { N_("wait"),       wait_cmd,      _("wait until no jobs are running")},
127              };
128 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
129
130 /*
131  * Execute a command from the UA
132  */
133 int do_a_command(UAContext *ua, const char *cmd)
134 {
135    unsigned int i;
136    int len, stat;
137    bool found = false;
138
139    stat = 1;
140
141    Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
142    if (ua->argc == 0) {
143       return 1;
144    }
145
146    len = strlen(ua->argk[0]);
147    for (i=0; i<comsize; i++) {     /* search for command */
148       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
149          if (!acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
150             break;
151          }
152          stat = (*commands[i].func)(ua, cmd);   /* go execute command */
153          found = true;
154          break;
155       }
156    }
157    if (!found) {
158       bnet_fsend(ua->UA_sock, _("%s: is an illegal command.\n"), ua->argk[0]);
159    }
160    return stat;
161 }
162
163 /*
164  * This is a common routine used to stuff the Pool DB record defaults
165  *   into the Media DB record just before creating a media (Volume)
166  *   record.
167  */
168 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
169 {
170    mr->PoolId = pr->PoolId;
171    bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus));
172    mr->Recycle = pr->Recycle;
173    mr->VolRetention = pr->VolRetention;
174    mr->VolUseDuration = pr->VolUseDuration;
175    mr->MaxVolJobs = pr->MaxVolJobs;
176    mr->MaxVolFiles = pr->MaxVolFiles;
177    mr->MaxVolBytes = pr->MaxVolBytes;
178    mr->LabelType = pr->LabelType;
179 }
180
181
182 /*
183  *  Add Volumes to an existing Pool
184  */
185 static int add_cmd(UAContext *ua, const char *cmd)
186 {
187    POOL_DBR pr;
188    MEDIA_DBR mr;
189    int num, i, max, startnum;
190    int first_id = 0;
191    char name[MAX_NAME_LENGTH];
192    STORE *store;
193    int Slot = 0, InChanger = 0;
194
195    bsendmsg(ua, _(
196 "You probably don't want to be using this command since it\n"
197 "creates database records without labeling the Volumes.\n"
198 "You probably want to use the \"label\" command.\n\n"));
199
200    if (!open_db(ua)) {
201       return 1;
202    }
203
204    memset(&pr, 0, sizeof(pr));
205    memset(&mr, 0, sizeof(mr));
206
207    if (!get_pool_dbr(ua, &pr)) {
208       return 1;
209    }
210
211    Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
212       pr.MaxVols, pr.PoolType);
213
214    while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
215       bsendmsg(ua, _("Pool already has maximum volumes = %d\n"), pr.MaxVols);
216       for (;;) {
217          if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
218             return 1;
219          }
220          pr.MaxVols = ua->pint32_val;
221       }
222    }
223
224    /* Get media type */
225    if ((store = get_storage_resource(ua, 0)) != NULL) {
226       bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
227    } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
228       return 1;
229    }
230
231    if (pr.MaxVols == 0) {
232       max = 1000;
233    } else {
234       max = pr.MaxVols - pr.NumVols;
235    }
236    for (;;) {
237       char buf[100];
238       bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
239       if (!get_pint(ua, buf)) {
240          return 1;
241       }
242       num = ua->pint32_val;
243       if (num < 0 || num > max) {
244          bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
245          continue;
246       }
247       break;
248    }
249 getVolName:
250    if (num == 0) {
251       if (!get_cmd(ua, _("Enter Volume name: "))) {
252          return 1;
253       }
254    } else {
255       if (!get_cmd(ua, _("Enter base volume name: "))) {
256          return 1;
257       }
258    }
259    /* Don't allow | in Volume name because it is the volume separator character */
260    if (!is_volume_name_legal(ua, ua->cmd)) {
261       goto getVolName;
262    }
263    if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
264       bsendmsg(ua, _("Volume name too long.\n"));
265       goto getVolName;
266    }
267    if (strlen(ua->cmd) == 0) {
268       bsendmsg(ua, _("Volume name must be at least one character long.\n"));
269       goto getVolName;
270    }
271
272    bstrncpy(name, ua->cmd, sizeof(name));
273    if (num > 0) {
274       bstrncat(name, "%04d", sizeof(name));
275
276       for (;;) {
277          if (!get_pint(ua, _("Enter the starting number: "))) {
278             return 1;
279          }
280          startnum = ua->pint32_val;
281          if (startnum < 1) {
282             bsendmsg(ua, _("Start number must be greater than zero.\n"));
283             continue;
284          }
285          break;
286       }
287    } else {
288       startnum = 1;
289       num = 1;
290    }
291
292    if (store && store->autochanger) {
293       if (!get_pint(ua, _("Enter slot (0 for none): "))) {
294          return 1;
295       }
296       Slot = ua->pint32_val;
297       if (!get_yesno(ua, _("InChanger? yes/no: "))) {
298          return 1;
299       }
300       InChanger = ua->pint32_val;
301    }
302
303    set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
304    for (i=startnum; i < num+startnum; i++) {
305       bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
306       mr.Slot = Slot++;
307       mr.InChanger = InChanger;
308       mr.StorageId = store->StorageId;
309       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
310       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
311          bsendmsg(ua, "%s", db_strerror(ua->db));
312          return 1;
313       }
314       if (i == startnum) {
315          first_id = mr.PoolId;
316       }
317    }
318    pr.NumVols += num;
319    Dmsg0(200, "Update pool record.\n");
320    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
321       bsendmsg(ua, "%s", db_strerror(ua->db));
322       return 1;
323    }
324    bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
325
326    return 1;
327 }
328
329 /*
330  * Turn auto mount on/off
331  *
332  *  automount on
333  *  automount off
334  */
335 int automount_cmd(UAContext *ua, const char *cmd)
336 {
337    char *onoff;
338
339    if (ua->argc != 2) {
340       if (!get_cmd(ua, _("Turn on or off? "))) {
341             return 1;
342       }
343       onoff = ua->cmd;
344    } else {
345       onoff = ua->argk[1];
346    }
347
348    ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
349    return 1;
350 }
351
352
353 /*
354  * Cancel a job
355  */
356 static int cancel_cmd(UAContext *ua, const char *cmd)
357 {
358    int i, ret;
359    int njobs = 0;
360    JCR *jcr = NULL;
361    char JobName[MAX_NAME_LENGTH];
362
363    if (!open_db(ua)) {
364       return 1;
365    }
366
367    for (i=1; i<ua->argc; i++) {
368       if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
369          uint32_t JobId;
370          if (!ua->argv[i]) {
371             break;
372          }
373          JobId = str_to_int64(ua->argv[i]);
374          if (!(jcr=get_jcr_by_id(JobId))) {
375             bsendmsg(ua, _("JobId %s is not running.\n"),  ua->argv[i]);
376             return 1;
377          }
378          break;
379       } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
380          if (!ua->argv[i]) {
381             break;
382          }
383          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
384             bsendmsg(ua, _("Job %s is not running.\n"), ua->argv[i]);
385             return 1;
386          }
387          break;
388       }
389    }
390    /* If we still do not have a jcr,
391     *   throw up a list and ask the user to select one.
392     */
393    if (!jcr) {
394       char buf[1000];
395       /* Count Jobs running */
396       foreach_jcr(jcr) {
397          if (jcr->JobId == 0) {      /* this is us */
398             free_jcr(jcr);
399             continue;
400          }
401          free_jcr(jcr);
402          njobs++;
403       }
404
405       if (njobs == 0) {
406          bsendmsg(ua, _("No Jobs running.\n"));
407          return 1;
408       }
409       start_prompt(ua, _("Select Job:\n"));
410       foreach_jcr(jcr) {
411          char ed1[50];
412          if (jcr->JobId == 0) {      /* this is us */
413             free_jcr(jcr);
414             continue;
415          }
416          bsnprintf(buf, sizeof(buf), "JobId=%s Job=%s", edit_int64(jcr->JobId, ed1), jcr->Job);
417          add_prompt(ua, buf);
418          free_jcr(jcr);
419       }
420
421       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
422          return 1;
423       }
424       if (njobs == 1) {
425          if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
426             return 1;
427          }
428       }
429       /* NOTE! This increments the ref_count */
430       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
431       jcr = get_jcr_by_full_name(JobName);
432       if (!jcr) {
433          bsendmsg(ua, _("Job %s not found.\n"), JobName);
434          return 1;
435       }
436    }
437
438    ret = cancel_job(ua, jcr);
439    free_jcr(jcr);
440
441    return ret;
442 }
443
444 /*
445  * This is a common routine to create or update a
446  *   Pool DB base record from a Pool Resource. We handle
447  *   the setting of MaxVols and NumVols slightly differently
448  *   depending on if we are creating the Pool or we are
449  *   simply bringing it into agreement with the resource (updage).
450  */
451 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
452 {
453    bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
454    if (op == POOL_OP_CREATE) {
455       pr->MaxVols = pool->max_volumes;
456       pr->NumVols = 0;
457    } else {          /* update pool */
458       if (pr->MaxVols != pool->max_volumes) {
459          pr->MaxVols = pool->max_volumes;
460       }
461       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
462          pr->MaxVols = pr->NumVols;
463       }
464    }
465    pr->LabelType = pool->LabelType;
466    pr->UseOnce = pool->use_volume_once;
467    pr->UseCatalog = pool->use_catalog;
468    pr->AcceptAnyVolume = pool->accept_any_volume;
469    pr->Recycle = pool->Recycle;
470    pr->VolRetention = pool->VolRetention;
471    pr->VolUseDuration = pool->VolUseDuration;
472    pr->MaxVolJobs = pool->MaxVolJobs;
473    pr->MaxVolFiles = pool->MaxVolFiles;
474    pr->MaxVolBytes = pool->MaxVolBytes;
475    pr->AutoPrune = pool->AutoPrune;
476    pr->Recycle = pool->Recycle;
477    if (pool->label_format) {
478       bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
479    } else {
480       bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat));    /* none */
481    }
482 }
483
484
485 /*
486  * Create a pool record from a given Pool resource
487  *   Also called from backup.c
488  * Returns: -1  on error
489  *           0  record already exists
490  *           1  record created
491  */
492
493 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
494 {
495    POOL_DBR  pr;
496
497    memset(&pr, 0, sizeof(POOL_DBR));
498
499    bstrncpy(pr.Name, pool->hdr.name, sizeof(pr.Name));
500
501    if (db_get_pool_record(jcr, db, &pr)) {
502       /* Pool Exists */
503       if (op == POOL_OP_UPDATE) {  /* update request */
504          set_pooldbr_from_poolres(&pr, pool, op);
505          db_update_pool_record(jcr, db, &pr);
506       }
507       return 0;                       /* exists */
508    }
509
510    set_pooldbr_from_poolres(&pr, pool, op);
511
512    if (!db_create_pool_record(jcr, db, &pr)) {
513       return -1;                      /* error */
514    }
515    return 1;
516 }
517
518
519
520 /*
521  * Create a Pool Record in the database.
522  *  It is always created from the Resource record.
523  */
524 static int create_cmd(UAContext *ua, const char *cmd)
525 {
526    POOL *pool;
527
528    if (!open_db(ua)) {
529       return 1;
530    }
531
532    pool = get_pool_resource(ua);
533    if (!pool) {
534       return 1;
535    }
536
537    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
538    case 0:
539       bsendmsg(ua, _("Error: Pool %s already exists.\n"
540                "Use update to change it.\n"), pool->hdr.name);
541       break;
542
543    case -1:
544       bsendmsg(ua, "%s", db_strerror(ua->db));
545       break;
546
547    default:
548      break;
549    }
550    bsendmsg(ua, _("Pool %s created.\n"), pool->hdr.name);
551    return 1;
552 }
553
554
555 extern DIRRES *director;
556
557 /*
558  * Python control command
559  *  python restart (restarts interpreter)
560  */
561 static int python_cmd(UAContext *ua, const char *cmd)
562 {
563    if (ua->argc >= 1 && strcasecmp(ua->argk[1], _("restart")) == 0) {
564       term_python_interpreter();
565       init_python_interpreter(director->hdr.name, 
566          director->scripts_directory, "DirStartUp");
567       bsendmsg(ua, _("Python interpreter restarted.\n"));
568    } else {
569       bsendmsg(ua, _("Nothing done.\n"));
570    }
571    return 1;
572 }
573
574
575 /*
576  * Set a new address in a Client resource. We do this only
577  *  if the Console name is the same as the Client name
578  *  and the Console can access the client.
579  */
580 static int setip_cmd(UAContext *ua, const char *cmd)
581 {
582    CLIENT *client;
583    char buf[1024];
584    if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->hdr.name)) {
585       bsendmsg(ua, _("Illegal command from this console.\n"));
586       return 1;
587    }
588    LockRes();
589    client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
590
591    if (!client) {
592       bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.name);
593       goto get_out;
594    }
595    if (client->address) {
596       free(client->address);
597    }
598    /* MA Bug 6 remove ifdef */
599    sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
600    client->address = bstrdup(buf);
601    bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
602             client->hdr.name, client->address);
603 get_out:
604    UnlockRes();
605    return 1;
606 }
607
608
609 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
610 {
611    BSOCK *sd;
612    JCR *jcr = ua->jcr;
613
614    set_storage(jcr, store);
615    /* Try connecting for up to 15 seconds */
616    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
617       store->hdr.name, store->address, store->SDport);
618    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
619       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
620       return;
621    }
622    Dmsg0(120, _("Connected to storage daemon\n"));
623    sd = jcr->store_bsock;
624    bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
625    if (bnet_recv(sd) >= 0) {
626       bsendmsg(ua, "%s", sd->msg);
627    }
628    bnet_sig(sd, BNET_TERMINATE);
629    bnet_close(sd);
630    jcr->store_bsock = NULL;
631    return;
632 }
633
634 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
635 {
636    BSOCK *fd;
637
638    /* Connect to File daemon */
639
640    ua->jcr->client = client;
641    /* Try to connect for 15 seconds */
642    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
643       client->hdr.name, client->address, client->FDport);
644    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
645       bsendmsg(ua, _("Failed to connect to Client.\n"));
646       return;
647    }
648    Dmsg0(120, "Connected to file daemon\n");
649    fd = ua->jcr->file_bsock;
650    bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
651    if (bnet_recv(fd) >= 0) {
652       bsendmsg(ua, "%s", fd->msg);
653    }
654    bnet_sig(fd, BNET_TERMINATE);
655    bnet_close(fd);
656    ua->jcr->file_bsock = NULL;
657    return;
658 }
659
660
661 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
662 {
663    STORE *store, **unique_store;
664    CLIENT *client, **unique_client;
665    int i, j, found;
666
667    /* Director */
668    debug_level = level;
669
670    /* Count Storage items */
671    LockRes();
672    store = NULL;
673    i = 0;
674    foreach_res(store, R_STORAGE) {
675       i++;
676    }
677    unique_store = (STORE **) malloc(i * sizeof(STORE));
678    /* Find Unique Storage address/port */
679    store = (STORE *)GetNextRes(R_STORAGE, NULL);
680    i = 0;
681    unique_store[i++] = store;
682    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
683       found = 0;
684       for (j=0; j<i; j++) {
685          if (strcmp(unique_store[j]->address, store->address) == 0 &&
686              unique_store[j]->SDport == store->SDport) {
687             found = 1;
688             break;
689          }
690       }
691       if (!found) {
692          unique_store[i++] = store;
693          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
694       }
695    }
696    UnlockRes();
697
698    /* Call each unique Storage daemon */
699    for (j=0; j<i; j++) {
700       do_storage_setdebug(ua, unique_store[j], level, trace_flag);
701    }
702    free(unique_store);
703
704    /* Count Client items */
705    LockRes();
706    client = NULL;
707    i = 0;
708    foreach_res(client, R_CLIENT) {
709       i++;
710    }
711    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
712    /* Find Unique Client address/port */
713    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
714    i = 0;
715    unique_client[i++] = client;
716    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
717       found = 0;
718       for (j=0; j<i; j++) {
719          if (strcmp(unique_client[j]->address, client->address) == 0 &&
720              unique_client[j]->FDport == client->FDport) {
721             found = 1;
722             break;
723          }
724       }
725       if (!found) {
726          unique_client[i++] = client;
727          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
728       }
729    }
730    UnlockRes();
731
732    /* Call each unique File daemon */
733    for (j=0; j<i; j++) {
734       do_client_setdebug(ua, unique_client[j], level, trace_flag);
735    }
736    free(unique_client);
737 }
738
739 /*
740  * setdebug level=nn all trace=1/0
741  */
742 static int setdebug_cmd(UAContext *ua, const char *cmd)
743 {
744    STORE *store;
745    CLIENT *client;
746    int level;
747    int trace_flag = -1;
748    int i;
749
750    if (!open_db(ua)) {
751       return 1;
752    }
753    Dmsg1(120, "setdebug:%s:\n", cmd);
754
755    level = -1;
756    i = find_arg_with_value(ua, _("level"));
757    if (i >= 0) {
758       level = atoi(ua->argv[i]);
759    }
760    if (level < 0) {
761       if (!get_pint(ua, _("Enter new debug level: "))) {
762          return 1;
763       }
764       level = ua->pint32_val;
765    }
766
767    /* Look for trace flag. -1 => not change */
768    i = find_arg_with_value(ua, _("trace"));
769    if (i >= 0) {
770       trace_flag = atoi(ua->argv[i]);
771       if (trace_flag > 0) {
772          trace_flag = 1;
773       }
774    }
775
776    /* General debug? */
777    for (i=1; i<ua->argc; i++) {
778       if (strcasecmp(ua->argk[i], _("all")) == 0) {
779          do_all_setdebug(ua, level, trace_flag);
780          return 1;
781       }
782       if (strcasecmp(ua->argk[i], _("dir")) == 0 ||
783           strcasecmp(ua->argk[i], _("director")) == 0) {
784          debug_level = level;
785          set_trace(trace_flag);
786          return 1;
787       }
788       if (strcasecmp(ua->argk[i], _("client")) == 0 ||
789           strcasecmp(ua->argk[i], _("fd")) == 0) {
790          client = NULL;
791          if (ua->argv[i]) {
792             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
793             if (client) {
794                do_client_setdebug(ua, client, level, trace_flag);
795                return 1;
796             }
797          }
798          client = select_client_resource(ua);
799          if (client) {
800             do_client_setdebug(ua, client, level, trace_flag);
801             return 1;
802          }
803       }
804
805       if (strcasecmp(ua->argk[i], _("store")) == 0 ||
806           strcasecmp(ua->argk[i], _("storage")) == 0 ||
807           strcasecmp(ua->argk[i], _("sd")) == 0) {
808          store = NULL;
809          if (ua->argv[i]) {
810             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
811             if (store) {
812                do_storage_setdebug(ua, store, level, trace_flag);
813                return 1;
814             }
815          }
816          store = get_storage_resource(ua, 0);
817          if (store) {
818             do_storage_setdebug(ua, store, level, trace_flag);
819             return 1;
820          }
821       }
822    }
823    /*
824     * We didn't find an appropriate keyword above, so
825     * prompt the user.
826     */
827    start_prompt(ua, _("Available daemons are: \n"));
828    add_prompt(ua, _("Director"));
829    add_prompt(ua, _("Storage"));
830    add_prompt(ua, _("Client"));
831    add_prompt(ua, _("All"));
832    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
833    case 0:                         /* Director */
834       debug_level = level;
835       set_trace(trace_flag);
836       break;
837    case 1:
838       store = get_storage_resource(ua, 0);
839       if (store) {
840          do_storage_setdebug(ua, store, level, trace_flag);
841       }
842       break;
843    case 2:
844       client = select_client_resource(ua);
845       if (client) {
846          do_client_setdebug(ua, client, level, trace_flag);
847       }
848       break;
849    case 3:
850       do_all_setdebug(ua, level, trace_flag);
851       break;
852    default:
853       break;
854    }
855    return 1;
856 }
857
858 /*
859  * Turn debug tracing to file on/off
860  */
861 static int trace_cmd(UAContext *ua, const char *cmd)
862 {
863    char *onoff;
864
865    if (ua->argc != 2) {
866       if (!get_cmd(ua, _("Turn on or off? "))) {
867             return 1;
868       }
869       onoff = ua->cmd;
870    } else {
871       onoff = ua->argk[1];
872    }
873
874    set_trace((strcasecmp(onoff, _("off")) == 0) ? false : true);
875    return 1;
876
877 }
878
879 static int var_cmd(UAContext *ua, const char *cmd)
880 {
881    POOLMEM *val = get_pool_memory(PM_FNAME);
882    char *var;
883
884    if (!open_db(ua)) {
885       return 1;
886    }
887    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
888       var++;
889    }
890    while (*var == ' ') {                 /* skip spaces */
891       var++;
892    }
893    Dmsg1(100, "Var=%s:\n", var);
894    variable_expansion(ua->jcr, var, &val);
895    bsendmsg(ua, "%s\n", val);
896    free_pool_memory(val);
897    return 1;
898 }
899
900 static int estimate_cmd(UAContext *ua, const char *cmd)
901 {
902    JOB *job = NULL;
903    CLIENT *client = NULL;
904    FILESET *fileset = NULL;
905    FILESET_DBR fsr;
906    int listing = 0;
907    char since[MAXSTRING];
908    JCR *jcr = ua->jcr;
909
910    jcr->JobLevel = L_FULL;
911    for (int i=1; i<ua->argc; i++) {
912       if (strcasecmp(ua->argk[i], _("client")) == 0 ||
913           strcasecmp(ua->argk[i], _("fd")) == 0) {
914          if (ua->argv[i]) {
915             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
916             continue;
917          }
918       }
919       if (strcasecmp(ua->argk[i], _("job")) == 0) {
920          if (ua->argv[i]) {
921             job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
922             continue;
923          }
924       }
925       if (strcasecmp(ua->argk[i], _("fileset")) == 0) {
926          if (ua->argv[i]) {
927             fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
928             continue;
929          }
930       }
931       if (strcasecmp(ua->argk[i], _("listing")) == 0) {
932          listing = 1;
933          continue;
934       }
935       if (strcasecmp(ua->argk[i], _("level")) == 0) {
936          if (!get_level_from_name(ua->jcr, ua->argv[i])) {
937             bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
938          }
939          continue;
940       }
941    }
942    if (!job && !(client && fileset)) {
943       if (!(job = select_job_resource(ua))) {
944          return 1;
945       }
946    }
947    if (!job) {
948       job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
949       if (!job) {
950          bsendmsg(ua, _("No job specified.\n"));
951          return 1;
952       }
953    }
954    if (!client) {
955       client = job->client;
956    }
957    if (!fileset) {
958       fileset = job->fileset;
959    }
960    jcr->client = client;
961    jcr->fileset = fileset;
962    close_db(ua);
963    ua->catalog = client->catalog;
964
965    if (!open_db(ua)) {
966       return 1;
967    }
968
969    jcr->job = job;
970    jcr->JobType = JT_BACKUP;
971    init_jcr_job_record(jcr);
972
973    if (!get_or_create_client_record(jcr)) {
974       return 1;
975    }
976    if (!get_or_create_fileset_record(jcr, &fsr)) {
977       return 1;
978    }
979
980    get_level_since_time(ua->jcr, since, sizeof(since));
981
982    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
983       job->client->hdr.name, job->client->address, job->client->FDport);
984    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
985       bsendmsg(ua, _("Failed to connect to Client.\n"));
986       return 1;
987    }
988
989    if (!send_include_list(jcr)) {
990       bsendmsg(ua, _("Error sending include list.\n"));
991       goto bail_out;
992    }
993
994    if (!send_exclude_list(jcr)) {
995       bsendmsg(ua, _("Error sending exclude list.\n"));
996       goto bail_out;
997    }
998
999    if (!send_level_command(jcr)) {
1000       goto bail_out;
1001    }
1002
1003    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1004    while (bnet_recv(jcr->file_bsock) >= 0) {
1005       bsendmsg(ua, "%s", jcr->file_bsock->msg);
1006    }
1007
1008 bail_out:
1009    if (jcr->file_bsock) {
1010       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1011       bnet_close(jcr->file_bsock);
1012       jcr->file_bsock = NULL;
1013    }
1014    return 1;
1015 }
1016
1017
1018 /*
1019  * print time
1020  */
1021 static int time_cmd(UAContext *ua, const char *cmd)
1022 {
1023    char sdt[50];
1024    time_t ttime = time(NULL);
1025    struct tm tm;
1026    localtime_r(&ttime, &tm);
1027    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1028    bsendmsg(ua, "%s\n", sdt);
1029    return 1;
1030 }
1031
1032 /*
1033  * reload the conf file
1034  */
1035 extern "C" void reload_config(int sig);
1036
1037 static int reload_cmd(UAContext *ua, const char *cmd)
1038 {
1039    reload_config(1);
1040    return 1;
1041 }
1042
1043 /*
1044  * Delete Pool records (should purge Media with it).
1045  *
1046  *  delete pool=<pool-name>
1047  *  delete volume pool=<pool-name> volume=<name>
1048  *  delete jobid=xxx
1049  */
1050 static int delete_cmd(UAContext *ua, const char *cmd)
1051 {
1052    static const char *keywords[] = {
1053       N_("volume"),
1054       N_("pool"),
1055       N_("jobid"),
1056       NULL};
1057
1058    if (!open_db(ua)) {
1059       return 1;
1060    }
1061
1062    switch (find_arg_keyword(ua, keywords)) {
1063    case 0:
1064       delete_volume(ua);
1065       return 1;
1066    case 1:
1067       delete_pool(ua);
1068       return 1;
1069    case 2:
1070       int i;
1071       while ((i=find_arg(ua, _("jobid"))) > 0) {
1072          delete_job(ua);
1073          *ua->argk[i] = 0;         /* zap keyword already visited */
1074       }
1075       return 1;
1076    default:
1077       break;
1078    }
1079
1080    bsendmsg(ua, _(
1081 "In general it is not a good idea to delete either a\n"
1082 "Pool or a Volume since they may contain data.\n\n"));
1083
1084    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1085    case 0:
1086       delete_volume(ua);
1087       break;
1088    case 1:
1089       delete_pool(ua);
1090       break;
1091    case 2:
1092       delete_job(ua);
1093       return 1;
1094    default:
1095       bsendmsg(ua, _("Nothing done.\n"));
1096       break;
1097    }
1098    return 1;
1099 }
1100
1101
1102 /*
1103  * delete_job has been modified to parse JobID lists like the
1104  * following:
1105  * delete JobID=3,4,6,7-11,14
1106  *
1107  * Thanks to Phil Stracchino for the above addition.
1108  */
1109
1110 static void delete_job(UAContext *ua)
1111 {
1112    JobId_t JobId;
1113    char *s,*sep,*tok;
1114
1115    int i = find_arg_with_value(ua, _("jobid"));
1116    if (i >= 0) {
1117       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1118         s = bstrdup(ua->argv[i]);
1119         tok = s;
1120         /*
1121          * We could use strtok() here.  But we're not going to, because:
1122          * (a) strtok() is deprecated, having been replaced by strsep();
1123          * (b) strtok() is broken in significant ways.
1124          * we could use strsep() instead, but it's not universally available.
1125          * so we grow our own using strchr().
1126          */
1127         sep = strchr(tok, ',');
1128         while (sep != NULL) {
1129            *sep = '\0';
1130            if (strchr(tok, '-')) {
1131                delete_job_id_range(ua, tok);
1132            } else {
1133               JobId = str_to_int64(tok);
1134               do_job_delete(ua, JobId);
1135            }
1136            tok = ++sep;
1137            sep = strchr(tok, ',');
1138         }
1139         /* pick up the last token */
1140         if (strchr(tok, '-')) {
1141             delete_job_id_range(ua, tok);
1142         } else {
1143             JobId = str_to_int64(tok);
1144             do_job_delete(ua, JobId);
1145         }
1146
1147          free(s);
1148       } else {
1149          JobId = str_to_int64(ua->argv[i]);
1150         do_job_delete(ua, JobId);
1151       }
1152    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1153       return;
1154    } else {
1155       JobId = ua->int64_val;
1156       do_job_delete(ua, JobId);
1157    }
1158 }
1159
1160 /*
1161  * we call delete_job_id_range to parse range tokens and iterate over ranges
1162  */
1163 static void delete_job_id_range(UAContext *ua, char *tok)
1164 {
1165    char *tok2;
1166    JobId_t j,j1,j2;
1167
1168    tok2 = strchr(tok, '-');
1169    *tok2 = '\0';
1170    tok2++;
1171    j1 = str_to_int64(tok);
1172    j2 = str_to_int64(tok2);
1173    for (j=j1; j<=j2; j++) {
1174       do_job_delete(ua, j);
1175    }
1176 }
1177
1178 /*
1179  * do_job_delete now performs the actual delete operation atomically
1180  * we always return 1 because C++ is pissy about void functions
1181  */
1182
1183 static void do_job_delete(UAContext *ua, JobId_t JobId)
1184 {
1185    POOLMEM *query = get_pool_memory(PM_MESSAGE);
1186    char ed1[50];
1187
1188    Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1189    db_sql_query(ua->db, query, NULL, (void *)NULL);
1190    Mmsg(query, "DELETE FROM File WHERE JobId=%s", edit_int64(JobId, ed1));
1191    db_sql_query(ua->db, query, NULL, (void *)NULL);
1192    Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", edit_int64(JobId, ed1));
1193    db_sql_query(ua->db, query, NULL, (void *)NULL);
1194    free_pool_memory(query);
1195    bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1196 }
1197
1198 /*
1199  * Delete media records from database -- dangerous
1200  */
1201 static int delete_volume(UAContext *ua)
1202 {
1203    MEDIA_DBR mr;
1204
1205    if (!select_media_dbr(ua, &mr)) {
1206       return 1;
1207    }
1208    bsendmsg(ua, _("\nThis command will delete volume %s\n"
1209       "and all Jobs saved on that volume from the Catalog\n"),
1210       mr.VolumeName);
1211
1212    if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1213       return 1;
1214    }
1215    if (ua->pint32_val) {
1216       db_delete_media_record(ua->jcr, ua->db, &mr);
1217    }
1218    return 1;
1219 }
1220
1221 /*
1222  * Delete a pool record from the database -- dangerous
1223  */
1224 static int delete_pool(UAContext *ua)
1225 {
1226    POOL_DBR  pr;
1227
1228    memset(&pr, 0, sizeof(pr));
1229
1230    if (!get_pool_dbr(ua, &pr)) {
1231       return 1;
1232    }
1233    if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1234       return 1;
1235    }
1236    if (ua->pint32_val) {
1237       db_delete_pool_record(ua->jcr, ua->db, &pr);
1238    }
1239    return 1;
1240 }
1241
1242
1243 static void do_mount_cmd(UAContext *ua, const char *command)
1244 {
1245    STORE *store;
1246    BSOCK *sd;
1247    JCR *jcr = ua->jcr;
1248    char dev_name[MAX_NAME_LENGTH];
1249
1250    if (!open_db(ua)) {
1251       return;
1252    }
1253    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1254
1255    store = get_storage_resource(ua, 1);
1256    if (!store) {
1257       return;
1258    }
1259
1260    Dmsg2(120, "Found storage, MediaType=%s DevName=%s\n",
1261       store->media_type, store->dev_name());
1262
1263    set_storage(jcr, store);
1264    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1265       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1266       return;
1267    }
1268    sd = jcr->store_bsock;
1269    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1270    bash_spaces(dev_name);
1271    bnet_fsend(sd, "%s %s", command, dev_name);
1272    while (bnet_recv(sd) >= 0) {
1273       bsendmsg(ua, "%s", sd->msg);
1274    }
1275    bnet_sig(sd, BNET_TERMINATE);
1276    bnet_close(sd);
1277    jcr->store_bsock = NULL;
1278 }
1279
1280 /*
1281  * mount [storage | device] <name>
1282  */
1283 static int mount_cmd(UAContext *ua, const char *cmd)
1284 {
1285    do_mount_cmd(ua, "mount");          /* mount */
1286    return 1;
1287 }
1288
1289
1290 /*
1291  * unmount [storage | device] <name>
1292  */
1293 static int unmount_cmd(UAContext *ua, const char *cmd)
1294 {
1295    do_mount_cmd(ua, "unmount");          /* unmount */
1296    return 1;
1297 }
1298
1299
1300 /*
1301  * release [storage | device] <name>
1302  */
1303 static int release_cmd(UAContext *ua, const char *cmd)
1304 {
1305    do_mount_cmd(ua, "release");          /* release */
1306    return 1;
1307 }
1308
1309
1310 /*
1311  * Switch databases
1312  *   use catalog=<name>
1313  */
1314 static int use_cmd(UAContext *ua, const char *cmd)
1315 {
1316    CAT *oldcatalog, *catalog;
1317
1318
1319    close_db(ua);                      /* close any previously open db */
1320    oldcatalog = ua->catalog;
1321
1322    if (!(catalog = get_catalog_resource(ua))) {
1323       ua->catalog = oldcatalog;
1324    } else {
1325       ua->catalog = catalog;
1326    }
1327    if (open_db(ua)) {
1328       bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1329          ua->catalog->hdr.name, ua->catalog->db_name);
1330    }
1331    return 1;
1332 }
1333
1334 int quit_cmd(UAContext *ua, const char *cmd)
1335 {
1336    ua->quit = TRUE;
1337    return 1;
1338 }
1339
1340 /*
1341  * Wait until no job is running
1342  */
1343 int wait_cmd(UAContext *ua, const char *cmd)
1344 {
1345    JCR *jcr;
1346    bmicrosleep(0, 200000);            /* let job actually start */
1347    for (bool running=true; running; ) {
1348       running = false;
1349       foreach_jcr(jcr) {
1350          if (jcr->JobId != 0) {
1351             running = true;
1352             free_jcr(jcr);
1353             break;
1354          }
1355          free_jcr(jcr);
1356       }
1357       if (running) {
1358          bmicrosleep(1, 0);
1359       }
1360    }
1361    return 1;
1362 }
1363
1364
1365 static int help_cmd(UAContext *ua, const char *cmd)
1366 {
1367    unsigned int i;
1368
1369    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
1370    for (i=0; i<comsize; i++) {
1371       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1372    }
1373    bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1374    return 1;
1375 }
1376
1377 int qhelp_cmd(UAContext *ua, const char *cmd)
1378 {
1379    unsigned int i;
1380
1381    for (i=0; i<comsize; i++) {
1382       bsendmsg(ua, _("%s %s\n"), _(commands[i].key), _(commands[i].help));
1383    }
1384    return 1;
1385 }
1386
1387 static int version_cmd(UAContext *ua, const char *cmd)
1388 {
1389    bsendmsg(ua, "%s Version: " VERSION " (" BDATE ")\n", my_name);
1390    return 1;
1391 }
1392
1393
1394 /* A bit brain damaged in that if the user has not done
1395  * a "use catalog xxx" command, we simply find the first
1396  * catalog resource and open it.
1397  */
1398 int open_db(UAContext *ua)
1399 {
1400    if (ua->db) {
1401       return 1;
1402    }
1403    if (!ua->catalog) {
1404       LockRes();
1405       ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1406       UnlockRes();
1407       if (!ua->catalog) {
1408          bsendmsg(ua, _("Could not find a Catalog resource\n"));
1409          return 0;
1410       } else {
1411          bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1412             ua->catalog->hdr.name, ua->catalog->db_name);
1413       }
1414    }
1415
1416    ua->jcr->catalog = ua->catalog;
1417
1418    Dmsg0(150, "Open database\n");
1419    ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1420                              ua->catalog->db_password, ua->catalog->db_address,
1421                              ua->catalog->db_port, ua->catalog->db_socket,
1422                              ua->catalog->mult_db_connections);
1423    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1424       bsendmsg(ua, _("Could not open database \"%s\".\n"),
1425                  ua->catalog->db_name);
1426       if (ua->db) {
1427          bsendmsg(ua, "%s", db_strerror(ua->db));
1428       }
1429       close_db(ua);
1430       return 0;
1431    }
1432    ua->jcr->db = ua->db;
1433    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1434    return 1;
1435 }
1436
1437 void close_db(UAContext *ua)
1438 {
1439    if (ua->db) {
1440       db_close_database(ua->jcr, ua->db);
1441       ua->db = NULL;
1442       if (ua->jcr) {
1443          ua->jcr->db = NULL;
1444       }
1445    }
1446 }