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