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