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