]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
kes Enable disk seeking on restore.
[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_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_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->hdr.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_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->hdr.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->hdr.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->hdr.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->hdr.name)) {
616       bsendmsg(ua, _("Unauthorized command from this console.\n"));
617       return 1;
618    }
619    LockRes();
620    client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->hdr.name);
621
622    if (!client) {
623       bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->hdr.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->hdr.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->hdr.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->hdr.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->hdr.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_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_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             continue;
999          }
1000       }
1001       if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1002          if (ua->argv[i]) {
1003             fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1004             continue;
1005          }
1006       }
1007       if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1008          listing = 1;
1009          continue;
1010       }
1011       if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1012          if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1013             bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1014          }
1015          continue;
1016       }
1017    }
1018    if (!job && !(client && fileset)) {
1019       if (!(job = select_job_resource(ua))) {
1020          return 1;
1021       }
1022    }
1023    if (!job) {
1024       job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1025       if (!job) {
1026          bsendmsg(ua, _("No job specified.\n"));
1027          return 1;
1028       }
1029    }
1030    if (!client) {
1031       client = job->client;
1032    }
1033    if (!fileset) {
1034       fileset = job->fileset;
1035    }
1036    jcr->client = client;
1037    jcr->fileset = fileset;
1038    close_db(ua);
1039    ua->catalog = client->catalog;
1040
1041    if (!open_db(ua)) {
1042       return 1;
1043    }
1044
1045    jcr->job = job;
1046    jcr->JobType = JT_BACKUP;
1047    init_jcr_job_record(jcr);
1048
1049    if (!get_or_create_client_record(jcr)) {
1050       return 1;
1051    }
1052    if (!get_or_create_fileset_record(jcr)) {
1053       return 1;
1054    }
1055
1056    get_level_since_time(ua->jcr, since, sizeof(since));
1057
1058    bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1059       job->client->hdr.name, job->client->address, job->client->FDport);
1060    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1061       bsendmsg(ua, _("Failed to connect to Client.\n"));
1062       return 1;
1063    }
1064
1065    if (!send_include_list(jcr)) {
1066       bsendmsg(ua, _("Error sending include list.\n"));
1067       goto bail_out;
1068    }
1069
1070    if (!send_exclude_list(jcr)) {
1071       bsendmsg(ua, _("Error sending exclude list.\n"));
1072       goto bail_out;
1073    }
1074
1075    if (!send_level_command(jcr)) {
1076       goto bail_out;
1077    }
1078
1079    bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1080    while (bnet_recv(jcr->file_bsock) >= 0) {
1081       bsendmsg(ua, "%s", jcr->file_bsock->msg);
1082    }
1083
1084 bail_out:
1085    if (jcr->file_bsock) {
1086       bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1087       bnet_close(jcr->file_bsock);
1088       jcr->file_bsock = NULL;
1089    }
1090    return 1;
1091 }
1092
1093
1094 /*
1095  * print time
1096  */
1097 static int time_cmd(UAContext *ua, const char *cmd)
1098 {
1099    char sdt[50];
1100    time_t ttime = time(NULL);
1101    struct tm tm;
1102    (void)localtime_r(&ttime, &tm);
1103    strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1104    bsendmsg(ua, "%s\n", sdt);
1105    return 1;
1106 }
1107
1108 /*
1109  * reload the conf file
1110  */
1111 extern "C" void reload_config(int sig);
1112
1113 static int reload_cmd(UAContext *ua, const char *cmd)
1114 {
1115    reload_config(1);
1116    return 1;
1117 }
1118
1119 /*
1120  * Delete Pool records (should purge Media with it).
1121  *
1122  *  delete pool=<pool-name>
1123  *  delete volume pool=<pool-name> volume=<name>
1124  *  delete jobid=xxx
1125  */
1126 static int delete_cmd(UAContext *ua, const char *cmd)
1127 {
1128    static const char *keywords[] = {
1129       NT_("volume"),
1130       NT_("pool"),
1131       NT_("jobid"),
1132       NULL};
1133
1134    if (!open_db(ua)) {
1135       return 1;
1136    }
1137
1138    switch (find_arg_keyword(ua, keywords)) {
1139    case 0:
1140       delete_volume(ua);
1141       return 1;
1142    case 1:
1143       delete_pool(ua);
1144       return 1;
1145    case 2:
1146       int i;
1147       while ((i=find_arg(ua, "jobid")) > 0) {
1148          delete_job(ua);
1149          *ua->argk[i] = 0;         /* zap keyword already visited */
1150       }
1151       return 1;
1152    default:
1153       break;
1154    }
1155
1156    bsendmsg(ua, _(
1157 "In general it is not a good idea to delete either a\n"
1158 "Pool or a Volume since they may contain data.\n\n"));
1159
1160    switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1161    case 0:
1162       delete_volume(ua);
1163       break;
1164    case 1:
1165       delete_pool(ua);
1166       break;
1167    case 2:
1168       delete_job(ua);
1169       return 1;
1170    default:
1171       bsendmsg(ua, _("Nothing done.\n"));
1172       break;
1173    }
1174    return 1;
1175 }
1176
1177
1178 /*
1179  * delete_job has been modified to parse JobID lists like the
1180  * following:
1181  * delete JobID=3,4,6,7-11,14
1182  *
1183  * Thanks to Phil Stracchino for the above addition.
1184  */
1185
1186 static void delete_job(UAContext *ua)
1187 {
1188    JobId_t JobId;
1189    char *s,*sep,*tok;
1190
1191    int i = find_arg_with_value(ua, NT_("jobid"));
1192    if (i >= 0) {
1193       if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1194         s = bstrdup(ua->argv[i]);
1195         tok = s;
1196         /*
1197          * We could use strtok() here.  But we're not going to, because:
1198          * (a) strtok() is deprecated, having been replaced by strsep();
1199          * (b) strtok() is broken in significant ways.
1200          * we could use strsep() instead, but it's not universally available.
1201          * so we grow our own using strchr().
1202          */
1203         sep = strchr(tok, ',');
1204         while (sep != NULL) {
1205            *sep = '\0';
1206            if (strchr(tok, '-')) {
1207                delete_job_id_range(ua, tok);
1208            } else {
1209               JobId = str_to_int64(tok);
1210               do_job_delete(ua, JobId);
1211            }
1212            tok = ++sep;
1213            sep = strchr(tok, ',');
1214         }
1215         /* pick up the last token */
1216         if (strchr(tok, '-')) {
1217             delete_job_id_range(ua, tok);
1218         } else {
1219             JobId = str_to_int64(tok);
1220             do_job_delete(ua, JobId);
1221         }
1222
1223          free(s);
1224       } else {
1225          JobId = str_to_int64(ua->argv[i]);
1226         do_job_delete(ua, JobId);
1227       }
1228    } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1229       return;
1230    } else {
1231       JobId = ua->int64_val;
1232       do_job_delete(ua, JobId);
1233    }
1234 }
1235
1236 /*
1237  * we call delete_job_id_range to parse range tokens and iterate over ranges
1238  */
1239 static void delete_job_id_range(UAContext *ua, char *tok)
1240 {
1241    char *tok2;
1242    JobId_t j,j1,j2;
1243
1244    tok2 = strchr(tok, '-');
1245    *tok2 = '\0';
1246    tok2++;
1247    j1 = str_to_int64(tok);
1248    j2 = str_to_int64(tok2);
1249    for (j=j1; j<=j2; j++) {
1250       do_job_delete(ua, j);
1251    }
1252 }
1253
1254 /*
1255  * do_job_delete now performs the actual delete operation atomically
1256  */
1257
1258 static void do_job_delete(UAContext *ua, JobId_t JobId)
1259 {
1260    POOL_MEM query(PM_MESSAGE);
1261    char ed1[50];
1262
1263    purge_files_from_job(ua, JobId);
1264    purge_job_from_catalog(ua, JobId);
1265    bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1266 }
1267
1268 /*
1269  * Delete media records from database -- dangerous
1270  */
1271 static int delete_volume(UAContext *ua)
1272 {
1273    MEDIA_DBR mr;
1274
1275    if (!select_media_dbr(ua, &mr)) {
1276       return 1;
1277    }
1278    bsendmsg(ua, _("\nThis command will delete volume %s\n"
1279       "and all Jobs saved on that volume from the Catalog\n"),
1280       mr.VolumeName);
1281
1282    if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1283       return 1;
1284    }
1285    if (ua->pint32_val) {
1286       db_delete_media_record(ua->jcr, ua->db, &mr);
1287    }
1288    return 1;
1289 }
1290
1291 /*
1292  * Delete a pool record from the database -- dangerous
1293  */
1294 static int delete_pool(UAContext *ua)
1295 {
1296    POOL_DBR  pr;
1297
1298    memset(&pr, 0, sizeof(pr));
1299
1300    if (!get_pool_dbr(ua, &pr)) {
1301       return 1;
1302    }
1303    if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1304       return 1;
1305    }
1306    if (ua->pint32_val) {
1307       db_delete_pool_record(ua->jcr, ua->db, &pr);
1308    }
1309    return 1;
1310 }
1311
1312
1313 static void do_mount_cmd(UAContext *ua, const char *command)
1314 {
1315    USTORE store;
1316    BSOCK *sd;
1317    JCR *jcr = ua->jcr;
1318    char dev_name[MAX_NAME_LENGTH];
1319    int drive;
1320    int slot = -1;
1321
1322    if (!open_db(ua)) {
1323       return;
1324    }
1325    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1326
1327    store.store = get_storage_resource(ua, true/*arg is storage*/);
1328    pm_strcpy(store.store_source, _("unknown source"));
1329    if (!store.store) {
1330       return;
1331    }
1332    set_wstorage(jcr, &store);
1333    drive = get_storage_drive(ua, store.store);
1334    if (strcmp(command, "mount") == 0) {
1335       slot = get_storage_slot(ua, store.store);
1336    }
1337
1338    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1339       store.store->media_type, store.store->dev_name(), drive);
1340
1341    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1342       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1343       return;
1344    }
1345    sd = jcr->store_bsock;
1346    bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1347    bash_spaces(dev_name);
1348    if (slot > 0) {
1349       bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1350    } else {
1351       bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1352    }
1353    while (bnet_recv(sd) >= 0) {
1354       bsendmsg(ua, "%s", sd->msg);
1355    }
1356    bnet_sig(sd, BNET_TERMINATE);
1357    bnet_close(sd);
1358    jcr->store_bsock = NULL;
1359 }
1360
1361 /*
1362  * mount [storage=<name>] [drive=nn] [slot=mm]
1363  */
1364 static int mount_cmd(UAContext *ua, const char *cmd)
1365 {
1366    do_mount_cmd(ua, "mount");          /* mount */
1367    return 1;
1368 }
1369
1370
1371 /*
1372  * unmount [storage=<name>] [drive=nn]
1373  */
1374 static int unmount_cmd(UAContext *ua, const char *cmd)
1375 {
1376    do_mount_cmd(ua, "unmount");          /* unmount */
1377    return 1;
1378 }
1379
1380
1381 /*
1382  * release [storage=<name>] [drive=nn]
1383  */
1384 static int release_cmd(UAContext *ua, const char *cmd)
1385 {
1386    do_mount_cmd(ua, "release");          /* release */
1387    return 1;
1388 }
1389
1390
1391 /*
1392  * Switch databases
1393  *   use catalog=<name>
1394  */
1395 static int use_cmd(UAContext *ua, const char *cmd)
1396 {
1397    CAT *oldcatalog, *catalog;
1398
1399
1400    close_db(ua);                      /* close any previously open db */
1401    oldcatalog = ua->catalog;
1402
1403    if (!(catalog = get_catalog_resource(ua))) {
1404       ua->catalog = oldcatalog;
1405    } else {
1406       ua->catalog = catalog;
1407    }
1408    if (open_db(ua)) {
1409       bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1410          ua->catalog->hdr.name, ua->catalog->db_name);
1411    }
1412    return 1;
1413 }
1414
1415 int quit_cmd(UAContext *ua, const char *cmd)
1416 {
1417    ua->quit = TRUE;
1418    return 1;
1419 }
1420
1421 /* Handler to get job status */
1422 static int status_handler(void *ctx, int num_fields, char **row)
1423 {
1424    char *val = (char *)ctx;
1425
1426    if (row[0]) {
1427       *val = row[0][0];
1428    } else {
1429       *val = '?';               /* Unknown by default */
1430    }
1431
1432    return 0;
1433 }
1434
1435 /*
1436  * Wait until no job is running
1437  */
1438 int wait_cmd(UAContext *ua, const char *cmd)
1439 {
1440    JCR *jcr;
1441
1442    /* no args
1443     * Wait until no job is running
1444     */
1445    if (ua->argc == 1) {
1446       bmicrosleep(0, 200000);            /* let job actually start */
1447       for (bool running=true; running; ) {
1448          running = false;
1449          foreach_jcr(jcr) {
1450             if (jcr->JobId != 0) {
1451                running = true;
1452                break;
1453             }
1454          }
1455          endeach_jcr(jcr);
1456
1457          if (running) {
1458             bmicrosleep(1, 0);
1459          }
1460       }
1461       return 1;
1462    }
1463
1464    /* we have jobid, jobname or ujobid argument */
1465
1466    uint32_t jobid = 0 ;
1467
1468    if (!open_db(ua)) {
1469       bsendmsg(ua, _("ERR: Can't open db\n")) ;
1470       return 1;
1471    }
1472
1473    for (int i=1; i<ua->argc; i++) {
1474       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1475          if (!ua->argv[i]) {
1476             break;
1477          }
1478          jobid = str_to_int64(ua->argv[i]);
1479          break;
1480       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1481                  strcasecmp(ua->argk[i], "job") == 0) {
1482          if (!ua->argv[i]) {
1483             break;
1484          }
1485          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1486          if (jcr) {
1487             jobid = jcr->JobId ;
1488             free_jcr(jcr);
1489          }
1490          break;
1491       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1492          if (!ua->argv[i]) {
1493             break;
1494          }
1495          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1496          if (jcr) {
1497             jobid = jcr->JobId ;
1498             free_jcr(jcr);
1499          }
1500          break;
1501       }
1502    }
1503
1504    if (jobid == 0) {
1505       bsendmsg(ua, _("ERR: Job was not found\n"));
1506       return 1 ;
1507    }
1508
1509    /*
1510     * We wait the end of job
1511     */
1512
1513    bmicrosleep(0, 200000);            /* let job actually start */
1514    for (bool running=true; running; ) {
1515       running = false;
1516
1517       jcr=get_jcr_by_id(jobid) ;
1518
1519       if (jcr) {
1520          running = true ;
1521          free_jcr(jcr);
1522       }
1523
1524       if (running) {
1525          bmicrosleep(1, 0);
1526       }
1527    }
1528
1529    /*
1530     * We have to get JobStatus
1531     */
1532
1533    int status ;
1534    char jobstatus = '?';        /* Unknown by default */
1535    char buf[256] ;
1536
1537    bsnprintf(buf, sizeof(buf),
1538              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1539
1540
1541    db_sql_query(ua->db, buf,
1542                 status_handler, (void *)&jobstatus);
1543
1544    switch (jobstatus) {
1545    case JS_Error:
1546       status = 1 ;         /* Warning */
1547       break;
1548
1549    case JS_FatalError:
1550    case JS_ErrorTerminated:
1551    case JS_Canceled:
1552       status = 2 ;         /* Critical */
1553       break;
1554
1555    case JS_Terminated:
1556       status = 0 ;         /* Ok */
1557       break;
1558
1559    default:
1560       status = 3 ;         /* Unknown */
1561       break;
1562    }
1563
1564    bsendmsg(ua, "JobId=%i\n", jobid) ;
1565    bsendmsg(ua, "JobStatus=%s (%c)\n", 
1566             job_status_to_str(jobstatus), 
1567             jobstatus) ;
1568
1569    if (ua->gui) {
1570       bsendmsg(ua, "ExitStatus=%i\n", status) ;
1571    }
1572
1573    return 1;
1574 }
1575
1576
1577 static int help_cmd(UAContext *ua, const char *cmd)
1578 {
1579    unsigned int i;
1580
1581    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
1582    for (i=0; i<comsize; i++) {
1583       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1584    }
1585    bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1586    return 1;
1587 }
1588
1589 int qhelp_cmd(UAContext *ua, const char *cmd)
1590 {
1591    unsigned int i;
1592
1593    for (i=0; i<comsize; i++) {
1594       bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1595    }
1596    return 1;
1597 }
1598
1599 static int version_cmd(UAContext *ua, const char *cmd)
1600 {
1601    bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1602    return 1;
1603 }
1604
1605
1606 /*
1607  * Open the catalog database.
1608  */
1609 bool open_db(UAContext *ua)
1610 {
1611    if (ua->db) {
1612       return true;
1613    }
1614    if (!ua->catalog) {
1615       ua->catalog = get_catalog_resource(ua);
1616       if (!ua->catalog) {
1617          bsendmsg(ua, _("Could not find a Catalog resource\n"));
1618          return false;
1619       }
1620    }
1621
1622    ua->jcr->catalog = ua->catalog;
1623
1624    Dmsg0(150, "Open database\n");
1625    ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1626                              ua->catalog->db_password, ua->catalog->db_address,
1627                              ua->catalog->db_port, ua->catalog->db_socket,
1628                              ua->catalog->mult_db_connections);
1629    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1630       bsendmsg(ua, _("Could not open database \"%s\".\n"),
1631                  ua->catalog->db_name);
1632       if (ua->db) {
1633          bsendmsg(ua, "%s", db_strerror(ua->db));
1634       }
1635       close_db(ua);
1636       return false;
1637    }
1638    ua->jcr->db = ua->db;
1639    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1640    return true;
1641 }
1642
1643 void close_db(UAContext *ua)
1644 {
1645    if (ua->db) {
1646       db_close_database(ua->jcr, ua->db);
1647       ua->db = NULL;
1648       if (ua->jcr) {
1649          ua->jcr->db = NULL;
1650       }
1651    }
1652 }