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