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