2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
23 static char CreateSnap[] = "CatReq Job=%127s new_snapshot name=%127s volume=%s device=%s tdate=%d type=%127s retention=%50s";
24 static char ListSnap[] = "CatReq Job=%127s list_snapshot name=%127s volume=%s device=%s tdate=%d type=%127s before=%50s after=%50s";
25 static char DelSnap[] = "CatReq Job=%127s del_snapshot name=%127s device=%s";
26 static char snapretentioncmd[] = "snapshot retention=%s\n";
29 static void send_list(void *ctx, const char *msg)
31 BSOCK *bs = (BSOCK *)ctx;
36 /* Scan command line for common snapshot arguments */
37 static void snapshot_scan_cmdline(UAContext *ua, int start, SNAPSHOT_DBR *snapdbr)
39 for (int j=start; j<ua->argc; j++) {
40 if (strcasecmp(ua->argk[j], NT_("device")) == 0 && ua->argv[j]) {
41 snapdbr->Device = bstrdup(ua->argv[j]);
42 snapdbr->need_to_free = true;
44 } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
45 snapdbr->JobId = str_to_int64(ua->argv[j]);
47 } else if (strcasecmp(ua->argk[j], NT_("type")) == 0 && ua->argv[j]) {
48 bstrncpy(snapdbr->Type, ua->argv[j], sizeof(snapdbr->Type));
50 } else if (strcasecmp(ua->argk[j], NT_("client")) == 0 && ua->argv[j]) {
51 bstrncpy(snapdbr->Client, ua->argv[j], sizeof(snapdbr->Client));
53 } else if (strcasecmp(ua->argk[j], NT_("snapshotid")) == 0 && ua->argv[j]) {
54 snapdbr->SnapshotId = str_to_int64(ua->argv[j]);
56 } else if (strcasecmp(ua->argk[j], NT_("snapshot")) == 0 && ua->argv[j]) {
57 bstrncpy(snapdbr->Name, ua->argv[j], sizeof(snapdbr->Name));
59 } else if (strcasecmp(ua->argk[j], NT_("volume")) == 0 && ua->argv[j]) {
60 snapdbr->Volume = bstrdup(ua->argv[j]);
61 snapdbr->need_to_free = true;
63 } else if (strcasecmp(ua->argk[j], NT_("createdate")) == 0 && ua->argv[j]) {
64 bstrncpy(snapdbr->CreateDate, ua->argv[j], sizeof(snapdbr->CreateDate));
65 snapdbr->CreateTDate = str_to_utime(ua->argv[j]);
67 } else if (strcasecmp(ua->argk[j], NT_("createtdate")) == 0 && ua->argv[j]) {
68 snapdbr->CreateTDate = str_to_uint64(ua->argv[j]);
69 bstrutime(snapdbr->CreateDate, sizeof(snapdbr->CreateDate), snapdbr->CreateTDate);
71 } else if (strcasecmp(ua->argk[j], NT_("name")) == 0 && ua->argv[j]) {
72 bstrncpy(snapdbr->Name, ua->argv[j], sizeof(snapdbr->Name));
74 } else if (strcasecmp(ua->argk[j], NT_("size")) == 0 && ua->argv[j]) {
75 snapdbr->Size = str_to_uint64(ua->argv[j]);
77 } else if (strcasecmp(ua->argk[j], NT_("status")) == 0 && ua->argv[j]) {
78 snapdbr->status = str_to_uint64(ua->argv[j]);
80 } else if (strcasecmp(ua->argk[j], NT_("error")) == 0 && ua->argv[j]) {
81 snapdbr->errmsg = bstrdup(ua->argv[j]);
82 unbash_spaces(snapdbr->errmsg);
83 snapdbr->need_to_free = true;
91 /* Get a snapshot record, and check that the current UA can access to the Client/FileSet */
92 static int get_snapshot_record(UAContext *ua, SNAPSHOT_DBR *snapdbr)
94 if (!open_client_db(ua)) {
95 Dmsg0(10, "Unable to open database\n");
98 if (!db_get_snapshot_record(ua->jcr, ua->db, snapdbr)) {
99 Dmsg0(10, "Unable to get snapshot record\n");
102 /* Need to check if the client is authorized */
103 if (!acl_access_client_ok(ua, snapdbr->Client, JT_BACKUP_RESTORE)) {
104 Dmsg0(10, "Client access denied\n");
107 if (snapdbr->FileSetId && !acl_access_ok(ua, FileSet_ACL, snapdbr->FileSet)) {
108 Dmsg0(10, "Fileset access denied\n");
114 static int check_response(UAContext *ua, BSOCK *sd, const char *resp, const char *cmd)
119 if (bget_msg(sd) > 0) {
120 unbash_spaces(sd->msg);
121 if (strcmp(sd->msg, resp) == 0) {
125 if (sd->is_error()) {
126 ua->error_msg(_("Comm error with SD. bad response to %s. ERR=%s\n"),
127 cmd, sd->bstrerror());
129 ua->error_msg(_("Bad response from SD to %s command. Wanted %s, got %s len=%ld\n"),
130 cmd, resp, sd->msg, sd->msglen);
135 bool send_snapshot_retention(JCR *jcr, utime_t val)
137 BSOCK *fd = jcr->file_bsock;
139 if (val > 0 && jcr->FDVersion >= 13) {
140 fd->fsend(snapretentioncmd, edit_uint64(val, ed1));
141 if (!response(jcr, fd, (char*)"2000 Snapshot retention\n", "set Snapshot Retention", DISPLAY_ERROR)) {
142 jcr->snapshot_retention = 0; /* can't set snapshot retention */
149 /* Called from delete_cmd() in ua_cmd.c */
150 int delete_snapshot(UAContext *ua)
153 SNAPSHOT_DBR snapdbr;
157 if (!open_new_client_db(ua)) {
161 /* If the client or the fileset are not authorized,
162 * the function will fail.
164 if (!select_snapshot_dbr(ua, &snapdbr)) {
165 ua->error_msg(_("Snapshot not found\n"));
170 client = (CLIENT *)GetResWithName(R_CLIENT, snapdbr.Client);
172 ua->error_msg(_("Client resource not found\n"));
176 /* Connect to File daemon */
177 ua->jcr->client = client;
179 /* Try to connect for 15 seconds */
180 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
181 client->name(), client->address(), client->FDport);
182 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
183 ua->error_msg(_("Failed to connect to Client.\n"));
184 ua->jcr->client = NULL;
188 fd = ua->jcr->file_bsock;
189 out = get_pool_memory(PM_FNAME);
190 fd->fsend("snapshot del %s\n", snapdbr.as_arg(&out));
191 free_pool_memory(out);
193 /* If the snapshot is not found, still delete ours */
194 if (check_response(ua, fd, "2000 Snapshot deleted ERR=\n", "Snapshot")) {
195 ua->send_msg(_("Snapshot \"%s\" deleted from client %s\n"), snapdbr.Name,
199 ua->jcr->file_bsock->signal(BNET_TERMINATE);
200 free_bsock(ua->jcr->file_bsock);
201 ua->jcr->client = NULL;
203 db_delete_snapshot_record(ua->jcr, ua->db, &snapdbr);
204 ua->send_msg(_("Snapshot \"%s\" deleted from catalog\n"), snapdbr.Name);
208 /* Called from menu, if snap_list is valid, the snapshot
209 * list will be stored in this list. (not_owned_by_alist)
211 int list_snapshot(UAContext *ua, alist *snap_list)
218 client = select_client_resource(ua, JT_BACKUP_RESTORE);
223 /* Connect to File daemon */
224 ua->jcr->client = client;
226 /* Try to connect for 15 seconds */
227 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
228 client->name(), client->address(), client->FDport);
229 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
230 ua->error_msg(_("Failed to connect to Client.\n"));
234 fd = ua->jcr->file_bsock;
236 /* The command line can have filters */
237 snapshot_scan_cmdline(ua, 0, &snap);
238 buf = get_pool_memory(PM_FNAME);
240 fd->fsend("snapshot list %s\n", snap.as_arg(&buf));
241 while (fd->recv() >= 0) {
243 SNAPSHOT_DBR *snapr = new SNAPSHOT_DBR();
244 parse_args(fd->msg, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
245 snapshot_scan_cmdline(ua, 0, snapr);
246 bstrncpy(snapr->Client, client->name(), sizeof(snapr->Client));
247 snap_list->append(snapr);
250 ua->send_msg("%s", fd->msg);
254 /* Reset the UA arg list */
255 parse_args(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
257 ua->jcr->file_bsock->signal(BNET_TERMINATE);
258 free_bsock(ua->jcr->file_bsock);
259 ua->jcr->client = NULL;
260 free_pool_memory(buf);
264 static void storeit(void *ctx, const char *msg)
267 alist *lst = (alist *)ctx;
268 if (sscanf(msg, "snapshotid=%50s", ed1) == 1) {
269 lst->append((void *)(intptr_t) str_to_int64(ed1));
273 int prune_snapshot(UAContext *ua)
275 /* First, we get the snapshot list that can be pruned */
276 CLIENT *client = NULL;
279 SNAPSHOT_DBR snapdbr;
283 snapshot_scan_cmdline(ua, 0, &snapdbr);
284 snapdbr.expired = true;
285 if (!open_client_db(ua)) {
286 Dmsg0(10, "Unable to open database\n");
290 buf = get_pool_memory(PM_FNAME);
291 lst = New(alist(10, not_owned_by_alist));
292 db_list_snapshot_records(ua->jcr, ua->db, &snapdbr, storeit, lst, ARG_LIST);
293 foreach_alist(id, lst) {
295 snapdbr.SnapshotId = id;
296 if (get_snapshot_record(ua, &snapdbr)) {
298 ua->send_msg(_("Snapshot \"%s\" on Client %s\n"), snapdbr.Name, snapdbr.Client);
299 if (!confirm_retention_yesno(ua, snapdbr.Retention, "Snapshot")) {
303 if (client && strcmp(client->hdr.name, snapdbr.Client) != 0) {
304 ua->jcr->file_bsock->signal(BNET_TERMINATE);
305 free_bsock(ua->jcr->file_bsock);
306 ua->jcr->client = NULL;
311 client = (CLIENT *)GetResWithName(R_CLIENT, snapdbr.Client);
316 /* Connect to File daemon */
317 ua->jcr->client = client;
319 /* Try to connect for 15 seconds */
320 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
321 client->name(), client->address(), client->FDport);
322 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
323 ua->error_msg(_("Failed to connect to Client.\n"));
324 free_bsock(ua->jcr->file_bsock);
325 ua->jcr->client = NULL;
330 fd = ua->jcr->file_bsock;
333 fd->fsend("snapshot del %s\n", snapdbr.as_arg(&buf));
336 if (strncmp(fd->msg, "2000", 4) == 0) {
337 ua->send_msg("Snapshot %s deleted\n", snapdbr.Volume);
338 db_delete_snapshot_record(ua->jcr, ua->db, &snapdbr);
340 unbash_spaces(fd->msg);
341 ua->send_msg("%s", fd->msg);
346 if (ua->jcr->file_bsock) {
347 ua->jcr->file_bsock->signal(BNET_TERMINATE);
348 free_bsock(ua->jcr->file_bsock);
349 ua->jcr->client = NULL;
352 free_pool_memory(buf);
358 /* Called from the FD, in catreq.c */
359 int snapshot_catreq(JCR *jcr, BSOCK *bs)
361 SNAPSHOT_DBR snapdbr;
362 char Job[MAX_NAME_LENGTH], ed1[50];
363 POOLMEM *vol = get_memory(bs->msglen);
364 POOLMEM *dev = get_memory(bs->msglen);
365 POOLMEM *err = get_pool_memory(PM_MESSAGE);
366 int n, ret = 1, expired;
369 Dmsg1(DT_SNAPSHOT|10, "Get snapshot catalog request %s\n", bs->msg);
371 /* We need to create a snapshot record in the catalog */
372 n = sscanf(bs->msg, CreateSnap, Job, snapdbr.Name, vol, dev,
373 &snapdbr.CreateTDate, snapdbr.Type, ed1);
375 snapdbr.Volume = vol;
376 snapdbr.Device = dev;
377 snapdbr.JobId = jcr->JobId;
378 unbash_spaces(snapdbr.Name);
379 unbash_spaces(snapdbr.Volume);
380 unbash_spaces(snapdbr.Device);
381 snapdbr.Retention = str_to_uint64(ed1);
382 bstrftimes(snapdbr.CreateDate, sizeof(snapdbr.CreateDate), snapdbr.CreateTDate);
383 unbash_spaces(snapdbr.Type);
384 bstrncpy(snapdbr.Client, jcr->client->hdr.name, sizeof(snapdbr.Client));
385 bstrncpy(snapdbr.FileSet, (jcr->fileset)?jcr->fileset->hdr.name:"", sizeof(snapdbr.FileSet));
387 Dmsg1(DT_SNAPSHOT|10, "Creating snapshot %s\n", snapdbr.Name);
390 /* We lock the db before to keep the error message */
392 ret = db_create_snapshot_record(jcr, jcr->db, &snapdbr);
393 pm_strcpy(err, jcr->db->errmsg);
397 bs->fsend("1000 Snapshot created\n");
400 bs->fsend("1999 Snapshot not created ERR=%s\n", err);
405 n = sscanf(bs->msg, ListSnap, Job, snapdbr.Name, vol, dev, &snapdbr.CreateTDate, snapdbr.Type,
406 snapdbr.created_before, snapdbr.created_after, &expired);
408 snapdbr.Volume = vol;
409 snapdbr.Device = dev;
410 unbash_spaces(snapdbr.Name);
411 unbash_spaces(snapdbr.Volume);
412 unbash_spaces(snapdbr.Device);
413 bstrftimes(snapdbr.CreateDate, sizeof(snapdbr.CreateDate), snapdbr.CreateTDate);
414 unbash_spaces(snapdbr.Type);
415 unbash_spaces(snapdbr.created_before);
416 unbash_spaces(snapdbr.created_after);
417 bstrncpy(snapdbr.Client, jcr->client->hdr.name, sizeof(snapdbr.Client));
418 snapdbr.expired = (expired != 0);
419 Dmsg0(DT_SNAPSHOT|10, "List snapshots\n");
421 db_list_snapshot_records(jcr, jcr->db, &snapdbr, send_list, bs, ARG_LIST);
422 bs->signal(BNET_EOD);
426 n = sscanf(bs->msg, DelSnap, Job, snapdbr.Name, dev);
428 snapdbr.Device = dev;
429 unbash_spaces(snapdbr.Name);
430 unbash_spaces(snapdbr.Device);
431 bstrncpy(snapdbr.Client, jcr->client->hdr.name, sizeof(snapdbr.Client));
432 Dmsg2(DT_SNAPSHOT|10, "Delete snapshot %s from %s\n", snapdbr.Name, snapdbr.Client);
435 /* We lock the db before to keep the error message */
437 ret = db_delete_snapshot_record(jcr, jcr->db, &snapdbr);
438 pm_strcpy(err, jcr->db->errmsg);
442 bs->fsend("1000 Snapshot deleted\n");
445 bs->fsend("1999 Snapshot not deleted ERR=%s\n", err);
452 free_pool_memory(vol);
453 free_pool_memory(dev);
454 free_pool_memory(err);
458 /* List snapshots, allow to use some parameters from the command line */
459 void snapshot_list(UAContext *ua, int i, DB_LIST_HANDLER *sendit, e_list_type llist)
461 SNAPSHOT_DBR snapdbr;
462 snapshot_scan_cmdline(ua, i, &snapdbr);
463 if (open_new_client_db(ua)) {
464 db_list_snapshot_records(ua->jcr, ua->db, &snapdbr, sendit, ua, llist);
468 static int list_client_snapshot(UAContext *ua, bool sync)
470 SNAPSHOT_DBR *s, stemp;
475 if (!open_new_client_db(ua)) {
480 lst = New(alist(10, not_owned_by_alist));
481 if (list_snapshot(ua, lst)) {
482 foreach_alist(s, lst) {
492 s->Name, NPRT(s->Volume), NPRT(s->Device),
494 // edit_uint64_with_suffix(s->Size, ed1),
495 s->Type, s->status?_("OK"):_("Error"), s->errmsg);
496 if (sync && s->Device && *s->Name) {
498 stemp.Device = s->Device;
499 bstrncpy(stemp.Name, s->Name, sizeof(stemp.Name));
500 if (!db_get_snapshot_record(ua->jcr, ua->db, &stemp)) {
501 if (db_create_snapshot_record(ua->jcr, ua->db, s)) {
502 ua->send_msg(_("Snapshot added in Catalog\n"));
507 if (lst->size() == 0) {
508 ua->send_msg(_("No snapshot found\n"));
511 /* Cleanup the list */
512 foreach_alist (s, lst) {
519 /* Ask client to create/prune/delete a snapshot via the command line */
520 int snapshot_cmd(UAContext *ua, const char *cmd)
522 SNAPSHOT_DBR snapdbr;
523 for (int i=0; i<ua->argc; i++) {
524 if (strcasecmp(ua->argk[i], NT_("purge")) == 0) {
526 } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
527 return prune_snapshot(ua);
529 } else if (strcasecmp(ua->argk[i], NT_("listclient")) == 0) {
530 return list_client_snapshot(ua, false);
532 } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
533 snapshot_list(ua, 0, prtit, HORZ_LIST);
536 } else if (strcasecmp(ua->argk[i], NT_("create")) == 0) {
537 /* We need a job definition, or a client */
539 } else if (strcasecmp(ua->argk[i], NT_("delete")) == 0) {
540 return delete_snapshot(ua);
542 } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
544 } else if (strcasecmp(ua->argk[i], NT_("sync")) == 0) {
545 return list_client_snapshot(ua, true);
547 } else if (strcasecmp(ua->argk[i], NT_("update")) == 0) {
548 return update_snapshot(ua);
557 start_prompt(ua, _("Snapshot choice: \n"));
558 add_prompt(ua, _("List snapshots in Catalog"));
559 add_prompt(ua, _("List snapshots on Client"));
560 add_prompt(ua, _("Prune snapshots"));
561 add_prompt(ua, _("Delete snapshot"));
562 add_prompt(ua, _("Update snapshot parameters"));
563 add_prompt(ua, _("Update catalog with Client snapshots"));
564 add_prompt(ua, _("Done"));
566 switch(do_prompt(ua, "", _("Select action to perform on Snapshot Engine"), NULL, 0)) {
567 case 0: /* list catalog */
568 snapshot_list(ua, 0, prtit, HORZ_LIST);
570 case 1: /* list client */
571 list_client_snapshot(ua, false);
579 case 4: /* update snapshot */
582 case 5: /* sync snapshot */
583 list_client_snapshot(ua, true);
587 ua->info_msg(_("Selection terminated.\n"));
594 /* Select a Snapshot record from the database, might be in ua_select.c */
595 int select_snapshot_dbr(UAContext *ua, SNAPSHOT_DBR *sr)
599 POOLMEM *err = get_pool_memory(PM_FNAME);
603 snapshot_scan_cmdline(ua, 0, sr);
605 if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Client[0] == 0)) {
607 memset(&cr, 0, sizeof(cr));
608 /* Get the pool from client=<client-name> */
609 if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
612 sr->ClientId = cr.ClientId;
613 db_list_snapshot_records(ua->jcr, ua->db, sr, prtit, ua, HORZ_LIST);
614 if (!get_cmd(ua, _("Enter a SnapshotId: "))) {
621 if (is_a_number(p)) {
622 sr->SnapshotId = str_to_int64(p);
628 if (!get_snapshot_record(ua, sr)) {
629 ua->error_msg(_("Unable to get Snapshot record.\n"));
637 ua->error_msg("%s", err);
639 free_pool_memory(err);
643 /* This part should be in ua_update.c */
644 static void update_snapretention(UAContext *ua, char *val, SNAPSHOT_DBR *sr)
647 POOL_MEM tmp(PM_MESSAGE);
649 if (!duration_to_utime(val, &sr->Retention)) {
650 ua->error_msg(_("Invalid retention period specified: %s\n"), val);
655 if (!(ret = db_update_snapshot_record(ua->jcr, ua->db, sr))) {
656 pm_strcpy(tmp, db_strerror(ua->db));
661 ua->error_msg("%s", tmp.c_str());
664 ua->info_msg(_("New retention period is: %s\n"),
665 edit_utime(sr->Retention, ed1, sizeof(ed1)));
669 /* This part should be in ua_update.c */
670 static void update_snapcomment(UAContext *ua, char *val, SNAPSHOT_DBR *sr)
672 POOL_MEM tmp(PM_MESSAGE);
675 bstrncpy(sr->Comment, val, sizeof(sr->Comment));
678 if (!(ret = db_update_snapshot_record(ua->jcr, ua->db, sr))) {
679 pm_strcpy(tmp, db_strerror(ua->db));
684 ua->error_msg("%s", tmp.c_str());
687 ua->info_msg(_("New Comment is: %s\n"), sr->Comment);
691 /* This part should be in ua_update.c */
692 bool update_snapshot(UAContext *ua)
700 NT_("Retention"), /* 0 */
701 NT_("Comment"), /* 1 */
704 for (i=0; kw[i]; i++) {
706 if ((j=find_arg_with_value(ua, kw[i])) > 0) {
707 /* If all from pool don't select a media record */
708 if (!select_snapshot_dbr(ua, &sr)) {
713 update_snapretention(ua, ua->argv[j], &sr);
716 update_snapcomment(ua, ua->argv[j], &sr);
726 start_prompt(ua, _("Parameters to modify:\n"));
727 add_prompt(ua, _("Snapshot Retention Period")); /* 0 */
728 add_prompt(ua, _("Snapshot Comment")); /* 1 */
729 add_prompt(ua, _("Done")); /* 2 */
730 i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);
735 if (!select_snapshot_dbr(ua, &sr)) { /* Get Snapshot record */
738 ua->info_msg(_("Updating Snapshot \"%s\" on \"%s\"\n"), sr.Name, sr.Client);
741 case 0: /* Snapshot retention */
742 ua->info_msg(_("Current retention period is: %s\n"),
743 edit_utime(sr.Retention, ed1, sizeof(ed1)));
744 if (!get_cmd(ua, _("Enter Snapshot Retention period: "))) {
747 update_snapretention(ua, ua->cmd, &sr);
750 ua->info_msg(_("Current comment is: %s\n"), NPRTB(sr.Comment));
751 if (!get_cmd(ua, _("Enter Snapshot comment: "))) {
754 update_snapcomment(ua, ua->cmd, &sr);
756 default: /* Done or error */
757 ua->info_msg(_("Selection terminated.\n"));