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