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