]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
ebl Change the new statistic implementation. Remove the UseStatistic
[bacula/bacula] / bacula / src / dird / ua_cmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Commands
31  *
32  *     Kern Sibbald, September MM
33  *
34  *   Version $Id$
35  */
36  
37 #include "bacula.h"
38 #include "dird.h"
39
40 /* 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->argk[0]);
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          } else {
1088             ua->error_msg(_("Client name missing.\n"));
1089             return 1;
1090          }
1091       }
1092       if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1093          if (ua->argv[i]) {
1094             job = GetJobResWithName(ua->argv[i]);
1095             if (!job) {
1096                ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1097                return 1;
1098             }
1099             if (!acl_access_ok(ua, Job_ACL, job->name())) {
1100                ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1101                return 1;
1102             }
1103             continue;
1104          } else {
1105             ua->error_msg(_("Job name missing.\n"));
1106             return 1;
1107          }
1108
1109       }
1110       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1111          if (ua->argv[i]) {
1112             fileset = GetFileSetResWithName(ua->argv[i]);
1113             if (!fileset) {
1114                ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1115                return 1;
1116             }
1117             if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1118                ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1119                return 1;
1120             }
1121             continue;
1122          } else {
1123             ua->error_msg(_("Fileset name missing.\n"));
1124             return 1;
1125          }
1126       }
1127       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1128          listing = 1;
1129          continue;
1130       }
1131       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1132          if (ua->argv[i]) {
1133             if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1134                ua->error_msg(_("Level \"%s\" not valid.\n"), ua->argv[i]);
1135             }
1136             continue;
1137          } else {
1138            ua->error_msg(_("Level value missing.\n"));
1139            return 1;
1140          }
1141       }
1142    }
1143    if (!job && !(client && fileset)) {
1144       if (!(job = select_job_resource(ua))) {
1145          return 1;
1146       }
1147    }
1148    if (!job) {
1149       job = GetJobResWithName(ua->argk[1]);
1150       if (!job) {
1151          ua->error_msg(_("No job specified.\n"));
1152          return 1;
1153       }
1154       if (!acl_access_ok(ua, Job_ACL, job->name())) {
1155          ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1156          return 1;
1157       }
1158    }
1159    if (!client) {
1160       client = job->client;
1161    }
1162    if (!fileset) {
1163       fileset = job->fileset;
1164    }
1165    jcr->client = client;
1166    jcr->fileset = fileset;
1167    close_db(ua);
1168    if (job->pool->catalog) {
1169       ua->catalog = job->pool->catalog;
1170    } else {
1171       ua->catalog = client->catalog;
1172    }
1173
1174    if (!open_db(ua)) {
1175       return 1;
1176    }
1177
1178    jcr->job = job;
1179    jcr->set_JobType(JT_BACKUP);
1180    init_jcr_job_record(jcr);
1181
1182    if (!get_or_create_client_record(jcr)) {
1183       return 1;
1184    }
1185    if (!get_or_create_fileset_record(jcr)) {
1186       return 1;
1187    }
1188
1189    get_level_since_time(ua->jcr, since, sizeof(since));
1190
1191    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1192       jcr->client->name(), jcr->client->address, jcr->client->FDport);
1193    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1194       ua->error_msg(_("Failed to connect to Client.\n"));
1195       return 1;
1196    }
1197
1198    if (!send_include_list(jcr)) {
1199       ua->error_msg(_("Error sending include list.\n"));
1200       goto bail_out;
1201    }
1202
1203    if (!send_exclude_list(jcr)) {
1204       ua->error_msg(_("Error sending exclude list.\n"));
1205       goto bail_out;
1206    }
1207
1208    if (!send_level_command(jcr)) {
1209       goto bail_out;
1210    }
1211
1212    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1213    while (bnet_recv(jcr->file_bsock) >= 0) {
1214       ua->send_msg("%s", jcr->file_bsock->msg);
1215    }
1216
1217 bail_out:
1218    if (jcr->file_bsock) {
1219       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1220       bnet_close(jcr->file_bsock);
1221       jcr->file_bsock = NULL;
1222    }
1223    return 1;
1224 }
1225
1226
1227 /*
1228  * print time
1229  */
1230 static int time_cmd(UAContext *ua, const char *cmd)
1231 {
1232    char sdt[50];
1233    time_t ttime = time(NULL);
1234    struct tm tm;
1235    (void)localtime_r(&ttime, &tm);
1236    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1237    ua->send_msg("%s\n", sdt);
1238    return 1;
1239 }
1240
1241 /*
1242  * reload the conf file
1243  */
1244 extern "C" void reload_config(int sig);
1245
1246 static int reload_cmd(UAContext *ua, const char *cmd)
1247 {
1248    reload_config(1);
1249    return 1;
1250 }
1251
1252 /*
1253  * Delete Pool records (should purge Media with it).
1254  *
1255  *  delete pool=<pool-name>
1256  *  delete volume pool=<pool-name> volume=<name>
1257  *  delete jobid=xxx
1258  */
1259 static int delete_cmd(UAContext *ua, const char *cmd)
1260 {
1261    static const char *keywords[] = {
1262       NT_("volume"),
1263       NT_("pool"),
1264       NT_("jobid"),
1265       NULL};
1266
1267    if (!open_client_db(ua)) {
1268       return 1;
1269    }
1270
1271    switch (find_arg_keyword(ua, keywords)) {
1272    case 0:
1273       delete_volume(ua);
1274       return 1;
1275    case 1:
1276       delete_pool(ua);
1277       return 1;
1278    case 2:
1279       int i;
1280       while ((i=find_arg(ua, "jobid")) > 0) {
1281          delete_job(ua);
1282          *ua->argk[i] = 0;         /* zap keyword already visited */
1283       }
1284       return 1;
1285    default:
1286       break;
1287    }
1288
1289    ua->warning_msg(_(
1290 "In general it is not a good idea to delete either a\n"
1291 "Pool or a Volume since they may contain data.\n\n"));
1292
1293    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1294    case 0:
1295       delete_volume(ua);
1296       break;
1297    case 1:
1298       delete_pool(ua);
1299       break;
1300    case 2:
1301       delete_job(ua);
1302       return 1;
1303    default:
1304       ua->warning_msg(_("Nothing done.\n"));
1305       break;
1306    }
1307    return 1;
1308 }
1309
1310
1311 /*
1312  * delete_job has been modified to parse JobID lists like the
1313  * following:
1314  * delete JobID=3,4,6,7-11,14
1315  *
1316  * Thanks to Phil Stracchino for the above addition.
1317  */
1318
1319 static void delete_job(UAContext *ua)
1320 {
1321    JobId_t JobId;
1322    char *s,*sep,*tok;
1323
1324    int i = find_arg_with_value(ua, NT_("jobid"));
1325    if (i >= 0) {
1326       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1327         s = bstrdup(ua->argv[i]);
1328         tok = s;
1329         /*
1330          * We could use strtok() here.  But we're not going to, because:
1331          * (a) strtok() is deprecated, having been replaced by strsep();
1332          * (b) strtok() is broken in significant ways.
1333          * we could use strsep() instead, but it's not universally available.
1334          * so we grow our own using strchr().
1335          */
1336         sep = strchr(tok, ',');
1337         while (sep != NULL) {
1338            *sep = '\0';
1339            if (strchr(tok, '-')) {
1340                delete_job_id_range(ua, tok);
1341            } else {
1342               JobId = str_to_int64(tok);
1343               do_job_delete(ua, JobId);
1344            }
1345            tok = ++sep;
1346            sep = strchr(tok, ',');
1347         }
1348         /* pick up the last token */
1349         if (strchr(tok, '-')) {
1350             delete_job_id_range(ua, tok);
1351         } else {
1352             JobId = str_to_int64(tok);
1353             do_job_delete(ua, JobId);
1354         }
1355
1356          free(s);
1357       } else {
1358          JobId = str_to_int64(ua->argv[i]);
1359         do_job_delete(ua, JobId);
1360       }
1361    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1362       return;
1363    } else {
1364       JobId = ua->int64_val;
1365       do_job_delete(ua, JobId);
1366    }
1367 }
1368
1369 /*
1370  * we call delete_job_id_range to parse range tokens and iterate over ranges
1371  */
1372 static void delete_job_id_range(UAContext *ua, char *tok)
1373 {
1374    char *tok2;
1375    JobId_t j,j1,j2;
1376
1377    tok2 = strchr(tok, '-');
1378    *tok2 = '\0';
1379    tok2++;
1380    j1 = str_to_int64(tok);
1381    j2 = str_to_int64(tok2);
1382    for (j=j1; j<=j2; j++) {
1383       do_job_delete(ua, j);
1384    }
1385 }
1386
1387 /*
1388  * do_job_delete now performs the actual delete operation atomically
1389  */
1390 static void do_job_delete(UAContext *ua, JobId_t JobId)
1391 {
1392    char ed1[50];
1393
1394    edit_int64(JobId, ed1);
1395    purge_jobs_from_catalog(ua, ed1);
1396    ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1397 }
1398
1399 /*
1400  * Delete media records from database -- dangerous
1401  */
1402 static int delete_volume(UAContext *ua)
1403 {
1404    MEDIA_DBR mr;
1405    char buf[1000];
1406
1407    if (!select_media_dbr(ua, &mr)) {
1408       return 1;
1409    }
1410    ua->warning_msg(_("\nThis command will delete volume %s\n"
1411       "and all Jobs saved on that volume from the Catalog\n"),
1412       mr.VolumeName);
1413
1414    if (find_arg(ua, "yes") >= 0) {
1415       ua->pint32_val = 1; /* Have "yes" on command line already" */
1416    } else {
1417       bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1418          mr.VolumeName);
1419       if (!get_yesno(ua, buf)) {
1420          return 1;
1421       }
1422    }
1423    if (ua->pint32_val) {
1424       db_delete_media_record(ua->jcr, ua->db, &mr);
1425    }
1426    return 1;
1427 }
1428
1429 /*
1430  * Delete a pool record from the database -- dangerous
1431  */
1432 static int delete_pool(UAContext *ua)
1433 {
1434    POOL_DBR  pr;
1435    char buf[200];
1436
1437    memset(&pr, 0, sizeof(pr));
1438
1439    if (!get_pool_dbr(ua, &pr)) {
1440       return 1;
1441    }
1442    bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1443       pr.Name);
1444    if (!get_yesno(ua, buf)) {
1445       return 1;
1446    }
1447    if (ua->pint32_val) {
1448       db_delete_pool_record(ua->jcr, ua->db, &pr);
1449    }
1450    return 1;
1451 }
1452
1453 int memory_cmd(UAContext *ua, const char *cmd)
1454 {
1455    list_dir_status_header(ua);
1456    sm_dump(false, true);
1457    return 1;
1458 }
1459
1460 static void do_mount_cmd(UAContext *ua, const char *command)
1461 {
1462    USTORE store;
1463    BSOCK *sd;
1464    JCR *jcr = ua->jcr;
1465    char dev_name[MAX_NAME_LENGTH];
1466    int drive;
1467    int slot = -1;
1468
1469    if (!open_client_db(ua)) {
1470       return;
1471    }
1472    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1473
1474    store.store = get_storage_resource(ua, true/*arg is storage*/);
1475    if (!store.store) {
1476       return;
1477    }
1478    pm_strcpy(store.store_source, _("unknown source"));
1479    set_wstorage(jcr, &store);
1480    drive = get_storage_drive(ua, store.store);
1481    if (strcmp(command, "mount") == 0) {
1482       slot = get_storage_slot(ua, store.store);
1483    }
1484
1485    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1486       store.store->media_type, store.store->dev_name(), drive);
1487
1488    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1489       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1490       return;
1491    }
1492    sd = jcr->store_bsock;
1493    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1494    bash_spaces(dev_name);
1495    if (slot > 0) {
1496       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1497    } else {
1498       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1499    }
1500    while (bnet_recv(sd) >= 0) {
1501       ua->send_msg("%s", sd->msg);
1502    }
1503    bnet_sig(sd, BNET_TERMINATE);
1504    bnet_close(sd);
1505    jcr->store_bsock = NULL;
1506 }
1507
1508 /*
1509  * mount [storage=<name>] [drive=nn] [slot=mm]
1510  */
1511 static int mount_cmd(UAContext *ua, const char *cmd)
1512 {
1513    do_mount_cmd(ua, "mount");          /* mount */
1514    return 1;
1515 }
1516
1517
1518 /*
1519  * unmount [storage=<name>] [drive=nn]
1520  */
1521 static int unmount_cmd(UAContext *ua, const char *cmd)
1522 {
1523    do_mount_cmd(ua, "unmount");          /* unmount */
1524    return 1;
1525 }
1526
1527
1528 /*
1529  * release [storage=<name>] [drive=nn]
1530  */
1531 static int release_cmd(UAContext *ua, const char *cmd)
1532 {
1533    do_mount_cmd(ua, "release");          /* release */
1534    return 1;
1535 }
1536
1537
1538 /*
1539  * Switch databases
1540  *   use catalog=<name>
1541  */
1542 static int use_cmd(UAContext *ua, const char *cmd)
1543 {
1544    CAT *oldcatalog, *catalog;
1545
1546
1547    close_db(ua);                      /* close any previously open db */
1548    oldcatalog = ua->catalog;
1549
1550    if (!(catalog = get_catalog_resource(ua))) {
1551       ua->catalog = oldcatalog;
1552    } else {
1553       ua->catalog = catalog;
1554    }
1555    if (open_db(ua)) {
1556       ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1557          ua->catalog->name(), ua->catalog->db_name);
1558    }
1559    return 1;
1560 }
1561
1562 int quit_cmd(UAContext *ua, const char *cmd)
1563 {
1564    ua->quit = true;
1565    return 1;
1566 }
1567
1568 /* Handler to get job status */
1569 static int status_handler(void *ctx, int num_fields, char **row)
1570 {
1571    char *val = (char *)ctx;
1572
1573    if (row[0]) {
1574       *val = row[0][0];
1575    } else {
1576       *val = '?';               /* Unknown by default */
1577    }
1578
1579    return 0;
1580 }
1581
1582 /*
1583  * Wait until no job is running
1584  */
1585 int wait_cmd(UAContext *ua, const char *cmd)
1586 {
1587    JCR *jcr;
1588    int i;
1589    time_t stop_time = 0;
1590
1591    /*
1592     * no args
1593     * Wait until no job is running
1594     */
1595    if (ua->argc == 1) {
1596       bmicrosleep(0, 200000);            /* let job actually start */
1597       for (bool running=true; running; ) {
1598          running = false;
1599          foreach_jcr(jcr) {
1600             if (jcr->JobId != 0) {
1601                running = true;
1602                break;
1603             }
1604          }
1605          endeach_jcr(jcr);
1606
1607          if (running) {
1608             bmicrosleep(1, 0);
1609          }
1610       }
1611       return 1;
1612    }
1613
1614    i = find_arg_with_value(ua, NT_("timeout")); 
1615    if (i > 0 && ua->argv[i]) {
1616       stop_time = time(NULL) + str_to_int64(ua->argv[i]);
1617    }
1618
1619    /* we have jobid, jobname or ujobid argument */
1620
1621    uint32_t jobid = 0 ;
1622
1623    if (!open_client_db(ua)) {
1624       ua->error_msg(_("ERR: Can't open db\n")) ;
1625       return 1;
1626    }
1627
1628    for (int i=1; i<ua->argc; i++) {
1629       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1630          if (!ua->argv[i]) {
1631             break;
1632          }
1633          jobid = str_to_int64(ua->argv[i]);
1634          break;
1635       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1636                  strcasecmp(ua->argk[i], "job") == 0) {
1637          if (!ua->argv[i]) {
1638             break;
1639          }
1640          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1641          if (jcr) {
1642             jobid = jcr->JobId ;
1643             free_jcr(jcr);
1644          }
1645          break;
1646       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1647          if (!ua->argv[i]) {
1648             break;
1649          }
1650          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1651          if (jcr) {
1652             jobid = jcr->JobId ;
1653             free_jcr(jcr);
1654          }
1655          break;
1656       /* Wait for a mount request */
1657       } else if (strcasecmp(ua->argk[i], "mount") == 0) {
1658          for (bool waiting=false; !waiting; ) {
1659             foreach_jcr(jcr) {
1660                if (jcr->JobId != 0 && 
1661                    (jcr->JobStatus == JS_WaitMedia || jcr->JobStatus == JS_WaitMount)) {
1662                   waiting = true;
1663                   break;
1664                }
1665             }
1666             endeach_jcr(jcr);
1667             if (waiting) {
1668                break;
1669             }
1670             if (stop_time && (time(NULL) >= stop_time)) {
1671                ua->warning_msg(_("Wait on mount timed out\n"));
1672                return 1;
1673             }
1674             bmicrosleep(1, 0);
1675          }
1676          return 1;
1677       }
1678    }
1679
1680    if (jobid == 0) {
1681       ua->error_msg(_("ERR: Job was not found\n"));
1682       return 1 ;
1683    }
1684
1685    /*
1686     * We wait the end of a specific job
1687     */
1688
1689    bmicrosleep(0, 200000);            /* let job actually start */
1690    for (bool running=true; running; ) {
1691       running = false;
1692
1693       jcr=get_jcr_by_id(jobid) ;
1694
1695       if (jcr) {
1696          running = true ;
1697          free_jcr(jcr);
1698       }
1699
1700       if (running) {
1701          bmicrosleep(1, 0);
1702       }
1703    }
1704
1705    /*
1706     * We have to get JobStatus
1707     */
1708
1709    int status ;
1710    char jobstatus = '?';        /* Unknown by default */
1711    char buf[256] ;
1712
1713    bsnprintf(buf, sizeof(buf),
1714              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1715
1716
1717    db_sql_query(ua->db, buf,
1718                 status_handler, (void *)&jobstatus);
1719
1720    switch (jobstatus) {
1721    case JS_Error:
1722       status = 1 ;         /* Warning */
1723       break;
1724
1725    case JS_FatalError:
1726    case JS_ErrorTerminated:
1727    case JS_Canceled:
1728       status = 2 ;         /* Critical */
1729       break;
1730
1731    case JS_Terminated:
1732       status = 0 ;         /* Ok */
1733       break;
1734
1735    default:
1736       status = 3 ;         /* Unknown */
1737       break;
1738    }
1739
1740    ua->send_msg("JobId=%i\n", jobid) ;
1741    ua->send_msg("JobStatus=%s (%c)\n", 
1742             job_status_to_str(jobstatus), 
1743             jobstatus) ;
1744
1745    if (ua->gui || ua->api) {
1746       ua->send_msg("ExitStatus=%i\n", status) ;
1747    }
1748
1749    return 1;
1750 }
1751
1752
1753 static int help_cmd(UAContext *ua, const char *cmd)
1754 {
1755    unsigned int i;
1756
1757    ua->send_msg(_("  Command    Description\n  =======    ===========\n"));
1758    for (i=0; i<comsize; i++) {
1759       ua->send_msg(_("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1760    }
1761    ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1762    return 1;
1763 }
1764
1765 int qhelp_cmd(UAContext *ua, const char *cmd)
1766 {
1767    unsigned int i;
1768
1769    for (i=0; i<comsize; i++) {
1770       ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1771    }
1772    return 1;
1773 }
1774
1775 #if 1 
1776 static int version_cmd(UAContext *ua, const char *cmd)
1777 {
1778    ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1779             HOST_OS, DISTNAME, DISTVER);
1780    return 1;
1781 }
1782 #else
1783 /*
1784  *  Test code -- turned on only for debug testing 
1785  */
1786 static int version_cmd(UAContext *ua, const char *cmd)
1787 {
1788    dbid_list ids;
1789    POOL_MEM query(PM_MESSAGE);
1790    open_db(ua);
1791    Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1792    db_get_query_dbids(ua->jcr, ua->db, query, ids);
1793    ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1794    for (int i=0; i < ids.num_ids; i++) {
1795       ua->send_msg("id=%d\n", ids.DBId[i]);
1796    }
1797    close_db(ua);
1798    return 1;
1799 }
1800 #endif
1801
1802 /* 
1803  * This call explicitly checks for a catalog=xxx and
1804  *  if given, opens that catalog.  It also checks for
1805  *  client=xxx and if found, opens the catalog 
1806  *  corresponding to that client. If we still don't 
1807  *  have a catalog, look for a Job keyword and get the
1808  *  catalog from its client record.
1809  */
1810 bool open_client_db(UAContext *ua)
1811 {
1812    int i;
1813    CAT *catalog;
1814    CLIENT *client;
1815    JOB *job;
1816
1817    /* Try for catalog keyword */
1818    i = find_arg_with_value(ua, NT_("catalog"));
1819    if (i >= 0) {
1820       if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1821          ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1822          return false;
1823       }
1824       catalog = GetCatalogResWithName(ua->argv[i]);
1825       if (catalog) {
1826          if (ua->catalog && ua->catalog != catalog) {
1827             close_db(ua);
1828          }
1829          ua->catalog = catalog;
1830          return open_db(ua);
1831       }
1832    }
1833
1834    /* Try for client keyword */
1835    i = find_arg_with_value(ua, NT_("client"));
1836    if (i >= 0) {
1837       if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1838          ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1839          return false;
1840       }
1841       client = GetClientResWithName(ua->argv[i]);
1842       if (client) {
1843          catalog = client->catalog;
1844          if (ua->catalog && ua->catalog != catalog) {
1845             close_db(ua);
1846          }
1847          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1848             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1849             return false;
1850          }
1851          ua->catalog = catalog;
1852          return open_db(ua);
1853       }
1854    }
1855
1856    /* Try for Job keyword */
1857    i = find_arg_with_value(ua, NT_("job"));
1858    if (i >= 0) {
1859       if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1860          ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1861          return false;
1862       }
1863       job = GetJobResWithName(ua->argv[i]);
1864       if (job) {
1865          catalog = job->client->catalog;
1866          if (ua->catalog && ua->catalog != catalog) {
1867             close_db(ua);
1868          }
1869          if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1870             ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1871             return false;
1872          }
1873          ua->catalog = catalog;
1874          return open_db(ua);
1875       }
1876    }
1877
1878    return open_db(ua);
1879 }
1880
1881
1882 /*
1883  * Open the catalog database.
1884  */
1885 bool open_db(UAContext *ua)
1886 {
1887    if (ua->db) {
1888       return true;
1889    }
1890    if (!ua->catalog) {
1891       ua->catalog = get_catalog_resource(ua);
1892       if (!ua->catalog) {
1893          ua->error_msg( _("Could not find a Catalog resource\n"));
1894          return false;
1895       }
1896    }
1897
1898    ua->jcr->catalog = ua->catalog;
1899
1900    Dmsg0(100, "UA Open database\n");
1901    ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name, 
1902                              ua->catalog->db_user,
1903                              ua->catalog->db_password, ua->catalog->db_address,
1904                              ua->catalog->db_port, ua->catalog->db_socket,
1905                              ua->catalog->mult_db_connections);
1906    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1907       ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1908                  ua->catalog->db_name);
1909       if (ua->db) {
1910          ua->error_msg("%s", db_strerror(ua->db));
1911       }
1912       close_db(ua);
1913       return false;
1914    }
1915    ua->jcr->db = ua->db;
1916    if (!ua->api) {
1917       ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name()); 
1918    }
1919    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1920    return true;
1921 }
1922
1923 void close_db(UAContext *ua)
1924 {
1925    if (ua->db) {
1926       db_close_database(ua->jcr, ua->db);
1927       ua->db = NULL;
1928       if (ua->jcr) {
1929          ua->jcr->db = NULL;
1930       }
1931    }
1932 }