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