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