]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/snapshot.c
Fix race condition between setip and the access to CLIENT::address()
[bacula/bacula] / bacula / src / dird / snapshot.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
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.
8
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.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19
20 #include "bacula.h"
21 #include "dird.h"
22
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";
27
28
29 static void send_list(void *ctx, const char *msg)
30 {
31    BSOCK *bs = (BSOCK *)ctx;
32    bs->fsend("%s", msg);
33 }
34
35
36 /* Scan command line for common snapshot arguments */
37 static void snapshot_scan_cmdline(UAContext *ua, int start, SNAPSHOT_DBR *snapdbr)
38 {
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;
43
44       } else if (strcasecmp(ua->argk[j], NT_("jobid")) == 0 && ua->argv[j]) {
45          snapdbr->JobId = str_to_int64(ua->argv[j]);
46
47       } else if (strcasecmp(ua->argk[j], NT_("type")) == 0 && ua->argv[j]) {
48          bstrncpy(snapdbr->Type, ua->argv[j], sizeof(snapdbr->Type));
49
50       } else if (strcasecmp(ua->argk[j], NT_("client")) == 0 && ua->argv[j]) {
51          bstrncpy(snapdbr->Client, ua->argv[j], sizeof(snapdbr->Client));
52
53       } else if (strcasecmp(ua->argk[j], NT_("snapshotid")) == 0 && ua->argv[j]) {
54          snapdbr->SnapshotId = str_to_int64(ua->argv[j]);
55
56       } else if (strcasecmp(ua->argk[j], NT_("snapshot")) == 0 && ua->argv[j]) {
57          bstrncpy(snapdbr->Name, ua->argv[j], sizeof(snapdbr->Name));
58
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;
62
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]);
66
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);
70
71       } else if (strcasecmp(ua->argk[j], NT_("name")) == 0 && ua->argv[j]) {
72          bstrncpy(snapdbr->Name, ua->argv[j], sizeof(snapdbr->Name));
73
74       } else if (strcasecmp(ua->argk[j], NT_("size")) == 0 && ua->argv[j]) {
75          snapdbr->Size = str_to_uint64(ua->argv[j]);
76
77       } else if (strcasecmp(ua->argk[j], NT_("status")) == 0 && ua->argv[j]) {
78          snapdbr->status = str_to_uint64(ua->argv[j]);
79
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;
84
85       } else {
86          continue;
87       }
88    }
89 }
90
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)
93 {
94    if (!open_client_db(ua)) {
95       Dmsg0(10, "Unable to open database\n");
96       return 0;
97    }
98    if (!db_get_snapshot_record(ua->jcr, ua->db, snapdbr)) {
99       Dmsg0(10, "Unable to get snapshot record\n");
100       return 0;
101    }
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");
105       return 0;
106    }
107    if (snapdbr->FileSetId && !acl_access_ok(ua, FileSet_ACL, snapdbr->FileSet)) {
108       Dmsg0(10, "Fileset access denied\n");
109       return 0;
110    }
111    return 1;
112 }
113
114 static int check_response(UAContext *ua, BSOCK *sd, const char *resp, const char *cmd)
115 {
116    if (sd->errors) {
117       return 0;
118    }
119    if (bget_msg(sd) > 0) {
120       unbash_spaces(sd->msg);
121       if (strcmp(sd->msg, resp) == 0) {
122          return 1;
123       }
124    }
125    if (sd->is_error()) {
126       ua->error_msg(_("Comm error with SD. bad response to %s. ERR=%s\n"),
127                     cmd, sd->bstrerror());
128    } else {
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);
131    }
132    return 0;
133 }
134
135 bool send_snapshot_retention(JCR *jcr, utime_t val)
136 {
137    BSOCK *fd = jcr->file_bsock;
138    char ed1[50];
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 */
143          return false;
144       }
145    }
146    return true;
147 }
148
149 /* Called from delete_cmd() in ua_cmd.c */
150 int delete_snapshot(UAContext *ua)
151 {
152    POOL_MEM     buf;
153    POOLMEM     *out;
154    SNAPSHOT_DBR snapdbr;
155    CLIENT      *client;
156    BSOCK       *fd;
157
158    if (!open_new_client_db(ua)) {
159       return 1;
160    }
161
162    /* If the client or the fileset are not authorized,
163     * the function will fail.
164     */
165    if (!select_snapshot_dbr(ua, &snapdbr)) {
166       ua->error_msg(_("Snapshot not found\n"));
167       snapdbr.debug(0);
168       return 0;
169    }
170
171    client = (CLIENT *)GetResWithName(R_CLIENT, snapdbr.Client);
172    if (!client) {
173       ua->error_msg(_("Client resource not found\n"));
174       return 0;
175    }
176
177    /* Connect to File daemon */
178    ua->jcr->client = client;
179
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;
186       return 0;
187    }
188
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);
193
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,
197                    snapdbr.Client);
198    }
199
200    ua->jcr->file_bsock->signal(BNET_TERMINATE);
201    free_bsock(ua->jcr->file_bsock);
202    ua->jcr->client = NULL;
203
204    db_delete_snapshot_record(ua->jcr, ua->db, &snapdbr);
205    ua->send_msg(_("Snapshot \"%s\" deleted from catalog\n"), snapdbr.Name);
206    return 1;
207 }
208
209 /* Called from menu, if snap_list is valid, the snapshot
210  * list will be stored in this list. (not_owned_by_alist)
211  */
212 int list_snapshot(UAContext *ua, alist *snap_list)
213 {
214    POOL_MEM     tmp;
215    SNAPSHOT_DBR snap;
216    POOLMEM     *buf;
217    CLIENT      *client;
218    BSOCK       *fd;
219
220    client = select_client_resource(ua, JT_BACKUP_RESTORE);
221    if (!client) {
222       return 0;
223    }
224
225    /* Connect to File daemon */
226    ua->jcr->client = client;
227
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);
231
232    if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
233       ua->error_msg(_("Failed to connect to Client.\n"));
234       return 0;
235    }
236
237    fd = ua->jcr->file_bsock;
238
239    /* The command line can have filters */
240    snapshot_scan_cmdline(ua, 0, &snap);
241    buf = get_pool_memory(PM_FNAME);
242
243    fd->fsend("snapshot list %s\n", snap.as_arg(&buf));
244    while (fd->recv() >= 0) {
245       if (snap_list) {
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);
251          snapr->debug(0);
252       } else {
253          ua->send_msg("%s", fd->msg);
254       }
255    }
256
257    /* Reset the UA arg list */
258    parse_args(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
259
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);
264    return 1;
265 }
266
267 static void storeit(void *ctx, const char *msg)
268 {
269    char ed1[51];
270    alist *lst = (alist *)ctx;
271    if (sscanf(msg, "snapshotid=%50s", ed1) == 1) {
272       lst->append((void *)(intptr_t) str_to_int64(ed1));
273    }
274 }
275
276 int prune_snapshot(UAContext *ua)
277 {
278    /* First, we get the snapshot list that can be pruned */
279    CLIENT *client = NULL;
280    BSOCK  *fd = NULL;
281    POOLMEM *buf = NULL;
282    POOL_MEM tmp;
283    SNAPSHOT_DBR snapdbr;
284    alist *lst;
285    intptr_t id;
286
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");
291       return 0;
292    }
293
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) {
298       snapdbr.reset();
299       snapdbr.SnapshotId = id;
300       if (get_snapshot_record(ua, &snapdbr)) {
301
302          ua->send_msg(_("Snapshot \"%s\" on Client %s\n"), snapdbr.Name, snapdbr.Client);
303          if (!confirm_retention_yesno(ua, snapdbr.Retention, "Snapshot")) {
304             continue;
305          }
306
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;
311             client = NULL;
312          }
313
314          if (!client) {
315             client = (CLIENT *)GetResWithName(R_CLIENT, snapdbr.Client);
316             if (!client) {
317                continue;
318             }
319
320             /* Connect to File daemon */
321             ua->jcr->client = client;
322
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;
330                client = NULL;
331                continue;
332             }
333
334             fd = ua->jcr->file_bsock;
335          }
336
337          fd->fsend("snapshot del %s\n", snapdbr.as_arg(&buf));
338
339          fd->recv();
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);
343          } else {
344             unbash_spaces(fd->msg);
345             ua->send_msg("%s", fd->msg);
346          }
347       }
348    }
349
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;
354    }
355
356    free_pool_memory(buf);
357    delete lst;
358    return 1;
359 }
360
361
362 /* Called from the FD, in catreq.c */
363 int snapshot_catreq(JCR *jcr, BSOCK *bs)
364 {
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;
371    *vol = *dev = 0;
372
373    Dmsg1(DT_SNAPSHOT|10, "Get snapshot catalog request %s\n", bs->msg);
374
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);
378    if (n == 7) {
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));
390
391       Dmsg1(DT_SNAPSHOT|10, "Creating snapshot %s\n", snapdbr.Name);
392       snapdbr.debug(20);
393
394       /* We lock the db before to keep the error message */
395       db_lock(jcr->db);
396       ret = db_create_snapshot_record(jcr, jcr->db, &snapdbr);
397       pm_strcpy(err, jcr->db->errmsg);
398       db_unlock(jcr->db);
399
400       if (ret) {
401          bs->fsend("1000 Snapshot created\n");
402
403       } else {
404          bs->fsend("1999 Snapshot not created ERR=%s\n", err);
405       }
406       goto bail_out;
407    }
408
409    n = sscanf(bs->msg, ListSnap, Job, snapdbr.Name, vol, dev, &snapdbr.CreateTDate, snapdbr.Type,
410               snapdbr.created_before, snapdbr.created_after, &expired);
411    if (n == 8) {
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");
424       snapdbr.debug(20);
425       db_list_snapshot_records(jcr, jcr->db, &snapdbr, send_list, bs, ARG_LIST);
426       bs->signal(BNET_EOD);
427       goto bail_out;
428    }
429
430    n = sscanf(bs->msg, DelSnap, Job, snapdbr.Name, dev);
431    if (n == 3) {
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);
437       snapdbr.debug(20);      
438
439       /* We lock the db before to keep the error message */
440       db_lock(jcr->db);
441       ret = db_delete_snapshot_record(jcr, jcr->db, &snapdbr);
442       pm_strcpy(err, jcr->db->errmsg);
443       db_unlock(jcr->db);
444
445       if (ret) {
446          bs->fsend("1000 Snapshot deleted\n");
447
448       } else {
449          bs->fsend("1999 Snapshot not deleted ERR=%s\n", err);
450       }
451       goto bail_out;
452    }
453    ret = 0;
454
455 bail_out:
456    free_pool_memory(vol);
457    free_pool_memory(dev);
458    free_pool_memory(err);
459    return ret;
460 }
461
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)
464 {
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);
469    }
470 }
471
472 static int list_client_snapshot(UAContext *ua, bool sync)
473 {
474    SNAPSHOT_DBR *s, stemp;
475    alist *lst;
476 //   char ed1[50];
477
478    if (sync) {
479       if (!open_new_client_db(ua)) {
480          return 1;
481       }
482    }
483
484    lst = New(alist(10, not_owned_by_alist));
485    if (list_snapshot(ua, lst)) {
486       foreach_alist(s, lst) {
487          ua->send_msg(_(
488             "Snapshot      %s:\n"
489             "  Volume:     %s\n"
490             "  Device:     %s\n"
491             "  CreateDate: %s\n"
492 //            "  Size:       %sB\n",
493             "  Type:       %s\n"
494             "  Status:     %s\n"
495             "  Error:      %s\n"),
496             s->Name, NPRT(s->Volume), NPRT(s->Device),
497             s->CreateDate,
498 //          edit_uint64_with_suffix(s->Size, ed1),
499                       s->Type, s->status?_("OK"):_("Error"), s->errmsg);
500          if (sync && s->Device && *s->Name) {
501             stemp.reset();
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"));
507                }
508             }
509          }
510       }
511       if (lst->size() == 0) {
512          ua->send_msg(_("No snapshot found\n"));
513       }
514    }
515    /* Cleanup the list */
516    foreach_alist (s, lst) {
517       delete s;
518    }
519    delete lst;
520    return 1;
521 }
522
523 /* Ask client to create/prune/delete a snapshot via the command line */
524 int snapshot_cmd(UAContext *ua, const char *cmd)
525 {
526    SNAPSHOT_DBR snapdbr;
527    for (int i=0; i<ua->argc; i++) {
528       if (strcasecmp(ua->argk[i], NT_("purge")) == 0) {
529
530       } else if (strcasecmp(ua->argk[i], NT_("prune")) == 0) {
531          return prune_snapshot(ua);
532
533       } else if (strcasecmp(ua->argk[i], NT_("listclient")) == 0) {
534          return list_client_snapshot(ua, false);
535
536       } else if (strcasecmp(ua->argk[i], NT_("list")) == 0) {
537          snapshot_list(ua, 0, prtit, HORZ_LIST);
538          return 1;
539
540       } else if (strcasecmp(ua->argk[i], NT_("create")) == 0) {
541          /* We need a job definition, or a client */
542
543       } else if (strcasecmp(ua->argk[i], NT_("delete")) == 0) {
544          return delete_snapshot(ua);
545
546       } else if (strcasecmp(ua->argk[i], NT_("status")) == 0) {
547
548       } else if (strcasecmp(ua->argk[i], NT_("sync")) == 0) {
549          return list_client_snapshot(ua, true);
550
551       } else if (strcasecmp(ua->argk[i], NT_("update")) == 0) {
552          return update_snapshot(ua);
553
554       } else {
555          continue;
556       }
557    }
558
559    for ( ;; ) {
560
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"));
569
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);
573          break;
574       case 1:                         /* list client */
575          list_client_snapshot(ua, false);
576          break;
577       case 2:                   /* prune */
578          prune_snapshot(ua);
579          break;
580       case 3:                   /* delete */
581          delete_snapshot(ua);
582          break;
583       case 4:                      /* update snapshot */
584          update_snapshot(ua);
585          break;
586       case 5:                      /* sync snapshot */
587          list_client_snapshot(ua, true);
588          break;
589       case 6:                   /* done */
590       default:
591          ua->info_msg(_("Selection terminated.\n"));
592          return 1;
593       }
594    }
595    return 1;
596 }
597
598 /* Select a Snapshot record from the database, might be in ua_select.c */
599 int select_snapshot_dbr(UAContext *ua, SNAPSHOT_DBR *sr)
600 {
601    int   ret = 0;
602    char *p;
603    POOLMEM *err = get_pool_memory(PM_FNAME);
604    *err=0;
605
606    sr->reset();
607    snapshot_scan_cmdline(ua, 0, sr);
608
609    if (sr->SnapshotId == 0 && (sr->Name[0] == 0 || sr->Client[0] == 0)) {
610       CLIENT_DBR cr;
611       memset(&cr, 0, sizeof(cr));
612       /* Get the pool from client=<client-name> */
613       if (!get_client_dbr(ua, &cr, JT_BACKUP_RESTORE)) {
614          goto bail_out;
615       }
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: "))) {
619          goto bail_out;
620       }
621       p = ua->cmd;
622       if (*p == '*') {
623          p++;
624       }
625       if (is_a_number(p)) {
626          sr->SnapshotId = str_to_int64(p);
627       } else {
628          goto bail_out;
629       }
630    }
631
632    if (!get_snapshot_record(ua, sr)) {
633       ua->error_msg(_("Unable to get Snapshot record.\n"));
634       goto bail_out;
635    }
636
637    ret = 1;
638
639 bail_out:
640    if (!ret && *err) {
641       ua->error_msg("%s", err);
642    }
643    free_pool_memory(err);
644    return ret;
645 }
646
647 /* This part should be in ua_update.c */
648 static void update_snapretention(UAContext *ua, char *val, SNAPSHOT_DBR *sr)
649 {
650    char ed1[150];
651    POOL_MEM tmp(PM_MESSAGE);
652    bool ret;
653    if (!duration_to_utime(val, &sr->Retention)) {
654       ua->error_msg(_("Invalid retention period specified: %s\n"), val);
655       return;
656    }
657
658    db_lock(ua->db);
659    if (!(ret = db_update_snapshot_record(ua->jcr, ua->db, sr))) {
660       pm_strcpy(tmp, db_strerror(ua->db));
661    }
662    db_unlock(ua->db);
663
664    if (!ret) {
665       ua->error_msg("%s", tmp.c_str());
666
667    } else {
668       ua->info_msg(_("New retention period is: %s\n"),
669          edit_utime(sr->Retention, ed1, sizeof(ed1)));
670    }
671 }
672
673 /* This part should be in ua_update.c */
674 static void update_snapcomment(UAContext *ua, char *val, SNAPSHOT_DBR *sr)
675 {
676    POOL_MEM tmp(PM_MESSAGE);
677    bool ret;
678
679    bstrncpy(sr->Comment, val, sizeof(sr->Comment));
680
681    db_lock(ua->db);
682    if (!(ret = db_update_snapshot_record(ua->jcr, ua->db, sr))) {
683       pm_strcpy(tmp, db_strerror(ua->db));
684    }
685    db_unlock(ua->db);
686
687    if (!ret) {
688       ua->error_msg("%s", tmp.c_str());
689
690    } else {
691       ua->info_msg(_("New Comment is: %s\n"), sr->Comment);
692    }
693 }
694
695 /* This part should be in ua_update.c */
696 bool update_snapshot(UAContext *ua)
697 {
698    SNAPSHOT_DBR sr;
699    POOL_MEM ret;
700    char ed1[130];
701    bool done = false;
702    int i;
703    const char *kw[] = {
704       NT_("Retention"),                /* 0 */
705       NT_("Comment"),                  /* 1 */
706       NULL };
707
708    for (i=0; kw[i]; i++) {
709       int j;
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)) {
713             return 0;
714          }
715          switch (i) {
716          case 0:
717             update_snapretention(ua, ua->argv[j], &sr);
718             break;
719          case 1:
720             update_snapcomment(ua, ua->argv[j], &sr);
721             break;
722          default:
723             break;
724          }
725          done = true;
726       }
727    }
728
729    for ( ; !done; ) {
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);
735       if (i == 2) {
736          return 0;
737       }
738
739       if (!select_snapshot_dbr(ua, &sr)) {  /* Get Snapshot record */
740          return 0;
741       }
742       ua->info_msg(_("Updating Snapshot \"%s\" on \"%s\"\n"), sr.Name, sr.Client);
743
744       switch (i) {
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: "))) {
749             return 0;
750          }
751          update_snapretention(ua, ua->cmd, &sr);
752          break;
753       case 1:
754          ua->info_msg(_("Current comment is: %s\n"), NPRTB(sr.Comment));
755          if (!get_cmd(ua, _("Enter Snapshot comment: "))) {
756             return 0;
757          }
758          update_snapcomment(ua, ua->cmd, &sr);
759          break;
760       default:                        /* Done or error */
761          ua->info_msg(_("Selection terminated.\n"));
762          return 1;
763       }
764    }
765    return 1;
766 }