]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
5fbe13f945596f554e356423aae6dfaf72f8e2f2
[bacula/bacula] / bacula / src / dird / ua_dotcmds.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 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  *
21  *   Bacula Director -- User Agent Commands
22  *     These are "dot" commands, i.e. commands preceded
23  *        by a period. These commands are meant to be used
24  *        by a program, so there is no prompting, and the
25  *        returned results are (supposed to be) predictable.
26  *
27  *     Kern Sibbald, April MMII
28  *
29  */
30
31 #include "bacula.h"
32 #include "dird.h"
33 #include "cats/bvfs.h"
34 #include "findlib/find.h"
35
36 /* Imported variables */
37 extern struct s_jl joblevels[];
38 extern struct s_jt jobtypes[];
39
40 /* Imported functions */
41 extern void do_messages(UAContext *ua, const char *cmd);
42 extern int quit_cmd(UAContext *ua, const char *cmd);
43 extern int qhelp_cmd(UAContext *ua, const char *cmd);
44 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
45
46
47 /* Forward referenced functions */
48 static bool admin_cmds(UAContext *ua, const char *cmd);
49 static bool jobscmd(UAContext *ua, const char *cmd);
50 static bool filesetscmd(UAContext *ua, const char *cmd);
51 static bool clientscmd(UAContext *ua, const char *cmd);
52 static bool msgscmd(UAContext *ua, const char *cmd);
53 static bool poolscmd(UAContext *ua, const char *cmd);
54 static bool schedulescmd(UAContext *ua, const char *cmd);
55 static bool storagecmd(UAContext *ua, const char *cmd);
56 static bool defaultscmd(UAContext *ua, const char *cmd);
57 static bool typescmd(UAContext *ua, const char *cmd);
58 static bool tagscmd(UAContext *ua, const char *cmd);
59 static bool backupscmd(UAContext *ua, const char *cmd);
60 static bool levelscmd(UAContext *ua, const char *cmd);
61 static bool getmsgscmd(UAContext *ua, const char *cmd);
62 static bool volstatuscmd(UAContext *ua, const char *cmd);
63 static bool mediatypescmd(UAContext *ua, const char *cmd);
64 static bool locationscmd(UAContext *ua, const char *cmd);
65 static bool mediacmd(UAContext *ua, const char *cmd);
66 static bool aopcmd(UAContext *ua, const char *cmd);
67 static bool catalogscmd(UAContext *ua, const char *cmd);
68
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
82 static bool putfile_cmd(UAContext *ua, const char *cmd);
83 static bool api_cmd(UAContext *ua, const char *cmd);
84 static bool sql_cmd(UAContext *ua, const char *cmd);
85 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
86 static bool dot_help_cmd(UAContext *ua, const char *cmd);
87 static int one_handler(void *ctx, int num_field, char **row);
88
89 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
90 static struct cmdstruct commands[] = { /* help */  /* can be used in runscript */
91  { NT_(".api"),        api_cmd,                  NULL,       false},
92  { NT_(".backups"),    backupscmd,               NULL,       false},
93  { NT_(".clients"),    clientscmd,               NULL,       true},
94  { NT_(".catalogs"),   catalogscmd,              NULL,       false},
95  { NT_(".defaults"),   defaultscmd,              NULL,       false},
96  { NT_(".die"),        admin_cmds,               NULL,       false},
97  { NT_(".dump"),       admin_cmds,               NULL,       false},
98  { NT_(".exit"),       admin_cmds,               NULL,       false},
99  { NT_(".filesets"),   filesetscmd,              NULL,       false},
100  { NT_(".help"),       dot_help_cmd,             NULL,       false},
101  { NT_(".jobs"),       jobscmd,                  NULL,       true},
102  { NT_(".levels"),     levelscmd,                NULL,       false},
103  { NT_(".messages"),   getmsgscmd,               NULL,       false},
104  { NT_(".msgs"),       msgscmd,                  NULL,       false},
105  { NT_(".pools"),      poolscmd,                 NULL,       true},
106  { NT_(".quit"),       dot_quit_cmd,             NULL,       false},
107  { NT_(".putfile"),    putfile_cmd,              NULL,       false}, /* use @putfile */
108  { NT_(".schedule"),   schedulescmd,             NULL,       false},
109  { NT_(".sql"),        sql_cmd,                  NULL,       false},
110  { NT_(".status"),     dot_status_cmd,           NULL,       false},
111  { NT_(".storage"),    storagecmd,               NULL,       true},
112  { NT_(".volstatus"),  volstatuscmd,             NULL,       true},
113  { NT_(".media"),      mediacmd,                 NULL,       true},
114  { NT_(".mediatypes"), mediatypescmd,            NULL,       true},
115  { NT_(".locations"),  locationscmd,             NULL,       true},
116  { NT_(".actiononpurge"),aopcmd,                 NULL,       true},
117  { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs,         NULL,       true},
118  { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles,        NULL,       true},
119  { NT_(".bvfs_get_volumes"),dot_bvfs_get_volumes,NULL,       true},
120  { NT_(".bvfs_update"), dot_bvfs_update,         NULL,       true},
121  { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL,       true},
122  { NT_(".bvfs_get_jobs"), dot_bvfs_get_jobs,     NULL,       true},
123  { NT_(".bvfs_versions"), dot_bvfs_versions,     NULL,       true},
124  { NT_(".bvfs_restore"),  dot_bvfs_restore,      NULL,       true},
125  { NT_(".bvfs_cleanup"),  dot_bvfs_cleanup,      NULL,       true},
126  { NT_(".bvfs_decode_lstat"),dot_bvfs_decode_lstat,NULL,     true},
127  { NT_(".bvfs_clear_cache"),dot_bvfs_clear_cache,NULL,       false},
128  { NT_(".bvfs_update_fv"),dot_bvfs_update_fv,    NULL,       true},
129  { NT_(".types"),      typescmd,                 NULL,       false},
130  { NT_(".tags"),      tagscmd,                   NULL,       false}
131 };
132 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
133
134 /*
135  * Execute a command from the UA
136  */
137 bool do_a_dot_command(UAContext *ua)
138 {
139    int i;
140    int len;
141    bool ok = false;
142    bool found = false;
143    BSOCK *user = ua->UA_sock;
144
145    Dmsg1(1400, "Dot command: %s\n", user?user->msg:"");
146    if (ua->argc == 0 || !user) {
147       return false;
148    }
149
150    len = strlen(ua->argk[0]);
151    if (len == 1) {
152       if (ua->api) user->signal(BNET_CMD_BEGIN);
153       if (ua->api) user->signal(BNET_CMD_OK);
154       return true;                    /* no op */
155    }
156    for (i=0; i<comsize; i++) {     /* search for command */
157       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
158          /* Check if this command is authorized in RunScript */
159          if (ua->runscript && !commands[i].use_in_rs) {
160             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
161             break;
162          }
163          bool gui = ua->gui;
164          /* Check if command permitted, but "quit" is always OK */
165          if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
166              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
167             break;
168          }
169          Dmsg1(100, "Cmd: %s\n", ua->cmd);
170          ua->gui = true;
171          if (ua->api) user->signal(BNET_CMD_BEGIN);
172          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
173          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
174          ua->gui = gui;
175          found = user->is_stop() ? false : true;
176          break;
177       }
178    }
179    if (!found) {
180       ua->error_msg("%s%s", ua->argk[0], _(": is an invalid command.\n"));
181       ok = false;
182    }
183    return ok;
184 }
185
186 static void bvfs_set_acl(UAContext *ua, Bvfs *bvfs)
187 {
188    /* If no console resource => default console and all is permitted */
189    if (!ua || !ua->cons) {
190       return;
191    }
192    bvfs->set_job_acl(ua->cons->ACL_lists[Job_ACL]);
193    bvfs->set_client_acl(ua->cons->ACL_lists[Client_ACL]);
194    bvfs->set_fileset_acl(ua->cons->ACL_lists[FileSet_ACL]);
195    bvfs->set_pool_acl(ua->cons->ACL_lists[Pool_ACL]);
196 }
197
198 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd)
199 {
200    int32_t LinkFI;
201    struct stat sp;
202    POOL_MEM q;
203    int pos = find_arg_with_value(ua, "lstat");
204
205    if (pos > 0) {
206       for (char *p = ua->argv[pos] ; *p ; p++) {
207          if (! (B_ISALPHA(*p) || B_ISDIGIT(*p) || B_ISSPACE(*p) || *p == '/' || *p == '+' || *p == '-')) {
208             ua->error_msg("Can't accept %c in lstat\n", *p);
209             return true;
210          }
211       }
212
213       decode_stat(ua->argv[pos], &sp, sizeof(sp), &LinkFI);
214       Mmsg(q, "st_nlink=%lld\nst_mode=%lld\nst_uid=%lld\nst_gid=%lld\nst_size=%lld\n"
215                "st_blocks=%lld\nst_ino=%lld\nst_ctime=%lld\nst_mtime=%lld\nst_mtime=%lld\n"
216                "st_dev=%lld\nLinkFI=%lld\n",
217            (int64_t) sp.st_nlink,
218            (int64_t) sp.st_mode,
219            (int64_t) sp.st_uid,
220            (int64_t) sp.st_gid,
221            (int64_t) sp.st_size,
222            (int64_t) sp.st_blocks,
223            (int64_t) sp.st_ino,
224            (int64_t) sp.st_ctime,
225            (int64_t) sp.st_mtime,
226            (int64_t) sp.st_atime,
227            (int64_t) sp.st_dev,
228            (int64_t) LinkFI
229          );
230
231       ua->send_msg("%s", q.c_str());
232    }
233    return true;
234 }
235
236 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
237 {
238    if (!open_new_client_db(ua)) {
239       return 1;
240    }
241
242    int pos = find_arg_with_value(ua, "jobid");
243    if (pos != -1 && is_a_number_list(ua->argv[pos])) {
244       if (!bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos])) {
245          ua->error_msg("ERROR: BVFS reported a problem for %s\n",
246                        ua->argv[pos]);
247       }
248    } else {
249       /* update cache for all jobids */
250       bvfs_update_cache(ua->jcr, ua->db);
251    }
252
253    return true;
254 }
255
256 static bool dot_bvfs_update_fv(UAContext *ua, const char *cmd)
257 {
258    int pos = find_arg_with_value(ua, "jobid");
259
260    if (pos == -1 || !is_a_number_list(ua->argv[pos])) {
261       ua->error_msg("Expecting to find jobid=1,2,3 argument\n");
262       return 1;
263    }
264
265    if (!open_new_client_db(ua)) {
266       return 1;
267    }
268
269    bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
270    bvfs_update_fv_cache(ua->jcr, ua->db, ua->argv[pos]);
271
272    ua->info_msg("OK\n");
273
274    return true;
275 }
276
277 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd)
278 {
279    if (!open_client_db(ua)) {
280       return 1;
281    }
282
283    int pos = find_arg(ua, "yes");
284    if (pos != -1) {
285       Bvfs fs(ua->jcr, ua->db);
286       fs.clear_cache();
287       ua->info_msg("OK\n");
288    } else {
289       ua->error_msg("Can't find 'yes' argument\n");
290    }
291
292    return true;
293 }
294
295 static int bvfs_result_handler(void *ctx, int fields, char **row)
296 {
297    UAContext *ua = (UAContext *)ctx;
298    struct stat statp;
299    int32_t LinkFI;
300    char *fileid=row[BVFS_FileId];
301    char *lstat=row[BVFS_LStat];
302    char *jobid=row[BVFS_JobId];
303
304    char empty[] = "A A A A A A A A A A A A A A";
305    char zero[] = "0";
306
307    /* We need to deal with non existant path */
308    if (!fileid || !is_a_number(fileid)) {
309       lstat = empty;
310       jobid = zero;
311       fileid = zero;
312    }
313
314    memset(&statp, 0, sizeof(struct stat));
315    decode_stat(lstat, &statp, sizeof(statp), &LinkFI);
316
317    Dmsg1(100, "type=%s\n", row[0]);
318    if (bvfs_is_dir(row)) {
319       char *path = bvfs_basename_dir(row[BVFS_Name]);
320       ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
321                    jobid, lstat, path);
322
323    } else if (bvfs_is_version(row)) {
324       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
325                    row[BVFS_FilenameId], fileid, jobid,
326                    lstat, row[BVFS_Md5], row[BVFS_VolName],
327                    row[BVFS_VolInchanger]);
328
329    } else if (bvfs_is_file(row)) {
330       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
331                    row[BVFS_FilenameId], fileid, jobid,
332                    lstat, row[BVFS_Name]);
333
334    } else if (bvfs_is_volume_list(row)) {
335       ua->send_msg("%s\t%s\n", row[BVFS_VolName],
336                    row[BVFS_VolInchanger]);
337    }
338
339    return 0;
340 }
341
342 static bool bvfs_parse_arg_version(UAContext *ua,
343                                    char **client,
344                                    FileId_t *fnid,
345                                    bool *versions,
346                                    bool *copies)
347 {
348    *fnid=0;
349    *client=NULL;
350    *versions=false;
351    *copies=false;
352
353    for (int i=1; i<ua->argc; i++) {
354       if (fnid && strcasecmp(ua->argk[i], NT_("fnid")) == 0) {
355          if (is_a_number(ua->argv[i])) {
356             *fnid = str_to_int64(ua->argv[i]);
357          }
358       }
359
360       if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
361          *client = ua->argv[i];
362       }
363
364       if (copies && strcasecmp(ua->argk[i], NT_("copies")) == 0) {
365          *copies = true;
366       }
367
368       if (versions && strcasecmp(ua->argk[i], NT_("versions")) == 0) {
369          *versions = true;
370       }
371    }
372    return (*client && *fnid > 0);
373 }
374
375 static bool bvfs_parse_arg(UAContext *ua,
376                            DBId_t *pathid, char **path, char **jobid,
377                            char **username,
378                            int *limit, int *offset)
379 {
380    *pathid=0;
381    *limit=2000;
382    *offset=0;
383    *path=NULL;
384    *username=NULL;
385    if (jobid) {
386       *jobid=NULL;
387    }
388
389    for (int i=1; i<ua->argc; i++) {
390       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
391          if (is_a_number(ua->argv[i])) {
392             *pathid = str_to_int64(ua->argv[i]);
393          }
394       }
395
396       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
397          *path = ua->argv[i];
398       }
399
400       if (strcasecmp(ua->argk[i], NT_("username")) == 0) {
401          *username = ua->argv[i];
402       }
403
404       if (jobid && strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
405          if (is_a_number_list(ua->argv[i])) {
406             *jobid = ua->argv[i];
407          }
408       }
409
410       if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
411          JOB_DBR jr;
412          memset(&jr, 0, sizeof(jr));
413          bstrncpy(jr.Job, ua->argv[i], sizeof(jr.Job));
414          if (!open_new_client_db(ua)) {
415             return false;
416          }
417          if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
418             return false;
419          }
420          if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
421             return false;
422          }
423          /* Store the jobid after the ua->cmd, a bit kluggy */
424          int len = strlen(ua->cmd);
425          ua->cmd = check_pool_memory_size(ua->cmd, len + 1 + 50);
426          *jobid = edit_uint64(jr.JobId, ua->cmd + len + 1);
427       }
428
429       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
430          if (is_a_number(ua->argv[i])) {
431             *limit = str_to_int64(ua->argv[i]);
432          }
433       }
434
435       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
436          if (is_a_number(ua->argv[i])) {
437             *offset = str_to_int64(ua->argv[i]);
438          }
439       }
440    }
441
442    if (jobid && *jobid == NULL) {
443       return false;
444    }
445
446    if (!(*pathid || *path)) {
447       return false;
448    }
449
450    return true;
451 }
452
453 /* .bvfs_cleanup path=b2XXXXX
454  */
455 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd)
456 {
457    int i;
458    if ((i = find_arg_with_value(ua, "path")) >= 0) {
459       if (!open_client_db(ua)) {
460          return 1;
461       }
462       Bvfs fs(ua->jcr, ua->db);
463       fs.drop_restore_list(ua->argv[i]);
464    }
465    return true;
466 }
467
468 /* .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
469  */
470 static bool dot_bvfs_restore(UAContext *ua, const char *cmd)
471 {
472    DBId_t pathid=0;
473    int limit=2000, offset=0, i;
474    char *path=NULL, *jobid=NULL, *username=NULL;
475    char *empty = (char *)"";
476    char *fileid, *dirid, *hardlink;
477    fileid = dirid = hardlink = empty;
478
479    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
480                        &limit, &offset))
481    {
482       ua->error_msg("Can't find jobid, pathid or path argument\n");
483       return true;              /* not enough param */
484    }
485
486    if (!open_new_client_db(ua)) {
487       return true;
488    }
489
490    Bvfs fs(ua->jcr, ua->db);
491    bvfs_set_acl(ua, &fs);
492    fs.set_username(username);
493    fs.set_jobids(jobid);
494
495    if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
496       fileid = ua->argv[i];
497    }
498    if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
499       dirid = ua->argv[i];
500    }
501    if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
502       hardlink = ua->argv[i];
503    }
504
505    if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
506       ua->send_msg("OK\n");
507    } else {
508       ua->error_msg("Can't create restore list\n");
509    }
510
511    return true;
512 }
513
514 /*
515  * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
516  * Vol001
517  * Vol002
518  * Vol003
519  */
520 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
521 {
522    DBId_t pathid=0;
523    FileId_t fileid=0;
524    char  *path=NULL, *jobid=NULL, *username=NULL;
525    char  *filename=NULL;
526    int    limit=2000, offset=0;
527    int    i;
528
529    bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
530
531    if ((i = find_arg_with_value(ua, "filename")) >= 0) {
532       if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
533          ua->error_msg("Can't find jobid, pathid or path argument\n");
534          return true;
535       }
536
537       filename = ua->argv[i];
538
539    } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
540       if (!is_a_number(ua->argv[i])) {
541          ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
542          return true;
543       }
544       fileid = str_to_int64(ua->argv[i]);
545    }
546
547    if (!open_new_client_db(ua)) {
548       return 1;
549    }
550
551    Bvfs fs(ua->jcr, ua->db);
552    bvfs_set_acl(ua, &fs);
553    fs.set_username(username);
554    fs.set_handler(bvfs_result_handler, ua);
555    fs.set_limit(limit);
556
557    if (filename) {
558       /* TODO */
559
560    } else {
561       fs.get_volumes(fileid);
562    }
563
564    return true;
565 }
566
567 /*
568  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
569  * .bvfs_lsfiles jobid=1,2,3,4 path=/
570  */
571 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
572 {
573    DBId_t pathid=0;
574    int limit=2000, offset=0;
575    char *path=NULL, *jobid=NULL, *username=NULL;
576    char *pattern=NULL, *filename=NULL;
577    int i;
578
579    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
580                        &limit, &offset))
581    {
582       ua->error_msg("Can't find jobid, pathid or path argument\n");
583       return true;              /* not enough param */
584    }
585    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
586       pattern = ua->argv[i];
587    }
588    if ((i = find_arg_with_value(ua, "filename")) >= 0) {
589       filename = ua->argv[i];
590    }
591
592    if (!open_new_client_db(ua)) {
593       return 1;
594    }
595
596    Bvfs fs(ua->jcr, ua->db);
597    bvfs_set_acl(ua, &fs);
598    fs.set_username(username);
599    fs.set_jobids(jobid);
600    fs.set_handler(bvfs_result_handler, ua);
601    fs.set_limit(limit);
602    if (pattern) {
603       fs.set_pattern(pattern);
604    }
605    if (filename) {
606       fs.set_filename(filename);
607    }
608    if (pathid) {
609       fs.ch_dir(pathid);
610    } else {
611       fs.ch_dir(path);
612    }
613
614    fs.set_offset(offset);
615
616    fs.ls_files();
617
618    return true;
619 }
620
621 /*
622  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
623  * .bvfs_lsdirs jobid=1,2,3,4 path=/
624  * .bvfs_lsdirs jobid=1,2,3,4 path=
625  */
626 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
627 {
628    DBId_t pathid=0;
629    int   limit=2000, offset=0;
630    char *path=NULL, *jobid=NULL, *username=NULL;
631    char *pattern=NULL;
632    int   dironly;
633    int i;
634
635    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
636                        &limit, &offset))
637    {
638       ua->error_msg("Can't find jobid, pathid or path argument\n");
639       return true;              /* not enough param */
640    }
641
642    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
643       pattern = ua->argv[i];
644    }
645
646    dironly = find_arg(ua, "dironly");
647
648    if (!open_new_client_db(ua)) {
649       return 1;
650    }
651
652    Bvfs fs(ua->jcr, ua->db);
653    bvfs_set_acl(ua, &fs);
654    fs.set_username(username);
655    fs.set_jobids(jobid);
656    fs.set_limit(limit);
657    fs.set_handler(bvfs_result_handler, ua);
658
659    if (pattern) {
660       fs.set_pattern(pattern);
661    }
662
663    if (pathid) {
664       fs.ch_dir(pathid);
665    } else {
666       fs.ch_dir(path);
667    }
668
669    fs.set_offset(offset);
670
671    fs.ls_special_dirs();
672
673    if (dironly < 0) {
674       fs.ls_dirs();
675    }
676    return true;
677 }
678
679 /*
680  * .bvfs_versions fnid=10 pathid=10 client=xxx copies versions
681  *
682  */
683 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
684 {
685    DBId_t pathid=0;
686    FileId_t fnid=0;
687    int limit=2000, offset=0;
688    char *path=NULL, *client=NULL, *username=NULL;
689    bool copies=false, versions=false;
690    if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
691                        &limit, &offset))
692    {
693       ua->error_msg("Can't find pathid or path argument\n");
694       return true;              /* not enough param */
695    }
696
697    if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
698    {
699       ua->error_msg("Can't find client or fnid argument\n");
700       return true;              /* not enough param */
701    }
702
703    if (!open_new_client_db(ua)) {
704       return 1;
705    }
706
707    Bvfs fs(ua->jcr, ua->db);
708    bvfs_set_acl(ua, &fs);
709    fs.set_limit(limit);
710    fs.set_see_all_versions(versions);
711    fs.set_see_copies(copies);
712    fs.set_handler(bvfs_result_handler, ua);
713    fs.set_offset(offset);
714    fs.get_all_file_versions(pathid, fnid, client);
715
716    return true;
717 }
718
719 /* .bvfs_get_jobids jobid=1
720  *  -> returns needed jobids to restore
721  * .bvfs_get_jobids ujobid=xxx only
722  *  -> returns the jobid of the job
723  * .bvfs_get_jobids jobid=1 jobname
724  *  -> returns the jobname
725  * .bvfs_get_jobids client=xxx
726  *  -> returns all jobid for the client
727  * .bvfs_get_jobids jobid=1 all
728  *  -> returns needed jobids to restore with all filesets a JobId=1 time
729  * .bvfs_get_jobids job=XXXXX
730  *  -> returns needed jobids to restore with the jobname
731  * .bvfs_get_jobids ujobid=JobName
732  *  -> returns needed jobids to restore
733  */
734 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
735 {
736    JOB_DBR jr;
737    memset(&jr, 0, sizeof(JOB_DBR));
738
739    db_list_ctx jobids, tempids;
740    int pos;
741    char ed1[50];
742    POOL_MEM query;
743    dbid_list ids;               /* Store all FileSetIds for this client */
744
745    if (!open_new_client_db(ua)) {
746       return true;
747    }
748
749    Bvfs fs(ua->jcr, ua->db);
750    bvfs_set_acl(ua, &fs);
751
752    if ((pos = find_arg_with_value(ua, "username")) >= 0) {
753       fs.set_username(ua->argv[pos]);
754    }
755
756    if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
757       bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
758    }
759
760    if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
761       jr.JobId = str_to_int64(ua->argv[pos]);
762
763    /* Guess JobId from Job name, take the last successful jobid */
764    } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
765       JOB *job;
766       bool ret;
767       int32_t JobId=0;
768
769       bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
770       /* TODO: enhance this function to take client and/or fileset as argument*/
771
772       job = GetJobResWithName(jr.Name);
773       if (!job) {
774          ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
775          return true;
776       }
777       db_lock(ua->db);
778       Mmsg(ua->db->cmd,
779       "SELECT JobId "
780         "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
781          "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
782            "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
783          "ORDER By JobTDate DESC LIMIT 1",
784            job->client->name(), job->fileset->name());
785       ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
786       db_unlock(ua->db);
787
788       if (!ret) {
789          ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
790       }
791
792       jr.JobId = JobId;
793
794    /* Get JobId from ujobid */
795    } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
796       bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
797
798    /* Return all backup jobid for a client */
799    } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
800       CLIENT *cli;
801       bool ret;
802
803       cli = GetClientResWithName(ua->argv[pos]);
804       if (!cli) {
805          ua->error_msg(_("Unable to get Client record for Client=%s\n"),
806                        ua->argv[pos]);
807          return true;
808       }
809       db_lock(ua->db);
810       Mmsg(ua->db->cmd,
811       "SELECT JobId "
812         "FROM Job JOIN Client USING (ClientId) "
813          "WHERE Client.Name = '%s' "
814            "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
815          "ORDER By JobTDate ASC",
816            cli->name());
817       ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
818       db_unlock(ua->db);
819
820       if (!ret) {
821          ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
822                        cli->name());
823       }
824
825       /* Apply the ACL filter on JobIds */
826       fs.set_jobids(jobids.list);
827       ua->send_msg("%s\n", fs.get_jobids());
828       return true;
829    }
830
831    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
832       ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
833                     ua->cmd, db_strerror(ua->db));
834       return true;
835    }
836
837    /* Display only the requested jobid or
838     * When in level base, we don't rely on any Full/Incr/Diff
839     */
840    if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
841       /* Apply the ACL filter on JobIds */
842       fs.set_jobid(jr.JobId);
843       ua->send_msg("%s\n", fs.get_jobids());
844       return true;
845    }
846
847    /* Display only the requested job name
848     */
849    if (find_arg(ua, "jobname") > 0) {
850       /* Apply the ACL filter on JobIds */
851       fs.set_jobid(jr.JobId);
852       if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
853          ua->send_msg("%s\n", jr.Job);
854       }
855       return true;
856    }
857
858    /* If we have the "all" option, we do a search on all defined fileset
859     * for this client
860     */
861    if (find_arg(ua, "all") > 0) {
862       edit_int64(jr.ClientId, ed1);
863       Mmsg(query, uar_sel_filesetid, ed1);
864       db_get_query_dbids(ua->jcr, ua->db, query, ids);
865    } else {
866       ids.num_ids = 1;
867       ids.DBId[0] = jr.FileSetId;
868    }
869
870    jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
871
872    /* Foreach different FileSet, we build a restore jobid list */
873    for (int i=0; i < ids.num_ids; i++) {
874       jr.FileSetId = ids.DBId[i];
875       if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &tempids)) {
876          return true;
877       }
878       jobids.add(tempids);
879    }
880
881    fs.set_jobids(jobids.list);
882    ua->send_msg("%s\n", fs.get_jobids());
883    return true;
884 }
885
886 static int jobs_handler(void *ctx, int num_field, char **row)
887 {
888    UAContext *ua = (UAContext *)ctx;
889    ua->send_msg("%s %s %s\n", row[0], row[1], row[2]);
890    return 0;
891 }
892
893 /* .bvfs_get_jobs client=xxx [fileset=yyyy]
894  * 1 yyyyy Backup1_xxx_xxx_xxxx_xxx
895  * 2 yyyyy Backup1_xxx_xxx_xxxx_xxx
896  */
897 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd)
898 {
899    int pos, posj;
900    POOL_MEM tmp;
901    char esc_cli[MAX_ESCAPE_NAME_LENGTH];
902    char esc_job[MAX_ESCAPE_NAME_LENGTH];
903    if (!open_new_client_db(ua)) {
904       return true;
905    }
906
907    if (((pos = find_arg_with_value(ua, "client")) < 0) ||
908        (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
909    {
910       return true;
911    }
912
913    if (!acl_access_ok(ua, Client_ACL, ua->argv[pos])) {
914       return true;
915    }
916
917    posj = find_arg_with_value(ua, "ujobid");
918    /* Do a little check on the size of the argument */
919    if (posj >= 0 && strlen(ua->argv[posj]) > MAX_NAME_LENGTH) {
920       return true;
921    }
922
923    db_lock(ua->db);
924    db_escape_string(ua->jcr, ua->db, esc_cli,
925                     ua->argv[pos], strlen(ua->argv[pos]));
926    if (posj >= 0) {
927       db_escape_string(ua->jcr, ua->db, esc_job,
928                        ua->argv[posj], strlen(ua->argv[pos]));
929       Mmsg(tmp, "AND Job.Job = '%s'", esc_job);
930    }
931    Mmsg(ua->db->cmd,
932       "SELECT JobId, JobTDate, Job "
933         "FROM Job JOIN Client USING (ClientId) "
934          "WHERE Client.Name = '%s' AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
935          "%s "
936          "ORDER By JobTDate DESC",
937         esc_cli, tmp.c_str());
938    db_sql_query(ua->db, ua->db->cmd, jobs_handler, ua);
939    db_unlock(ua->db);
940    return true;
941 }
942
943 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
944 {
945    quit_cmd(ua, cmd);
946    return true;
947 }
948
949 static bool dot_help_cmd(UAContext *ua, const char *cmd)
950 {
951    qhelp_cmd(ua, cmd);
952    return true;
953 }
954
955 static bool getmsgscmd(UAContext *ua, const char *cmd)
956 {
957    if (console_msg_pending) {
958       do_messages(ua, cmd);
959    }
960    return 1;
961 }
962
963 #ifdef DEVELOPER
964 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
965 {
966    BSOCK *sd;
967    JCR *jcr = ua->jcr;
968    USTORE lstore;
969
970    lstore.store = store;
971    pm_strcpy(lstore.store_source, _("unknown source"));
972    set_wstorage(jcr, &lstore);
973    /* Try connecting for up to 15 seconds */
974    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
975       store->name(), store->address, store->SDport);
976    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
977       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
978       return;
979    }
980    Dmsg0(120, _("Connected to storage daemon\n"));
981    sd = jcr->store_bsock;
982    sd->fsend("%s", cmd);
983    if (sd->recv() >= 0) {
984       ua->send_msg("%s", sd->msg);
985    }
986    sd->signal(BNET_TERMINATE);
987    free_bsock(ua->jcr->store_bsock);
988    return;
989 }
990
991 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
992 {
993    BSOCK *fd;
994
995    /* Connect to File daemon */
996
997    ua->jcr->client = client;
998    /* Try to connect for 15 seconds */
999    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1000       client->name(), client->address, client->FDport);
1001    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1002       ua->error_msg(_("Failed to connect to Client.\n"));
1003       return;
1004    }
1005    Dmsg0(120, "Connected to file daemon\n");
1006    fd = ua->jcr->file_bsock;
1007    fd->fsend("%s", cmd);
1008    if (fd->recv() >= 0) {
1009       ua->send_msg("%s", fd->msg);
1010    }
1011    fd->signal(BNET_TERMINATE);
1012    free_bsock(ua->jcr->file_bsock);
1013    return;
1014 }
1015
1016 /*
1017  *   .die (seg fault)
1018  *   .dump (sm_dump)
1019  *   .exit (no arg => .quit)
1020  */
1021 static bool admin_cmds(UAContext *ua, const char *cmd)
1022 {
1023    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1024    STORE *store=NULL;
1025    CLIENT *client=NULL;
1026    bool dir=false;
1027    bool do_deadlock=false;
1028    const char *remote_cmd;
1029    int i;
1030    JCR *jcr = NULL;
1031    int a;
1032    if (strncmp(ua->argk[0], ".die", 4) == 0) {
1033       if (find_arg(ua, "deadlock") > 0) {
1034          do_deadlock = true;
1035          remote_cmd = ".die deadlock";
1036       } else {
1037          remote_cmd = ".die";
1038       }
1039    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
1040       remote_cmd = "sm_dump";
1041    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
1042       remote_cmd = "exit";
1043    } else {
1044       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1045       return true;
1046    }
1047    /* General debug? */
1048    for (i=1; i<ua->argc; i++) {
1049       if (strcasecmp(ua->argk[i], "dir") == 0 ||
1050           strcasecmp(ua->argk[i], "director") == 0) {
1051          dir = true;
1052       }
1053       if (strcasecmp(ua->argk[i], "client") == 0 ||
1054           strcasecmp(ua->argk[i], "fd") == 0) {
1055          client = NULL;
1056          if (ua->argv[i]) {
1057             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1058          }
1059          if (!client) {
1060             client = select_client_resource(ua);
1061          }
1062       }
1063
1064       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1065           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1066           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1067          store = NULL;
1068          if (ua->argv[i]) {
1069             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1070          }
1071          if (!store) {
1072             store = get_storage_resource(ua, false/*no default*/);
1073          }
1074       }
1075    }
1076
1077    if (!dir && !store && !client) {
1078       /*
1079        * We didn't find an appropriate keyword above, so
1080        * prompt the user.
1081        */
1082       start_prompt(ua, _("Available daemons are: \n"));
1083       add_prompt(ua, _("Director"));
1084       add_prompt(ua, _("Storage"));
1085       add_prompt(ua, _("Client"));
1086       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
1087       case 0:                         /* Director */
1088          dir=true;
1089          break;
1090       case 1:
1091          store = get_storage_resource(ua, false/*no default*/);
1092          break;
1093       case 2:
1094          client = select_client_resource(ua);
1095          break;
1096       default:
1097          break;
1098       }
1099    }
1100
1101    if (store) {
1102       do_storage_cmd(ua, store, remote_cmd);
1103    }
1104
1105    if (client) {
1106       do_client_cmd(ua, client, remote_cmd);
1107    }
1108
1109    if (dir) {
1110       if (strncmp(remote_cmd, ".die", 4) == 0) {
1111          if (do_deadlock) {
1112             ua->send_msg(_("The Director will generate a deadlock.\n"));
1113             P(mutex);
1114             P(mutex);
1115          }
1116          ua->send_msg(_("The Director will segment fault.\n"));
1117          a = jcr->JobId; /* ref NULL pointer */
1118          jcr->JobId = 1000; /* another ref NULL pointer */
1119          jcr->JobId = a;
1120
1121       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
1122          sm_dump(false, true);
1123       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
1124          dot_quit_cmd(ua, cmd);
1125       }
1126    }
1127
1128    return true;
1129 }
1130
1131 #else
1132
1133 /*
1134  * Dummy routine for non-development version
1135  */
1136 static bool admin_cmds(UAContext *ua, const char *cmd)
1137 {
1138    ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1139    return true;
1140 }
1141
1142 #endif
1143
1144 /*
1145  * Send a file to the director from bconsole @putfile command
1146  * The .putfile can not be used directly.
1147  */
1148 static bool putfile_cmd(UAContext *ua, const char *cmd)
1149 {
1150    int         pos, i, pnl, fnl;
1151    bool        ok = true;
1152    POOLMEM    *name = get_pool_memory(PM_FNAME);
1153    POOLMEM    *path = get_pool_memory(PM_FNAME);
1154    POOLMEM    *fname= get_pool_memory(PM_FNAME);
1155    const char *key = "putfile";
1156    FILE       *fp = NULL;
1157
1158    if ((pos = find_arg_with_value(ua, "key")) > 0) {
1159       /* Check the string if the string is valid */
1160       for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1161
1162       if (ua->argv[pos][i] == 0) {
1163          key = ua->argv[pos];
1164
1165       } else {
1166          ua->error_msg("Invalid key name for putfile command");
1167          ok = false;
1168          goto bail_out;
1169       }
1170    }
1171
1172    /* the (intptr_t)ua will allow one file per console session */
1173    make_unique_filename(&name, (intptr_t)ua, (char *)key);
1174
1175    fp = fopen(name, "w");
1176    if (!fp) {
1177       berrno be;
1178       ua->error_msg("Unable to open destination file. ERR=%s\n",
1179                     be.bstrerror(errno));
1180       ok = false;
1181       goto bail_out;
1182    }
1183
1184    while (ua->UA_sock->recv() > 0) {
1185       if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1186          berrno be;
1187          ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1188                        be.bstrerror(errno));
1189          ok = false;
1190          /* TODO: Check if we need to quit here (data will still be in the
1191           * buffer...) */
1192       }
1193    }
1194
1195    split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1196
1197 bail_out:
1198    if (ok) {
1199       ua->send_msg("OK\n");
1200
1201    } else {
1202       ua->send_msg("ERROR\n");
1203    }
1204
1205    free_pool_memory(name);
1206    free_pool_memory(path);
1207    free_pool_memory(fname);
1208    if (fp) {
1209       fclose(fp);
1210    }
1211    return true;
1212 }
1213
1214 /*
1215  * Can use an argument to filter on JobType
1216  * .jobs [type=B]
1217  */
1218 static bool jobscmd(UAContext *ua, const char *cmd)
1219 {
1220    JOB *job;
1221    uint32_t type = 0;
1222    int pos;
1223    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1224       type = ua->argv[pos][0];
1225    }
1226    LockRes();
1227    foreach_res(job, R_JOB) {
1228       if (!type || type == job->JobType) {
1229          if (acl_access_ok(ua, Job_ACL, job->name())) {
1230             ua->send_msg("%s\n", job->name());
1231          }
1232       }
1233    }
1234    UnlockRes();
1235    return true;
1236 }
1237
1238 static bool filesetscmd(UAContext *ua, const char *cmd)
1239 {
1240    FILESET *fs;
1241    LockRes();
1242    foreach_res(fs, R_FILESET) {
1243       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1244          ua->send_msg("%s\n", fs->name());
1245       }
1246    }
1247    UnlockRes();
1248    return true;
1249 }
1250
1251 static bool catalogscmd(UAContext *ua, const char *cmd)
1252 {
1253    CAT *cat;
1254    LockRes();
1255    foreach_res(cat, R_CATALOG) {
1256       if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1257          ua->send_msg("%s\n", cat->name());
1258       }
1259    }
1260    UnlockRes();
1261    return true;
1262 }
1263
1264 static bool clientscmd(UAContext *ua, const char *cmd)
1265 {
1266    CLIENT *client;
1267    LockRes();
1268    foreach_res(client, R_CLIENT) {
1269       if (acl_access_ok(ua, Client_ACL, client->name())) {
1270          ua->send_msg("%s\n", client->name());
1271       }
1272    }
1273    UnlockRes();
1274    return true;
1275 }
1276
1277 static bool msgscmd(UAContext *ua, const char *cmd)
1278 {
1279    MSGS *msgs = NULL;
1280    LockRes();
1281    foreach_res(msgs, R_MSGS) {
1282       ua->send_msg("%s\n", msgs->name());
1283    }
1284    UnlockRes();
1285    return true;
1286 }
1287
1288 static bool poolscmd(UAContext *ua, const char *cmd)
1289 {
1290    POOL *pool;
1291    LockRes();
1292    foreach_res(pool, R_POOL) {
1293       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1294          ua->send_msg("%s\n", pool->name());
1295       }
1296    }
1297    UnlockRes();
1298    return true;
1299 }
1300
1301 static bool schedulescmd(UAContext *ua, const char *cmd)
1302 {
1303    SCHED *sched;
1304    LockRes();
1305    foreach_res(sched, R_SCHEDULE) {
1306       if (acl_access_ok(ua, Schedule_ACL, sched->name())) {
1307          ua->send_msg("%s\n", sched->name());
1308       }
1309    }
1310    UnlockRes();
1311    return true;
1312 }
1313
1314 static bool storagecmd(UAContext *ua, const char *cmd)
1315 {
1316    STORE *store;
1317    LockRes();
1318    foreach_res(store, R_STORAGE) {
1319       if (acl_access_ok(ua, Storage_ACL, store->name())) {
1320          ua->send_msg("%s\n", store->name());
1321       }
1322    }
1323    UnlockRes();
1324    return true;
1325 }
1326
1327 static bool aopcmd(UAContext *ua, const char *cmd)
1328 {
1329    ua->send_msg("None\n");
1330    ua->send_msg("Truncate\n");
1331    return true;
1332 }
1333
1334 static bool typescmd(UAContext *ua, const char *cmd)
1335 {
1336    ua->send_msg("Backup\n");
1337    ua->send_msg("Restore\n");
1338    ua->send_msg("Admin\n");
1339    ua->send_msg("Verify\n");
1340    ua->send_msg("Migrate\n");
1341    ua->send_msg("Copy\n");
1342    return true;
1343 }
1344
1345 static bool tagscmd(UAContext *ua, const char *cmd)
1346 {
1347    uint32_t i = 0;
1348    for (const char *p = debug_get_tag(i++, NULL) ; p ; p = debug_get_tag(i++, NULL)) {
1349       ua->send_msg("%s\n", p);
1350    }
1351    return true;
1352 }
1353
1354 /*
1355  * If this command is called, it tells the director that we
1356  *  are a program that wants a sort of API, and hence,
1357  *  we will probably suppress certain output, include more
1358  *  error codes, and most of all send back a good number
1359  *  of new signals that indicate whether or not the command
1360  *  succeeded.
1361  */
1362 static bool api_cmd(UAContext *ua, const char *cmd)
1363 {
1364    int i;
1365    if (ua->argc >= 2) {
1366       ua->api = atoi(ua->argk[1]);
1367
1368       /* Get output configuration options such as time format or separator */
1369       if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1370          bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1371
1372       } else {
1373          *ua->api_opts = 0;
1374       }
1375    } else {
1376       ua->api = 1;
1377    }
1378    return true;
1379 }
1380
1381 static int client_backups_handler(void *ctx, int num_field, char **row)
1382 {
1383    UAContext *ua = (UAContext *)ctx;
1384    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1385       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1386    return 0;
1387 }
1388
1389 /*
1390  * Return the backups for this client
1391  *
1392  *  .backups client=xxx fileset=yyy
1393  *
1394  */
1395 static bool backupscmd(UAContext *ua, const char *cmd)
1396 {
1397    if (!open_client_db(ua)) {
1398       return true;
1399    }
1400    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1401        strcmp(ua->argk[2], "fileset") != 0) {
1402       return true;
1403    }
1404    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
1405        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1406       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1407       return true;
1408    }
1409    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1410    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1411       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1412       return true;
1413    }
1414    return true;
1415 }
1416
1417 static int sql_handler(void *ctx, int num_field, char **row)
1418 {
1419    UAContext *ua = (UAContext *)ctx;
1420    POOL_MEM rows(PM_MESSAGE);
1421
1422    /* Check for nonsense */
1423    if (num_field == 0 || row == NULL || row[0] == NULL) {
1424       return 0;                       /* nothing returned */
1425    }
1426    for (int i=0; num_field--; i++) {
1427       if (i == 0) {
1428          pm_strcpy(rows, NPRT(row[0]));
1429       } else {
1430          pm_strcat(rows, NPRT(row[i]));
1431       }
1432       pm_strcat(rows, "\t");
1433    }
1434    if (!rows.c_str() || !*rows.c_str()) {
1435       ua->send_msg("\t");
1436    } else {
1437       ua->send_msg("%s", rows.c_str());
1438    }
1439    return 0;
1440 }
1441
1442 static bool sql_cmd(UAContext *ua, const char *cmd)
1443 {
1444    int index;
1445    if (!open_new_client_db(ua)) {
1446       return true;
1447    }
1448    index = find_arg_with_value(ua, "query");
1449    if (index < 0) {
1450       ua->error_msg(_("query keyword not found.\n"));
1451       return true;
1452    }
1453    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1454       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1455       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1456       return true;
1457    }
1458    return true;
1459 }
1460
1461 static int one_handler(void *ctx, int num_field, char **row)
1462 {
1463    UAContext *ua = (UAContext *)ctx;
1464    ua->send_msg("%s\n", row[0]);
1465    return 0;
1466 }
1467
1468 static bool mediatypescmd(UAContext *ua, const char *cmd)
1469 {
1470    if (!open_client_db(ua)) {
1471       return true;
1472    }
1473    if (!db_sql_query(ua->db,
1474            "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1475            one_handler, (void *)ua))
1476    {
1477       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1478    }
1479    return true;
1480 }
1481
1482 static bool mediacmd(UAContext *ua, const char *cmd)
1483 {
1484    if (!open_client_db(ua)) {
1485       return true;
1486    }
1487    if (!db_sql_query(ua->db,
1488           "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1489           one_handler, (void *)ua))
1490    {
1491       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1492    }
1493    return true;
1494 }
1495
1496 static bool locationscmd(UAContext *ua, const char *cmd)
1497 {
1498    if (!open_client_db(ua)) {
1499       return true;
1500    }
1501    if (!db_sql_query(ua->db,
1502            "SELECT DISTINCT Location FROM Location ORDER BY Location",
1503            one_handler, (void *)ua))
1504    {
1505       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1506    }
1507    return true;
1508 }
1509
1510 static bool levelscmd(UAContext *ua, const char *cmd)
1511 {
1512    int i;
1513    /* Note some levels are blank, which means none is needed */
1514    if (ua->argc == 1) {
1515       for (i=0; joblevels[i].level_name; i++) {
1516          if (joblevels[i].level_name[0] != ' ') {
1517             ua->send_msg("%s\n", joblevels[i].level_name);
1518          }
1519       }
1520    } else if (ua->argc == 2) {
1521       int jobtype = 0;
1522       /* Assume that first argument is the Job Type */
1523       for (i=0; jobtypes[i].type_name; i++) {
1524          if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
1525             jobtype = jobtypes[i].job_type;
1526             break;
1527          }
1528       }
1529       for (i=0; joblevels[i].level_name; i++) {
1530          if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
1531             ua->send_msg("%s\n", joblevels[i].level_name);
1532          }
1533       }
1534    }
1535
1536    return true;
1537 }
1538
1539 static bool volstatuscmd(UAContext *ua, const char *cmd)
1540 {
1541    ua->send_msg("Append\n");
1542    ua->send_msg("Full\n");
1543    ua->send_msg("Used\n");
1544    ua->send_msg("Recycle\n");
1545    ua->send_msg("Purged\n");
1546    ua->send_msg("Cleaning\n");
1547    ua->send_msg("Error\n");
1548    return true;
1549 }
1550
1551 /*
1552  * Return default values for a job
1553  */
1554 static bool defaultscmd(UAContext *ua, const char *cmd)
1555 {
1556    char ed1[50];
1557    if (ua->argc != 2 || !ua->argv[1]) {
1558       return true;
1559    }
1560
1561    /* Send Job defaults */
1562    if (strcmp(ua->argk[1], "job") == 0) {
1563       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
1564          return true;
1565       }
1566       JOB *job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
1567       if (job) {
1568          USTORE store;
1569          ua->send_msg("job=%s", job->name());
1570          ua->send_msg("pool=%s", job->pool->name());
1571          ua->send_msg("messages=%s", job->messages->name());
1572          ua->send_msg("client=%s", job->client?job->client->name():_("*None*"));
1573          get_job_storage(&store, job, NULL);
1574          ua->send_msg("storage=%s", store.store->name());
1575          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
1576          ua->send_msg("level=%s", level_to_str(job->JobLevel));
1577          ua->send_msg("type=%s", job_type_to_str(job->JobType));
1578          ua->send_msg("fileset=%s", job->fileset->name());
1579          ua->send_msg("enabled=%d", job->enabled);
1580          ua->send_msg("catalog=%s", job->client?job->client->catalog->name():_("*None*"));
1581       }
1582    }
1583    /* Send Pool defaults */
1584    else if (strcmp(ua->argk[1], "pool") == 0) {
1585       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
1586          return true;
1587       }
1588       POOL *pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
1589       if (pool) {
1590          ua->send_msg("pool=%s", pool->name());
1591          ua->send_msg("pool_type=%s", pool->pool_type);
1592          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
1593          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
1594          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
1595          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
1596          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
1597          ua->send_msg("max_volumes=%d", pool->max_volumes);
1598          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
1599          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
1600          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
1601          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
1602          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
1603          ua->send_msg("auto_prune=%d", pool->AutoPrune);
1604          ua->send_msg("recycle=%d", pool->Recycle);
1605          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
1606          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
1607       }
1608    } 
1609    /* Send Storage defaults */
1610    else if (strcmp(ua->argk[1], "storage") == 0) {
1611       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
1612          return true;
1613       }
1614       STORE *storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
1615       DEVICE *device;
1616       if (storage) {
1617          ua->send_msg("storage=%s", storage->name());
1618          ua->send_msg("address=%s", storage->address);
1619          ua->send_msg("enabled=%d", storage->enabled);
1620          ua->send_msg("media_type=%s", storage->media_type);
1621          ua->send_msg("sdport=%d", storage->SDport);
1622          device = (DEVICE *)storage->device->first();
1623          ua->send_msg("device=%s", device->name());
1624          if (storage->device && storage->device->size() > 1) {
1625             while ((device = (DEVICE *)storage->device->next())) {
1626                ua->send_msg(",%s", device->name());
1627             }
1628          }
1629       }
1630    } 
1631    /* Send Client defaults */
1632    else if (strcmp(ua->argk[1], "client") == 0) {
1633       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
1634          return true;
1635       }
1636       CLIENT *client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
1637       if (client) {
1638          ua->send_msg("client=%s", client->name());
1639          ua->send_msg("address=%s", client->address);
1640          ua->send_msg("fdport=%d", client->FDport);
1641          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
1642          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
1643          ua->send_msg("autoprune=%d", client->AutoPrune);
1644          ua->send_msg("catalog=%s", client->catalog->name());
1645       }
1646    }
1647    return true;
1648 }