]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
kes Add Martin's fixes to argument scanning for the estimate
[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             if (!client) {
1086                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1087                return 1;
1088             }
1089             continue;
1090          }
1091       }
1092       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1093          if (ua->argv[i]) {
1094             job = GetJobResWithName(ua->argv[i]);
1095             if (!job) {
1096                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1097                return 1;
1098             }
1099             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1100                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1101                return 1;
1102             }
1103             continue;
1104          }
1105       }
1106       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1107          if (ua->argv[i]) {
1108             fileset = GetFileSetResWithName(ua->argv[i]);
1109             if (!fileset) {
1110                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1111                return 1;
1112             }
1113             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1114                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1115                return 1;
1116             }
1117             continue;
1118          }
1119       }
1120       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1121          listing = 1;
1122          continue;
1123       }
1124       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1125          if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1126             ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1127          }
1128          continue;
1129       }
1130    }
1131    if (!job && !(client && fileset)) {
1132       if (!(job = select_job_resource(ua))) {
1133          return 1;
1134       }
1135    }
1136    if (!job) {
1137       job = GetJobResWithName(ua->argk[1]);
1138       if (!job) {
1139          ua->error_msg(_("No job specified.\n"));
1140          return 1;
1141       }
1142       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1143          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1144          return 1;
1145       }
1146    }
1147    if (!client) {
1148       client = job->client;
1149    }
1150    if (!fileset) {
1151       fileset = job->fileset;
1152    }
1153    jcr->client = client;
1154    jcr->fileset = fileset;
1155    close_db(ua);
1156    if (job->pool->catalog) {
1157       ua->catalog = job->pool->catalog;
1158    } else {
1159       ua->catalog = client->catalog;
1160    }
1161
1162    if (!open_db(ua)) {
1163       return 1;
1164    }
1165
1166    jcr->job = job;
1167    jcr->JobType = JT_BACKUP;
1168    init_jcr_job_record(jcr);
1169
1170    if (!get_or_create_client_record(jcr)) {
1171       return 1;
1172    }
1173    if (!get_or_create_fileset_record(jcr)) {
1174       return 1;
1175    }
1176
1177    get_level_since_time(ua->jcr, since, sizeof(since));
1178
1179    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1180       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1181    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1182       ua->error_msg(_("Failed to connect to Client.\n"));
1183       return 1;
1184    }
1185
1186    if (!send_include_list(jcr)) {
1187       ua->error_msg(_("Error sending include list.\n"));
1188       goto bail_out;
1189    }
1190
1191    if (!send_exclude_list(jcr)) {
1192       ua->error_msg(_("Error sending exclude list.\n"));
1193       goto bail_out;
1194    }
1195
1196    if (!send_level_command(jcr)) {
1197       goto bail_out;
1198    }
1199
1200    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1201    while (bnet_recv(jcr->file_bsock) >= 0) {
1202       ua->send_msg("%s", jcr->file_bsock->msg);
1203    }
1204
1205 bail_out:
1206    if (jcr->file_bsock) {
1207       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1208       bnet_close(jcr->file_bsock);
1209       jcr->file_bsock = NULL;
1210    }
1211    return 1;
1212 }
1213
1214
1215 /*
1216  * print time
1217  */
1218 static int time_cmd(UAContext *ua, const char *cmd)
1219 {
1220    char sdt[50];
1221    time_t ttime = time(NULL);
1222    struct tm tm;
1223    (void)localtime_r(&ttime, &tm);
1224    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1225    ua->send_msg("%s\n", sdt);
1226    return 1;
1227 }
1228
1229 /*
1230  * reload the conf file
1231  */
1232 extern "C" void reload_config(int sig);
1233
1234 static int reload_cmd(UAContext *ua, const char *cmd)
1235 {
1236    reload_config(1);
1237    return 1;
1238 }
1239
1240 /*
1241  * Delete Pool records (should purge Media with it).
1242  *
1243  *  delete pool=<pool-name>
1244  *  delete volume pool=<pool-name> volume=<name>
1245  *  delete jobid=xxx
1246  */
1247 static int delete_cmd(UAContext *ua, const char *cmd)
1248 {
1249    static const char *keywords[] = {
1250       NT_("volume"),
1251       NT_("pool"),
1252       NT_("jobid"),
1253       NULL};
1254
1255    if (!open_client_db(ua)) {
1256       return 1;
1257    }
1258
1259    switch (find_arg_keyword(ua, keywords)) {
1260    case 0:
1261       delete_volume(ua);
1262       return 1;
1263    case 1:
1264       delete_pool(ua);
1265       return 1;
1266    case 2:
1267       int i;
1268       while ((i=find_arg(ua, "jobid")) > 0) {
1269          delete_job(ua);
1270          *ua->argk[i] = 0;         /* zap keyword already visited */
1271       }
1272       return 1;
1273    default:
1274       break;
1275    }
1276
1277    ua->warning_msg(_(
1278 "In general it is not a good idea to delete either a\n"
1279 "Pool or a Volume since they may contain data.\n\n"));
1280
1281    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1282    case 0:
1283       delete_volume(ua);
1284       break;
1285    case 1:
1286       delete_pool(ua);
1287       break;
1288    case 2:
1289       delete_job(ua);
1290       return 1;
1291    default:
1292       ua->warning_msg(_("Nothing done.\n"));
1293       break;
1294    }
1295    return 1;
1296 }
1297
1298
1299 /*
1300  * delete_job has been modified to parse JobID lists like the
1301  * following:
1302  * delete JobID=3,4,6,7-11,14
1303  *
1304  * Thanks to Phil Stracchino for the above addition.
1305  */
1306
1307 static void delete_job(UAContext *ua)
1308 {
1309    JobId_t JobId;
1310    char *s,*sep,*tok;
1311
1312    int i = find_arg_with_value(ua, NT_("jobid"));
1313    if (i >= 0) {
1314       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1315         s = bstrdup(ua->argv[i]);
1316         tok = s;
1317         /*
1318          * We could use strtok() here.  But we're not going to, because:
1319          * (a) strtok() is deprecated, having been replaced by strsep();
1320          * (b) strtok() is broken in significant ways.
1321          * we could use strsep() instead, but it's not universally available.
1322          * so we grow our own using strchr().
1323          */
1324         sep = strchr(tok, ',');
1325         while (sep != NULL) {
1326            *sep = '\0';
1327            if (strchr(tok, '-')) {
1328                delete_job_id_range(ua, tok);
1329            } else {
1330               JobId = str_to_int64(tok);
1331               do_job_delete(ua, JobId);
1332            }
1333            tok = ++sep;
1334            sep = strchr(tok, ',');
1335         }
1336         /* pick up the last token */
1337         if (strchr(tok, '-')) {
1338             delete_job_id_range(ua, tok);
1339         } else {
1340             JobId = str_to_int64(tok);
1341             do_job_delete(ua, JobId);
1342         }
1343
1344          free(s);
1345       } else {
1346          JobId = str_to_int64(ua->argv[i]);
1347         do_job_delete(ua, JobId);
1348       }
1349    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1350       return;
1351    } else {
1352       JobId = ua->int64_val;
1353       do_job_delete(ua, JobId);
1354    }
1355 }
1356
1357 /*
1358  * we call delete_job_id_range to parse range tokens and iterate over ranges
1359  */
1360 static void delete_job_id_range(UAContext *ua, char *tok)
1361 {
1362    char *tok2;
1363    JobId_t j,j1,j2;
1364
1365    tok2 = strchr(tok, '-');
1366    *tok2 = '\0';
1367    tok2++;
1368    j1 = str_to_int64(tok);
1369    j2 = str_to_int64(tok2);
1370    for (j=j1; j<=j2; j++) {
1371       do_job_delete(ua, j);
1372    }
1373 }
1374
1375 /*
1376  * do_job_delete now performs the actual delete operation atomically
1377  */
1378 static void do_job_delete(UAContext *ua, JobId_t JobId)
1379 {
1380    char ed1[50];
1381
1382    edit_int64(JobId, ed1);
1383    purge_jobs_from_catalog(ua, ed1);
1384    ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1385 }
1386
1387 /*
1388  * Delete media records from database -- dangerous
1389  */
1390 static int delete_volume(UAContext *ua)
1391 {
1392    MEDIA_DBR mr;
1393    char buf[1000];
1394
1395    if (!select_media_dbr(ua, &mr)) {
1396       return 1;
1397    }
1398    ua->warning_msg(_("\nThis command will delete volume %s\n"
1399       "and all Jobs saved on that volume from the Catalog\n"),
1400       mr.VolumeName);
1401
1402    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1403       mr.VolumeName);
1404    if (!get_yesno(ua, buf)) {
1405       return 1;
1406    }
1407    if (ua->pint32_val) {
1408       db_delete_media_record(ua->jcr, ua->db, &mr);
1409    }
1410    return 1;
1411 }
1412
1413 /*
1414  * Delete a pool record from the database -- dangerous
1415  */
1416 static int delete_pool(UAContext *ua)
1417 {
1418    POOL_DBR  pr;
1419    char buf[200];
1420
1421    memset(&pr, 0, sizeof(pr));
1422
1423    if (!get_pool_dbr(ua, &pr)) {
1424       return 1;
1425    }
1426    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1427       pr.Name);
1428    if (!get_yesno(ua, buf)) {
1429       return 1;
1430    }
1431    if (ua->pint32_val) {
1432       db_delete_pool_record(ua->jcr, ua->db, &pr);
1433    }
1434    return 1;
1435 }
1436
1437 int memory_cmd(UAContext *ua, const char *cmd)
1438 {
1439    list_dir_status_header(ua);
1440    sm_dump(false, true);
1441    return 1;
1442 }
1443
1444 static void do_mount_cmd(UAContext *ua, const char *command)
1445 {
1446    USTORE store;
1447    BSOCK *sd;
1448    JCR *jcr = ua->jcr;
1449    char dev_name[MAX_NAME_LENGTH];
1450    int drive;
1451    int slot = -1;
1452
1453    if (!open_client_db(ua)) {
1454       return;
1455    }
1456    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1457
1458    store.store = get_storage_resource(ua, true/*arg is storage*/);
1459    if (!store.store) {
1460       return;
1461    }
1462    pm_strcpy(store.store_source, _("unknown source"));
1463    set_wstorage(jcr, &store);
1464    drive = get_storage_drive(ua, store.store);
1465    if (strcmp(command, "mount") == 0) {
1466       slot = get_storage_slot(ua, store.store);
1467    }
1468
1469    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1470       store.store->media_type, store.store->dev_name(), drive);
1471
1472    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1473       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1474       return;
1475    }
1476    sd = jcr->store_bsock;
1477    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1478    bash_spaces(dev_name);
1479    if (slot > 0) {
1480       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1481    } else {
1482       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1483    }
1484    while (bnet_recv(sd) >= 0) {
1485       ua->send_msg("%s", sd->msg);
1486    }
1487    bnet_sig(sd, BNET_TERMINATE);
1488    bnet_close(sd);
1489    jcr->store_bsock = NULL;
1490 }
1491
1492 /*
1493  * mount [storage=<name>] [drive=nn] [slot=mm]
1494  */
1495 static int mount_cmd(UAContext *ua, const char *cmd)
1496 {
1497    do_mount_cmd(ua, "mount");          /* mount */
1498    return 1;
1499 }
1500
1501
1502 /*
1503  * unmount [storage=<name>] [drive=nn]
1504  */
1505 static int unmount_cmd(UAContext *ua, const char *cmd)
1506 {
1507    do_mount_cmd(ua, "unmount");          /* unmount */
1508    return 1;
1509 }
1510
1511
1512 /*
1513  * release [storage=<name>] [drive=nn]
1514  */
1515 static int release_cmd(UAContext *ua, const char *cmd)
1516 {
1517    do_mount_cmd(ua, "release");          /* release */
1518    return 1;
1519 }
1520
1521
1522 /*
1523  * Switch databases
1524  *   use catalog=<name>
1525  */
1526 static int use_cmd(UAContext *ua, const char *cmd)
1527 {
1528    CAT *oldcatalog, *catalog;
1529
1530
1531    close_db(ua);                      /* close any previously open db */
1532    oldcatalog = ua->catalog;
1533
1534    if (!(catalog = get_catalog_resource(ua))) {
1535       ua->catalog = oldcatalog;
1536    } else {
1537       ua->catalog = catalog;
1538    }
1539    if (open_db(ua)) {
1540       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1541          ua->catalog->name(), ua->catalog->db_name);
1542    }
1543    return 1;
1544 }
1545
1546 int quit_cmd(UAContext *ua, const char *cmd)
1547 {
1548    ua->quit = true;
1549    return 1;
1550 }
1551
1552 /* Handler to get job status */
1553 static int status_handler(void *ctx, int num_fields, char **row)
1554 {
1555    char *val = (char *)ctx;
1556
1557    if (row[0]) {
1558       *val = row[0][0];
1559    } else {
1560       *val = '?';               /* Unknown by default */
1561    }
1562
1563    return 0;
1564 }
1565
1566 /*
1567  * Wait until no job is running
1568  */
1569 int wait_cmd(UAContext *ua, const char *cmd)
1570 {
1571    JCR *jcr;
1572
1573    /*
1574     * no args
1575     * Wait until no job is running
1576     */
1577    if (ua->argc == 1) {
1578       bmicrosleep(0, 200000);            /* let job actually start */
1579       for (bool running=true; running; ) {
1580          running = false;
1581          foreach_jcr(jcr) {
1582             if (jcr->JobId != 0) {
1583                running = true;
1584                break;
1585             }
1586          }
1587          endeach_jcr(jcr);
1588
1589          if (running) {
1590             bmicrosleep(1, 0);
1591          }
1592       }
1593       return 1;
1594    }
1595
1596    /* we have jobid, jobname or ujobid argument */
1597
1598    uint32_t jobid = 0 ;
1599
1600    if (!open_client_db(ua)) {
1601       ua->error_msg(_("ERR: Can't open db\n")) ;
1602       return 1;
1603    }
1604
1605    for (int i=1; i<ua->argc; i++) {
1606       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1607          if (!ua->argv[i]) {
1608             break;
1609          }
1610          jobid = str_to_int64(ua->argv[i]);
1611          break;
1612       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1613                  strcasecmp(ua->argk[i], "job") == 0) {
1614          if (!ua->argv[i]) {
1615             break;
1616          }
1617          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1618          if (jcr) {
1619             jobid = jcr->JobId ;
1620             free_jcr(jcr);
1621          }
1622          break;
1623       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1624          if (!ua->argv[i]) {
1625             break;
1626          }
1627          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1628          if (jcr) {
1629             jobid = jcr->JobId ;
1630             free_jcr(jcr);
1631          }
1632          break;
1633       }
1634    }
1635
1636    if (jobid == 0) {
1637       ua->error_msg(_("ERR: Job was not found\n"));
1638       return 1 ;
1639    }
1640
1641    /*
1642     * We wait the end of a specific job
1643     */
1644
1645    bmicrosleep(0, 200000);            /* let job actually start */
1646    for (bool running=true; running; ) {
1647       running = false;
1648
1649       jcr=get_jcr_by_id(jobid) ;
1650
1651       if (jcr) {
1652          running = true ;
1653          free_jcr(jcr);
1654       }
1655
1656       if (running) {
1657          bmicrosleep(1, 0);
1658       }
1659    }
1660
1661    /*
1662     * We have to get JobStatus
1663     */
1664
1665    int status ;
1666    char jobstatus = '?';        /* Unknown by default */
1667    char buf[256] ;
1668
1669    bsnprintf(buf, sizeof(buf),
1670              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1671
1672
1673    db_sql_query(ua->db, buf,
1674                 status_handler, (void *)&jobstatus);
1675
1676    switch (jobstatus) {
1677    case JS_Error:
1678       status = 1 ;         /* Warning */
1679       break;
1680
1681    case JS_FatalError:
1682    case JS_ErrorTerminated:
1683    case JS_Canceled:
1684       status = 2 ;         /* Critical */
1685       break;
1686
1687    case JS_Terminated:
1688       status = 0 ;         /* Ok */
1689       break;
1690
1691    default:
1692       status = 3 ;         /* Unknown */
1693       break;
1694    }
1695
1696    ua->send_msg("JobId=%i\n", jobid) ;
1697    ua->send_msg("JobStatus=%s (%c)\n", 
1698             job_status_to_str(jobstatus), 
1699             jobstatus) ;
1700
1701    if (ua->gui || ua->api) {
1702       ua->send_msg("ExitStatus=%i\n", status) ;
1703    }
1704
1705    return 1;
1706 }
1707
1708
1709 static int help_cmd(UAContext *ua, const char *cmd)
1710 {
1711    unsigned int i;
1712
1713    ua->send_msg(_("  Command    Description\n  =======    ===========\n"));
1714    for (i=0; i<comsize; i++) {
1715       ua->send_msg(_("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1716    }
1717    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1718    return 1;
1719 }
1720
1721 int qhelp_cmd(UAContext *ua, const char *cmd)
1722 {
1723    unsigned int i;
1724
1725    for (i=0; i<comsize; i++) {
1726       ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1727    }
1728    return 1;
1729 }
1730
1731 #if 1 
1732 static int version_cmd(UAContext *ua, const char *cmd)
1733 {
1734    ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1735             HOST_OS, DISTNAME, DISTVER);
1736    return 1;
1737 }
1738 #else
1739 /*
1740  *  Test code -- turned on only for debug testing 
1741  */
1742 static int version_cmd(UAContext *ua, const char *cmd)
1743 {
1744    dbid_list ids;
1745    POOL_MEM query(PM_MESSAGE);
1746    open_db(ua);
1747    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1748    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1749    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1750    for (int i=0; i < ids.num_ids; i++) {
1751       ua->send_msg("id=%d\n", ids.DBId[i]);
1752    }
1753    close_db(ua);
1754    return 1;
1755 }
1756 #endif
1757
1758 /* 
1759  * This call explicitly checks for a catalog=xxx and
1760  *  if given, opens that catalog.  It also checks for
1761  *  client=xxx and if found, opens the catalog 
1762  *  corresponding to that client. If we still don't 
1763  *  have a catalog, look for a Job keyword and get the
1764  *  catalog from its client record.
1765  */
1766 bool open_client_db(UAContext *ua)
1767 {
1768    int i;
1769    CAT *catalog;
1770    CLIENT *client;
1771    JOB *job;
1772
1773    /* Try for catalog keyword */
1774    i = find_arg_with_value(ua, NT_("catalog"));
1775    if (i >= 0) {
1776       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1777          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1778          return false;
1779       }
1780       catalog = GetCatalogResWithName(ua->argv[i]);
1781       if (catalog) {
1782          if (ua->catalog && ua->catalog != catalog) {
1783             close_db(ua);
1784          }
1785          ua->catalog = catalog;
1786          return open_db(ua);
1787       }
1788    }
1789
1790    /* Try for client keyword */
1791    i = find_arg_with_value(ua, NT_("client"));
1792    if (i >= 0) {
1793       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1794          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1795          return false;
1796       }
1797       client = GetClientResWithName(ua->argv[i]);
1798       if (client) {
1799          catalog = client->catalog;
1800          if (ua->catalog && ua->catalog != catalog) {
1801             close_db(ua);
1802          }
1803          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1804             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1805             return false;
1806          }
1807          ua->catalog = catalog;
1808          return open_db(ua);
1809       }
1810    }
1811
1812    /* Try for Job keyword */
1813    i = find_arg_with_value(ua, NT_("job"));
1814    if (i >= 0) {
1815       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1816          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1817          return false;
1818       }
1819       job = GetJobResWithName(ua->argv[i]);
1820       if (job) {
1821          catalog = job->client->catalog;
1822          if (ua->catalog && ua->catalog != catalog) {
1823             close_db(ua);
1824          }
1825          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1826             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1827             return false;
1828          }
1829          ua->catalog = catalog;
1830          return open_db(ua);
1831       }
1832    }
1833
1834    return open_db(ua);
1835 }
1836
1837
1838 /*
1839  * Open the catalog database.
1840  */
1841 bool open_db(UAContext *ua)
1842 {
1843    if (ua->db) {
1844       return true;
1845    }
1846    if (!ua->catalog) {
1847       ua->catalog = get_catalog_resource(ua);
1848       if (!ua->catalog) {
1849          ua->error_msg( _("Could not find a Catalog resource\n"));
1850          return false;
1851       }
1852    }
1853
1854    ua->jcr->catalog = ua->catalog;
1855
1856    Dmsg0(100, "UA Open database\n");
1857    ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
1858                              ua->catalog->db_user,
1859                              ua->catalog->db_password, ua->catalog->db_address,
1860                              ua->catalog->db_port, ua->catalog->db_socket,
1861                              ua->catalog->mult_db_connections);
1862    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1863       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1864                  ua->catalog->db_name);
1865       if (ua->db) {
1866          ua->error_msg("%s", db_strerror(ua->db));
1867       }
1868       close_db(ua);
1869       return false;
1870    }
1871    ua->jcr->db = ua->db;
1872    if (!ua->api) {
1873       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
1874    }
1875    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1876    return true;
1877 }
1878
1879 void close_db(UAContext *ua)
1880 {
1881    if (ua->db) {
1882       db_close_database(ua->jcr, ua->db);
1883       ua->db = NULL;
1884       if (ua->jcr) {
1885          ua->jcr->db = NULL;
1886       }
1887    }
1888 }