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