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