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_lsdirs(UAContext *ua, const char *cmd);
70 static bool dot_lsfiles(UAContext *ua, const char *cmd);
71 static bool dot_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_(".lsdirs"), dot_lsdirs, NULL, true},
98 { NT_(".lsfiles"), dot_lsfiles, NULL, true},
99 { NT_(".update"), dot_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_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.list, ua->argv[pos]);
168 bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, &jobids);
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;
181 char empty[] = "A A A A A A A A A A A A A A";
183 memset(&statp, 0, sizeof(struct stat));
184 decode_stat((row[BVFS_LStat] && row[BVFS_LStat][0])?row[BVFS_LStat]:empty,
187 if (fields == BVFS_DIR_RECORD) {
188 char *path = bvfs_basename_dir(row[BVFS_Name]);
189 ua->send_msg("%s\t%s\t\%s\n", row[BVFS_Id], row[BVFS_JobId], path);
190 } else if (fields == BVFS_FILE_RECORD) {
191 ua->send_msg("%s\t%s\t\%s\n", row[BVFS_Id], row[BVFS_JobId], row[BVFS_Name]);
197 static bool bvfs_parse_arg(UAContext *ua,
198 DBId_t *pathid, char **path, char **jobid,
199 int *limit, int *offset)
207 for (int i=1; i<ua->argc; i++) {
208 if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
209 if (is_a_number(ua->argv[i])) {
210 *pathid = str_to_int64(ua->argv[i]);
213 if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
217 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
218 if (is_a_number_list(ua->argv[i])) {
219 *jobid = ua->argv[i];
223 if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
224 if (is_a_number(ua->argv[i])) {
225 *limit = str_to_int64(ua->argv[i]);
229 if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
230 if (is_a_number(ua->argv[i])) {
231 *offset = str_to_int64(ua->argv[i]);
236 if (!((pathid || path) && jobid)) {
240 if (!open_client_db(ua)) {
248 * .lsfiles jobid=1,2,3,4 pathid=10
249 * .lsfiles jobid=1,2,3,4 path=/
251 static bool dot_lsfiles(UAContext *ua, const char *cmd)
254 int limit=2000, offset=0;
255 char *path=NULL, *jobid=NULL;
257 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
260 ua->error_msg("Can't find jobid, pathid or path argument\n");
261 return true; /* not enough param */
264 Bvfs fs(ua->jcr, ua->db);
265 fs.set_jobids(jobid);
266 fs.set_handler(bvfs_result_handler, ua);
275 fs.set_offset(offset);
283 * .lsdirs jobid=1,2,3,4 pathid=10
284 * .lsdirs jobid=1,2,3,4 path=/
285 * .lsdirs jobid=1,2,3,4 path=
287 static bool dot_lsdirs(UAContext *ua, const char *cmd)
290 int limit=2000, offset=0;
291 char *path=NULL, *jobid=NULL;
293 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
296 ua->error_msg("Can't find jobid, pathid or path argument\n");
297 return true; /* not enough param */
300 Bvfs fs(ua->jcr, ua->db);
301 fs.set_jobids(jobid);
303 fs.set_handler(bvfs_result_handler, ua);
311 fs.set_offset(offset);
313 fs.ls_special_dirs();
319 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
325 static bool dot_help_cmd(UAContext *ua, const char *cmd)
331 static bool getmsgscmd(UAContext *ua, const char *cmd)
333 if (console_msg_pending) {
334 do_messages(ua, cmd);
340 static void do_storage_die(UAContext *ua, STORE *store)
346 lstore.store = store;
347 pm_strcpy(lstore.store_source, _("unknown source"));
348 set_wstorage(jcr, &lstore);
349 /* Try connecting for up to 15 seconds */
350 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
351 store->name(), store->address, store->SDport);
352 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
353 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
356 Dmsg0(120, _("Connected to storage daemon\n"));
357 sd = jcr->store_bsock;
359 if (sd->recv() >= 0) {
360 ua->send_msg("%s", sd->msg);
362 sd->signal(BNET_TERMINATE);
364 jcr->store_bsock = NULL;
368 static void do_client_die(UAContext *ua, CLIENT *client)
372 /* Connect to File daemon */
374 ua->jcr->client = client;
375 /* Try to connect for 15 seconds */
376 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
377 client->name(), client->address, client->FDport);
378 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
379 ua->error_msg(_("Failed to connect to Client.\n"));
382 Dmsg0(120, "Connected to file daemon\n");
383 fd = ua->jcr->file_bsock;
385 if (fd->recv() >= 0) {
386 ua->send_msg("%s", fd->msg);
388 fd->signal(BNET_TERMINATE);
390 ua->jcr->file_bsock = NULL;
395 * Create segmentation fault
397 static bool diecmd(UAContext *ua, const char *cmd)
405 Dmsg1(120, "diecmd:%s:\n", cmd);
408 for (i=1; i<ua->argc; i++) {
409 if (strcasecmp(ua->argk[i], "dir") == 0 ||
410 strcasecmp(ua->argk[i], "director") == 0) {
411 ua->send_msg(_("The Director will segment fault.\n"));
412 a = jcr->JobId; /* ref NULL pointer */
413 jcr->JobId = 1000; /* another ref NULL pointer */
416 if (strcasecmp(ua->argk[i], "client") == 0 ||
417 strcasecmp(ua->argk[i], "fd") == 0) {
420 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
422 do_client_die(ua, client);
426 client = select_client_resource(ua);
428 do_client_die(ua, client);
433 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
434 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
435 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
438 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
440 do_storage_die(ua, store);
444 store = get_storage_resource(ua, false/*no default*/);
446 do_storage_die(ua, store);
452 * We didn't find an appropriate keyword above, so
455 start_prompt(ua, _("Available daemons are: \n"));
456 add_prompt(ua, _("Director"));
457 add_prompt(ua, _("Storage"));
458 add_prompt(ua, _("Client"));
459 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
460 case 0: /* Director */
461 ua->send_msg(_("The Director will segment fault.\n"));
462 a = jcr->JobId; /* ref NULL pointer */
463 jcr->JobId = 1000; /* another ref NULL pointer */
466 store = get_storage_resource(ua, false/*no default*/);
468 do_storage_die(ua, store);
472 client = select_client_resource(ua);
474 do_client_die(ua, client);
486 * Dummy routine for non-development version
488 static bool diecmd(UAContext *ua, const char *cmd)
495 static bool jobscmd(UAContext *ua, const char *cmd)
499 foreach_res(job, R_JOB) {
500 if (acl_access_ok(ua, Job_ACL, job->name())) {
501 ua->send_msg("%s\n", job->name());
508 static bool filesetscmd(UAContext *ua, const char *cmd)
512 foreach_res(fs, R_FILESET) {
513 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
514 ua->send_msg("%s\n", fs->name());
521 static bool clientscmd(UAContext *ua, const char *cmd)
525 foreach_res(client, R_CLIENT) {
526 if (acl_access_ok(ua, Client_ACL, client->name())) {
527 ua->send_msg("%s\n", client->name());
534 static bool msgscmd(UAContext *ua, const char *cmd)
538 foreach_res(msgs, R_MSGS) {
539 ua->send_msg("%s\n", msgs->name());
545 static bool poolscmd(UAContext *ua, const char *cmd)
549 foreach_res(pool, R_POOL) {
550 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
551 ua->send_msg("%s\n", pool->name());
558 static bool storagecmd(UAContext *ua, const char *cmd)
562 foreach_res(store, R_STORAGE) {
563 if (acl_access_ok(ua, Storage_ACL, store->name())) {
564 ua->send_msg("%s\n", store->name());
572 static bool typescmd(UAContext *ua, const char *cmd)
574 ua->send_msg("Backup\n");
575 ua->send_msg("Restore\n");
576 ua->send_msg("Admin\n");
577 ua->send_msg("Verify\n");
578 ua->send_msg("Migrate\n");
584 * If this command is called, it tells the director that we
585 * are a program that wants a sort of API, and hence,
586 * we will probably suppress certain output, include more
587 * error codes, and most of all send back a good number
588 * of new signals that indicate whether or not the command
591 static bool api_cmd(UAContext *ua, const char *cmd)
594 ua->api = atoi(ua->argk[1]);
601 static int client_backups_handler(void *ctx, int num_field, char **row)
603 UAContext *ua = (UAContext *)ctx;
604 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
605 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
610 * Return the backups for this client
612 * .backups client=xxx fileset=yyy
615 static bool backupscmd(UAContext *ua, const char *cmd)
617 if (!open_client_db(ua)) {
620 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
621 strcmp(ua->argk[2], "fileset") != 0) {
624 if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
625 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
626 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
629 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
630 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
631 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
637 static int sql_handler(void *ctx, int num_field, char **row)
639 UAContext *ua = (UAContext *)ctx;
640 POOL_MEM rows(PM_MESSAGE);
642 /* Check for nonsense */
643 if (num_field == 0 || row == NULL || row[0] == NULL) {
644 return 0; /* nothing returned */
646 for (int i=0; num_field--; i++) {
648 pm_strcpy(rows, NPRT(row[0]));
650 pm_strcat(rows, NPRT(row[i]));
652 pm_strcat(rows, "\t");
654 if (!rows.c_str() || !*rows.c_str()) {
657 ua->send_msg("%s", rows.c_str());
662 static bool sql_cmd(UAContext *ua, const char *cmd)
665 if (!open_client_db(ua)) {
668 index = find_arg_with_value(ua, "query");
670 ua->error_msg(_("query keyword not found.\n"));
673 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
674 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
675 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
683 static bool levelscmd(UAContext *ua, const char *cmd)
685 ua->send_msg("Incremental\n");
686 ua->send_msg("Full\n");
687 ua->send_msg("Differential\n");
688 ua->send_msg("VirtualFull\n");
689 ua->send_msg("Catalog\n");
690 ua->send_msg("InitCatalog\n");
691 ua->send_msg("VolumeToCatalog\n");
696 * Return default values for a job
698 static bool defaultscmd(UAContext *ua, const char *cmd)
706 if (ua->argc != 2 || !ua->argv[1]) {
711 if (strcmp(ua->argk[1], "job") == 0) {
712 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
715 job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
718 ua->send_msg("job=%s", job->name());
719 ua->send_msg("pool=%s", job->pool->name());
720 ua->send_msg("messages=%s", job->messages->name());
721 ua->send_msg("client=%s", job->client->name());
722 get_job_storage(&store, job, NULL);
723 ua->send_msg("storage=%s", store.store->name());
724 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
725 ua->send_msg("level=%s", level_to_str(job->JobLevel));
726 ua->send_msg("type=%s", job_type_to_str(job->JobType));
727 ua->send_msg("fileset=%s", job->fileset->name());
728 ua->send_msg("enabled=%d", job->enabled);
729 ua->send_msg("catalog=%s", job->client->catalog->name());
732 /* Client defaults */
733 else if (strcmp(ua->argk[1], "client") == 0) {
734 if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
737 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
739 ua->send_msg("client=%s", client->name());
740 ua->send_msg("address=%s", client->address);
741 ua->send_msg("fdport=%d", client->FDport);
742 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
743 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
744 ua->send_msg("autoprune=%d", client->AutoPrune);
745 ua->send_msg("catalog=%s", client->catalog->name());
748 /* Storage defaults */
749 else if (strcmp(ua->argk[1], "storage") == 0) {
750 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
753 storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
756 ua->send_msg("storage=%s", storage->name());
757 ua->send_msg("address=%s", storage->address);
758 ua->send_msg("enabled=%d", storage->enabled);
759 ua->send_msg("media_type=%s", storage->media_type);
760 ua->send_msg("sdport=%d", storage->SDport);
761 device = (DEVICE *)storage->device->first();
762 ua->send_msg("device=%s", device->name());
763 if (storage->device->size() > 1) {
764 while ((device = (DEVICE *)storage->device->next())) {
765 ua->send_msg(",%s", device->name());
771 else if (strcmp(ua->argk[1], "pool") == 0) {
772 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
775 pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
777 ua->send_msg("pool=%s", pool->name());
778 ua->send_msg("pool_type=%s", pool->pool_type);
779 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
780 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
781 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
782 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
783 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
784 ua->send_msg("max_volumes=%d", pool->max_volumes);
785 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
786 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
787 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
788 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
789 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
790 ua->send_msg("auto_prune=%d", pool->AutoPrune);
791 ua->send_msg("recycle=%d", pool->Recycle);