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)
154 SNAPSHOT_DBR snapdbr;
158 if (!open_new_client_db(ua)) {
162 /* If the client or the fileset are not authorized,
163 * the function will fail.
165 if (!select_snapshot_dbr(ua, &snapdbr)) {
166 ua->error_msg(_("Snapshot not found\n"));
171 client = (CLIENT *)GetResWithName(R_CLIENT, snapdbr.Client);
173 ua->error_msg(_("Client resource not found\n"));
177 /* Connect to File daemon */
178 ua->jcr->client = client;
180 /* Try to connect for 15 seconds */
181 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
182 client->name(), client->address(buf.addr()), client->FDport);
183 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
184 ua->error_msg(_("Failed to connect to Client.\n"));
185 ua->jcr->client = NULL;
189 fd = ua->jcr->file_bsock;
190 out = get_pool_memory(PM_FNAME);
191 fd->fsend("snapshot del %s\n", snapdbr.as_arg(&out));
192 free_pool_memory(out);
194 /* If the snapshot is not found, still delete ours */
195 if (check_response(ua, fd, "2000 Snapshot deleted ERR=\n", "Snapshot")) {
196 ua->send_msg(_("Snapshot \"%s\" deleted from client %s\n"), snapdbr.Name,
200 ua->jcr->file_bsock->signal(BNET_TERMINATE);
201 free_bsock(ua->jcr->file_bsock);
202 ua->jcr->client = NULL;
204 db_delete_snapshot_record(ua->jcr, ua->db, &snapdbr);
205 ua->send_msg(_("Snapshot \"%s\" deleted from catalog\n"), snapdbr.Name);
209 /* Called from menu, if snap_list is valid, the snapshot
210 * list will be stored in this list. (not_owned_by_alist)
212 int list_snapshot(UAContext *ua, alist *snap_list)
220 client = select_client_resource(ua, JT_BACKUP_RESTORE);
225 /* Connect to File daemon */
226 ua->jcr->client = client;
228 /* Try to connect for 15 seconds */
229 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
230 client->name(), client->address(tmp.addr()), client->FDport);
232 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
233 ua->error_msg(_("Failed to connect to Client.\n"));
237 fd = ua->jcr->file_bsock;
239 /* The command line can have filters */
240 snapshot_scan_cmdline(ua, 0, &snap);
241 buf = get_pool_memory(PM_FNAME);
243 fd->fsend("snapshot list %s\n", snap.as_arg(&buf));
244 while (fd->recv() >= 0) {
246 SNAPSHOT_DBR *snapr = new SNAPSHOT_DBR();
247 parse_args(fd->msg, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
248 snapshot_scan_cmdline(ua, 0, snapr);
249 bstrncpy(snapr->Client, client->name(), sizeof(snapr->Client));
250 snap_list->append(snapr);
253 ua->send_msg("%s", fd->msg);
257 /* Reset the UA arg list */
258 parse_args(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
260 ua->jcr->file_bsock->signal(BNET_TERMINATE);
261 free_bsock(ua->jcr->file_bsock);
262 ua->jcr->client = NULL;
263 free_pool_memory(buf);
267 static void storeit(void *ctx, const char *msg)
270 alist *lst = (alist *)ctx;
271 if (sscanf(msg, "snapshotid=%50s", ed1) == 1) {
272 lst->append((void *)(intptr_t) str_to_int64(ed1));
276 int prune_snapshot(UAContext *ua)
278 /* First, we get the snapshot list that can be pruned */
279 CLIENT *client = NULL;
283 SNAPSHOT_DBR snapdbr;
287 snapshot_scan_cmdline(ua, 0, &snapdbr);
288 snapdbr.expired = true;
289 if (!open_client_db(ua)) {
290 Dmsg0(10, "Unable to open database\n");
294 buf = get_pool_memory(PM_FNAME);
295 lst = New(alist(10, not_owned_by_alist));
296 db_list_snapshot_records(ua->jcr, ua->db, &snapdbr, storeit, lst, ARG_LIST);
297 foreach_alist(id, lst) {
299 snapdbr.SnapshotId = id;
300 if (get_snapshot_record(ua, &snapdbr)) {
302 ua->send_msg(_("Snapshot \"%s\" on Client %s\n"), snapdbr.Name, snapdbr.Client);
303 if (!confirm_retention_yesno(ua, snapdbr.Retention, "Snapshot")) {
307 if (client && strcmp(client->hdr.name, snapdbr.Client) != 0) {
308 ua->jcr->file_bsock->signal(BNET_TERMINATE);
309 free_bsock(ua->jcr->file_bsock);
310 ua->jcr->client = NULL;
315 client = (CLIENT *)GetResWithName(R_CLIENT, snapdbr.Client);
320 /* Connect to File daemon */
321 ua->jcr->client = client;
323 /* Try to connect for 15 seconds */
324 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
325 client->name(), client->address(tmp.addr()), client->FDport);
326 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
327 ua->error_msg(_("Failed to connect to Client.\n"));
328 free_bsock(ua->jcr->file_bsock);
329 ua->jcr->client = NULL;
334 fd = ua->jcr->file_bsock;
337 fd->fsend("snapshot del %s\n", snapdbr.as_arg(&buf));
340 if (strncmp(fd->msg, "2000", 4) == 0) {
341 ua->send_msg("Snapshot %s deleted\n", snapdbr.Volume);
342 db_delete_snapshot_record(ua->jcr, ua->db, &snapdbr);
344 unbash_spaces(fd->msg);
345 ua->send_msg("%s", fd->msg);
350 if (ua->jcr->file_bsock) {
351 ua->jcr->file_bsock->signal(BNET_TERMINATE);
352 free_bsock(ua->jcr->file_bsock);
353 ua->jcr->client = NULL;
356 free_pool_memory(buf);
362 /* Called from the FD, in catreq.c */
363 int snapshot_catreq(JCR *jcr, BSOCK *bs)
365 SNAPSHOT_DBR snapdbr;
366 char Job[MAX_NAME_LENGTH], ed1[50];
367 POOLMEM *vol = get_memory(bs->msglen);
368 POOLMEM *dev = get_memory(bs->msglen);
369 POOLMEM *err = get_pool_memory(PM_MESSAGE);
370 int n, ret = 1, expired;
373 Dmsg1(DT_SNAPSHOT|10, "Get snapshot catalog request %s\n", bs->msg);
375 /* We need to create a snapshot record in the catalog */
376 n = sscanf(bs->msg, CreateSnap, Job, snapdbr.Name, vol, dev,
377 &snapdbr.CreateTDate, snapdbr.Type, ed1);
379 snapdbr.Volume = vol;
380 snapdbr.Device = dev;
381 snapdbr.JobId = jcr->JobId;
382 unbash_spaces(snapdbr.Name);
383 unbash_spaces(snapdbr.Volume);
384 unbash_spaces(snapdbr.Device);
385 snapdbr.Retention = str_to_uint64(ed1);
386 bstrftimes(snapdbr.CreateDate, sizeof(snapdbr.CreateDate), snapdbr.CreateTDate);
387 unbash_spaces(snapdbr.Type);
388 bstrncpy(snapdbr.Client, jcr->client->hdr.name, sizeof(snapdbr.Client));
389 bstrncpy(snapdbr.FileSet, (jcr->fileset)?jcr->fileset->hdr.name:"", sizeof(snapdbr.FileSet));
391 Dmsg1(DT_SNAPSHOT|10, "Creating snapshot %s\n", snapdbr.Name);
394 /* We lock the db before to keep the error message */
396 ret = db_create_snapshot_record(jcr, jcr->db, &snapdbr);
397 pm_strcpy(err, jcr->db->errmsg);
401 bs->fsend("1000 Snapshot created\n");
404 bs->fsend("1999 Snapshot not created ERR=%s\n", err);
409 n = sscanf(bs->msg, ListSnap, Job, snapdbr.Name, vol, dev, &snapdbr.CreateTDate, snapdbr.Type,
410 snapdbr.created_before, snapdbr.created_after, &expired);
412 snapdbr.Volume = vol;
413 snapdbr.Device = dev;
414 unbash_spaces(snapdbr.Name);
415 unbash_spaces(snapdbr.Volume);
416 unbash_spaces(snapdbr.Device);
417 bstrftimes(snapdbr.CreateDate, sizeof(snapdbr.CreateDate), snapdbr.CreateTDate);
418 unbash_spaces(snapdbr.Type);
419 unbash_spaces(snapdbr.created_before);
420 unbash_spaces(snapdbr.created_after);
421 bstrncpy(snapdbr.Client, jcr->client->hdr.name, sizeof(snapdbr.Client));
422 snapdbr.expired = (expired != 0);
423 Dmsg0(DT_SNAPSHOT|10, "List snapshots\n");
425 db_list_snapshot_records(jcr, jcr->db, &snapdbr, send_list, bs, ARG_LIST);
426 bs->signal(BNET_EOD);
430 n = sscanf(bs->msg, DelSnap, Job, snapdbr.Name, dev);
432 snapdbr.Device = dev;
433 unbash_spaces(snapdbr.Name);
434 unbash_spaces(snapdbr.Device);
435 bstrncpy(snapdbr.Client, jcr->client->hdr.name, sizeof(snapdbr.Client));
436 Dmsg2(DT_SNAPSHOT|10, "Delete snapshot %s from %s\n", snapdbr.Name, snapdbr.Client);
439 /* We lock the db before to keep the error message */
441 ret = db_delete_snapshot_record(jcr, jcr->db, &snapdbr);
442 pm_strcpy(err, jcr->db->errmsg);
446 bs->fsend("1000 Snapshot deleted\n");
449 bs->fsend("1999 Snapshot not deleted ERR=%s\n", err);
456 free_pool_memory(vol);
457 free_pool_memory(dev);
458 free_pool_memory(err);
462 /* List snapshots, allow to use some parameters from the command line */
463 void snapshot_list(UAContext *ua, int i, DB_LIST_HANDLER *sendit, e_list_type llist)
465 SNAPSHOT_DBR snapdbr;
466 snapshot_scan_cmdline(ua, i, &snapdbr);
467 if (open_new_client_db(ua)) {
468 db_list_snapshot_records(ua->jcr, ua->db, &snapdbr, sendit, ua, llist);
472 static int list_client_snapshot(UAContext *ua, bool sync)
474 SNAPSHOT_DBR *s, stemp;
479 if (!open_new_client_db(ua)) {
484 lst = New(alist(10, not_owned_by_alist));
485 if (list_snapshot(ua, lst)) {
486 foreach_alist(s, lst) {
496 s->Name, NPRT(s->Volume), NPRT(s->Device),
498 // edit_uint64_with_suffix(s->Size, ed1),
499 s->Type, s->status?_("OK"):_("Error"), s->errmsg);
500 if (sync && s->Device && *s->Name) {
502 stemp.Device = s->Device;
503 bstrncpy(stemp.Name, s->Name, sizeof(stemp.Name));
504 if (!db_get_snapshot_record(ua->jcr, ua->db, &stemp)) {
505 if (db_create_snapshot_record(ua->jcr, ua->db, s)) {
506 ua->send_msg(_("Snapshot added in Catalog\n"));
511 if (lst->size() == 0) {
512 ua->send_msg(_("No snapshot found\n"));
515 /* Cleanup the list */
516 foreach_alist (s, lst) {
523 /* Ask client to create/prune/delete a snapshot via the command line */
524 int snapshot_cmd(UAContext *ua, const char *cmd)
526 SNAPSHOT_DBR snapdbr;
527 for (int i=0; i<ua->argc; i++) {
528 if (strcasecmp(ua->argk[i], NT_("purge")) == 0) {
530 } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
531 return prune_snapshot(ua);
533 } else if (strcasecmp(ua->argk[i], NT_("listclient")) == 0) {
534 return list_client_snapshot(ua, false);
536 } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
537 snapshot_list(ua, 0, prtit, HORZ_LIST);
540 } else if (strcasecmp(ua->argk[i], NT_("create")) == 0) {
541 /* We need a job definition, or a client */
543 } else if (strcasecmp(ua->argk[i], NT_("delete")) == 0) {
544 return delete_snapshot(ua);
546 } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
548 } else if (strcasecmp(ua->argk[i], NT_("sync")) == 0) {
549 return list_client_snapshot(ua, true);
551 } else if (strcasecmp(ua->argk[i], NT_("update")) == 0) {
552 return update_snapshot(ua);
561 start_prompt(ua, _("Snapshot choice: \n"));
562 add_prompt(ua, _("List snapshots in Catalog"));
563 add_prompt(ua, _("List snapshots on Client"));
564 add_prompt(ua, _("Prune snapshots"));
565 add_prompt(ua, _("Delete snapshot"));
566 add_prompt(ua, _("Update snapshot parameters"));
567 add_prompt(ua, _("Update catalog with Client snapshots"));
568 add_prompt(ua, _("Done"));
570 switch(do_prompt(ua, "", _("Select action to perform on Snapshot Engine"), NULL, 0)) {
571 case 0: /* list catalog */
572 snapshot_list(ua, 0, prtit, HORZ_LIST);
574 case 1: /* list client */
575 list_client_snapshot(ua, false);
583 case 4: /* update snapshot */
586 case 5: /* sync snapshot */
587 list_client_snapshot(ua, true);
591 ua->info_msg(_("Selection terminated.\n"));
598 /* Select a Snapshot record from the database, might be in ua_select.c */
599 int select_snapshot_dbr(UAContext *ua, SNAPSHOT_DBR *sr)
603 POOLMEM *err = get_pool_memory(PM_FNAME);
607 snapshot_scan_cmdline(ua, 0, sr);
609 if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Client[0] == 0)) {
611 memset(&cr, 0, sizeof(cr));
612 /* Get the pool from client=<client-name> */
613 if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
616 sr->ClientId = cr.ClientId;
617 db_list_snapshot_records(ua->jcr, ua->db, sr, prtit, ua, HORZ_LIST);
618 if (!get_cmd(ua, _("Enter a SnapshotId: "))) {
625 if (is_a_number(p)) {
626 sr->SnapshotId = str_to_int64(p);
632 if (!get_snapshot_record(ua, sr)) {
633 ua->error_msg(_("Unable to get Snapshot record.\n"));
641 ua->error_msg("%s", err);
643 free_pool_memory(err);
647 /* This part should be in ua_update.c */
648 static void update_snapretention(UAContext *ua, char *val, SNAPSHOT_DBR *sr)
651 POOL_MEM tmp(PM_MESSAGE);
653 if (!duration_to_utime(val, &sr->Retention)) {
654 ua->error_msg(_("Invalid retention period specified: %s\n"), val);
659 if (!(ret = db_update_snapshot_record(ua->jcr, ua->db, sr))) {
660 pm_strcpy(tmp, db_strerror(ua->db));
665 ua->error_msg("%s", tmp.c_str());
668 ua->info_msg(_("New retention period is: %s\n"),
669 edit_utime(sr->Retention, ed1, sizeof(ed1)));
673 /* This part should be in ua_update.c */
674 static void update_snapcomment(UAContext *ua, char *val, SNAPSHOT_DBR *sr)
676 POOL_MEM tmp(PM_MESSAGE);
679 bstrncpy(sr->Comment, val, sizeof(sr->Comment));
682 if (!(ret = db_update_snapshot_record(ua->jcr, ua->db, sr))) {
683 pm_strcpy(tmp, db_strerror(ua->db));
688 ua->error_msg("%s", tmp.c_str());
691 ua->info_msg(_("New Comment is: %s\n"), sr->Comment);
695 /* This part should be in ua_update.c */
696 bool update_snapshot(UAContext *ua)
704 NT_("Retention"), /* 0 */
705 NT_("Comment"), /* 1 */
708 for (i=0; kw[i]; i++) {
710 if ((j=find_arg_with_value(ua, kw[i])) > 0) {
711 /* If all from pool don't select a media record */
712 if (!select_snapshot_dbr(ua, &sr)) {
717 update_snapretention(ua, ua->argv[j], &sr);
720 update_snapcomment(ua, ua->argv[j], &sr);
730 start_prompt(ua, _("Parameters to modify:\n"));
731 add_prompt(ua, _("Snapshot Retention Period")); /* 0 */
732 add_prompt(ua, _("Snapshot Comment")); /* 1 */
733 add_prompt(ua, _("Done")); /* 2 */
734 i = do_prompt(ua, "", _("Select parameter to modify"), NULL, 0);
739 if (!select_snapshot_dbr(ua, &sr)) { /* Get Snapshot record */
742 ua->info_msg(_("Updating Snapshot \"%s\" on \"%s\"\n"), sr.Name, sr.Client);
745 case 0: /* Snapshot retention */
746 ua->info_msg(_("Current retention period is: %s\n"),
747 edit_utime(sr.Retention, ed1, sizeof(ed1)));
748 if (!get_cmd(ua, _("Enter Snapshot Retention period: "))) {
751 update_snapretention(ua, ua->cmd, &sr);
754 ua->info_msg(_("Current comment is: %s\n"), NPRTB(sr.Comment));
755 if (!get_cmd(ua, _("Enter Snapshot comment: "))) {
758 update_snapcomment(ua, ua->cmd, &sr);
760 default: /* Done or error */
761 ua->info_msg(_("Selection terminated.\n"));