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