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