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