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