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