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