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