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