]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/fd_snapshot.c
135dca7c59ace92543bd3fa11e894b217c57d646
[bacula/bacula] / bacula / src / filed / fd_snapshot.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 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    Written by Eric Bollengier, 2015
20 */
21
22 /*
23            Documentation about snapshot backend
24 ----------------------------------------------------------------
25
26 The snapshot manager is using environment variables to communicate.
27
28 Variables:
29
30 SNAPSHOT_ACTION
31   Operation such as:
32      create, delete, list, mount, unmount, check, support, subvolume
33
34 SNAPSHOT_VOLUME
35   Volume name
36    ex: /dev/vgroot/home_Test-2014-01-01_00_00  (lvm)
37        /home/.snapshots/Test-2014-01-01_00_00  (btrfs)
38        /.zfs/snapshot/Test-2014-01-01_00_00    (zfs)
39
40   The volume name is generated by the create command
41
42 SNAPSHOT_DEVICE
43   Device name
44    ex: /dev/vgroot/home (lvm)
45        /home            (btrfs)
46        /                (zfs)
47
48   The device name can be found via getmntent()
49
50 SNAPSHOT_NAME
51   Snapshot name, usually the Job name
52
53 SNAPSHOT_FSTYPE
54   Device filesystem type, can be found via getmntent()
55   ex: btrfs, zfs, ext4
56
57 SNAPSHOT_TYPE
58   Snapshot backend type, generated by support command
59   ex: lvm, btrfs, zfs
60
61 SNAPSHOT_MOUNTPOINT
62   Device mount point, found via getmntent()
63
64 SNAPSHOT_SNAPMOUNTPOINT
65   Snapshot mount point is generated by the mount command
66
67
68                    Protocol
69 ----------------------------------------------------------------
70
71 OK: exit code 0 and status=1 in the output
72 ERR: exit code <> 0 and/or status=0 in the output
73
74 status=1 keyword="value" keyword2="value2"
75
76 status=0 error="Error message"
77
78
79                    Workflow
80 ----------------------------------------------------------------
81
82 1) List filesystems
83  get SNAPSHOT_DEVICE, SNAPSHOT_FSTYPE, SNAPSHOT_MOUNTPOINT
84
85  volume="" name="" device="" createtime=""
86
87 2) Test if a filesystem supports snapshot feature
88  SNAPSHOT_ACTION=support
89  SNAPSHOT_DEVICE=/home
90  SNAPSHOT_MOUNTPOINT=/home
91  SNAPSHOT_FSTYPE=btrfs
92
93  => status=1 type=btrfs device=/home
94  => status=0
95
96 2a) Test if a filesystem contains subvolumes
97  SNAPSHOT_ACTION=subvolumes
98  SNAPSHOT_DEVICE=/home
99  SNAPSHOT_FSTYPE=btrfs
100
101  => dev=10 mountpoint=/home/subvol fstype=btrfs
102
103 3) Create a snapshot
104  SNAPSHOT_ACTION=create
105  SNAPSHOT_NAME=Test-2014-01-01_00_00 
106  SNAPSHOT_DEVICE=/home
107  SNAPSHOT_MOUNTPOINT=/home
108  SNAPSHOT_FSTYPE=btrfs
109  SNAPSHOT_TYPE=btrfs
110
111  => status=1 volume="/home/.snapshots/Test-2014-01-01_00_00" createtdate=1418935776 type=btrfs
112
113 4) Mount the snapshot
114  SNAPSHOT_ACTION=mount
115  SNAPSHOT_NAME=Test-2014-01-01_00_00 
116  SNAPSHOT_DEVICE=/home
117  SNAPSHOT_MOUNTPOINT=/home
118  SNAPSHOT_FSTYPE=btrfs
119  SNAPSHOT_TYPE=btrfs
120
121  => status=1 volume="/home/.snapshots/Test-2014-01-01_00_00" createtdate=1418935776 type=btrfs
122  
123
124 5) Unmount the snapshot
125  SNAPSHOT_ACTION=unmount
126  SNAPSHOT_SNAPMOUNTPOINT=/home/.snapshots/Test-2014-01-01_00_00
127
128  => status=1
129
130 6) Delete the snapshot
131  SNAPSHOT_ACTION=delete
132  SNAPSHOT_VOLUME=/home/.snapshot/Test-2014-01-01_00_00
133
134  => status=1
135
136  */
137
138 #include "bacula.h"
139 #include "filed.h"
140 #define USE_CMD_PARSER
141 #include "plugins/fd/fd_common.h"
142 #undef Jmsg
143 #include "fd_snapshot.h"
144 #define APPMANAGER_CMD "%eappmanager"
145 #define APP_DIR "/tmp/app.d"
146 #define SNAPSHOT_CMD "%ebsnapshot"
147
148 /* Defined in messages.c */
149 extern char *exepath;
150
151 /* Catalog interface with the director */
152 static char CreateSnap[] = "CatReq Job=%s new_snapshot name=%s volume=%s device=%s tdate=%d type=%s retention=%s";
153 static char DelSnap[] = "CatReq Job=%s del_snapshot name=%s device=%s";
154 static char GetSnap[] = "CatReq Job=%s get_snapshot name=%s volume=%s";
155
156 /* Command line interface with the director */
157 static char LsCmd[] = "snapshot ls name=%127s volume=%s device=%s tdate=%d type=%127s path=%s";
158 static char DelCmd[] = "snapshot del name=%127s volume=%s device=%s tdate=%d type=%127s";
159 static char QueryCmd[] = "snapshot query name=%127s volume=%s device=%s tdate=%d type=%127s";
160 static char PruneCmd[] = "snapshot prune volume=%s type=%127s";
161 static char SyncCmd[] = "snapshot sync volume=%s type=%127%";
162 static char ListCmd[] = "snapshot list";
163 static char ConfCmd[] = "snapshot retention=%50s";
164
165 /* Small function to quickly tell us if we can do snapshot here */
166 static bool is_snapshot_supported(JCR *jcr)
167 {
168    bool ret;
169    struct stat sp;
170    POOLMEM    *cmd = get_pool_memory(PM_FNAME);
171    const char *p;
172    const char *str;
173    char add[20];
174
175    /* We are not really interested by arguments, just
176     * the filename
177     */
178    *cmd = 0;
179    for (p=me->snapshot_command; *p; p++) {
180       if (*p == '%') {
181          switch (*++p) {
182          case '%':
183             str = "%";
184             break;
185          case 'e':
186             str = NPRTB(exepath);
187             break;
188          default:
189             add[0] = '%';
190             add[1] = *p;
191             add[2] = 0;
192             str = add;
193          }
194
195       } else if (*p == ' ') {
196          break;
197
198       } else {
199          add[0] = *p;
200          add[1] = 0;
201          str = add;
202       }
203       pm_strcat(cmd, str);
204    }
205
206    ret = stat(cmd, &sp) == 0;
207    free_pool_memory(cmd);
208    Dmsg1(10, "Snapshot = %d\n", ret);
209    return ret;
210 }
211
212 /* Return the default snapshot handler, must be freed at the end */
213 char *snapshot_get_command()
214 {
215    return bstrdup(SNAPSHOT_CMD);
216 }
217
218 /* Initialize the snapshot manager at the begining of the
219  * job and create snapshots
220  */
221 bool open_snapshot_backup_session(JCR *jcr)
222 {
223    if (!is_snapshot_supported(jcr)) {
224       Dmsg0(DT_SNAPSHOT, "Snapshot not supported\n");
225       return false;
226    }
227
228    jcr->snap_mgr = New(snapshot_manager(jcr));
229    /* Get all volumes and subvolumes */
230    if (!jcr->snap_mgr->scan_mtab()) {
231       berrno be;                /* error probably in errno */
232       Dmsg1(DT_SNAPSHOT, "Unable to scan mtab. ERR=%s\n", be.bstrerror());
233       Jmsg(jcr, M_ERROR, 0, "Unable to scan mtab to determine devices to snapshot\n");
234       return false;
235    }
236    /* Match the volume list with the fileset */
237    if (!jcr->snap_mgr->scan_fileset()) {
238       Jmsg(jcr, M_ERROR,0, "Unable to scan fileset to determine devices to snapshot\n");
239       return false;
240    }
241    /* Create fileset needed */
242    if (!jcr->snap_mgr->create_snapshots()) {
243       /* Error message already displayed if needed */
244       return false;
245    }
246    return true;                 /* We should have some snapshots */
247 }
248
249 /* Command that can be called from outside */
250 bool list_all_snapshots(JCR *jcr, alist *lst)
251 {
252    snapshot_manager snap_mgr(jcr);
253
254    /* Get all volumes and subvolumes */
255    if (!snap_mgr.scan_mtab()) {
256       return false;
257    }
258    /* list snapshots */
259    if (snap_mgr.list_snapshots(lst)) {
260       return false;
261    }
262    return true;
263 }
264
265 /* Cleanup the snapshot manager at the end of the job */
266 void close_snapshot_backup_session(JCR *jcr)
267 {
268    if (jcr->snap_mgr) {
269       jcr->snap_mgr->cleanup_snapshots();
270       delete jcr->snap_mgr;
271       jcr->snap_mgr = NULL;
272    }
273 }
274
275 /* Special cmd_parser subclass to not look after plugin
276  * names when decoding the line
277  */
278 class arg_parser: public cmd_parser
279 {
280 public:
281    arg_parser(): cmd_parser() { use_name = false; };
282    virtual ~arg_parser() {};
283 };
284
285 class snapshot;
286
287 /* Device that exists on the system */
288 class fs_device: public SMARTALLOC
289 {
290 public:
291    rblink link;
292
293    uint32_t  dev;               /* dev no */
294    char     *mountpoint;        /* where it's mounted */
295    char     *fstype;            /* ntfs, ext3, ext4... */
296    char     *device;            /* /dev/mapper/xxx */
297
298    bool      supportSnapshotTested; /* True if support() was called */
299    bool      isSuitableForSnapshot; /* Compatible with snapshots */
300    bool      inSnapshotSet;
301    bool      inFileSet;
302    snapshot *snap;              /* Associated snapshot */
303
304    dlist     *include;          /* Where the fs_device was found in the fileset */
305    void      *node;             /* At which node */
306
307
308    fs_device(): 
309       dev(0), mountpoint(NULL), fstype(NULL), device(NULL), 
310          supportSnapshotTested(false), isSuitableForSnapshot(false), snap(NULL)
311       {
312       };
313
314    fs_device(uint32_t adev, const char *adevice, const char *amountpoint, const char *aftype) {
315       dev        = adev;
316       fstype     = bstrdup(aftype);
317       device    = bstrdup(adevice);
318       mountpoint = bstrdup(amountpoint);
319       supportSnapshotTested = false;
320       isSuitableForSnapshot = false;
321       inSnapshotSet = false;
322       inFileSet = false;
323       snap       = NULL;
324       include    = NULL;
325       node       = NULL;
326    };
327
328    ~fs_device() {
329       destroy();
330    };
331
332    /* Call support() and cache the result in supportSnapshotTested and isSuitableForSnapshot */
333    bool can_do_snapshot();
334
335    void setInFileSet(dlist *inc, void *where) {
336       include = inc;            /* where we need to include subvolumes */
337       node = where;             /* after which node */
338       inFileSet = true;
339       inSnapshotSet = true;
340    };
341
342    void set_snap(snapshot *s) {
343       snap = s;
344    };
345
346    void destroy();
347 };
348
349 /* The device list is stored in a rblist, using the
350  * dev no as key. The devno can be found in every stat()
351  * packet.
352  */
353 static int compare_entries(void *item1, void *item2)
354 {
355    fs_device *dev1 = (fs_device *) item1;
356    fs_device *dev2 = (fs_device *) item2;
357    if (dev1->dev > dev2->dev) {
358       return 1;
359
360    } else if (dev1->dev < dev2->dev) {
361       return -1;
362
363    } else {
364       return 0;
365    }
366 }
367
368 static int search_entry(void *item1, void *item2)
369 {
370    uint32_t dev1 = (intptr_t) item1;
371    fs_device* dev2 = (fs_device *) item2;
372    if (dev1 > dev2->dev) {
373       return 1;
374
375    } else if (dev1 < dev2->dev) {
376       return -1;
377
378    } else {
379       return 0;
380    }
381 }
382
383 /* List of all fd_device that are on the system
384  * Some devices are excluded automatically from
385  * the list, such as proc, sysfs, etc...
386  */
387 class mtab: public SMARTALLOC
388 {
389 public:
390    rblist     *entries;
391    int         sCount;          /* Snapshot count */
392    int         dCount;          /* Device count */
393    mtab() {
394       fs_device *elt = NULL;
395       entries = New(rblist(elt, &elt->link));
396       dCount = sCount = 0;
397    };
398
399    ~mtab() {
400       fs_device *elt;
401       foreach_rblist(elt, entries) {
402          elt->destroy();
403       }
404       delete entries;
405    };
406
407    /* Have we devices for snapshot in our list ? */
408    bool empty() {
409       return sCount == 0;
410    };
411
412    /* Get a fs_device corresponding to a file */
413    fs_device *search(char *file);
414
415    /* Get subvolumes for a specific device */
416    bool get_subvolumes(uint32_t dev, alist *items, FF_PKT *ff) {
417       fs_device *elt, *elt2;
418       elt = (fs_device *)entries->search((void*)(intptr_t)dev, search_entry);
419       if (!elt) {
420          return false;
421       }
422
423       foreach_rblist(elt2, entries) {
424          if (elt2->dev == elt->dev) {
425             continue;
426          }
427          if (strncmp(elt2->mountpoint, elt->mountpoint, strlen(elt->mountpoint)) == 0) {
428             /* the mount point is included in the volume */
429
430             if (file_is_excluded(ff, elt2->mountpoint)) {
431                Dmsg1(DT_SNAPSHOT|50, "Looks to be excluded %s\n", elt2->mountpoint);
432
433             } else {
434                items->append(elt2);
435             }
436          }
437       }
438       return items->size() > 0;
439    };
440
441    bool add_in_snapshot_set(char *file, dlist *inc, void *node) {
442
443       fs_device *elt = search(file);
444       if (!elt) {
445          Dmsg1(DT_SNAPSHOT, "%s will not be added to snapshot set\n", file);
446          return sCount == dCount;        /* not found in our list, skip it */
447       }
448       return add_in_snapshot_set(elt, inc, node);
449    };
450
451    bool add_in_snapshot_set(fs_device *elt, dlist *inc, void *node) {
452       Dmsg4(DT_SNAPSHOT|10, "%s in=%d can=%d tested=%d\n", elt->mountpoint, elt->inSnapshotSet, 
453             elt->isSuitableForSnapshot, elt->supportSnapshotTested);
454       if (!elt->inSnapshotSet && elt->can_do_snapshot()) {
455          Dmsg1(DT_SNAPSHOT, "Marking %s for snapshot\n", elt->mountpoint);
456          elt->setInFileSet(inc, node);
457          sCount++;
458       }
459       /* It will help to count when all devices are in the snapshot set */
460       Dmsg2(DT_SNAPSHOT|10, "sCount %d = dCount %d\n", sCount, dCount); 
461       return sCount == dCount;
462    };
463
464    bool add_entry(fs_device *vol) {
465       fs_device *ret = (fs_device *) entries->insert(vol, compare_entries);
466       if (ret == vol && vol->snap) {
467          dCount++;              /* We skip directly FS such as /proc, /sys or /dev */
468       }
469       return ret == vol;
470    };
471 };
472
473 /* Snapshot descriptor, used to communicate with the snapshot
474  * backend on the system.
475  */
476 class snapshot: public SMARTALLOC
477 {
478 private:
479    JCR *jcr;
480
481 public:
482    const char *action;             /* current action */
483    char     Name[MAX_NAME_LENGTH]; /* Name of the snapshot */
484    char     Type[MAX_NAME_LENGTH]; /* lvm, btrfs, netapp */
485    char     FSType[MAX_NAME_LENGTH];     /* btrfs, zfs, ext3 */
486    char     CreateDate[MAX_TIME_LENGTH]; /* Creation date */
487    time_t   CreateTDate;                 /* Creation date in seconds */
488    int64_t  size;               /* Size of the snapshot */
489    int      status;             /* snapshot status */
490    utime_t  Retention;          /* Snapshot retention, might come from Pool/FileSet */
491
492    POOLMEM *Volume;             /* Path of the volume */
493    POOLMEM *Device;             /* Device path */
494    POOLMEM *MountPoint;         /* Device Mount point */
495    POOLMEM *SnapMountPoint;     /* Snapshot Mount point */
496    POOLMEM *path;               /* path used in ls query */
497    POOLMEM *errmsg;             /* Error message generated by commands */
498    POOLMEM *SnapDirectory;      /* Where snapshots are stored */
499
500    char   **env;                /* Variables used to call snapshot */
501    bool     mounted;            /* True if mounted on SnapMountPoint */
502    bool     created;            /* True if the snapshot is created */
503
504    snapshot(JCR *ajcr) {
505       jcr    = ajcr;
506       env    = NULL;
507       path   = get_pool_memory(PM_FNAME);
508       errmsg = get_pool_memory(PM_MESSAGE);
509       Volume = get_pool_memory(PM_FNAME);
510       Device = get_pool_memory(PM_FNAME);
511       MountPoint     = get_pool_memory(PM_FNAME);
512       SnapMountPoint = get_pool_memory(PM_FNAME);
513       SnapDirectory  = get_pool_memory(PM_FNAME);
514       reset();
515    };
516
517    ~snapshot() {
518       free_pool_memory(path);
519       free_pool_memory(errmsg);
520       free_pool_memory(Volume);
521       free_pool_memory(Device);
522       free_pool_memory(MountPoint);
523       free_pool_memory(SnapMountPoint);
524       free_pool_memory(SnapDirectory);
525       free_env();
526    };
527
528    void reset() {
529       *SnapDirectory = *Type = *FSType = *SnapMountPoint = 0;
530       *MountPoint = *Volume = *Device = *path = *errmsg = 0;
531       action    = NULL;
532       size      = -1;
533       status    = 0;
534       mounted   = false;
535       created   = false;
536       Retention = jcr->snapshot_retention;
537    };
538
539    /* Free the env[] structure */
540    void free_env() {
541       if (env) {
542          for (int i=0; env[i] ; i++) {
543             free(env[i]);
544          }
545          free(env);
546          env = NULL;
547       }
548    };
549
550    void set_device(const char *d) {
551       pm_strcpy(Device, d);
552    };
553
554    void set_mountpoint(const char *d) {
555       pm_strcpy(MountPoint, d);
556    };
557
558    void set_name(const char *n) {
559       bstrncpy(Name, n, sizeof(Name));
560    };
561
562    void set_fstype(const char *n) {
563       bstrncpy(FSType, n, sizeof(FSType));
564    };
565
566    void set_action(const char *a) {
567       action = a;
568    };
569
570    /* Convert a real top path to a snapshot path
571     * and set proper variables inside ff_pkt
572     * to translate back all subfiles.
573     */
574    bool convert_path(FF_PKT *ff) {
575       if (!*MountPoint || !*SnapMountPoint) {
576          Dmsg2(DT_SNAPSHOT, "MountPoint=%s SnapMountPoint=%s\n", 
577                NPRT(MountPoint), NPRT(SnapMountPoint));
578          return false;
579       }
580       if (!ff->snap_top_fname) {
581          ff->snap_top_fname = get_pool_memory(PM_FNAME);
582       }
583       ff->volume_path = MountPoint;       /* /tmp */
584       ff->snapshot_path = SnapMountPoint; /* /tmp/.snapshot/Job.20140502.01.01.01 */
585       ff->top_fname_save = ff->top_fname;
586
587       int mp_first = strlen(MountPoint); /* will point to after MountPoint in top_fname */
588       int last = pm_strcpy(ff->snap_top_fname, SnapMountPoint);
589       last = MAX(last - 1, 0);
590
591       /* We need to concat path and avoid double / and no / */
592       if (ff->snap_top_fname[last] == '/') {
593          if (ff->top_fname[mp_first] == '/') {
594             ff->snap_top_fname[last] = 0; /* strip double / */
595          }
596       } else {                            /* no / at all */
597          if (ff->top_fname[mp_first] != '/') {
598             pm_strcat(ff->snap_top_fname, "/");
599          }
600       }
601
602       pm_strcat(ff->snap_top_fname, ff->top_fname + mp_first);
603       ff->top_fname = ff->snap_top_fname;
604       ff->strip_snap_path = true;
605       Dmsg1(DT_SNAPSHOT|50, "top_fname=%s\n", ff->top_fname);
606       return true;
607    };
608
609    /* Create a environment used in the child process */
610    int edit_snapshot_env() {
611       int      i   = 0;
612       POOLMEM *tmp = get_pool_memory(PM_FNAME);
613       free_env();
614
615       /* Update "10" to add more variables */
616       env = (char **) malloc(sizeof(char *) * 10);
617
618       if (*Name) {
619          Mmsg(tmp, "SNAPSHOT_NAME=%s", Name);
620          env[i++] = bstrdup(tmp);
621       }
622       if (*Volume) {
623          Mmsg(tmp, "SNAPSHOT_VOLUME=%s", Volume);
624          env[i++] = bstrdup(tmp);
625       }
626       if (*Device) {
627          Mmsg(tmp, "SNAPSHOT_DEVICE=%s", Device);
628          env[i++] = bstrdup(tmp);
629       }
630       if (*Type) {
631          Mmsg(tmp, "SNAPSHOT_TYPE=%s", Type);
632          env[i++] = bstrdup(tmp);
633       }
634       if (*FSType) {
635          Mmsg(tmp, "SNAPSHOT_FSTYPE=%s", FSType);
636          env[i++] = bstrdup(tmp);
637       }
638       if (*MountPoint) {
639          Mmsg(tmp, "SNAPSHOT_MOUNTPOINT=%s", MountPoint);
640          env[i++] = bstrdup(tmp);
641       }
642       if (*SnapDirectory) {
643          Mmsg(tmp, "SNAPSHOT_SNAPDIRECTORY=%s", SnapDirectory);
644          env[i++] = bstrdup(tmp);
645       }
646       if (*SnapMountPoint) {
647          Mmsg(tmp, "SNAPSHOT_SNAPMOUNTPOINT=%s", SnapMountPoint);
648          env[i++] = bstrdup(tmp);
649       }
650       /* When adding new entries, do not forget to add more slots to env[] */
651
652       Mmsg(tmp, "SNAPSHOT_ACTION=%s", action);
653       env[i++] = bstrdup(tmp);
654
655       env[i] = NULL;            /* last record */
656
657       if (chk_dbglvl(DT_SNAPSHOT|100)) {
658          for (i = 0; env[i] ; i++) {
659             Dmsg1(0, "%s\n", env[i]);
660          }
661       }
662
663       free_pool_memory(tmp);
664       return 1;
665    };
666
667    /* Edit the command line if needed */
668    int edit_snapshot_codes(POOLMEM **omsg, const char *imsg) {
669       const char *p;
670       const char *str;
671       char add[20];
672
673       **omsg = 0;
674       for (p=imsg; *p; p++) {
675          if (*p == '%') {
676             switch (*++p) {
677             case '%':
678                str = "%";
679                break;
680             case 'e':
681                str = NPRTB(exepath);
682                break;
683             case 'n':
684                str = Name;
685                break;
686             case 'v':
687                str = Volume;
688                break;
689             case 'd':
690                str = Device;
691                break;
692             case 'D':
693                str = SnapDirectory;
694                break;
695             case 'a':
696                str = NPRT(action);
697                break;
698             case 't':
699                str = Type;
700                break;
701             case 'f':
702                str = FSType;
703                break;
704             case 'p':
705                str = MountPoint;
706                break;
707             case 's':
708                str = SnapMountPoint;
709                break;
710             default:
711                add[0] = '%';
712                add[1] = *p;
713                add[2] = 0;
714                str = add;
715             }
716
717          } else {
718             add[0] = *p;
719             add[1] = 0;
720             str = add;
721          }
722          pm_strcat(omsg, str);
723       }
724
725       if (chk_dbglvl(DT_SNAPSHOT|10)) {
726          POOL_MEM tmp;
727          Mmsg(tmp, " -d %d -o /tmp/bsnapshot.log ", debug_level);
728          pm_strcat(omsg, tmp.c_str());
729       }
730
731       Dmsg2(DT_SNAPSHOT|30, "edit_snapshot_codes: %s -> %s\n", imsg, *omsg);
732       return 1;
733    };
734
735    /* Call the snapshot backend to know if we can snapshot the current FS */
736    int support_snapshot(fs_device *vol) {
737       arg_parser cmd;
738       status = 0;
739
740       reset();
741       set_device(vol->device);
742       set_mountpoint(vol->mountpoint);
743       set_fstype(vol->fstype);
744
745       if (!do_command("support", &cmd)) {
746          goto bail_out;
747       }
748       scan_arg(&cmd);
749
750    bail_out:
751       Dmsg2(DT_SNAPSHOT|50, "%s snapshot support status=%d\n", vol->mountpoint, status);
752       return status;
753    };
754
755    /* Scan sub volumes for a particular volume */
756    int scan_subvolumes(fs_device *vol, alist *lst) {
757       int ret = 0;
758       arg_parser cmd;
759       uint32_t   dev=0;
760       char      *mp=NULL, *fstype=NULL, *device=Device;
761
762       reset();
763       set_device(vol->device);
764       set_mountpoint(vol->mountpoint);
765       set_fstype(vol->fstype);
766
767       if (!do_command("subvolumes", &cmd)) {
768          goto bail_out;
769       }
770
771       for (int i = 0; i < cmd.argc ; i++) {
772          if (strcasecmp(cmd.argk[i], "Dev") == 0 && cmd.argv[i]) {
773             dev = str_to_int64(cmd.argv[i]);
774
775          } else if (strcasecmp(cmd.argk[i], "MountPoint") == 0 && cmd.argv[i]) {
776             mp = cmd.argv[i];
777
778          } else if (strcasecmp(cmd.argk[i], "Device") == 0 && cmd.argv[i]) {
779             device = cmd.argv[i];
780
781          } else if (strcasecmp(cmd.argk[i], "FSType") == 0 && cmd.argv[i]) {
782             fstype = cmd.argv[i];
783             if (mp && fstype && dev) {
784                fs_device *elt = New(fs_device(dev, device, mp, fstype));
785                lst->append(elt);
786                /* reset variables */
787             }
788             dev = 0;
789             mp  = fstype = NULL;
790             device = Device;
791          }
792       }
793       ret = 1;
794
795    bail_out:
796       return ret;
797    };
798
799    /* Prune current snapshots
800     *  - List snapshots available on the director, keep a list locally
801     *  - get mtab, list snapshots for all devices, or devices that are in the director list
802     */
803    int prune(BSOCK *bs) {
804       return 1;
805    };
806
807    /* List local snapshots, list director snapshots, and synchronize the two */
808    int sync(BSOCK *bs) {
809       return 1;
810    };
811
812    /* List files from a snapshot
813     *  Need to set the Volume and the Path
814     */
815    int ls(BSOCK *bs) {
816       return 1;
817    };
818
819    /* Scan common arguments */
820    int scan_arg(arg_parser *cmd) {
821       for (int i = 0; i < cmd->argc ; i++) {
822          if (strcasecmp(cmd->argk[i], "Volume") == 0 && cmd->argv[i]) {
823             pm_strcpy(Volume, cmd->argv[i]);
824
825          } else if (strcasecmp(cmd->argk[i], "CreateDate") == 0 && cmd->argv[i]) {
826             bstrncpy(CreateDate, cmd->argv[i], sizeof(CreateDate));
827             CreateTDate = str_to_utime(CreateDate);
828
829          } else if (strcasecmp(cmd->argk[i], "CreateTDate") == 0 && cmd->argv[i]) {
830             CreateTDate = str_to_int64(cmd->argv[i]);
831             bstrftimes(CreateDate, sizeof(CreateDate), CreateTDate);
832
833          } else if (strcasecmp(cmd->argk[i], "Type") == 0 && cmd->argv[i]) {
834             bstrncpy(Type, cmd->argv[i], sizeof(Type));
835
836          } else if (strcasecmp(cmd->argk[i], "SnapMountPoint") == 0 && cmd->argv[i]) {
837             pm_strcpy(SnapMountPoint, cmd->argv[i]);
838
839          } else if (strcasecmp(cmd->argk[i], "SnapDirectory") == 0 && cmd->argv[i]) {
840             pm_strcpy(SnapDirectory, cmd->argv[i]);
841
842          } else if (strcasecmp(cmd->argk[i], "status") == 0 && cmd->argv[i]) {
843             status = str_to_int64(cmd->argv[i]);
844
845          } else if (strcasecmp(cmd->argk[i], "Device") == 0 && cmd->argv[i]) {
846             pm_strcpy(Device, cmd->argv[i]);
847          }
848       }
849       return 1;
850    };
851
852    /* Create a snapshot with already given attributes
853     *  Need to set Name and Device at the minimum
854     */
855    int create() {
856       int ret = 0;
857       arg_parser cmd;
858
859       if (!*Name || !*Device) {
860          goto bail_out;
861       }
862
863       Dmsg2(DT_SNAPSHOT, "Create Snapshot of %s %s\n", Device, Name);
864
865       /* TODO: see if we handle multiple snapshots per call */
866       if (!do_command("create", &cmd)) {
867          goto bail_out;
868       }
869
870       scan_arg(&cmd);
871       created = 1;
872
873       ret = 1;
874
875    bail_out:
876       return ret;
877    };
878
879    int mount() {
880       arg_parser cmd;
881       status = 0;
882
883       if (!*Name || !*Volume || !*Device || !*Type) {
884          goto bail_out;
885       }
886
887       Dmsg1(DT_SNAPSHOT, "Doing mount of %s\n", Volume);
888
889       if (!do_command("mount", &cmd)) {
890          goto bail_out;
891       }
892
893       *SnapMountPoint = 0;
894
895       scan_arg(&cmd);
896
897       mounted = (status > 0 && *SnapMountPoint);
898
899    bail_out:
900       return status;
901    };
902
903    int unmount() {
904       arg_parser cmd;
905       status = 0;
906
907       if (!*Name || !*SnapMountPoint || !*Type || !mounted) {
908          goto bail_out;
909       }
910
911       Dmsg1(DT_SNAPSHOT, "Doing unmount of a %s\n", SnapMountPoint);
912
913       if (!do_command("unmount", &cmd)) {
914          goto bail_out;
915       }
916
917       scan_arg(&cmd);
918
919       mounted = 0;
920
921    bail_out:
922       return status;
923    };
924
925    /* Delete a snapshot with the given device name */
926    int del() {
927       int ret = 0;
928       arg_parser cmd;
929       if (!*Name || !*Volume || !created) {
930          goto bail_out;
931       }
932       ret = do_command("delete", &cmd);
933    bail_out:
934       return ret;
935    };
936
937    /* TODO: Need to read stdout as well */
938    int do_command(const char *act, arg_parser *cmd) {
939       int ret = 0, rcode;
940       POOLMEM *command = get_pool_memory(PM_FNAME);
941       POOLMEM *out = get_pool_memory(PM_FNAME);
942
943       set_action(act);
944       edit_snapshot_codes(&command, me->snapshot_command);
945       edit_snapshot_env();
946
947       Dmsg1(DT_SNAPSHOT|20, "Execute %s command\n", act);
948
949       if (*command == 0) {
950          Mmsg(errmsg, _("Error while creating command string %s.\n"), act);
951          Dmsg1(DT_SNAPSHOT, "%s", errmsg);
952          goto bail_out;
953       }
954
955       /* If the exit code is 1, we can expect to have a clear error
956        * message in the output
957        */
958       if ((rcode = run_program_full_output(command, 600, out, env)) != 0) {
959          if ((rcode & ~b_errno_exit) == 1) { /* exit 1, look the output */
960             if (cmd->parse_cmd(out) == bRC_OK) {
961                int i = cmd->find_arg_with_value("status");
962                if (i >= 0) {
963                   /* If we have a status, take it */
964                   status = str_to_int64(cmd->argv[i]);
965                } else {
966                   status = 0;
967                }
968                i = cmd->find_arg_with_value("error");
969                if (i >= 0) {
970                   pm_strcpy(errmsg, cmd->argv[i]);
971                   Dmsg1(DT_SNAPSHOT|20, "%s\n", errmsg);
972                   goto bail_out;
973                }
974             }
975          }
976          berrno be;
977          Mmsg(errmsg, _("Error while executing \"%s\" %s. %s %s\n"), 
978               act, command, out, be.bstrerror());
979          Dmsg1(DT_SNAPSHOT, "%s", errmsg);
980          goto bail_out;
981       }
982
983       /* Need to decode the output of the script
984        * TODO: some commands will have multiple lines
985        */
986       if (cmd->parse_cmd(out) != bRC_OK) {
987          Dmsg2(DT_SNAPSHOT, "snapshot command %s output error: [%s]\n", act, out);
988          Mmsg(errmsg, _("Unable to parse snapshot command output\n"));
989          goto bail_out;
990       }
991       Dmsg1(DT_SNAPSHOT|50, "ret = %s\n", out);
992       ret = 1;
993    bail_out:
994       free_pool_memory(command);
995       free_pool_memory(out);
996       return ret;
997    };
998
999    int list(alist *lst) {
1000       Dmsg0(DT_SNAPSHOT, "Doing list of a snapshots of a given device\n");
1001       snapshot  *snap;
1002       arg_parser cmd;
1003       int i, ret=0, status=1;
1004       char *volume=NULL, *name=NULL, *device=NULL, *createdate=NULL, *error=NULL;
1005       utime_t createtdate = 0;
1006
1007       /* TODO: Here we need to loop over a list */
1008       if (!do_command("list", &cmd)) {
1009          goto bail_out;
1010       }
1011       ret = 1;
1012
1013       /* should get
1014        * volume=xxx device=zzzz name=yyy createtdate=12121212 size=xx status=xx error=xx type=lvm
1015        */
1016       for (i = 0; i < cmd.argc ; i++) {
1017          if (strcasecmp(cmd.argk[i], "Volume") == 0) {
1018             volume = cmd.argv[i];
1019
1020          } else if (strcasecmp(cmd.argk[i], "Name") == 0) {
1021             name = cmd.argv[i];
1022
1023          } else if (strcasecmp(cmd.argk[i], "Device") == 0) {
1024             device = cmd.argv[i];
1025
1026          } else if (strcasecmp(cmd.argk[i], "Error") == 0) {
1027             error = cmd.argv[i];
1028
1029          } else if (strcasecmp(cmd.argk[i], "Status") == 0) {
1030             status = str_to_int64(cmd.argv[i]);
1031
1032          } else if (strcasecmp(cmd.argk[i], "Type") == 0) {
1033             snap = New(snapshot(jcr));
1034             pm_strcpy(snap->Volume, volume);
1035             pm_strcpy(snap->Device, NPRTB(device));
1036             bstrncpy(snap->Name, NPRTB(name), sizeof(snap->Name));
1037             bstrncpy(snap->Type, cmd.argv[i], sizeof(snap->Type));
1038             bstrncpy(snap->CreateDate, createdate, sizeof(snap->CreateDate));
1039             pm_strcpy(snap->errmsg, NPRTB(error));
1040             snap->status = status;
1041             snap->CreateTDate = createtdate;
1042             error = createdate = device = name = volume = NULL;
1043             status = 1;
1044             createtdate = 0;
1045             lst->append(snap);
1046
1047          } else if (strcasecmp(cmd.argk[i], "CreateTDate") == 0) {
1048             createtdate = str_to_int64(cmd.argv[i]);
1049
1050          } else if (strcasecmp(cmd.argk[i], "CreateDate") == 0) {
1051             createdate = cmd.argv[i];
1052             createtdate = str_to_utime(cmd.argv[i]);
1053          }
1054       }
1055    bail_out:
1056       return ret;
1057    };
1058
1059    /* Query information about snapshot */
1060    int query() {
1061       Dmsg0(0, "Doing query of a snapshot\n");
1062       arg_parser cmd;
1063       int i, ret=0;
1064
1065       if (!*Volume) {
1066          goto bail_out;
1067       }
1068
1069       if (!do_command("query", &cmd)) {
1070          goto bail_out;
1071       }
1072
1073       if ((i = cmd.find_arg_with_value("size")) >= 0) {
1074          size = str_to_int64(cmd.argv[i]);
1075       }
1076
1077       if ((i = cmd.find_arg_with_value("status")) >= 0) {
1078          status = str_to_int64(cmd.argv[i]);
1079       }
1080
1081       ret = 1;
1082
1083    bail_out:
1084       return ret;
1085    };
1086
1087    /* Quickly unbash all attributes after a sscanf */
1088    void unbash_spaces() {
1089       ::unbash_spaces(Volume);
1090       ::unbash_spaces(Device);
1091       ::unbash_spaces(path);
1092       ::unbash_spaces(Name);
1093       ::unbash_spaces(CreateDate);
1094       ::unbash_spaces(errmsg);
1095    };
1096
1097    void bash_spaces() {
1098       ::bash_spaces(Volume);
1099       ::bash_spaces(Device);
1100       ::bash_spaces(path);
1101       ::bash_spaces(Name);
1102       ::bash_spaces(CreateDate);
1103       ::bash_spaces(errmsg);
1104    };
1105
1106    /* Quicky make sure we have enough space to handle the request */
1107    void check_buffer_size(int len) {
1108       Volume = check_pool_memory_size(Volume, len);
1109       Device = check_pool_memory_size(Device, len);
1110       path   = check_pool_memory_size(path, len);
1111    };
1112
1113    /* Create Catalog entry for the current snapshot */
1114    int create_catalog_entry() {
1115       int ret = 0;
1116       char ed1[50];
1117       bash_spaces();
1118       jcr->dir_bsock->fsend(CreateSnap,
1119                             jcr->Job, Name, Volume,
1120                             Device, CreateTDate, Type, edit_uint64(Retention, ed1));
1121       if (jcr->dir_bsock->recv() < 0) {
1122          Mmsg(errmsg, _("Unable to create snapshot record. ERR=%s\n"),
1123               jcr->dir_bsock->bstrerror());
1124
1125       } else if (strncmp(jcr->dir_bsock->msg, "1000", 4) != 0) {
1126          Mmsg(errmsg, _("Unable to create snapshot record, got %s\n"),
1127               jcr->dir_bsock->msg);
1128
1129       } else {
1130          ret = 1;               /* OK */
1131       }
1132       unbash_spaces();
1133       return ret;
1134    };
1135
1136    /* Delete Catalog entry of the current snapshot */
1137    int delete_catalog_entry() {
1138       int ret = 0;
1139       bash_spaces();
1140       jcr->dir_bsock->fsend(DelSnap, jcr->Job, Name, Device);
1141
1142       if (jcr->dir_bsock->recv() < 0) {
1143          Mmsg(errmsg, _("Unable to delete snapshot record. ERR=%s\n"),
1144               jcr->dir_bsock->bstrerror());
1145
1146       } else if (strncmp(jcr->dir_bsock->msg, "1000", 4) != 0) {
1147          Mmsg(errmsg, _("Unable to delete snapshot record, got %s\n"),
1148               jcr->dir_bsock->msg);
1149
1150       } else {
1151          ret = 1;               /* OK */
1152       }
1153
1154       unbash_spaces();
1155       return ret;
1156    };
1157
1158    /* Get Catalog entry of the current snapshot */
1159    int get_catalog_entry() {
1160       int ret = 0;
1161       arg_parser cmd;
1162
1163       if (!*Name || !*Volume) {
1164          return ret;
1165       }
1166
1167       bash_spaces();
1168       jcr->dir_bsock->fsend(GetSnap, jcr->Job, Name, Volume);
1169
1170       if (jcr->dir_bsock->recv() < 0) {
1171          Mmsg(errmsg, _("Unable to get snapshot record. ERR=%s\n"),
1172               jcr->dir_bsock->bstrerror());
1173
1174       } else if (strncmp(jcr->dir_bsock->msg, "1000", 4) != 0) {
1175          Mmsg(errmsg, _("Unable to get snapshot record, got %s\n"),
1176               jcr->dir_bsock->msg);
1177
1178       } else {
1179          if (cmd.parse_cmd(jcr->dir_bsock->msg) != bRC_OK) {
1180             Mmsg(errmsg, _("Unable to parse command output\n"));
1181             scan_arg(&cmd);     /* Fill all parameters from director */
1182
1183          } else {
1184             ret = 1;               /* OK */
1185          }
1186       }
1187
1188       unbash_spaces();
1189       return ret;
1190    };
1191
1192 };
1193
1194 /* Should be after snapshot declaration */
1195 void fs_device::destroy() {
1196    if (fstype) {
1197       free(fstype);
1198       fstype = NULL;
1199    }
1200    if (mountpoint) {
1201       free(mountpoint);
1202       mountpoint = NULL;
1203    }
1204    if (device) {
1205       free(device);
1206       device = NULL;
1207    }
1208    if (snap) {
1209       delete snap;
1210       snap = NULL;
1211    }
1212 }
1213
1214 bool fs_device::can_do_snapshot() {
1215    if (snap && !supportSnapshotTested) {
1216       if (snap->support_snapshot(this)) {
1217          Dmsg2(DT_SNAPSHOT, "%s suitable for snapshot, type %s\n",
1218                mountpoint, snap->Type);
1219          isSuitableForSnapshot = true;
1220
1221       } else {
1222          Dmsg2(DT_SNAPSHOT, "%s is not suitable for snapshot, type %s\n",
1223                mountpoint, snap->Type);
1224       }
1225       supportSnapshotTested = true;
1226    }
1227    return isSuitableForSnapshot;
1228 }
1229
1230 /* Should be after the snapshot declaration */
1231 fs_device *mtab::search(char *file) {
1232    struct stat statp;
1233    if (lstat(file, &statp) != 0) {
1234       Dmsg1(DT_SNAPSHOT, "%s not found\n", file);
1235       return NULL;              /* not found */
1236    }
1237    
1238    fs_device *elt = (fs_device *)entries->search((void *)((intptr_t)(statp.st_dev)),
1239                                                  search_entry);
1240    if (!elt) {
1241       Dmsg2(DT_SNAPSHOT, "Device %d for file %s not found in our mount list\n",
1242             statp.st_dev, file);
1243       return NULL;        /* not found in our list, skip it */
1244    }
1245
1246    if (!elt->can_do_snapshot()) {
1247       Dmsg2(DT_SNAPSHOT, "Device %d for file %s not snapshotable\n",
1248             statp.st_dev, file);
1249       return NULL;
1250    }
1251    Dmsg2(DT_SNAPSHOT, "Found device %d for file %s\n", elt->dev, file);
1252    return elt;
1253 }
1254
1255 /* Application to quiesce/un-quiesce */
1256 struct app {
1257    BPIPE *fd;                   /* Communication channel */
1258    char  *name;                 /* Pointer to the script name */
1259    char   cmd[1];               /* Command line */
1260 };
1261
1262 /* In the application manager, we want to run a set
1263  * of scripts and see if applications are running or
1264  * not on our partitions.
1265  *
1266  * We can specify application in the fileset, or just
1267  * try all application that are installed.
1268  *
1269  */
1270 class app_manager: public SMARTALLOC
1271 {
1272 private:
1273    JCR     *jcr;
1274    char    *appdir;             /* Where to find application scripts */
1275    alist   *applst;             /* Application list (script list to call) */
1276    int      apptimeout;         /* Timeout when trying to quiesce application */
1277    mtab    *mount_list;         /* snapshot set */
1278
1279 public:
1280     app_manager(JCR *ajcr, mtab *m, char *dir): 
1281       jcr(ajcr),
1282       appdir(dir),
1283       applst(New(alist(10, owned_by_alist))),
1284       apptimeout(300),
1285       mount_list(m) {};
1286
1287    ~app_manager() {
1288       delete applst;
1289    };
1290
1291    /* Put in a file the list of all devices that
1292     * are in the snapshot set
1293     */
1294    bool dump_snapshotset() {
1295       return true;
1296    };
1297
1298    /* Scan application */
1299    bool scan() {
1300       POOLMEM *results;
1301       bool  ret=false;
1302       char *end, *start;
1303       struct stat sp;
1304       struct app *elt = NULL;
1305
1306       if (!appdir || !*appdir || stat(appdir, &sp) == -1) {
1307          Dmsg0(DT_SNAPSHOT, "app not configured\n");
1308          return true;
1309       }
1310
1311       /* Create a file with the list of all devices that are suitable
1312        * for snapshot
1313        */
1314       dump_snapshotset();
1315
1316       results = get_pool_memory(PM_FNAME);
1317       if (run_program_full_output((char*)APPMANAGER_CMD, apptimeout, results) != 0) {
1318          berrno be;
1319          Dmsg2(DT_SNAPSHOT, "app scan error results=%s ERR=%s\n", results, be.bstrerror());
1320          goto bail_out;
1321       }
1322
1323       ret = true;
1324       start = results;
1325
1326       /* Put each line of the output in our list */
1327       for (start = results; start && *start;) {
1328          end = strchr(start, '\n');
1329          if (end) {
1330             *end = 0;
1331             elt = (struct app *) malloc(sizeof(struct app) + strlen(start) + 1);
1332
1333             elt->fd = NULL;
1334             strcpy(elt->cmd, start);
1335             elt->name = (char *)last_path_separator(elt->cmd);
1336             if (elt->name) {
1337                elt->name++;
1338
1339             } else {
1340                elt->name = elt->cmd;
1341             }
1342  
1343             applst->append(elt);
1344             Dmsg2(10, "+ %s (%s)\n", elt->name, elt->cmd);
1345             *end = '\n';
1346             end++;
1347          }
1348          start = end;
1349       }
1350    bail_out:
1351       free_pool_memory(results);
1352       return ret;
1353    };
1354
1355    bool unquiesce() {
1356       if (applst->size() == 0) {
1357          return true;
1358       }
1359
1360       Jmsg(jcr, M_INFO, 0, _("Un-Quiescing applications\n"));
1361       return true;
1362    };
1363
1364    /* Quiesce applications */
1365    bool quiesce() {
1366       bool ret = true;
1367
1368       if (applst->size() == 0) {
1369          return true;
1370       }
1371
1372       Jmsg(jcr, M_INFO, 0, _("Quiescing applications\n"));
1373
1374       for (int i = 0 ; i < applst->size() ; i++) {
1375          struct app *elt = (struct app *) applst->get(i);
1376          elt->fd = open_bpipe(elt->cmd, 0, "rw");
1377          if (!elt->fd) {
1378             /* Unable to execute the program */
1379             continue;
1380          }
1381          /* Send some commands here */
1382       }
1383       return ret;
1384    };
1385 };
1386
1387 snapshot_manager::snapshot_manager(JCR *ajcr):
1388    jcr(ajcr), mount_list(New(mtab())) {
1389 }
1390
1391 snapshot_manager::~snapshot_manager() {
1392    delete mount_list;
1393 }
1394
1395 bool snapshot_manager::cleanup_snapshots()
1396 {
1397    fs_device *elt;
1398    foreach_rblist(elt, mount_list->entries) {
1399       if (elt->can_do_snapshot() && elt->inSnapshotSet) {
1400          snapshot *s = elt->snap;
1401          if (!s->created) {
1402             continue;
1403          }
1404          if (s->unmount()) {
1405             /* TODO: Display an error? Can check mounted status */
1406          }
1407          /* When no retention is set, we delete the snapshot
1408           * just after the backup
1409           */
1410          if (s->Retention == 0) {
1411             if (s->del()) {
1412                Jmsg(jcr, M_INFO, 0, _("   Delete Snapshot for %s\n"), elt->mountpoint);
1413
1414             } else {
1415                Jmsg(jcr, M_ERROR, 0, _("   Unable to delete snapshot of %s ERR=%s\n"),
1416                     elt->mountpoint, s->errmsg);
1417             }
1418          }
1419       }
1420    }
1421    return true;
1422 }
1423
1424 bool snapshot_manager::create_snapshots()
1425 {
1426    /* No snapshot, no quiescing */
1427    if (mount_list->empty()) {
1428       Dmsg0(DT_SNAPSHOT, "The mount list is empty, no snapshot to take\n");
1429       return false;
1430    }
1431
1432    /* First thing to do is to quiesce application */
1433    app_manager apps(jcr, mount_list, (char *)APP_DIR);
1434
1435    /* TODO: Let see if we really need to abort
1436     * the snapshot part if application 
1437     */
1438    if (!apps.scan()) {
1439       return false;
1440    }
1441
1442    if (!apps.quiesce()) {
1443       return false;
1444    }
1445
1446    fs_device *elt;
1447    foreach_rblist(elt, mount_list->entries) {
1448       if (elt->can_do_snapshot() && elt->inSnapshotSet) {
1449          snapshot *s = elt->snap;
1450          if (s->create()) {
1451             Jmsg(jcr, M_INFO, 0, _("   Create Snapshot for %s\n"), elt->mountpoint);
1452
1453             if (s->Retention > 0) {/* Create snapshot catalog entry if we need to keep them */
1454                s->create_catalog_entry();
1455             }
1456
1457          } else if (s->status == 2) { /* Use Error message */
1458             elt->isSuitableForSnapshot = false; /* Disable snapshot for this device */
1459             Jmsg(jcr, M_ERROR, 0, _("   Unable to create snapshot of %s ERR=%s\n"),
1460                  elt->mountpoint, s->errmsg);
1461
1462          } else {               /* By default, an error in the creation should be fatal */
1463             Jmsg(jcr, M_FATAL, 0, _("   Unable to create snapshot of %s ERR=%s\n"),
1464                  elt->mountpoint, s->errmsg);
1465             apps.unquiesce();
1466             return false;
1467          }
1468
1469       } else {
1470          Dmsg3(DT_SNAPSHOT|20, "No Snapshot for %s suitable=%d inset=%d\n",
1471                elt->mountpoint, elt->isSuitableForSnapshot, elt->inSnapshotSet);
1472       }
1473    }
1474
1475    /* Snapshots are ok, we need to release applications */
1476    if (!apps.unquiesce()) {
1477       return false;
1478    }
1479
1480    /* Save the include context */
1481    POOL_MEM t;
1482    findINCEXE *old = get_incexe(jcr);
1483    findINCEXE *exclude = NULL;
1484    foreach_rblist(elt, mount_list->entries) {
1485       if (elt->can_do_snapshot() && elt->inSnapshotSet) {
1486          snapshot *s = elt->snap;
1487
1488          if (!s->mount()) {
1489             Jmsg(jcr, M_ERROR, 0, "   Unable to mount snapshot %s ERR=%s\n",
1490                  elt->mountpoint, s->errmsg);
1491
1492          } else if (*s->SnapDirectory) {
1493             if (!exclude) {
1494                exclude = new_exclude(jcr);
1495                /* Set the Exclude context */
1496                set_incexe(jcr, exclude);
1497             }
1498             Mmsg(t, "%s/.snapshots", elt->snap->SnapMountPoint);
1499             Dmsg1(DT_SNAPSHOT|10, "Excluding %s\n", t.c_str());
1500             add_file_to_fileset(jcr, t.c_str(), true);
1501          }
1502       }
1503    }
1504
1505    /* Restore the current context */
1506    if (exclude) {
1507       set_incexe(jcr, old);
1508    }
1509    return true;
1510 }
1511
1512 /* TODO: We might want to use some filters here */
1513 bool snapshot_manager::list_snapshots(alist *lst)
1514 {
1515    fs_device *elt;
1516
1517    /* No device, no snapshot */
1518    if (mount_list->dCount == 0) {
1519       Dmsg0(DT_SNAPSHOT, "mount list is empty, no snapshot\n");
1520       return false;
1521    }
1522
1523    foreach_rblist(elt, mount_list->entries) {
1524       if (elt->can_do_snapshot()) {
1525          elt->snap->list(lst);
1526       }
1527    }
1528    return true;
1529 }
1530
1531 void snapshot_manager::add_mount_point(uint32_t dev, const char *device,
1532                                        const char *mountpoint, const char *fstype)
1533 {
1534    bool       check=true;
1535    alist      list(10, not_owned_by_alist);
1536    fs_device *vol = New(fs_device(dev, device, mountpoint, fstype));
1537
1538    /* These FS are not supposed to use snapshot */
1539    const char *specialmp[] = {
1540       "/proc/",
1541       "/sys/",
1542       NULL
1543    };
1544    /* These FS are not supposed to use snapshot */
1545    const char *specialfs[] = {
1546       "autofs",
1547       "binfmt_misc",
1548       "cgroup",
1549       "configfs",
1550       "debugfs",
1551       "dev",
1552       "devpts",
1553       "devtmpfs",
1554       "ecryptfs",
1555       "fuse.gvfsd-fuse",
1556       "fusectl",
1557       "fd",
1558       "hugetlbfs",
1559       "mqueue",
1560       "proc",
1561       "pstore",
1562       "rpc_pipefs",
1563       "securityfs",
1564       "selinuxfs",
1565       "sysfs",
1566       "systemd-1",
1567       "tmpfs",
1568       NULL
1569    };
1570
1571    /* We skip directly /proc, /sys */
1572    for (int i=0; specialmp[i] ; i++) {
1573       if (strncasecmp(specialmp[i], mountpoint, strlen(specialmp[i])) == 0) {
1574          check = false;
1575          break;
1576       }
1577    }
1578
1579    if (check) {
1580       for (int i=0; specialfs[i] ; i++) {
1581          if (strcasecmp(specialfs[i], fstype) == 0) {
1582             check = false;
1583             break;
1584          }
1585       }
1586    }
1587
1588    if (check) {
1589       snapshot *snap = New(snapshot(jcr));
1590       snap->set_name(jcr->Job);
1591       vol->snap = snap;
1592
1593       if (snap->scan_subvolumes(vol, &list)) {
1594          for (int i = list.size() - 1 ; i >= 0 ; i--) {
1595             fs_device *d = (fs_device *)list.remove(i);
1596             add_mount_point(d->dev, d->device, d->mountpoint, d->fstype);
1597             delete d;
1598          }
1599       }
1600    }
1601
1602    Dmsg4(DT_SNAPSHOT|20, "Adding %s dev=%d snap?=%d to the mount list (%d)\n",
1603          mountpoint, dev, check, mount_list->dCount);
1604
1605    if (!mount_list->add_entry(vol)) {
1606       Dmsg1(DT_SNAPSHOT, "%s Already exists in mount list\n", vol->mountpoint);
1607       delete vol;
1608    }
1609 }
1610
1611 /* In this handler, we need to fill a mtab structure */
1612 static void add_handler(void *user_ctx,
1613                         struct stat *st,
1614                         const char *fstype,
1615                         const char *mountpoint,
1616                         const char *mntopts,
1617                         const char *device)
1618 {
1619    Dmsg5(DT_SNAPSHOT|50, "dev=%ld device=%s mountpoint=%s fstype=%s mntopts=%s\n",
1620          st->st_dev, device, mountpoint, fstype, mntopts);
1621
1622    /* TODO: If the fstype is btrfs or zfs, the fs might contains subvolumes,
1623     * and these subvolumes may not be reported in the mntent list. In this
1624     * case, we need to call proper btrfs/zfs commands to list them.
1625     *   # btrfs subvolume list /mnt
1626     *   ID 256 gen 17 top level 5 path test
1627     *   ID 259 gen 18 top level 5 path test/titi
1628     */
1629    snapshot_manager *snapmgr = (snapshot_manager *)user_ctx;
1630    snapmgr->add_mount_point(st->st_dev, device, mountpoint, fstype);
1631 }
1632
1633 bool snapshot_manager::scan_mtab()
1634 {
1635    return read_mtab(add_handler, this);
1636 }
1637
1638
1639 /* Scan the fileset to select partitions to snapshot */
1640 bool snapshot_manager::scan_fileset()
1641 {
1642    if (!jcr->ff || !jcr->ff->fileset) {
1643       Dmsg0(DT_SNAPSHOT, "No fileset associated with JCR\n");
1644       return false;
1645    }
1646
1647    findFILESET *fileset = jcr->ff->fileset;
1648    dlistString *node;
1649    int64_t      flags = 0;
1650
1651    for (int i=0; i<fileset->include_list.size(); i++) {
1652
1653       findFOPTS  *fo;
1654       findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
1655
1656       /* look through all files */
1657       foreach_dlist(node, &incexe->name_list) {
1658          char *fname = node->c_str();
1659          if (mount_list->add_in_snapshot_set(fname, &incexe->name_list, node)) {
1660             /* When all volumes are selected, we can stop */
1661             Dmsg0(DT_SNAPSHOT, "All Volumes are marked, stopping the loop here\n");
1662             goto all_included;
1663          }
1664       }
1665
1666       foreach_alist(fo, &incexe->opts_list) {
1667          flags |= fo->flags; /* We are looking for FO_MULTIFS and recurse */
1668       }
1669    }
1670
1671 all_included:
1672    /* If we allow recursion and multifs, we need to include sub volumes by hand
1673     * in the backup list
1674     */
1675    if (flags & FO_MULTIFS) {
1676       fs_device *elt, *elt2;
1677
1678       foreach_rblist(elt, mount_list->entries) {
1679          if (!elt->inFileSet) {
1680             continue;
1681          }
1682          alist     *lst = New(alist(10, not_owned_by_alist));
1683          mount_list->get_subvolumes(elt->dev, lst, jcr->ff);
1684          foreach_alist(elt2, lst) {
1685             if (elt2->inFileSet) {
1686                continue;
1687             }
1688
1689             /* TODO: See how to avoid having two entries for the same directory */
1690             /* Add the directory explicitely in the fileset */
1691             elt->include->insert_after(new_dlistString(elt2->mountpoint), elt->node);
1692
1693             if (mount_list->add_in_snapshot_set(elt2, elt->include, elt->node)) {
1694                /* When all volumes are selected, we can stop */
1695                Dmsg0(DT_SNAPSHOT, "All Volumes are marked, stopping the loop here\n");
1696                delete lst;
1697                return true;
1698             }
1699          }
1700          delete lst;
1701       }
1702    }
1703    return true;
1704 }
1705
1706 int snapshot_cmd(JCR *jcr)
1707 {
1708    BSOCK *dir = jcr->dir_bsock;
1709    int n, ret = 1;
1710    char ed1[50];
1711    snapshot snap(jcr);
1712    snap.check_buffer_size(dir->msglen);
1713
1714    n = sscanf(dir->msg, QueryCmd, snap.Name, snap.Volume, snap.Device, &snap.CreateTDate, snap.Type);
1715    if (n == 5) {
1716       snap.unbash_spaces();
1717       Dmsg0(DT_SNAPSHOT|10, "Doing query of a snapshot\n");
1718       n = snap.query();
1719       bash_spaces(snap.errmsg);
1720       dir->fsend("%d Snapshot status=%d size=%lld ERR=%s\n", n?2000:2999, snap.status, snap.size, snap.errmsg);
1721       goto bail_out;
1722    }
1723
1724    n = sscanf(dir->msg, LsCmd, snap.Name, snap.Volume, snap.Device, &snap.CreateTDate, snap.Type, snap.path);
1725    if (n == 6) {
1726       snap.unbash_spaces();
1727       Dmsg0(DT_SNAPSHOT|10, "Doing ls of a snapshot\n");
1728       n = snap.ls(dir);
1729       dir->signal(BNET_EOD);
1730       goto bail_out;
1731    }
1732
1733    n = sscanf(dir->msg, DelCmd, snap.Name, snap.Volume, snap.Device, &snap.CreateTDate, snap.Type);
1734    if (n == 5) {
1735       Dmsg0(DT_SNAPSHOT|10, "Doing del of a snapshot\n");
1736       snap.unbash_spaces();
1737       n = snap.del();
1738       bash_spaces(snap.errmsg);
1739       dir->fsend("%d Snapshot deleted ERR=%s\n", n?2000:2999, snap.errmsg);
1740       goto bail_out;
1741    }
1742
1743    n = sscanf(dir->msg, PruneCmd, snap.Volume, snap.Type);
1744    if (n == 2) {
1745       snap.unbash_spaces();
1746       n = snap.prune(dir);
1747       bash_spaces(snap.errmsg);
1748       dir->fsend("%d Snapshot pruned ERR=%s\n", n?2000:2999, snap.errmsg);
1749       Dmsg0(DT_SNAPSHOT|10, "Doing pruning of snapshots\n");
1750       goto bail_out;
1751    }
1752
1753    n = sscanf(dir->msg, SyncCmd, snap.Volume, snap.Type);
1754    if (n == 2) {
1755       snap.unbash_spaces();
1756       n = snap.sync(dir);
1757       bash_spaces(snap.errmsg);
1758       dir->fsend("%d Snapshot synced ERR=%s\n", n?2000:2999, snap.errmsg);
1759       Dmsg0(DT_SNAPSHOT|10, "Doing sync of snapshots\n");
1760       goto bail_out;
1761    }
1762
1763    /* TODO: Include a path name or a device name */
1764    if (strncmp(dir->msg, ListCmd, strlen(ListCmd)) == 0) {
1765       snapshot *elt;
1766       char      ed1[50];
1767       alist    *lst = New(alist(10, not_owned_by_alist));
1768       list_all_snapshots(jcr, lst);
1769       foreach_alist(elt, lst) {
1770          elt->bash_spaces();
1771          dir->fsend("volume=\"%s\" createtdate=\"%s\" name=\"%s\" device=\"%s\" status=%d error=\"%s\" type=\"%s\"\n",
1772                     elt->Volume, edit_uint64(elt->CreateTDate, ed1), 
1773                     elt->Name, elt->Device, elt->status, elt->errmsg, elt->Type);
1774          delete elt;
1775       }
1776       delete lst;
1777       dir->signal(BNET_EOD);      
1778       goto bail_out;
1779    }
1780
1781    n = sscanf(dir->msg, ConfCmd, ed1);
1782    if (n == 1) {
1783       jcr->snapshot_retention = str_to_uint64(ed1);
1784       dir->fsend("2000 Snapshot retention\n");
1785       goto bail_out;
1786    }
1787
1788    dir->fsend("2999 Snapshot command not found\n");
1789    dir->signal(BNET_EOD);
1790    ret = 0;
1791
1792 bail_out:
1793    return ret;
1794 }
1795
1796 bool snapshot_convert_path(JCR *jcr, FF_PKT *ff, dlist *filelist, dlistString *node)
1797 {
1798    Dmsg1(DT_SNAPSHOT, "snapshot_convert_path(%s)\n", ff->top_fname);
1799    snapshot_manager *snapmgr = jcr->snap_mgr;
1800    ff->strip_snap_path = false;
1801
1802    if (!snapmgr) {
1803       return true;
1804    }
1805
1806    fs_device *elt = snapmgr->mount_list->search(ff->top_fname);
1807    if (!elt) {
1808       return true;              /* not found */
1809    }
1810
1811    if (!ff->snap_fname) {
1812       ff->snap_fname = get_pool_memory(PM_FNAME);
1813    }
1814
1815    /* Convert the filename to the original path */
1816    if (!elt->snap->convert_path(ff)) {
1817       Dmsg2(DT_SNAPSHOT, "Device %d for file %s not snapshotable\n",
1818             elt->dev, ff->top_fname);
1819       return true;
1820    }
1821    return true;
1822 }
1823
1824 /* ListSnap[] = "CatReq Job=%s list_snapshot name=%s volume=%s device=%s tdate=%d type=%s before=%s after=%s expired=%d"; */
1825
1826 /* List Catalog entry of the current client */
1827 int snapshot_list_catalog(JCR *jcr,
1828                           const char *query,
1829                           alist *lst)
1830 {
1831    int ret = 0, i;
1832    arg_parser cmd;
1833    POOL_MEM q, tmp;
1834    if (cmd.parse_cmd(query) != bRC_OK) {
1835       Dmsg1(DT_SNAPSHOT, "Unable to decode query %s\n", query);
1836       return 0;
1837    }
1838    Mmsg(q, "CatReq Job=%s list_snapshot name=", jcr->Job);
1839    if ((i = cmd.find_arg_with_value("name")) >= 0) {
1840       bash_spaces(cmd.argv[i]);
1841       pm_strcat(q, cmd.argv[i]);
1842    }
1843
1844    pm_strcat(q, " volume=");
1845    if ((i = cmd.find_arg_with_value("volume")) >= 0) {
1846       bash_spaces(cmd.argv[i]);
1847       pm_strcat(q, cmd.argv[i]);
1848    }
1849
1850    pm_strcat(q, " device=");
1851    if ((i = cmd.find_arg_with_value("device")) >= 0) {
1852       bash_spaces(cmd.argv[i]);
1853       pm_strcat(q, cmd.argv[i]);
1854    }
1855
1856    pm_strcat(q, " tdate=");
1857    if ((i = cmd.find_arg_with_value("tdate")) >= 0) {
1858       bash_spaces(cmd.argv[i]);
1859       pm_strcat(q, cmd.argv[i]);
1860    }
1861
1862    pm_strcat(q, " type=");
1863    if ((i = cmd.find_arg_with_value("type")) >= 0) {
1864       bash_spaces(cmd.argv[i]);
1865       pm_strcat(q, cmd.argv[i]);
1866    }
1867
1868    pm_strcat(q, " before=");
1869    if ((i = cmd.find_arg_with_value("before")) >= 0) {
1870       bash_spaces(cmd.argv[i]);
1871       pm_strcat(q, cmd.argv[i]);
1872    }
1873
1874    pm_strcat(q, " after=");
1875    if ((i = cmd.find_arg_with_value("after")) >= 0) {
1876       bash_spaces(cmd.argv[i]);
1877       pm_strcat(q, cmd.argv[i]);
1878    }
1879
1880    pm_strcat(q, " expired=");
1881    if ((i = cmd.find_arg_with_value("expired")) >= 0) {
1882       bash_spaces(cmd.argv[i]);
1883       pm_strcat(q, cmd.argv[i]);
1884    }
1885
1886    jcr->dir_bsock->fsend("%s\n", q.c_str());
1887
1888    while (jcr->dir_bsock->recv() > 0) {
1889       if (cmd.parse_cmd(jcr->dir_bsock->msg) != bRC_OK) {
1890          Dmsg1(DT_SNAPSHOT, "Unable to decode director output %s\n", jcr->dir_bsock->msg);
1891
1892       } else {
1893          ret = 1;               /* OK */
1894          snapshot *s = New(snapshot(jcr));
1895          s->scan_arg(&cmd);
1896          lst->append(s);
1897       }
1898    }
1899    return ret;
1900 }