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