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