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