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