]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
ebl add RecyclePool to Pool. Media will take Pool.RecyclePool
[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->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 (update).
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, try to update it with 'update pool=%s'\n"),pool->name(),rpool.Name, rpool.Name,pool->name());
549          
550          ret = false;
551       }
552    } else {                     /* no RecyclePool used, set it to 0 */
553       pr->RecyclePoolId = 0;
554    }
555    return ret;
556 }
557
558 /*
559  * Create a pool record from a given Pool resource
560  *   Also called from backup.c
561  * Returns: -1  on error
562  *           0  record already exists
563  *           1  record created
564  */
565
566 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
567 {
568    POOL_DBR  pr;
569
570    memset(&pr, 0, sizeof(POOL_DBR));
571
572    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
573
574    if (db_get_pool_record(jcr, db, &pr)) {
575       /* Pool Exists */
576       if (op == POOL_OP_UPDATE) {  /* update request */
577          set_pooldbr_from_poolres(&pr, pool, op);
578          db_update_pool_record(jcr, db, &pr);
579       }
580       return 0;                       /* exists */
581    }
582
583    set_pooldbr_from_poolres(&pr, pool, op);
584    set_pooldbr_recyclepoolid(jcr, db, &pr, pool);
585
586    if (!db_create_pool_record(jcr, db, &pr)) {
587       return -1;                      /* error */
588    }
589    return 1;
590 }
591
592
593
594 /*
595  * Create a Pool Record in the database.
596  *  It is always created from the Resource record.
597  */
598 static int create_cmd(UAContext *ua, const char *cmd)
599 {
600    POOL *pool;
601
602    if (!open_client_db(ua)) {
603       return 1;
604    }
605
606    pool = get_pool_resource(ua);
607    if (!pool) {
608       return 1;
609    }
610
611    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
612    case 0:
613       bsendmsg(ua, _("Error: Pool %s already exists.\n"
614                "Use update to change it.\n"), pool->name());
615       break;
616
617    case -1:
618       bsendmsg(ua, "%s", db_strerror(ua->db));
619       break;
620
621    default:
622      break;
623    }
624    bsendmsg(ua, _("Pool %s created.\n"), pool->name());
625    return 1;
626 }
627
628
629 extern DIRRES *director;
630
631 /*
632  * Python control command
633  *  python restart (restarts interpreter)
634  */
635 static int python_cmd(UAContext *ua, const char *cmd)
636 {
637    if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
638       term_python_interpreter();
639       init_python_interpreter(director->name(), 
640          director->scripts_directory, "DirStartUp");
641       bsendmsg(ua, _("Python interpreter restarted.\n"));
642    } else {
643       bsendmsg(ua, _("Nothing done.\n"));
644    }
645    return 1;
646 }
647
648
649 /*
650  * Set a new address in a Client resource. We do this only
651  *  if the Console name is the same as the Client name
652  *  and the Console can access the client.
653  */
654 static int setip_cmd(UAContext *ua, const char *cmd)
655 {
656    CLIENT *client;
657    char buf[1024];
658    if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
659       bsendmsg(ua, _("Unauthorized command from this console.\n"));
660       return 1;
661    }
662    LockRes();
663    client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
664
665    if (!client) {
666       bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
667       goto get_out;
668    }
669    if (client->address) {
670       free(client->address);
671    }
672    /* MA Bug 6 remove ifdef */
673    sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
674    client->address = bstrdup(buf);
675    bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
676             client->name(), client->address);
677 get_out:
678    UnlockRes();
679    return 1;
680 }
681
682
683 static void do_en_disable_cmd(UAContext *ua, bool setting)
684 {
685    JOB *job;
686    int i;
687
688    i = find_arg_with_value(ua, NT_("job")); 
689    if (i < 0) { 
690       job = select_job_resource(ua);
691       if (!job) {
692          return;
693       }
694    } else {
695       LockRes();
696       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
697       UnlockRes();
698    } 
699    if (!job) {
700       bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
701       return;
702    }
703
704    if (!acl_access_ok(ua, Job_ACL, job->name())) {
705       bsendmsg(ua, _("Unauthorized command from this console.\n"));
706       return;
707    }
708    job->enabled = setting;
709    bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
710    return;
711 }
712
713 static int enable_cmd(UAContext *ua, const char *cmd)
714 {
715    do_en_disable_cmd(ua, true);
716    return 1;
717 }
718
719 static int disable_cmd(UAContext *ua, const char *cmd)
720 {
721    do_en_disable_cmd(ua, false);
722    return 1;
723 }
724
725
726 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
727 {
728    BSOCK *sd;
729    JCR *jcr = ua->jcr;
730    USTORE lstore;
731    
732    lstore.store = store;
733    pm_strcpy(lstore.store_source, _("unknown source"));
734    set_wstorage(jcr, &lstore);
735    /* Try connecting for up to 15 seconds */
736    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
737       store->name(), store->address, store->SDport);
738    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
739       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
740       return;
741    }
742    Dmsg0(120, _("Connected to storage daemon\n"));
743    sd = jcr->store_bsock;
744    bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
745    if (bnet_recv(sd) >= 0) {
746       bsendmsg(ua, "%s", sd->msg);
747    }
748    bnet_sig(sd, BNET_TERMINATE);
749    bnet_close(sd);
750    jcr->store_bsock = NULL;
751    return;
752 }
753
754 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
755 {
756    BSOCK *fd;
757
758    /* Connect to File daemon */
759
760    ua->jcr->client = client;
761    /* Try to connect for 15 seconds */
762    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
763       client->name(), client->address, client->FDport);
764    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
765       bsendmsg(ua, _("Failed to connect to Client.\n"));
766       return;
767    }
768    Dmsg0(120, "Connected to file daemon\n");
769    fd = ua->jcr->file_bsock;
770    bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
771    if (bnet_recv(fd) >= 0) {
772       bsendmsg(ua, "%s", fd->msg);
773    }
774    bnet_sig(fd, BNET_TERMINATE);
775    bnet_close(fd);
776    ua->jcr->file_bsock = NULL;
777    return;
778 }
779
780
781 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
782 {
783    STORE *store, **unique_store;
784    CLIENT *client, **unique_client;
785    int i, j, found;
786
787    /* Director */
788    debug_level = level;
789
790    /* Count Storage items */
791    LockRes();
792    store = NULL;
793    i = 0;
794    foreach_res(store, R_STORAGE) {
795       i++;
796    }
797    unique_store = (STORE **) malloc(i * sizeof(STORE));
798    /* Find Unique Storage address/port */
799    store = (STORE *)GetNextRes(R_STORAGE, NULL);
800    i = 0;
801    unique_store[i++] = store;
802    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
803       found = 0;
804       for (j=0; j<i; j++) {
805          if (strcmp(unique_store[j]->address, store->address) == 0 &&
806              unique_store[j]->SDport == store->SDport) {
807             found = 1;
808             break;
809          }
810       }
811       if (!found) {
812          unique_store[i++] = store;
813          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
814       }
815    }
816    UnlockRes();
817
818    /* Call each unique Storage daemon */
819    for (j=0; j<i; j++) {
820       do_storage_setdebug(ua, unique_store[j], level, trace_flag);
821    }
822    free(unique_store);
823
824    /* Count Client items */
825    LockRes();
826    client = NULL;
827    i = 0;
828    foreach_res(client, R_CLIENT) {
829       i++;
830    }
831    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
832    /* Find Unique Client address/port */
833    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
834    i = 0;
835    unique_client[i++] = client;
836    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
837       found = 0;
838       for (j=0; j<i; j++) {
839          if (strcmp(unique_client[j]->address, client->address) == 0 &&
840              unique_client[j]->FDport == client->FDport) {
841             found = 1;
842             break;
843          }
844       }
845       if (!found) {
846          unique_client[i++] = client;
847          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
848       }
849    }
850    UnlockRes();
851
852    /* Call each unique File daemon */
853    for (j=0; j<i; j++) {
854       do_client_setdebug(ua, unique_client[j], level, trace_flag);
855    }
856    free(unique_client);
857 }
858
859 /*
860  * setdebug level=nn all trace=1/0
861  */
862 static int setdebug_cmd(UAContext *ua, const char *cmd)
863 {
864    STORE *store;
865    CLIENT *client;
866    int level;
867    int trace_flag = -1;
868    int i;
869
870    if (!open_client_db(ua)) {
871       return 1;
872    }
873    Dmsg1(120, "setdebug:%s:\n", cmd);
874
875    level = -1;
876    i = find_arg_with_value(ua, "level");
877    if (i >= 0) {
878       level = atoi(ua->argv[i]);
879    }
880    if (level < 0) {
881       if (!get_pint(ua, _("Enter new debug level: "))) {
882          return 1;
883       }
884       level = ua->pint32_val;
885    }
886
887    /* Look for trace flag. -1 => not change */
888    i = find_arg_with_value(ua, "trace");
889    if (i >= 0) {
890       trace_flag = atoi(ua->argv[i]);
891       if (trace_flag > 0) {
892          trace_flag = 1;
893       }
894    }
895
896    /* General debug? */
897    for (i=1; i<ua->argc; i++) {
898       if (strcasecmp(ua->argk[i], "all") == 0) {
899          do_all_setdebug(ua, level, trace_flag);
900          return 1;
901       }
902       if (strcasecmp(ua->argk[i], "dir") == 0 ||
903           strcasecmp(ua->argk[i], "director") == 0) {
904          debug_level = level;
905          set_trace(trace_flag);
906          return 1;
907       }
908       if (strcasecmp(ua->argk[i], "client") == 0 ||
909           strcasecmp(ua->argk[i], "fd") == 0) {
910          client = NULL;
911          if (ua->argv[i]) {
912             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
913             if (client) {
914                do_client_setdebug(ua, client, level, trace_flag);
915                return 1;
916             }
917          }
918          client = select_client_resource(ua);
919          if (client) {
920             do_client_setdebug(ua, client, level, trace_flag);
921             return 1;
922          }
923       }
924
925       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
926           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
927           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
928          store = NULL;
929          if (ua->argv[i]) {
930             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
931             if (store) {
932                do_storage_setdebug(ua, store, level, trace_flag);
933                return 1;
934             }
935          }
936          store = get_storage_resource(ua, false/*no default*/);
937          if (store) {
938             do_storage_setdebug(ua, store, level, trace_flag);
939             return 1;
940          }
941       }
942    }
943    /*
944     * We didn't find an appropriate keyword above, so
945     * prompt the user.
946     */
947    start_prompt(ua, _("Available daemons are: \n"));
948    add_prompt(ua, _("Director"));
949    add_prompt(ua, _("Storage"));
950    add_prompt(ua, _("Client"));
951    add_prompt(ua, _("All"));
952    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
953    case 0:                         /* Director */
954       debug_level = level;
955       set_trace(trace_flag);
956       break;
957    case 1:
958       store = get_storage_resource(ua, false/*no default*/);
959       if (store) {
960          do_storage_setdebug(ua, store, level, trace_flag);
961       }
962       break;
963    case 2:
964       client = select_client_resource(ua);
965       if (client) {
966          do_client_setdebug(ua, client, level, trace_flag);
967       }
968       break;
969    case 3:
970       do_all_setdebug(ua, level, trace_flag);
971       break;
972    default:
973       break;
974    }
975    return 1;
976 }
977
978 /*
979  * Turn debug tracing to file on/off
980  */
981 static int trace_cmd(UAContext *ua, const char *cmd)
982 {
983    char *onoff;
984
985    if (ua->argc != 2) {
986       if (!get_cmd(ua, _("Turn on or off? "))) {
987             return 1;
988       }
989       onoff = ua->cmd;
990    } else {
991       onoff = ua->argk[1];
992    }
993
994    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
995    return 1;
996
997 }
998
999 static int var_cmd(UAContext *ua, const char *cmd)
1000 {
1001    POOLMEM *val = get_pool_memory(PM_FNAME);
1002    char *var;
1003
1004    if (!open_client_db(ua)) {
1005       return 1;
1006    }
1007    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1008       var++;
1009    }
1010    while (*var == ' ') {                 /* skip spaces */
1011       var++;
1012    }
1013    Dmsg1(100, "Var=%s:\n", var);
1014    variable_expansion(ua->jcr, var, &val);
1015    bsendmsg(ua, "%s\n", val);
1016    free_pool_memory(val);
1017    return 1;
1018 }
1019
1020 static int estimate_cmd(UAContext *ua, const char *cmd)
1021 {
1022    JOB *job = NULL;
1023    CLIENT *client = NULL;
1024    FILESET *fileset = NULL;
1025    int listing = 0;
1026    char since[MAXSTRING];
1027    JCR *jcr = ua->jcr;
1028
1029    jcr->JobLevel = L_FULL;
1030    for (int i=1; i<ua->argc; i++) {
1031       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1032           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1033          if (ua->argv[i]) {
1034             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1035             continue;
1036          }
1037       }
1038       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1039          if (ua->argv[i]) {
1040             job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1041             if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1042                bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1043                return 1;
1044             }
1045             continue;
1046          }
1047       }
1048       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1049          if (ua->argv[i]) {
1050             fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1051             if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1052                bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1053                return 1;
1054             }
1055             continue;
1056          }
1057       }
1058       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1059          listing = 1;
1060          continue;
1061       }
1062       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1063          if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1064             bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1065          }
1066          continue;
1067       }
1068    }
1069    if (!job && !(client && fileset)) {
1070       if (!(job = select_job_resource(ua))) {
1071          return 1;
1072       }
1073    }
1074    if (!job) {
1075       job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1076       if (!job) {
1077          bsendmsg(ua, _("No job specified.\n"));
1078          return 1;
1079       }
1080       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1081          bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1082          return 1;
1083       }
1084    }
1085    if (!client) {
1086       client = job->client;
1087    }
1088    if (!fileset) {
1089       fileset = job->fileset;
1090    }
1091    jcr->client = client;
1092    jcr->fileset = fileset;
1093    close_db(ua);
1094    ua->catalog = client->catalog;
1095
1096    if (!open_db(ua)) {
1097       return 1;
1098    }
1099
1100    jcr->job = job;
1101    jcr->JobType = JT_BACKUP;
1102    init_jcr_job_record(jcr);
1103
1104    if (!get_or_create_client_record(jcr)) {
1105       return 1;
1106    }
1107    if (!get_or_create_fileset_record(jcr)) {
1108       return 1;
1109    }
1110
1111    get_level_since_time(ua->jcr, since, sizeof(since));
1112
1113    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1114       job->client->name(), job->client->address, job->client->FDport);
1115    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1116       bsendmsg(ua, _("Failed to connect to Client.\n"));
1117       return 1;
1118    }
1119
1120    if (!send_include_list(jcr)) {
1121       bsendmsg(ua, _("Error sending include list.\n"));
1122       goto bail_out;
1123    }
1124
1125    if (!send_exclude_list(jcr)) {
1126       bsendmsg(ua, _("Error sending exclude list.\n"));
1127       goto bail_out;
1128    }
1129
1130    if (!send_level_command(jcr)) {
1131       goto bail_out;
1132    }
1133
1134    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1135    while (bnet_recv(jcr->file_bsock) >= 0) {
1136       bsendmsg(ua, "%s", jcr->file_bsock->msg);
1137    }
1138
1139 bail_out:
1140    if (jcr->file_bsock) {
1141       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1142       bnet_close(jcr->file_bsock);
1143       jcr->file_bsock = NULL;
1144    }
1145    return 1;
1146 }
1147
1148
1149 /*
1150  * print time
1151  */
1152 static int time_cmd(UAContext *ua, const char *cmd)
1153 {
1154    char sdt[50];
1155    time_t ttime = time(NULL);
1156    struct tm tm;
1157    (void)localtime_r(&ttime, &tm);
1158    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1159    bsendmsg(ua, "%s\n", sdt);
1160    return 1;
1161 }
1162
1163 /*
1164  * reload the conf file
1165  */
1166 extern "C" void reload_config(int sig);
1167
1168 static int reload_cmd(UAContext *ua, const char *cmd)
1169 {
1170    reload_config(1);
1171    return 1;
1172 }
1173
1174 /*
1175  * Delete Pool records (should purge Media with it).
1176  *
1177  *  delete pool=<pool-name>
1178  *  delete volume pool=<pool-name> volume=<name>
1179  *  delete jobid=xxx
1180  */
1181 static int delete_cmd(UAContext *ua, const char *cmd)
1182 {
1183    static const char *keywords[] = {
1184       NT_("volume"),
1185       NT_("pool"),
1186       NT_("jobid"),
1187       NULL};
1188
1189    if (!open_client_db(ua)) {
1190       return 1;
1191    }
1192
1193    switch (find_arg_keyword(ua, keywords)) {
1194    case 0:
1195       delete_volume(ua);
1196       return 1;
1197    case 1:
1198       delete_pool(ua);
1199       return 1;
1200    case 2:
1201       int i;
1202       while ((i=find_arg(ua, "jobid")) > 0) {
1203          delete_job(ua);
1204          *ua->argk[i] = 0;         /* zap keyword already visited */
1205       }
1206       return 1;
1207    default:
1208       break;
1209    }
1210
1211    bsendmsg(ua, _(
1212 "In general it is not a good idea to delete either a\n"
1213 "Pool or a Volume since they may contain data.\n\n"));
1214
1215    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1216    case 0:
1217       delete_volume(ua);
1218       break;
1219    case 1:
1220       delete_pool(ua);
1221       break;
1222    case 2:
1223       delete_job(ua);
1224       return 1;
1225    default:
1226       bsendmsg(ua, _("Nothing done.\n"));
1227       break;
1228    }
1229    return 1;
1230 }
1231
1232
1233 /*
1234  * delete_job has been modified to parse JobID lists like the
1235  * following:
1236  * delete JobID=3,4,6,7-11,14
1237  *
1238  * Thanks to Phil Stracchino for the above addition.
1239  */
1240
1241 static void delete_job(UAContext *ua)
1242 {
1243    JobId_t JobId;
1244    char *s,*sep,*tok;
1245
1246    int i = find_arg_with_value(ua, NT_("jobid"));
1247    if (i >= 0) {
1248       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1249         s = bstrdup(ua->argv[i]);
1250         tok = s;
1251         /*
1252          * We could use strtok() here.  But we're not going to, because:
1253          * (a) strtok() is deprecated, having been replaced by strsep();
1254          * (b) strtok() is broken in significant ways.
1255          * we could use strsep() instead, but it's not universally available.
1256          * so we grow our own using strchr().
1257          */
1258         sep = strchr(tok, ',');
1259         while (sep != NULL) {
1260            *sep = '\0';
1261            if (strchr(tok, '-')) {
1262                delete_job_id_range(ua, tok);
1263            } else {
1264               JobId = str_to_int64(tok);
1265               do_job_delete(ua, JobId);
1266            }
1267            tok = ++sep;
1268            sep = strchr(tok, ',');
1269         }
1270         /* pick up the last token */
1271         if (strchr(tok, '-')) {
1272             delete_job_id_range(ua, tok);
1273         } else {
1274             JobId = str_to_int64(tok);
1275             do_job_delete(ua, JobId);
1276         }
1277
1278          free(s);
1279       } else {
1280          JobId = str_to_int64(ua->argv[i]);
1281         do_job_delete(ua, JobId);
1282       }
1283    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1284       return;
1285    } else {
1286       JobId = ua->int64_val;
1287       do_job_delete(ua, JobId);
1288    }
1289 }
1290
1291 /*
1292  * we call delete_job_id_range to parse range tokens and iterate over ranges
1293  */
1294 static void delete_job_id_range(UAContext *ua, char *tok)
1295 {
1296    char *tok2;
1297    JobId_t j,j1,j2;
1298
1299    tok2 = strchr(tok, '-');
1300    *tok2 = '\0';
1301    tok2++;
1302    j1 = str_to_int64(tok);
1303    j2 = str_to_int64(tok2);
1304    for (j=j1; j<=j2; j++) {
1305       do_job_delete(ua, j);
1306    }
1307 }
1308
1309 /*
1310  * do_job_delete now performs the actual delete operation atomically
1311  */
1312
1313 static void do_job_delete(UAContext *ua, JobId_t JobId)
1314 {
1315    POOL_MEM query(PM_MESSAGE);
1316    char ed1[50];
1317
1318    purge_files_from_job(ua, JobId);
1319    purge_job_from_catalog(ua, JobId);
1320    bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1321 }
1322
1323 /*
1324  * Delete media records from database -- dangerous
1325  */
1326 static int delete_volume(UAContext *ua)
1327 {
1328    MEDIA_DBR mr;
1329
1330    if (!select_media_dbr(ua, &mr)) {
1331       return 1;
1332    }
1333    bsendmsg(ua, _("\nThis command will delete volume %s\n"
1334       "and all Jobs saved on that volume from the Catalog\n"),
1335       mr.VolumeName);
1336
1337    if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1338       return 1;
1339    }
1340    if (ua->pint32_val) {
1341       db_delete_media_record(ua->jcr, ua->db, &mr);
1342    }
1343    return 1;
1344 }
1345
1346 /*
1347  * Delete a pool record from the database -- dangerous
1348  */
1349 static int delete_pool(UAContext *ua)
1350 {
1351    POOL_DBR  pr;
1352
1353    memset(&pr, 0, sizeof(pr));
1354
1355    if (!get_pool_dbr(ua, &pr)) {
1356       return 1;
1357    }
1358    if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1359       return 1;
1360    }
1361    if (ua->pint32_val) {
1362       db_delete_pool_record(ua->jcr, ua->db, &pr);
1363    }
1364    return 1;
1365 }
1366
1367
1368 static void do_mount_cmd(UAContext *ua, const char *command)
1369 {
1370    USTORE store;
1371    BSOCK *sd;
1372    JCR *jcr = ua->jcr;
1373    char dev_name[MAX_NAME_LENGTH];
1374    int drive;
1375    int slot = -1;
1376
1377    if (!open_client_db(ua)) {
1378       return;
1379    }
1380    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1381
1382    store.store = get_storage_resource(ua, true/*arg is storage*/);
1383    pm_strcpy(store.store_source, _("unknown source"));
1384    if (!store.store) {
1385       return;
1386    }
1387    set_wstorage(jcr, &store);
1388    drive = get_storage_drive(ua, store.store);
1389    if (strcmp(command, "mount") == 0) {
1390       slot = get_storage_slot(ua, store.store);
1391    }
1392
1393    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1394       store.store->media_type, store.store->dev_name(), drive);
1395
1396    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1397       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1398       return;
1399    }
1400    sd = jcr->store_bsock;
1401    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1402    bash_spaces(dev_name);
1403    if (slot > 0) {
1404       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1405    } else {
1406       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1407    }
1408    while (bnet_recv(sd) >= 0) {
1409       bsendmsg(ua, "%s", sd->msg);
1410    }
1411    bnet_sig(sd, BNET_TERMINATE);
1412    bnet_close(sd);
1413    jcr->store_bsock = NULL;
1414 }
1415
1416 /*
1417  * mount [storage=<name>] [drive=nn] [slot=mm]
1418  */
1419 static int mount_cmd(UAContext *ua, const char *cmd)
1420 {
1421    do_mount_cmd(ua, "mount");          /* mount */
1422    return 1;
1423 }
1424
1425
1426 /*
1427  * unmount [storage=<name>] [drive=nn]
1428  */
1429 static int unmount_cmd(UAContext *ua, const char *cmd)
1430 {
1431    do_mount_cmd(ua, "unmount");          /* unmount */
1432    return 1;
1433 }
1434
1435
1436 /*
1437  * release [storage=<name>] [drive=nn]
1438  */
1439 static int release_cmd(UAContext *ua, const char *cmd)
1440 {
1441    do_mount_cmd(ua, "release");          /* release */
1442    return 1;
1443 }
1444
1445
1446 /*
1447  * Switch databases
1448  *   use catalog=<name>
1449  */
1450 static int use_cmd(UAContext *ua, const char *cmd)
1451 {
1452    CAT *oldcatalog, *catalog;
1453
1454
1455    close_db(ua);                      /* close any previously open db */
1456    oldcatalog = ua->catalog;
1457
1458    if (!(catalog = get_catalog_resource(ua))) {
1459       ua->catalog = oldcatalog;
1460    } else {
1461       ua->catalog = catalog;
1462    }
1463    if (open_db(ua)) {
1464       bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1465          ua->catalog->name(), ua->catalog->db_name);
1466    }
1467    return 1;
1468 }
1469
1470 int quit_cmd(UAContext *ua, const char *cmd)
1471 {
1472    ua->quit = TRUE;
1473    return 1;
1474 }
1475
1476 /* Handler to get job status */
1477 static int status_handler(void *ctx, int num_fields, char **row)
1478 {
1479    char *val = (char *)ctx;
1480
1481    if (row[0]) {
1482       *val = row[0][0];
1483    } else {
1484       *val = '?';               /* Unknown by default */
1485    }
1486
1487    return 0;
1488 }
1489
1490 /*
1491  * Wait until no job is running
1492  */
1493 int wait_cmd(UAContext *ua, const char *cmd)
1494 {
1495    JCR *jcr;
1496
1497    /* no args
1498     * Wait until no job is running
1499     */
1500    if (ua->argc == 1) {
1501       bmicrosleep(0, 200000);            /* let job actually start */
1502       for (bool running=true; running; ) {
1503          running = false;
1504          foreach_jcr(jcr) {
1505             if (jcr->JobId != 0) {
1506                running = true;
1507                break;
1508             }
1509          }
1510          endeach_jcr(jcr);
1511
1512          if (running) {
1513             bmicrosleep(1, 0);
1514          }
1515       }
1516       return 1;
1517    }
1518
1519    /* we have jobid, jobname or ujobid argument */
1520
1521    uint32_t jobid = 0 ;
1522
1523    if (!open_client_db(ua)) {
1524       bsendmsg(ua, _("ERR: Can't open db\n")) ;
1525       return 1;
1526    }
1527
1528    for (int i=1; i<ua->argc; i++) {
1529       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1530          if (!ua->argv[i]) {
1531             break;
1532          }
1533          jobid = str_to_int64(ua->argv[i]);
1534          break;
1535       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1536                  strcasecmp(ua->argk[i], "job") == 0) {
1537          if (!ua->argv[i]) {
1538             break;
1539          }
1540          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1541          if (jcr) {
1542             jobid = jcr->JobId ;
1543             free_jcr(jcr);
1544          }
1545          break;
1546       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1547          if (!ua->argv[i]) {
1548             break;
1549          }
1550          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1551          if (jcr) {
1552             jobid = jcr->JobId ;
1553             free_jcr(jcr);
1554          }
1555          break;
1556       }
1557    }
1558
1559    if (jobid == 0) {
1560       bsendmsg(ua, _("ERR: Job was not found\n"));
1561       return 1 ;
1562    }
1563
1564    /*
1565     * We wait the end of job
1566     */
1567
1568    bmicrosleep(0, 200000);            /* let job actually start */
1569    for (bool running=true; running; ) {
1570       running = false;
1571
1572       jcr=get_jcr_by_id(jobid) ;
1573
1574       if (jcr) {
1575          running = true ;
1576          free_jcr(jcr);
1577       }
1578
1579       if (running) {
1580          bmicrosleep(1, 0);
1581       }
1582    }
1583
1584    /*
1585     * We have to get JobStatus
1586     */
1587
1588    int status ;
1589    char jobstatus = '?';        /* Unknown by default */
1590    char buf[256] ;
1591
1592    bsnprintf(buf, sizeof(buf),
1593              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1594
1595
1596    db_sql_query(ua->db, buf,
1597                 status_handler, (void *)&jobstatus);
1598
1599    switch (jobstatus) {
1600    case JS_Error:
1601       status = 1 ;         /* Warning */
1602       break;
1603
1604    case JS_FatalError:
1605    case JS_ErrorTerminated:
1606    case JS_Canceled:
1607       status = 2 ;         /* Critical */
1608       break;
1609
1610    case JS_Terminated:
1611       status = 0 ;         /* Ok */
1612       break;
1613
1614    default:
1615       status = 3 ;         /* Unknown */
1616       break;
1617    }
1618
1619    bsendmsg(ua, "JobId=%i\n", jobid) ;
1620    bsendmsg(ua, "JobStatus=%s (%c)\n", 
1621             job_status_to_str(jobstatus), 
1622             jobstatus) ;
1623
1624    if (ua->gui) {
1625       bsendmsg(ua, "ExitStatus=%i\n", status) ;
1626    }
1627
1628    return 1;
1629 }
1630
1631
1632 static int help_cmd(UAContext *ua, const char *cmd)
1633 {
1634    unsigned int i;
1635
1636    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
1637    for (i=0; i<comsize; i++) {
1638       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1639    }
1640    bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1641    return 1;
1642 }
1643
1644 int qhelp_cmd(UAContext *ua, const char *cmd)
1645 {
1646    unsigned int i;
1647
1648    for (i=0; i<comsize; i++) {
1649       bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1650    }
1651    return 1;
1652 }
1653
1654 static int version_cmd(UAContext *ua, const char *cmd)
1655 {
1656    bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1657    return 1;
1658 }
1659
1660 /* 
1661  * This call explicitly checks for a catalog=xxx and
1662  *  if given, opens that catalog.  It also checks for
1663  *  client=xxx and if found, opens the catalog 
1664  *  corresponding to that client. If we still don't 
1665  *  have a catalog, look for a Job keyword and get the
1666  *  catalog from its client record.
1667  */
1668 bool open_client_db(UAContext *ua)
1669 {
1670    int i;
1671    CAT *catalog;
1672    CLIENT *client;
1673    JOB *job;
1674
1675    /* Try for catalog keyword */
1676    i = find_arg_with_value(ua, NT_("catalog"));
1677    if (i >= 0) {
1678       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1679          bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1680          return false;
1681       }
1682       catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1683       if (catalog) {
1684          if (ua->catalog && ua->catalog != catalog) {
1685             close_db(ua);
1686          }
1687          ua->catalog = catalog;
1688          return open_db(ua);
1689       }
1690    }
1691
1692    /* Try for client keyword */
1693    i = find_arg_with_value(ua, NT_("client"));
1694    if (i >= 0) {
1695       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1696          bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1697          return false;
1698       }
1699       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1700       if (client) {
1701          catalog = client->catalog;
1702          if (ua->catalog && ua->catalog != catalog) {
1703             close_db(ua);
1704          }
1705          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1706             bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1707             return false;
1708          }
1709          ua->catalog = catalog;
1710          return open_db(ua);
1711       }
1712    }
1713
1714    /* Try for Job keyword */
1715    i = find_arg_with_value(ua, NT_("job"));
1716    if (i >= 0) {
1717       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1718          bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1719          return false;
1720       }
1721       job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1722       if (job) {
1723          catalog = job->client->catalog;
1724          if (ua->catalog && ua->catalog != catalog) {
1725             close_db(ua);
1726          }
1727          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1728             bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1729             return false;
1730          }
1731          ua->catalog = catalog;
1732          return open_db(ua);
1733       }
1734    }
1735
1736    return open_db(ua);
1737 }
1738
1739
1740 /*
1741  * Open the catalog database.
1742  */
1743 bool open_db(UAContext *ua)
1744 {
1745    if (ua->db) {
1746       return true;
1747    }
1748    if (!ua->catalog) {
1749       ua->catalog = get_catalog_resource(ua);
1750       if (!ua->catalog) {
1751          bsendmsg(ua, _("Could not find a Catalog resource\n"));
1752          return false;
1753       }
1754    }
1755
1756    ua->jcr->catalog = ua->catalog;
1757
1758    Dmsg0(150, "Open database\n");
1759    ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1760                              ua->catalog->db_password, ua->catalog->db_address,
1761                              ua->catalog->db_port, ua->catalog->db_socket,
1762                              ua->catalog->mult_db_connections);
1763    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1764       bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1765                  ua->catalog->db_name);
1766       if (ua->db) {
1767          bsendmsg(ua, "%s", db_strerror(ua->db));
1768       }
1769       close_db(ua);
1770       return false;
1771    }
1772    ua->jcr->db = ua->db;
1773    bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name()); 
1774    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1775    return true;
1776 }
1777
1778 void close_db(UAContext *ua)
1779 {
1780    if (ua->db) {
1781       db_close_database(ua->jcr, ua->db);
1782       ua->db = NULL;
1783       if (ua->jcr) {
1784          ua->jcr->db = NULL;
1785       }
1786    }
1787 }