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