]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Small cleanup for bvfs
[bacula/bacula] / bacula / src / dird / ua_dotcmds.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2010 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 three of the GNU Affero 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 Affero 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 admin_cmds(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 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd);
77 static bool dot_bvfs_versions(UAContext *ua, const char *cmd);
78 static bool dot_bvfs_restore(UAContext *ua, const char *cmd);
79 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd);
80
81 static bool api_cmd(UAContext *ua, const char *cmd);
82 static bool sql_cmd(UAContext *ua, const char *cmd);
83 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
84 static bool dot_help_cmd(UAContext *ua, const char *cmd);
85 static int one_handler(void *ctx, int num_field, char **row);
86
87 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
88 static struct cmdstruct commands[] = { /* help */  /* can be used in runscript */
89  { NT_(".api"),        api_cmd,                  NULL,       false},
90  { NT_(".backups"),    backupscmd,               NULL,       false},
91  { NT_(".clients"),    clientscmd,               NULL,       true},
92  { NT_(".defaults"),   defaultscmd,              NULL,       false},
93  { NT_(".die"),        admin_cmds,               NULL,       false},
94  { NT_(".dump"),       admin_cmds,               NULL,       false},
95  { NT_(".exit"),       admin_cmds,               NULL,       false},
96  { NT_(".filesets"),   filesetscmd,              NULL,       false},
97  { NT_(".help"),       dot_help_cmd,             NULL,       false},
98  { NT_(".jobs"),       jobscmd,                  NULL,       true},
99  { NT_(".levels"),     levelscmd,                NULL,       false},
100  { NT_(".messages"),   getmsgscmd,               NULL,       false},
101  { NT_(".msgs"),       msgscmd,                  NULL,       false},
102  { NT_(".pools"),      poolscmd,                 NULL,       true},
103  { NT_(".quit"),       dot_quit_cmd,             NULL,       false},
104  { NT_(".sql"),        sql_cmd,                  NULL,       false},
105  { NT_(".status"),     dot_status_cmd,           NULL,       false},
106  { NT_(".storage"),    storagecmd,               NULL,       true},
107  { NT_(".volstatus"),  volstatuscmd,             NULL,       true},
108  { NT_(".media"),      mediacmd,                 NULL,       true},
109  { NT_(".mediatypes"), mediatypescmd,            NULL,       true},
110  { NT_(".locations"),  locationscmd,             NULL,       true},
111  { NT_(".actiononpurge"),aopcmd,                 NULL,       true},
112  { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs,         NULL,       true},
113  { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles,        NULL,       true},
114  { NT_(".bvfs_update"), dot_bvfs_update,         NULL,       true},
115  { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL,       true},
116  { NT_(".bvfs_versions"), dot_bvfs_versions,     NULL,       true},
117  { NT_(".bvfs_restore"), dot_bvfs_restore,       NULL,       true},
118  { NT_(".bvfs_cleanup"), dot_bvfs_cleanup,       NULL,       true},
119  { NT_(".types"),      typescmd,                 NULL,       false}
120              };
121 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
122
123 /*
124  * Execute a command from the UA
125  */
126 bool do_a_dot_command(UAContext *ua) 
127 {
128    int i;
129    int len;
130    bool ok = false;
131    bool found = false;
132    BSOCK *user = ua->UA_sock;
133
134    Dmsg1(1400, "Dot command: %s\n", user->msg);
135    if (ua->argc == 0) {
136       return false;
137    }
138
139    len = strlen(ua->argk[0]);
140    if (len == 1) {
141       if (ua->api) user->signal(BNET_CMD_BEGIN);
142       if (ua->api) user->signal(BNET_CMD_OK);
143       return true;                    /* no op */
144    }
145    for (i=0; i<comsize; i++) {     /* search for command */
146       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
147          /* Check if this command is authorized in RunScript */
148          if (ua->runscript && !commands[i].use_in_rs) {
149             ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
150             break;
151          }
152          bool gui = ua->gui;
153          /* Check if command permitted, but "quit" is always OK */
154          if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
155              !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
156             break;
157          }
158          Dmsg1(100, "Cmd: %s\n", ua->cmd);
159          ua->gui = true;
160          if (ua->api) user->signal(BNET_CMD_BEGIN);
161          ok = (*commands[i].func)(ua, ua->cmd);   /* go execute command */
162          if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
163          ua->gui = gui;
164          found = true;
165          break;
166       }
167    }
168    if (!found) {
169       pm_strcat(user->msg, _(": is an invalid command.\n"));
170       ua->error_msg("%s", user->msg);
171       ok = false;
172    }
173    return ok;
174 }
175
176 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
177 {
178    if (!open_new_client_db(ua)) {
179       return 1;
180    }
181
182    int pos = find_arg_with_value(ua, "jobid");
183    if (pos != -1 && is_a_number_list(ua->argv[pos])) {
184       bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
185    } else {
186       /* update cache for all jobids */
187       bvfs_update_cache(ua->jcr, ua->db);
188    }
189    
190    close_db(ua);
191    return true;
192 }
193
194 static int bvfs_result_handler(void *ctx, int fields, char **row)
195 {
196    UAContext *ua = (UAContext *)ctx;
197    struct stat statp;
198    int32_t LinkFI;
199    char *fileid=row[BVFS_FileId];
200    char *lstat=row[BVFS_LStat];
201    char *jobid=row[BVFS_JobId];
202    
203    char empty[] = "A A A A A A A A A A A A A A";
204    char zero[] = "0";
205
206    /* We need to deal with non existant path */
207    if (!fileid || !is_a_number(fileid)) {
208       lstat = empty;
209       jobid = zero;
210       fileid = zero;
211    }
212
213    memset(&statp, 0, sizeof(struct stat));
214    decode_stat(lstat, &statp, &LinkFI);
215
216    Dmsg1(100, "type=%s\n", row[0]);
217    if (bvfs_is_dir(row)) {
218       char *path = bvfs_basename_dir(row[BVFS_Name]);
219       ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
220                    jobid, lstat, path);
221
222    } else if (bvfs_is_version(row)) {
223       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
224                    row[BVFS_FilenameId], fileid, jobid,
225                    lstat, row[BVFS_Md5], row[BVFS_VolName], 
226                    row[BVFS_VolInchanger]);
227
228    } else if (bvfs_is_file(row)) {
229       ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
230                    row[BVFS_FilenameId], fileid, jobid,
231                    lstat, row[BVFS_Name]);
232    }
233
234    return 0;
235 }
236
237 static bool bvfs_parse_arg_version(UAContext *ua,
238                                    char **client,
239                                    DBId_t *fnid, 
240                                    bool *versions, 
241                                    bool *copies)
242 {
243    *fnid=0;
244    *client=NULL;
245    *versions=false;
246    *copies=false;
247
248    for (int i=1; i<ua->argc; i++) {
249       if (fnid && strcasecmp(ua->argk[i], NT_("fnid")) == 0) {
250          if (is_a_number(ua->argv[i])) {
251             *fnid = str_to_int64(ua->argv[i]);
252          }
253       }
254
255       if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
256          *client = ua->argv[i];
257       }
258
259       if (copies && strcasecmp(ua->argk[i], NT_("copies")) == 0) {
260          *copies = true;
261       }
262
263       if (versions && strcasecmp(ua->argk[i], NT_("versions")) == 0) {
264          *versions = true;
265       }
266    }
267    return (*client && *fnid > 0);
268 }
269
270 static bool bvfs_parse_arg(UAContext *ua, 
271                            DBId_t *pathid, char **path, char **jobid,
272                            int *limit, int *offset)
273 {
274    *pathid=0;
275    *limit=2000;
276    *offset=0;
277    *path=NULL;
278    *jobid=NULL;
279
280    for (int i=1; i<ua->argc; i++) {
281       if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
282          if (is_a_number(ua->argv[i])) {
283             *pathid = str_to_int64(ua->argv[i]);
284          }
285       }
286
287       if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
288          *path = ua->argv[i];
289       }
290       
291       if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
292          if (is_a_number_list(ua->argv[i])) {
293             *jobid = ua->argv[i];
294          }
295       }
296
297       if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
298          if (is_a_number(ua->argv[i])) {
299             *limit = str_to_int64(ua->argv[i]);
300          }
301       }
302
303       if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
304          if (is_a_number(ua->argv[i])) {
305             *offset = str_to_int64(ua->argv[i]);
306          }
307       }
308    }
309
310    if (!((*pathid || *path) && *jobid)) {
311       return false;
312    }
313
314    if (!open_client_db(ua)) {
315       return false;
316    }
317
318    return true;
319 }
320
321 /* .bvfs_cleanup path=b2XXXXX
322  */
323 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd)
324 {
325    int i;
326    if ((i = find_arg_with_value(ua, "path")) >= 0) {
327       open_client_db(ua);
328       Bvfs fs(ua->jcr, ua->db);
329       fs.drop_restore_list(ua->argv[i]);
330    }
331    return true;
332 }
333
334 /* .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
335  */
336 static bool dot_bvfs_restore(UAContext *ua, const char *cmd)
337 {
338    DBId_t pathid=0;
339    int limit=2000, offset=0, i;
340    char *path=NULL, *jobid=NULL;
341    char *empty = (char *)"";
342    char *fileid, *dirid, *hardlink, *id;
343    id = fileid = dirid = hardlink = empty;
344
345    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
346                        &limit, &offset))
347    {
348       ua->error_msg("Can't find jobid, pathid or path argument\n");
349       return true;              /* not enough param */
350    }
351
352    Bvfs fs(ua->jcr, ua->db);
353    fs.set_jobids(jobid);
354
355    if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
356       fileid = ua->argv[i];
357    }
358    if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
359       dirid = ua->argv[i];
360    }
361    if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
362       hardlink = ua->argv[i];
363    }
364
365    if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
366       ua->send_msg("OK\n");
367    } else {
368       ua->error_msg("Can't create restore list\n");
369    }
370    return true;
371 }
372
373 /* 
374  * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
375  * .bvfs_lsfiles jobid=1,2,3,4 path=/
376  */
377 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
378 {
379    DBId_t pathid=0;
380    int limit=2000, offset=0;
381    char *path=NULL, *jobid=NULL;
382    char *pattern=NULL;
383    int i;
384
385    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
386                        &limit, &offset))
387    {
388       ua->error_msg("Can't find jobid, pathid or path argument\n");
389       return true;              /* not enough param */
390    }
391    if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
392       pattern = ua->argv[i];
393    }
394
395    Bvfs fs(ua->jcr, ua->db);
396    fs.set_jobids(jobid);   
397    fs.set_handler(bvfs_result_handler, ua);
398    fs.set_limit(limit);
399    if (pattern) {
400       fs.set_pattern(pattern);
401    }
402    if (pathid) {
403       fs.ch_dir(pathid);
404    } else {
405       fs.ch_dir(path);
406    }
407    
408    fs.set_offset(offset);
409
410    fs.ls_files();
411
412    return true;
413 }
414
415 /* 
416  * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
417  * .bvfs_lsdirs jobid=1,2,3,4 path=/
418  * .bvfs_lsdirs jobid=1,2,3,4 path=
419  */
420 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
421 {
422    DBId_t pathid=0;
423    int limit=2000, offset=0;
424    char *path=NULL, *jobid=NULL;
425
426    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
427                        &limit, &offset))
428    {
429       ua->error_msg("Can't find jobid, pathid or path argument\n");
430       return true;              /* not enough param */
431    }
432
433    Bvfs fs(ua->jcr, ua->db);
434    fs.set_jobids(jobid);   
435    fs.set_limit(limit);
436    fs.set_handler(bvfs_result_handler, ua);
437
438    if (pathid) {
439       fs.ch_dir(pathid);
440    } else {
441       fs.ch_dir(path);
442    }
443
444    fs.set_offset(offset);
445
446    fs.ls_special_dirs();
447    fs.ls_dirs();
448
449    return true;
450 }
451
452 /* 
453  * .bvfs_versions jobid=x fnid=10 pathid=10 copies versions
454  * (jobid isn't used)
455  */
456 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
457 {
458    DBId_t pathid=0, fnid=0;
459    int limit=2000, offset=0;
460    char *path=NULL, *jobid=NULL, *client=NULL;
461    bool copies=false, versions=false;
462    if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
463                        &limit, &offset))
464    {
465       ua->error_msg("Can't find jobid, pathid or path argument\n");
466       return true;              /* not enough param */
467    }
468
469    if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
470    {
471       ua->error_msg("Can't find client or fnid argument\n");
472       return true;              /* not enough param */
473    }
474
475    Bvfs fs(ua->jcr, ua->db);
476    fs.set_limit(limit);
477    fs.set_see_all_versions(versions);
478    fs.set_see_copies(copies);
479    fs.set_handler(bvfs_result_handler, ua);
480    fs.set_offset(offset);
481    fs.get_all_file_versions(pathid, fnid, client);
482
483    return true;
484 }
485
486 /* .bvfs_get_jobids jobid=1
487  *  -> returns needed jobids to restore
488  * .bvfs_get_jobids jobid=1 all
489  *  -> returns needed jobids to restore with all filesets a JobId=1 time
490  */
491 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
492 {
493    JOB_DBR jr;
494    db_list_ctx jobids, tempids;
495    int pos;
496    char ed1[50];
497    POOL_MEM query;
498    dbid_list ids;               /* Store all FileSetIds for this client */
499
500    if (!open_client_db(ua)) {
501       return true;
502    }
503
504    memset(&jr, 0, sizeof(JOB_DBR));
505    if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
506       jr.JobId = str_to_int64(ua->argv[pos]);
507    }
508
509    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
510       ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
511                     ua->cmd, db_strerror(ua->db));
512       return true;
513    }
514
515    /* If we have the "all" option, we do a search on all defined fileset
516     * for this client
517     */
518    if (find_arg(ua, "all") > 0) {
519       edit_int64(jr.ClientId, ed1);
520       Mmsg(query, uar_sel_filesetid, ed1);
521       db_get_query_dbids(ua->jcr, ua->db, query, ids);
522    } else {
523       ids.num_ids = 1;
524       ids.DBId[0] = jr.FileSetId;
525    }
526
527    jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
528
529    /* Foreach different FileSet, we build a restore jobid list */
530    for (int i=0; i < ids.num_ids; i++) {
531       jr.FileSetId = ids.DBId[i];
532       if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids)) {
533          return true;
534       }
535       jobids.cat(tempids);
536    }
537
538    ua->send_msg("%s\n", jobids.list);
539    return true;
540 }
541
542 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
543 {
544    quit_cmd(ua, cmd);
545    return true;
546 }
547
548 static bool dot_help_cmd(UAContext *ua, const char *cmd)
549 {
550    qhelp_cmd(ua, cmd);
551    return true;
552 }
553
554 static bool getmsgscmd(UAContext *ua, const char *cmd)
555 {
556    if (console_msg_pending) {
557       do_messages(ua, cmd);
558    }
559    return 1;
560 }
561
562 #ifdef DEVELOPER
563 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
564 {
565    BSOCK *sd;
566    JCR *jcr = ua->jcr;
567    USTORE lstore;
568    
569    lstore.store = store;
570    pm_strcpy(lstore.store_source, _("unknown source"));
571    set_wstorage(jcr, &lstore);
572    /* Try connecting for up to 15 seconds */
573    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
574       store->name(), store->address, store->SDport);
575    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
576       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
577       return;
578    }
579    Dmsg0(120, _("Connected to storage daemon\n"));
580    sd = jcr->store_bsock;
581    sd->fsend("%s", cmd);
582    if (sd->recv() >= 0) {
583       ua->send_msg("%s", sd->msg);
584    }
585    sd->signal(BNET_TERMINATE);
586    sd->close();
587    jcr->store_bsock = NULL;
588    return;
589 }
590
591 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
592 {
593    BSOCK *fd;
594
595    /* Connect to File daemon */
596
597    ua->jcr->client = client;
598    /* Try to connect for 15 seconds */
599    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
600       client->name(), client->address, client->FDport);
601    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
602       ua->error_msg(_("Failed to connect to Client.\n"));
603       return;
604    }
605    Dmsg0(120, "Connected to file daemon\n");
606    fd = ua->jcr->file_bsock;
607    fd->fsend("%s", cmd);
608    if (fd->recv() >= 0) {
609       ua->send_msg("%s", fd->msg);
610    }
611    fd->signal(BNET_TERMINATE);
612    fd->close();
613    ua->jcr->file_bsock = NULL;
614    return;
615 }
616
617 /*
618  *   .die (seg fault)
619  *   .dump (sm_dump)
620  *   .exit (no arg => .quit)
621  */
622 static bool admin_cmds(UAContext *ua, const char *cmd)
623 {
624    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
625    STORE *store=NULL;
626    CLIENT *client=NULL;
627    bool dir=false;
628    bool do_deadlock=false;
629    const char *remote_cmd;
630    int i;
631    JCR *jcr = NULL;
632    int a;
633    if (strncmp(ua->argk[0], ".die", 4) == 0) {
634       if (find_arg(ua, "deadlock") > 0) {
635          do_deadlock = true;
636          remote_cmd = ".die deadlock";
637       } else {
638          remote_cmd = ".die";
639       }
640    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
641       remote_cmd = "sm_dump";
642    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
643       remote_cmd = "exit";
644    } else {
645       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
646       return true;
647    }
648    /* General debug? */
649    for (i=1; i<ua->argc; i++) {
650       if (strcasecmp(ua->argk[i], "dir") == 0 ||
651           strcasecmp(ua->argk[i], "director") == 0) {
652          dir = true;
653       }
654       if (strcasecmp(ua->argk[i], "client") == 0 ||
655           strcasecmp(ua->argk[i], "fd") == 0) {
656          client = NULL;
657          if (ua->argv[i]) {
658             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
659          }
660          if (!client) {
661             client = select_client_resource(ua);
662          }
663       }
664    
665       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
666           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
667           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
668          store = NULL;
669          if (ua->argv[i]) {
670             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
671          }
672          if (!store) {
673             store = get_storage_resource(ua, false/*no default*/);
674          }
675       }
676    }
677
678    if (!dir && !store && !client) {
679       /*
680        * We didn't find an appropriate keyword above, so
681        * prompt the user.
682        */
683       start_prompt(ua, _("Available daemons are: \n"));
684       add_prompt(ua, _("Director"));
685       add_prompt(ua, _("Storage"));
686       add_prompt(ua, _("Client"));
687       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
688       case 0:                         /* Director */
689          dir=true;
690          break;
691       case 1:
692          store = get_storage_resource(ua, false/*no default*/);
693          break;
694       case 2:
695          client = select_client_resource(ua);
696          break;
697       default:
698          break;
699       }
700    }
701
702    if (store) {
703       do_storage_cmd(ua, store, remote_cmd);
704    }
705
706    if (client) {
707       do_client_cmd(ua, client, remote_cmd);
708    }
709
710    if (dir) {
711       if (strncmp(remote_cmd, ".die", 4) == 0) {
712          if (do_deadlock) {
713             ua->send_msg(_("The Director will generate a deadlock.\n"));
714             P(mutex); 
715             P(mutex);
716          }
717          ua->send_msg(_("The Director will segment fault.\n"));
718          a = jcr->JobId; /* ref NULL pointer */
719          jcr->JobId = 1000; /* another ref NULL pointer */
720
721       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
722          sm_dump(false, true);
723       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
724          dot_quit_cmd(ua, cmd);
725       }
726    }
727
728    return true;
729 }
730
731 #else
732
733 /*
734  * Dummy routine for non-development version
735  */
736 static bool admin_cmds(UAContext *ua, const char *cmd)
737 {
738    ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
739    return true;
740 }
741
742 #endif
743
744 /* 
745  * Can use an argument to filter on JobType
746  * .jobs [type=B]
747  */
748 static bool jobscmd(UAContext *ua, const char *cmd)
749 {
750    JOB *job;
751    uint32_t type = 0;
752    int pos;
753    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
754       type = ua->argv[pos][0];
755    }
756    LockRes();
757    foreach_res(job, R_JOB) {
758       if (!type || type == job->JobType) {
759          if (acl_access_ok(ua, Job_ACL, job->name())) {
760             ua->send_msg("%s\n", job->name());
761          }
762       }
763    }
764    UnlockRes();
765    return true;
766 }
767
768 static bool filesetscmd(UAContext *ua, const char *cmd)
769 {
770    FILESET *fs;
771    LockRes();
772    foreach_res(fs, R_FILESET) {
773       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
774          ua->send_msg("%s\n", fs->name());
775       }
776    }
777    UnlockRes();
778    return true;
779 }
780
781 static bool clientscmd(UAContext *ua, const char *cmd)
782 {
783    CLIENT *client;       
784    LockRes();
785    foreach_res(client, R_CLIENT) {
786       if (acl_access_ok(ua, Client_ACL, client->name())) {
787          ua->send_msg("%s\n", client->name());
788       }
789    }
790    UnlockRes();
791    return true;
792 }
793
794 static bool msgscmd(UAContext *ua, const char *cmd)
795 {
796    MSGS *msgs = NULL;
797    LockRes();
798    foreach_res(msgs, R_MSGS) {
799       ua->send_msg("%s\n", msgs->name());
800    }
801    UnlockRes();
802    return true;
803 }
804
805 static bool poolscmd(UAContext *ua, const char *cmd)
806 {
807    POOL *pool;       
808    LockRes();
809    foreach_res(pool, R_POOL) {
810       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
811          ua->send_msg("%s\n", pool->name());
812       }
813    }
814    UnlockRes();
815    return true;
816 }
817
818 static bool storagecmd(UAContext *ua, const char *cmd)
819 {
820    STORE *store;
821    LockRes();
822    foreach_res(store, R_STORAGE) {
823       if (acl_access_ok(ua, Storage_ACL, store->name())) {
824          ua->send_msg("%s\n", store->name());
825       }
826    }
827    UnlockRes();
828    return true;
829 }
830
831 static bool aopcmd(UAContext *ua, const char *cmd)
832 {
833    ua->send_msg("None\n");
834    ua->send_msg("Truncate\n");
835    return true;
836 }
837
838 static bool typescmd(UAContext *ua, const char *cmd)
839 {
840    ua->send_msg("Backup\n");
841    ua->send_msg("Restore\n");
842    ua->send_msg("Admin\n");
843    ua->send_msg("Verify\n");
844    ua->send_msg("Migrate\n");
845    ua->send_msg("Copy\n");
846    return true;
847 }
848
849 /*
850  * If this command is called, it tells the director that we
851  *  are a program that wants a sort of API, and hence,
852  *  we will probably suppress certain output, include more
853  *  error codes, and most of all send back a good number
854  *  of new signals that indicate whether or not the command
855  *  succeeded.
856  */
857 static bool api_cmd(UAContext *ua, const char *cmd)
858 {
859    if (ua->argc == 2) {
860       ua->api = atoi(ua->argk[1]);
861    } else {
862       ua->api = 1;
863    }
864    return true;
865 }
866
867 static int client_backups_handler(void *ctx, int num_field, char **row)
868 {
869    UAContext *ua = (UAContext *)ctx;
870    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
871       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
872    return 0;
873 }
874
875 /*
876  * Return the backups for this client 
877  *
878  *  .backups client=xxx fileset=yyy
879  *
880  */
881 static bool backupscmd(UAContext *ua, const char *cmd)
882 {
883    if (!open_client_db(ua)) {
884       return true;
885    }
886    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
887        strcmp(ua->argk[2], "fileset") != 0) {
888       return true;
889    }
890    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
891        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
892       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
893       return true;
894    }
895    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
896    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
897       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
898       return true;
899    }
900    return true;
901 }
902
903 static int sql_handler(void *ctx, int num_field, char **row)
904 {
905    UAContext *ua = (UAContext *)ctx;
906    POOL_MEM rows(PM_MESSAGE);
907
908    /* Check for nonsense */
909    if (num_field == 0 || row == NULL || row[0] == NULL) {
910       return 0;                       /* nothing returned */
911    }
912    for (int i=0; num_field--; i++) {
913       if (i == 0) {
914          pm_strcpy(rows, NPRT(row[0]));
915       } else {
916          pm_strcat(rows, NPRT(row[i]));
917       }
918       pm_strcat(rows, "\t");
919    }
920    if (!rows.c_str() || !*rows.c_str()) {
921       ua->send_msg("\t");
922    } else {
923       ua->send_msg("%s", rows.c_str());
924    }
925    return 0;
926 }
927
928 static bool sql_cmd(UAContext *ua, const char *cmd)
929 {
930    int index;
931    if (!open_client_db(ua)) {
932       return true;
933    }
934    index = find_arg_with_value(ua, "query");
935    if (index < 0) {
936       ua->error_msg(_("query keyword not found.\n"));
937       return true;
938    }
939    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
940       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
941       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
942       return true;
943    }
944    return true;
945 }
946       
947 static int one_handler(void *ctx, int num_field, char **row)
948 {
949    UAContext *ua = (UAContext *)ctx;
950    ua->send_msg("%s\n", row[0]);
951    return 0;
952 }
953
954 static bool mediatypescmd(UAContext *ua, const char *cmd)
955 {
956    if (!open_client_db(ua)) {
957       return true;
958    }
959    if (!db_sql_query(ua->db, 
960                   "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
961                   one_handler, (void *)ua)) 
962    {
963       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
964    }
965    return true;
966 }
967
968 static bool mediacmd(UAContext *ua, const char *cmd)
969 {
970    if (!open_client_db(ua)) {
971       return true;
972    }
973    if (!db_sql_query(ua->db, 
974                   "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
975                   one_handler, (void *)ua)) 
976    {
977       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
978    }
979    return true;
980 }
981
982 static bool locationscmd(UAContext *ua, const char *cmd)
983 {
984    if (!open_client_db(ua)) {
985       return true;
986    }
987    if (!db_sql_query(ua->db, 
988                   "SELECT DISTINCT Location FROM Location ORDER BY Location",
989                   one_handler, (void *)ua)) 
990    {
991       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
992    }
993    return true;
994 }
995
996 static bool levelscmd(UAContext *ua, const char *cmd)
997 {
998    ua->send_msg("Incremental\n");
999    ua->send_msg("Full\n");
1000    ua->send_msg("Differential\n");
1001    ua->send_msg("VirtualFull\n");
1002    ua->send_msg("Catalog\n");
1003    ua->send_msg("InitCatalog\n");
1004    ua->send_msg("VolumeToCatalog\n");
1005    ua->send_msg("Base\n");
1006    return true;
1007 }
1008
1009 static bool volstatuscmd(UAContext *ua, const char *cmd)
1010 {
1011    ua->send_msg("Append\n");
1012    ua->send_msg("Full\n");
1013    ua->send_msg("Used\n");
1014    ua->send_msg("Recycle\n");
1015    ua->send_msg("Purged\n");
1016    ua->send_msg("Cleaning\n");
1017    ua->send_msg("Error\n");
1018    return true;
1019 }
1020
1021 /*
1022  * Return default values for a job
1023  */
1024 static bool defaultscmd(UAContext *ua, const char *cmd)
1025 {
1026    JOB *job;
1027    CLIENT *client;
1028    STORE *storage;
1029    POOL *pool;
1030    char ed1[50];
1031
1032    if (ua->argc != 2 || !ua->argv[1]) {
1033       return true;
1034    }
1035
1036    /* Job defaults */   
1037    if (strcmp(ua->argk[1], "job") == 0) {
1038       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
1039          return true;
1040       }
1041       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
1042       if (job) {
1043          USTORE store;
1044          ua->send_msg("job=%s", job->name());
1045          ua->send_msg("pool=%s", job->pool->name());
1046          ua->send_msg("messages=%s", job->messages->name());
1047          ua->send_msg("client=%s", job->client->name());
1048          get_job_storage(&store, job, NULL);
1049          ua->send_msg("storage=%s", store.store->name());
1050          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
1051          ua->send_msg("level=%s", level_to_str(job->JobLevel));
1052          ua->send_msg("type=%s", job_type_to_str(job->JobType));
1053          ua->send_msg("fileset=%s", job->fileset->name());
1054          ua->send_msg("enabled=%d", job->enabled);
1055          ua->send_msg("catalog=%s", job->client->catalog->name());
1056       }
1057    } 
1058    /* Client defaults */
1059    else if (strcmp(ua->argk[1], "client") == 0) {
1060       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
1061          return true;   
1062       }
1063       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
1064       if (client) {
1065          ua->send_msg("client=%s", client->name());
1066          ua->send_msg("address=%s", client->address);
1067          ua->send_msg("fdport=%d", client->FDport);
1068          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
1069          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
1070          ua->send_msg("autoprune=%d", client->AutoPrune);
1071          ua->send_msg("catalog=%s", client->catalog->name());
1072       }
1073    }
1074    /* Storage defaults */
1075    else if (strcmp(ua->argk[1], "storage") == 0) {
1076       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
1077          return true;
1078       }
1079       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
1080       DEVICE *device;
1081       if (storage) {
1082          ua->send_msg("storage=%s", storage->name());
1083          ua->send_msg("address=%s", storage->address);
1084          ua->send_msg("enabled=%d", storage->enabled);
1085          ua->send_msg("media_type=%s", storage->media_type);
1086          ua->send_msg("sdport=%d", storage->SDport);
1087          device = (DEVICE *)storage->device->first();
1088          ua->send_msg("device=%s", device->name());
1089          if (storage->device->size() > 1) {
1090             while ((device = (DEVICE *)storage->device->next())) {
1091                ua->send_msg(",%s", device->name());
1092             }
1093          }
1094       }
1095    }
1096    /* Pool defaults */
1097    else if (strcmp(ua->argk[1], "pool") == 0) {
1098       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
1099          return true;
1100       }
1101       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
1102       if (pool) {
1103          ua->send_msg("pool=%s", pool->name());
1104          ua->send_msg("pool_type=%s", pool->pool_type);
1105          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
1106          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
1107          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
1108          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
1109          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
1110          ua->send_msg("max_volumes=%d", pool->max_volumes);
1111          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
1112          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
1113          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
1114          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
1115          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
1116          ua->send_msg("auto_prune=%d", pool->AutoPrune);
1117          ua->send_msg("recycle=%d", pool->Recycle);
1118          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
1119          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
1120       }
1121    }
1122    return true;
1123 }