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