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