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