]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Add .lsfiles, .lsdirs, .update command to interface user with bvfs object
[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    if (!open_client_db(ua)) {
160       return 1;
161    }
162
163    int pos = find_arg_with_value(ua, "jobid");
164    if (pos != -1) {             /* find jobid arg */
165       if (is_a_number_list(ua->argv[pos])) {
166          bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
167       }
168    } else {
169       /* update cache for all jobids */
170       bvfs_update_cache(ua->jcr, ua->db);
171    }
172    return true;
173 }
174
175 static int bvfs_result_handler(void *ctx, int fields, char **row)
176 {
177    UAContext *ua = (UAContext *)ctx;
178    struct stat statp;
179    int32_t LinkFI;
180    char empty[] = "A A A A A A A A A A A A A A";
181
182    memset(&statp, 0, sizeof(struct stat));
183    decode_stat((row[BVFS_LStat] && row[BVFS_LStat][0])?row[BVFS_LStat]:empty,
184                &statp, &LinkFI);
185
186    if (fields == BVFS_DIR_RECORD) {
187       char *path = bvfs_basename_dir(row[BVFS_Name]);
188       ua->send_msg("%s\t%s\t\%s\n", row[BVFS_Id], row[BVFS_JobId], path);
189    } else if (fields == BVFS_FILE_RECORD) {
190       ua->send_msg("%s\t%s\t\%s\n", row[BVFS_Id], row[BVFS_JobId], row[BVFS_Name]);
191    }
192
193    return 0;
194 }
195
196 static bool bvfs_parse_arg(UAContext *ua, 
197                            DBId_t *pathid, char **path, char **jobid,
198                            int *limit, int *offset)
199 {
200    *pathid=0;
201    *limit=2000;
202    *offset=0;
203    *path=NULL;
204    *jobid=NULL;
205
206    for (int i=1; i<ua->argc; i++) {
207       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
208          if (is_a_number(ua->argv[i])) {
209             *pathid = str_to_int64(ua->argv[i]);
210          }
211       }
212       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
213          *path = ua->argv[i];
214       }
215       
216       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
217          if (is_a_number_list(ua->argv[i])) {
218             *jobid = ua->argv[i];
219          }
220       }
221
222       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
223          if (is_a_number(ua->argv[i])) {
224             *limit = str_to_int64(ua->argv[i]);
225          }
226       }
227
228       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
229          if (is_a_number(ua->argv[i])) {
230             *offset = str_to_int64(ua->argv[i]);
231          }
232       }
233    }
234
235    if (!((pathid || path) && jobid)) {
236       return false;
237    }
238
239    if (!open_client_db(ua)) {
240       return 1;
241    }
242
243    return true;
244 }
245
246 static bool dot_lsfiles(UAContext *ua, const char *cmd)
247 {
248    DBId_t pathid=0;
249    int limit=2000, offset=0;
250    char *path=NULL, *jobid=NULL;
251
252    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
253                        &limit, &offset))
254    {
255       ua->error_msg("Can't find jobid, pathid or path argument\n");
256       return true;              /* not enough param */
257    }
258
259    Bvfs fs(ua->jcr, ua->db);
260    fs.set_jobids(jobid);   
261    fs.set_limit(limit);
262    fs.set_offset(offset);
263    fs.set_handler(bvfs_result_handler, ua);
264
265    if (pathid) {
266       fs.ch_dir(pathid);
267    } else {
268       fs.ch_dir(path);
269    }
270
271    fs.ls_files();
272
273    return true;
274 }
275
276 static bool dot_lsdirs(UAContext *ua, const char *cmd)
277 {
278    DBId_t pathid=0;
279    int limit=2000, offset=0;
280    char *path=NULL, *jobid=NULL;
281
282    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
283                        &limit, &offset))
284    {
285       ua->error_msg("Can't find jobid, pathid or path argument\n");
286       return true;              /* not enough param */
287    }
288
289    Bvfs fs(ua->jcr, ua->db);
290    fs.set_jobids(jobid);   
291    fs.set_limit(limit);
292    fs.set_offset(offset);
293    fs.set_handler(bvfs_result_handler, ua);
294
295    if (pathid) {
296       fs.ch_dir(pathid);
297    } else {
298       fs.ch_dir(path);
299    }
300
301    fs.ls_dirs();
302
303    return true;
304 }
305
306 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
307 {
308    quit_cmd(ua, cmd);
309    return true;
310 }
311
312 static bool dot_help_cmd(UAContext *ua, const char *cmd)
313 {
314    qhelp_cmd(ua, cmd);
315    return true;
316 }
317
318 static bool getmsgscmd(UAContext *ua, const char *cmd)
319 {
320    if (console_msg_pending) {
321       do_messages(ua, cmd);
322    }
323    return 1;
324 }
325
326 #ifdef DEVELOPER
327 static void do_storage_die(UAContext *ua, STORE *store)
328 {
329    BSOCK *sd;
330    JCR *jcr = ua->jcr;
331    USTORE lstore;
332    
333    lstore.store = store;
334    pm_strcpy(lstore.store_source, _("unknown source"));
335    set_wstorage(jcr, &lstore);
336    /* Try connecting for up to 15 seconds */
337    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
338       store->name(), store->address, store->SDport);
339    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
340       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
341       return;
342    }
343    Dmsg0(120, _("Connected to storage daemon\n"));
344    sd = jcr->store_bsock;
345    sd->fsend(".die");
346    if (sd->recv() >= 0) {
347       ua->send_msg("%s", sd->msg);
348    }
349    sd->signal(BNET_TERMINATE);
350    sd->close();
351    jcr->store_bsock = NULL;
352    return;
353 }
354
355 static void do_client_die(UAContext *ua, CLIENT *client)
356 {
357    BSOCK *fd;
358
359    /* Connect to File daemon */
360
361    ua->jcr->client = client;
362    /* Try to connect for 15 seconds */
363    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
364       client->name(), client->address, client->FDport);
365    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
366       ua->error_msg(_("Failed to connect to Client.\n"));
367       return;
368    }
369    Dmsg0(120, "Connected to file daemon\n");
370    fd = ua->jcr->file_bsock;
371    fd->fsend(".die");
372    if (fd->recv() >= 0) {
373       ua->send_msg("%s", fd->msg);
374    }
375    fd->signal(BNET_TERMINATE);
376    fd->close();
377    ua->jcr->file_bsock = NULL;
378    return;
379 }
380
381 /*
382  * Create segmentation fault
383  */
384 static bool diecmd(UAContext *ua, const char *cmd)
385 {
386    STORE *store;
387    CLIENT *client;
388    int i;
389    JCR *jcr = NULL;
390    int a;
391
392    Dmsg1(120, "diecmd:%s:\n", cmd);
393
394    /* General debug? */
395    for (i=1; i<ua->argc; i++) {
396       if (strcasecmp(ua->argk[i], "dir") == 0 ||
397           strcasecmp(ua->argk[i], "director") == 0) {
398          ua->send_msg(_("The Director will segment fault.\n"));
399          a = jcr->JobId; /* ref NULL pointer */
400          jcr->JobId = 1000; /* another ref NULL pointer */
401          return 1;
402       }
403       if (strcasecmp(ua->argk[i], "client") == 0 ||
404           strcasecmp(ua->argk[i], "fd") == 0) {
405          client = NULL;
406          if (ua->argv[i]) {
407             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
408             if (client) {
409                do_client_die(ua, client);
410                return 1;
411             }
412          }
413          client = select_client_resource(ua);
414          if (client) {
415             do_client_die(ua, client);
416             return 1;
417          }
418       }
419
420       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
421           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
422           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
423          store = NULL;
424          if (ua->argv[i]) {
425             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
426             if (store) {
427                do_storage_die(ua, store);
428                return 1;
429             }
430          }
431          store = get_storage_resource(ua, false/*no default*/);
432          if (store) {
433             do_storage_die(ua, store);
434             return 1;
435          }
436       }
437    }
438    /*
439     * We didn't find an appropriate keyword above, so
440     * prompt the user.
441     */
442    start_prompt(ua, _("Available daemons are: \n"));
443    add_prompt(ua, _("Director"));
444    add_prompt(ua, _("Storage"));
445    add_prompt(ua, _("Client"));
446    switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
447    case 0:                         /* Director */
448       ua->send_msg(_("The Director will segment fault.\n"));
449       a = jcr->JobId; /* ref NULL pointer */
450       jcr->JobId = 1000; /* another ref NULL pointer */
451       break;
452    case 1:
453       store = get_storage_resource(ua, false/*no default*/);
454       if (store) {
455          do_storage_die(ua, store);
456       }
457       break;
458    case 2:
459       client = select_client_resource(ua);
460       if (client) {
461          do_client_die(ua, client);
462       }
463       break;
464    default:
465       break;
466    }
467    return true;
468 }
469
470 #else
471
472 /*
473  * Dummy routine for non-development version
474  */
475 static bool diecmd(UAContext *ua, const char *cmd)
476 {
477    return true;
478 }
479
480 #endif
481
482 static bool jobscmd(UAContext *ua, const char *cmd)
483 {
484    JOB *job;
485    LockRes();
486    foreach_res(job, R_JOB) {
487       if (acl_access_ok(ua, Job_ACL, job->name())) {
488          ua->send_msg("%s\n", job->name());
489       }
490    }
491    UnlockRes();
492    return true;
493 }
494
495 static bool filesetscmd(UAContext *ua, const char *cmd)
496 {
497    FILESET *fs;
498    LockRes();
499    foreach_res(fs, R_FILESET) {
500       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
501          ua->send_msg("%s\n", fs->name());
502       }
503    }
504    UnlockRes();
505    return true;
506 }
507
508 static bool clientscmd(UAContext *ua, const char *cmd)
509 {
510    CLIENT *client;       
511    LockRes();
512    foreach_res(client, R_CLIENT) {
513       if (acl_access_ok(ua, Client_ACL, client->name())) {
514          ua->send_msg("%s\n", client->name());
515       }
516    }
517    UnlockRes();
518    return true;
519 }
520
521 static bool msgscmd(UAContext *ua, const char *cmd)
522 {
523    MSGS *msgs = NULL;
524    LockRes();
525    foreach_res(msgs, R_MSGS) {
526       ua->send_msg("%s\n", msgs->name());
527    }
528    UnlockRes();
529    return true;
530 }
531
532 static bool poolscmd(UAContext *ua, const char *cmd)
533 {
534    POOL *pool;       
535    LockRes();
536    foreach_res(pool, R_POOL) {
537       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
538          ua->send_msg("%s\n", pool->name());
539       }
540    }
541    UnlockRes();
542    return true;
543 }
544
545 static bool storagecmd(UAContext *ua, const char *cmd)
546 {
547    STORE *store;
548    LockRes();
549    foreach_res(store, R_STORAGE) {
550       if (acl_access_ok(ua, Storage_ACL, store->name())) {
551          ua->send_msg("%s\n", store->name());
552       }
553    }
554    UnlockRes();
555    return true;
556 }
557
558
559 static bool typescmd(UAContext *ua, const char *cmd)
560 {
561    ua->send_msg("Backup\n");
562    ua->send_msg("Restore\n");
563    ua->send_msg("Admin\n");
564    ua->send_msg("Verify\n");
565    ua->send_msg("Migrate\n");
566    return true;
567 }
568
569
570 /*
571  * If this command is called, it tells the director that we
572  *  are a program that wants a sort of API, and hence,
573  *  we will probably suppress certain output, include more
574  *  error codes, and most of all send back a good number
575  *  of new signals that indicate whether or not the command
576  *  succeeded.
577  */
578 static bool api_cmd(UAContext *ua, const char *cmd)
579 {
580    if (ua->argc == 2) {
581       ua->api = atoi(ua->argk[1]);
582    } else {
583       ua->api = 1;
584    }
585    return true;
586 }
587
588 static int client_backups_handler(void *ctx, int num_field, char **row)
589 {
590    UAContext *ua = (UAContext *)ctx;
591    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
592       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
593    return 0;
594 }
595
596 /*
597  * Return the backups for this client 
598  *
599  *  .backups client=xxx fileset=yyy
600  *
601  */
602 static bool backupscmd(UAContext *ua, const char *cmd)
603 {
604    if (!open_client_db(ua)) {
605       return true;
606    }
607    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
608        strcmp(ua->argk[2], "fileset") != 0) {
609       return true;
610    }
611    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
612        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
613       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
614       return true;
615    }
616    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
617    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
618       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
619       return true;
620    }
621    return true;
622 }
623
624 static int sql_handler(void *ctx, int num_field, char **row)
625 {
626    UAContext *ua = (UAContext *)ctx;
627    POOL_MEM rows(PM_MESSAGE);
628
629    /* Check for nonsense */
630    if (num_field == 0 || row == NULL || row[0] == NULL) {
631       return 0;                       /* nothing returned */
632    }
633    for (int i=0; num_field--; i++) {
634       if (i == 0) {
635          pm_strcpy(rows, NPRT(row[0]));
636       } else {
637          pm_strcat(rows, NPRT(row[i]));
638       }
639       pm_strcat(rows, "\t");
640    }
641    if (!rows.c_str() || !*rows.c_str()) {
642       ua->send_msg("\t");
643    } else {
644       ua->send_msg("%s", rows.c_str());
645    }
646    return 0;
647 }
648
649 static bool sql_cmd(UAContext *ua, const char *cmd)
650 {
651    int index;
652    if (!open_client_db(ua)) {
653       return true;
654    }
655    index = find_arg_with_value(ua, "query");
656    if (index < 0) {
657       ua->error_msg(_("query keyword not found.\n"));
658       return true;
659    }
660    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
661       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
662       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
663       return true;
664    }
665    return true;
666 }
667       
668
669
670 static bool levelscmd(UAContext *ua, const char *cmd)
671 {
672    ua->send_msg("Incremental\n");
673    ua->send_msg("Full\n");
674    ua->send_msg("Differential\n");
675    ua->send_msg("VirtualFull\n");
676    ua->send_msg("Catalog\n");
677    ua->send_msg("InitCatalog\n");
678    ua->send_msg("VolumeToCatalog\n");
679    return true;
680 }
681
682 /*
683  * Return default values for a job
684  */
685 static bool defaultscmd(UAContext *ua, const char *cmd)
686 {
687    JOB *job;
688    CLIENT *client;
689    STORE *storage;
690    POOL *pool;
691    char ed1[50];
692
693    if (ua->argc != 2 || !ua->argv[1]) {
694       return true;
695    }
696
697    /* Job defaults */   
698    if (strcmp(ua->argk[1], "job") == 0) {
699       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
700          return true;
701       }
702       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
703       if (job) {
704          USTORE store;
705          ua->send_msg("job=%s", job->name());
706          ua->send_msg("pool=%s", job->pool->name());
707          ua->send_msg("messages=%s", job->messages->name());
708          ua->send_msg("client=%s", job->client->name());
709          get_job_storage(&store, job, NULL);
710          ua->send_msg("storage=%s", store.store->name());
711          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
712          ua->send_msg("level=%s", level_to_str(job->JobLevel));
713          ua->send_msg("type=%s", job_type_to_str(job->JobType));
714          ua->send_msg("fileset=%s", job->fileset->name());
715          ua->send_msg("enabled=%d", job->enabled);
716          ua->send_msg("catalog=%s", job->client->catalog->name());
717       }
718    } 
719    /* Client defaults */
720    else if (strcmp(ua->argk[1], "client") == 0) {
721       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
722          return true;   
723       }
724       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
725       if (client) {
726          ua->send_msg("client=%s", client->name());
727          ua->send_msg("address=%s", client->address);
728          ua->send_msg("fdport=%d", client->FDport);
729          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
730          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
731          ua->send_msg("autoprune=%d", client->AutoPrune);
732          ua->send_msg("catalog=%s", client->catalog->name());
733       }
734    }
735    /* Storage defaults */
736    else if (strcmp(ua->argk[1], "storage") == 0) {
737       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
738          return true;
739       }
740       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
741       DEVICE *device;
742       if (storage) {
743          ua->send_msg("storage=%s", storage->name());
744          ua->send_msg("address=%s", storage->address);
745          ua->send_msg("enabled=%d", storage->enabled);
746          ua->send_msg("media_type=%s", storage->media_type);
747          ua->send_msg("sdport=%d", storage->SDport);
748          device = (DEVICE *)storage->device->first();
749          ua->send_msg("device=%s", device->name());
750          if (storage->device->size() > 1) {
751             while ((device = (DEVICE *)storage->device->next())) {
752                ua->send_msg(",%s", device->name());
753             }
754          }
755       }
756    }
757    /* Pool defaults */
758    else if (strcmp(ua->argk[1], "pool") == 0) {
759       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
760          return true;
761       }
762       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
763       if (pool) {
764          ua->send_msg("pool=%s", pool->name());
765          ua->send_msg("pool_type=%s", pool->pool_type);
766          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
767          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
768          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
769          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
770          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
771          ua->send_msg("max_volumes=%d", pool->max_volumes);
772          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
773          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
774          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
775          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
776          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
777          ua->send_msg("auto_prune=%d", pool->AutoPrune);
778          ua->send_msg("recycle=%d", pool->Recycle);
779       }
780    }
781    return true;
782 }