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)
159 if (!open_client_db(ua)) {
163 int pos = find_arg_with_value(ua, "jobid");
164 if (pos != -1) { /* find jobid arg */
165 if (is_a_number_list(ua->argv[pos])) {
166 bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
169 /* update cache for all jobids */
170 bvfs_update_cache(ua->jcr, ua->db);
175 static int bvfs_result_handler(void *ctx, int fields, char **row)
177 UAContext *ua = (UAContext *)ctx;
180 char empty[] = "A A A A A A A A A A A A A A";
182 memset(&statp, 0, sizeof(struct stat));
183 decode_stat((row[BVFS_LStat] && row[BVFS_LStat][0])?row[BVFS_LStat]:empty,
186 if (fields == BVFS_DIR_RECORD) {
187 char *path = bvfs_basename_dir(row[BVFS_Name]);
188 ua->send_msg("%s\t%s\t\%s\n", row[BVFS_Id], row[BVFS_JobId], path);
189 } else if (fields == BVFS_FILE_RECORD) {
190 ua->send_msg("%s\t%s\t\%s\n", row[BVFS_Id], row[BVFS_JobId], row[BVFS_Name]);
196 static bool bvfs_parse_arg(UAContext *ua,
197 DBId_t *pathid, char **path, char **jobid,
198 int *limit, int *offset)
206 for (int i=1; i<ua->argc; i++) {
207 if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
208 if (is_a_number(ua->argv[i])) {
209 *pathid = str_to_int64(ua->argv[i]);
212 if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
216 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
217 if (is_a_number_list(ua->argv[i])) {
218 *jobid = ua->argv[i];
222 if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
223 if (is_a_number(ua->argv[i])) {
224 *limit = str_to_int64(ua->argv[i]);
228 if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
229 if (is_a_number(ua->argv[i])) {
230 *offset = str_to_int64(ua->argv[i]);
235 if (!((pathid || path) && jobid)) {
239 if (!open_client_db(ua)) {
246 static bool dot_lsfiles(UAContext *ua, const char *cmd)
249 int limit=2000, offset=0;
250 char *path=NULL, *jobid=NULL;
252 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
255 ua->error_msg("Can't find jobid, pathid or path argument\n");
256 return true; /* not enough param */
259 Bvfs fs(ua->jcr, ua->db);
260 fs.set_jobids(jobid);
262 fs.set_offset(offset);
263 fs.set_handler(bvfs_result_handler, ua);
276 static bool dot_lsdirs(UAContext *ua, const char *cmd)
279 int limit=2000, offset=0;
280 char *path=NULL, *jobid=NULL;
282 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
285 ua->error_msg("Can't find jobid, pathid or path argument\n");
286 return true; /* not enough param */
289 Bvfs fs(ua->jcr, ua->db);
290 fs.set_jobids(jobid);
292 fs.set_offset(offset);
293 fs.set_handler(bvfs_result_handler, ua);
306 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
312 static bool dot_help_cmd(UAContext *ua, const char *cmd)
318 static bool getmsgscmd(UAContext *ua, const char *cmd)
320 if (console_msg_pending) {
321 do_messages(ua, cmd);
327 static void do_storage_die(UAContext *ua, STORE *store)
333 lstore.store = store;
334 pm_strcpy(lstore.store_source, _("unknown source"));
335 set_wstorage(jcr, &lstore);
336 /* Try connecting for up to 15 seconds */
337 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
338 store->name(), store->address, store->SDport);
339 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
340 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
343 Dmsg0(120, _("Connected to storage daemon\n"));
344 sd = jcr->store_bsock;
346 if (sd->recv() >= 0) {
347 ua->send_msg("%s", sd->msg);
349 sd->signal(BNET_TERMINATE);
351 jcr->store_bsock = NULL;
355 static void do_client_die(UAContext *ua, CLIENT *client)
359 /* Connect to File daemon */
361 ua->jcr->client = client;
362 /* Try to connect for 15 seconds */
363 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
364 client->name(), client->address, client->FDport);
365 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
366 ua->error_msg(_("Failed to connect to Client.\n"));
369 Dmsg0(120, "Connected to file daemon\n");
370 fd = ua->jcr->file_bsock;
372 if (fd->recv() >= 0) {
373 ua->send_msg("%s", fd->msg);
375 fd->signal(BNET_TERMINATE);
377 ua->jcr->file_bsock = NULL;
382 * Create segmentation fault
384 static bool diecmd(UAContext *ua, const char *cmd)
392 Dmsg1(120, "diecmd:%s:\n", cmd);
395 for (i=1; i<ua->argc; i++) {
396 if (strcasecmp(ua->argk[i], "dir") == 0 ||
397 strcasecmp(ua->argk[i], "director") == 0) {
398 ua->send_msg(_("The Director will segment fault.\n"));
399 a = jcr->JobId; /* ref NULL pointer */
400 jcr->JobId = 1000; /* another ref NULL pointer */
403 if (strcasecmp(ua->argk[i], "client") == 0 ||
404 strcasecmp(ua->argk[i], "fd") == 0) {
407 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
409 do_client_die(ua, client);
413 client = select_client_resource(ua);
415 do_client_die(ua, client);
420 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
421 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
422 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
425 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
427 do_storage_die(ua, store);
431 store = get_storage_resource(ua, false/*no default*/);
433 do_storage_die(ua, store);
439 * We didn't find an appropriate keyword above, so
442 start_prompt(ua, _("Available daemons are: \n"));
443 add_prompt(ua, _("Director"));
444 add_prompt(ua, _("Storage"));
445 add_prompt(ua, _("Client"));
446 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
447 case 0: /* Director */
448 ua->send_msg(_("The Director will segment fault.\n"));
449 a = jcr->JobId; /* ref NULL pointer */
450 jcr->JobId = 1000; /* another ref NULL pointer */
453 store = get_storage_resource(ua, false/*no default*/);
455 do_storage_die(ua, store);
459 client = select_client_resource(ua);
461 do_client_die(ua, client);
473 * Dummy routine for non-development version
475 static bool diecmd(UAContext *ua, const char *cmd)
482 static bool jobscmd(UAContext *ua, const char *cmd)
486 foreach_res(job, R_JOB) {
487 if (acl_access_ok(ua, Job_ACL, job->name())) {
488 ua->send_msg("%s\n", job->name());
495 static bool filesetscmd(UAContext *ua, const char *cmd)
499 foreach_res(fs, R_FILESET) {
500 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
501 ua->send_msg("%s\n", fs->name());
508 static bool clientscmd(UAContext *ua, const char *cmd)
512 foreach_res(client, R_CLIENT) {
513 if (acl_access_ok(ua, Client_ACL, client->name())) {
514 ua->send_msg("%s\n", client->name());
521 static bool msgscmd(UAContext *ua, const char *cmd)
525 foreach_res(msgs, R_MSGS) {
526 ua->send_msg("%s\n", msgs->name());
532 static bool poolscmd(UAContext *ua, const char *cmd)
536 foreach_res(pool, R_POOL) {
537 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
538 ua->send_msg("%s\n", pool->name());
545 static bool storagecmd(UAContext *ua, const char *cmd)
549 foreach_res(store, R_STORAGE) {
550 if (acl_access_ok(ua, Storage_ACL, store->name())) {
551 ua->send_msg("%s\n", store->name());
559 static bool typescmd(UAContext *ua, const char *cmd)
561 ua->send_msg("Backup\n");
562 ua->send_msg("Restore\n");
563 ua->send_msg("Admin\n");
564 ua->send_msg("Verify\n");
565 ua->send_msg("Migrate\n");
571 * If this command is called, it tells the director that we
572 * are a program that wants a sort of API, and hence,
573 * we will probably suppress certain output, include more
574 * error codes, and most of all send back a good number
575 * of new signals that indicate whether or not the command
578 static bool api_cmd(UAContext *ua, const char *cmd)
581 ua->api = atoi(ua->argk[1]);
588 static int client_backups_handler(void *ctx, int num_field, char **row)
590 UAContext *ua = (UAContext *)ctx;
591 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
592 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
597 * Return the backups for this client
599 * .backups client=xxx fileset=yyy
602 static bool backupscmd(UAContext *ua, const char *cmd)
604 if (!open_client_db(ua)) {
607 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
608 strcmp(ua->argk[2], "fileset") != 0) {
611 if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
612 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
613 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
616 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
617 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
618 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
624 static int sql_handler(void *ctx, int num_field, char **row)
626 UAContext *ua = (UAContext *)ctx;
627 POOL_MEM rows(PM_MESSAGE);
629 /* Check for nonsense */
630 if (num_field == 0 || row == NULL || row[0] == NULL) {
631 return 0; /* nothing returned */
633 for (int i=0; num_field--; i++) {
635 pm_strcpy(rows, NPRT(row[0]));
637 pm_strcat(rows, NPRT(row[i]));
639 pm_strcat(rows, "\t");
641 if (!rows.c_str() || !*rows.c_str()) {
644 ua->send_msg("%s", rows.c_str());
649 static bool sql_cmd(UAContext *ua, const char *cmd)
652 if (!open_client_db(ua)) {
655 index = find_arg_with_value(ua, "query");
657 ua->error_msg(_("query keyword not found.\n"));
660 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
661 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
662 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
670 static bool levelscmd(UAContext *ua, const char *cmd)
672 ua->send_msg("Incremental\n");
673 ua->send_msg("Full\n");
674 ua->send_msg("Differential\n");
675 ua->send_msg("VirtualFull\n");
676 ua->send_msg("Catalog\n");
677 ua->send_msg("InitCatalog\n");
678 ua->send_msg("VolumeToCatalog\n");
683 * Return default values for a job
685 static bool defaultscmd(UAContext *ua, const char *cmd)
693 if (ua->argc != 2 || !ua->argv[1]) {
698 if (strcmp(ua->argk[1], "job") == 0) {
699 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
702 job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
705 ua->send_msg("job=%s", job->name());
706 ua->send_msg("pool=%s", job->pool->name());
707 ua->send_msg("messages=%s", job->messages->name());
708 ua->send_msg("client=%s", job->client->name());
709 get_job_storage(&store, job, NULL);
710 ua->send_msg("storage=%s", store.store->name());
711 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
712 ua->send_msg("level=%s", level_to_str(job->JobLevel));
713 ua->send_msg("type=%s", job_type_to_str(job->JobType));
714 ua->send_msg("fileset=%s", job->fileset->name());
715 ua->send_msg("enabled=%d", job->enabled);
716 ua->send_msg("catalog=%s", job->client->catalog->name());
719 /* Client defaults */
720 else if (strcmp(ua->argk[1], "client") == 0) {
721 if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
724 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
726 ua->send_msg("client=%s", client->name());
727 ua->send_msg("address=%s", client->address);
728 ua->send_msg("fdport=%d", client->FDport);
729 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
730 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
731 ua->send_msg("autoprune=%d", client->AutoPrune);
732 ua->send_msg("catalog=%s", client->catalog->name());
735 /* Storage defaults */
736 else if (strcmp(ua->argk[1], "storage") == 0) {
737 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
740 storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
743 ua->send_msg("storage=%s", storage->name());
744 ua->send_msg("address=%s", storage->address);
745 ua->send_msg("enabled=%d", storage->enabled);
746 ua->send_msg("media_type=%s", storage->media_type);
747 ua->send_msg("sdport=%d", storage->SDport);
748 device = (DEVICE *)storage->device->first();
749 ua->send_msg("device=%s", device->name());
750 if (storage->device->size() > 1) {
751 while ((device = (DEVICE *)storage->device->next())) {
752 ua->send_msg(",%s", device->name());
758 else if (strcmp(ua->argk[1], "pool") == 0) {
759 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
762 pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
764 ua->send_msg("pool=%s", pool->name());
765 ua->send_msg("pool_type=%s", pool->pool_type);
766 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
767 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
768 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
769 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
770 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
771 ua->send_msg("max_volumes=%d", pool->max_volumes);
772 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
773 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
774 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
775 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
776 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
777 ua->send_msg("auto_prune=%d", pool->AutoPrune);
778 ua->send_msg("recycle=%d", pool->Recycle);