]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
kes Make first step toward eliminating globals from config
[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 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->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->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
1570    /*
1571     * no args
1572     * Wait until no job is running
1573     */
1574    if (ua->argc == 1) {
1575       bmicrosleep(0, 200000);            /* let job actually start */
1576       for (bool running=true; running; ) {
1577          running = false;
1578          foreach_jcr(jcr) {
1579             if (jcr->JobId != 0) {
1580                running = true;
1581                break;
1582             }
1583          }
1584          endeach_jcr(jcr);
1585
1586          if (running) {
1587             bmicrosleep(1, 0);
1588          }
1589       }
1590       return 1;
1591    }
1592
1593    /* we have jobid, jobname or ujobid argument */
1594
1595    uint32_t jobid = 0 ;
1596
1597    if (!open_client_db(ua)) {
1598       ua->error_msg(_("ERR: Can't open db\n")) ;
1599       return 1;
1600    }
1601
1602    for (int i=1; i<ua->argc; i++) {
1603       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1604          if (!ua->argv[i]) {
1605             break;
1606          }
1607          jobid = str_to_int64(ua->argv[i]);
1608          break;
1609       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1610                  strcasecmp(ua->argk[i], "job") == 0) {
1611          if (!ua->argv[i]) {
1612             break;
1613          }
1614          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1615          if (jcr) {
1616             jobid = jcr->JobId ;
1617             free_jcr(jcr);
1618          }
1619          break;
1620       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1621          if (!ua->argv[i]) {
1622             break;
1623          }
1624          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1625          if (jcr) {
1626             jobid = jcr->JobId ;
1627             free_jcr(jcr);
1628          }
1629          break;
1630       }
1631    }
1632
1633    if (jobid == 0) {
1634       ua->error_msg(_("ERR: Job was not found\n"));
1635       return 1 ;
1636    }
1637
1638    /*
1639     * We wait the end of a specific job
1640     */
1641
1642    bmicrosleep(0, 200000);            /* let job actually start */
1643    for (bool running=true; running; ) {
1644       running = false;
1645
1646       jcr=get_jcr_by_id(jobid) ;
1647
1648       if (jcr) {
1649          running = true ;
1650          free_jcr(jcr);
1651       }
1652
1653       if (running) {
1654          bmicrosleep(1, 0);
1655       }
1656    }
1657
1658    /*
1659     * We have to get JobStatus
1660     */
1661
1662    int status ;
1663    char jobstatus = '?';        /* Unknown by default */
1664    char buf[256] ;
1665
1666    bsnprintf(buf, sizeof(buf),
1667              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1668
1669
1670    db_sql_query(ua->db, buf,
1671                 status_handler, (void *)&jobstatus);
1672
1673    switch (jobstatus) {
1674    case JS_Error:
1675       status = 1 ;         /* Warning */
1676       break;
1677
1678    case JS_FatalError:
1679    case JS_ErrorTerminated:
1680    case JS_Canceled:
1681       status = 2 ;         /* Critical */
1682       break;
1683
1684    case JS_Terminated:
1685       status = 0 ;         /* Ok */
1686       break;
1687
1688    default:
1689       status = 3 ;         /* Unknown */
1690       break;
1691    }
1692
1693    ua->send_msg("JobId=%i\n", jobid) ;
1694    ua->send_msg("JobStatus=%s (%c)\n", 
1695             job_status_to_str(jobstatus), 
1696             jobstatus) ;
1697
1698    if (ua->gui || ua->api) {
1699       ua->send_msg("ExitStatus=%i\n", status) ;
1700    }
1701
1702    return 1;
1703 }
1704
1705
1706 static int help_cmd(UAContext *ua, const char *cmd)
1707 {
1708    unsigned int i;
1709
1710    ua->send_msg(_("  Command    Description\n  =======    ===========\n"));
1711    for (i=0; i<comsize; i++) {
1712       ua->send_msg(_("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1713    }
1714    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1715    return 1;
1716 }
1717
1718 int qhelp_cmd(UAContext *ua, const char *cmd)
1719 {
1720    unsigned int i;
1721
1722    for (i=0; i<comsize; i++) {
1723       ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1724    }
1725    return 1;
1726 }
1727
1728 #if 1 
1729 static int version_cmd(UAContext *ua, const char *cmd)
1730 {
1731    ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1732             HOST_OS, DISTNAME, DISTVER);
1733    return 1;
1734 }
1735 #else
1736 /*
1737  *  Test code -- turned on only for debug testing 
1738  */
1739 static int version_cmd(UAContext *ua, const char *cmd)
1740 {
1741    dbid_list ids;
1742    POOL_MEM query(PM_MESSAGE);
1743    open_db(ua);
1744    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1745    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1746    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1747    for (int i=0; i < ids.num_ids; i++) {
1748       ua->send_msg("id=%d\n", ids.DBId[i]);
1749    }
1750    close_db(ua);
1751    return 1;
1752 }
1753 #endif
1754
1755 /* 
1756  * This call explicitly checks for a catalog=xxx and
1757  *  if given, opens that catalog.  It also checks for
1758  *  client=xxx and if found, opens the catalog 
1759  *  corresponding to that client. If we still don't 
1760  *  have a catalog, look for a Job keyword and get the
1761  *  catalog from its client record.
1762  */
1763 bool open_client_db(UAContext *ua)
1764 {
1765    int i;
1766    CAT *catalog;
1767    CLIENT *client;
1768    JOB *job;
1769
1770    /* Try for catalog keyword */
1771    i = find_arg_with_value(ua, NT_("catalog"));
1772    if (i >= 0) {
1773       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1774          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1775          return false;
1776       }
1777       catalog = GetCatalogResWithName(ua->argv[i]);
1778       if (catalog) {
1779          if (ua->catalog && ua->catalog != catalog) {
1780             close_db(ua);
1781          }
1782          ua->catalog = catalog;
1783          return open_db(ua);
1784       }
1785    }
1786
1787    /* Try for client keyword */
1788    i = find_arg_with_value(ua, NT_("client"));
1789    if (i >= 0) {
1790       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1791          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1792          return false;
1793       }
1794       client = GetClientResWithName(ua->argv[i]);
1795       if (client) {
1796          catalog = client->catalog;
1797          if (ua->catalog && ua->catalog != catalog) {
1798             close_db(ua);
1799          }
1800          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1801             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1802             return false;
1803          }
1804          ua->catalog = catalog;
1805          return open_db(ua);
1806       }
1807    }
1808
1809    /* Try for Job keyword */
1810    i = find_arg_with_value(ua, NT_("job"));
1811    if (i >= 0) {
1812       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1813          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1814          return false;
1815       }
1816       job = GetJobResWithName(ua->argv[i]);
1817       if (job) {
1818          catalog = job->client->catalog;
1819          if (ua->catalog && ua->catalog != catalog) {
1820             close_db(ua);
1821          }
1822          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1823             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1824             return false;
1825          }
1826          ua->catalog = catalog;
1827          return open_db(ua);
1828       }
1829    }
1830
1831    return open_db(ua);
1832 }
1833
1834
1835 /*
1836  * Open the catalog database.
1837  */
1838 bool open_db(UAContext *ua)
1839 {
1840    if (ua->db) {
1841       return true;
1842    }
1843    if (!ua->catalog) {
1844       ua->catalog = get_catalog_resource(ua);
1845       if (!ua->catalog) {
1846          ua->error_msg( _("Could not find a Catalog resource\n"));
1847          return false;
1848       }
1849    }
1850
1851    ua->jcr->catalog = ua->catalog;
1852
1853    Dmsg0(100, "UA Open database\n");
1854    ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
1855                              ua->catalog->db_user,
1856                              ua->catalog->db_password, ua->catalog->db_address,
1857                              ua->catalog->db_port, ua->catalog->db_socket,
1858                              ua->catalog->mult_db_connections);
1859    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1860       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1861                  ua->catalog->db_name);
1862       if (ua->db) {
1863          ua->error_msg("%s", db_strerror(ua->db));
1864       }
1865       close_db(ua);
1866       return false;
1867    }
1868    ua->jcr->db = ua->db;
1869    if (!ua->api) {
1870       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
1871    }
1872    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1873    return true;
1874 }
1875
1876 void close_db(UAContext *ua)
1877 {
1878    if (ua->db) {
1879       db_close_database(ua->jcr, ua->db);
1880       ua->db = NULL;
1881       if (ua->jcr) {
1882          ua->jcr->db = NULL;
1883       }
1884    }
1885 }