]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_cmds.c
This commit was manufactured by cvs2svn to create tag
[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  { N_("add"),        add_cmd,         _("add media to a pool")},
91  { N_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
92  { N_("automount"),   automount_cmd,  _("automount [on|off] -- after label")},
93  { N_("cancel"),     cancel_cmd,    _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
94  { N_("create"),     create_cmd,    _("create DB Pool from resource")},
95  { N_("delete"),     delete_cmd,    _("delete [pool=<pool-name> | media volume=<volume-name>]")},
96  { N_("disable"),    disable_cmd,   _("disable <job=name> -- disable a job")},
97  { N_("enable"),     enable_cmd,    _("enable <job=name> -- enable a job")},
98  { N_("estimate"),   estimate_cmd,  _("performs FileSet estimate, listing gives full listing")},
99  { N_("exit"),       quit_cmd,      _("exit = quit")},
100  { N_("gui"),        gui_cmd,       _("gui [on|off] -- non-interactive gui mode")},
101  { N_("help"),       help_cmd,      _("print this command")},
102  { N_("list"),       list_cmd,      _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
103  { N_("label"),      label_cmd,     _("label a tape")},
104  { N_("llist"),      llist_cmd,     _("full or long list like list command")},
105  { N_("messages"),   messagescmd,   _("messages")},
106  { N_("mount"),      mount_cmd,     _("mount <storage-name>")},
107  { N_("prune"),      prunecmd,      _("prune expired records from catalog")},
108  { N_("purge"),      purgecmd,      _("purge records from catalog")},
109  { N_("python"),     python_cmd,    _("python control commands")},
110  { N_("quit"),       quit_cmd,      _("quit")},
111  { N_("query"),      querycmd,      _("query catalog")},
112  { N_("restore"),    restore_cmd,   _("restore files")},
113  { N_("relabel"),    relabel_cmd,   _("relabel a tape")},
114  { N_("release"),    release_cmd,   _("release <storage-name>")},
115  { N_("reload"),     reload_cmd,    _("reload conf file")},
116  { N_("run"),        run_cmd,       _("run <job-name>")},
117  { N_("status"),     status_cmd,    _("status [storage | client]=<name>")},
118  { N_("setdebug"),   setdebug_cmd,  _("sets debug level")},
119  { N_("setip"),      setip_cmd,     _("sets new client address -- if authorized")},
120  { N_("show"),       show_cmd,      _("show (resource records) [jobs | pools | ... | all]")},
121  { N_("sqlquery"),   sqlquerycmd,   _("use SQL to query catalog")},
122  { N_("time"),       time_cmd,      _("print current time")},
123  { N_("trace"),      trace_cmd,     _("turn on/off trace to file")},
124  { N_("unmount"),    unmount_cmd,   _("unmount <storage-name>")},
125  { N_("umount"),     unmount_cmd,   _("umount <storage-name> for old-time Unix guys")},
126  { N_("update"),     update_cmd,    _("update Volume, Pool or slots")},
127  { N_("use"),        use_cmd,       _("use catalog xxx")},
128  { N_("var"),        var_cmd,       _("does variable expansion")},
129  { N_("version"),    version_cmd,   _("print Director version")},
130  { N_("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], "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, "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 }
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       Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
320       if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
321          bsendmsg(ua, "%s", db_strerror(ua->db));
322          return 1;
323       }
324       if (i == startnum) {
325          first_id = mr.PoolId;
326       }
327    }
328    pr.NumVols += num;
329    Dmsg0(200, "Update pool record.\n");
330    if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
331       bsendmsg(ua, "%s", db_strerror(ua->db));
332       return 1;
333    }
334    bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
335
336    return 1;
337 }
338
339 /*
340  * Turn auto mount on/off
341  *
342  *  automount on
343  *  automount off
344  */
345 int automount_cmd(UAContext *ua, const char *cmd)
346 {
347    char *onoff;
348
349    if (ua->argc != 2) {
350       if (!get_cmd(ua, _("Turn on or off? "))) {
351             return 1;
352       }
353       onoff = ua->cmd;
354    } else {
355       onoff = ua->argk[1];
356    }
357
358    ua->automount = (strcasecmp(onoff, _("off")) == 0) ? 0 : 1;
359    return 1;
360 }
361
362
363 /*
364  * Cancel a job
365  */
366 static int cancel_cmd(UAContext *ua, const char *cmd)
367 {
368    int i, ret;
369    int njobs = 0;
370    JCR *jcr = NULL;
371    char JobName[MAX_NAME_LENGTH];
372
373    if (!open_db(ua)) {
374       return 1;
375    }
376
377    for (i=1; i<ua->argc; i++) {
378       if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
379          uint32_t JobId;
380          if (!ua->argv[i]) {
381             break;
382          }
383          JobId = str_to_int64(ua->argv[i]);
384          if (!(jcr=get_jcr_by_id(JobId))) {
385             bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"),  ua->argv[i]);
386             return 1;
387          }
388          break;
389       } else if (strcasecmp(ua->argk[i], _("job")) == 0) {
390          if (!ua->argv[i]) {
391             break;
392          }
393          if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
394             bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
395             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
396             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
397          }
398          break;
399       } else if (strcasecmp(ua->argk[i], _("ujobid")) == 0) {
400          if (!ua->argv[i]) {
401             break;
402          }
403          if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
404             bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
405             jcr = new_jcr(sizeof(JCR), dird_free_jcr);
406             bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
407          }
408          break;
409       }
410
411    }
412    /* If we still do not have a jcr,
413     *   throw up a list and ask the user to select one.
414     */
415    if (!jcr) {
416       char buf[1000];
417       /* Count Jobs running */
418       foreach_jcr(jcr) {
419          if (jcr->JobId == 0) {      /* this is us */
420             continue;
421          }
422          njobs++;
423       }
424       endeach_jcr(jcr);
425
426       if (njobs == 0) {
427          bsendmsg(ua, _("No Jobs running.\n"));
428          return 1;
429       }
430       start_prompt(ua, _("Select Job:\n"));
431       foreach_jcr(jcr) {
432          char ed1[50];
433          if (jcr->JobId == 0) {      /* this is us */
434             continue;
435          }
436          bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
437          add_prompt(ua, buf);
438       }
439       endeach_jcr(jcr);
440
441       if (do_prompt(ua, _("Job"),  _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
442          return 1;
443       }
444       if (njobs == 1) {
445          if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
446             return 1;
447          }
448       }
449       sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
450       jcr = get_jcr_by_full_name(JobName);
451       if (!jcr) {
452          bsendmsg(ua, _("Job %s not found.\n"), JobName);
453          return 1;
454       }
455    }
456
457    ret = cancel_job(ua, jcr);
458    free_jcr(jcr);
459    return ret;
460 }
461
462 /*
463  * This is a common routine to create or update a
464  *   Pool DB base record from a Pool Resource. We handle
465  *   the setting of MaxVols and NumVols slightly differently
466  *   depending on if we are creating the Pool or we are
467  *   simply bringing it into agreement with the resource (updage).
468  */
469 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
470 {
471    bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
472    if (op == POOL_OP_CREATE) {
473       pr->MaxVols = pool->max_volumes;
474       pr->NumVols = 0;
475    } else {          /* update pool */
476       if (pr->MaxVols != pool->max_volumes) {
477          pr->MaxVols = pool->max_volumes;
478       }
479       if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
480          pr->MaxVols = pr->NumVols;
481       }
482    }
483    pr->LabelType = pool->LabelType;
484    pr->UseOnce = pool->use_volume_once;
485    pr->UseCatalog = pool->use_catalog;
486    pr->AcceptAnyVolume = pool->accept_any_volume;
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 >= 1 && strcasecmp(ua->argk[1], _("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, N_("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_storage(jcr, store);
676    /* Try connecting for up to 15 seconds */
677    bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
678       store->hdr.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], "store") == 0 ||
867           strcasecmp(ua->argk[i], "storage") == 0 ||
868           strcasecmp(ua->argk[i], "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, _("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], "client") == 0 ||
973           strcasecmp(ua->argk[i], "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], "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], "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], "listing") == 0) {
992          listing = 1;
993          continue;
994       }
995       if (strcasecmp(ua->argk[i], "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    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       N_("volume"),
1114       N_("pool"),
1115       N_("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, N_("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  * we always return 1 because C++ is pissy about void functions
1241  */
1242
1243 static void do_job_delete(UAContext *ua, JobId_t JobId)
1244 {
1245    POOLMEM *query = get_pool_memory(PM_MESSAGE);
1246    char ed1[50];
1247
1248    Mmsg(query, "DELETE FROM Job WHERE JobId=%s", edit_int64(JobId, ed1));
1249    db_sql_query(ua->db, query, NULL, (void *)NULL);
1250    Mmsg(query, "DELETE FROM File WHERE JobId=%s", ed1);
1251    db_sql_query(ua->db, query, NULL, (void *)NULL);
1252    Mmsg(query, "DELETE FROM JobMedia WHERE JobId=%s", ed1);
1253    db_sql_query(ua->db, query, NULL, (void *)NULL);
1254    free_pool_memory(query);
1255    bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1256 }
1257
1258 /*
1259  * Delete media records from database -- dangerous
1260  */
1261 static int delete_volume(UAContext *ua)
1262 {
1263    MEDIA_DBR mr;
1264
1265    if (!select_media_dbr(ua, &mr)) {
1266       return 1;
1267    }
1268    bsendmsg(ua, _("\nThis command will delete volume %s\n"
1269       "and all Jobs saved on that volume from the Catalog\n"),
1270       mr.VolumeName);
1271
1272    if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1273       return 1;
1274    }
1275    if (ua->pint32_val) {
1276       db_delete_media_record(ua->jcr, ua->db, &mr);
1277    }
1278    return 1;
1279 }
1280
1281 /*
1282  * Delete a pool record from the database -- dangerous
1283  */
1284 static int delete_pool(UAContext *ua)
1285 {
1286    POOL_DBR  pr;
1287
1288    memset(&pr, 0, sizeof(pr));
1289
1290    if (!get_pool_dbr(ua, &pr)) {
1291       return 1;
1292    }
1293    if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1294       return 1;
1295    }
1296    if (ua->pint32_val) {
1297       db_delete_pool_record(ua->jcr, ua->db, &pr);
1298    }
1299    return 1;
1300 }
1301
1302
1303 static void do_mount_cmd(UAContext *ua, const char *command)
1304 {
1305    STORE *store;
1306    BSOCK *sd;
1307    JCR *jcr = ua->jcr;
1308    char dev_name[MAX_NAME_LENGTH];
1309    int drive;
1310
1311    if (!open_db(ua)) {
1312       return;
1313    }
1314    Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1315
1316    store = get_storage_resource(ua, true/*arg is storage*/);
1317    if (!store) {
1318       return;
1319    }
1320    set_storage(jcr, store);
1321    drive = get_storage_drive(ua, store);
1322
1323    Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1324       store->media_type, store->dev_name(), drive);
1325
1326    if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1327       bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1328       return;
1329    }
1330    sd = jcr->store_bsock;
1331    bstrncpy(dev_name, store->dev_name(), sizeof(dev_name));
1332    bash_spaces(dev_name);
1333    bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1334    while (bnet_recv(sd) >= 0) {
1335       bsendmsg(ua, "%s", sd->msg);
1336    }
1337    bnet_sig(sd, BNET_TERMINATE);
1338    bnet_close(sd);
1339    jcr->store_bsock = NULL;
1340 }
1341
1342 /*
1343  * mount [storage=<name>] [drive=nn]
1344  */
1345 static int mount_cmd(UAContext *ua, const char *cmd)
1346 {
1347    do_mount_cmd(ua, "mount");          /* mount */
1348    return 1;
1349 }
1350
1351
1352 /*
1353  * unmount [storage=<name>] [drive=nn]
1354  */
1355 static int unmount_cmd(UAContext *ua, const char *cmd)
1356 {
1357    do_mount_cmd(ua, "unmount");          /* unmount */
1358    return 1;
1359 }
1360
1361
1362 /*
1363  * release [storage=<name>] [drive=nn]
1364  */
1365 static int release_cmd(UAContext *ua, const char *cmd)
1366 {
1367    do_mount_cmd(ua, "release");          /* release */
1368    return 1;
1369 }
1370
1371
1372 /*
1373  * Switch databases
1374  *   use catalog=<name>
1375  */
1376 static int use_cmd(UAContext *ua, const char *cmd)
1377 {
1378    CAT *oldcatalog, *catalog;
1379
1380
1381    close_db(ua);                      /* close any previously open db */
1382    oldcatalog = ua->catalog;
1383
1384    if (!(catalog = get_catalog_resource(ua))) {
1385       ua->catalog = oldcatalog;
1386    } else {
1387       ua->catalog = catalog;
1388    }
1389    if (open_db(ua)) {
1390       bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1391          ua->catalog->hdr.name, ua->catalog->db_name);
1392    }
1393    return 1;
1394 }
1395
1396 int quit_cmd(UAContext *ua, const char *cmd)
1397 {
1398    ua->quit = TRUE;
1399    return 1;
1400 }
1401
1402 /* Handler to get job status */
1403 static int status_handler(void *ctx, int num_fields, char **row)
1404 {
1405    char *val = (char *)ctx;
1406
1407    if (row[0]) {
1408       *val = row[0][0];
1409    } else {
1410       *val = '?';               /* Unknown by default */
1411    }
1412
1413    return 0;
1414 }
1415
1416 /*
1417  * Wait until no job is running
1418  */
1419 int wait_cmd(UAContext *ua, const char *cmd)
1420 {
1421    JCR *jcr;
1422
1423    /* no args
1424     * Wait until no job is running
1425     */
1426    if (ua->argc == 1) {
1427       bmicrosleep(0, 200000);            /* let job actually start */
1428       for (bool running=true; running; ) {
1429          running = false;
1430          foreach_jcr(jcr) {
1431             if (jcr->JobId != 0) {
1432                running = true;
1433                break;
1434             }
1435          }
1436          endeach_jcr(jcr);
1437
1438          if (running) {
1439             bmicrosleep(1, 0);
1440          }
1441       }
1442       return 1;
1443    }
1444
1445    /* we have jobid, jobname or ujobid argument */
1446
1447    uint32_t jobid = 0 ;
1448
1449    if (!open_db(ua)) {
1450       bsendmsg(ua, _("ERR: Can't open db\n")) ;
1451       return 1;
1452    }
1453
1454    for (int i=1; i<ua->argc; i++) {
1455       if (strcasecmp(ua->argk[i], "jobid") == 0) {
1456          if (!ua->argv[i]) {
1457             break;
1458          }
1459          jobid = str_to_int64(ua->argv[i]);
1460          break;
1461       } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1462                  strcasecmp(ua->argk[i], "job") == 0) {
1463          if (!ua->argv[i]) {
1464             break;
1465          }
1466          jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1467          if (jcr) {
1468             jobid = jcr->JobId ;
1469             free_jcr(jcr);
1470          }
1471          break;
1472       } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1473          if (!ua->argv[i]) {
1474             break;
1475          }
1476          jcr=get_jcr_by_full_name(ua->argv[i]) ;
1477          if (jcr) {
1478             jobid = jcr->JobId ;
1479             free_jcr(jcr);
1480          }
1481          break;
1482       }
1483    }
1484
1485    if (jobid == 0) {
1486       bsendmsg(ua, _("ERR: Job was not found\n"));
1487       return 1 ;
1488    }
1489
1490    /*
1491     * We wait the end of job
1492     */
1493
1494    bmicrosleep(0, 200000);            /* let job actually start */
1495    for (bool running=true; running; ) {
1496       running = false;
1497
1498       jcr=get_jcr_by_id(jobid) ;
1499
1500       if (jcr) {
1501          running = true ;
1502          free_jcr(jcr);
1503       }
1504
1505       if (running) {
1506          bmicrosleep(1, 0);
1507       }
1508    }
1509
1510    /*
1511     * We have to get JobStatus
1512     */
1513
1514    int status ;
1515    char jobstatus = '?';        /* Unknown by default */
1516    char buf[256] ;
1517
1518    bsnprintf(buf, sizeof(buf),
1519              "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1520
1521
1522    db_sql_query(ua->db, buf,
1523                 status_handler, (void *)&jobstatus);
1524
1525    switch (jobstatus) {
1526    case JS_Error:
1527       status = 1 ;         /* Warning */
1528       break;
1529
1530    case JS_FatalError:
1531    case JS_ErrorTerminated:
1532    case JS_Canceled:
1533       status = 2 ;         /* Critical */
1534       break;
1535
1536    case JS_Terminated:
1537       status = 0 ;         /* Ok */
1538       break;
1539
1540    default:
1541       status = 3 ;         /* Unknown */
1542       break;
1543    }
1544
1545    bsendmsg(ua, "JobId=%i\n", jobid) ;
1546    bsendmsg(ua, "JobStatus=%s (%c)\n", 
1547             job_status_to_str(jobstatus), 
1548             jobstatus) ;
1549
1550    if (ua->gui) {
1551       bsendmsg(ua, "ExitStatus=%i\n", status) ;
1552    }
1553
1554    return 1;
1555 }
1556
1557
1558 static int help_cmd(UAContext *ua, const char *cmd)
1559 {
1560    unsigned int i;
1561
1562    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
1563    for (i=0; i<comsize; i++) {
1564       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1565    }
1566    bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1567    return 1;
1568 }
1569
1570 int qhelp_cmd(UAContext *ua, const char *cmd)
1571 {
1572    unsigned int i;
1573
1574    for (i=0; i<comsize; i++) {
1575       bsendmsg(ua, "%s %s\n", _(commands[i].key), _(commands[i].help));
1576    }
1577    return 1;
1578 }
1579
1580 static int version_cmd(UAContext *ua, const char *cmd)
1581 {
1582    bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1583    return 1;
1584 }
1585
1586
1587 /* A bit brain damaged in that if the user has not done
1588  * a "use catalog xxx" command, we simply find the first
1589  * catalog resource and open it.
1590  */
1591 bool open_db(UAContext *ua)
1592 {
1593    if (ua->db) {
1594       return true;
1595    }
1596    if (!ua->catalog) {
1597       LockRes();
1598       ua->catalog = (CAT *)GetNextRes(R_CATALOG, NULL);
1599       UnlockRes();
1600       if (!ua->catalog) {
1601          bsendmsg(ua, _("Could not find a Catalog resource\n"));
1602          return false;
1603       } else if (!acl_access_ok(ua, Catalog_ACL, ua->catalog->hdr.name)) {
1604          bsendmsg(ua, _("You must specify a \"use <catalog-name>\" command before continuing.\n"));
1605          ua->catalog = NULL;
1606          return false;
1607       } else {
1608          bsendmsg(ua, _("Using default Catalog name=%s DB=%s\n"),
1609             ua->catalog->hdr.name, ua->catalog->db_name);
1610       }
1611    }
1612
1613    ua->jcr->catalog = ua->catalog;
1614
1615    Dmsg0(150, "Open database\n");
1616    ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1617                              ua->catalog->db_password, ua->catalog->db_address,
1618                              ua->catalog->db_port, ua->catalog->db_socket,
1619                              ua->catalog->mult_db_connections);
1620    if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1621       bsendmsg(ua, _("Could not open database \"%s\".\n"),
1622                  ua->catalog->db_name);
1623       if (ua->db) {
1624          bsendmsg(ua, "%s", db_strerror(ua->db));
1625       }
1626       close_db(ua);
1627       return false;
1628    }
1629    ua->jcr->db = ua->db;
1630    Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1631    return true;
1632 }
1633
1634 void close_db(UAContext *ua)
1635 {
1636    if (ua->db) {
1637       db_close_database(ua->jcr, ua->db);
1638       ua->db = NULL;
1639       if (ua->jcr) {
1640          ua->jcr->db = NULL;
1641       }
1642    }
1643 }