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