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