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