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