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