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