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