]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
f684e3b094e4e80b6c6771d1c20d703441a31a66
[bacula/bacula] / bacula / src / dird / ua_dotcmds.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Bacula Director -- User Agent Commands
21  *     These are "dot" commands, i.e. commands preceded
22  *        by a period. These commands are meant to be used
23  *        by a program, so there is no prompting, and the
24  *        returned results are (supposed to be) predictable.
25  *
26  *     Kern Sibbald, April MMII
27  */
28
29 #include "bacula.h"
30 #include "dird.h"
31 #include "cats/bvfs.h"
32 #include "findlib/find.h"
33
34 /* Imported variables */
35 extern struct s_jl joblevels[];
36 extern struct s_jt jobtypes[];
37
38 /* Imported functions */
39 extern void do_messages(UAContext *ua, const char *cmd);
40 extern int quit_cmd(UAContext *ua, const char *cmd);
41 extern int qhelp_cmd(UAContext *ua, const char *cmd);
42 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
43
44
45 /* Forward referenced functions */
46 static bool admin_cmds(UAContext *ua, const char *cmd);
47 static bool jobscmd(UAContext *ua, const char *cmd);
48 static bool dotestimatecmd(UAContext *ua, const char *cmd);
49 static bool filesetscmd(UAContext *ua, const char *cmd);
50 static bool clientscmd(UAContext *ua, const char *cmd);
51 static bool msgscmd(UAContext *ua, const char *cmd);
52 static bool poolscmd(UAContext *ua, const char *cmd);
53 static bool schedulescmd(UAContext *ua, const char *cmd);
54 static bool storagecmd(UAContext *ua, const char *cmd);
55 static bool defaultscmd(UAContext *ua, const char *cmd);
56 static bool typescmd(UAContext *ua, const char *cmd);
57 static bool tagscmd(UAContext *ua, const char *cmd);
58 static bool backupscmd(UAContext *ua, const char *cmd);
59 static bool levelscmd(UAContext *ua, const char *cmd);
60 static bool getmsgscmd(UAContext *ua, const char *cmd);
61 static bool volstatuscmd(UAContext *ua, const char *cmd);
62 static bool mediatypescmd(UAContext *ua, const char *cmd);
63 static bool locationscmd(UAContext *ua, const char *cmd);
64 static bool mediacmd(UAContext *ua, const char *cmd);
65 static bool aopcmd(UAContext *ua, const char *cmd);
66 static bool catalogscmd(UAContext *ua, const char *cmd);
67
68 static bool dot_ls_cmd(UAContext *ua, const char *cmd);
69 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd);
70 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd);
71 static bool dot_bvfs_update(UAContext *ua, const char *cmd);
72 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd);
73 static bool dot_bvfs_versions(UAContext *ua, const char *cmd);
74 static bool dot_bvfs_restore(UAContext *ua, const char *cmd);
75 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd);
76 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd);
77 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd);
78 static bool dot_bvfs_update_fv(UAContext *ua, const char *cmd);
79 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd);
80 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd);
81 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd);
82 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd);
83 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len);
84
85 static bool putfile_cmd(UAContext *ua, const char *cmd);
86 static bool api_cmd(UAContext *ua, const char *cmd);
87 static bool sql_cmd(UAContext *ua, const char *cmd);
88 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
89 static bool dot_help_cmd(UAContext *ua, const char *cmd);
90 static int one_handler(void *ctx, int num_field, char **row);
91
92 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
93 static struct cmdstruct commands[] = { /* help */  /* can be used in runscript */
94  { NT_(".api"),        api_cmd,                  NULL,       false},
95  { NT_(".backups"),    backupscmd,               NULL,       false},
96  { NT_(".clients"),    clientscmd,               NULL,       true},
97  { NT_(".catalogs"),   catalogscmd,              NULL,       false},
98  { NT_(".defaults"),   defaultscmd,              NULL,       false},
99  { NT_(".die"),        admin_cmds,               NULL,       false},
100  { NT_(".dump"),       admin_cmds,               NULL,       false},
101  { NT_(".exit"),       admin_cmds,               NULL,       false},
102  { NT_(".filesets"),   filesetscmd,              NULL,       false},
103  { NT_(".help"),       dot_help_cmd,             NULL,       false},
104  { NT_(".jobs"),       jobscmd,                  NULL,       true},
105  { NT_(".estimate"),   dotestimatecmd,           NULL,       false},
106  { NT_(".levels"),     levelscmd,                NULL,       false},
107  { NT_(".messages"),   getmsgscmd,               NULL,       false},
108  { NT_(".msgs"),       msgscmd,                  NULL,       false},
109  { NT_(".pools"),      poolscmd,                 NULL,       true},
110  { NT_(".quit"),       dot_quit_cmd,             NULL,       false},
111  { NT_(".putfile"),    putfile_cmd,              NULL,       false}, /* use @putfile */
112  { NT_(".schedule"),   schedulescmd,             NULL,       false},
113  { NT_(".sql"),        sql_cmd,                  NULL,       false},
114  { NT_(".status"),     dot_status_cmd,           NULL,       false},
115  { NT_(".storage"),    storagecmd,               NULL,       true},
116  { NT_(".volstatus"),  volstatuscmd,             NULL,       true},
117  { NT_(".media"),      mediacmd,                 NULL,       true},
118  { NT_(".mediatypes"), mediatypescmd,            NULL,       true},
119  { NT_(".locations"),  locationscmd,             NULL,       true},
120  { NT_(".actiononpurge"),aopcmd,                 NULL,       true},
121  { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs,         NULL,       true},
122  { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles,        NULL,       true},
123  { NT_(".bvfs_get_volumes"),dot_bvfs_get_volumes,NULL,       true},
124  { NT_(".bvfs_update"), dot_bvfs_update,         NULL,       true},
125  { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL,       true},
126  { NT_(".bvfs_get_jobs"), dot_bvfs_get_jobs,     NULL,       true},
127  { NT_(".bvfs_get_bootstrap"), dot_bvfs_get_bootstrap,NULL,  true},
128  { NT_(".bvfs_versions"), dot_bvfs_versions,     NULL,       true},
129  { NT_(".bvfs_get_delta"), dot_bvfs_get_delta,   NULL,       true},
130  { NT_(".bvfs_restore"),  dot_bvfs_restore,      NULL,       true},
131  { NT_(".bvfs_cleanup"),  dot_bvfs_cleanup,      NULL,       true},
132  { NT_(".bvfs_decode_lstat"),dot_bvfs_decode_lstat,NULL,     true},
133  { NT_(".bvfs_clear_cache"),dot_bvfs_clear_cache,NULL,       false},
134  { NT_(".bvfs_update_fv"),dot_bvfs_update_fv,    NULL,       true},
135  { NT_(".ls"), dot_ls_cmd,                       NULL,       false},
136  { NT_(".types"),      typescmd,                 NULL,       false},
137  { NT_(".tags"),      tagscmd,                   NULL,       false}
138 };
139 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
140
141 /*
142  * Execute a command from the UA
143  */
144 bool do_a_dot_command(UAContext *ua)
145 {
146    int i;
147    int len;
148    bool ok = false;
149    bool found = false;
150
151    Dmsg1(1400, "Dot command: %s\n", ua->UA_sock?ua->UA_sock->msg:"");
152    if (ua->argc == 0 || !ua->UA_sock) {
153       return false;
154    }
155
156    len = strlen(ua->argk[0]);
157    if (len == 1) {
158       if (ua->api) ua->signal(BNET_CMD_BEGIN);
159       if (ua->api) ua->signal(BNET_CMD_OK);
160       return true;                    /* no op */
161    }
162    for (i=0; i<comsize; i++) {     /* search for command */
163       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
164          /* Check if this command is authorized in RunScript */
165          if (ua->runscript && !commands[i].use_in_rs) {
166             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
167             break;
168          }
169          bool gui = ua->gui;
170          /* Check if command permitted, but "quit" is always OK */
171          if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
172              strcmp(ua->argk[0], NT_(".api"))  != 0 &&
173              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
174             Dmsg1(100, "not allowed %s\n", ua->cmd);
175             break;
176          }
177          Dmsg1(100, "Cmd: %s\n", ua->cmd);
178          ua->gui = true;
179          if (ua->api) ua->signal(BNET_CMD_BEGIN);
180          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
181          if (ua->api) ua->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
182          ua->gui = gui;
183          found = ua->UA_sock->is_stop() ? false : true;
184          break;
185       }
186    }
187    if (!found) {
188       ua->error_msg("%s%s", ua->argk[0], _(": is an invalid command.\n"));
189       ok = false;
190    }
191    return ok;
192 }
193
194 /*
195  * Send ls to Client
196  */
197 static bool dot_ls_cmd(UAContext *ua, const char *cmd)
198 {
199    CLIENT *client = NULL;
200    char *path = NULL;
201    JCR *jcr = ua->jcr;
202    int i;
203
204    jcr->setJobLevel(L_FULL);
205    i = find_arg_with_value(ua, NT_("client"));
206    if (i > 0) {
207       client = GetClientResWithName(ua->argv[i]);
208       if (!client) {
209          ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
210          return false;
211       }
212       if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
213          ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
214          return false;
215       }
216
217    } else {
218       ua->error_msg(_("Client name missing.\n"));
219       return false;
220    }
221
222    i = find_arg_with_value(ua, NT_("path"));
223    if (i > 0) {
224       path = ua->argv[i];
225
226    } else {
227       ua->error_msg(_("path name missing.\n"));
228       return false;
229    }
230
231    jcr->client = client;
232
233    jcr->setJobType(JT_BACKUP);
234    jcr->start_time = time(NULL);
235    init_jcr_job_record(jcr);           // need job
236
237    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
238       jcr->client->name(), jcr->client->address(), jcr->client->FDport);
239
240    if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
241       ua->error_msg(_("Failed to connect to Client.\n"));
242       return false;
243    }
244
245    if (!send_ls_fileset(jcr, path)) {
246       ua->error_msg(_("Failed to send command to Client.\n"));
247       goto bail_out;
248    }
249
250    jcr->file_bsock->fsend("estimate listing=%d\n", 1);
251    while (jcr->file_bsock->recv() >= 0) {
252       ua->send_msg("%s", jcr->file_bsock->msg);
253    }
254
255 bail_out:
256    if (jcr->file_bsock) {
257       jcr->file_bsock->signal(BNET_TERMINATE);
258       free_bsock(ua->jcr->file_bsock);
259    }
260    return true;
261 }
262
263 static void bvfs_set_acl(UAContext *ua, Bvfs *bvfs)
264 {
265    if (!ua) {
266       return;
267    }
268
269    /* If no console resource => default console and all is permitted */
270    if (!ua->cons) {
271       return;
272    }
273    bvfs->set_job_acl(ua->cons->ACL_lists[Job_ACL]);
274    bvfs->set_client_acl(ua->cons->ACL_lists[Client_ACL]);
275    bvfs->set_fileset_acl(ua->cons->ACL_lists[FileSet_ACL]);
276    bvfs->set_pool_acl(ua->cons->ACL_lists[Pool_ACL]);
277 }
278
279 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd)
280 {
281    int32_t LinkFI;
282    struct stat sp;
283    POOL_MEM q;
284    char buf[32];
285    int pos = find_arg_with_value(ua, "lstat");
286
287    if (pos > 0) {
288       for (char *p = ua->argv[pos] ; *p ; p++) {
289          if (! (B_ISALPHA(*p) || B_ISDIGIT(*p) || B_ISSPACE(*p) || *p == '/' || *p == '+' || *p == '-')) {
290             ua->error_msg("Can't accept %c in lstat\n", *p);
291             return true;
292          }
293       }
294
295       decode_stat(ua->argv[pos], &sp, sizeof(sp), &LinkFI);
296       encode_mode(sp.st_mode, buf);
297       Mmsg(q, "st_nlink=%lld\nst_mode=%lld\nperm=%s\nst_uid=%lld\nst_gid=%lld\n"
298               "st_size=%lld\nst_blocks=%lld\nst_ino=%lld\nst_ctime=%lld\n"
299               "st_mtime=%lld\nst_atime=%lld\nst_dev=%lld\nLinkFI=%lld\n",
300            (int64_t) sp.st_nlink,
301            (int64_t) sp.st_mode,
302            buf,
303            (int64_t) sp.st_uid,
304            (int64_t) sp.st_gid,
305            (int64_t) sp.st_size,
306            (int64_t) sp.st_blocks,
307            (int64_t) sp.st_ino,
308            (int64_t) sp.st_ctime,
309            (int64_t) sp.st_mtime,
310            (int64_t) sp.st_atime,
311            (int64_t) sp.st_dev,
312            (int64_t) LinkFI
313          );
314
315       ua->send_msg("%s", q.c_str());
316    }
317    return true;
318 }
319
320 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
321 {
322    if (!open_new_client_db(ua)) {
323       return 1;
324    }
325
326    int pos = find_arg_with_value(ua, "jobid");
327    if (pos != -1 && is_a_number_list(ua->argv[pos])) {
328       if (!bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos])) {
329          ua->error_msg("ERROR: BVFS reported a problem for %s\n",
330                        ua->argv[pos]);
331       }
332    } else {
333       /* update cache for all jobids */
334       bvfs_update_cache(ua->jcr, ua->db);
335    }
336
337    return true;
338 }
339
340 static bool dot_bvfs_update_fv(UAContext *ua, const char *cmd)
341 {
342    int pos = find_arg_with_value(ua, "jobid");
343
344    if (pos == -1 || !is_a_number_list(ua->argv[pos])) {
345       ua->error_msg("Expecting to find jobid=1,2,3 argument\n");
346       return 1;
347    }
348
349    if (!open_new_client_db(ua)) {
350       return 1;
351    }
352
353    bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
354    bvfs_update_fv_cache(ua->jcr, ua->db, ua->argv[pos]);
355
356    ua->info_msg("OK\n");
357
358    return true;
359 }
360
361 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd)
362 {
363    if (!open_client_db(ua)) {
364       return 1;
365    }
366
367    int pos = find_arg(ua, "yes");
368    if (pos != -1) {
369       Bvfs fs(ua->jcr, ua->db);
370       fs.clear_cache();
371       ua->info_msg("OK\n");
372    } else {
373       ua->error_msg("Can't find 'yes' argument\n");
374    }
375
376    return true;
377 }
378
379 static int bvfs_result_handler(void *ctx, int fields, char **row)
380 {
381    UAContext *ua = (UAContext *)ctx;
382    struct stat statp;
383    int32_t LinkFI;
384    char *fileid=row[BVFS_FileId];
385    char *lstat=row[BVFS_LStat];
386    char *jobid=row[BVFS_JobId];
387
388    char empty[] = "A A A A A A A A A A A A A A";
389    char zero[] = "0";
390
391    /* We need to deal with non existant path */
392    if (!fileid || !is_a_number(fileid)) {
393       lstat = empty;
394       jobid = zero;
395       fileid = zero;
396    }
397
398    memset(&statp, 0, sizeof(struct stat));
399    decode_stat(lstat, &statp, sizeof(statp), &LinkFI);
400    Dmsg1(100, "type=%s\n", row[0]);
401    if (bvfs_is_dir(row)) {
402       char *path = bvfs_basename_dir(row[BVFS_Name]);
403       ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
404                    jobid, lstat, path);
405
406    } else if (bvfs_is_version(row)) {
407       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
408                    row[BVFS_FilenameId], fileid, jobid,
409                    lstat, row[BVFS_Md5], row[BVFS_VolName],
410                    row[BVFS_VolInchanger]);
411
412    } else if (bvfs_is_file(row)) {
413       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
414                    row[BVFS_FilenameId], fileid, jobid,
415                    lstat, row[BVFS_Name]);
416
417    } else if (bvfs_is_volume_list(row)) {
418       ua->send_msg("%s\t%s\n", row[BVFS_VolName],
419                    row[BVFS_VolInchanger]);
420
421    } else if (bvfs_is_delta_list(row)) {
422       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
423                    row[BVFS_FilenameId], fileid, jobid,
424                    lstat, row[BVFS_DeltaSeq], row[BVFS_JobTDate]);
425    }
426
427    return 0;
428 }
429
430 static bool bvfs_parse_arg_version(UAContext *ua,
431                                    char **client,
432                                    FileId_t *fnid,
433                                    bool *versions,
434                                    bool *copies)
435 {
436    *fnid=0;
437    *client=NULL;
438    *versions=false;
439    *copies=false;
440
441    for (int i=1; i<ua->argc; i++) {
442       if (fnid && strcasecmp(ua->argk[i], NT_("fnid")) == 0) {
443          if (is_a_number(ua->argv[i])) {
444             *fnid = str_to_int64(ua->argv[i]);
445          }
446       }
447
448       if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
449          *client = ua->argv[i];
450       }
451
452       if (copies && strcasecmp(ua->argk[i], NT_("copies")) == 0) {
453          *copies = true;
454       }
455
456       if (versions && strcasecmp(ua->argk[i], NT_("versions")) == 0) {
457          *versions = true;
458       }
459    }
460    return (*client && *fnid > 0);
461 }
462
463 static bool bvfs_parse_arg(UAContext *ua,
464                            DBId_t *pathid, char **path, char **jobid,
465                            char **username,
466                            int *limit, int *offset)
467 {
468    *pathid=0;
469    *limit=2000;
470    *offset=0;
471    *path=NULL;
472    *username=NULL;
473    if (jobid) {
474       *jobid=NULL;
475    }
476
477    for (int i=1; i<ua->argc; i++) {
478       if (!ua->argv[i]) {
479          continue;
480       }
481       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
482          if (is_a_number(ua->argv[i])) {
483             *pathid = str_to_int64(ua->argv[i]);
484          }
485       }
486
487       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
488          *path = ua->argv[i];
489       }
490
491       if (strcasecmp(ua->argk[i], NT_("username")) == 0) {
492          *username = ua->argv[i];
493       }
494
495       if (jobid && strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
496          if (is_a_number_list(ua->argv[i])) {
497             *jobid = ua->argv[i];
498          }
499       }
500
501       if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
502          JOB_DBR jr;
503          memset(&jr, 0, sizeof(jr));
504          bstrncpy(jr.Job, ua->argv[i], sizeof(jr.Job));
505          if (!open_new_client_db(ua)) {
506             return false;
507          }
508          if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
509             return false;
510          }
511          if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
512             return false;
513          }
514          /* Store the jobid after the ua->cmd, a bit kluggy */
515          int len = strlen(ua->cmd);
516          ua->cmd = check_pool_memory_size(ua->cmd, len + 1 + 50);
517          *jobid = edit_uint64(jr.JobId, ua->cmd + len + 1);
518       }
519
520       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
521          if (is_a_number(ua->argv[i])) {
522             *limit = str_to_int64(ua->argv[i]);
523          }
524       }
525
526       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
527          if (is_a_number(ua->argv[i])) {
528             *offset = str_to_int64(ua->argv[i]);
529          }
530       }
531    }
532
533    if (jobid && *jobid == NULL) {
534       return false;
535    }
536
537    if (!(*pathid || *path)) {
538       return false;
539    }
540
541    return true;
542 }
543
544 /* .bvfs_cleanup path=b2XXXXX
545  */
546 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd)
547 {
548    int i;
549    if ((i = find_arg_with_value(ua, "path")) >= 0) {
550       if (!open_client_db(ua)) {
551          return 1;
552       }
553       Bvfs fs(ua->jcr, ua->db);
554       fs.drop_restore_list(ua->argv[i]);
555    }
556    return true;
557 }
558
559 /* .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
560  */
561 static bool dot_bvfs_restore(UAContext *ua, const char *cmd)
562 {
563    DBId_t pathid=0;
564    int limit=2000, offset=0, i;
565    char *path=NULL, *jobid=NULL, *username=NULL;
566    char *empty = (char *)"";
567    char *fileid, *dirid, *hardlink;
568
569    fileid = dirid = hardlink = empty;
570
571    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
572                        &limit, &offset) || !path)
573    {
574       ua->error_msg("Can't find jobid, pathid or path argument\n");
575       return true;              /* not enough param */
576    }
577
578    if (!open_new_client_db(ua)) {
579       return true;
580    }
581
582    Bvfs fs(ua->jcr, ua->db);
583    bvfs_set_acl(ua, &fs);
584    fs.set_username(username);
585    fs.set_jobids(jobid);
586
587    if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
588       fileid = ua->argv[i];
589    }
590    if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
591       dirid = ua->argv[i];
592    }
593    if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
594       hardlink = ua->argv[i];
595    }
596    if ((i = find_arg(ua, "nodelta")) >= 0) {
597       fs.set_compute_delta(false);
598    }
599    if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
600       ua->send_msg("OK\n");
601    } else {
602       ua->error_msg("Cannot create restore list.\n");
603    }
604
605    return true;
606 }
607
608 /* Get a bootstrap for a given bvfs restore session
609  * .bvfs_get_bootstrap path=b21xxxxxx
610  * Volume=Vol1
611  * Storage=Store1
612  * VolAddress=10
613  * VolSessionTime=xxx
614  * VolSessionId=yyyy
615  */
616 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd)
617 {
618    RESTORE_CTX rx;                    /* restore context */
619    POOLMEM *buf = get_pool_memory(PM_MESSAGE);
620    int pos;
621
622    new_rx(&rx);
623    if (!open_new_client_db(ua)) {
624       ua->error_msg("ERROR: Unable to open database\n");
625       goto bail_out;
626    }
627    pos = find_arg_with_value(ua, "path");
628    if (pos < 0) {
629       ua->error_msg("ERROR: Unable to get path argument\n");
630       goto bail_out;
631    }
632
633    insert_table_into_findex_list(ua, &rx, ua->argv[pos]);
634
635    if (rx.bsr_list->size() > 0) {
636       if (!complete_bsr(ua, rx.bsr_list)) {   /* find Vol, SessId, SessTime from JobIds */
637          ua->error_msg("ERROR: Unable to construct a valid BSR. Cannot continue.\n");
638          goto bail_out;
639       }
640       if (!(rx.selected_files = write_bsr_file(ua, rx))) {
641          ua->error_msg("ERROR: No files selected to be restored.\n");
642          goto bail_out;
643       }
644       FILE *fp = bfopen(ua->jcr->RestoreBootstrap, "r");
645       if (!fp) {
646          ua->error_msg("ERROR: Unable to open bootstrap file\n");
647          goto bail_out;
648       }
649       while (bfgets(buf, fp)) {
650          ua->send_msg("%s", buf);
651       }
652       fclose(fp);
653    } else {
654       ua->error_msg("ERROR: Unable to find files to restore\n");
655       goto bail_out;
656    }
657
658 bail_out:
659    if (ua->jcr->unlink_bsr) {
660       unlink(ua->jcr->RestoreBootstrap);
661       ua->jcr->unlink_bsr = false;
662    }
663    free_pool_memory(buf);
664    free_rx(&rx);
665    return true;
666 }
667
668 /*
669  * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
670  * Vol001
671  * Vol002
672  * Vol003
673  */
674 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
675 {
676    DBId_t pathid=0;
677    FileId_t fileid=0;
678    char  *path=NULL, *jobid=NULL, *username=NULL;
679    char  *filename=NULL;
680    int    limit=2000, offset=0;
681    int    i;
682
683    bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
684
685    if ((i = find_arg_with_value(ua, "filename")) >= 0) {
686       if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
687          ua->error_msg("Can't find jobid, pathid or path argument\n");
688          return true;
689       }
690
691       filename = ua->argv[i];
692
693    } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
694       if (!is_a_number(ua->argv[i])) {
695          ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
696          return true;
697       }
698       fileid = str_to_int64(ua->argv[i]);
699    }
700
701    if (!open_new_client_db(ua)) {
702       return 1;
703    }
704
705    Bvfs fs(ua->jcr, ua->db);
706    bvfs_set_acl(ua, &fs);
707    fs.set_username(username);
708    fs.set_handler(bvfs_result_handler, ua);
709    fs.set_limit(limit);
710    ua->bvfs = &fs;
711
712    if (filename) {
713       /* TODO */
714
715    } else {
716       fs.get_volumes(fileid);
717    }
718    ua->bvfs = NULL;
719    return true;
720 }
721
722 /*
723  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
724  * .bvfs_lsfiles jobid=1,2,3,4 path=/
725  */
726 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
727 {
728    DBId_t pathid=0;
729    int limit=2000, offset=0;
730    char *path=NULL, *jobid=NULL, *username=NULL;
731    char *pattern=NULL, *filename=NULL;
732    bool ok;
733    int i;
734
735    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
736                        &limit, &offset))
737    {
738       ua->error_msg("Can't find jobid, pathid or path argument\n");
739       return true;              /* not enough param */
740    }
741    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
742       pattern = ua->argv[i];
743    }
744    if ((i = find_arg_with_value(ua, "filename")) >= 0) {
745       filename = ua->argv[i];
746    }
747
748    if (!open_new_client_db(ua)) {
749       return 1;
750    }
751
752    Bvfs fs(ua->jcr, ua->db);
753    bvfs_set_acl(ua, &fs);
754    fs.set_username(username);
755    fs.set_jobids(jobid);
756    fs.set_handler(bvfs_result_handler, ua);
757    fs.set_limit(limit);
758    fs.set_offset(offset);
759    ua->bvfs = &fs;
760    if (pattern) {
761       fs.set_pattern(pattern);
762    }
763    if (filename) {
764       fs.set_filename(filename);
765    }
766    if (pathid) {
767       ok = fs.ch_dir(pathid);
768    } else {
769       ok = fs.ch_dir(path);
770    }
771    if (!ok) {
772       goto bail_out;
773    }
774
775    fs.ls_files();
776
777 bail_out:
778    ua->bvfs = NULL;
779    return true;
780 }
781
782 /*
783  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
784  * .bvfs_lsdirs jobid=1,2,3,4 path=/
785  * .bvfs_lsdirs jobid=1,2,3,4 path=
786  */
787 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
788 {
789    DBId_t pathid=0;
790    int   limit=2000, offset=0;
791    char *path=NULL, *jobid=NULL, *username=NULL;
792    char *pattern=NULL;
793    int   dironly;
794    bool  ok;
795    int i;
796
797    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
798                        &limit, &offset))
799    {
800       ua->error_msg("Can't find jobid, pathid or path argument\n");
801       return true;              /* not enough param */
802    }
803
804    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
805       pattern = ua->argv[i];
806    }
807
808    dironly = find_arg(ua, "dironly");
809
810    if (!open_new_client_db(ua)) {
811       return 1;
812    }
813
814    Bvfs fs(ua->jcr, ua->db);
815    bvfs_set_acl(ua, &fs);
816    fs.set_username(username);
817    fs.set_jobids(jobid);
818    fs.set_limit(limit);
819    fs.set_handler(bvfs_result_handler, ua);
820    fs.set_offset(offset);
821    ua->bvfs = &fs;
822
823    if (pattern) {
824       fs.set_pattern(pattern);
825    }
826
827    if (pathid) {
828       ok = fs.ch_dir(pathid);
829    } else {
830       ok = fs.ch_dir(path);
831    }
832
833    if (!ok) {
834       goto bail_out;
835    }
836
837    fs.ls_special_dirs();
838
839    if (dironly < 0) {
840       fs.ls_dirs();
841    }
842 bail_out:
843    ua->bvfs = NULL;
844    return true;
845 }
846
847 /*
848  * .bvfs_get_delta fileid=10 
849  *
850  */
851 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd)
852 {
853    bool ret;
854    FileId_t fileid=0;
855    int i;
856
857    if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
858       if (!is_a_number(ua->argv[i])) {
859          ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
860          return true;
861       }
862       fileid = str_to_int64(ua->argv[i]);
863
864    } else {
865       ua->error_msg("Expecting FileId\n");
866       return true;
867    }
868
869    if (!open_new_client_db(ua)) {
870       return 1;
871    }
872    Bvfs fs(ua->jcr, ua->db);
873    bvfs_set_acl(ua, &fs);
874    fs.set_handler(bvfs_result_handler, ua);
875    ua->bvfs = &fs;
876    ret = fs.get_delta(fileid);
877    ua->bvfs = NULL;
878    return ret;
879 }
880
881 /*
882  * .bvfs_versions fnid=10 pathid=10 client=xxx copies versions
883  *
884  */
885 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
886 {
887    DBId_t pathid=0;
888    FileId_t fnid=0;
889    int limit=2000, offset=0;
890    char *path=NULL, *client=NULL, *username=NULL;
891    bool copies=false, versions=false;
892    if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
893                        &limit, &offset))
894    {
895       ua->error_msg("Can't find pathid or path argument\n");
896       return true;              /* not enough param */
897    }
898
899    if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
900    {
901       ua->error_msg("Can't find client or fnid argument\n");
902       return true;              /* not enough param */
903    }
904
905    if (!open_new_client_db(ua)) {
906       return 1;
907    }
908
909    Bvfs fs(ua->jcr, ua->db);
910    bvfs_set_acl(ua, &fs);
911    fs.set_limit(limit);
912    fs.set_see_all_versions(versions);
913    fs.set_see_copies(copies);
914    fs.set_handler(bvfs_result_handler, ua);
915    fs.set_offset(offset);
916    ua->bvfs = &fs;
917
918    fs.get_all_file_versions(pathid, fnid, client);
919
920    ua->bvfs = NULL;
921    return true;
922 }
923
924 /* .bvfs_get_jobids jobid=1
925  *  -> returns needed jobids to restore
926  * .bvfs_get_jobids ujobid=xxx only
927  *  -> returns the jobid of the job
928  * .bvfs_get_jobids jobid=1 jobname
929  *  -> returns the jobname
930  * .bvfs_get_jobids client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
931  *  -> returns all jobid for the client
932  * .bvfs_get_jobids client=xxx count
933  *  -> returns the number of jobids for the client
934  * .bvfs_get_jobids jobid=1 all
935  *  -> returns needed jobids to restore with all filesets a JobId=1 time
936  * .bvfs_get_jobids job=XXXXX
937  *  -> returns needed jobids to restore with the jobname
938  * .bvfs_get_jobids ujobid=JobName
939  *  -> returns needed jobids to restore
940  */
941 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
942 {
943    JOB_DBR jr;
944    memset(&jr, 0, sizeof(JOB_DBR));
945
946    db_list_ctx jobids, tempids;
947    int pos;
948    char ed1[50];
949    POOL_MEM query;
950    dbid_list ids;               /* Store all FileSetIds for this client */
951
952    if (!open_new_client_db(ua)) {
953       return true;
954    }
955
956    Bvfs fs(ua->jcr, ua->db);
957    bvfs_set_acl(ua, &fs);
958
959    if ((pos = find_arg_with_value(ua, "username")) >= 0) {
960       fs.set_username(ua->argv[pos]);
961    }
962
963    if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
964       bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
965    }
966
967    if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
968       jr.JobId = str_to_int64(ua->argv[pos]);
969
970    /* Guess JobId from Job name, take the last successful jobid */
971    } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
972       JOB *job;
973       bool ret;
974       int32_t JobId=0;
975
976       bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
977       /* TODO: enhance this function to take client and/or fileset as argument*/
978
979       job = GetJobResWithName(jr.Name);
980       if (!job) {
981          ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
982          return true;
983       }
984       db_lock(ua->db);
985       Mmsg(ua->db->cmd,
986       "SELECT JobId "
987         "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
988          "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
989            "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
990          "ORDER By JobTDate DESC LIMIT 1",
991            job->client->name(), job->fileset->name());
992       ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
993       db_unlock(ua->db);
994
995       if (!ret) {
996          ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
997       }
998
999       jr.JobId = JobId;
1000
1001    /* Get JobId from ujobid */
1002    } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
1003       bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
1004
1005    /* Return all backup jobid for a client */
1006    } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
1007       CLIENT *cli;
1008       POOL_MEM where;
1009       char limit[50];
1010       bool ret;
1011       int  nbjobs;
1012
1013       cli = GetClientResWithName(ua->argv[pos]);
1014       if (!cli) {
1015          ua->error_msg(_("Unable to get Client record for Client=%s\n"),
1016                        ua->argv[pos]);
1017          return true;
1018       }
1019       db_lock(ua->db);
1020
1021       bvfs_get_filter(ua, where, limit, sizeof(limit));
1022
1023       Mmsg(ua->db->cmd,
1024       "SELECT JobId "
1025         "FROM Job JOIN Client USING (ClientId) "
1026          "WHERE Client.Name = '%s' "
1027            "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') %s "
1028          "ORDER By JobTDate ASC %s",
1029            cli->name(), where.c_str(), limit);
1030       ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
1031       db_unlock(ua->db);
1032
1033       if (!ret) {
1034          ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
1035                        cli->name());
1036       }
1037
1038       nbjobs = fs.set_jobids(jobids.list);
1039
1040       /* Apply the ACL filter on JobIds */
1041       if (find_arg(ua, "count") >= 0) {
1042          ua->send_msg("%d\n", nbjobs);
1043
1044       } else {
1045          ua->send_msg("%s\n", fs.get_jobids());
1046       }
1047       return true;
1048    }
1049
1050    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
1051       ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
1052                     ua->cmd, db_strerror(ua->db));
1053       return true;
1054    }
1055
1056    /* Display only the requested jobid or
1057     * When in level base, we don't rely on any Full/Incr/Diff
1058     */
1059    if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
1060       /* Apply the ACL filter on JobIds */
1061       fs.set_jobid(jr.JobId);
1062       ua->send_msg("%s\n", fs.get_jobids());
1063       return true;
1064    }
1065
1066    /* Display only the requested job name
1067     */
1068    if (find_arg(ua, "jobname") > 0) {
1069       /* Apply the ACL filter on JobIds */
1070       fs.set_jobid(jr.JobId);
1071       if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
1072          ua->send_msg("%s\n", jr.Job);
1073       }
1074       return true;
1075    }
1076
1077    /* If we have the "all" option, we do a search on all defined fileset
1078     * for this client
1079     */
1080    if (find_arg(ua, "all") > 0) {
1081       edit_int64(jr.ClientId, ed1);
1082       Mmsg(query, uar_sel_filesetid, ed1);
1083       db_get_query_dbids(ua->jcr, ua->db, query, ids);
1084    } else {
1085       ids.num_ids = 1;
1086       ids.DBId[0] = jr.FileSetId;
1087    }
1088
1089    jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
1090
1091    /* Foreach different FileSet, we build a restore jobid list */
1092    for (int i=0; i < ids.num_ids; i++) {
1093       jr.FileSetId = ids.DBId[i];
1094       if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &tempids)) {
1095          return true;
1096       }
1097       jobids.add(tempids);
1098    }
1099
1100    fs.set_jobids(jobids.list);
1101    ua->send_msg("%s\n", fs.get_jobids());
1102    return true;
1103 }
1104
1105 static int jobs_handler(void *ctx, int num_field, char **row)
1106 {
1107    UAContext *ua = (UAContext *)ctx;
1108    ua->send_msg("%s %s %s %s\n", row[0], row[1], row[2], row[3]);
1109    return 0;
1110 }
1111
1112 static char *get_argument(UAContext *ua, const char *arg, char *esc, bool convert)
1113 {
1114    int pos;
1115    if (((pos = find_arg_with_value(ua, arg)) < 0) ||
1116        (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1117    {
1118       return NULL;
1119    }
1120    db_escape_string(ua->jcr, ua->db, esc,
1121                     ua->argv[pos], strlen(ua->argv[pos]));
1122    if (convert) {
1123       for (int i=0; esc[i] ; i++) {
1124          if (esc[i] == '*') {
1125             esc[i] = '%';
1126          }
1127       }
1128    }
1129    return esc;
1130 }
1131
1132 /* The DB should be locked */
1133 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len)
1134 {
1135    POOL_MEM tmp;
1136    char esc_name[MAX_ESCAPE_NAME_LENGTH];
1137
1138    if (get_argument(ua, "jobname", esc_name, true) != NULL) {
1139       Mmsg(where, "AND Job.Job LIKE '%s' ", esc_name);
1140    }
1141
1142    if (get_argument(ua, "fileset", esc_name, true) != NULL) {
1143       Mmsg(tmp, "AND FileSet.FileSet LIKE '%s' ", esc_name);
1144       pm_strcat(where, tmp.c_str());
1145    }
1146
1147    if (get_argument(ua, "jobid", esc_name, false) != NULL) {
1148       Mmsg(tmp, "AND Job.JobId = '%s' ", esc_name);
1149       pm_strcat(where, tmp.c_str());
1150    }
1151
1152    if (get_argument(ua, "ujobid", esc_name, false) != NULL) {
1153       Mmsg(tmp, "AND Job.Job = '%s' ", esc_name);
1154       pm_strcat(where, tmp.c_str());
1155    }
1156
1157    if (get_argument(ua, "start", esc_name, false) != NULL) {
1158       Mmsg(tmp, "AND Job.StartTime >= '%s' ", esc_name);
1159       pm_strcat(where, tmp.c_str());
1160    }
1161
1162    if (get_argument(ua, "end", esc_name, false) != NULL) {
1163       Mmsg(tmp, "AND Job.EndTime <= '%s' ", esc_name);
1164       pm_strcat(where, tmp.c_str());
1165    }
1166
1167    *limit = 0;
1168    if (get_argument(ua, "limit", esc_name, false) != NULL) {
1169       if (is_a_number(esc_name)) {
1170          bsnprintf(limit, len, "LIMIT %s ", esc_name);
1171       }
1172    }
1173 }
1174
1175 /* .bvfs_get_jobs client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
1176  * 1 yyyyy 1 Backup1_xxx_xxx_xxxx_xxx
1177  * 2 yyyyy 0 Backup1_xxx_xxx_xxxx_xxx
1178  */
1179 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd)
1180 {
1181    int pos;
1182    POOL_MEM where;
1183    char esc_cli[MAX_ESCAPE_NAME_LENGTH];
1184    char limit[MAX_ESCAPE_NAME_LENGTH];
1185    if (!open_new_client_db(ua)) {
1186       return true;
1187    }
1188
1189    if (((pos = find_arg_with_value(ua, "client")) < 0) ||
1190        (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1191    {
1192       return true;
1193    }
1194
1195    /* TODO: Do checks on Jobs, FileSet, etc... */
1196    if (!acl_access_client_ok(ua, ua->argv[pos], JT_BACKUP_RESTORE)) {
1197       return true;
1198    }
1199
1200    db_lock(ua->db);
1201    db_escape_string(ua->jcr, ua->db, esc_cli,
1202                     ua->argv[pos], strlen(ua->argv[pos]));
1203
1204    bvfs_get_filter(ua, where, limit, sizeof(limit));
1205
1206    Mmsg(ua->db->cmd,
1207         "SELECT JobId, JobTDate, HasCache, Job "
1208           "FROM Job JOIN Client USING (ClientId) JOIN FileSet USING (FileSetId) "
1209          "WHERE Client.Name = '%s' AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
1210             "%s "
1211          "ORDER By JobTDate DESC %s",
1212         esc_cli, where.c_str(), limit);
1213
1214    db_sql_query(ua->db, ua->db->cmd, jobs_handler, ua);
1215    db_unlock(ua->db);
1216    return true;
1217 }
1218
1219 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
1220 {
1221    quit_cmd(ua, cmd);
1222    return true;
1223 }
1224
1225 static bool dot_help_cmd(UAContext *ua, const char *cmd)
1226 {
1227    qhelp_cmd(ua, cmd);
1228    return true;
1229 }
1230
1231 static bool getmsgscmd(UAContext *ua, const char *cmd)
1232 {
1233    if (console_msg_pending) {
1234       do_messages(ua, cmd);
1235    }
1236    return 1;
1237 }
1238
1239 #ifdef DEVELOPER
1240 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
1241 {
1242    BSOCK *sd;
1243    JCR *jcr = ua->jcr;
1244    USTORE lstore;
1245
1246    lstore.store = store;
1247    pm_strcpy(lstore.store_source, _("unknown source"));
1248    set_wstorage(jcr, &lstore);
1249    /* Try connecting for up to 15 seconds */
1250    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
1251       store->name(), store->address, store->SDport);
1252    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
1253       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1254       return;
1255    }
1256    Dmsg0(120, _("Connected to storage daemon\n"));
1257    sd = jcr->store_bsock;
1258    sd->fsend("%s", cmd);
1259    if (sd->recv() >= 0) {
1260       ua->send_msg("%s", sd->msg);
1261    }
1262    sd->signal(BNET_TERMINATE);
1263    free_bsock(ua->jcr->store_bsock);
1264    return;
1265 }
1266
1267 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
1268 {
1269    BSOCK *fd;
1270
1271    /* Connect to File daemon */
1272
1273    ua->jcr->client = client;
1274    /* Try to connect for 15 seconds */
1275    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1276       client->name(), client->address(), client->FDport);
1277    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1278       ua->error_msg(_("Failed to connect to Client.\n"));
1279       return;
1280    }
1281    Dmsg0(120, "Connected to file daemon\n");
1282    fd = ua->jcr->file_bsock;
1283    fd->fsend("%s", cmd);
1284    if (fd->recv() >= 0) {
1285       ua->send_msg("%s", fd->msg);
1286    }
1287    fd->signal(BNET_TERMINATE);
1288    free_bsock(ua->jcr->file_bsock);
1289    return;
1290 }
1291
1292 /*
1293  *   .die (seg fault)
1294  *   .dump (sm_dump)
1295  *   .exit (no arg => .quit)
1296  */
1297 static bool admin_cmds(UAContext *ua, const char *cmd)
1298 {
1299    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1300    STORE *store=NULL;
1301    CLIENT *client=NULL;
1302    bool dir=false;
1303    bool do_deadlock=false;
1304    const char *remote_cmd;
1305    int i;
1306    JCR *jcr = NULL;
1307    int a;
1308    if (strncmp(ua->argk[0], ".die", 4) == 0) {
1309       if (find_arg(ua, "deadlock") > 0) {
1310          do_deadlock = true;
1311          remote_cmd = ".die deadlock";
1312       } else {
1313          remote_cmd = ".die";
1314       }
1315    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
1316       remote_cmd = "sm_dump";
1317    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
1318       remote_cmd = "exit";
1319    } else {
1320       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1321       return true;
1322    }
1323    /* General debug? */
1324    for (i=1; i<ua->argc; i++) {
1325       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1326           strcasecmp(ua->argk[i], "director") == 0) {
1327          dir = true;
1328       }
1329       if (strcasecmp(ua->argk[i], "client") == 0 ||
1330           strcasecmp(ua->argk[i], "fd") == 0) {
1331          client = NULL;
1332          if (ua->argv[i]) {
1333             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1334          }
1335          if (!client) {
1336             client = select_client_resource(ua, JT_SYSTEM);
1337          }
1338       }
1339
1340       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1341           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1342           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1343          store = NULL;
1344          if (ua->argv[i]) {
1345             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1346          }
1347          if (!store) {
1348             store = get_storage_resource(ua, false/*no default*/);
1349          }
1350       }
1351    }
1352
1353    if (!dir && !store && !client) {
1354       /*
1355        * We didn't find an appropriate keyword above, so
1356        * prompt the user.
1357        */
1358       start_prompt(ua, _("Available daemons are: \n"));
1359       add_prompt(ua, _("Director"));
1360       add_prompt(ua, _("Storage"));
1361       add_prompt(ua, _("Client"));
1362       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
1363       case 0:                         /* Director */
1364          dir=true;
1365          break;
1366       case 1:
1367          store = get_storage_resource(ua, false/*no default*/);
1368          break;
1369       case 2:
1370          client = select_client_resource(ua, JT_BACKUP_RESTORE);
1371          break;
1372       default:
1373          break;
1374       }
1375    }
1376
1377    if (store) {
1378       do_storage_cmd(ua, store, remote_cmd);
1379    }
1380
1381    if (client) {
1382       do_client_cmd(ua, client, remote_cmd);
1383    }
1384
1385    if (dir) {
1386       if (strncmp(remote_cmd, ".die", 4) == 0) {
1387          if (do_deadlock) {
1388             ua->send_msg(_("The Director will generate a deadlock.\n"));
1389             P(mutex);
1390             P(mutex);
1391          }
1392          ua->send_msg(_("The Director will segment fault.\n"));
1393          a = jcr->JobId; /* ref NULL pointer */
1394          jcr->JobId = 1000; /* another ref NULL pointer */
1395          jcr->JobId = a;
1396
1397       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
1398          sm_dump(false, true);
1399       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
1400          dot_quit_cmd(ua, cmd);
1401       }
1402    }
1403
1404    return true;
1405 }
1406
1407 #else
1408
1409 /*
1410  * Dummy routine for non-development version
1411  */
1412 static bool admin_cmds(UAContext *ua, const char *cmd)
1413 {
1414    ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1415    return true;
1416 }
1417
1418 #endif
1419
1420 /*
1421  * Send a file to the director from bconsole @putfile command
1422  * The .putfile can not be used directly.
1423  */
1424 static bool putfile_cmd(UAContext *ua, const char *cmd)
1425 {
1426    int         pos, i, pnl, fnl;
1427    bool        ok = true;
1428    POOLMEM    *name = get_pool_memory(PM_FNAME);
1429    POOLMEM    *path = get_pool_memory(PM_FNAME);
1430    POOLMEM    *fname= get_pool_memory(PM_FNAME);
1431    const char *key = "putfile";
1432    FILE       *fp = NULL;
1433
1434    if ((pos = find_arg_with_value(ua, "key")) > 0) {
1435       /* Check the string if the string is valid */
1436       for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1437
1438       if (ua->argv[pos][i] == 0) {
1439          key = ua->argv[pos];
1440
1441       } else {
1442          ua->error_msg("Invalid key name for putfile command");
1443          ok = false;
1444          goto bail_out;
1445       }
1446    }
1447
1448    /* the (intptr_t)ua will allow one file per console session */
1449    make_unique_filename(&name, (intptr_t)ua, (char *)key);
1450
1451    fp = bfopen(name, "w");
1452    if (!fp) {
1453       berrno be;
1454       ua->error_msg("Unable to open destination file. ERR=%s\n",
1455                     be.bstrerror(errno));
1456       ok = false;
1457       goto bail_out;
1458    }
1459
1460    while (ua->UA_sock->recv() > 0) {
1461       if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1462          berrno be;
1463          ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1464                        be.bstrerror(errno));
1465          ok = false;
1466          /* TODO: Check if we need to quit here (data will still be in the
1467           * buffer...) */
1468       }
1469    }
1470
1471    split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1472
1473 bail_out:
1474    if (ok) {
1475       ua->send_msg("OK\n");
1476
1477    } else {
1478       ua->send_msg("ERROR\n");
1479    }
1480
1481    free_pool_memory(name);
1482    free_pool_memory(path);
1483    free_pool_memory(fname);
1484    if (fp) {
1485       fclose(fp);
1486    }
1487    return true;
1488 }
1489
1490 /* .estimate command */
1491 static bool dotestimatecmd(UAContext *ua, const char *cmd)
1492 {
1493    JOB *jres;
1494    JOB_DBR jr;
1495    //FILESET_DBR fr;
1496    //CLIENT_DBR cr;
1497    char *job = NULL, level = 0, *fileset = NULL, *client = NULL;
1498    memset(&jr, 0, sizeof(jr));
1499
1500    for (int i = 1 ; i < ua->argc ; i++) {
1501       if (!ua->argv[i]) {
1502          ua->error_msg(_("Invalid argument for %s\n"), ua->argk[i]);
1503          return true;
1504
1505       } else if (strcasecmp(ua->argk[i], "job") == 0) {
1506          job = ua->argv[i];
1507
1508       } else if (strcasecmp(ua->argk[i], "level") == 0) {
1509          level = toupper(ua->argv[i][0]);
1510
1511       } else if (strcasecmp(ua->argk[i], "fileset") == 0) {
1512          fileset = ua->argv[i];
1513
1514       } else if (strcasecmp(ua->argk[i], "client") == 0) {
1515          client = ua->argv[i];
1516       }
1517    }
1518    if (!job) {
1519       ua->error_msg(_("Invalid argument for job\n"));
1520       return true;
1521    }
1522    if (!acl_access_ok(ua, Job_ACL, job) ||
1523        (fileset && !acl_access_ok(ua, FileSet_ACL, fileset)) ||
1524        (client && !acl_access_client_ok(ua, client, JT_BACKUP)))
1525    {
1526       ua->error_msg(_("Access to specified Job, FileSet or Client not allowed.\n"));
1527       return true;
1528    }
1529    jres = (JOB *) GetResWithName(R_JOB, job);
1530    if (!jres) {
1531       ua->error_msg(_("Invalid argument for job\n"));
1532       return true;
1533    }
1534    if (!open_client_db(ua)) {
1535       ua->error_msg(_("Unable to open the catalog.\n"));
1536       return true;
1537    }
1538    
1539    bstrncpy(jr.Name, jres->hdr.name, sizeof(jr.Name));
1540    jr.JobLevel = level ? level : jres->JobLevel;
1541    if (fileset) {
1542       /* Get FileSetId */
1543    }
1544    if (client) {
1545       /* Get ClientId */
1546    }
1547    db_lock(ua->db);
1548    if (db_get_job_statistics(ua->jcr, ua->db, &jr)) {
1549       db_unlock(ua->db);
1550       OutputWriter o(ua->api_opts);
1551       char *p = o.get_output(OT_START_OBJ,
1552                    OT_JOBLEVEL, "level",     jr.JobLevel,
1553                    OT_INT,      "nbjob",     jr.CorrNbJob,
1554                    OT_INT,      "corrbytes", jr.CorrJobBytes,
1555                    OT_SIZE,     "jobbytes",  jr.JobBytes,
1556                    OT_INT,      "corrfiles", jr.CorrJobFiles,
1557                    OT_INT32,    "jobfiles",  jr.JobFiles,
1558                    OT_INT,      "duration",  (int)0,
1559                    OT_STRING,   "job",       jres->hdr.name,
1560                    OT_END_OBJ,
1561                    OT_END);
1562       ua->send_msg("%s", p);
1563    } else {
1564       /* We unlock the DB after the errmsg copy */
1565       pm_strcpy(ua->jcr->errmsg, ua->db->errmsg);
1566       db_unlock(ua->db);
1567       ua->error_msg("Error with .estimate %s\n", ua->jcr->errmsg);
1568    }
1569    return true;
1570 }
1571
1572
1573 /*
1574  * Can use an argument to filter on JobType
1575  * .jobs [type=B] or [type=!B]
1576  */
1577 static bool jobscmd(UAContext *ua, const char *cmd)
1578 {
1579    JOB *job;
1580    uint32_t type = 0;
1581    bool exclude=false;
1582    int pos;
1583    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1584       if (ua->argv[pos][0] == '!') {
1585          exclude = true;
1586          type = ua->argv[pos][1];
1587       } else {
1588          type = ua->argv[pos][0];
1589       }
1590    }
1591    LockRes();
1592    foreach_res(job, R_JOB) {
1593       if (type) {
1594          if ((exclude && type == job->JobType) || (!exclude && type != job->JobType)) {
1595             continue;
1596          }
1597       }
1598       if (acl_access_ok(ua, Job_ACL, job->name())) {
1599          ua->send_msg("%s\n", job->name());
1600       }
1601    }
1602    UnlockRes();
1603    return true;
1604 }
1605
1606 static bool filesetscmd(UAContext *ua, const char *cmd)
1607 {
1608    FILESET *fs;
1609    LockRes();
1610    foreach_res(fs, R_FILESET) {
1611       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1612          ua->send_msg("%s\n", fs->name());
1613       }
1614    }
1615    UnlockRes();
1616    return true;
1617 }
1618
1619 static bool catalogscmd(UAContext *ua, const char *cmd)
1620 {
1621    CAT *cat;
1622    LockRes();
1623    foreach_res(cat, R_CATALOG) {
1624       if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1625          ua->send_msg("%s\n", cat->name());
1626       }
1627    }
1628    UnlockRes();
1629    return true;
1630 }
1631
1632 /* This is not a good idea to lock the entire resource list to send information
1633  * on the network or query the DNS. So, we don't use the foreach_res() command
1634  * with a global lock and we do a copy of the client list in a specific list to
1635  * avoid any problem, I'm pretty sure we can use the res_head directly without
1636  * a global lock, but it needs testing to avoid race conditions.
1637  */
1638 class TmpClient
1639 {
1640 public:
1641    char *name;
1642    char *address;
1643
1644    TmpClient(char *n, char *a):
1645      name(bstrdup(n)), address(bstrdup(a))
1646    {
1647    };
1648    ~TmpClient() {
1649       free(name);
1650       free(address);
1651    };
1652 };
1653
1654 static bool clientscmd(UAContext *ua, const char *cmd)
1655 {
1656    int i;
1657    CLIENT *client;
1658    const char *ip=NULL;
1659    bool    found=false;
1660    alist  *clientlist = NULL;
1661    TmpClient *elt;
1662
1663    if ((i = find_arg_with_value(ua, "address")) >= 0) {
1664       ip = ua->argv[i];
1665       clientlist = New(alist(50, not_owned_by_alist));
1666    }
1667
1668    /* This is not a good idea to lock the entire resource list
1669     * to send information on the network or query the DNS. So,
1670     * we don't use the foreach_res() command with a global lock here.
1671     */
1672    LockRes();
1673    foreach_res(client, R_CLIENT) {
1674       if (acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
1675          if (ip) {
1676             elt = new TmpClient(client->name(), client->address());
1677             clientlist->append(elt);
1678
1679          } else {
1680             /* do not check for a specific ip, display everything */
1681             ua->send_msg("%s\n", client->name());
1682          }
1683       }
1684    }
1685    UnlockRes();
1686
1687    if (!ip) {
1688       return true;
1689    }
1690
1691    foreach_alist(elt, clientlist) {
1692       /* We look for a client that matches the specific ip address */
1693       dlist  *addr_list=NULL;
1694       IPADDR *ipaddr;
1695       char    buf[128];
1696       const char *errstr;
1697
1698       if (strcmp(elt->address, ip) == 0) {
1699          found = true;
1700
1701       } else if ((addr_list = bnet_host2ipaddrs(elt->address, 0, &errstr)) == NULL) {
1702          Dmsg2(10, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n",
1703                elt->address, errstr);
1704
1705       } else {
1706          /* Try to find the ip address from the list, we might have
1707           * other ways to compare ip addresses
1708           */
1709          foreach_dlist(ipaddr, addr_list) {
1710             if (strcmp(ip, ipaddr->get_address(buf, sizeof(buf))) == 0) {
1711                found = true;
1712                break;
1713             }
1714          }
1715          free_addresses(addr_list);
1716       }
1717
1718       if (found) {
1719          ua->send_msg("%s\n", elt->name);
1720          break;
1721       }
1722    }
1723    /* Cleanup the temp list */
1724    foreach_alist(elt, clientlist) {
1725       delete elt;
1726    }
1727    delete clientlist;
1728    return true;
1729 }
1730
1731 static bool msgscmd(UAContext *ua, const char *cmd)
1732 {
1733    MSGS *msgs = NULL;
1734    LockRes();
1735    foreach_res(msgs, R_MSGS) {
1736       ua->send_msg("%s\n", msgs->name());
1737    }
1738    UnlockRes();
1739    return true;
1740 }
1741
1742 static bool poolscmd(UAContext *ua, const char *cmd)
1743 {
1744    POOL *pool;
1745    LockRes();
1746    foreach_res(pool, R_POOL) {
1747       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1748          ua->send_msg("%s\n", pool->name());
1749       }
1750    }
1751    UnlockRes();
1752    return true;
1753 }
1754
1755 static bool schedulescmd(UAContext *ua, const char *cmd)
1756 {
1757    SCHED *sched;
1758    LockRes();
1759    foreach_res(sched, R_SCHEDULE) {
1760       if (acl_access_ok(ua, Schedule_ACL, sched->name())) {
1761          ua->send_msg("%s\n", sched->name());
1762       }
1763    }
1764    UnlockRes();
1765    return true;
1766 }
1767
1768 static bool storagecmd(UAContext *ua, const char *cmd)
1769 {
1770    STORE *store;
1771    POOL_MEM tmp;
1772    bool unique=false;
1773    alist *already_in = NULL;
1774
1775    /* .storage unique */
1776    if (find_arg(ua, "unique") > 0) {
1777       unique=true;
1778       already_in = New(alist(10, owned_by_alist));
1779    }
1780
1781    LockRes();
1782    foreach_res(store, R_STORAGE) {
1783       if (acl_access_ok(ua, Storage_ACL, store->name())) {
1784          char *elt;
1785          bool  display=true;
1786
1787          if (unique) {
1788             Mmsg(tmp, "%s:%d", store->address, store->SDport);
1789             foreach_alist(elt, already_in) { /* TODO: See if we need a hash or an ordered list here */
1790                if (strcmp(tmp.c_str(), elt) == 0) {
1791                   display = false;
1792                   break;
1793                }
1794             }
1795             if (display) {
1796                already_in->append(bstrdup(tmp.c_str()));
1797             }
1798          }
1799          if (display) {
1800             ua->send_msg("%s\n", store->name());
1801          }
1802       }
1803    }
1804    UnlockRes();
1805    if (already_in) {
1806       delete already_in;
1807    }
1808    return true;
1809 }
1810
1811 static bool aopcmd(UAContext *ua, const char *cmd)
1812 {
1813    ua->send_msg("None\n");
1814    ua->send_msg("Truncate\n");
1815    return true;
1816 }
1817
1818 static bool typescmd(UAContext *ua, const char *cmd)
1819 {
1820    ua->send_msg("Backup\n");
1821    ua->send_msg("Restore\n");
1822    ua->send_msg("Admin\n");
1823    ua->send_msg("Verify\n");
1824    ua->send_msg("Migrate\n");
1825    ua->send_msg("Copy\n");
1826    return true;
1827 }
1828
1829 static bool tagscmd(UAContext *ua, const char *cmd)
1830 {
1831    uint32_t i = 0;
1832    for (const char *p = debug_get_tag(i++, NULL) ; p ; p = debug_get_tag(i++, NULL)) {
1833       ua->send_msg("%s\n", p);
1834    }
1835    return true;
1836 }
1837
1838 /*
1839  * If this command is called, it tells the director that we
1840  *  are a program that wants a sort of API, and hence,
1841  *  we will probably suppress certain output, include more
1842  *  error codes, and most of all send back a good number
1843  *  of new signals that indicate whether or not the command
1844  *  succeeded.
1845  */
1846 static bool api_cmd(UAContext *ua, const char *cmd)
1847 {
1848    int i;
1849    if (ua->argc >= 2) {
1850       ua->api = atoi(ua->argk[1]);
1851
1852       /* Get output configuration options such as time format or separator */
1853       if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1854          bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1855
1856       } else {
1857          *ua->api_opts = 0;
1858       }
1859    } else {
1860       ua->api = 1;
1861    }
1862    return true;
1863 }
1864
1865 static int client_backups_handler(void *ctx, int num_field, char **row)
1866 {
1867    UAContext *ua = (UAContext *)ctx;
1868    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1869       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1870    return 0;
1871 }
1872
1873 /*
1874  * Return the backups for this client
1875  *
1876  *  .backups client=xxx fileset=yyy
1877  *
1878  */
1879 static bool backupscmd(UAContext *ua, const char *cmd)
1880 {
1881    if (!open_client_db(ua)) {
1882       return true;
1883    }
1884    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1885        strcmp(ua->argk[2], "fileset") != 0) {
1886       return true;
1887    }
1888    if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE) ||
1889        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1890       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1891       return true;
1892    }
1893    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1894    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1895       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1896       return true;
1897    }
1898    return true;
1899 }
1900
1901 static int sql_handler(void *ctx, int num_field, char **row)
1902 {
1903    UAContext *ua = (UAContext *)ctx;
1904    POOL_MEM rows(PM_MESSAGE);
1905
1906    /* Check for nonsense */
1907    if (num_field == 0 || row == NULL || row[0] == NULL) {
1908       return 0;                       /* nothing returned */
1909    }
1910    for (int i=0; num_field--; i++) {
1911       if (i == 0) {
1912          pm_strcpy(rows, NPRT(row[0]));
1913       } else {
1914          pm_strcat(rows, NPRT(row[i]));
1915       }
1916       pm_strcat(rows, "\t");
1917    }
1918    if (!rows.c_str() || !*rows.c_str()) {
1919       ua->send_msg("\t");
1920    } else {
1921       ua->send_msg("%s", rows.c_str());
1922    }
1923    return 0;
1924 }
1925
1926 static bool sql_cmd(UAContext *ua, const char *cmd)
1927 {
1928    int index;
1929    if (!open_new_client_db(ua)) {
1930       return true;
1931    }
1932    index = find_arg_with_value(ua, "query");
1933    if (index < 0) {
1934       ua->error_msg(_("query keyword not found.\n"));
1935       return true;
1936    }
1937    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1938       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1939       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1940       return true;
1941    }
1942    return true;
1943 }
1944
1945 static int one_handler(void *ctx, int num_field, char **row)
1946 {
1947    UAContext *ua = (UAContext *)ctx;
1948    ua->send_msg("%s\n", row[0]);
1949    return 0;
1950 }
1951
1952 static bool mediatypescmd(UAContext *ua, const char *cmd)
1953 {
1954    if (!open_client_db(ua)) {
1955       return true;
1956    }
1957    if (!db_sql_query(ua->db,
1958            "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1959            one_handler, (void *)ua))
1960    {
1961       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1962    }
1963    return true;
1964 }
1965
1966 static bool mediacmd(UAContext *ua, const char *cmd)
1967 {
1968    if (!open_client_db(ua)) {
1969       return true;
1970    }
1971    if (!db_sql_query(ua->db,
1972           "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1973           one_handler, (void *)ua))
1974    {
1975       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1976    }
1977    return true;
1978 }
1979
1980 static bool locationscmd(UAContext *ua, const char *cmd)
1981 {
1982    if (!open_client_db(ua)) {
1983       return true;
1984    }
1985    if (!db_sql_query(ua->db,
1986            "SELECT DISTINCT Location FROM Location ORDER BY Location",
1987            one_handler, (void *)ua))
1988    {
1989       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1990    }
1991    return true;
1992 }
1993
1994 static bool levelscmd(UAContext *ua, const char *cmd)
1995 {
1996    int i;
1997    /* Note some levels are blank, which means none is needed */
1998    if (ua->argc == 1) {
1999       for (i=0; joblevels[i].level_name; i++) {
2000          if (joblevels[i].level_name[0] != ' ') {
2001             ua->send_msg("%s\n", joblevels[i].level_name);
2002          }
2003       }
2004    } else if (ua->argc == 2) {
2005       int jobtype = 0;
2006       /* Assume that first argument is the Job Type */
2007       for (i=0; jobtypes[i].type_name; i++) {
2008          if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
2009             jobtype = jobtypes[i].job_type;
2010             break;
2011          }
2012       }
2013       for (i=0; joblevels[i].level_name; i++) {
2014          if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
2015             ua->send_msg("%s\n", joblevels[i].level_name);
2016          }
2017       }
2018    }
2019
2020    return true;
2021 }
2022
2023 static bool volstatuscmd(UAContext *ua, const char *cmd)
2024 {
2025    ua->send_msg("Append\n");
2026    ua->send_msg("Full\n");
2027    ua->send_msg("Used\n");
2028    ua->send_msg("Recycle\n");
2029    ua->send_msg("Purged\n");
2030    ua->send_msg("Cleaning\n");
2031    ua->send_msg("Error\n");
2032    return true;
2033 }
2034
2035 /*
2036  * Return default values for a job
2037  */
2038 static bool defaultscmd(UAContext *ua, const char *cmd)
2039 {
2040    char ed1[50];
2041    if (ua->argc != 2 || !ua->argv[1]) {
2042       return true;
2043    }
2044
2045    /* Send Job defaults */
2046    if (strcmp(ua->argk[1], "job") == 0) {
2047       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
2048          return true;
2049       }
2050       JOB *job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
2051       if (job) {
2052          USTORE store;
2053          ua->send_msg("job=%s", job->name());
2054          ua->send_msg("pool=%s", job->pool->name());
2055          ua->send_msg("messages=%s", job->messages->name());
2056          ua->send_msg("client=%s", job->client?job->client->name():_("*None*"));
2057          get_job_storage(&store, job, NULL);
2058          ua->send_msg("storage=%s", store.store->name());
2059          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
2060          ua->send_msg("level=%s", level_to_str(job->JobLevel));
2061          ua->send_msg("type=%s", job_type_to_str(job->JobType));
2062          ua->send_msg("fileset=%s", job->fileset->name());
2063          ua->send_msg("enabled=%d", job->is_enabled());
2064          ua->send_msg("catalog=%s", job->client?job->client->catalog->name():_("*None*"));
2065          ua->send_msg("priority=%d", job->Priority);
2066       }
2067    }
2068    /* Send Pool defaults */
2069    else if (strcmp(ua->argk[1], "pool") == 0) {
2070       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
2071          return true;
2072       }
2073       POOL *pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
2074       if (pool) {
2075          ua->send_msg("pool=%s", pool->name());
2076          ua->send_msg("pool_type=%s", pool->pool_type);
2077          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
2078          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
2079          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
2080          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
2081          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
2082          ua->send_msg("max_volumes=%d", pool->max_volumes);
2083          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
2084          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
2085          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
2086          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
2087          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
2088          ua->send_msg("auto_prune=%d", pool->AutoPrune);
2089          ua->send_msg("recycle=%d", pool->Recycle);
2090          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
2091          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
2092       }
2093    } 
2094    /* Send Storage defaults */
2095    else if (strcmp(ua->argk[1], "storage") == 0) {
2096       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
2097          return true;
2098       }
2099       STORE *storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
2100       DEVICE *device;
2101       if (storage) {
2102          ua->send_msg("storage=%s", storage->name());
2103          ua->send_msg("address=%s", storage->address);
2104          ua->send_msg("enabled=%d", storage->is_enabled());
2105          ua->send_msg("media_type=%s", storage->media_type);
2106          ua->send_msg("sdport=%d", storage->SDport);
2107          device = (DEVICE *)storage->device->first();
2108          ua->send_msg("device=%s", device->name());
2109          if (storage->device && storage->device->size() > 1) {
2110             while ((device = (DEVICE *)storage->device->next())) {
2111                ua->send_msg(",%s", device->name());
2112             }
2113          }
2114       }
2115    } 
2116    /* Send Client defaults */
2117    else if (strcmp(ua->argk[1], "client") == 0) {
2118       if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE)) {
2119          return true;
2120       }
2121       CLIENT *client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
2122       if (client) {
2123          ua->send_msg("client=%s", client->name());
2124          ua->send_msg("address=%s", client->address());
2125          ua->send_msg("fdport=%d", client->FDport);
2126          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
2127          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
2128          ua->send_msg("autoprune=%d", client->AutoPrune);
2129          ua->send_msg("catalog=%s", client->catalog->name());
2130       }
2131    }
2132    return true;
2133 }