]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
ebl Tweak version string to display versionid field at the end
[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>] -- 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          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
197          found = true;
198          break;
199       }
200    }
201    if (!found) {
202       ua->error_msg(_("%s: is an invalid command.\n"), ua->argk[0]);
203       ok = false;
204    }
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 and Pool.ScratchPoolId in Catalog */
569 int update_pool_references(JCR *jcr, B_DB *db, POOL *pool)
570 {
571    POOL_DBR  pr;
572
573    if (!pool->RecyclePool && !pool->ScratchPool) {
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_references(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 and POOL_DBR.ScratchPoolId from Pool resource 
597  * works with set_pooldbr_from_poolres
598  */
599 bool set_pooldbr_references(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
622    if (pool->ScratchPool) {
623       memset(&rpool, 0, sizeof(POOL_DBR));
624
625       bstrncpy(rpool.Name, pool->ScratchPool->name(), sizeof(rpool.Name));
626       if (db_get_pool_record(jcr, db, &rpool)) {
627         pr->ScratchPoolId = rpool.PoolId;
628       } else {
629         Jmsg(jcr, M_WARNING, 0,
630         _("Can't set %s ScratchPool to %s, %s is not in database.\n" \
631           "Try to update it with 'update pool=%s'\n"),
632         pool->name(), rpool.Name, rpool.Name,pool->name());
633         ret = false;
634       }
635    } else {                    /* no ScratchPool used, set it to 0 */
636       pr->ScratchPoolId = 0;
637    }
638  
639    return ret;
640 }
641
642
643 /*
644  * Create a pool record from a given Pool resource
645  *   Also called from backup.c
646  * Returns: -1  on error
647  *           0  record already exists
648  *           1  record created
649  */
650
651 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
652 {
653    POOL_DBR  pr;
654
655    memset(&pr, 0, sizeof(POOL_DBR));
656
657    bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
658
659    if (db_get_pool_record(jcr, db, &pr)) {
660       /* Pool Exists */
661       if (op == POOL_OP_UPDATE) {  /* update request */
662          set_pooldbr_from_poolres(&pr, pool, op);
663          set_pooldbr_references(jcr, db, &pr, pool);
664          db_update_pool_record(jcr, db, &pr);
665       }
666       return 0;                       /* exists */
667    }
668
669    set_pooldbr_from_poolres(&pr, pool, op);
670    set_pooldbr_references(jcr, db, &pr, pool);
671
672    if (!db_create_pool_record(jcr, db, &pr)) {
673       return -1;                      /* error */
674    }
675    return 1;
676 }
677
678
679
680 /*
681  * Create a Pool Record in the database.
682  *  It is always created from the Resource record.
683  */
684 static int create_cmd(UAContext *ua, const char *cmd)
685 {
686    POOL *pool;
687
688    if (!open_client_db(ua)) {
689       return 1;
690    }
691
692    pool = get_pool_resource(ua);
693    if (!pool) {
694       return 1;
695    }
696
697    switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
698    case 0:
699       ua->error_msg(_("Error: Pool %s already exists.\n"
700                "Use update to change it.\n"), pool->name());
701       break;
702
703    case -1:
704       ua->error_msg("%s", db_strerror(ua->db));
705       break;
706
707    default:
708      break;
709    }
710    ua->send_msg(_("Pool %s created.\n"), pool->name());
711    return 1;
712 }
713
714
715 extern DIRRES *director;
716 extern char *configfile;
717
718 /*
719  * Python control command
720  *  python restart (restarts interpreter)
721  */
722 static int python_cmd(UAContext *ua, const char *cmd)
723 {
724 #ifdef HAVE_PYTHON
725    init_python_interpreter_args python_args;
726
727    if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
728       term_python_interpreter();
729
730       python_args.progname = director->name();
731       python_args.scriptdir = director->scripts_directory;
732       python_args.modulename = "DirStartUp";
733       python_args.configfile = configfile;
734       python_args.workingdir = director->working_directory;
735       python_args.job_getattr = job_getattr;
736       python_args.job_setattr = job_setattr;
737
738       init_python_interpreter(&python_args);
739
740       ua->send_msg(_("Python interpreter restarted.\n"));
741    } else {
742 #endif /* HAVE_PYTHON */
743       ua->warning_msg(_("Nothing done.\n"));
744 #ifdef HAVE_PYTHON
745    }
746 #endif /* HAVE_PYTHON */
747    return 1;
748 }
749
750
751 /*
752  * Set a new address in a Client resource. We do this only
753  *  if the Console name is the same as the Client name
754  *  and the Console can access the client.
755  */
756 static int setip_cmd(UAContext *ua, const char *cmd)
757 {
758    CLIENT *client;
759    char buf[1024];
760    if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
761       ua->error_msg(_("Unauthorized command from this console.\n"));
762       return 1;
763    }
764    LockRes();
765    client = GetClientResWithName(ua->cons->name());
766
767    if (!client) {
768       ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
769       goto get_out;
770    }
771    if (client->address) {
772       free(client->address);
773    }
774    /* MA Bug 6 remove ifdef */
775    sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
776    client->address = bstrdup(buf);
777    ua->send_msg(_("Client \"%s\" address set to %s\n"),
778             client->name(), client->address);
779 get_out:
780    UnlockRes();
781    return 1;
782 }
783
784
785 static void do_en_disable_cmd(UAContext *ua, bool setting)
786 {
787    JOB *job;
788    int i;
789
790    i = find_arg_with_value(ua, NT_("job")); 
791    if (i < 0) { 
792       job = select_job_resource(ua);
793       if (!job) {
794          return;
795       }
796    } else {
797       LockRes();
798       job = GetJobResWithName(ua->argv[i]);
799       UnlockRes();
800    } 
801    if (!job) {
802       ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
803       return;
804    }
805
806    if (!acl_access_ok(ua, Job_ACL, job->name())) {
807       ua->error_msg(_("Unauthorized command from this console.\n"));
808       return;
809    }
810    job->enabled = setting;
811    ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
812    return;
813 }
814
815 static int enable_cmd(UAContext *ua, const char *cmd)
816 {
817    do_en_disable_cmd(ua, true);
818    return 1;
819 }
820
821 static int disable_cmd(UAContext *ua, const char *cmd)
822 {
823    do_en_disable_cmd(ua, false);
824    return 1;
825 }
826
827
828 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
829 {
830    BSOCK *sd;
831    JCR *jcr = ua->jcr;
832    USTORE lstore;
833    
834    lstore.store = store;
835    pm_strcpy(lstore.store_source, _("unknown source"));
836    set_wstorage(jcr, &lstore);
837    /* Try connecting for up to 15 seconds */
838    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
839       store->name(), store->address, store->SDport);
840    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
841       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
842       return;
843    }
844    Dmsg0(120, _("Connected to storage daemon\n"));
845    sd = jcr->store_bsock;
846    sd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
847    if (sd->recv() >= 0) {
848       ua->send_msg("%s", sd->msg);
849    }
850    sd->signal(BNET_TERMINATE);
851    sd->close();
852    jcr->store_bsock = NULL;
853    return;
854 }
855
856 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
857 {
858    BSOCK *fd;
859
860    /* Connect to File daemon */
861
862    ua->jcr->client = client;
863    /* Try to connect for 15 seconds */
864    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
865       client->name(), client->address, client->FDport);
866    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
867       ua->error_msg(_("Failed to connect to Client.\n"));
868       return;
869    }
870    Dmsg0(120, "Connected to file daemon\n");
871    fd = ua->jcr->file_bsock;
872    fd->fsend("setdebug=%d trace=%d\n", level, trace_flag);
873    if (fd->recv() >= 0) {
874       ua->send_msg("%s", fd->msg);
875    }
876    fd->signal(BNET_TERMINATE);
877    fd->close();
878    ua->jcr->file_bsock = NULL;
879    return;
880 }
881
882
883 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
884 {
885    STORE *store, **unique_store;
886    CLIENT *client, **unique_client;
887    int i, j, found;
888
889    /* Director */
890    debug_level = level;
891
892    /* Count Storage items */
893    LockRes();
894    store = NULL;
895    i = 0;
896    foreach_res(store, R_STORAGE) {
897       i++;
898    }
899    unique_store = (STORE **) malloc(i * sizeof(STORE));
900    /* Find Unique Storage address/port */
901    store = (STORE *)GetNextRes(R_STORAGE, NULL);
902    i = 0;
903    unique_store[i++] = store;
904    while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
905       found = 0;
906       for (j=0; j<i; j++) {
907          if (strcmp(unique_store[j]->address, store->address) == 0 &&
908              unique_store[j]->SDport == store->SDport) {
909             found = 1;
910             break;
911          }
912       }
913       if (!found) {
914          unique_store[i++] = store;
915          Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
916       }
917    }
918    UnlockRes();
919
920    /* Call each unique Storage daemon */
921    for (j=0; j<i; j++) {
922       do_storage_setdebug(ua, unique_store[j], level, trace_flag);
923    }
924    free(unique_store);
925
926    /* Count Client items */
927    LockRes();
928    client = NULL;
929    i = 0;
930    foreach_res(client, R_CLIENT) {
931       i++;
932    }
933    unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
934    /* Find Unique Client address/port */
935    client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
936    i = 0;
937    unique_client[i++] = client;
938    while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
939       found = 0;
940       for (j=0; j<i; j++) {
941          if (strcmp(unique_client[j]->address, client->address) == 0 &&
942              unique_client[j]->FDport == client->FDport) {
943             found = 1;
944             break;
945          }
946       }
947       if (!found) {
948          unique_client[i++] = client;
949          Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
950       }
951    }
952    UnlockRes();
953
954    /* Call each unique File daemon */
955    for (j=0; j<i; j++) {
956       do_client_setdebug(ua, unique_client[j], level, trace_flag);
957    }
958    free(unique_client);
959 }
960
961 /*
962  * setdebug level=nn all trace=1/0
963  */
964 static int setdebug_cmd(UAContext *ua, const char *cmd)
965 {
966    STORE *store;
967    CLIENT *client;
968    int level;
969    int trace_flag = -1;
970    int i;
971
972    Dmsg1(120, "setdebug:%s:\n", cmd);
973
974    level = -1;
975    i = find_arg_with_value(ua, "level");
976    if (i >= 0) {
977       level = atoi(ua->argv[i]);
978    }
979    if (level < 0) {
980       if (!get_pint(ua, _("Enter new debug level: "))) {
981          return 1;
982       }
983       level = ua->pint32_val;
984    }
985
986    /* Look for trace flag. -1 => not change */
987    i = find_arg_with_value(ua, "trace");
988    if (i >= 0) {
989       trace_flag = atoi(ua->argv[i]);
990       if (trace_flag > 0) {
991          trace_flag = 1;
992       }
993    }
994
995    /* General debug? */
996    for (i=1; i<ua->argc; i++) {
997       if (strcasecmp(ua->argk[i], "all") == 0) {
998          do_all_setdebug(ua, level, trace_flag);
999          return 1;
1000       }
1001       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1002           strcasecmp(ua->argk[i], "director") == 0) {
1003          debug_level = level;
1004          set_trace(trace_flag);
1005          return 1;
1006       }
1007       if (strcasecmp(ua->argk[i], "client") == 0 ||
1008           strcasecmp(ua->argk[i], "fd") == 0) {
1009          client = NULL;
1010          if (ua->argv[i]) {
1011             client = GetClientResWithName(ua->argv[i]);
1012             if (client) {
1013                do_client_setdebug(ua, client, level, trace_flag);
1014                return 1;
1015             }
1016          }
1017          client = select_client_resource(ua);
1018          if (client) {
1019             do_client_setdebug(ua, client, level, trace_flag);
1020             return 1;
1021          }
1022       }
1023
1024       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1025           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1026           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1027          store = NULL;
1028          if (ua->argv[i]) {
1029             store = GetStoreResWithName(ua->argv[i]);
1030             if (store) {
1031                do_storage_setdebug(ua, store, level, trace_flag);
1032                return 1;
1033             }
1034          }
1035          store = get_storage_resource(ua, false/*no default*/);
1036          if (store) {
1037             do_storage_setdebug(ua, store, level, trace_flag);
1038             return 1;
1039          }
1040       }
1041    }
1042    /*
1043     * We didn't find an appropriate keyword above, so
1044     * prompt the user.
1045     */
1046    start_prompt(ua, _("Available daemons are: \n"));
1047    add_prompt(ua, _("Director"));
1048    add_prompt(ua, _("Storage"));
1049    add_prompt(ua, _("Client"));
1050    add_prompt(ua, _("All"));
1051    switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1052    case 0:                         /* Director */
1053       debug_level = level;
1054       set_trace(trace_flag);
1055       break;
1056    case 1:
1057       store = get_storage_resource(ua, false/*no default*/);
1058       if (store) {
1059          do_storage_setdebug(ua, store, level, trace_flag);
1060       }
1061       break;
1062    case 2:
1063       client = select_client_resource(ua);
1064       if (client) {
1065          do_client_setdebug(ua, client, level, trace_flag);
1066       }
1067       break;
1068    case 3:
1069       do_all_setdebug(ua, level, trace_flag);
1070       break;
1071    default:
1072       break;
1073    }
1074    return 1;
1075 }
1076
1077 /*
1078  * Turn debug tracing to file on/off
1079  */
1080 static int trace_cmd(UAContext *ua, const char *cmd)
1081 {
1082    char *onoff;
1083
1084    if (ua->argc != 2) {
1085       if (!get_cmd(ua, _("Turn on or off? "))) {
1086             return 1;
1087       }
1088       onoff = ua->cmd;
1089    } else {
1090       onoff = ua->argk[1];
1091    }
1092
1093    set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1094    return 1;
1095
1096 }
1097
1098 static int var_cmd(UAContext *ua, const char *cmd)
1099 {
1100    POOLMEM *val = get_pool_memory(PM_FNAME);
1101    char *var;
1102
1103    if (!open_client_db(ua)) {
1104       return 1;
1105    }
1106    for (var=ua->cmd; *var != ' '; ) {    /* skip command */
1107       var++;
1108    }
1109    while (*var == ' ') {                 /* skip spaces */
1110       var++;
1111    }
1112    Dmsg1(100, "Var=%s:\n", var);
1113    variable_expansion(ua->jcr, var, &val);
1114    ua->send_msg("%s\n", val);
1115    free_pool_memory(val);
1116    return 1;
1117 }
1118
1119 static int estimate_cmd(UAContext *ua, const char *cmd)
1120 {
1121    JOB *job = NULL;
1122    CLIENT *client = NULL;
1123    FILESET *fileset = NULL;
1124    int listing = 0;
1125    char since[MAXSTRING];
1126    JCR *jcr = ua->jcr;
1127
1128    jcr->set_JobLevel(L_FULL);
1129    for (int i=1; i<ua->argc; i++) {
1130       if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1131           strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1132          if (ua->argv[i]) {
1133             client = GetClientResWithName(ua->argv[i]);
1134             if (!client) {
1135                ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1136                return 1;
1137             }
1138             continue;
1139          } else {
1140             ua->error_msg(_("Client name missing.\n"));
1141             return 1;
1142          }
1143       }
1144       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1145          if (ua->argv[i]) {
1146             job = GetJobResWithName(ua->argv[i]);
1147             if (!job) {
1148                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1149                return 1;
1150             }
1151             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1152                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1153                return 1;
1154             }
1155             continue;
1156          } else {
1157             ua->error_msg(_("Job name missing.\n"));
1158             return 1;
1159          }
1160
1161       }
1162       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1163          if (ua->argv[i]) {
1164             fileset = GetFileSetResWithName(ua->argv[i]);
1165             if (!fileset) {
1166                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1167                return 1;
1168             }
1169             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1170                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1171                return 1;
1172             }
1173             continue;
1174          } else {
1175             ua->error_msg(_("Fileset name missing.\n"));
1176             return 1;
1177          }
1178       }
1179       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1180          listing = 1;
1181          continue;
1182       }
1183       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1184          if (ua->argv[i]) {
1185             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1186                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1187             }
1188             continue;
1189          } else {
1190            ua->error_msg(_("Level value missing.\n"));
1191            return 1;
1192          }
1193       }
1194    }
1195    if (!job && !(client && fileset)) {
1196       if (!(job = select_job_resource(ua))) {
1197          return 1;
1198       }
1199    }
1200    if (!job) {
1201       job = GetJobResWithName(ua->argk[1]);
1202       if (!job) {
1203          ua->error_msg(_("No job specified.\n"));
1204          return 1;
1205       }
1206       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1207          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1208          return 1;
1209       }
1210    }
1211    if (!client) {
1212       client = job->client;
1213    }
1214    if (!fileset) {
1215       fileset = job->fileset;
1216    }
1217    jcr->client = client;
1218    jcr->fileset = fileset;
1219    close_db(ua);
1220    if (job->pool->catalog) {
1221       ua->catalog = job->pool->catalog;
1222    } else {
1223       ua->catalog = client->catalog;
1224    }
1225
1226    if (!open_db(ua)) {
1227       return 1;
1228    }
1229
1230    jcr->job = job;
1231    jcr->set_JobType(JT_BACKUP);
1232    init_jcr_job_record(jcr);
1233
1234    if (!get_or_create_client_record(jcr)) {
1235       return 1;
1236    }
1237    if (!get_or_create_fileset_record(jcr)) {
1238       return 1;
1239    }
1240
1241    get_level_since_time(ua->jcr, since, sizeof(since));
1242
1243    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1244       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1245    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1246       ua->error_msg(_("Failed to connect to Client.\n"));
1247       return 1;
1248    }
1249
1250    if (!send_include_list(jcr)) {
1251       ua->error_msg(_("Error sending include list.\n"));
1252       goto bail_out;
1253    }
1254
1255    if (!send_exclude_list(jcr)) {
1256       ua->error_msg(_("Error sending exclude list.\n"));
1257       goto bail_out;
1258    }
1259
1260    if (!send_level_command(jcr)) {
1261       goto bail_out;
1262    }
1263
1264    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1265    while (bnet_recv(jcr->file_bsock) >= 0) {
1266       ua->send_msg("%s", jcr->file_bsock->msg);
1267    }
1268
1269 bail_out:
1270    if (jcr->file_bsock) {
1271       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1272       bnet_close(jcr->file_bsock);
1273       jcr->file_bsock = NULL;
1274    }
1275    return 1;
1276 }
1277
1278
1279 /*
1280  * print time
1281  */
1282 static int time_cmd(UAContext *ua, const char *cmd)
1283 {
1284    char sdt[50];
1285    time_t ttime = time(NULL);
1286    struct tm tm;
1287    (void)localtime_r(&ttime, &tm);
1288    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1289    ua->send_msg("%s\n", sdt);
1290    return 1;
1291 }
1292
1293 /*
1294  * reload the conf file
1295  */
1296 extern "C" void reload_config(int sig);
1297
1298 static int reload_cmd(UAContext *ua, const char *cmd)
1299 {
1300    reload_config(1);
1301    return 1;
1302 }
1303
1304 /*
1305  * Delete Pool records (should purge Media with it).
1306  *
1307  *  delete pool=<pool-name>
1308  *  delete volume pool=<pool-name> volume=<name>
1309  *  delete jobid=xxx
1310  */
1311 static int delete_cmd(UAContext *ua, const char *cmd)
1312 {
1313    static const char *keywords[] = {
1314       NT_("volume"),
1315       NT_("pool"),
1316       NT_("jobid"),
1317       NULL};
1318
1319    if (!open_client_db(ua)) {
1320       return 1;
1321    }
1322
1323    switch (find_arg_keyword(ua, keywords)) {
1324    case 0:
1325       delete_volume(ua);
1326       return 1;
1327    case 1:
1328       delete_pool(ua);
1329       return 1;
1330    case 2:
1331       int i;
1332       while ((i=find_arg(ua, "jobid")) > 0) {
1333          delete_job(ua);
1334          *ua->argk[i] = 0;         /* zap keyword already visited */
1335       }
1336       return 1;
1337    default:
1338       break;
1339    }
1340
1341    ua->warning_msg(_(
1342 "In general it is not a good idea to delete either a\n"
1343 "Pool or a Volume since they may contain data.\n\n"));
1344
1345    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1346    case 0:
1347       delete_volume(ua);
1348       break;
1349    case 1:
1350       delete_pool(ua);
1351       break;
1352    case 2:
1353       delete_job(ua);
1354       return 1;
1355    default:
1356       ua->warning_msg(_("Nothing done.\n"));
1357       break;
1358    }
1359    return 1;
1360 }
1361
1362
1363 /*
1364  * delete_job has been modified to parse JobID lists like the
1365  * following:
1366  * delete JobID=3,4,6,7-11,14
1367  *
1368  * Thanks to Phil Stracchino for the above addition.
1369  */
1370
1371 static void delete_job(UAContext *ua)
1372 {
1373    JobId_t JobId;
1374    char *s,*sep,*tok;
1375
1376    int i = find_arg_with_value(ua, NT_("jobid"));
1377    if (i >= 0) {
1378       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1379         s = bstrdup(ua->argv[i]);
1380         tok = s;
1381         /*
1382          * We could use strtok() here.  But we're not going to, because:
1383          * (a) strtok() is deprecated, having been replaced by strsep();
1384          * (b) strtok() is broken in significant ways.
1385          * we could use strsep() instead, but it's not universally available.
1386          * so we grow our own using strchr().
1387          */
1388         sep = strchr(tok, ',');
1389         while (sep != NULL) {
1390            *sep = '\0';
1391            if (strchr(tok, '-')) {
1392                delete_job_id_range(ua, tok);
1393            } else {
1394               JobId = str_to_int64(tok);
1395               do_job_delete(ua, JobId);
1396            }
1397            tok = ++sep;
1398            sep = strchr(tok, ',');
1399         }
1400         /* pick up the last token */
1401         if (strchr(tok, '-')) {
1402             delete_job_id_range(ua, tok);
1403         } else {
1404             JobId = str_to_int64(tok);
1405             do_job_delete(ua, JobId);
1406         }
1407
1408          free(s);
1409       } else {
1410          JobId = str_to_int64(ua->argv[i]);
1411         do_job_delete(ua, JobId);
1412       }
1413    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1414       return;
1415    } else {
1416       JobId = ua->int64_val;
1417       do_job_delete(ua, JobId);
1418    }
1419 }
1420
1421 /*
1422  * we call delete_job_id_range to parse range tokens and iterate over ranges
1423  */
1424 static void delete_job_id_range(UAContext *ua, char *tok)
1425 {
1426    char *tok2;
1427    JobId_t j,j1,j2;
1428
1429    tok2 = strchr(tok, '-');
1430    *tok2 = '\0';
1431    tok2++;
1432    j1 = str_to_int64(tok);
1433    j2 = str_to_int64(tok2);
1434    for (j=j1; j<=j2; j++) {
1435       do_job_delete(ua, j);
1436    }
1437 }
1438
1439 /*
1440  * do_job_delete now performs the actual delete operation atomically
1441  */
1442 static void do_job_delete(UAContext *ua, JobId_t JobId)
1443 {
1444    char ed1[50];
1445
1446    edit_int64(JobId, ed1);
1447    purge_jobs_from_catalog(ua, ed1);
1448    ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1449 }
1450
1451 /*
1452  * Delete media records from database -- dangerous
1453  */
1454 static int delete_volume(UAContext *ua)
1455 {
1456    MEDIA_DBR mr;
1457    char buf[1000];
1458
1459    if (!select_media_dbr(ua, &mr)) {
1460       return 1;
1461    }
1462    ua->warning_msg(_("\nThis command will delete volume %s\n"
1463       "and all Jobs saved on that volume from the Catalog\n"),
1464       mr.VolumeName);
1465
1466    if (find_arg(ua, "yes") >= 0) {
1467       ua->pint32_val = 1; /* Have "yes" on command line already" */
1468    } else {
1469       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1470          mr.VolumeName);
1471       if (!get_yesno(ua, buf)) {
1472          return 1;
1473       }
1474    }
1475    if (ua->pint32_val) {
1476       db_delete_media_record(ua->jcr, ua->db, &mr);
1477    }
1478    return 1;
1479 }
1480
1481 /*
1482  * Delete a pool record from the database -- dangerous
1483  */
1484 static int delete_pool(UAContext *ua)
1485 {
1486    POOL_DBR  pr;
1487    char buf[200];
1488
1489    memset(&pr, 0, sizeof(pr));
1490
1491    if (!get_pool_dbr(ua, &pr)) {
1492       return 1;
1493    }
1494    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1495       pr.Name);
1496    if (!get_yesno(ua, buf)) {
1497       return 1;
1498    }
1499    if (ua->pint32_val) {
1500       db_delete_pool_record(ua->jcr, ua->db, &pr);
1501    }
1502    return 1;
1503 }
1504
1505 int memory_cmd(UAContext *ua, const char *cmd)
1506 {
1507    list_dir_status_header(ua);
1508    sm_dump(false, true);
1509    return 1;
1510 }
1511
1512 static void do_mount_cmd(UAContext *ua, const char *command)
1513 {
1514    USTORE store;
1515    BSOCK *sd;
1516    JCR *jcr = ua->jcr;
1517    char dev_name[MAX_NAME_LENGTH];
1518    int drive;
1519    int slot = -1;
1520
1521    if (!open_client_db(ua)) {
1522       return;
1523    }
1524    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1525
1526    store.store = get_storage_resource(ua, true/*arg is storage*/);
1527    if (!store.store) {
1528       return;
1529    }
1530    pm_strcpy(store.store_source, _("unknown source"));
1531    set_wstorage(jcr, &store);
1532    drive = get_storage_drive(ua, store.store);
1533    if (strcmp(command, "mount") == 0) {
1534       slot = get_storage_slot(ua, store.store);
1535    }
1536
1537    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1538       store.store->media_type, store.store->dev_name(), drive);
1539
1540    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1541       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1542       return;
1543    }
1544    sd = jcr->store_bsock;
1545    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1546    bash_spaces(dev_name);
1547    if (slot > 0) {
1548       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1549    } else {
1550       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1551    }
1552    while (bnet_recv(sd) >= 0) {
1553       ua->send_msg("%s", sd->msg);
1554    }
1555    bnet_sig(sd, BNET_TERMINATE);
1556    bnet_close(sd);
1557    jcr->store_bsock = NULL;
1558 }
1559
1560 /*
1561  * mount [storage=<name>] [drive=nn] [slot=mm]
1562  */
1563 static int mount_cmd(UAContext *ua, const char *cmd)
1564 {
1565    do_mount_cmd(ua, "mount");          /* mount */
1566    return 1;
1567 }
1568
1569
1570 /*
1571  * unmount [storage=<name>] [drive=nn]
1572  */
1573 static int unmount_cmd(UAContext *ua, const char *cmd)
1574 {
1575    do_mount_cmd(ua, "unmount");          /* unmount */
1576    return 1;
1577 }
1578
1579
1580 /*
1581  * release [storage=<name>] [drive=nn]
1582  */
1583 static int release_cmd(UAContext *ua, const char *cmd)
1584 {
1585    do_mount_cmd(ua, "release");          /* release */
1586    return 1;
1587 }
1588
1589
1590 /*
1591  * Switch databases
1592  *   use catalog=<name>
1593  */
1594 static int use_cmd(UAContext *ua, const char *cmd)
1595 {
1596    CAT *oldcatalog, *catalog;
1597
1598
1599    close_db(ua);                      /* close any previously open db */
1600    oldcatalog = ua->catalog;
1601
1602    if (!(catalog = get_catalog_resource(ua))) {
1603       ua->catalog = oldcatalog;
1604    } else {
1605       ua->catalog = catalog;
1606    }
1607    if (open_db(ua)) {
1608       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1609          ua->catalog->name(), ua->catalog->db_name);
1610    }
1611    return 1;
1612 }
1613
1614 int quit_cmd(UAContext *ua, const char *cmd)
1615 {
1616    ua->quit = true;
1617    return 1;
1618 }
1619
1620 /* Handler to get job status */
1621 static int status_handler(void *ctx, int num_fields, char **row)
1622 {
1623    char *val = (char *)ctx;
1624
1625    if (row[0]) {
1626       *val = row[0][0];
1627    } else {
1628       *val = '?';               /* Unknown by default */
1629    }
1630
1631    return 0;
1632 }
1633
1634 /*
1635  * Wait until no job is running
1636  */
1637 int wait_cmd(UAContext *ua, const char *cmd)
1638 {
1639    JCR *jcr;
1640    int i;
1641    time_t stop_time = 0;
1642
1643    /*
1644     * no args
1645     * Wait until no job is running
1646     */
1647    if (ua->argc == 1) {
1648       bmicrosleep(0, 200000);            /* let job actually start */
1649       for (bool running=true; running; ) {
1650          running = false;
1651          foreach_jcr(jcr) {
1652             if (jcr->JobId != 0) {
1653                running = true;
1654                break;
1655             }
1656          }
1657          endeach_jcr(jcr);
1658
1659          if (running) {
1660             bmicrosleep(1, 0);
1661          }
1662       }
1663       return 1;
1664    }
1665
1666    i = find_arg_with_value(ua, NT_("timeout")); 
1667    if (i > 0 && ua->argv[i]) {
1668       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1669    }
1670
1671    /* we have jobid, jobname or ujobid argument */
1672
1673    uint32_t jobid = 0 ;
1674
1675    if (!open_client_db(ua)) {
1676       ua->error_msg(_("ERR: Can't open db\n")) ;
1677       return 1;
1678    }
1679
1680    for (int i=1; i<ua->argc; i++) {
1681       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1682          if (!ua->argv[i]) {
1683             break;
1684          }
1685          jobid = str_to_int64(ua->argv[i]);
1686          break;
1687       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1688                  strcasecmp(ua->argk[i], "job") == 0) {
1689          if (!ua->argv[i]) {
1690             break;
1691          }
1692          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1693          if (jcr) {
1694             jobid = jcr->JobId ;
1695             free_jcr(jcr);
1696          }
1697          break;
1698       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1699          if (!ua->argv[i]) {
1700             break;
1701          }
1702          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1703          if (jcr) {
1704             jobid = jcr->JobId ;
1705             free_jcr(jcr);
1706          }
1707          break;
1708       /* Wait for a mount request */
1709       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1710          for (bool waiting=false; !waiting; ) {
1711             foreach_jcr(jcr) {
1712                if (jcr->JobId != 0 && 
1713                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1714                   waiting = true;
1715                   break;
1716                }
1717             }
1718             endeach_jcr(jcr);
1719             if (waiting) {
1720                break;
1721             }
1722             if (stop_time && (time(NULL) >= stop_time)) {
1723                ua->warning_msg(_("Wait on mount timed out\n"));
1724                return 1;
1725             }
1726             bmicrosleep(1, 0);
1727          }
1728          return 1;
1729       }
1730    }
1731
1732    if (jobid == 0) {
1733       ua->error_msg(_("ERR: Job was not found\n"));
1734       return 1 ;
1735    }
1736
1737    /*
1738     * We wait the end of a specific job
1739     */
1740
1741    bmicrosleep(0, 200000);            /* let job actually start */
1742    for (bool running=true; running; ) {
1743       running = false;
1744
1745       jcr=get_jcr_by_id(jobid) ;
1746
1747       if (jcr) {
1748          running = true ;
1749          free_jcr(jcr);
1750       }
1751
1752       if (running) {
1753          bmicrosleep(1, 0);
1754       }
1755    }
1756
1757    /*
1758     * We have to get JobStatus
1759     */
1760
1761    int status ;
1762    char jobstatus = '?';        /* Unknown by default */
1763    char buf[256] ;
1764
1765    bsnprintf(buf, sizeof(buf),
1766              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1767
1768
1769    db_sql_query(ua->db, buf,
1770                 status_handler, (void *)&jobstatus);
1771
1772    switch (jobstatus) {
1773    case JS_Error:
1774       status = 1 ;         /* Warning */
1775       break;
1776
1777    case JS_FatalError:
1778    case JS_ErrorTerminated:
1779    case JS_Canceled:
1780       status = 2 ;         /* Critical */
1781       break;
1782
1783    case JS_Warnings:
1784    case JS_Terminated:
1785       status = 0 ;         /* Ok */
1786       break;
1787
1788    default:
1789       status = 3 ;         /* Unknown */
1790       break;
1791    }
1792
1793    ua->send_msg("JobId=%i\n", jobid) ;
1794    ua->send_msg("JobStatus=%s (%c)\n", 
1795             job_status_to_str(jobstatus), 
1796             jobstatus) ;
1797
1798    if (ua->gui || ua->api) {
1799       ua->send_msg("ExitStatus=%i\n", status) ;
1800    }
1801
1802    return 1;
1803 }
1804
1805
1806 static int help_cmd(UAContext *ua, const char *cmd)
1807 {
1808    unsigned int i;
1809
1810    ua->send_msg(_("  Command    Description\n  =======    ===========\n"));
1811    for (i=0; i<comsize; i++) {
1812       ua->send_msg(_("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1813    }
1814    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1815    return 1;
1816 }
1817
1818 int qhelp_cmd(UAContext *ua, const char *cmd)
1819 {
1820    unsigned int i;
1821
1822    for (i=0; i<comsize; i++) {
1823       ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1824    }
1825    return 1;
1826 }
1827
1828 #if 1 
1829 static int version_cmd(UAContext *ua, const char *cmd)
1830 {
1831    ua->send_msg(_("%s Version: %s (%s) %s %s %s %s\n"), my_name, VERSION, BDATE,
1832                 HOST_OS, DISTNAME, DISTVER, NPRTB(director->verid));
1833    return 1;
1834 }
1835 #else
1836 /*
1837  *  Test code -- turned on only for debug testing 
1838  */
1839 static int version_cmd(UAContext *ua, const char *cmd)
1840 {
1841    dbid_list ids;
1842    POOL_MEM query(PM_MESSAGE);
1843    open_db(ua);
1844    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1845    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1846    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1847    for (int i=0; i < ids.num_ids; i++) {
1848       ua->send_msg("id=%d\n", ids.DBId[i]);
1849    }
1850    close_db(ua);
1851    return 1;
1852 }
1853 #endif
1854
1855 /* 
1856  * This call explicitly checks for a catalog=xxx and
1857  *  if given, opens that catalog.  It also checks for
1858  *  client=xxx and if found, opens the catalog 
1859  *  corresponding to that client. If we still don't 
1860  *  have a catalog, look for a Job keyword and get the
1861  *  catalog from its client record.
1862  */
1863 bool open_client_db(UAContext *ua)
1864 {
1865    int i;
1866    CAT *catalog;
1867    CLIENT *client;
1868    JOB *job;
1869
1870    /* Try for catalog keyword */
1871    i = find_arg_with_value(ua, NT_("catalog"));
1872    if (i >= 0) {
1873       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1874          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1875          return false;
1876       }
1877       catalog = GetCatalogResWithName(ua->argv[i]);
1878       if (catalog) {
1879          if (ua->catalog && ua->catalog != catalog) {
1880             close_db(ua);
1881          }
1882          ua->catalog = catalog;
1883          return open_db(ua);
1884       }
1885    }
1886
1887    /* Try for client keyword */
1888    i = find_arg_with_value(ua, NT_("client"));
1889    if (i >= 0) {
1890       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1891          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1892          return false;
1893       }
1894       client = GetClientResWithName(ua->argv[i]);
1895       if (client) {
1896          catalog = client->catalog;
1897          if (ua->catalog && ua->catalog != catalog) {
1898             close_db(ua);
1899          }
1900          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1901             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1902             return false;
1903          }
1904          ua->catalog = catalog;
1905          return open_db(ua);
1906       }
1907    }
1908
1909    /* Try for Job keyword */
1910    i = find_arg_with_value(ua, NT_("job"));
1911    if (i >= 0) {
1912       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1913          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1914          return false;
1915       }
1916       job = GetJobResWithName(ua->argv[i]);
1917       if (job) {
1918          catalog = job->client->catalog;
1919          if (ua->catalog && ua->catalog != catalog) {
1920             close_db(ua);
1921          }
1922          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1923             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1924             return false;
1925          }
1926          ua->catalog = catalog;
1927          return open_db(ua);
1928       }
1929    }
1930
1931    return open_db(ua);
1932 }
1933
1934
1935 /*
1936  * Open the catalog database.
1937  */
1938 bool open_db(UAContext *ua)
1939 {
1940    if (ua->db) {
1941       return true;
1942    }
1943    if (!ua->catalog) {
1944       ua->catalog = get_catalog_resource(ua);
1945       if (!ua->catalog) {
1946          ua->error_msg( _("Could not find a Catalog resource\n"));
1947          return false;
1948       }
1949    }
1950
1951    ua->jcr->catalog = ua->catalog;
1952
1953    Dmsg0(100, "UA Open database\n");
1954    ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
1955                              ua->catalog->db_user,
1956                              ua->catalog->db_password, ua->catalog->db_address,
1957                              ua->catalog->db_port, ua->catalog->db_socket,
1958                              ua->catalog->mult_db_connections);
1959    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1960       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1961                  ua->catalog->db_name);
1962       if (ua->db) {
1963          ua->error_msg("%s", db_strerror(ua->db));
1964       }
1965       close_db(ua);
1966       return false;
1967    }
1968    ua->jcr->db = ua->db;
1969    if (!ua->api) {
1970       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
1971    }
1972    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1973    return true;
1974 }
1975
1976 void close_db(UAContext *ua)
1977 {
1978    if (ua->db) {
1979       db_close_database(ua->jcr, ua->db);
1980       ua->db = NULL;
1981       if (ua->jcr) {
1982          ua->jcr->db = NULL;
1983       }
1984    }
1985 }