]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Big backport from Enterprise
[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    fileid = dirid = hardlink = empty;
569
570    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
571                        &limit, &offset) || !path)
572    {
573       ua->error_msg("Can't find jobid, pathid or path argument\n");
574       return true;              /* not enough param */
575    }
576
577    if (!open_new_client_db(ua)) {
578       return true;
579    }
580
581    Bvfs fs(ua->jcr, ua->db);
582    bvfs_set_acl(ua, &fs);
583    fs.set_username(username);
584    fs.set_jobids(jobid);
585
586    if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
587       fileid = ua->argv[i];
588    }
589    if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
590       dirid = ua->argv[i];
591    }
592    if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
593       hardlink = ua->argv[i];
594    }
595    if ((i = find_arg(ua, "nodelta")) >= 0) {
596       fs.set_compute_delta(false);
597    }
598    if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
599       ua->send_msg("OK\n");
600    } else {
601       ua->error_msg("Can't create restore list\n");
602    }
603
604    return true;
605 }
606
607 /* Get a bootstrap for a given bvfs restore session
608  * .bvfs_get_bootstrap path=b21xxxxxx
609  * Volume=Vol1
610  * Storage=Store1
611  * VolAddress=10
612  * VolSessionTime=xxx
613  * VolSessionId=yyyy
614  */
615 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd)
616 {
617    RESTORE_CTX rx;                    /* restore context */
618    POOLMEM *buf = get_pool_memory(PM_MESSAGE);
619    int pos;
620
621    new_rx(&rx);
622    if (!open_new_client_db(ua)) {
623       ua->error_msg("ERROR: Unable to open database\n");
624       goto bail_out;
625    }
626    pos = find_arg_with_value(ua, "path");
627    if (pos < 0) {
628       ua->error_msg("ERROR: Unable to get path argument\n");
629       goto bail_out;
630    }
631
632    insert_table_into_findex_list(ua, &rx, ua->argv[pos]);
633
634    if (rx.bsr_list->size() > 0) {
635       if (!complete_bsr(ua, rx.bsr_list)) {   /* find Vol, SessId, SessTime from JobIds */
636          ua->error_msg("ERROR: Unable to construct a valid BSR. Cannot continue.\n");
637          goto bail_out;
638       }
639       if (!(rx.selected_files = write_bsr_file(ua, rx))) {
640          ua->error_msg("ERROR: No files selected to be restored.\n");
641          goto bail_out;
642       }
643       FILE *fp = bfopen(ua->jcr->RestoreBootstrap, "r");
644       if (!fp) {
645          ua->error_msg("ERROR: Unable to open bootstrap file\n");
646          goto bail_out;
647       }
648       while (bfgets(buf, fp)) {
649          ua->send_msg("%s", buf);
650       }
651       fclose(fp);
652    } else {
653       ua->error_msg("ERROR: Unable to find files to restore\n");
654       goto bail_out;
655    }
656
657 bail_out:
658    if (ua->jcr->unlink_bsr) {
659       unlink(ua->jcr->RestoreBootstrap);
660       ua->jcr->unlink_bsr = false;
661    }
662    free_pool_memory(buf);
663    free_rx(&rx);
664    return true;
665 }
666
667 /*
668  * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
669  * Vol001
670  * Vol002
671  * Vol003
672  */
673 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
674 {
675    DBId_t pathid=0;
676    FileId_t fileid=0;
677    char  *path=NULL, *jobid=NULL, *username=NULL;
678    char  *filename=NULL;
679    int    limit=2000, offset=0;
680    int    i;
681
682    bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
683
684    if ((i = find_arg_with_value(ua, "filename")) >= 0) {
685       if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
686          ua->error_msg("Can't find jobid, pathid or path argument\n");
687          return true;
688       }
689
690       filename = ua->argv[i];
691
692    } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
693       if (!is_a_number(ua->argv[i])) {
694          ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
695          return true;
696       }
697       fileid = str_to_int64(ua->argv[i]);
698    }
699
700    if (!open_new_client_db(ua)) {
701       return 1;
702    }
703
704    Bvfs fs(ua->jcr, ua->db);
705    bvfs_set_acl(ua, &fs);
706    fs.set_username(username);
707    fs.set_handler(bvfs_result_handler, ua);
708    fs.set_limit(limit);
709    ua->bvfs = &fs;
710
711    if (filename) {
712       /* TODO */
713
714    } else {
715       fs.get_volumes(fileid);
716    }
717    ua->bvfs = NULL;
718    return true;
719 }
720
721 /*
722  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
723  * .bvfs_lsfiles jobid=1,2,3,4 path=/
724  */
725 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
726 {
727    DBId_t pathid=0;
728    int limit=2000, offset=0;
729    char *path=NULL, *jobid=NULL, *username=NULL;
730    char *pattern=NULL, *filename=NULL;
731    bool ok;
732    int i;
733
734    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
735                        &limit, &offset))
736    {
737       ua->error_msg("Can't find jobid, pathid or path argument\n");
738       return true;              /* not enough param */
739    }
740    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
741       pattern = ua->argv[i];
742    }
743    if ((i = find_arg_with_value(ua, "filename")) >= 0) {
744       filename = ua->argv[i];
745    }
746
747    if (!open_new_client_db(ua)) {
748       return 1;
749    }
750
751    Bvfs fs(ua->jcr, ua->db);
752    bvfs_set_acl(ua, &fs);
753    fs.set_username(username);
754    fs.set_jobids(jobid);
755    fs.set_handler(bvfs_result_handler, ua);
756    fs.set_limit(limit);
757    fs.set_offset(offset);
758    ua->bvfs = &fs;
759    if (pattern) {
760       fs.set_pattern(pattern);
761    }
762    if (filename) {
763       fs.set_filename(filename);
764    }
765    if (pathid) {
766       ok = fs.ch_dir(pathid);
767    } else {
768       ok = fs.ch_dir(path);
769    }
770    if (!ok) {
771       goto bail_out;
772    }
773
774    fs.ls_files();
775
776 bail_out:
777    ua->bvfs = NULL;
778    return true;
779 }
780
781 /*
782  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
783  * .bvfs_lsdirs jobid=1,2,3,4 path=/
784  * .bvfs_lsdirs jobid=1,2,3,4 path=
785  */
786 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
787 {
788    DBId_t pathid=0;
789    int   limit=2000, offset=0;
790    char *path=NULL, *jobid=NULL, *username=NULL;
791    char *pattern=NULL;
792    int   dironly;
793    bool  ok;
794    int i;
795
796    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
797                        &limit, &offset))
798    {
799       ua->error_msg("Can't find jobid, pathid or path argument\n");
800       return true;              /* not enough param */
801    }
802
803    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
804       pattern = ua->argv[i];
805    }
806
807    dironly = find_arg(ua, "dironly");
808
809    if (!open_new_client_db(ua)) {
810       return 1;
811    }
812
813    Bvfs fs(ua->jcr, ua->db);
814    bvfs_set_acl(ua, &fs);
815    fs.set_username(username);
816    fs.set_jobids(jobid);
817    fs.set_limit(limit);
818    fs.set_handler(bvfs_result_handler, ua);
819    fs.set_offset(offset);
820    ua->bvfs = &fs;
821
822    if (pattern) {
823       fs.set_pattern(pattern);
824    }
825
826    if (pathid) {
827       ok = fs.ch_dir(pathid);
828    } else {
829       ok = fs.ch_dir(path);
830    }
831
832    if (!ok) {
833       goto bail_out;
834    }
835
836    fs.ls_special_dirs();
837
838    if (dironly < 0) {
839       fs.ls_dirs();
840    }
841 bail_out:
842    ua->bvfs = NULL;
843    return true;
844 }
845
846 /*
847  * .bvfs_get_delta fileid=10 
848  *
849  */
850 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd)
851 {
852    bool ret;
853    FileId_t fileid=0;
854    int i;
855
856    if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
857       if (!is_a_number(ua->argv[i])) {
858          ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
859          return true;
860       }
861       fileid = str_to_int64(ua->argv[i]);
862
863    } else {
864       ua->error_msg("Expecting FileId\n");
865       return true;
866    }
867
868    if (!open_new_client_db(ua)) {
869       return 1;
870    }
871    Bvfs fs(ua->jcr, ua->db);
872    bvfs_set_acl(ua, &fs);
873    fs.set_handler(bvfs_result_handler, ua);
874    ua->bvfs = &fs;
875    ret = fs.get_delta(fileid);
876    ua->bvfs = NULL;
877    return ret;
878 }
879
880 /*
881  * .bvfs_versions fnid=10 pathid=10 client=xxx copies versions
882  *
883  */
884 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
885 {
886    DBId_t pathid=0;
887    FileId_t fnid=0;
888    int limit=2000, offset=0;
889    char *path=NULL, *client=NULL, *username=NULL;
890    bool copies=false, versions=false;
891    if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
892                        &limit, &offset))
893    {
894       ua->error_msg("Can't find pathid or path argument\n");
895       return true;              /* not enough param */
896    }
897
898    if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
899    {
900       ua->error_msg("Can't find client or fnid argument\n");
901       return true;              /* not enough param */
902    }
903
904    if (!open_new_client_db(ua)) {
905       return 1;
906    }
907
908    Bvfs fs(ua->jcr, ua->db);
909    bvfs_set_acl(ua, &fs);
910    fs.set_limit(limit);
911    fs.set_see_all_versions(versions);
912    fs.set_see_copies(copies);
913    fs.set_handler(bvfs_result_handler, ua);
914    fs.set_offset(offset);
915    ua->bvfs = &fs;
916
917    fs.get_all_file_versions(pathid, fnid, client);
918
919    ua->bvfs = NULL;
920    return true;
921 }
922
923 /* .bvfs_get_jobids jobid=1
924  *  -> returns needed jobids to restore
925  * .bvfs_get_jobids ujobid=xxx only
926  *  -> returns the jobid of the job
927  * .bvfs_get_jobids jobid=1 jobname
928  *  -> returns the jobname
929  * .bvfs_get_jobids client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
930  *  -> returns all jobid for the client
931  * .bvfs_get_jobids client=xxx count
932  *  -> returns the number of jobids for the client
933  * .bvfs_get_jobids jobid=1 all
934  *  -> returns needed jobids to restore with all filesets a JobId=1 time
935  * .bvfs_get_jobids job=XXXXX
936  *  -> returns needed jobids to restore with the jobname
937  * .bvfs_get_jobids ujobid=JobName
938  *  -> returns needed jobids to restore
939  */
940 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
941 {
942    JOB_DBR jr;
943    memset(&jr, 0, sizeof(JOB_DBR));
944
945    db_list_ctx jobids, tempids;
946    int pos;
947    char ed1[50];
948    POOL_MEM query;
949    dbid_list ids;               /* Store all FileSetIds for this client */
950
951    if (!open_new_client_db(ua)) {
952       return true;
953    }
954
955    Bvfs fs(ua->jcr, ua->db);
956    bvfs_set_acl(ua, &fs);
957
958    if ((pos = find_arg_with_value(ua, "username")) >= 0) {
959       fs.set_username(ua->argv[pos]);
960    }
961
962    if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
963       bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
964    }
965
966    if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
967       jr.JobId = str_to_int64(ua->argv[pos]);
968
969    /* Guess JobId from Job name, take the last successful jobid */
970    } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
971       JOB *job;
972       bool ret;
973       int32_t JobId=0;
974
975       bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
976       /* TODO: enhance this function to take client and/or fileset as argument*/
977
978       job = GetJobResWithName(jr.Name);
979       if (!job) {
980          ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
981          return true;
982       }
983       db_lock(ua->db);
984       Mmsg(ua->db->cmd,
985       "SELECT JobId "
986         "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
987          "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
988            "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
989          "ORDER By JobTDate DESC LIMIT 1",
990            job->client->name(), job->fileset->name());
991       ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
992       db_unlock(ua->db);
993
994       if (!ret) {
995          ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
996       }
997
998       jr.JobId = JobId;
999
1000    /* Get JobId from ujobid */
1001    } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
1002       bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
1003
1004    /* Return all backup jobid for a client */
1005    } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
1006       CLIENT *cli;
1007       POOL_MEM where;
1008       char limit[50];
1009       bool ret;
1010       int  nbjobs;
1011
1012       cli = GetClientResWithName(ua->argv[pos]);
1013       if (!cli) {
1014          ua->error_msg(_("Unable to get Client record for Client=%s\n"),
1015                        ua->argv[pos]);
1016          return true;
1017       }
1018       db_lock(ua->db);
1019
1020       bvfs_get_filter(ua, where, limit, sizeof(limit));
1021
1022       Mmsg(ua->db->cmd,
1023       "SELECT JobId "
1024         "FROM Job JOIN Client USING (ClientId) "
1025          "WHERE Client.Name = '%s' "
1026            "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') %s "
1027          "ORDER By JobTDate ASC %s",
1028            cli->name(), where.c_str(), limit);
1029       ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
1030       db_unlock(ua->db);
1031
1032       if (!ret) {
1033          ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
1034                        cli->name());
1035       }
1036
1037       nbjobs = fs.set_jobids(jobids.list);
1038
1039       /* Apply the ACL filter on JobIds */
1040       if (find_arg(ua, "count") >= 0) {
1041          ua->send_msg("%d\n", nbjobs);
1042
1043       } else {
1044          ua->send_msg("%s\n", fs.get_jobids());
1045       }
1046       return true;
1047    }
1048
1049    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
1050       ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
1051                     ua->cmd, db_strerror(ua->db));
1052       return true;
1053    }
1054
1055    /* Display only the requested jobid or
1056     * When in level base, we don't rely on any Full/Incr/Diff
1057     */
1058    if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
1059       /* Apply the ACL filter on JobIds */
1060       fs.set_jobid(jr.JobId);
1061       ua->send_msg("%s\n", fs.get_jobids());
1062       return true;
1063    }
1064
1065    /* Display only the requested job name
1066     */
1067    if (find_arg(ua, "jobname") > 0) {
1068       /* Apply the ACL filter on JobIds */
1069       fs.set_jobid(jr.JobId);
1070       if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
1071          ua->send_msg("%s\n", jr.Job);
1072       }
1073       return true;
1074    }
1075
1076    /* If we have the "all" option, we do a search on all defined fileset
1077     * for this client
1078     */
1079    if (find_arg(ua, "all") > 0) {
1080       edit_int64(jr.ClientId, ed1);
1081       Mmsg(query, uar_sel_filesetid, ed1);
1082       db_get_query_dbids(ua->jcr, ua->db, query, ids);
1083    } else {
1084       ids.num_ids = 1;
1085       ids.DBId[0] = jr.FileSetId;
1086    }
1087
1088    jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
1089
1090    /* Foreach different FileSet, we build a restore jobid list */
1091    for (int i=0; i < ids.num_ids; i++) {
1092       jr.FileSetId = ids.DBId[i];
1093       if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &tempids)) {
1094          return true;
1095       }
1096       jobids.add(tempids);
1097    }
1098
1099    fs.set_jobids(jobids.list);
1100    ua->send_msg("%s\n", fs.get_jobids());
1101    return true;
1102 }
1103
1104 static int jobs_handler(void *ctx, int num_field, char **row)
1105 {
1106    UAContext *ua = (UAContext *)ctx;
1107    ua->send_msg("%s %s %s %s\n", row[0], row[1], row[2], row[3]);
1108    return 0;
1109 }
1110
1111 static char *get_argument(UAContext *ua, const char *arg, char *esc, bool convert)
1112 {
1113    int pos;
1114    if (((pos = find_arg_with_value(ua, arg)) < 0) ||
1115        (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1116    {
1117       return NULL;
1118    }
1119    db_escape_string(ua->jcr, ua->db, esc,
1120                     ua->argv[pos], strlen(ua->argv[pos]));
1121    if (convert) {
1122       for (int i=0; esc[i] ; i++) {
1123          if (esc[i] == '*') {
1124             esc[i] = '%';
1125          }
1126       }
1127    }
1128    return esc;
1129 }
1130
1131 /* The DB should be locked */
1132 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len)
1133 {
1134    POOL_MEM tmp;
1135    char esc_name[MAX_ESCAPE_NAME_LENGTH];
1136
1137    if (get_argument(ua, "jobname", esc_name, true) != NULL) {
1138       Mmsg(where, "AND Job.Job LIKE '%s' ", esc_name);
1139    }
1140
1141    if (get_argument(ua, "fileset", esc_name, true) != NULL) {
1142       Mmsg(tmp, "AND FileSet.FileSet LIKE '%s' ", esc_name);
1143       pm_strcat(where, tmp.c_str());
1144    }
1145
1146    if (get_argument(ua, "jobid", esc_name, false) != NULL) {
1147       Mmsg(tmp, "AND Job.JobId = '%s' ", esc_name);
1148       pm_strcat(where, tmp.c_str());
1149    }
1150
1151    if (get_argument(ua, "ujobid", esc_name, false) != NULL) {
1152       Mmsg(tmp, "AND Job.Job = '%s' ", esc_name);
1153       pm_strcat(where, tmp.c_str());
1154    }
1155
1156    if (get_argument(ua, "start", esc_name, false) != NULL) {
1157       Mmsg(tmp, "AND Job.StartTime >= '%s' ", esc_name);
1158       pm_strcat(where, tmp.c_str());
1159    }
1160
1161    if (get_argument(ua, "end", esc_name, false) != NULL) {
1162       Mmsg(tmp, "AND Job.EndTime <= '%s' ", esc_name);
1163       pm_strcat(where, tmp.c_str());
1164    }
1165
1166    *limit = 0;
1167    if (get_argument(ua, "limit", esc_name, false) != NULL) {
1168       if (is_a_number(esc_name)) {
1169          bsnprintf(limit, len, "LIMIT %s ", esc_name);
1170       }
1171    }
1172 }
1173
1174 /* .bvfs_get_jobs client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
1175  * 1 yyyyy 1 Backup1_xxx_xxx_xxxx_xxx
1176  * 2 yyyyy 0 Backup1_xxx_xxx_xxxx_xxx
1177  */
1178 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd)
1179 {
1180    int pos;
1181    POOL_MEM where;
1182    char esc_cli[MAX_ESCAPE_NAME_LENGTH];
1183    char limit[MAX_ESCAPE_NAME_LENGTH];
1184    if (!open_new_client_db(ua)) {
1185       return true;
1186    }
1187
1188    if (((pos = find_arg_with_value(ua, "client")) < 0) ||
1189        (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1190    {
1191       return true;
1192    }
1193
1194    /* TODO: Do checks on Jobs, FileSet, etc... */
1195    if (!acl_access_client_ok(ua, ua->argv[pos], JT_BACKUP_RESTORE)) {
1196       return true;
1197    }
1198
1199    db_lock(ua->db);
1200    db_escape_string(ua->jcr, ua->db, esc_cli,
1201                     ua->argv[pos], strlen(ua->argv[pos]));
1202
1203    bvfs_get_filter(ua, where, limit, sizeof(limit));
1204
1205    Mmsg(ua->db->cmd,
1206         "SELECT JobId, JobTDate, HasCache, Job "
1207           "FROM Job JOIN Client USING (ClientId) JOIN FileSet USING (FileSetId) "
1208          "WHERE Client.Name = '%s' AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
1209             "%s "
1210          "ORDER By JobTDate DESC %s",
1211         esc_cli, where.c_str(), limit);
1212
1213    db_sql_query(ua->db, ua->db->cmd, jobs_handler, ua);
1214    db_unlock(ua->db);
1215    return true;
1216 }
1217
1218 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
1219 {
1220    quit_cmd(ua, cmd);
1221    return true;
1222 }
1223
1224 static bool dot_help_cmd(UAContext *ua, const char *cmd)
1225 {
1226    qhelp_cmd(ua, cmd);
1227    return true;
1228 }
1229
1230 static bool getmsgscmd(UAContext *ua, const char *cmd)
1231 {
1232    if (console_msg_pending) {
1233       do_messages(ua, cmd);
1234    }
1235    return 1;
1236 }
1237
1238 #ifdef DEVELOPER
1239 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
1240 {
1241    BSOCK *sd;
1242    JCR *jcr = ua->jcr;
1243    USTORE lstore;
1244
1245    lstore.store = store;
1246    pm_strcpy(lstore.store_source, _("unknown source"));
1247    set_wstorage(jcr, &lstore);
1248    /* Try connecting for up to 15 seconds */
1249    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
1250       store->name(), store->address, store->SDport);
1251    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
1252       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1253       return;
1254    }
1255    Dmsg0(120, _("Connected to storage daemon\n"));
1256    sd = jcr->store_bsock;
1257    sd->fsend("%s", cmd);
1258    if (sd->recv() >= 0) {
1259       ua->send_msg("%s", sd->msg);
1260    }
1261    sd->signal(BNET_TERMINATE);
1262    free_bsock(ua->jcr->store_bsock);
1263    return;
1264 }
1265
1266 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
1267 {
1268    BSOCK *fd;
1269
1270    /* Connect to File daemon */
1271
1272    ua->jcr->client = client;
1273    /* Try to connect for 15 seconds */
1274    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1275       client->name(), client->address(), client->FDport);
1276    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1277       ua->error_msg(_("Failed to connect to Client.\n"));
1278       return;
1279    }
1280    Dmsg0(120, "Connected to file daemon\n");
1281    fd = ua->jcr->file_bsock;
1282    fd->fsend("%s", cmd);
1283    if (fd->recv() >= 0) {
1284       ua->send_msg("%s", fd->msg);
1285    }
1286    fd->signal(BNET_TERMINATE);
1287    free_bsock(ua->jcr->file_bsock);
1288    return;
1289 }
1290
1291 /*
1292  *   .die (seg fault)
1293  *   .dump (sm_dump)
1294  *   .exit (no arg => .quit)
1295  */
1296 static bool admin_cmds(UAContext *ua, const char *cmd)
1297 {
1298    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1299    STORE *store=NULL;
1300    CLIENT *client=NULL;
1301    bool dir=false;
1302    bool do_deadlock=false;
1303    const char *remote_cmd;
1304    int i;
1305    JCR *jcr = NULL;
1306    int a;
1307    if (strncmp(ua->argk[0], ".die", 4) == 0) {
1308       if (find_arg(ua, "deadlock") > 0) {
1309          do_deadlock = true;
1310          remote_cmd = ".die deadlock";
1311       } else {
1312          remote_cmd = ".die";
1313       }
1314    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
1315       remote_cmd = "sm_dump";
1316    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
1317       remote_cmd = "exit";
1318    } else {
1319       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1320       return true;
1321    }
1322    /* General debug? */
1323    for (i=1; i<ua->argc; i++) {
1324       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1325           strcasecmp(ua->argk[i], "director") == 0) {
1326          dir = true;
1327       }
1328       if (strcasecmp(ua->argk[i], "client") == 0 ||
1329           strcasecmp(ua->argk[i], "fd") == 0) {
1330          client = NULL;
1331          if (ua->argv[i]) {
1332             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1333          }
1334          if (!client) {
1335             client = select_client_resource(ua, JT_SYSTEM);
1336          }
1337       }
1338
1339       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1340           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1341           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1342          store = NULL;
1343          if (ua->argv[i]) {
1344             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1345          }
1346          if (!store) {
1347             store = get_storage_resource(ua, false/*no default*/);
1348          }
1349       }
1350    }
1351
1352    if (!dir && !store && !client) {
1353       /*
1354        * We didn't find an appropriate keyword above, so
1355        * prompt the user.
1356        */
1357       start_prompt(ua, _("Available daemons are: \n"));
1358       add_prompt(ua, _("Director"));
1359       add_prompt(ua, _("Storage"));
1360       add_prompt(ua, _("Client"));
1361       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
1362       case 0:                         /* Director */
1363          dir=true;
1364          break;
1365       case 1:
1366          store = get_storage_resource(ua, false/*no default*/);
1367          break;
1368       case 2:
1369          client = select_client_resource(ua, JT_BACKUP_RESTORE);
1370          break;
1371       default:
1372          break;
1373       }
1374    }
1375
1376    if (store) {
1377       do_storage_cmd(ua, store, remote_cmd);
1378    }
1379
1380    if (client) {
1381       do_client_cmd(ua, client, remote_cmd);
1382    }
1383
1384    if (dir) {
1385       if (strncmp(remote_cmd, ".die", 4) == 0) {
1386          if (do_deadlock) {
1387             ua->send_msg(_("The Director will generate a deadlock.\n"));
1388             P(mutex);
1389             P(mutex);
1390          }
1391          ua->send_msg(_("The Director will segment fault.\n"));
1392          a = jcr->JobId; /* ref NULL pointer */
1393          jcr->JobId = 1000; /* another ref NULL pointer */
1394          jcr->JobId = a;
1395
1396       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
1397          sm_dump(false, true);
1398       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
1399          dot_quit_cmd(ua, cmd);
1400       }
1401    }
1402
1403    return true;
1404 }
1405
1406 #else
1407
1408 /*
1409  * Dummy routine for non-development version
1410  */
1411 static bool admin_cmds(UAContext *ua, const char *cmd)
1412 {
1413    ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1414    return true;
1415 }
1416
1417 #endif
1418
1419 /*
1420  * Send a file to the director from bconsole @putfile command
1421  * The .putfile can not be used directly.
1422  */
1423 static bool putfile_cmd(UAContext *ua, const char *cmd)
1424 {
1425    int         pos, i, pnl, fnl;
1426    bool        ok = true;
1427    POOLMEM    *name = get_pool_memory(PM_FNAME);
1428    POOLMEM    *path = get_pool_memory(PM_FNAME);
1429    POOLMEM    *fname= get_pool_memory(PM_FNAME);
1430    const char *key = "putfile";
1431    FILE       *fp = NULL;
1432
1433    if ((pos = find_arg_with_value(ua, "key")) > 0) {
1434       /* Check the string if the string is valid */
1435       for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1436
1437       if (ua->argv[pos][i] == 0) {
1438          key = ua->argv[pos];
1439
1440       } else {
1441          ua->error_msg("Invalid key name for putfile command");
1442          ok = false;
1443          goto bail_out;
1444       }
1445    }
1446
1447    /* the (intptr_t)ua will allow one file per console session */
1448    make_unique_filename(&name, (intptr_t)ua, (char *)key);
1449
1450    fp = bfopen(name, "w");
1451    if (!fp) {
1452       berrno be;
1453       ua->error_msg("Unable to open destination file. ERR=%s\n",
1454                     be.bstrerror(errno));
1455       ok = false;
1456       goto bail_out;
1457    }
1458
1459    while (ua->UA_sock->recv() > 0) {
1460       if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1461          berrno be;
1462          ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1463                        be.bstrerror(errno));
1464          ok = false;
1465          /* TODO: Check if we need to quit here (data will still be in the
1466           * buffer...) */
1467       }
1468    }
1469
1470    split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1471
1472 bail_out:
1473    if (ok) {
1474       ua->send_msg("OK\n");
1475
1476    } else {
1477       ua->send_msg("ERROR\n");
1478    }
1479
1480    free_pool_memory(name);
1481    free_pool_memory(path);
1482    free_pool_memory(fname);
1483    if (fp) {
1484       fclose(fp);
1485    }
1486    return true;
1487 }
1488
1489 /* .estimate command */
1490 static bool dotestimatecmd(UAContext *ua, const char *cmd)
1491 {
1492    JOB *jres;
1493    JOB_DBR jr;
1494    //FILESET_DBR fr;
1495    //CLIENT_DBR cr;
1496    char *job = NULL, level = 0, *fileset = NULL, *client = NULL;
1497    memset(&jr, 0, sizeof(jr));
1498
1499    for (int i = 1 ; i < ua->argc ; i++) {
1500       if (!ua->argv[i]) {
1501          ua->error_msg(_("Invalid argument for %s\n"), ua->argk[i]);
1502          return true;
1503
1504       } else if (strcasecmp(ua->argk[i], "job") == 0) {
1505          job = ua->argv[i];
1506
1507       } else if (strcasecmp(ua->argk[i], "level") == 0) {
1508          level = toupper(ua->argv[i][0]);
1509
1510       } else if (strcasecmp(ua->argk[i], "fileset") == 0) {
1511          fileset = ua->argv[i];
1512
1513       } else if (strcasecmp(ua->argk[i], "client") == 0) {
1514          client = ua->argv[i];
1515       }
1516    }
1517    if (!job) {
1518       ua->error_msg(_("Invalid argument for job\n"));
1519       return true;
1520    }
1521    if (!acl_access_ok(ua, Job_ACL, job) ||
1522        (fileset && !acl_access_ok(ua, FileSet_ACL, fileset)) ||
1523        (client && !acl_access_client_ok(ua, client, JT_BACKUP)))
1524    {
1525       ua->error_msg(_("Access to specified Job, FileSet or Client not allowed.\n"));
1526       return true;
1527    }
1528    jres = (JOB *) GetResWithName(R_JOB, job);
1529    if (!jres) {
1530       ua->error_msg(_("Invalid argument for job\n"));
1531       return true;
1532    }
1533    if (!open_client_db(ua)) {
1534       ua->error_msg(_("Unable to open the catalog.\n"));
1535       return true;
1536    }
1537    
1538    bstrncpy(jr.Name, jres->hdr.name, sizeof(jr.Name));
1539    jr.JobLevel = level ? level : jres->JobLevel;
1540    if (fileset) {
1541       /* Get FileSetId */
1542    }
1543    if (client) {
1544       /* Get ClientId */
1545    }
1546    db_lock(ua->db);
1547    if (db_get_job_statistics(ua->jcr, ua->db, &jr)) {
1548       db_unlock(ua->db);
1549       OutputWriter o(ua->api_opts);
1550       char *p = o.get_output(OT_START_OBJ,
1551                    OT_JOBLEVEL, "level",     jr.JobLevel,
1552                    OT_INT,      "nbjob",     jr.CorrNbJob,
1553                    OT_INT,      "corrbytes", jr.CorrJobBytes,
1554                    OT_SIZE,     "jobbytes",  jr.JobBytes,
1555                    OT_INT,      "corrfiles", jr.CorrJobFiles,
1556                    OT_INT32,    "jobfiles",  jr.JobFiles,
1557                    OT_INT,      "duration",  (int)0,
1558                    OT_STRING,   "job",       jres->hdr.name,
1559                    OT_END_OBJ,
1560                    OT_END);
1561       ua->send_msg("%s", p);
1562    } else {
1563       /* We unlock the DB after the errmsg copy */
1564       pm_strcpy(ua->jcr->errmsg, ua->db->errmsg);
1565       db_unlock(ua->db);
1566       ua->error_msg("Error with .estimate %s\n", ua->jcr->errmsg);
1567    }
1568    return true;
1569 }
1570
1571
1572 /*
1573  * Can use an argument to filter on JobType
1574  * .jobs [type=B] or [type=!B]
1575  */
1576 static bool jobscmd(UAContext *ua, const char *cmd)
1577 {
1578    JOB *job;
1579    uint32_t type = 0;
1580    bool exclude=false;
1581    int pos;
1582    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1583       if (ua->argv[pos][0] == '!') {
1584          exclude = true;
1585          type = ua->argv[pos][1];
1586       } else {
1587          type = ua->argv[pos][0];
1588       }
1589    }
1590    LockRes();
1591    foreach_res(job, R_JOB) {
1592       if (type) {
1593          if ((exclude && type == job->JobType) || (!exclude && type != job->JobType)) {
1594             continue;
1595          }
1596       }
1597       if (acl_access_ok(ua, Job_ACL, job->name())) {
1598          ua->send_msg("%s\n", job->name());
1599       }
1600    }
1601    UnlockRes();
1602    return true;
1603 }
1604
1605 static bool filesetscmd(UAContext *ua, const char *cmd)
1606 {
1607    FILESET *fs;
1608    LockRes();
1609    foreach_res(fs, R_FILESET) {
1610       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1611          ua->send_msg("%s\n", fs->name());
1612       }
1613    }
1614    UnlockRes();
1615    return true;
1616 }
1617
1618 static bool catalogscmd(UAContext *ua, const char *cmd)
1619 {
1620    CAT *cat;
1621    LockRes();
1622    foreach_res(cat, R_CATALOG) {
1623       if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1624          ua->send_msg("%s\n", cat->name());
1625       }
1626    }
1627    UnlockRes();
1628    return true;
1629 }
1630
1631 /* This is not a good idea to lock the entire resource list to send information
1632  * on the network or query the DNS. So, we don't use the foreach_res() command
1633  * with a global lock and we do a copy of the client list in a specific list to
1634  * avoid any problem, I'm pretty sure we can use the res_head directly without
1635  * a global lock, but it needs testing to avoid race conditions.
1636  */
1637 class TmpClient
1638 {
1639 public:
1640    char *name;
1641    char *address;
1642
1643    TmpClient(char *n, char *a):
1644      name(bstrdup(n)), address(bstrdup(a))
1645    {
1646    };
1647    ~TmpClient() {
1648       free(name);
1649       free(address);
1650    };
1651 };
1652
1653 static bool clientscmd(UAContext *ua, const char *cmd)
1654 {
1655    int i;
1656    CLIENT *client;
1657    const char *ip=NULL;
1658    bool    found=false;
1659    alist  *clientlist = NULL;
1660    TmpClient *elt;
1661
1662    if ((i = find_arg_with_value(ua, "address")) >= 0) {
1663       ip = ua->argv[i];
1664       clientlist = New(alist(50, not_owned_by_alist));
1665    }
1666
1667    /* This is not a good idea to lock the entire resource list
1668     * to send information on the network or query the DNS. So,
1669     * we don't use the foreach_res() command with a global lock here.
1670     */
1671    LockRes();
1672    foreach_res(client, R_CLIENT) {
1673       if (acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
1674          if (ip) {
1675             elt = new TmpClient(client->name(), client->address());
1676             clientlist->append(elt);
1677
1678          } else {
1679             /* do not check for a specific ip, display everything */
1680             ua->send_msg("%s\n", client->name());
1681          }
1682       }
1683    }
1684    UnlockRes();
1685
1686    if (!ip) {
1687       return true;
1688    }
1689
1690    foreach_alist(elt, clientlist) {
1691       /* We look for a client that matches the specific ip address */
1692       dlist  *addr_list=NULL;
1693       IPADDR *ipaddr;
1694       char    buf[128];
1695       const char *errstr;
1696
1697       if (strcmp(elt->address, ip) == 0) {
1698          found = true;
1699
1700       } else if ((addr_list = bnet_host2ipaddrs(elt->address, 0, &errstr)) == NULL) {
1701          Dmsg2(10, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n",
1702                elt->address, errstr);
1703
1704       } else {
1705          /* Try to find the ip address from the list, we might have
1706           * other ways to compare ip addresses
1707           */
1708          foreach_dlist(ipaddr, addr_list) {
1709             if (strcmp(ip, ipaddr->get_address(buf, sizeof(buf))) == 0) {
1710                found = true;
1711                break;
1712             }
1713          }
1714          free_addresses(addr_list);
1715       }
1716
1717       if (found) {
1718          ua->send_msg("%s\n", elt->name);
1719          break;
1720       }
1721    }
1722    /* Cleanup the temp list */
1723    foreach_alist(elt, clientlist) {
1724       delete elt;
1725    }
1726    delete clientlist;
1727    return true;
1728 }
1729
1730 static bool msgscmd(UAContext *ua, const char *cmd)
1731 {
1732    MSGS *msgs = NULL;
1733    LockRes();
1734    foreach_res(msgs, R_MSGS) {
1735       ua->send_msg("%s\n", msgs->name());
1736    }
1737    UnlockRes();
1738    return true;
1739 }
1740
1741 static bool poolscmd(UAContext *ua, const char *cmd)
1742 {
1743    POOL *pool;
1744    LockRes();
1745    foreach_res(pool, R_POOL) {
1746       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1747          ua->send_msg("%s\n", pool->name());
1748       }
1749    }
1750    UnlockRes();
1751    return true;
1752 }
1753
1754 static bool schedulescmd(UAContext *ua, const char *cmd)
1755 {
1756    SCHED *sched;
1757    LockRes();
1758    foreach_res(sched, R_SCHEDULE) {
1759       if (acl_access_ok(ua, Schedule_ACL, sched->name())) {
1760          ua->send_msg("%s\n", sched->name());
1761       }
1762    }
1763    UnlockRes();
1764    return true;
1765 }
1766
1767 static bool storagecmd(UAContext *ua, const char *cmd)
1768 {
1769    STORE *store;
1770    POOL_MEM tmp;
1771    bool unique=false;
1772    alist *already_in = NULL;
1773
1774    /* .storage unique */
1775    if (find_arg(ua, "unique") > 0) {
1776       unique=true;
1777       already_in = New(alist(10, owned_by_alist));
1778    }
1779
1780    LockRes();
1781    foreach_res(store, R_STORAGE) {
1782       if (acl_access_ok(ua, Storage_ACL, store->name())) {
1783          char *elt;
1784          bool  display=true;
1785
1786          if (unique) {
1787             Mmsg(tmp, "%s:%d", store->address, store->SDport);
1788             foreach_alist(elt, already_in) { /* TODO: See if we need a hash or an ordered list here */
1789                if (strcmp(tmp.c_str(), elt) == 0) {
1790                   display = false;
1791                   break;
1792                }
1793             }
1794             if (display) {
1795                already_in->append(bstrdup(tmp.c_str()));
1796             }
1797          }
1798          if (display) {
1799             ua->send_msg("%s\n", store->name());
1800          }
1801       }
1802    }
1803    UnlockRes();
1804    if (already_in) {
1805       delete already_in;
1806    }
1807    return true;
1808 }
1809
1810 static bool aopcmd(UAContext *ua, const char *cmd)
1811 {
1812    ua->send_msg("None\n");
1813    ua->send_msg("Truncate\n");
1814    return true;
1815 }
1816
1817 static bool typescmd(UAContext *ua, const char *cmd)
1818 {
1819    ua->send_msg("Backup\n");
1820    ua->send_msg("Restore\n");
1821    ua->send_msg("Admin\n");
1822    ua->send_msg("Verify\n");
1823    ua->send_msg("Migrate\n");
1824    ua->send_msg("Copy\n");
1825    return true;
1826 }
1827
1828 static bool tagscmd(UAContext *ua, const char *cmd)
1829 {
1830    uint32_t i = 0;
1831    for (const char *p = debug_get_tag(i++, NULL) ; p ; p = debug_get_tag(i++, NULL)) {
1832       ua->send_msg("%s\n", p);
1833    }
1834    return true;
1835 }
1836
1837 /*
1838  * If this command is called, it tells the director that we
1839  *  are a program that wants a sort of API, and hence,
1840  *  we will probably suppress certain output, include more
1841  *  error codes, and most of all send back a good number
1842  *  of new signals that indicate whether or not the command
1843  *  succeeded.
1844  */
1845 static bool api_cmd(UAContext *ua, const char *cmd)
1846 {
1847    int i;
1848    if (ua->argc >= 2) {
1849       ua->api = atoi(ua->argk[1]);
1850
1851       /* Get output configuration options such as time format or separator */
1852       if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1853          bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1854
1855       } else {
1856          *ua->api_opts = 0;
1857       }
1858    } else {
1859       ua->api = 1;
1860    }
1861    return true;
1862 }
1863
1864 static int client_backups_handler(void *ctx, int num_field, char **row)
1865 {
1866    UAContext *ua = (UAContext *)ctx;
1867    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1868       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1869    return 0;
1870 }
1871
1872 /*
1873  * Return the backups for this client
1874  *
1875  *  .backups client=xxx fileset=yyy
1876  *
1877  */
1878 static bool backupscmd(UAContext *ua, const char *cmd)
1879 {
1880    if (!open_client_db(ua)) {
1881       return true;
1882    }
1883    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1884        strcmp(ua->argk[2], "fileset") != 0) {
1885       return true;
1886    }
1887    if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE) ||
1888        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1889       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1890       return true;
1891    }
1892    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1893    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1894       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1895       return true;
1896    }
1897    return true;
1898 }
1899
1900 static int sql_handler(void *ctx, int num_field, char **row)
1901 {
1902    UAContext *ua = (UAContext *)ctx;
1903    POOL_MEM rows(PM_MESSAGE);
1904
1905    /* Check for nonsense */
1906    if (num_field == 0 || row == NULL || row[0] == NULL) {
1907       return 0;                       /* nothing returned */
1908    }
1909    for (int i=0; num_field--; i++) {
1910       if (i == 0) {
1911          pm_strcpy(rows, NPRT(row[0]));
1912       } else {
1913          pm_strcat(rows, NPRT(row[i]));
1914       }
1915       pm_strcat(rows, "\t");
1916    }
1917    if (!rows.c_str() || !*rows.c_str()) {
1918       ua->send_msg("\t");
1919    } else {
1920       ua->send_msg("%s", rows.c_str());
1921    }
1922    return 0;
1923 }
1924
1925 static bool sql_cmd(UAContext *ua, const char *cmd)
1926 {
1927    int index;
1928    if (!open_new_client_db(ua)) {
1929       return true;
1930    }
1931    index = find_arg_with_value(ua, "query");
1932    if (index < 0) {
1933       ua->error_msg(_("query keyword not found.\n"));
1934       return true;
1935    }
1936    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1937       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1938       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1939       return true;
1940    }
1941    return true;
1942 }
1943
1944 static int one_handler(void *ctx, int num_field, char **row)
1945 {
1946    UAContext *ua = (UAContext *)ctx;
1947    ua->send_msg("%s\n", row[0]);
1948    return 0;
1949 }
1950
1951 static bool mediatypescmd(UAContext *ua, const char *cmd)
1952 {
1953    if (!open_client_db(ua)) {
1954       return true;
1955    }
1956    if (!db_sql_query(ua->db,
1957            "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1958            one_handler, (void *)ua))
1959    {
1960       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1961    }
1962    return true;
1963 }
1964
1965 static bool mediacmd(UAContext *ua, const char *cmd)
1966 {
1967    if (!open_client_db(ua)) {
1968       return true;
1969    }
1970    if (!db_sql_query(ua->db,
1971           "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1972           one_handler, (void *)ua))
1973    {
1974       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1975    }
1976    return true;
1977 }
1978
1979 static bool locationscmd(UAContext *ua, const char *cmd)
1980 {
1981    if (!open_client_db(ua)) {
1982       return true;
1983    }
1984    if (!db_sql_query(ua->db,
1985            "SELECT DISTINCT Location FROM Location ORDER BY Location",
1986            one_handler, (void *)ua))
1987    {
1988       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1989    }
1990    return true;
1991 }
1992
1993 static bool levelscmd(UAContext *ua, const char *cmd)
1994 {
1995    int i;
1996    /* Note some levels are blank, which means none is needed */
1997    if (ua->argc == 1) {
1998       for (i=0; joblevels[i].level_name; i++) {
1999          if (joblevels[i].level_name[0] != ' ') {
2000             ua->send_msg("%s\n", joblevels[i].level_name);
2001          }
2002       }
2003    } else if (ua->argc == 2) {
2004       int jobtype = 0;
2005       /* Assume that first argument is the Job Type */
2006       for (i=0; jobtypes[i].type_name; i++) {
2007          if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
2008             jobtype = jobtypes[i].job_type;
2009             break;
2010          }
2011       }
2012       for (i=0; joblevels[i].level_name; i++) {
2013          if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
2014             ua->send_msg("%s\n", joblevels[i].level_name);
2015          }
2016       }
2017    }
2018
2019    return true;
2020 }
2021
2022 static bool volstatuscmd(UAContext *ua, const char *cmd)
2023 {
2024    ua->send_msg("Append\n");
2025    ua->send_msg("Full\n");
2026    ua->send_msg("Used\n");
2027    ua->send_msg("Recycle\n");
2028    ua->send_msg("Purged\n");
2029    ua->send_msg("Cleaning\n");
2030    ua->send_msg("Error\n");
2031    return true;
2032 }
2033
2034 /*
2035  * Return default values for a job
2036  */
2037 static bool defaultscmd(UAContext *ua, const char *cmd)
2038 {
2039    char ed1[50];
2040    if (ua->argc != 2 || !ua->argv[1]) {
2041       return true;
2042    }
2043
2044    /* Send Job defaults */
2045    if (strcmp(ua->argk[1], "job") == 0) {
2046       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
2047          return true;
2048       }
2049       JOB *job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
2050       if (job) {
2051          USTORE store;
2052          ua->send_msg("job=%s", job->name());
2053          ua->send_msg("pool=%s", job->pool->name());
2054          ua->send_msg("messages=%s", job->messages->name());
2055          ua->send_msg("client=%s", job->client?job->client->name():_("*None*"));
2056          get_job_storage(&store, job, NULL);
2057          ua->send_msg("storage=%s", store.store->name());
2058          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
2059          ua->send_msg("level=%s", level_to_str(job->JobLevel));
2060          ua->send_msg("type=%s", job_type_to_str(job->JobType));
2061          ua->send_msg("fileset=%s", job->fileset->name());
2062          ua->send_msg("enabled=%d", job->is_enabled());
2063          ua->send_msg("catalog=%s", job->client?job->client->catalog->name():_("*None*"));
2064          ua->send_msg("priority=%d", job->Priority);
2065       }
2066    }
2067    /* Send Pool defaults */
2068    else if (strcmp(ua->argk[1], "pool") == 0) {
2069       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
2070          return true;
2071       }
2072       POOL *pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
2073       if (pool) {
2074          ua->send_msg("pool=%s", pool->name());
2075          ua->send_msg("pool_type=%s", pool->pool_type);
2076          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
2077          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
2078          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
2079          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
2080          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
2081          ua->send_msg("max_volumes=%d", pool->max_volumes);
2082          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
2083          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
2084          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
2085          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
2086          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
2087          ua->send_msg("auto_prune=%d", pool->AutoPrune);
2088          ua->send_msg("recycle=%d", pool->Recycle);
2089          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
2090          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
2091       }
2092    } 
2093    /* Send Storage defaults */
2094    else if (strcmp(ua->argk[1], "storage") == 0) {
2095       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
2096          return true;
2097       }
2098       STORE *storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
2099       DEVICE *device;
2100       if (storage) {
2101          ua->send_msg("storage=%s", storage->name());
2102          ua->send_msg("address=%s", storage->address);
2103          ua->send_msg("enabled=%d", storage->is_enabled());
2104          ua->send_msg("media_type=%s", storage->media_type);
2105          ua->send_msg("sdport=%d", storage->SDport);
2106          device = (DEVICE *)storage->device->first();
2107          ua->send_msg("device=%s", device->name());
2108          if (storage->device && storage->device->size() > 1) {
2109             while ((device = (DEVICE *)storage->device->next())) {
2110                ua->send_msg(",%s", device->name());
2111             }
2112          }
2113       }
2114    } 
2115    /* Send Client defaults */
2116    else if (strcmp(ua->argk[1], "client") == 0) {
2117       if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE)) {
2118          return true;
2119       }
2120       CLIENT *client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
2121       if (client) {
2122          ua->send_msg("client=%s", client->name());
2123          ua->send_msg("address=%s", client->address());
2124          ua->send_msg("fdport=%d", client->FDport);
2125          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
2126          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
2127          ua->send_msg("autoprune=%d", client->AutoPrune);
2128          ua->send_msg("catalog=%s", client->catalog->name());
2129       }
2130    }
2131    return true;
2132 }