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)) {
247 * .lsfiles jobid=1,2,3,4 pathid=10
248 * .lsfiles jobid=1,2,3,4 path=/
250 static bool dot_lsfiles(UAContext *ua, const char *cmd)
253 int limit=2000, offset=0;
254 char *path=NULL, *jobid=NULL;
256 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
259 ua->error_msg("Can't find jobid, pathid or path argument\n");
260 return true; /* not enough param */
263 Bvfs fs(ua->jcr, ua->db);
264 fs.set_jobids(jobid);
265 fs.set_handler(bvfs_result_handler, ua);
274 fs.set_offset(offset);
282 * .lsdirs jobid=1,2,3,4 pathid=10
283 * .lsdirs jobid=1,2,3,4 path=/
284 * .lsdirs jobid=1,2,3,4 path=
286 static bool dot_lsdirs(UAContext *ua, const char *cmd)
289 int limit=2000, offset=0;
290 char *path=NULL, *jobid=NULL;
292 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid,
295 ua->error_msg("Can't find jobid, pathid or path argument\n");
296 return true; /* not enough param */
299 Bvfs fs(ua->jcr, ua->db);
300 fs.set_jobids(jobid);
302 fs.set_handler(bvfs_result_handler, ua);
310 fs.set_offset(offset);
312 fs.ls_special_dirs();
318 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
324 static bool dot_help_cmd(UAContext *ua, const char *cmd)
330 static bool getmsgscmd(UAContext *ua, const char *cmd)
332 if (console_msg_pending) {
333 do_messages(ua, cmd);
339 static void do_storage_die(UAContext *ua, STORE *store)
345 lstore.store = store;
346 pm_strcpy(lstore.store_source, _("unknown source"));
347 set_wstorage(jcr, &lstore);
348 /* Try connecting for up to 15 seconds */
349 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
350 store->name(), store->address, store->SDport);
351 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
352 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
355 Dmsg0(120, _("Connected to storage daemon\n"));
356 sd = jcr->store_bsock;
358 if (sd->recv() >= 0) {
359 ua->send_msg("%s", sd->msg);
361 sd->signal(BNET_TERMINATE);
363 jcr->store_bsock = NULL;
367 static void do_client_die(UAContext *ua, CLIENT *client)
371 /* Connect to File daemon */
373 ua->jcr->client = client;
374 /* Try to connect for 15 seconds */
375 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
376 client->name(), client->address, client->FDport);
377 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
378 ua->error_msg(_("Failed to connect to Client.\n"));
381 Dmsg0(120, "Connected to file daemon\n");
382 fd = ua->jcr->file_bsock;
384 if (fd->recv() >= 0) {
385 ua->send_msg("%s", fd->msg);
387 fd->signal(BNET_TERMINATE);
389 ua->jcr->file_bsock = NULL;
394 * Create segmentation fault
396 static bool diecmd(UAContext *ua, const char *cmd)
404 Dmsg1(120, "diecmd:%s:\n", cmd);
407 for (i=1; i<ua->argc; i++) {
408 if (strcasecmp(ua->argk[i], "dir") == 0 ||
409 strcasecmp(ua->argk[i], "director") == 0) {
410 ua->send_msg(_("The Director will segment fault.\n"));
411 a = jcr->JobId; /* ref NULL pointer */
412 jcr->JobId = 1000; /* another ref NULL pointer */
415 if (strcasecmp(ua->argk[i], "client") == 0 ||
416 strcasecmp(ua->argk[i], "fd") == 0) {
419 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
421 do_client_die(ua, client);
425 client = select_client_resource(ua);
427 do_client_die(ua, client);
432 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
433 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
434 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
437 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
439 do_storage_die(ua, store);
443 store = get_storage_resource(ua, false/*no default*/);
445 do_storage_die(ua, store);
451 * We didn't find an appropriate keyword above, so
454 start_prompt(ua, _("Available daemons are: \n"));
455 add_prompt(ua, _("Director"));
456 add_prompt(ua, _("Storage"));
457 add_prompt(ua, _("Client"));
458 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
459 case 0: /* Director */
460 ua->send_msg(_("The Director will segment fault.\n"));
461 a = jcr->JobId; /* ref NULL pointer */
462 jcr->JobId = 1000; /* another ref NULL pointer */
465 store = get_storage_resource(ua, false/*no default*/);
467 do_storage_die(ua, store);
471 client = select_client_resource(ua);
473 do_client_die(ua, client);
485 * Dummy routine for non-development version
487 static bool diecmd(UAContext *ua, const char *cmd)
494 static bool jobscmd(UAContext *ua, const char *cmd)
498 foreach_res(job, R_JOB) {
499 if (acl_access_ok(ua, Job_ACL, job->name())) {
500 ua->send_msg("%s\n", job->name());
507 static bool filesetscmd(UAContext *ua, const char *cmd)
511 foreach_res(fs, R_FILESET) {
512 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
513 ua->send_msg("%s\n", fs->name());
520 static bool clientscmd(UAContext *ua, const char *cmd)
524 foreach_res(client, R_CLIENT) {
525 if (acl_access_ok(ua, Client_ACL, client->name())) {
526 ua->send_msg("%s\n", client->name());
533 static bool msgscmd(UAContext *ua, const char *cmd)
537 foreach_res(msgs, R_MSGS) {
538 ua->send_msg("%s\n", msgs->name());
544 static bool poolscmd(UAContext *ua, const char *cmd)
548 foreach_res(pool, R_POOL) {
549 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
550 ua->send_msg("%s\n", pool->name());
557 static bool storagecmd(UAContext *ua, const char *cmd)
561 foreach_res(store, R_STORAGE) {
562 if (acl_access_ok(ua, Storage_ACL, store->name())) {
563 ua->send_msg("%s\n", store->name());
571 static bool typescmd(UAContext *ua, const char *cmd)
573 ua->send_msg("Backup\n");
574 ua->send_msg("Restore\n");
575 ua->send_msg("Admin\n");
576 ua->send_msg("Verify\n");
577 ua->send_msg("Migrate\n");
583 * If this command is called, it tells the director that we
584 * are a program that wants a sort of API, and hence,
585 * we will probably suppress certain output, include more
586 * error codes, and most of all send back a good number
587 * of new signals that indicate whether or not the command
590 static bool api_cmd(UAContext *ua, const char *cmd)
593 ua->api = atoi(ua->argk[1]);
600 static int client_backups_handler(void *ctx, int num_field, char **row)
602 UAContext *ua = (UAContext *)ctx;
603 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
604 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
609 * Return the backups for this client
611 * .backups client=xxx fileset=yyy
614 static bool backupscmd(UAContext *ua, const char *cmd)
616 if (!open_client_db(ua)) {
619 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
620 strcmp(ua->argk[2], "fileset") != 0) {
623 if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
624 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
625 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
628 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
629 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
630 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
636 static int sql_handler(void *ctx, int num_field, char **row)
638 UAContext *ua = (UAContext *)ctx;
639 POOL_MEM rows(PM_MESSAGE);
641 /* Check for nonsense */
642 if (num_field == 0 || row == NULL || row[0] == NULL) {
643 return 0; /* nothing returned */
645 for (int i=0; num_field--; i++) {
647 pm_strcpy(rows, NPRT(row[0]));
649 pm_strcat(rows, NPRT(row[i]));
651 pm_strcat(rows, "\t");
653 if (!rows.c_str() || !*rows.c_str()) {
656 ua->send_msg("%s", rows.c_str());
661 static bool sql_cmd(UAContext *ua, const char *cmd)
664 if (!open_client_db(ua)) {
667 index = find_arg_with_value(ua, "query");
669 ua->error_msg(_("query keyword not found.\n"));
672 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
673 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
674 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
682 static bool levelscmd(UAContext *ua, const char *cmd)
684 ua->send_msg("Incremental\n");
685 ua->send_msg("Full\n");
686 ua->send_msg("Differential\n");
687 ua->send_msg("VirtualFull\n");
688 ua->send_msg("Catalog\n");
689 ua->send_msg("InitCatalog\n");
690 ua->send_msg("VolumeToCatalog\n");
695 * Return default values for a job
697 static bool defaultscmd(UAContext *ua, const char *cmd)
705 if (ua->argc != 2 || !ua->argv[1]) {
710 if (strcmp(ua->argk[1], "job") == 0) {
711 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
714 job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
717 ua->send_msg("job=%s", job->name());
718 ua->send_msg("pool=%s", job->pool->name());
719 ua->send_msg("messages=%s", job->messages->name());
720 ua->send_msg("client=%s", job->client->name());
721 get_job_storage(&store, job, NULL);
722 ua->send_msg("storage=%s", store.store->name());
723 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
724 ua->send_msg("level=%s", level_to_str(job->JobLevel));
725 ua->send_msg("type=%s", job_type_to_str(job->JobType));
726 ua->send_msg("fileset=%s", job->fileset->name());
727 ua->send_msg("enabled=%d", job->enabled);
728 ua->send_msg("catalog=%s", job->client->catalog->name());
731 /* Client defaults */
732 else if (strcmp(ua->argk[1], "client") == 0) {
733 if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
736 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
738 ua->send_msg("client=%s", client->name());
739 ua->send_msg("address=%s", client->address);
740 ua->send_msg("fdport=%d", client->FDport);
741 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
742 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
743 ua->send_msg("autoprune=%d", client->AutoPrune);
744 ua->send_msg("catalog=%s", client->catalog->name());
747 /* Storage defaults */
748 else if (strcmp(ua->argk[1], "storage") == 0) {
749 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
752 storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
755 ua->send_msg("storage=%s", storage->name());
756 ua->send_msg("address=%s", storage->address);
757 ua->send_msg("enabled=%d", storage->enabled);
758 ua->send_msg("media_type=%s", storage->media_type);
759 ua->send_msg("sdport=%d", storage->SDport);
760 device = (DEVICE *)storage->device->first();
761 ua->send_msg("device=%s", device->name());
762 if (storage->device->size() > 1) {
763 while ((device = (DEVICE *)storage->device->next())) {
764 ua->send_msg(",%s", device->name());
770 else if (strcmp(ua->argk[1], "pool") == 0) {
771 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
774 pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
776 ua->send_msg("pool=%s", pool->name());
777 ua->send_msg("pool_type=%s", pool->pool_type);
778 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
779 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
780 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
781 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
782 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
783 ua->send_msg("max_volumes=%d", pool->max_volumes);
784 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
785 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
786 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
787 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
788 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
789 ua->send_msg("auto_prune=%d", pool->AutoPrune);
790 ua->send_msg("recycle=%d", pool->Recycle);