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