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