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