]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_dotcmds.c
Add ujobid to .bvfs_get_jobids
[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  * .bvfs_get_jobids ujobid=JobName
527  *  -> returns needed jobids to restore
528  */
529 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
530 {
531    JOB_DBR jr;
532    db_list_ctx jobids, tempids;
533    int pos;
534    char ed1[50];
535    POOL_MEM query;
536    dbid_list ids;               /* Store all FileSetIds for this client */
537
538    if (!open_client_db(ua)) {
539       return true;
540    }
541
542    memset(&jr, 0, sizeof(JOB_DBR));
543
544    if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
545       bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
546    }
547
548    if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
549       jr.JobId = str_to_int64(ua->argv[pos]);
550    }
551
552    if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
553       ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
554                     ua->cmd, db_strerror(ua->db));
555       return true;
556    }
557
558    /* When in level base, we don't rely on any Full/Incr/Diff */
559    if (jr.JobLevel == L_BASE) {
560       ua->send_msg("%s\n", edit_int64(jr.JobId, ed1));
561       return true;
562    }
563
564    /* If we have the "all" option, we do a search on all defined fileset
565     * for this client
566     */
567    if (find_arg(ua, "all") > 0) {
568       edit_int64(jr.ClientId, ed1);
569       Mmsg(query, uar_sel_filesetid, ed1);
570       db_get_query_dbids(ua->jcr, ua->db, query, ids);
571    } else {
572       ids.num_ids = 1;
573       ids.DBId[0] = jr.FileSetId;
574    }
575
576    jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
577
578    /* Foreach different FileSet, we build a restore jobid list */
579    for (int i=0; i < ids.num_ids; i++) {
580       jr.FileSetId = ids.DBId[i];
581       if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids)) {
582          return true;
583       }
584       jobids.add(tempids);
585    }
586
587    ua->send_msg("%s\n", jobids.list);
588    return true;
589 }
590
591 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
592 {
593    quit_cmd(ua, cmd);
594    return true;
595 }
596
597 static bool dot_help_cmd(UAContext *ua, const char *cmd)
598 {
599    qhelp_cmd(ua, cmd);
600    return true;
601 }
602
603 static bool getmsgscmd(UAContext *ua, const char *cmd)
604 {
605    if (console_msg_pending) {
606       do_messages(ua, cmd);
607    }
608    return 1;
609 }
610
611 #ifdef DEVELOPER
612 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
613 {
614    BSOCK *sd;
615    JCR *jcr = ua->jcr;
616    USTORE lstore;
617    
618    lstore.store = store;
619    pm_strcpy(lstore.store_source, _("unknown source"));
620    set_wstorage(jcr, &lstore);
621    /* Try connecting for up to 15 seconds */
622    ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
623       store->name(), store->address, store->SDport);
624    if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
625       ua->error_msg(_("Failed to connect to Storage daemon.\n"));
626       return;
627    }
628    Dmsg0(120, _("Connected to storage daemon\n"));
629    sd = jcr->store_bsock;
630    sd->fsend("%s", cmd);
631    if (sd->recv() >= 0) {
632       ua->send_msg("%s", sd->msg);
633    }
634    sd->signal(BNET_TERMINATE);
635    sd->close();
636    jcr->store_bsock = NULL;
637    return;
638 }
639
640 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
641 {
642    BSOCK *fd;
643
644    /* Connect to File daemon */
645
646    ua->jcr->client = client;
647    /* Try to connect for 15 seconds */
648    ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
649       client->name(), client->address, client->FDport);
650    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
651       ua->error_msg(_("Failed to connect to Client.\n"));
652       return;
653    }
654    Dmsg0(120, "Connected to file daemon\n");
655    fd = ua->jcr->file_bsock;
656    fd->fsend("%s", cmd);
657    if (fd->recv() >= 0) {
658       ua->send_msg("%s", fd->msg);
659    }
660    fd->signal(BNET_TERMINATE);
661    fd->close();
662    ua->jcr->file_bsock = NULL;
663    return;
664 }
665
666 /*
667  *   .die (seg fault)
668  *   .dump (sm_dump)
669  *   .exit (no arg => .quit)
670  */
671 static bool admin_cmds(UAContext *ua, const char *cmd)
672 {
673    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
674    STORE *store=NULL;
675    CLIENT *client=NULL;
676    bool dir=false;
677    bool do_deadlock=false;
678    const char *remote_cmd;
679    int i;
680    JCR *jcr = NULL;
681    int a;
682    if (strncmp(ua->argk[0], ".die", 4) == 0) {
683       if (find_arg(ua, "deadlock") > 0) {
684          do_deadlock = true;
685          remote_cmd = ".die deadlock";
686       } else {
687          remote_cmd = ".die";
688       }
689    } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
690       remote_cmd = "sm_dump";
691    } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
692       remote_cmd = "exit";
693    } else {
694       ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
695       return true;
696    }
697    /* General debug? */
698    for (i=1; i<ua->argc; i++) {
699       if (strcasecmp(ua->argk[i], "dir") == 0 ||
700           strcasecmp(ua->argk[i], "director") == 0) {
701          dir = true;
702       }
703       if (strcasecmp(ua->argk[i], "client") == 0 ||
704           strcasecmp(ua->argk[i], "fd") == 0) {
705          client = NULL;
706          if (ua->argv[i]) {
707             client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
708          }
709          if (!client) {
710             client = select_client_resource(ua);
711          }
712       }
713    
714       if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
715           strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
716           strcasecmp(ua->argk[i], NT_("sd")) == 0) {
717          store = NULL;
718          if (ua->argv[i]) {
719             store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
720          }
721          if (!store) {
722             store = get_storage_resource(ua, false/*no default*/);
723          }
724       }
725    }
726
727    if (!dir && !store && !client) {
728       /*
729        * We didn't find an appropriate keyword above, so
730        * prompt the user.
731        */
732       start_prompt(ua, _("Available daemons are: \n"));
733       add_prompt(ua, _("Director"));
734       add_prompt(ua, _("Storage"));
735       add_prompt(ua, _("Client"));
736       switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
737       case 0:                         /* Director */
738          dir=true;
739          break;
740       case 1:
741          store = get_storage_resource(ua, false/*no default*/);
742          break;
743       case 2:
744          client = select_client_resource(ua);
745          break;
746       default:
747          break;
748       }
749    }
750
751    if (store) {
752       do_storage_cmd(ua, store, remote_cmd);
753    }
754
755    if (client) {
756       do_client_cmd(ua, client, remote_cmd);
757    }
758
759    if (dir) {
760       if (strncmp(remote_cmd, ".die", 4) == 0) {
761          if (do_deadlock) {
762             ua->send_msg(_("The Director will generate a deadlock.\n"));
763             P(mutex); 
764             P(mutex);
765          }
766          ua->send_msg(_("The Director will segment fault.\n"));
767          a = jcr->JobId; /* ref NULL pointer */
768          jcr->JobId = 1000; /* another ref NULL pointer */
769          jcr->JobId = a;
770
771       } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
772          sm_dump(false, true);
773       } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
774          dot_quit_cmd(ua, cmd);
775       }
776    }
777
778    return true;
779 }
780
781 #else
782
783 /*
784  * Dummy routine for non-development version
785  */
786 static bool admin_cmds(UAContext *ua, const char *cmd)
787 {
788    ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
789    return true;
790 }
791
792 #endif
793
794 /* 
795  * Can use an argument to filter on JobType
796  * .jobs [type=B]
797  */
798 static bool jobscmd(UAContext *ua, const char *cmd)
799 {
800    JOB *job;
801    uint32_t type = 0;
802    int pos;
803    if ((pos = find_arg_with_value(ua, "type")) >= 0) {
804       type = ua->argv[pos][0];
805    }
806    LockRes();
807    foreach_res(job, R_JOB) {
808       if (!type || type == job->JobType) {
809          if (acl_access_ok(ua, Job_ACL, job->name())) {
810             ua->send_msg("%s\n", job->name());
811          }
812       }
813    }
814    UnlockRes();
815    return true;
816 }
817
818 static bool filesetscmd(UAContext *ua, const char *cmd)
819 {
820    FILESET *fs;
821    LockRes();
822    foreach_res(fs, R_FILESET) {
823       if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
824          ua->send_msg("%s\n", fs->name());
825       }
826    }
827    UnlockRes();
828    return true;
829 }
830
831 static bool catalogscmd(UAContext *ua, const char *cmd)
832 {
833    CAT *cat;
834    LockRes();
835    foreach_res(cat, R_CATALOG) {
836       if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
837          ua->send_msg("%s\n", cat->name());
838       }
839    }
840    UnlockRes();
841    return true;
842 }
843
844 static bool clientscmd(UAContext *ua, const char *cmd)
845 {
846    CLIENT *client;       
847    LockRes();
848    foreach_res(client, R_CLIENT) {
849       if (acl_access_ok(ua, Client_ACL, client->name())) {
850          ua->send_msg("%s\n", client->name());
851       }
852    }
853    UnlockRes();
854    return true;
855 }
856
857 static bool msgscmd(UAContext *ua, const char *cmd)
858 {
859    MSGS *msgs = NULL;
860    LockRes();
861    foreach_res(msgs, R_MSGS) {
862       ua->send_msg("%s\n", msgs->name());
863    }
864    UnlockRes();
865    return true;
866 }
867
868 static bool poolscmd(UAContext *ua, const char *cmd)
869 {
870    POOL *pool;       
871    LockRes();
872    foreach_res(pool, R_POOL) {
873       if (acl_access_ok(ua, Pool_ACL, pool->name())) {
874          ua->send_msg("%s\n", pool->name());
875       }
876    }
877    UnlockRes();
878    return true;
879 }
880
881 static bool storagecmd(UAContext *ua, const char *cmd)
882 {
883    STORE *store;
884    LockRes();
885    foreach_res(store, R_STORAGE) {
886       if (acl_access_ok(ua, Storage_ACL, store->name())) {
887          ua->send_msg("%s\n", store->name());
888       }
889    }
890    UnlockRes();
891    return true;
892 }
893
894 static bool aopcmd(UAContext *ua, const char *cmd)
895 {
896    ua->send_msg("None\n");
897    ua->send_msg("Truncate\n");
898    return true;
899 }
900
901 static bool typescmd(UAContext *ua, const char *cmd)
902 {
903    ua->send_msg("Backup\n");
904    ua->send_msg("Restore\n");
905    ua->send_msg("Admin\n");
906    ua->send_msg("Verify\n");
907    ua->send_msg("Migrate\n");
908    ua->send_msg("Copy\n");
909    return true;
910 }
911
912 /*
913  * If this command is called, it tells the director that we
914  *  are a program that wants a sort of API, and hence,
915  *  we will probably suppress certain output, include more
916  *  error codes, and most of all send back a good number
917  *  of new signals that indicate whether or not the command
918  *  succeeded.
919  */
920 static bool api_cmd(UAContext *ua, const char *cmd)
921 {
922    if (ua->argc == 2) {
923       ua->api = atoi(ua->argk[1]);
924    } else {
925       ua->api = 1;
926    }
927    return true;
928 }
929
930 static int client_backups_handler(void *ctx, int num_field, char **row)
931 {
932    UAContext *ua = (UAContext *)ctx;
933    ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
934       row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
935    return 0;
936 }
937
938 /*
939  * Return the backups for this client 
940  *
941  *  .backups client=xxx fileset=yyy
942  *
943  */
944 static bool backupscmd(UAContext *ua, const char *cmd)
945 {
946    if (!open_client_db(ua)) {
947       return true;
948    }
949    if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 || 
950        strcmp(ua->argk[2], "fileset") != 0) {
951       return true;
952    }
953    if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
954        !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
955       ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
956       return true;
957    }
958    Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
959    if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
960       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
961       return true;
962    }
963    return true;
964 }
965
966 static int sql_handler(void *ctx, int num_field, char **row)
967 {
968    UAContext *ua = (UAContext *)ctx;
969    POOL_MEM rows(PM_MESSAGE);
970
971    /* Check for nonsense */
972    if (num_field == 0 || row == NULL || row[0] == NULL) {
973       return 0;                       /* nothing returned */
974    }
975    for (int i=0; num_field--; i++) {
976       if (i == 0) {
977          pm_strcpy(rows, NPRT(row[0]));
978       } else {
979          pm_strcat(rows, NPRT(row[i]));
980       }
981       pm_strcat(rows, "\t");
982    }
983    if (!rows.c_str() || !*rows.c_str()) {
984       ua->send_msg("\t");
985    } else {
986       ua->send_msg("%s", rows.c_str());
987    }
988    return 0;
989 }
990
991 static bool sql_cmd(UAContext *ua, const char *cmd)
992 {
993    int index;
994    if (!open_client_db(ua)) {
995       return true;
996    }
997    index = find_arg_with_value(ua, "query");
998    if (index < 0) {
999       ua->error_msg(_("query keyword not found.\n"));
1000       return true;
1001    }
1002    if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1003       Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1004       ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1005       return true;
1006    }
1007    return true;
1008 }
1009       
1010 static int one_handler(void *ctx, int num_field, char **row)
1011 {
1012    UAContext *ua = (UAContext *)ctx;
1013    ua->send_msg("%s\n", row[0]);
1014    return 0;
1015 }
1016
1017 static bool mediatypescmd(UAContext *ua, const char *cmd)
1018 {
1019    if (!open_client_db(ua)) {
1020       return true;
1021    }
1022    if (!db_sql_query(ua->db, 
1023                   "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1024                   one_handler, (void *)ua)) 
1025    {
1026       ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1027    }
1028    return true;
1029 }
1030
1031 static bool mediacmd(UAContext *ua, const char *cmd)
1032 {
1033    if (!open_client_db(ua)) {
1034       return true;
1035    }
1036    if (!db_sql_query(ua->db, 
1037                   "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1038                   one_handler, (void *)ua)) 
1039    {
1040       ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1041    }
1042    return true;
1043 }
1044
1045 static bool locationscmd(UAContext *ua, const char *cmd)
1046 {
1047    if (!open_client_db(ua)) {
1048       return true;
1049    }
1050    if (!db_sql_query(ua->db, 
1051                   "SELECT DISTINCT Location FROM Location ORDER BY Location",
1052                   one_handler, (void *)ua)) 
1053    {
1054       ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1055    }
1056    return true;
1057 }
1058
1059 static bool levelscmd(UAContext *ua, const char *cmd)
1060 {
1061    int i;
1062    /* Note some levels are blank, which means none is needed */
1063    if (ua->argc == 1) {
1064       for (i=0; joblevels[i].level_name; i++) {
1065          if (joblevels[i].level_name[0] != ' ') {
1066             ua->send_msg("%s\n", joblevels[i].level_name);
1067          }
1068       }
1069    } else if (ua->argc == 2) {
1070       int jobtype = 0;
1071       /* Assume that first argument is the Job Type */
1072       for (i=0; jobtypes[i].type_name; i++) {
1073          if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
1074             jobtype = jobtypes[i].job_type;
1075             break;
1076          }
1077       }
1078       for (i=0; joblevels[i].level_name; i++) {
1079          if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
1080             ua->send_msg("%s\n", joblevels[i].level_name);
1081          }
1082       }
1083    }
1084
1085    return true;
1086 }
1087
1088 static bool volstatuscmd(UAContext *ua, const char *cmd)
1089 {
1090    ua->send_msg("Append\n");
1091    ua->send_msg("Full\n");
1092    ua->send_msg("Used\n");
1093    ua->send_msg("Recycle\n");
1094    ua->send_msg("Purged\n");
1095    ua->send_msg("Cleaning\n");
1096    ua->send_msg("Error\n");
1097    return true;
1098 }
1099
1100 /*
1101  * Return default values for a job
1102  */
1103 static bool defaultscmd(UAContext *ua, const char *cmd)
1104 {
1105    JOB *job;
1106    CLIENT *client;
1107    STORE *storage;
1108    POOL *pool;
1109    char ed1[50];
1110
1111    if (ua->argc != 2 || !ua->argv[1]) {
1112       return true;
1113    }
1114
1115    /* Job defaults */   
1116    if (strcmp(ua->argk[1], "job") == 0) {
1117       if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
1118          return true;
1119       }
1120       job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
1121       if (job) {
1122          USTORE store;
1123          ua->send_msg("job=%s", job->name());
1124          ua->send_msg("pool=%s", job->pool->name());
1125          ua->send_msg("messages=%s", job->messages->name());
1126          ua->send_msg("client=%s", job->client->name());
1127          get_job_storage(&store, job, NULL);
1128          ua->send_msg("storage=%s", store.store->name());
1129          ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
1130          ua->send_msg("level=%s", level_to_str(job->JobLevel));
1131          ua->send_msg("type=%s", job_type_to_str(job->JobType));
1132          ua->send_msg("fileset=%s", job->fileset->name());
1133          ua->send_msg("enabled=%d", job->enabled);
1134          ua->send_msg("catalog=%s", job->client->catalog->name());
1135       }
1136    } 
1137    /* Client defaults */
1138    else if (strcmp(ua->argk[1], "client") == 0) {
1139       if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
1140          return true;   
1141       }
1142       client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
1143       if (client) {
1144          ua->send_msg("client=%s", client->name());
1145          ua->send_msg("address=%s", client->address);
1146          ua->send_msg("fdport=%d", client->FDport);
1147          ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
1148          ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
1149          ua->send_msg("autoprune=%d", client->AutoPrune);
1150          ua->send_msg("catalog=%s", client->catalog->name());
1151       }
1152    }
1153    /* Storage defaults */
1154    else if (strcmp(ua->argk[1], "storage") == 0) {
1155       if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
1156          return true;
1157       }
1158       storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
1159       DEVICE *device;
1160       if (storage) {
1161          ua->send_msg("storage=%s", storage->name());
1162          ua->send_msg("address=%s", storage->address);
1163          ua->send_msg("enabled=%d", storage->enabled);
1164          ua->send_msg("media_type=%s", storage->media_type);
1165          ua->send_msg("sdport=%d", storage->SDport);
1166          device = (DEVICE *)storage->device->first();
1167          ua->send_msg("device=%s", device->name());
1168          if (storage->device->size() > 1) {
1169             while ((device = (DEVICE *)storage->device->next())) {
1170                ua->send_msg(",%s", device->name());
1171             }
1172          }
1173       }
1174    }
1175    /* Pool defaults */
1176    else if (strcmp(ua->argk[1], "pool") == 0) {
1177       if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
1178          return true;
1179       }
1180       pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
1181       if (pool) {
1182          ua->send_msg("pool=%s", pool->name());
1183          ua->send_msg("pool_type=%s", pool->pool_type);
1184          ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
1185          ua->send_msg("use_volume_once=%d", pool->use_volume_once);
1186          ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
1187          ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
1188          ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
1189          ua->send_msg("max_volumes=%d", pool->max_volumes);
1190          ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
1191          ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
1192          ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
1193          ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
1194          ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
1195          ua->send_msg("auto_prune=%d", pool->AutoPrune);
1196          ua->send_msg("recycle=%d", pool->Recycle);
1197          ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
1198          ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
1199       }
1200    }
1201    return true;
1202 }