]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Add .dump and .exit commands for daemons
[bacula/bacula] / bacula / src / dird / ua_dotcmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2010 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  */
39
40 #include "bacula.h"
41 #include "dird.h"
42 #include "cats/bvfs.h"
43 #include "findlib/find.h"
44
45 /* Imported variables */
46
47 /* Imported functions */
48 extern void do_messages(UAContext *ua, const char *cmd);
49 extern int quit_cmd(UAContext *ua, const char *cmd);
50 extern int qhelp_cmd(UAContext *ua, const char *cmd);
51 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
52
53
54 /* Forward referenced functions */
55 static bool admin_cmds(UAContext *ua, const char *cmd);
56 static bool jobscmd(UAContext *ua, const char *cmd);
57 static bool filesetscmd(UAContext *ua, const char *cmd);
58 static bool clientscmd(UAContext *ua, const char *cmd);
59 static bool msgscmd(UAContext *ua, const char *cmd);
60 static bool poolscmd(UAContext *ua, const char *cmd);
61 static bool storagecmd(UAContext *ua, const char *cmd);
62 static bool defaultscmd(UAContext *ua, const char *cmd);
63 static bool typescmd(UAContext *ua, const char *cmd);
64 static bool backupscmd(UAContext *ua, const char *cmd);
65 static bool levelscmd(UAContext *ua, const char *cmd);
66 static bool getmsgscmd(UAContext *ua, const char *cmd);
67 static bool volstatuscmd(UAContext *ua, const char *cmd);
68 static bool mediatypescmd(UAContext *ua, const char *cmd);
69 static bool locationscmd(UAContext *ua, const char *cmd);
70 static bool mediacmd(UAContext *ua, const char *cmd);
71 static bool aopcmd(UAContext *ua, const char *cmd);
72
73 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd);
74 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd);
75 static bool dot_bvfs_update(UAContext *ua, const char *cmd);
76
77 static bool api_cmd(UAContext *ua, const char *cmd);
78 static bool sql_cmd(UAContext *ua, const char *cmd);
79 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
80 static bool dot_help_cmd(UAContext *ua, const char *cmd);
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_(".defaults"),   defaultscmd,      NULL,       false},
88  { NT_(".die"),        admin_cmds,       NULL,       false},
89  { NT_(".dump"),       admin_cmds,       NULL,       false},
90  { NT_(".exit"),       admin_cmds,       NULL,       false},
91  { NT_(".filesets"),   filesetscmd,      NULL,       false},
92  { NT_(".help"),       dot_help_cmd,     NULL,       false},
93  { NT_(".jobs"),       jobscmd,          NULL,       true},
94  { NT_(".levels"),     levelscmd,        NULL,       false},
95  { NT_(".messages"),   getmsgscmd,       NULL,       false},
96  { NT_(".msgs"),       msgscmd,          NULL,       false},
97  { NT_(".pools"),      poolscmd,         NULL,       true},
98  { NT_(".quit"),       dot_quit_cmd,     NULL,       false},
99  { NT_(".sql"),        sql_cmd,          NULL,       false},
100  { NT_(".status"),     dot_status_cmd,   NULL,       false},
101  { NT_(".storage"),    storagecmd,       NULL,       true},
102  { NT_(".volstatus"),  volstatuscmd,     NULL,       true},
103  { NT_(".media"),      mediacmd,         NULL,       true},
104  { NT_(".mediatypes"), mediatypescmd,    NULL,       true},
105  { NT_(".locations"),  locationscmd,     NULL,       true},
106  { NT_(".actiononpurge"),aopcmd,         NULL,       true},
107  { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL,       true},
108  { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles,NULL,       true},
109  { NT_(".bvfs_update"), dot_bvfs_update, NULL,       true},
110  { NT_(".types"),      typescmd,         NULL,       false}
111              };
112 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
113
114 /*
115  * Execute a command from the UA
116  */
117 bool do_a_dot_command(UAContext *ua) 
118 {
119    int i;
120    int len;
121    bool ok = false;
122    bool found = false;
123    BSOCK *user = ua->UA_sock;
124
125    Dmsg1(1400, "Dot command: %s\n", user->msg);
126    if (ua->argc == 0) {
127       return false;
128    }
129
130    len = strlen(ua->argk[0]);
131    if (len == 1) {
132       if (ua->api) user->signal(BNET_CMD_BEGIN);
133       if (ua->api) user->signal(BNET_CMD_OK);
134       return true;                    /* no op */
135    }
136    for (i=0; i<comsize; i++) {     /* search for command */
137       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
138          /* Check if this command is authorized in RunScript */
139          if (ua->runscript && !commands[i].use_in_rs) {
140             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
141             break;
142          }
143          bool gui = ua->gui;
144          /* Check if command permitted, but "quit" is always OK */
145          if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
146              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
147             break;
148          }
149          Dmsg1(100, "Cmd: %s\n", ua->cmd);
150          ua->gui = true;
151          if (ua->api) user->signal(BNET_CMD_BEGIN);
152          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
153          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
154          ua->gui = gui;
155          found = true;
156          break;
157       }
158    }
159    if (!found) {
160       pm_strcat(user->msg, _(": is an invalid command.\n"));
161       ua->error_msg("%s", user->msg);
162       ok = false;
163    }
164    return ok;
165 }
166
167 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
168 {
169    if (!open_new_client_db(ua)) {
170       return 1;
171    }
172
173    int pos = find_arg_with_value(ua, "jobid");
174    if (pos != -1 && is_a_number_list(ua->argv[pos])) {
175       POOL_MEM jobids;
176       pm_strcpy(jobids, ua->argv[pos]);
177       bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, jobids.c_str());
178    } else {
179       /* update cache for all jobids */
180       bvfs_update_cache(ua->jcr, ua->db);
181    }
182    
183    close_db(ua);
184    return true;
185 }
186
187 static int bvfs_result_handler(void *ctx, int fields, char **row)
188 {
189    UAContext *ua = (UAContext *)ctx;
190    struct stat statp;
191    int32_t LinkFI;
192    char *fileid=row[BVFS_FileId];
193    char *lstat=row[BVFS_LStat];
194    char *jobid=row[BVFS_JobId];
195    
196    char empty[] = "A A A A A A A A A A A A A A";
197    char zero[] = "0";
198
199    /* We need to deal with non existant path */
200    if (!fileid || !is_a_number(fileid)) {
201       lstat = empty;
202       jobid = zero;
203       fileid = zero;
204    }
205
206    memset(&statp, 0, sizeof(struct stat));
207    decode_stat(lstat, &statp, &LinkFI);
208
209    Dmsg1(100, "type=%s\n", row[0]);
210    if (bvfs_is_dir(row)) {
211       char *path = bvfs_basename_dir(row[BVFS_Name]);
212       ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
213                    jobid, lstat, path);
214
215    } else if (bvfs_is_file(row)) {
216       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
217                    row[BVFS_FilenameId], fileid, jobid,
218                    lstat, row[BVFS_Name]);
219    }
220
221    return 0;
222 }
223
224 static bool bvfs_parse_arg(UAContext *ua, 
225                            DBId_t *pathid, char **path, char **jobid,
226                            int *limit, int *offset)
227 {
228    *pathid=0;
229    *limit=2000;
230    *offset=0;
231    *path=NULL;
232    *jobid=NULL;
233
234    for (int i=1; i<ua->argc; i++) {
235       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
236          if (is_a_number(ua->argv[i])) {
237             *pathid = str_to_int64(ua->argv[i]);
238          }
239       }
240       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
241          *path = ua->argv[i];
242       }
243       
244       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
245          if (is_a_number_list(ua->argv[i])) {
246             *jobid = ua->argv[i];
247          }
248       }
249
250       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
251          if (is_a_number(ua->argv[i])) {
252             *limit = str_to_int64(ua->argv[i]);
253          }
254       }
255
256       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
257          if (is_a_number(ua->argv[i])) {
258             *offset = str_to_int64(ua->argv[i]);
259          }
260       }
261    }
262
263    if (!((*pathid || *path) && *jobid)) {
264       return false;
265    }
266
267    if (!open_client_db(ua)) {
268       return false;
269    }
270
271    return true;
272 }
273
274 /* 
275  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
276  * .bvfs_lsfiles jobid=1,2,3,4 path=/
277  */
278 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
279 {
280    DBId_t pathid=0;
281    int limit=2000, offset=0;
282    char *path=NULL, *jobid=NULL;
283
284    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
285                        &limit, &offset))
286    {
287       ua->error_msg("Can't find jobid, pathid or path argument\n");
288       return true;              /* not enough param */
289    }
290
291    Bvfs fs(ua->jcr, ua->db);
292    fs.set_jobids(jobid);   
293    fs.set_handler(bvfs_result_handler, ua);
294    fs.set_limit(limit);
295
296    if (pathid) {
297       fs.ch_dir(pathid);
298    } else {
299       fs.ch_dir(path);
300    }
301
302    fs.set_offset(offset);
303
304    fs.ls_files();
305
306    return true;
307 }
308
309 /* 
310  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
311  * .bvfs_lsdirs jobid=1,2,3,4 path=/
312  * .bvfs_lsdirs jobid=1,2,3,4 path=
313  */
314 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
315 {
316    DBId_t pathid=0;
317    int limit=2000, offset=0;
318    char *path=NULL, *jobid=NULL;
319
320    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
321                        &limit, &offset))
322    {
323       ua->error_msg("Can't find jobid, pathid or path argument\n");
324       return true;              /* not enough param */
325    }
326
327    Bvfs fs(ua->jcr, ua->db);
328    fs.set_jobids(jobid);   
329    fs.set_limit(limit);
330    fs.set_handler(bvfs_result_handler, ua);
331
332    if (pathid) {
333       fs.ch_dir(pathid);
334    } else {
335       fs.ch_dir(path);
336    }
337
338    fs.set_offset(offset);
339
340    fs.ls_special_dirs();
341    fs.ls_dirs();
342
343    return true;
344 }
345
346 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
347 {
348    quit_cmd(ua, cmd);
349    return true;
350 }
351
352 static bool dot_help_cmd(UAContext *ua, const char *cmd)
353 {
354    qhelp_cmd(ua, cmd);
355    return true;
356 }
357
358 static bool getmsgscmd(UAContext *ua, const char *cmd)
359 {
360    if (console_msg_pending) {
361       do_messages(ua, cmd);
362    }
363    return 1;
364 }
365
366 #ifdef DEVELOPER
367 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
368 {
369    BSOCK *sd;
370    JCR *jcr = ua->jcr;
371    USTORE lstore;
372    
373    lstore.store = store;
374    pm_strcpy(lstore.store_source, _("unknown source"));
375    set_wstorage(jcr, &lstore);
376    /* Try connecting for up to 15 seconds */
377    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
378       store->name(), store->address, store->SDport);
379    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
380       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
381       return;
382    }
383    Dmsg0(120, _("Connected to storage daemon\n"));
384    sd = jcr->store_bsock;
385    sd->fsend("%s", cmd);
386    if (sd->recv() >= 0) {
387       ua->send_msg("%s", sd->msg);
388    }
389    sd->signal(BNET_TERMINATE);
390    sd->close();
391    jcr->store_bsock = NULL;
392    return;
393 }
394
395 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
396 {
397    BSOCK *fd;
398
399    /* Connect to File daemon */
400
401    ua->jcr->client = client;
402    /* Try to connect for 15 seconds */
403    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
404       client->name(), client->address, client->FDport);
405    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
406       ua->error_msg(_("Failed to connect to Client.\n"));
407       return;
408    }
409    Dmsg0(120, "Connected to file daemon\n");
410    fd = ua->jcr->file_bsock;
411    fd->fsend("%s", cmd);
412    if (fd->recv() >= 0) {
413       ua->send_msg("%s", fd->msg);
414    }
415    fd->signal(BNET_TERMINATE);
416    fd->close();
417    ua->jcr->file_bsock = NULL;
418    return;
419 }
420
421 /*
422  *   .die (seg fault)
423  *   .dump (sm_dump)
424  *   .exit (no arg => .quit)
425  */
426 static bool admin_cmds(UAContext *ua, const char *cmd)
427 {
428    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
429    STORE *store=NULL;
430    CLIENT *client=NULL;
431    bool dir=false;
432    bool do_deadlock=false;
433    const char *remote_cmd;
434    int i;
435    JCR *jcr = NULL;
436    int a;
437    if (strncmp(ua->argk[0], ".die", 4) == 0) {
438       if (find_arg(ua, "deadlock") > 0) {
439          do_deadlock = true;
440          remote_cmd = ".die deadlock";
441       } else {
442          remote_cmd = ".die";
443       }
444    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
445       remote_cmd = "sm_dump";
446    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
447       remote_cmd = "exit";
448    } else {
449       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
450       return true;
451    }
452    /* General debug? */
453    for (i=1; i<ua->argc; i++) {
454       if (strcasecmp(ua->argk[i], "dir") == 0 ||
455           strcasecmp(ua->argk[i], "director") == 0) {
456          dir = true;
457       }
458       if (strcasecmp(ua->argk[i], "client") == 0 ||
459           strcasecmp(ua->argk[i], "fd") == 0) {
460          client = NULL;
461          if (ua->argv[i]) {
462             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
463          }
464          if (!client) {
465             client = select_client_resource(ua);
466          }
467       }
468    
469       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
470           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
471           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
472          store = NULL;
473          if (ua->argv[i]) {
474             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
475          }
476          if (!store) {
477             store = get_storage_resource(ua, false/*no default*/);
478          }
479       }
480    }
481
482    if (!dir && !store && !client) {
483       /*
484        * We didn't find an appropriate keyword above, so
485        * prompt the user.
486        */
487       start_prompt(ua, _("Available daemons are: \n"));
488       add_prompt(ua, _("Director"));
489       add_prompt(ua, _("Storage"));
490       add_prompt(ua, _("Client"));
491       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
492       case 0:                         /* Director */
493          dir=true;
494          break;
495       case 1:
496          store = get_storage_resource(ua, false/*no default*/);
497          break;
498       case 2:
499          client = select_client_resource(ua);
500          break;
501       default:
502          break;
503       }
504    }
505
506    if (store) {
507       do_storage_cmd(ua, store, remote_cmd);
508    }
509
510    if (client) {
511       do_client_cmd(ua, client, remote_cmd);
512    }
513
514    if (dir) {
515       if (strncmp(remote_cmd, ".die", 4) == 0) {
516          if (do_deadlock) {
517             ua->send_msg(_("The Director will generate a deadlock.\n"));
518             P(mutex); 
519             P(mutex);
520          }
521          ua->send_msg(_("The Director will segment fault.\n"));
522          a = jcr->JobId; /* ref NULL pointer */
523          jcr->JobId = 1000; /* another ref NULL pointer */
524
525       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
526          sm_dump(false, true);
527       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
528          dot_quit_cmd(ua, cmd);
529       }
530    }
531
532    return true;
533 }
534
535 #else
536
537 /*
538  * Dummy routine for non-development version
539  */
540 static bool die_or_dump_cmd(UAContext *ua, const char *cmd)
541 {
542    return true;
543 }
544
545 #endif
546
547 /* 
548  * Can use an argument to filter on JobType
549  * .jobs [type=B]
550  */
551 static bool jobscmd(UAContext *ua, const char *cmd)
552 {
553    JOB *job;
554    uint32_t type = 0;
555    int pos;
556    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
557       type = ua->argv[pos][0];
558    }
559    LockRes();
560    foreach_res(job, R_JOB) {
561       if (!type || type == job->JobType) {
562          if (acl_access_ok(ua, Job_ACL, job->name())) {
563             ua->send_msg("%s\n", job->name());
564          }
565       }
566    }
567    UnlockRes();
568    return true;
569 }
570
571 static bool filesetscmd(UAContext *ua, const char *cmd)
572 {
573    FILESET *fs;
574    LockRes();
575    foreach_res(fs, R_FILESET) {
576       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
577          ua->send_msg("%s\n", fs->name());
578       }
579    }
580    UnlockRes();
581    return true;
582 }
583
584 static bool clientscmd(UAContext *ua, const char *cmd)
585 {
586    CLIENT *client;       
587    LockRes();
588    foreach_res(client, R_CLIENT) {
589       if (acl_access_ok(ua, Client_ACL, client->name())) {
590          ua->send_msg("%s\n", client->name());
591       }
592    }
593    UnlockRes();
594    return true;
595 }
596
597 static bool msgscmd(UAContext *ua, const char *cmd)
598 {
599    MSGS *msgs = NULL;
600    LockRes();
601    foreach_res(msgs, R_MSGS) {
602       ua->send_msg("%s\n", msgs->name());
603    }
604    UnlockRes();
605    return true;
606 }
607
608 static bool poolscmd(UAContext *ua, const char *cmd)
609 {
610    POOL *pool;       
611    LockRes();
612    foreach_res(pool, R_POOL) {
613       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
614          ua->send_msg("%s\n", pool->name());
615       }
616    }
617    UnlockRes();
618    return true;
619 }
620
621 static bool storagecmd(UAContext *ua, const char *cmd)
622 {
623    STORE *store;
624    LockRes();
625    foreach_res(store, R_STORAGE) {
626       if (acl_access_ok(ua, Storage_ACL, store->name())) {
627          ua->send_msg("%s\n", store->name());
628       }
629    }
630    UnlockRes();
631    return true;
632 }
633
634 static bool aopcmd(UAContext *ua, const char *cmd)
635 {
636    ua->send_msg("None\n");
637    ua->send_msg("Truncate\n");
638    return true;
639 }
640
641 static bool typescmd(UAContext *ua, const char *cmd)
642 {
643    ua->send_msg("Backup\n");
644    ua->send_msg("Restore\n");
645    ua->send_msg("Admin\n");
646    ua->send_msg("Verify\n");
647    ua->send_msg("Migrate\n");
648    ua->send_msg("Copy\n");
649    return true;
650 }
651
652 /*
653  * If this command is called, it tells the director that we
654  *  are a program that wants a sort of API, and hence,
655  *  we will probably suppress certain output, include more
656  *  error codes, and most of all send back a good number
657  *  of new signals that indicate whether or not the command
658  *  succeeded.
659  */
660 static bool api_cmd(UAContext *ua, const char *cmd)
661 {
662    if (ua->argc == 2) {
663       ua->api = atoi(ua->argk[1]);
664    } else {
665       ua->api = 1;
666    }
667    return true;
668 }
669
670 static int client_backups_handler(void *ctx, int num_field, char **row)
671 {
672    UAContext *ua = (UAContext *)ctx;
673    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
674       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
675    return 0;
676 }
677
678 /*
679  * Return the backups for this client 
680  *
681  *  .backups client=xxx fileset=yyy
682  *
683  */
684 static bool backupscmd(UAContext *ua, const char *cmd)
685 {
686    if (!open_client_db(ua)) {
687       return true;
688    }
689    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
690        strcmp(ua->argk[2], "fileset") != 0) {
691       return true;
692    }
693    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
694        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
695       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
696       return true;
697    }
698    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
699    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
700       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
701       return true;
702    }
703    return true;
704 }
705
706 static int sql_handler(void *ctx, int num_field, char **row)
707 {
708    UAContext *ua = (UAContext *)ctx;
709    POOL_MEM rows(PM_MESSAGE);
710
711    /* Check for nonsense */
712    if (num_field == 0 || row == NULL || row[0] == NULL) {
713       return 0;                       /* nothing returned */
714    }
715    for (int i=0; num_field--; i++) {
716       if (i == 0) {
717          pm_strcpy(rows, NPRT(row[0]));
718       } else {
719          pm_strcat(rows, NPRT(row[i]));
720       }
721       pm_strcat(rows, "\t");
722    }
723    if (!rows.c_str() || !*rows.c_str()) {
724       ua->send_msg("\t");
725    } else {
726       ua->send_msg("%s", rows.c_str());
727    }
728    return 0;
729 }
730
731 static bool sql_cmd(UAContext *ua, const char *cmd)
732 {
733    int index;
734    if (!open_client_db(ua)) {
735       return true;
736    }
737    index = find_arg_with_value(ua, "query");
738    if (index < 0) {
739       ua->error_msg(_("query keyword not found.\n"));
740       return true;
741    }
742    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
743       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
744       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
745       return true;
746    }
747    return true;
748 }
749       
750 static int one_handler(void *ctx, int num_field, char **row)
751 {
752    UAContext *ua = (UAContext *)ctx;
753    ua->send_msg("%s\n", row[0]);
754    return 0;
755 }
756
757 static bool mediatypescmd(UAContext *ua, const char *cmd)
758 {
759    if (!open_client_db(ua)) {
760       return true;
761    }
762    if (!db_sql_query(ua->db, 
763                   "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
764                   one_handler, (void *)ua)) 
765    {
766       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
767    }
768    return true;
769 }
770
771 static bool mediacmd(UAContext *ua, const char *cmd)
772 {
773    if (!open_client_db(ua)) {
774       return true;
775    }
776    if (!db_sql_query(ua->db, 
777                   "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
778                   one_handler, (void *)ua)) 
779    {
780       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
781    }
782    return true;
783 }
784
785 static bool locationscmd(UAContext *ua, const char *cmd)
786 {
787    if (!open_client_db(ua)) {
788       return true;
789    }
790    if (!db_sql_query(ua->db, 
791                   "SELECT DISTINCT Location FROM Location ORDER BY Location",
792                   one_handler, (void *)ua)) 
793    {
794       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
795    }
796    return true;
797 }
798
799 static bool levelscmd(UAContext *ua, const char *cmd)
800 {
801    ua->send_msg("Incremental\n");
802    ua->send_msg("Full\n");
803    ua->send_msg("Differential\n");
804    ua->send_msg("VirtualFull\n");
805    ua->send_msg("Catalog\n");
806    ua->send_msg("InitCatalog\n");
807    ua->send_msg("VolumeToCatalog\n");
808    ua->send_msg("Base\n");
809    return true;
810 }
811
812 static bool volstatuscmd(UAContext *ua, const char *cmd)
813 {
814    ua->send_msg("Append\n");
815    ua->send_msg("Full\n");
816    ua->send_msg("Used\n");
817    ua->send_msg("Recycle\n");
818    ua->send_msg("Purged\n");
819    ua->send_msg("Cleaning\n");
820    ua->send_msg("Error\n");
821    return true;
822 }
823
824 /*
825  * Return default values for a job
826  */
827 static bool defaultscmd(UAContext *ua, const char *cmd)
828 {
829    JOB *job;
830    CLIENT *client;
831    STORE *storage;
832    POOL *pool;
833    char ed1[50];
834
835    if (ua->argc != 2 || !ua->argv[1]) {
836       return true;
837    }
838
839    /* Job defaults */   
840    if (strcmp(ua->argk[1], "job") == 0) {
841       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
842          return true;
843       }
844       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
845       if (job) {
846          USTORE store;
847          ua->send_msg("job=%s", job->name());
848          ua->send_msg("pool=%s", job->pool->name());
849          ua->send_msg("messages=%s", job->messages->name());
850          ua->send_msg("client=%s", job->client->name());
851          get_job_storage(&store, job, NULL);
852          ua->send_msg("storage=%s", store.store->name());
853          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
854          ua->send_msg("level=%s", level_to_str(job->JobLevel));
855          ua->send_msg("type=%s", job_type_to_str(job->JobType));
856          ua->send_msg("fileset=%s", job->fileset->name());
857          ua->send_msg("enabled=%d", job->enabled);
858          ua->send_msg("catalog=%s", job->client->catalog->name());
859       }
860    } 
861    /* Client defaults */
862    else if (strcmp(ua->argk[1], "client") == 0) {
863       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
864          return true;   
865       }
866       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
867       if (client) {
868          ua->send_msg("client=%s", client->name());
869          ua->send_msg("address=%s", client->address);
870          ua->send_msg("fdport=%d", client->FDport);
871          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
872          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
873          ua->send_msg("autoprune=%d", client->AutoPrune);
874          ua->send_msg("catalog=%s", client->catalog->name());
875       }
876    }
877    /* Storage defaults */
878    else if (strcmp(ua->argk[1], "storage") == 0) {
879       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
880          return true;
881       }
882       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
883       DEVICE *device;
884       if (storage) {
885          ua->send_msg("storage=%s", storage->name());
886          ua->send_msg("address=%s", storage->address);
887          ua->send_msg("enabled=%d", storage->enabled);
888          ua->send_msg("media_type=%s", storage->media_type);
889          ua->send_msg("sdport=%d", storage->SDport);
890          device = (DEVICE *)storage->device->first();
891          ua->send_msg("device=%s", device->name());
892          if (storage->device->size() > 1) {
893             while ((device = (DEVICE *)storage->device->next())) {
894                ua->send_msg(",%s", device->name());
895             }
896          }
897       }
898    }
899    /* Pool defaults */
900    else if (strcmp(ua->argk[1], "pool") == 0) {
901       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
902          return true;
903       }
904       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
905       if (pool) {
906          ua->send_msg("pool=%s", pool->name());
907          ua->send_msg("pool_type=%s", pool->pool_type);
908          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
909          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
910          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
911          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
912          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
913          ua->send_msg("max_volumes=%d", pool->max_volumes);
914          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
915          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
916          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
917          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
918          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
919          ua->send_msg("auto_prune=%d", pool->AutoPrune);
920          ua->send_msg("recycle=%d", pool->Recycle);
921          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
922          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
923       }
924    }
925    return true;
926 }