]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Fix and document new queries
[bacula/bacula] / bacula / src / dird / ua_dotcmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Commands
31  *     These are "dot" commands, i.e. commands preceded
32  *        by a period. These commands are meant to be used
33  *        by a program, so there is no prompting, and the
34  *        returned results are (supposed to be) predictable.
35  *
36  *     Kern Sibbald, April MMII
37  *
38  *   Version $Id$
39  */
40
41 #include "bacula.h"
42 #include "dird.h"
43 #include "cats/bvfs.h"
44 #include "findlib/find.h"
45
46 /* Imported variables */
47
48 /* Imported functions */
49 extern void do_messages(UAContext *ua, const char *cmd);
50 extern int quit_cmd(UAContext *ua, const char *cmd);
51 extern int qhelp_cmd(UAContext *ua, const char *cmd);
52 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
53
54
55 /* Forward referenced functions */
56 static bool diecmd(UAContext *ua, const char *cmd);
57 static bool jobscmd(UAContext *ua, const char *cmd);
58 static bool filesetscmd(UAContext *ua, const char *cmd);
59 static bool clientscmd(UAContext *ua, const char *cmd);
60 static bool msgscmd(UAContext *ua, const char *cmd);
61 static bool poolscmd(UAContext *ua, const char *cmd);
62 static bool storagecmd(UAContext *ua, const char *cmd);
63 static bool defaultscmd(UAContext *ua, const char *cmd);
64 static bool typescmd(UAContext *ua, const char *cmd);
65 static bool backupscmd(UAContext *ua, const char *cmd);
66 static bool levelscmd(UAContext *ua, const char *cmd);
67 static bool getmsgscmd(UAContext *ua, const char *cmd);
68
69 static bool dot_lsdirs(UAContext *ua, const char *cmd);
70 static bool dot_lsfiles(UAContext *ua, const char *cmd);
71 static bool dot_update(UAContext *ua, const char *cmd);
72
73 static bool api_cmd(UAContext *ua, const char *cmd);
74 static bool sql_cmd(UAContext *ua, const char *cmd);
75 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
76 static bool dot_help_cmd(UAContext *ua, const char *cmd);
77
78 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
79 static struct cmdstruct commands[] = { /* help */  /* can be used in runscript */
80  { NT_(".api"),        api_cmd,          NULL,       false},
81  { NT_(".backups"),    backupscmd,       NULL,       false},
82  { NT_(".clients"),    clientscmd,       NULL,       true},
83  { NT_(".defaults"),   defaultscmd,      NULL,       false},
84  { NT_(".die"),        diecmd,           NULL,       false},
85  { NT_(".exit"),       dot_quit_cmd,     NULL,       false},
86  { NT_(".filesets"),   filesetscmd,      NULL,       false},
87  { NT_(".help"),       dot_help_cmd,     NULL,       false},
88  { NT_(".jobs"),       jobscmd,          NULL,       true},
89  { NT_(".levels"),     levelscmd,        NULL,       false},
90  { NT_(".messages"),   getmsgscmd,       NULL,       false},
91  { NT_(".msgs"),       msgscmd,          NULL,       false},
92  { NT_(".pools"),      poolscmd,         NULL,       true},
93  { NT_(".quit"),       dot_quit_cmd,     NULL,       false},
94  { NT_(".sql"),        sql_cmd,          NULL,       false},
95  { NT_(".status"),     dot_status_cmd,   NULL,       false},
96  { NT_(".storage"),    storagecmd,       NULL,       true},
97  { NT_(".lsdirs"),     dot_lsdirs,       NULL,       true},
98  { NT_(".lsfiles"),    dot_lsfiles,      NULL,       true},
99  { NT_(".update"),     dot_update,       NULL,       true},
100  { NT_(".types"),      typescmd,         NULL,       false} 
101              };
102 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
103
104 /*
105  * Execute a command from the UA
106  */
107 bool do_a_dot_command(UAContext *ua) 
108 {
109    int i;
110    int len;
111    bool ok = false;
112    bool found = false;
113    BSOCK *user = ua->UA_sock;
114
115    Dmsg1(1400, "Dot command: %s\n", user->msg);
116    if (ua->argc == 0) {
117       return false;
118    }
119
120    len = strlen(ua->argk[0]);
121    if (len == 1) {
122       if (ua->api) user->signal(BNET_CMD_BEGIN);
123       if (ua->api) user->signal(BNET_CMD_OK);
124       return true;                    /* no op */
125    }
126    for (i=0; i<comsize; i++) {     /* search for command */
127       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
128          /* Check if this command is authorized in RunScript */
129          if (ua->runscript && !commands[i].use_in_rs) {
130             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
131             break;
132          }
133          bool gui = ua->gui;
134          /* Check if command permitted, but "quit" is always OK */
135          if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
136              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
137             break;
138          }
139          Dmsg1(100, "Cmd: %s\n", ua->cmd);
140          ua->gui = true;
141          if (ua->api) user->signal(BNET_CMD_BEGIN);
142          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
143          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
144          ua->gui = gui;
145          found = true;
146          break;
147       }
148    }
149    if (!found) {
150       pm_strcat(user->msg, _(": is an invalid command.\n"));
151       ua->error_msg("%s", user->msg);
152       ok = false;
153    }
154    return ok;
155 }
156
157 static bool dot_update(UAContext *ua, const char *cmd)
158 {
159
160    if (!open_client_db(ua)) {
161       return 1;
162    }
163
164    int pos = find_arg_with_value(ua, "jobid");
165    if (pos != -1 && is_a_number_list(ua->argv[pos])) {
166       POOL_MEM jobids;
167       pm_strcpy(jobids, ua->argv[pos]);
168       bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, jobids.c_str());
169    } else {
170       /* update cache for all jobids */
171       bvfs_update_cache(ua->jcr, ua->db);
172    }
173    return true;
174 }
175
176 static int bvfs_result_handler(void *ctx, int fields, char **row)
177 {
178    UAContext *ua = (UAContext *)ctx;
179    struct stat statp;
180    int32_t LinkFI;
181    const char *fileid;
182    char *lstat;
183    char empty[] = "A A A A A A A A A A A A A A";
184
185    lstat = (row[BVFS_LStat] && row[BVFS_LStat][0])?row[BVFS_LStat]:empty;
186    fileid = (row[BVFS_FileId] && row[BVFS_FileId][0])?row[BVFS_FileId]:"0";
187
188    memset(&statp, 0, sizeof(struct stat));
189    decode_stat(lstat, &statp, &LinkFI);
190
191    Dmsg1(0, "type=%s\n", row[0]);
192    if (bvfs_is_dir(row)) {
193       char *path = bvfs_basename_dir(row[BVFS_Name]);
194       ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
195                    row[BVFS_JobId], row[BVFS_LStat], path);
196
197    } else if (bvfs_is_file(row)) {
198       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
199                    row[BVFS_FilenameId], fileid, row[BVFS_JobId],
200                    row[BVFS_LStat], row[BVFS_Name]);
201    }
202
203    return 0;
204 }
205
206 static bool bvfs_parse_arg(UAContext *ua, 
207                            DBId_t *pathid, char **path, char **jobid,
208                            int *limit, int *offset)
209 {
210    *pathid=0;
211    *limit=2000;
212    *offset=0;
213    *path=NULL;
214    *jobid=NULL;
215
216    for (int i=1; i<ua->argc; i++) {
217       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
218          if (is_a_number(ua->argv[i])) {
219             *pathid = str_to_int64(ua->argv[i]);
220          }
221       }
222       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
223          *path = ua->argv[i];
224       }
225       
226       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
227          if (is_a_number_list(ua->argv[i])) {
228             *jobid = ua->argv[i];
229          }
230       }
231
232       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
233          if (is_a_number(ua->argv[i])) {
234             *limit = str_to_int64(ua->argv[i]);
235          }
236       }
237
238       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
239          if (is_a_number(ua->argv[i])) {
240             *offset = str_to_int64(ua->argv[i]);
241          }
242       }
243    }
244
245    if (!((*pathid || *path) && *jobid)) {
246       return false;
247    }
248
249    if (!open_client_db(ua)) {
250       return false;
251    }
252
253    return true;
254 }
255
256 /* 
257  * .lsfiles jobid=1,2,3,4 pathid=10
258  * .lsfiles jobid=1,2,3,4 path=/
259  */
260 static bool dot_lsfiles(UAContext *ua, const char *cmd)
261 {
262    DBId_t pathid=0;
263    int limit=2000, offset=0;
264    char *path=NULL, *jobid=NULL;
265
266    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
267                        &limit, &offset))
268    {
269       ua->error_msg("Can't find jobid, pathid or path argument\n");
270       return true;              /* not enough param */
271    }
272
273    Bvfs fs(ua->jcr, ua->db);
274    fs.set_jobids(jobid);   
275    fs.set_handler(bvfs_result_handler, ua);
276    fs.set_limit(limit);
277
278    if (pathid) {
279       fs.ch_dir(pathid);
280    } else {
281       fs.ch_dir(path);
282    }
283
284    fs.set_offset(offset);
285
286    fs.ls_files();
287
288    return true;
289 }
290
291 /* 
292  * .lsdirs jobid=1,2,3,4 pathid=10
293  * .lsdirs jobid=1,2,3,4 path=/
294  * .lsdirs jobid=1,2,3,4 path=
295  */
296 static bool dot_lsdirs(UAContext *ua, const char *cmd)
297 {
298    DBId_t pathid=0;
299    int limit=2000, offset=0;
300    char *path=NULL, *jobid=NULL;
301
302    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
303                        &limit, &offset))
304    {
305       ua->error_msg("Can't find jobid, pathid or path argument\n");
306       return true;              /* not enough param */
307    }
308
309    Bvfs fs(ua->jcr, ua->db);
310    fs.set_jobids(jobid);   
311    fs.set_limit(limit);
312    fs.set_handler(bvfs_result_handler, ua);
313
314    if (pathid) {
315       fs.ch_dir(pathid);
316    } else {
317       fs.ch_dir(path);
318    }
319
320    fs.set_offset(offset);
321
322    fs.ls_special_dirs();
323    fs.ls_dirs();
324
325    return true;
326 }
327
328 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
329 {
330    quit_cmd(ua, cmd);
331    return true;
332 }
333
334 static bool dot_help_cmd(UAContext *ua, const char *cmd)
335 {
336    qhelp_cmd(ua, cmd);
337    return true;
338 }
339
340 static bool getmsgscmd(UAContext *ua, const char *cmd)
341 {
342    if (console_msg_pending) {
343       do_messages(ua, cmd);
344    }
345    return 1;
346 }
347
348 #ifdef DEVELOPER
349 static void do_storage_die(UAContext *ua, STORE *store)
350 {
351    BSOCK *sd;
352    JCR *jcr = ua->jcr;
353    USTORE lstore;
354    
355    lstore.store = store;
356    pm_strcpy(lstore.store_source, _("unknown source"));
357    set_wstorage(jcr, &lstore);
358    /* Try connecting for up to 15 seconds */
359    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
360       store->name(), store->address, store->SDport);
361    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
362       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
363       return;
364    }
365    Dmsg0(120, _("Connected to storage daemon\n"));
366    sd = jcr->store_bsock;
367    sd->fsend(".die");
368    if (sd->recv() >= 0) {
369       ua->send_msg("%s", sd->msg);
370    }
371    sd->signal(BNET_TERMINATE);
372    sd->close();
373    jcr->store_bsock = NULL;
374    return;
375 }
376
377 static void do_client_die(UAContext *ua, CLIENT *client)
378 {
379    BSOCK *fd;
380
381    /* Connect to File daemon */
382
383    ua->jcr->client = client;
384    /* Try to connect for 15 seconds */
385    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
386       client->name(), client->address, client->FDport);
387    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
388       ua->error_msg(_("Failed to connect to Client.\n"));
389       return;
390    }
391    Dmsg0(120, "Connected to file daemon\n");
392    fd = ua->jcr->file_bsock;
393    fd->fsend(".die");
394    if (fd->recv() >= 0) {
395       ua->send_msg("%s", fd->msg);
396    }
397    fd->signal(BNET_TERMINATE);
398    fd->close();
399    ua->jcr->file_bsock = NULL;
400    return;
401 }
402
403 /*
404  * Create segmentation fault
405  */
406 static bool diecmd(UAContext *ua, const char *cmd)
407 {
408    STORE *store;
409    CLIENT *client;
410    int i;
411    JCR *jcr = NULL;
412    int a;
413
414    Dmsg1(120, "diecmd:%s:\n", cmd);
415
416    /* General debug? */
417    for (i=1; i<ua->argc; i++) {
418       if (strcasecmp(ua->argk[i], "dir") == 0 ||
419           strcasecmp(ua->argk[i], "director") == 0) {
420          ua->send_msg(_("The Director will segment fault.\n"));
421          a = jcr->JobId; /* ref NULL pointer */
422          jcr->JobId = 1000; /* another ref NULL pointer */
423          return 1;
424       }
425       if (strcasecmp(ua->argk[i], "client") == 0 ||
426           strcasecmp(ua->argk[i], "fd") == 0) {
427          client = NULL;
428          if (ua->argv[i]) {
429             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
430             if (client) {
431                do_client_die(ua, client);
432                return 1;
433             }
434          }
435          client = select_client_resource(ua);
436          if (client) {
437             do_client_die(ua, client);
438             return 1;
439          }
440       }
441
442       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
443           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
444           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
445          store = NULL;
446          if (ua->argv[i]) {
447             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
448             if (store) {
449                do_storage_die(ua, store);
450                return 1;
451             }
452          }
453          store = get_storage_resource(ua, false/*no default*/);
454          if (store) {
455             do_storage_die(ua, store);
456             return 1;
457          }
458       }
459    }
460    /*
461     * We didn't find an appropriate keyword above, so
462     * prompt the user.
463     */
464    start_prompt(ua, _("Available daemons are: \n"));
465    add_prompt(ua, _("Director"));
466    add_prompt(ua, _("Storage"));
467    add_prompt(ua, _("Client"));
468    switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
469    case 0:                         /* Director */
470       ua->send_msg(_("The Director will segment fault.\n"));
471       a = jcr->JobId; /* ref NULL pointer */
472       jcr->JobId = 1000; /* another ref NULL pointer */
473       break;
474    case 1:
475       store = get_storage_resource(ua, false/*no default*/);
476       if (store) {
477          do_storage_die(ua, store);
478       }
479       break;
480    case 2:
481       client = select_client_resource(ua);
482       if (client) {
483          do_client_die(ua, client);
484       }
485       break;
486    default:
487       break;
488    }
489    return true;
490 }
491
492 #else
493
494 /*
495  * Dummy routine for non-development version
496  */
497 static bool diecmd(UAContext *ua, const char *cmd)
498 {
499    return true;
500 }
501
502 #endif
503
504 static bool jobscmd(UAContext *ua, const char *cmd)
505 {
506    JOB *job;
507    LockRes();
508    foreach_res(job, R_JOB) {
509       if (acl_access_ok(ua, Job_ACL, job->name())) {
510          ua->send_msg("%s\n", job->name());
511       }
512    }
513    UnlockRes();
514    return true;
515 }
516
517 static bool filesetscmd(UAContext *ua, const char *cmd)
518 {
519    FILESET *fs;
520    LockRes();
521    foreach_res(fs, R_FILESET) {
522       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
523          ua->send_msg("%s\n", fs->name());
524       }
525    }
526    UnlockRes();
527    return true;
528 }
529
530 static bool clientscmd(UAContext *ua, const char *cmd)
531 {
532    CLIENT *client;       
533    LockRes();
534    foreach_res(client, R_CLIENT) {
535       if (acl_access_ok(ua, Client_ACL, client->name())) {
536          ua->send_msg("%s\n", client->name());
537       }
538    }
539    UnlockRes();
540    return true;
541 }
542
543 static bool msgscmd(UAContext *ua, const char *cmd)
544 {
545    MSGS *msgs = NULL;
546    LockRes();
547    foreach_res(msgs, R_MSGS) {
548       ua->send_msg("%s\n", msgs->name());
549    }
550    UnlockRes();
551    return true;
552 }
553
554 static bool poolscmd(UAContext *ua, const char *cmd)
555 {
556    POOL *pool;       
557    LockRes();
558    foreach_res(pool, R_POOL) {
559       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
560          ua->send_msg("%s\n", pool->name());
561       }
562    }
563    UnlockRes();
564    return true;
565 }
566
567 static bool storagecmd(UAContext *ua, const char *cmd)
568 {
569    STORE *store;
570    LockRes();
571    foreach_res(store, R_STORAGE) {
572       if (acl_access_ok(ua, Storage_ACL, store->name())) {
573          ua->send_msg("%s\n", store->name());
574       }
575    }
576    UnlockRes();
577    return true;
578 }
579
580
581 static bool typescmd(UAContext *ua, const char *cmd)
582 {
583    ua->send_msg("Backup\n");
584    ua->send_msg("Restore\n");
585    ua->send_msg("Admin\n");
586    ua->send_msg("Verify\n");
587    ua->send_msg("Migrate\n");
588    return true;
589 }
590
591
592 /*
593  * If this command is called, it tells the director that we
594  *  are a program that wants a sort of API, and hence,
595  *  we will probably suppress certain output, include more
596  *  error codes, and most of all send back a good number
597  *  of new signals that indicate whether or not the command
598  *  succeeded.
599  */
600 static bool api_cmd(UAContext *ua, const char *cmd)
601 {
602    if (ua->argc == 2) {
603       ua->api = atoi(ua->argk[1]);
604    } else {
605       ua->api = 1;
606    }
607    return true;
608 }
609
610 static int client_backups_handler(void *ctx, int num_field, char **row)
611 {
612    UAContext *ua = (UAContext *)ctx;
613    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
614       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
615    return 0;
616 }
617
618 /*
619  * Return the backups for this client 
620  *
621  *  .backups client=xxx fileset=yyy
622  *
623  */
624 static bool backupscmd(UAContext *ua, const char *cmd)
625 {
626    if (!open_client_db(ua)) {
627       return true;
628    }
629    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
630        strcmp(ua->argk[2], "fileset") != 0) {
631       return true;
632    }
633    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
634        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
635       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
636       return true;
637    }
638    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
639    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
640       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
641       return true;
642    }
643    return true;
644 }
645
646 static int sql_handler(void *ctx, int num_field, char **row)
647 {
648    UAContext *ua = (UAContext *)ctx;
649    POOL_MEM rows(PM_MESSAGE);
650
651    /* Check for nonsense */
652    if (num_field == 0 || row == NULL || row[0] == NULL) {
653       return 0;                       /* nothing returned */
654    }
655    for (int i=0; num_field--; i++) {
656       if (i == 0) {
657          pm_strcpy(rows, NPRT(row[0]));
658       } else {
659          pm_strcat(rows, NPRT(row[i]));
660       }
661       pm_strcat(rows, "\t");
662    }
663    if (!rows.c_str() || !*rows.c_str()) {
664       ua->send_msg("\t");
665    } else {
666       ua->send_msg("%s", rows.c_str());
667    }
668    return 0;
669 }
670
671 static bool sql_cmd(UAContext *ua, const char *cmd)
672 {
673    int index;
674    if (!open_client_db(ua)) {
675       return true;
676    }
677    index = find_arg_with_value(ua, "query");
678    if (index < 0) {
679       ua->error_msg(_("query keyword not found.\n"));
680       return true;
681    }
682    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
683       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
684       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
685       return true;
686    }
687    return true;
688 }
689       
690
691
692 static bool levelscmd(UAContext *ua, const char *cmd)
693 {
694    ua->send_msg("Incremental\n");
695    ua->send_msg("Full\n");
696    ua->send_msg("Differential\n");
697    ua->send_msg("VirtualFull\n");
698    ua->send_msg("Catalog\n");
699    ua->send_msg("InitCatalog\n");
700    ua->send_msg("VolumeToCatalog\n");
701    return true;
702 }
703
704 /*
705  * Return default values for a job
706  */
707 static bool defaultscmd(UAContext *ua, const char *cmd)
708 {
709    JOB *job;
710    CLIENT *client;
711    STORE *storage;
712    POOL *pool;
713    char ed1[50];
714
715    if (ua->argc != 2 || !ua->argv[1]) {
716       return true;
717    }
718
719    /* Job defaults */   
720    if (strcmp(ua->argk[1], "job") == 0) {
721       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
722          return true;
723       }
724       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
725       if (job) {
726          USTORE store;
727          ua->send_msg("job=%s", job->name());
728          ua->send_msg("pool=%s", job->pool->name());
729          ua->send_msg("messages=%s", job->messages->name());
730          ua->send_msg("client=%s", job->client->name());
731          get_job_storage(&store, job, NULL);
732          ua->send_msg("storage=%s", store.store->name());
733          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
734          ua->send_msg("level=%s", level_to_str(job->JobLevel));
735          ua->send_msg("type=%s", job_type_to_str(job->JobType));
736          ua->send_msg("fileset=%s", job->fileset->name());
737          ua->send_msg("enabled=%d", job->enabled);
738          ua->send_msg("catalog=%s", job->client->catalog->name());
739       }
740    } 
741    /* Client defaults */
742    else if (strcmp(ua->argk[1], "client") == 0) {
743       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
744          return true;   
745       }
746       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
747       if (client) {
748          ua->send_msg("client=%s", client->name());
749          ua->send_msg("address=%s", client->address);
750          ua->send_msg("fdport=%d", client->FDport);
751          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
752          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
753          ua->send_msg("autoprune=%d", client->AutoPrune);
754          ua->send_msg("catalog=%s", client->catalog->name());
755       }
756    }
757    /* Storage defaults */
758    else if (strcmp(ua->argk[1], "storage") == 0) {
759       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
760          return true;
761       }
762       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
763       DEVICE *device;
764       if (storage) {
765          ua->send_msg("storage=%s", storage->name());
766          ua->send_msg("address=%s", storage->address);
767          ua->send_msg("enabled=%d", storage->enabled);
768          ua->send_msg("media_type=%s", storage->media_type);
769          ua->send_msg("sdport=%d", storage->SDport);
770          device = (DEVICE *)storage->device->first();
771          ua->send_msg("device=%s", device->name());
772          if (storage->device->size() > 1) {
773             while ((device = (DEVICE *)storage->device->next())) {
774                ua->send_msg(",%s", device->name());
775             }
776          }
777       }
778    }
779    /* Pool defaults */
780    else if (strcmp(ua->argk[1], "pool") == 0) {
781       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
782          return true;
783       }
784       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
785       if (pool) {
786          ua->send_msg("pool=%s", pool->name());
787          ua->send_msg("pool_type=%s", pool->pool_type);
788          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
789          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
790          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
791          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
792          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
793          ua->send_msg("max_volumes=%d", pool->max_volumes);
794          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
795          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
796          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
797          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
798          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
799          ua->send_msg("auto_prune=%d", pool->AutoPrune);
800          ua->send_msg("recycle=%d", pool->Recycle);
801       }
802    }
803    return true;
804 }