2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2009 Free Software Foundation Europe e.V.
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 two of the GNU General Public
10 License as published by the Free Software Foundation and included
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.
18 You should have received a copy of the GNU 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
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.
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.
36 * Kern Sibbald, April MMII
43 #include "cats/bvfs.h"
44 #include "findlib/find.h"
46 /* Imported variables */
48 /* Imported functions */
49 extern void do_messages(UAContext *ua, const char *cmd);
50 extern int quit_cmd(UAContext *ua, const char *cmd);
51 extern int qhelp_cmd(UAContext *ua, const char *cmd);
52 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
55 /* Forward referenced functions */
56 static bool diecmd(UAContext *ua, const char *cmd);
57 static bool jobscmd(UAContext *ua, const char *cmd);
58 static bool filesetscmd(UAContext *ua, const char *cmd);
59 static bool clientscmd(UAContext *ua, const char *cmd);
60 static bool msgscmd(UAContext *ua, const char *cmd);
61 static bool poolscmd(UAContext *ua, const char *cmd);
62 static bool storagecmd(UAContext *ua, const char *cmd);
63 static bool defaultscmd(UAContext *ua, const char *cmd);
64 static bool typescmd(UAContext *ua, const char *cmd);
65 static bool backupscmd(UAContext *ua, const char *cmd);
66 static bool levelscmd(UAContext *ua, const char *cmd);
67 static bool getmsgscmd(UAContext *ua, const char *cmd);
69 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd);
70 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd);
71 static bool dot_bvfs_update(UAContext *ua, const char *cmd);
73 static bool api_cmd(UAContext *ua, const char *cmd);
74 static bool sql_cmd(UAContext *ua, const char *cmd);
75 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
76 static bool dot_help_cmd(UAContext *ua, const char *cmd);
78 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
79 static struct cmdstruct commands[] = { /* help */ /* can be used in runscript */
80 { NT_(".api"), api_cmd, NULL, false},
81 { NT_(".backups"), backupscmd, NULL, false},
82 { NT_(".clients"), clientscmd, NULL, true},
83 { NT_(".defaults"), defaultscmd, NULL, false},
84 { NT_(".die"), diecmd, NULL, false},
85 { NT_(".exit"), dot_quit_cmd, NULL, false},
86 { NT_(".filesets"), filesetscmd, NULL, false},
87 { NT_(".help"), dot_help_cmd, NULL, false},
88 { NT_(".jobs"), jobscmd, NULL, true},
89 { NT_(".levels"), levelscmd, NULL, false},
90 { NT_(".messages"), getmsgscmd, NULL, false},
91 { NT_(".msgs"), msgscmd, NULL, false},
92 { NT_(".pools"), poolscmd, NULL, true},
93 { NT_(".quit"), dot_quit_cmd, NULL, false},
94 { NT_(".sql"), sql_cmd, NULL, false},
95 { NT_(".status"), dot_status_cmd, NULL, false},
96 { NT_(".storage"), storagecmd, NULL, true},
97 { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL, true},
98 { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles,NULL, true},
99 { NT_(".bvfs_update"), dot_bvfs_update, NULL, true},
100 { NT_(".types"), typescmd, NULL, false}
102 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
105 * Execute a command from the UA
107 bool do_a_dot_command(UAContext *ua)
113 BSOCK *user = ua->UA_sock;
115 Dmsg1(1400, "Dot command: %s\n", user->msg);
120 len = strlen(ua->argk[0]);
122 if (ua->api) user->signal(BNET_CMD_BEGIN);
123 if (ua->api) user->signal(BNET_CMD_OK);
124 return true; /* no op */
126 for (i=0; i<comsize; i++) { /* search for command */
127 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
128 /* Check if this command is authorized in RunScript */
129 if (ua->runscript && !commands[i].use_in_rs) {
130 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
134 /* Check if command permitted, but "quit" is always OK */
135 if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
136 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
139 Dmsg1(100, "Cmd: %s\n", ua->cmd);
141 if (ua->api) user->signal(BNET_CMD_BEGIN);
142 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
143 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
150 pm_strcat(user->msg, _(": is an invalid command.\n"));
151 ua->error_msg("%s", user->msg);
157 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
160 if (!open_client_db(ua)) {
164 int pos = find_arg_with_value(ua, "jobid");
165 if (pos != -1 && is_a_number_list(ua->argv[pos])) {
167 pm_strcpy(jobids, ua->argv[pos]);
168 bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, jobids.c_str());
170 /* update cache for all jobids */
171 bvfs_update_cache(ua->jcr, ua->db);
176 static int bvfs_result_handler(void *ctx, int fields, char **row)
178 UAContext *ua = (UAContext *)ctx;
183 char empty[] = "A A A A A A A A A A A A A A";
185 lstat = (row[BVFS_LStat] && row[BVFS_LStat][0])?row[BVFS_LStat]:empty;
186 fileid = (row[BVFS_FileId] && row[BVFS_FileId][0])?row[BVFS_FileId]:"0";
188 memset(&statp, 0, sizeof(struct stat));
189 decode_stat(lstat, &statp, &LinkFI);
191 Dmsg1(0, "type=%s\n", row[0]);
192 if (bvfs_is_dir(row)) {
193 char *path = bvfs_basename_dir(row[BVFS_Name]);
194 ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
195 row[BVFS_JobId], row[BVFS_LStat], path);
197 } else if (bvfs_is_file(row)) {
198 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
199 row[BVFS_FilenameId], fileid, row[BVFS_JobId],
200 row[BVFS_LStat], row[BVFS_Name]);
206 static bool bvfs_parse_arg(UAContext *ua,
207 DBId_t *pathid, char **path, char **jobid,
208 int *limit, int *offset)
216 for (int i=1; i<ua->argc; i++) {
217 if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
218 if (is_a_number(ua->argv[i])) {
219 *pathid = str_to_int64(ua->argv[i]);
222 if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
226 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
227 if (is_a_number_list(ua->argv[i])) {
228 *jobid = ua->argv[i];
232 if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
233 if (is_a_number(ua->argv[i])) {
234 *limit = str_to_int64(ua->argv[i]);
238 if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
239 if (is_a_number(ua->argv[i])) {
240 *offset = str_to_int64(ua->argv[i]);
245 if (!((*pathid || *path) && *jobid)) {
249 if (!open_client_db(ua)) {
257 * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
258 * .bvfs_lsfiles jobid=1,2,3,4 path=/
260 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
263 int limit=2000, offset=0;
264 char *path=NULL, *jobid=NULL;
266 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
269 ua->error_msg("Can't find jobid, pathid or path argument\n");
270 return true; /* not enough param */
273 Bvfs fs(ua->jcr, ua->db);
274 fs.set_jobids(jobid);
275 fs.set_handler(bvfs_result_handler, ua);
284 fs.set_offset(offset);
292 * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
293 * .bvfs_lsdirs jobid=1,2,3,4 path=/
294 * .bvfs_lsdirs jobid=1,2,3,4 path=
296 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
299 int limit=2000, offset=0;
300 char *path=NULL, *jobid=NULL;
302 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
305 ua->error_msg("Can't find jobid, pathid or path argument\n");
306 return true; /* not enough param */
309 Bvfs fs(ua->jcr, ua->db);
310 fs.set_jobids(jobid);
312 fs.set_handler(bvfs_result_handler, ua);
320 fs.set_offset(offset);
322 fs.ls_special_dirs();
328 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
334 static bool dot_help_cmd(UAContext *ua, const char *cmd)
340 static bool getmsgscmd(UAContext *ua, const char *cmd)
342 if (console_msg_pending) {
343 do_messages(ua, cmd);
349 static void do_storage_die(UAContext *ua, STORE *store)
355 lstore.store = store;
356 pm_strcpy(lstore.store_source, _("unknown source"));
357 set_wstorage(jcr, &lstore);
358 /* Try connecting for up to 15 seconds */
359 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
360 store->name(), store->address, store->SDport);
361 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
362 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
365 Dmsg0(120, _("Connected to storage daemon\n"));
366 sd = jcr->store_bsock;
368 if (sd->recv() >= 0) {
369 ua->send_msg("%s", sd->msg);
371 sd->signal(BNET_TERMINATE);
373 jcr->store_bsock = NULL;
377 static void do_client_die(UAContext *ua, CLIENT *client)
381 /* Connect to File daemon */
383 ua->jcr->client = client;
384 /* Try to connect for 15 seconds */
385 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
386 client->name(), client->address, client->FDport);
387 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
388 ua->error_msg(_("Failed to connect to Client.\n"));
391 Dmsg0(120, "Connected to file daemon\n");
392 fd = ua->jcr->file_bsock;
394 if (fd->recv() >= 0) {
395 ua->send_msg("%s", fd->msg);
397 fd->signal(BNET_TERMINATE);
399 ua->jcr->file_bsock = NULL;
404 * Create segmentation fault
406 static bool diecmd(UAContext *ua, const char *cmd)
414 Dmsg1(120, "diecmd:%s:\n", cmd);
417 for (i=1; i<ua->argc; i++) {
418 if (strcasecmp(ua->argk[i], "dir") == 0 ||
419 strcasecmp(ua->argk[i], "director") == 0) {
420 ua->send_msg(_("The Director will segment fault.\n"));
421 a = jcr->JobId; /* ref NULL pointer */
422 jcr->JobId = 1000; /* another ref NULL pointer */
425 if (strcasecmp(ua->argk[i], "client") == 0 ||
426 strcasecmp(ua->argk[i], "fd") == 0) {
429 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
431 do_client_die(ua, client);
435 client = select_client_resource(ua);
437 do_client_die(ua, client);
442 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
443 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
444 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
447 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
449 do_storage_die(ua, store);
453 store = get_storage_resource(ua, false/*no default*/);
455 do_storage_die(ua, store);
461 * We didn't find an appropriate keyword above, so
464 start_prompt(ua, _("Available daemons are: \n"));
465 add_prompt(ua, _("Director"));
466 add_prompt(ua, _("Storage"));
467 add_prompt(ua, _("Client"));
468 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
469 case 0: /* Director */
470 ua->send_msg(_("The Director will segment fault.\n"));
471 a = jcr->JobId; /* ref NULL pointer */
472 jcr->JobId = 1000; /* another ref NULL pointer */
475 store = get_storage_resource(ua, false/*no default*/);
477 do_storage_die(ua, store);
481 client = select_client_resource(ua);
483 do_client_die(ua, client);
495 * Dummy routine for non-development version
497 static bool diecmd(UAContext *ua, const char *cmd)
504 static bool jobscmd(UAContext *ua, const char *cmd)
508 foreach_res(job, R_JOB) {
509 if (acl_access_ok(ua, Job_ACL, job->name())) {
510 ua->send_msg("%s\n", job->name());
517 static bool filesetscmd(UAContext *ua, const char *cmd)
521 foreach_res(fs, R_FILESET) {
522 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
523 ua->send_msg("%s\n", fs->name());
530 static bool clientscmd(UAContext *ua, const char *cmd)
534 foreach_res(client, R_CLIENT) {
535 if (acl_access_ok(ua, Client_ACL, client->name())) {
536 ua->send_msg("%s\n", client->name());
543 static bool msgscmd(UAContext *ua, const char *cmd)
547 foreach_res(msgs, R_MSGS) {
548 ua->send_msg("%s\n", msgs->name());
554 static bool poolscmd(UAContext *ua, const char *cmd)
558 foreach_res(pool, R_POOL) {
559 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
560 ua->send_msg("%s\n", pool->name());
567 static bool storagecmd(UAContext *ua, const char *cmd)
571 foreach_res(store, R_STORAGE) {
572 if (acl_access_ok(ua, Storage_ACL, store->name())) {
573 ua->send_msg("%s\n", store->name());
581 static bool typescmd(UAContext *ua, const char *cmd)
583 ua->send_msg("Backup\n");
584 ua->send_msg("Restore\n");
585 ua->send_msg("Admin\n");
586 ua->send_msg("Verify\n");
587 ua->send_msg("Migrate\n");
593 * If this command is called, it tells the director that we
594 * are a program that wants a sort of API, and hence,
595 * we will probably suppress certain output, include more
596 * error codes, and most of all send back a good number
597 * of new signals that indicate whether or not the command
600 static bool api_cmd(UAContext *ua, const char *cmd)
603 ua->api = atoi(ua->argk[1]);
610 static int client_backups_handler(void *ctx, int num_field, char **row)
612 UAContext *ua = (UAContext *)ctx;
613 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
614 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
619 * Return the backups for this client
621 * .backups client=xxx fileset=yyy
624 static bool backupscmd(UAContext *ua, const char *cmd)
626 if (!open_client_db(ua)) {
629 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
630 strcmp(ua->argk[2], "fileset") != 0) {
633 if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
634 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
635 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
638 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
639 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
640 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
646 static int sql_handler(void *ctx, int num_field, char **row)
648 UAContext *ua = (UAContext *)ctx;
649 POOL_MEM rows(PM_MESSAGE);
651 /* Check for nonsense */
652 if (num_field == 0 || row == NULL || row[0] == NULL) {
653 return 0; /* nothing returned */
655 for (int i=0; num_field--; i++) {
657 pm_strcpy(rows, NPRT(row[0]));
659 pm_strcat(rows, NPRT(row[i]));
661 pm_strcat(rows, "\t");
663 if (!rows.c_str() || !*rows.c_str()) {
666 ua->send_msg("%s", rows.c_str());
671 static bool sql_cmd(UAContext *ua, const char *cmd)
674 if (!open_client_db(ua)) {
677 index = find_arg_with_value(ua, "query");
679 ua->error_msg(_("query keyword not found.\n"));
682 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
683 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
684 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
692 static bool levelscmd(UAContext *ua, const char *cmd)
694 ua->send_msg("Incremental\n");
695 ua->send_msg("Full\n");
696 ua->send_msg("Differential\n");
697 ua->send_msg("VirtualFull\n");
698 ua->send_msg("Catalog\n");
699 ua->send_msg("InitCatalog\n");
700 ua->send_msg("VolumeToCatalog\n");
705 * Return default values for a job
707 static bool defaultscmd(UAContext *ua, const char *cmd)
715 if (ua->argc != 2 || !ua->argv[1]) {
720 if (strcmp(ua->argk[1], "job") == 0) {
721 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
724 job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
727 ua->send_msg("job=%s", job->name());
728 ua->send_msg("pool=%s", job->pool->name());
729 ua->send_msg("messages=%s", job->messages->name());
730 ua->send_msg("client=%s", job->client->name());
731 get_job_storage(&store, job, NULL);
732 ua->send_msg("storage=%s", store.store->name());
733 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
734 ua->send_msg("level=%s", level_to_str(job->JobLevel));
735 ua->send_msg("type=%s", job_type_to_str(job->JobType));
736 ua->send_msg("fileset=%s", job->fileset->name());
737 ua->send_msg("enabled=%d", job->enabled);
738 ua->send_msg("catalog=%s", job->client->catalog->name());
741 /* Client defaults */
742 else if (strcmp(ua->argk[1], "client") == 0) {
743 if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
746 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
748 ua->send_msg("client=%s", client->name());
749 ua->send_msg("address=%s", client->address);
750 ua->send_msg("fdport=%d", client->FDport);
751 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
752 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
753 ua->send_msg("autoprune=%d", client->AutoPrune);
754 ua->send_msg("catalog=%s", client->catalog->name());
757 /* Storage defaults */
758 else if (strcmp(ua->argk[1], "storage") == 0) {
759 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
762 storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
765 ua->send_msg("storage=%s", storage->name());
766 ua->send_msg("address=%s", storage->address);
767 ua->send_msg("enabled=%d", storage->enabled);
768 ua->send_msg("media_type=%s", storage->media_type);
769 ua->send_msg("sdport=%d", storage->SDport);
770 device = (DEVICE *)storage->device->first();
771 ua->send_msg("device=%s", device->name());
772 if (storage->device->size() > 1) {
773 while ((device = (DEVICE *)storage->device->next())) {
774 ua->send_msg(",%s", device->name());
780 else if (strcmp(ua->argk[1], "pool") == 0) {
781 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
784 pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
786 ua->send_msg("pool=%s", pool->name());
787 ua->send_msg("pool_type=%s", pool->pool_type);
788 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
789 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
790 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
791 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
792 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
793 ua->send_msg("max_volumes=%d", pool->max_volumes);
794 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
795 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
796 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
797 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
798 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
799 ua->send_msg("auto_prune=%d", pool->AutoPrune);
800 ua->send_msg("recycle=%d", pool->Recycle);