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