]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Tweak: add comments to new action on purge code
[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 die_or_dump_cmd(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"),        die_or_dump_cmd,  NULL,       false},
89  { NT_(".dump"),       die_or_dump_cmd,  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    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  * Create segmentation fault or dump memory
423  */
424 static bool die_or_dump_cmd(UAContext *ua, const char *cmd)
425 {
426    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
427    STORE *store=NULL;
428    CLIENT *client=NULL;
429    bool dir=false;
430    bool do_deadlock=false;
431    const char *remote_cmd="sm_dump";
432    int i;
433    JCR *jcr = NULL;
434    int a;
435    if (!strncmp(ua->argk[0], ".die", 4)) {
436       if (find_arg(ua, "deadlock") > 0) {
437          do_deadlock=true;
438          remote_cmd = ".die deadlock";
439       } else {
440          remote_cmd = ".die";
441       }
442    }
443    /* General debug? */
444    for (i=1; i<ua->argc; i++) {
445       if (strcasecmp(ua->argk[i], "dir") == 0 ||
446           strcasecmp(ua->argk[i], "director") == 0) {
447          dir=true;
448       }
449       if (strcasecmp(ua->argk[i], "client") == 0 ||
450           strcasecmp(ua->argk[i], "fd") == 0) {
451          client = NULL;
452          if (ua->argv[i]) {
453             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
454          }
455          if (!client) {
456             client = select_client_resource(ua);
457          }
458       }
459    
460       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
461           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
462           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
463          store = NULL;
464          if (ua->argv[i]) {
465             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
466          }
467          if (!store) {
468             store = get_storage_resource(ua, false/*no default*/);
469          }
470       }
471    }
472
473    if (!dir && !store && !client) {
474       /*
475        * We didn't find an appropriate keyword above, so
476        * prompt the user.
477        */
478       start_prompt(ua, _("Available daemons are: \n"));
479       add_prompt(ua, _("Director"));
480       add_prompt(ua, _("Storage"));
481       add_prompt(ua, _("Client"));
482       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
483       case 0:                         /* Director */
484          dir=true;
485          break;
486       case 1:
487          store = get_storage_resource(ua, false/*no default*/);
488          break;
489       case 2:
490          client = select_client_resource(ua);
491          break;
492       default:
493          break;
494       }
495    }
496
497    if (store) {
498       do_storage_cmd(ua, store, remote_cmd);
499    }
500
501    if (client) {
502       do_client_cmd(ua, client, remote_cmd);
503    }
504
505    if (dir) {
506       if (!strncmp(remote_cmd, ".die", 4)) {
507          if (do_deadlock) {
508             ua->send_msg(_("The Director will generate a deadlock.\n"));
509             P(mutex); 
510             P(mutex);
511          }
512          ua->send_msg(_("The Director will segment fault.\n"));
513          a = jcr->JobId; /* ref NULL pointer */
514          jcr->JobId = 1000; /* another ref NULL pointer */
515
516       } else {                  /* .dump */
517          sm_dump(false, true);
518       }
519    }
520
521    return true;
522 }
523
524 #else
525
526 /*
527  * Dummy routine for non-development version
528  */
529 static bool die_or_dump_cmd(UAContext *ua, const char *cmd)
530 {
531    return true;
532 }
533
534 #endif
535
536 /* 
537  * Can use an argument to filter on JobType
538  * .jobs [type=B]
539  */
540 static bool jobscmd(UAContext *ua, const char *cmd)
541 {
542    JOB *job;
543    uint32_t type = 0;
544    int pos;
545    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
546       type = ua->argv[pos][0];
547    }
548    LockRes();
549    foreach_res(job, R_JOB) {
550       if (!type || type == job->JobType) {
551          if (acl_access_ok(ua, Job_ACL, job->name())) {
552             ua->send_msg("%s\n", job->name());
553          }
554       }
555    }
556    UnlockRes();
557    return true;
558 }
559
560 static bool filesetscmd(UAContext *ua, const char *cmd)
561 {
562    FILESET *fs;
563    LockRes();
564    foreach_res(fs, R_FILESET) {
565       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
566          ua->send_msg("%s\n", fs->name());
567       }
568    }
569    UnlockRes();
570    return true;
571 }
572
573 static bool clientscmd(UAContext *ua, const char *cmd)
574 {
575    CLIENT *client;       
576    LockRes();
577    foreach_res(client, R_CLIENT) {
578       if (acl_access_ok(ua, Client_ACL, client->name())) {
579          ua->send_msg("%s\n", client->name());
580       }
581    }
582    UnlockRes();
583    return true;
584 }
585
586 static bool msgscmd(UAContext *ua, const char *cmd)
587 {
588    MSGS *msgs = NULL;
589    LockRes();
590    foreach_res(msgs, R_MSGS) {
591       ua->send_msg("%s\n", msgs->name());
592    }
593    UnlockRes();
594    return true;
595 }
596
597 static bool poolscmd(UAContext *ua, const char *cmd)
598 {
599    POOL *pool;       
600    LockRes();
601    foreach_res(pool, R_POOL) {
602       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
603          ua->send_msg("%s\n", pool->name());
604       }
605    }
606    UnlockRes();
607    return true;
608 }
609
610 static bool storagecmd(UAContext *ua, const char *cmd)
611 {
612    STORE *store;
613    LockRes();
614    foreach_res(store, R_STORAGE) {
615       if (acl_access_ok(ua, Storage_ACL, store->name())) {
616          ua->send_msg("%s\n", store->name());
617       }
618    }
619    UnlockRes();
620    return true;
621 }
622
623 static bool aopcmd(UAContext *ua, const char *cmd)
624 {
625    ua->send_msg("None\n");
626    ua->send_msg("Truncate\n");
627    return true;
628 }
629
630 static bool typescmd(UAContext *ua, const char *cmd)
631 {
632    ua->send_msg("Backup\n");
633    ua->send_msg("Restore\n");
634    ua->send_msg("Admin\n");
635    ua->send_msg("Verify\n");
636    ua->send_msg("Migrate\n");
637    ua->send_msg("Copy\n");
638    return true;
639 }
640
641 /*
642  * If this command is called, it tells the director that we
643  *  are a program that wants a sort of API, and hence,
644  *  we will probably suppress certain output, include more
645  *  error codes, and most of all send back a good number
646  *  of new signals that indicate whether or not the command
647  *  succeeded.
648  */
649 static bool api_cmd(UAContext *ua, const char *cmd)
650 {
651    if (ua->argc == 2) {
652       ua->api = atoi(ua->argk[1]);
653    } else {
654       ua->api = 1;
655    }
656    return true;
657 }
658
659 static int client_backups_handler(void *ctx, int num_field, char **row)
660 {
661    UAContext *ua = (UAContext *)ctx;
662    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
663       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
664    return 0;
665 }
666
667 /*
668  * Return the backups for this client 
669  *
670  *  .backups client=xxx fileset=yyy
671  *
672  */
673 static bool backupscmd(UAContext *ua, const char *cmd)
674 {
675    if (!open_client_db(ua)) {
676       return true;
677    }
678    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
679        strcmp(ua->argk[2], "fileset") != 0) {
680       return true;
681    }
682    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
683        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
684       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
685       return true;
686    }
687    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
688    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
689       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
690       return true;
691    }
692    return true;
693 }
694
695 static int sql_handler(void *ctx, int num_field, char **row)
696 {
697    UAContext *ua = (UAContext *)ctx;
698    POOL_MEM rows(PM_MESSAGE);
699
700    /* Check for nonsense */
701    if (num_field == 0 || row == NULL || row[0] == NULL) {
702       return 0;                       /* nothing returned */
703    }
704    for (int i=0; num_field--; i++) {
705       if (i == 0) {
706          pm_strcpy(rows, NPRT(row[0]));
707       } else {
708          pm_strcat(rows, NPRT(row[i]));
709       }
710       pm_strcat(rows, "\t");
711    }
712    if (!rows.c_str() || !*rows.c_str()) {
713       ua->send_msg("\t");
714    } else {
715       ua->send_msg("%s", rows.c_str());
716    }
717    return 0;
718 }
719
720 static bool sql_cmd(UAContext *ua, const char *cmd)
721 {
722    int index;
723    if (!open_client_db(ua)) {
724       return true;
725    }
726    index = find_arg_with_value(ua, "query");
727    if (index < 0) {
728       ua->error_msg(_("query keyword not found.\n"));
729       return true;
730    }
731    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
732       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
733       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
734       return true;
735    }
736    return true;
737 }
738       
739 static int one_handler(void *ctx, int num_field, char **row)
740 {
741    UAContext *ua = (UAContext *)ctx;
742    ua->send_msg("%s\n", row[0]);
743    return 0;
744 }
745
746 static bool mediatypescmd(UAContext *ua, const char *cmd)
747 {
748    if (!open_client_db(ua)) {
749       return true;
750    }
751    if (!db_sql_query(ua->db, 
752                   "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
753                   one_handler, (void *)ua)) 
754    {
755       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
756    }
757    return true;
758 }
759
760 static bool mediacmd(UAContext *ua, const char *cmd)
761 {
762    if (!open_client_db(ua)) {
763       return true;
764    }
765    if (!db_sql_query(ua->db, 
766                   "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
767                   one_handler, (void *)ua)) 
768    {
769       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
770    }
771    return true;
772 }
773
774 static bool locationscmd(UAContext *ua, const char *cmd)
775 {
776    if (!open_client_db(ua)) {
777       return true;
778    }
779    if (!db_sql_query(ua->db, 
780                   "SELECT DISTINCT Location FROM Location ORDER BY Location",
781                   one_handler, (void *)ua)) 
782    {
783       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
784    }
785    return true;
786 }
787
788 static bool levelscmd(UAContext *ua, const char *cmd)
789 {
790    ua->send_msg("Incremental\n");
791    ua->send_msg("Full\n");
792    ua->send_msg("Differential\n");
793    ua->send_msg("VirtualFull\n");
794    ua->send_msg("Catalog\n");
795    ua->send_msg("InitCatalog\n");
796    ua->send_msg("VolumeToCatalog\n");
797    ua->send_msg("Base\n");
798    return true;
799 }
800
801 static bool volstatuscmd(UAContext *ua, const char *cmd)
802 {
803    ua->send_msg("Append\n");
804    ua->send_msg("Full\n");
805    ua->send_msg("Used\n");
806    ua->send_msg("Recycle\n");
807    ua->send_msg("Purged\n");
808    ua->send_msg("Cleaning\n");
809    ua->send_msg("Error\n");
810    return true;
811 }
812
813 /*
814  * Return default values for a job
815  */
816 static bool defaultscmd(UAContext *ua, const char *cmd)
817 {
818    JOB *job;
819    CLIENT *client;
820    STORE *storage;
821    POOL *pool;
822    char ed1[50];
823
824    if (ua->argc != 2 || !ua->argv[1]) {
825       return true;
826    }
827
828    /* Job defaults */   
829    if (strcmp(ua->argk[1], "job") == 0) {
830       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
831          return true;
832       }
833       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
834       if (job) {
835          USTORE store;
836          ua->send_msg("job=%s", job->name());
837          ua->send_msg("pool=%s", job->pool->name());
838          ua->send_msg("messages=%s", job->messages->name());
839          ua->send_msg("client=%s", job->client->name());
840          get_job_storage(&store, job, NULL);
841          ua->send_msg("storage=%s", store.store->name());
842          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
843          ua->send_msg("level=%s", level_to_str(job->JobLevel));
844          ua->send_msg("type=%s", job_type_to_str(job->JobType));
845          ua->send_msg("fileset=%s", job->fileset->name());
846          ua->send_msg("enabled=%d", job->enabled);
847          ua->send_msg("catalog=%s", job->client->catalog->name());
848       }
849    } 
850    /* Client defaults */
851    else if (strcmp(ua->argk[1], "client") == 0) {
852       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
853          return true;   
854       }
855       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
856       if (client) {
857          ua->send_msg("client=%s", client->name());
858          ua->send_msg("address=%s", client->address);
859          ua->send_msg("fdport=%d", client->FDport);
860          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
861          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
862          ua->send_msg("autoprune=%d", client->AutoPrune);
863          ua->send_msg("catalog=%s", client->catalog->name());
864       }
865    }
866    /* Storage defaults */
867    else if (strcmp(ua->argk[1], "storage") == 0) {
868       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
869          return true;
870       }
871       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
872       DEVICE *device;
873       if (storage) {
874          ua->send_msg("storage=%s", storage->name());
875          ua->send_msg("address=%s", storage->address);
876          ua->send_msg("enabled=%d", storage->enabled);
877          ua->send_msg("media_type=%s", storage->media_type);
878          ua->send_msg("sdport=%d", storage->SDport);
879          device = (DEVICE *)storage->device->first();
880          ua->send_msg("device=%s", device->name());
881          if (storage->device->size() > 1) {
882             while ((device = (DEVICE *)storage->device->next())) {
883                ua->send_msg(",%s", device->name());
884             }
885          }
886       }
887    }
888    /* Pool defaults */
889    else if (strcmp(ua->argk[1], "pool") == 0) {
890       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
891          return true;
892       }
893       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
894       if (pool) {
895          ua->send_msg("pool=%s", pool->name());
896          ua->send_msg("pool_type=%s", pool->pool_type);
897          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
898          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
899          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
900          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
901          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
902          ua->send_msg("max_volumes=%d", pool->max_volumes);
903          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
904          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
905          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
906          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
907          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
908          ua->send_msg("auto_prune=%d", pool->AutoPrune);
909          ua->send_msg("recycle=%d", pool->Recycle);
910          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
911          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
912       }
913    }
914    return true;
915 }