2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
5 Copyright (C) 2011-2014 Free Software Foundation Europe e.V.
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.
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.
15 This notice must be preserved when any source code is
16 conveyed and/or propagated.
18 Bacula(R) is a registered trademark of Kern Sibbald.
25 #include <sys/types.h>
26 #include <sys/mkdev.h> /* Define major() and minor() */
29 #define Dmsg(level, ...) do { \
30 if (level <= debug_level) { \
31 fprintf(debug, "%s:%d ", __FILE__ , __LINE__); \
32 fprintf(debug, __VA_ARGS__ ); \
36 #define Pmsg(level, ...) do { \
37 if (level <= debug_level) { \
38 fprintf(stderr, "%s:%d ", __FILE__ , __LINE__ ); \
39 fprintf(stderr, __VA_ARGS__ ); \
43 #define BSNAPSHOT_CONF SYSCONFDIR "/bsnapshot.conf"
45 static FILE *debug = NULL;
47 static void usage(const char *msg=NULL)
50 fprintf(stderr, _("ERROR %s\n\n"), msg);
56 " -d level Set debug level\n"
59 " -o logfile send debug to logfile\n"
61 " -T type volume type\n"
62 " -t check compatibility\n"
63 " -c specify configuration file\n"
64 "\n"), VERSION, LSMDATE);
68 static const char *Months[] = {
84 /* Skip leading slash(es) */
85 static bool makedir(char *path)
89 while (IsPathSeparator(*p)) {
92 while ((p = first_path_separator(p))) {
98 while (IsPathSeparator(*p)) {
102 /* If not having a ending / */
103 if (!IsPathSeparator(path[strlen(path) - 1])) {
109 /* Strip trailing junk and " */
110 void strip_quotes(char *str)
112 strip_trailing_junk(str);
113 for(char *p = str; *p ; p++) {
120 static void set_trace_file(const char *path)
122 char dt[MAX_TIME_LENGTH];
123 if (debug && debug != stderr) {
126 debug = fopen(path, "a");
130 Dmsg(10, "Starting bsnapshot %s\n",
131 bstrftime(dt, MAX_TIME_LENGTH, time(NULL)));
135 /* Small function to avoid double // in path name */
136 static void path_concat(POOLMEM *&dest, const char *path1, const char *path2, const char *path3) {
139 last = pm_strcpy(dest, path1);
140 last = MAX(last - 1, 0);
142 /* Check if the last char of dest is / and the first of path2 is / */
143 if (dest[last] == '/') {
144 if (path2[0] == '/') {
148 if (path2[0] != '/') {
149 pm_strcat(dest, "/");
153 last = pm_strcat(dest, path2);
154 last = MAX(last - 1, 0);
157 if (dest[last] == '/') {
158 if (path3[0] == '/') {
162 if (path3[0] != '/') {
163 pm_strcat(dest, "/");
166 pm_strcat(dest, path3);
170 static struct ini_items bsnap_cfg[] = {
171 // name handler comment required default
172 { "trace", ini_store_str, "", 0, NULL},
173 { "debug", ini_store_int32, "", 0, NULL},
174 { "sudo", ini_store_bool, "", 0, NULL},
175 { "disabled", ini_store_bool, "", 0, "no"},
176 { "retry", ini_store_int32, "", 0, "3"},
177 { "lvm_snapshot_size", ini_store_alist_str,"", 0, NULL},
178 { "skip_volume", ini_store_alist_str,"", 0, NULL},
179 { "snapshot_dir", ini_store_str, "", 0, NULL},
180 { "fail_job_on_error", ini_store_bool, "", 0, "yes"},
181 { NULL, NULL, NULL, 0, NULL}
186 char *action; /* list, create, delete... */
187 char *volume; /* snapshot device */
188 char *device; /* original device name */
189 char *name; /* snapshot name */
190 char *mountpoint; /* device mountpoint */
191 char *snapmountpoint; /* snapshot mountpoint */
192 char *type; /* snapshot type */
193 char *fstype; /* filesystem type */
194 const char *snapdir; /* .snapshot */
195 const char *sudo; /* prepend sudo to commands */
197 int retry; /* retry some operations */
198 bool disabled; /* disabled by config file */
199 bool fail_job_on_error; /* Fail job on snapshot error */
200 ConfigFile ini; /* Configuration file */
201 POOL_MEM config_file; /* Path to a config file */
204 action(getenv("SNAPSHOT_ACTION")),
205 volume(getenv("SNAPSHOT_VOLUME")),
206 device(getenv("SNAPSHOT_DEVICE")),
207 name( getenv("SNAPSHOT_NAME")),
208 mountpoint(getenv("SNAPSHOT_MOUNTPOINT")),
209 snapmountpoint(getenv("SNAPSHOT_SNAPMOUNTPOINT")),
210 type( getenv("SNAPSHOT_TYPE")),
211 fstype(getenv("SNAPSHOT_FSTYPE")),
212 snapdir(".snapshots"),
217 fail_job_on_error(true)
220 ini.register_items(bsnap_cfg, sizeof(struct ini_items));
222 if (stat(BSNAPSHOT_CONF, &sp) == 0) {
223 Dmsg(10, "conf=%s\n", BSNAPSHOT_CONF);
224 pm_strcpy(config_file, BSNAPSHOT_CONF);
236 if (strcmp(config_file.c_str(), "") != 0) {
237 Dmsg(10, "Reading configuration from %s\n", config_file.c_str());
238 if (!ini.parse(config_file.c_str())) {
239 printf("status=1 error=\"Unable to parse %s\"\n",
240 config_file.c_str());
243 pos = ini.get_item("debug");
244 if (ini.items[pos].found && debug_level == 0) {
245 debug_level = ini.items[pos].val.int32val;
247 pos = ini.get_item("trace");
248 if (ini.items[pos].found) {
249 set_trace_file(ini.items[pos].val.strval);
251 pos = ini.get_item("sudo");
252 if (ini.items[pos].found && ini.items[pos].val.boolval) {
255 pos = ini.get_item("snapshot_dir");
256 if (ini.items[pos].found) {
257 snapdir = ini.items[pos].val.strval;
259 pos = ini.get_item("retry");
260 if (ini.items[pos].found) {
261 retry = ini.items[pos].val.int32val;
263 pos = ini.get_item("disabled");
264 if (ini.items[pos].found) {
265 disabled = ini.items[pos].val.boolval;
267 pos = ini.get_item("fail_job_on_error");
268 if (ini.items[pos].found) {
269 fail_job_on_error = ini.items[pos].val.boolval;
278 const char *type; /* snapshot type, btrfs, zfs, etc.. */
279 POOLMEM *cmd; /* buffer to edit a command */
280 POOLMEM *path; /* buffer to edit volume path */
281 POOLMEM *fname; /* used for split_path_and_filename */
282 POOLMEM *errmsg; /* buffer to edit error message */
283 arguments *arg; /* program argument */
284 int pnl; /* path length */
285 int fnl; /* fname length */
287 snapshot(arguments *a, const char *t):
289 cmd(get_pool_memory(PM_NAME)),
290 path(get_pool_memory(PM_NAME)),
291 fname(get_pool_memory(PM_NAME)),
292 errmsg(get_pool_memory(PM_NAME)),
299 virtual ~snapshot() {
300 free_pool_memory(cmd);
301 free_pool_memory(path);
302 free_pool_memory(fname);
303 free_pool_memory(errmsg);
306 /* Basically, we check parameters here that are
307 * common to all backends
309 virtual int mount() {
310 Dmsg(10, "[%s] Doing mount command\n", type);
311 if (!arg->volume || !arg->name || !arg->device || !arg->mountpoint) {
312 Dmsg(10, "volume=%s name=%s device=%s mountpoint=%s\n",
313 NPRT(arg->volume), NPRT(arg->name),
314 NPRT(arg->device), NPRT(arg->mountpoint));
320 virtual int unmount() {
321 Dmsg(10, "[%s] Doing unmount command on %s\n", type,
322 NPRT(arg->snapmountpoint));
323 if (!arg->snapmountpoint) {
324 Dmsg(10, "snapmountpoint=%s\n", NPRT(arg->snapmountpoint));
330 virtual int support() {
331 Dmsg(10, "[%s] Doing support on %s (%s)\n", type, NPRT(arg->mountpoint),
333 if (!arg->fstype || !arg->mountpoint || !arg->device) {
334 Dmsg(10, "fstype=%s mountpoint=%s device=%s\n",
335 NPRT(arg->fstype), NPRT(arg->mountpoint), NPRT(arg->device));
341 virtual int check() {
342 Dmsg(10, "[%s] Doing check on %s\n", type, NPRT(arg->mountpoint));
343 if (!arg->mountpoint) {
344 Dmsg(10, "mountpoint=%s\n", NPRT(arg->mountpoint));
350 virtual int create() {
351 Dmsg(10, "[%s] Doing create %s\n", type, NPRT(arg->mountpoint));
352 if (!arg->mountpoint || !arg->name || !arg->device) {
353 Dmsg(10, "mountpoint=%s name=%s device=%s\n",
354 NPRT(arg->mountpoint), NPRT(arg->name), NPRT(arg->device));
361 Dmsg(10, "[%s] Doing del %s\n", type, NPRT(arg->volume));
362 if (!arg->volume || !arg->name) {
363 Dmsg(10, "volume=%s name=%s\n",
364 NPRT(arg->volume), NPRT(arg->name));
371 Dmsg(10, "[%s] Doing list on %s\n", type, NPRT(arg->device));
372 if (!arg->type || !arg->device || !arg->mountpoint) {
378 virtual int subvolumes() {
379 Dmsg(10, "[%s] Doing subvolumes %s\n", type, NPRT(arg->mountpoint));
380 if (!arg->fstype || !arg->device || !arg->mountpoint) {
386 /* Function used in create() to know if we mark the error as FATAL */
387 int get_error_code() {
388 Dmsg1(0, "get_error_code = %d\n", (int)arg->fail_job_on_error);
390 if (arg->fail_job_on_error) {
391 return 0; /* Fatal */
393 return 2; /* Error */
397 /* Structure used to sort subvolumes with btrfs backend */
402 char uuid[MAX_NAME_LENGTH];
403 char puuid[MAX_NAME_LENGTH];
404 char otime[MAX_NAME_LENGTH];
408 int vols_compare_id(void *item1, void *item2)
410 vols *vol1 = (vols *) item1;
411 vols *vol2 = (vols *) item2;
413 if (vol1->id > vol2->id) {
416 } else if (vol1->id < vol2->id) {
424 int vols_compare_uuid(void *item1, void *item2)
426 vols *vol1 = (vols *) item1;
427 vols *vol2 = (vols *) item2;
429 return strcmp(vol1->uuid, vol2->uuid);
433 class btrfs: public snapshot {
435 btrfs(arguments *arg): snapshot(arg, "btrfs") {};
437 /* With BTRFS, the volume is already mounted */
439 if (!snapshot::mount()) {
442 split_path_and_filename(arg->volume, &path, &pnl, &fname, &fnl);
443 fprintf(stdout, "status=1 snapmountpoint=\"%s\" snapdirectory=\"%s\"\n",
449 if (!snapshot::unmount()) {
452 printf("status=1\n");
457 if (!snapshot::support()) {
460 /* If the fstype is btrfs, snapshots are supported */
462 Mmsg(cmd, "%sbtrfs filesystem label \"%s\"", arg->sudo, arg->mountpoint);
463 if (run_program(cmd, 60, errmsg)) {
464 printf("status=0 type=btrfs\n");
467 Dmsg(0, "output=%s\n", errmsg);
469 printf("status=1 device=\"%s\" type=btrfs\n", arg->mountpoint);
474 if (!snapshot::check()) {
481 utime_t createdate = 0;
483 if (!snapshot::create()) {
487 Mmsg(path, "%s/%s", arg->mountpoint, arg->snapdir);
488 if (!makedir(path)) {
489 printf("status=%d error=\"Unable to create mountpoint directory %s errno=%d\n",
491 arg->mountpoint, errno);
495 Dmsg(10, "mountpoint=%s snapdir=%s name=%s\n", arg->mountpoint, arg->snapdir, arg->name);
496 path_concat(path, arg->mountpoint, arg->snapdir, arg->name);
497 Dmsg(10, "path=%s\n", path);
499 /* Create the actual btrfs snapshot */
500 Mmsg(cmd, "%sbtrfs subvolume snapshot -r \"%s\" \"%s\"",
501 arg->sudo, arg->mountpoint, path);
503 if (run_program(cmd, 60, errmsg)) {
504 Dmsg(10, "Unable to create snapshot %s %s\n", arg->mountpoint, errmsg);
505 strip_quotes(errmsg);
506 printf("status=%d error=\"Unable to create snapshot %s\"\n",
512 /* On SLES12 btrfs 3.16, commands on "/" returns "doesn't belong to btrfs mount point" */
513 Mmsg(cmd, "%sbtrfs subvolume show \"%s\"", arg->sudo, path);
514 if (run_program_full_output(cmd, 60, errmsg)) {
515 Dmsg(10, "Unable to display snapshot stats %s %s\n", arg->mountpoint, errmsg);
518 /* TODO: Check that btrfs subvolume show is reporting "Creation time:" */
519 char *p = strstr(errmsg, "Creation time:");
521 p += strlen("Creation time:");
523 createdate = str_to_utime(p);
526 Dmsg(10, "Unable to find Creation time on %s %s\n", arg->mountpoint, errmsg);
531 createdate = time(NULL);
533 printf("status=1 volume=\"%s\" createtdate=%s type=btrfs\n",
534 path, edit_uint64(createdate, ed1));
539 if (!snapshot::del()) {
543 Mmsg(cmd, "%sbtrfs subvolume delete \"%s\"", arg->sudo, arg->volume);
544 if (run_program(cmd, 300, errmsg)) {
545 Dmsg(10, "Unable to delete snapshot %s\n", errmsg);
546 strip_quotes(errmsg);
547 printf("status=0 type=btrfs error=\"%s\"\n", errmsg);
550 printf("status=1\n");
554 /* btrfs subvolume list -u -q -s /tmp/regress/btrfs
555 * ID 259 gen 52 top level 5 parent_uuid - uuid baf4b5d7-28d0-9b4a-856e-36e6fd4fbc96 path .snapshots/aaa
558 char *p, *p2, *end, *path;
559 char id[50], day[50], hour[50];
560 struct vols *v = NULL, *v2;
563 if (!snapshot::list()) {
566 Mmsg(cmd, "%sbtrfs subvolume list -u -q -o -s \"%s\"", arg->sudo, arg->mountpoint);
567 if (run_program_full_output(cmd, 300, errmsg)) {
568 Dmsg(10, "Unable to list snapshot %s\n", errmsg);
569 strip_quotes(errmsg);
570 printf("status=0 type=btrfs error=\"%s\"\n", errmsg);
574 lst = New(rblist(v, &v->link));
576 /* ID 259 gen 52 top level 5 parent_uuid - uuid baf4b5d7-28d0-9b4a-856e-36e6fd4fbc96 path .snapshots/aaa */
577 for (p = errmsg; p && *p ;) {
578 Dmsg(20, "getting subvolumes from %s", p);
580 /* Replace final \n by \0 to have strstr() happy */
581 end = strchr(p, '\n');
583 /* If end=NULL, we are at the end of the buffer (without trailing \n) */
588 /* Each line is supposed to start with "ID", and end with "path" */
590 if (sscanf(p, "ID %50s ", id) == 1) { /* We found ID, look for path */
591 p2 = strstr(p, "path ");
593 path = p2 + strlen("path ");
594 v = (struct vols*) malloc(sizeof (vols) + strlen(path) + 1);
595 *v->otime = *v->uuid = *v->puuid = 0;
596 v->id = str_to_int64(id);
598 strcpy(v->path, path);
600 p2 = strstr(p, "otime");
601 if (p2 && sscanf(p2, "otime %50s %50s", day, hour) == 2) {
602 bsnprintf(v->otime, sizeof(v->otime), "%s %s", day, hour);
605 p2 = strstr(p, "parent_uuid ");
606 if (p2 && sscanf(p2, "parent_uuid %127s", v->puuid) == 1) {
608 p2 = strstr(p, " uuid ");
609 if (p2 && sscanf(p2, " uuid %127s", v->uuid) == 1) {
611 v2 = (struct vols *)lst->insert(v, vols_compare_uuid);
617 /* Replace final \n by \0 to have strstr() happy */
618 Dmsg(10, "puuid=%s uuid=%s path=%s\n", v2->puuid, v2->uuid, v2->path);
624 Dmsg(10, "Unable to decode \"%s\" line\n", p);
630 /* If end==NULL, we stop */
634 foreach_rblist(v, lst) {
635 char *name = v->path;
636 int len = strlen(arg->snapdir);
637 if ((p = strstr(v->path, arg->snapdir))) {
638 name = p + len + ((arg->snapdir[len-1] == '/') ? 0 : 1);
640 printf("volume=\"%s%s%s\" name=\"%s\" device=\"%s\" createdate=\"%s\" type=\"btrfs\"\n",
642 arg->mountpoint[strlen(arg->mountpoint) - 1] == '/' ? "": "/",
654 void scan_subvolumes(char *buf, rblist *lst) {
658 struct vols *elt1 = NULL, *elt2 = NULL;
660 /* btrfs subvolume list /var/lib/pacman/
661 * ID 349 gen 383 top level 5 path test
662 * ID 354 gen 391 cgen 391 top level 5 otime 2014-11-05 17:49:07 path .snapshots/aa
664 for (p = buf; p && *p ;) {
665 Dmsg(20, "getting subvolumes from %s", p);
667 /* Replace final \n by \0 to have strstr() happy */
668 end = strchr(p, '\n');
669 /* If end=NULL, we are at the end of the buffer (without trailing \n) */
674 /* Each line is supposed to start with "ID", and end with "path" */
675 ok = (sscanf(p, "ID %50s ", id) == 1);
676 if (ok) { /* We found ID, look for path */
677 p = strstr(p, "path ");
679 p += strlen("path ");
681 elt1 = (struct vols *) malloc(sizeof(struct vols) + strlen(p) + 1);
682 elt1->id = str_to_int64(id);
684 strcpy(elt1->path, p);
685 Dmsg(10, "Found path %s for id %s\n", elt1->path, id);
686 elt2 = (struct vols *)lst->insert(elt1, vols_compare_id);
692 Dmsg(10, "Unable to find the path in this line\n");
696 Dmsg(10, "Unable to decode %s line\n", p);
702 /* If end==NULL, we stop */
707 /* List subvolumes, they may not be listed by mount */
711 struct vols *elt1 = NULL;
714 Mmsg(cmd, "%sbtrfs subvolume show \"%s\"", arg->sudo, arg->mountpoint);
715 if (run_program_full_output(cmd, 300, errmsg)) {
716 Dmsg(10, "Unable to get information %s\n", errmsg);
717 strip_quotes(errmsg);
718 printf("status=0 type=btrfs error=\"%s\"\n", errmsg);
722 /* TODO: Very week way to analyse FS */
723 if (!strstr(errmsg, "is btrfs root")) {
724 printf("status=0 type=btrfs error=\"Not btrfs root fs\"\n");
728 Mmsg(cmd, "%sbtrfs subvolume list -s \"%s\"", arg->sudo, arg->mountpoint);
729 if (run_program_full_output(cmd, 300, errmsg)) {
730 Dmsg(10, "Unable to list snapshot snapshot %s\n", errmsg);
731 strip_quotes(errmsg);
732 printf("status=0 type=btrfs error=\"%s\"\n", errmsg);
736 lst = New(rblist(elt1, &elt1->link));
737 scan_subvolumes(errmsg, lst);
739 Mmsg(cmd, "%sbtrfs subvolume list \"%s\"", arg->sudo, arg->mountpoint);
740 if (run_program_full_output(cmd, 300, errmsg)) {
741 Dmsg(10, "Unable to list subvolume %s\n", errmsg);
742 strip_quotes(errmsg);
743 printf("status=0 type=btrfs error=\"%s\"\n", errmsg);
747 scan_subvolumes(errmsg, lst);
749 foreach_rblist(elt1, lst) {
750 if (elt1->count > 0) { /* Looks to be a snapshot, we saw two entries */
754 path_concat(path, arg->mountpoint, elt1->path, NULL);
756 if (stat(path, &sp) == 0) {
757 printf("dev=%s mountpoint=\"%s\" fstype=btrfs\n",
758 edit_uint64(sp.st_dev, ed1), path);
761 Dmsg(10, "Unable to stat %s (%s)\n", elt1->path, path);
770 * zpool create pool /dev/device
771 * zfs create pool/eric
772 * zfs set mountpoint=/mnt test/eric
773 * zfs mount pool/eric
776 class zfs: public snapshot {
778 zfs(arguments *arg): snapshot(arg, "zfs") {
779 arg->snapdir = ".zfs/snapshot";
782 /* With ZFS, the volume is already mounted
783 * but on linux https://github.com/zfsonlinux/zfs/issues/173
784 * we need to use the mount command.
785 * TODO: Adapt the code for solaris
790 if (!snapshot::mount()) {
794 path_concat(path, arg->mountpoint, arg->snapdir, arg->name);
796 if (stat(path, &sp) != 0) {
797 /* See if we can change the snapdir attribute */
798 Mmsg(cmd, "%szfs set snapdir=visible \"%s\"", arg->sudo, arg->device);
799 if (run_program(cmd, 60, errmsg)) {
800 Dmsg(10, "Unable to change the snapdir attribute %s %s\n", arg->device, errmsg);
801 strip_quotes(errmsg);
802 printf("status=0 error=\"Unable to mount snapshot %s\"\n", errmsg);
805 if (stat(path, &sp) != 0) {
806 Dmsg(10, "Unable to get the snapdir %s %s\n", arg->snapdir, arg->device);
807 strip_quotes(errmsg);
808 printf("status=0 error=\"Unable to mount snapshot, no snapdir %s\"\n", arg->snapdir);
812 #if 0 /* On linux, this function is broken for now */
814 Mmsg(cmd, "%smount -t %s \"%s\" \"%s\"", arg->sudo, arg->fstype, arg->volume, path);
815 if (run_program(cmd, 60, errmsg)) {
816 Dmsg(10, "Unable to create mount snapshot %s %s\n", arg->volume, errmsg);
817 strip_quotes(errmsg);
818 printf("status=0 error=\"Unable to mount snapshot %s\"\n", errmsg);
823 fprintf(stdout, "status=1 snapmountpoint=\"%s\" snapdirectory=\"%s/%s\"\n",
824 path, arg->mountpoint, arg->snapdir);
828 /* No need to unmount something special */
830 printf("status=1\n");
835 if (!snapshot::support()) {
838 Mmsg(cmd, "%szfs list -H -o name \"%s\"", arg->sudo, arg->mountpoint);
839 if (run_program(cmd, 60, errmsg)) {
840 Dmsg(10, "Unable to get device %s %s\n", arg->mountpoint, errmsg);
841 strip_quotes(errmsg);
842 printf("status=0 error=\"Unable to get device %s\"\n", errmsg);
845 strip_trailing_junk(errmsg);
846 /* If the fstype is zfs, snapshots are supported */
847 printf("status=1 device=\"%s\" type=zfs\n", errmsg);
854 if (!snapshot::create()) {
858 Mmsg(path, "%s@%s", arg->device, arg->name);
860 /* Create the actual zfs snapshot */
861 Mmsg(cmd, "%szfs snapshot \"%s\"", arg->sudo, path);
863 if (run_program(cmd, 60, errmsg)) {
864 Dmsg(10, "Unable to create snapshot %s %s\n", arg->device, errmsg);
865 strip_quotes(errmsg);
866 printf("status=%d error=\"Unable to create snapshot %s\"\n",
872 Mmsg(cmd, "%szfs get -p creation \"%s\"", arg->sudo, path);
873 if (run_program_full_output(cmd, 60, errmsg)) {
874 Dmsg(10, "Unable to display snapshot stats %s %s\n", arg->device, errmsg);
875 strip_quotes(errmsg);
876 printf("status=%d error=\"Unable to get snapshot info %s\"\n",
882 /* TODO: Check that zfs get is reporting "creation" time */
883 Mmsg(cmd, "NAME PROPERTY VALUE SOURCE\n%s creation %%s", path);
884 if (sscanf(errmsg, cmd, ed1) == 1) {
885 Dmsg(10, "Found CreateTDate=%s\n", ed1);
886 printf("status=1 volume=\"%s\" createtdate=%s type=zfs\n",
890 printf("status=1 volume=\"%s\" createtdate=%s type=zfs\n",
891 path, edit_uint64(time(NULL), ed1));
897 if (!snapshot::del()) {
901 Mmsg(cmd, "%szfs destroy \"%s\"", arg->sudo, arg->volume);
902 if (run_program(cmd, 300, errmsg)) {
903 Dmsg(10, "Unable to delete snapshot %s\n", errmsg);
904 strip_quotes(errmsg);
905 printf("status=0 type=zfs error=\"%s\"\n", errmsg);
908 printf("status=1\n");
912 /* zfs list -t snapshot
913 * test/eric@snap1 17K - 21K -
914 * test/eric@snap2 17K - 21K -
916 * it is possible to change fields to display with -o
920 if (!snapshot::list()) {
924 Mmsg(cmd, "%szfs list -t snapshot -H -o name,used,creation", arg->sudo);
925 /* rpool@basezone_snap00 0 Fri Mar 6 9:55 2015 */
926 if (run_program_full_output(cmd, 60, errmsg)) {
927 Dmsg(10, "Unable to list snapshot %s\n", errmsg);
928 strip_quotes(errmsg);
929 printf("status=0 error=\"Unable to list snapshot %s\"\n", errmsg);
933 int i = 1, Day, Year, Hour, Min;
934 char DayW[50], Month[50], CreateDate[50];
938 for (char *p = errmsg; p && *p ; p++) {
941 /* Flush the current one */
942 if (!arg->device || strcmp(arg->device, buf[0]) == 0) {
944 if (sscanf(buf[3], "%s %s %d %d:%d %d",
945 DayW, Month, &Day, &Hour, &Min, &Year) == 6)
947 /* Get a clean iso format */
948 for (int j=1; j <= 12 ; j++) {
949 if (strcmp(Month, Months[j]) == 0) {
950 snprintf(Month, sizeof(Month), "%02d", j);
953 snprintf(CreateDate, sizeof(CreateDate), "%d-%s-%02d %02d:%02d:00",
954 Year, Month, Day, Hour, Min);
957 printf("volume=\"%s@%s\" name=\"%s\" device=\"%s\" size=\"%s\" "
958 "createdate=\"%s\" status=1 error=\"\" type=\"zfs\"\n",
959 buf[0], buf[1], buf[1], buf[0], buf[2], buf[3]);
961 Dmsg(10, "Do not list %s@%s\n", buf[0], buf[1]);
966 buf[1] = buf[2] = buf[3] = "";
968 } else if ((*p == '\t' || *p == '@') && i < 4) {
978 /* Structure of the LVS output */
984 /* -1 is mandatory, -2 is optionnal */
985 static Header lvs_header[] = {
987 {"Path", -1}, /* Volume Path: /dev/ubuntu-vg/root */
988 {"DMPath",-2}, /* Device mapper Path /dev/mapper/ubuntu--vg-root */
989 {"LV", -1}, /* Volume Name: root */
990 {"Attr", -1}, /* Attributes: -wi-ao--- */
991 {"KMaj", -1}, /* Kernel Major: 252 */
992 {"KMin", -1}, /* Kernel Minor: 0 */
993 {"LSize", -1}, /* Size (b) */
994 {"#Seg", -1}, /* Number of segments */
998 {"Time", -1}, /* Creation date */
1002 static Header vgs_header[] = {
1004 {"VG", -1}, /* VG Name: vgroot */
1005 {"VSize", -1}, /* Size */
1006 {"VFree", -1}, /* Space left */
1007 {"#Ext", -1}, /* Nb Ext */
1008 {"Free", -1}, /* Nb Ext free */
1009 {"Ext", -1}, /* Ext size */
1013 /* LVM backend, not finished */
1014 class lvm: public snapshot {
1017 int lvs_nbelt, vgs_nbelt;
1019 lvm(arguments *arg):
1020 snapshot(arg, "lvm"), lvs(NULL), vgs(NULL), lvs_nbelt(0),
1024 free_header(lvs, lvs_nbelt);
1025 free_header(vgs, vgs_nbelt);
1028 void free_header(alist *lst, int nbelt) {
1031 /* cleanup at the end */
1032 foreach_alist(current, lst) {
1033 for (int j=0; j < nbelt ; j++) {
1034 Dmsg(50, "current[%d] = %s\n", j, current[j]);
1043 char *get_vg_from_lv_path(char *path, char *vg, int max) {
1050 /* Make a copy of the path */
1051 bstrncpy(vg, path, max);
1054 if (strncmp(path, "/dev/", 5) != 0) {
1055 Dmsg(10, "Strange path %s\n", path);
1058 path += 5; /* skip /dev/ */
1060 /* End the string at the last / */
1061 p = strchr(path, '/');
1063 Dmsg(10, "Strange end of path %s\n", path);
1071 /* Report the space available on VG */
1072 int64_t get_space_available(char *lv) {
1074 char *vgname = get_vg_from_lv_path(get_lv_value(lv, "Path"),
1078 char *s = get_vg_value(vgname, "VFree");
1080 return str_to_int64(s);
1083 Dmsg(10, "Unable to get VFree\n");
1087 Dmsg(10, "Unable to get VG from %s\n", lv);
1092 /* return vg_ssd-pacman */
1093 char *get_lv_from_dm(char *dm, POOLMEM **ret, uint32_t *major, uint32_t *minor) {
1098 /* Looks to be a device mapper, need to convert the name */
1099 if (strncmp(dm, "/dev/dm", strlen("/dev/dm")) != 0) {
1102 if (stat(dm, &sp) < 0) {
1106 Mmsg(cmd, "%sdmsetup ls", arg->sudo);
1107 if (run_program_full_output(cmd, 60, errmsg)) {
1108 Dmsg(10, "Unable to query dmsetup %s\n", errmsg);
1111 /* vg_ssd-pacman-real (254:1)
1112 * vg_ssd-pacman (254:0)
1114 * vg_ssd-pacman-real (254, 1)
1115 * vg_ssd-pacman-real (254, 1)
1117 *ret = check_pool_memory_size(*ret, strlen(errmsg)+1);
1118 for (start = p = errmsg; *p ; p++) {
1121 if (sscanf(start, "%s (%d:%d)", *ret, &maj, &min) == 3 ||
1122 sscanf(start, "%s (%d, %d)", *ret, &maj, &min) == 3)
1124 if (maj == major(sp.st_rdev) &&
1125 min == minor(sp.st_rdev))
1136 /* The LV path from name or dmpath */
1137 char **get_lv(char *lv) {
1138 char **elt = NULL, *dm = NULL;
1139 int path = get_value_pos(lvs_header, "Path");
1140 int dmpath = get_value_pos(lvs_header, "DMPath");
1141 int kmaj = get_value_pos(lvs_header, "KMaj");
1142 int kmin = get_value_pos(lvs_header, "KMin");
1143 uint32_t min = 0, maj = 0;
1144 POOLMEM *buf = get_pool_memory(PM_FNAME);
1146 if (!lv || (path < 0 && dmpath < 0)) {
1147 Dmsg(10, "Unable to get LV parameters\n");
1151 dm = get_lv_from_dm(lv, &buf, &maj, &min);
1152 Dmsg(50, "%s = get_lv_from_dm(%s, %s, %d, %d)\n", dm, lv, buf, maj, min);
1154 /* HERE: Need to loop over LVs */
1155 foreach_alist(elt, lvs) {
1156 if (path > 0 && strcmp(NPRT(elt[path]), lv) == 0) {
1160 if (dmpath > 0 && strcmp(NPRT(elt[dmpath]), lv) == 0) {
1164 /* Try by Minor/Major if comming from device mapper */
1165 if ((maj && kmaj && str_to_uint64(elt[kmaj]) == maj) &&
1166 (min && kmin && str_to_uint64(elt[kmin]) == min))
1171 /* Find if /dev/mapper/vg_ssd-pacman matches vg_ssd-pacman */
1172 if (dm && dmpath && strlen(elt[dmpath]) > strlen("/dev/mapper/")) {
1173 if (strcmp(elt[dmpath] + strlen("/dev/mapper/"), dm) == 0) {
1178 /* Special case for old LVM where mapper path doesn't exist */
1179 if (dmpath < 0 && strncmp("/dev/mapper/", lv, 12) == 0) {
1181 POOLMEM *buf2 = get_memory(strlen(elt[path])*2+10);
1182 pm_strcpy(buf2, "/dev/mapper/");
1184 char *d = buf2 + 12; /* Skip /dev/mapper/ */
1187 /* Keep the same path, but escape - to -- and / to - */
1188 for (char *p = elt[path]+5; *p ; p++) {
1192 /* Escape / to - if needed */
1193 *d++ = (*p == '/') ? '-' : *p;
1196 ret = (strcmp(buf2, lv) == 0);
1197 free_pool_memory(buf2);
1204 Dmsg(10, "%s not found in lv list\n", lv);
1205 return NULL; /* not found */
1209 free_pool_memory(buf);
1214 /* Report LV Size in bytes */
1215 int64_t get_lv_size(char *name) {
1216 char **elt = get_lv(arg->device);
1223 sp = get_value_pos(lvs_header, "LSize");
1224 /* Check if we have enough space on the VG */
1225 return str_to_int64(elt[sp]);
1228 char *get_lv_value(char *name, const char *value) {
1229 return get_value(lvs_header, lvs_nbelt, lvs, name, value);
1232 int get_value_pos(Header *header, const char *value) {
1233 for (int i = 0; header[i].name ; i++) {
1234 if (strcmp(header[i].name, value) == 0) {
1235 return header[i].pos;
1238 return -1; /* not found */
1241 /* Return an element value */
1242 char *get_value(Header *header, int nbelt, alist *lst,
1243 char *name, const char *value) {
1245 int pos = get_value_pos(header, value);
1246 int id = header[0].pos; /* position name */
1248 if (pos < 0 || id == -1) {
1251 /* Loop over elements we have, and return the value that is asked */
1252 foreach_alist(elt, lst) {
1253 if (strcmp(NPRT(elt[id]), name) == 0) {
1260 /* Return a parameter for a VolumeGroup */
1261 char *get_vg_value(char *vg, const char *value) {
1262 return get_value(vgs_header, vgs_nbelt, vgs, vg, value);
1265 /* Get snapshot size, look in config file if needed */
1266 int get_lvm_snapshot_size(char *lv) {
1272 int pos = arg->ini.get_item("lvm_snapshot_size");
1273 if (!arg->ini.items[pos].found) {
1274 return -1; /* Nothing specified, stop here */
1277 lst = arg->ini.items[pos].val.alistval;
1279 /* /dev/ubuntu-vg/root:100M
1280 * /dev/ubuntu-vg/home:10%
1281 * /dev/ubuntu-vg/var:200GB
1283 foreach_alist(tmp, lst) {
1284 char *p = strchr(tmp, ':');
1286 /* Check the LV name */
1287 if (p && strncmp(tmp, lv, p - tmp) != 0) {
1291 /* This is a percent */
1292 if (strchr(p+1, '%') != NULL) {
1293 Dmsg(10, "Found a %%\n");
1294 s = str_to_int64(p+1);
1296 /* Compute the requested size */
1297 sp = get_value_pos(lvs_header, "LSize");
1299 size = str_to_int64(elt[sp]);
1300 return size * (s / 100);
1303 /* It might be a size */
1304 if (size_to_uint64(p+1, strlen(p+1), &s)) {
1305 Dmsg(10, "Found size %ld\n", s);
1308 Dmsg(10, "Unable to use %s\n", tmp);
1316 char *name, *ts, buf[128], *lvname;
1317 int64_t size, ssize, maxsize;
1318 if (!snapshot::create()) {
1322 if (!parse_lvs_output() ||
1323 !parse_vgs_output())
1325 printf("status=%d error=\"Unable parse lvs or vgs output\"\n",
1330 path_concat(path, arg->mountpoint, arg->snapdir, arg->name);
1332 if (!makedir(path)) {
1333 printf("status=%d error=\"Unable to create mountpoint directory %s errno=%d\n",
1335 arg->mountpoint, errno);
1339 name = get_lv_value(arg->device, "LV");
1340 size = get_lv_size(arg->device);
1342 printf("status=%d error=\"Unable to get lv size\"\n",
1347 ssize = get_lvm_snapshot_size(arg->device);
1351 size = size / 10; /* Ask to get 10% */
1354 size = (size / 512L) * 512L;
1356 lvname = get_lv_value(arg->device, "Path");
1357 maxsize = get_space_available(lvname);
1358 Dmsg(10, "maxsize=%ld size=%ld\n", maxsize, size);
1361 printf("status=%d error=\"Unable to detect maxsize\" type=lvm\n",
1366 if (size > maxsize) {
1367 char ed1[50], ed2[50];
1368 printf("status=%d error=\"Not enough space left on VG %sB, "
1369 "%sB is required\" type=lvm\n",
1371 edit_uint64_with_suffix(maxsize, ed1),
1372 edit_uint64_with_suffix(size, ed2));
1376 /* TODO: Need to get the volume name and add the snapshot
1379 Mmsg(cmd, "%slvcreate -s -n \"%s_%s\" -L %lldb \"%s\"",
1380 arg->sudo, name, arg->name, size, arg->device);
1381 if (run_program(cmd, 60, errmsg)) {
1382 Dmsg(10, "Unable to create snapshot %s %s\n", arg->name, errmsg);
1383 strip_quotes(errmsg);
1384 printf("status=0 error=\"Unable to create snapshot %s\"\n", errmsg);
1387 if (!parse_lvs_output()) {
1388 Dmsg(10, "Unable to parse lvm output after snapshot creation\n");
1389 printf("status=0 error=\"Unable to parse lvs\"\n");
1393 Mmsg(cmd, "%s_%s", arg->device, arg->name);
1394 ts = get_lv_value(cmd, "Time");
1396 Dmsg(10, "Unable to find snapshot in lvs output\n");
1397 bstrftimes(buf, sizeof(buf), time(NULL));
1400 Dmsg(10, "status=1 volume=\"%s_%s\" createdate=\"%s\" type=lvm\n",
1401 arg->device, arg->name, ts);
1402 printf("status=1 volume=\"%s_%s\" createdate=\"%s\" type=lvm\n",
1403 arg->device, arg->name, ts);
1408 if (!snapshot::del()) {
1411 Mmsg(cmd, "%slvremove -f \"%s\"",
1412 arg->sudo, arg->volume);
1414 if (run_program(cmd, 60, errmsg)) {
1415 Dmsg(10, "Unable to delete snapshot %s %s\n", arg->name, errmsg);
1416 strip_quotes(errmsg);
1417 printf("status=0 error=\"Unable to delete snapshot %s\"\n", errmsg);
1421 printf("status=1\n");
1426 if (!snapshot::check()) {
1430 for (int i = 0; vgs_header[i].name ; i++) {
1431 if (vgs_header[i].pos == -1) {
1432 printf("status=0 error=\"Unable to use output of vgs command."
1433 " %s is missing.\"\n",
1434 vgs_header[i].name);
1440 for (int i = 0; lvs_header[i].name ; i++) {
1441 if (lvs_header[i].pos == -1) {
1442 printf("status=0 error=\"Unable to use output of lvs command."
1443 " %s is missing.\"\n",
1444 lvs_header[i].name);
1451 void strip_double_slashes(char *fname)
1455 p = strpbrk(p, "/\\");
1457 if (IsPathSeparator(p[1])) {
1466 if (!snapshot::mount()) {
1470 path_concat(path, arg->mountpoint, arg->snapdir, arg->name);
1472 if (!makedir(path)) {
1473 printf("status=0 error=\"Unable to create mount point %s errno=%d\"\n",
1478 Mmsg(cmd, "%smount -o ro \"%s\" \"%s\"", arg->sudo, arg->volume, path);
1479 if (run_program(cmd, 60, errmsg) != 0) {
1480 Dmsg(10, "Unable to mount volume. ERR=%s\n", errmsg);
1481 strip_quotes(errmsg);
1482 printf("status=0 error=\"Unable to mount the device %s\"\n", errmsg);
1486 Dmsg(10, "status=1 snapmountpoint=\"%s\" snapdirectory=\"%s/%s\"\n",
1487 path, arg->mountpoint, arg->snapdir);
1488 printf("status=1 snapmountpoint=\"%s\" snapdirectory=\"%s/%s\"\n",
1489 path, arg->mountpoint, arg->snapdir);
1494 int ret, retry = arg->retry;
1496 if (!snapshot::unmount()) {
1500 Mmsg(cmd, "%sumount \"%s\"", arg->sudo, arg->snapmountpoint);
1502 ret = run_program(cmd, 60, errmsg);
1504 Dmsg(10, "Unable to unmount the directory. ERR=%s\n", errmsg);
1507 } while (ret != 0 && retry-- > 0);
1510 Dmsg(10, "Unable to mount volume. ERR=%s\n", errmsg);
1511 strip_quotes(errmsg);
1512 printf("status=0 error=\"Unable to umount the device %s\"\n", errmsg);
1518 Dmsg(10, "Trying to delete mountpoint %s\n", arg->snapmountpoint);
1519 if ((ret = rmdir(arg->snapmountpoint)) != 0) {
1522 } while (retry-- > 0 && ret != 0);
1526 Dmsg(10, "Unable to delete mountpoint after unmount\n");
1527 printf("error=\"Unable to delete mountpoint after unmount errno=%s\"",
1528 be.bstrerror(errno));
1530 printf(" status=1\n");
1534 /* TODO: Here we need to check LVM settings */
1539 if (!snapshot::support()) {
1546 elt = get_lv(arg->device);
1549 Dmsg(10, "Not detected as LVM\n");
1550 printf("status=0 error=\"Not detected as LVM\"\n");
1553 mp = get_value_pos(lvs_header ,"Path");
1554 printf("status=1 device=\"%s\" type=lvm\n", elt[mp]);
1558 /* count the number of column in the output */
1559 int count_col(char *l, char sep) {
1561 for (char *p = l ; *p ; p++) {
1569 /* Decode the Attr field */
1570 int decode_attr(char *l) {
1572 * Volume type: (m)irrored, (M)irrored without initial sync,
1573 * (o)rigin, (O)rigin with merging snapshot, (r)aid, (R)aid
1574 * without initial sync, (s)napshot, merging (S)napshot,
1575 * (p)vmove, (v)irtual, mirror or raid (i)mage, mirror or raid
1576 * (I)mage out-of-sync, mirror (l)og device, under (c)onversion,
1577 * thin (V)olume, (t)hin pool, (T)hin pool data, raid or thin
1584 bool parse_vgs_output() {
1585 Mmsg(cmd, "%svgs -o vg_all --separator=; --units b --nosuffix", arg->sudo);
1587 free_header(vgs, vgs_nbelt);
1590 vgs = New(alist(10, not_owned_by_alist));
1591 if (!parse_output(cmd, vgs, &vgs_nbelt, vgs_header)) {
1597 bool parse_lvs_output() {
1598 Mmsg(cmd, "%slvs -o lv_all --separator=; --units b --nosuffix", arg->sudo);
1600 free_header(lvs, lvs_nbelt);
1603 lvs = New(alist(10, not_owned_by_alist));
1604 if (!parse_output(cmd, lvs, &lvs_nbelt, lvs_header)) {
1610 /* Function to parse LVM command output */
1611 bool parse_output(char *cmd, alist *ret, int *ret_nbelt, Header *hdr) {
1616 char buf[2048]; /* Size for a single line */
1617 bool header_done=false;
1619 if (run_program_full_output(cmd, 60, errmsg)) {
1620 strip_quotes(errmsg);
1621 Dmsg(10, "Unable to run lvs. ERR=%s\n", errmsg);
1625 char **current = NULL;
1627 for (p = errmsg; *p ; p++) {
1628 if (*p == ';') { /* We have a separator, handle current value */
1631 nbelt++; /* Keep the number of element in the line */
1633 /* Find if we need this value, and where to store it */
1634 for (int j=0; hdr[j].name ; j++) {
1635 if (strcasecmp(buf, hdr[j].name) == 0) {
1643 /* First item, need to allocate new array */
1644 current = (char **)malloc(nbelt * sizeof(char *) + 1);
1645 memset(current, 0, nbelt * sizeof(char *) + 1);
1646 ret->append(current);
1648 /* Keep the current value */
1649 current[pos] = bstrdup(buf);
1653 } else if (*p == '\n') {
1654 /* We deal with a new line, so the header is done (if in) */
1659 } else if (i < (int)sizeof(buf)) {
1663 Dmsg(10, "Output too big !!! %s\n", errmsg);
1672 char **elt, **elt2 = NULL;
1673 const char *err = NULL;
1674 int p_attr, p_path, p_origin, p_time, p_size;
1676 int fnl, pnl, status;
1678 if (!snapshot::list()) {
1682 if (!parse_lvs_output()) {
1686 p_attr = get_value_pos(lvs_header, "Attr");
1687 p_path = get_value_pos(lvs_header, "Path");
1688 p_time = get_value_pos(lvs_header, "Time");
1689 p_size = get_value_pos(lvs_header, "Snap%");
1690 p_origin = get_value_pos(lvs_header, "Origin");
1692 if (p_time < 0 || p_origin < 0) {
1693 printf("status=1 error=\"Unable to get snapshot Origin from lvs command\"\n");
1697 p = get_pool_memory(PM_FNAME);
1698 f = get_pool_memory(PM_FNAME);
1699 d = get_pool_memory(PM_FNAME);
1701 elt2 = get_lv(arg->device);
1703 /* TODO: We need to get the device name from the mount point */
1704 foreach_alist(elt, lvs) {
1705 char *attr = elt[p_attr];
1707 if (attr[0] == 's') {
1708 if (attr[4] == 'I') {
1709 /* 5 State: (a)ctive, (s)uspended, (I)nvalid snapshot, invalid (S)uspended
1710 * snapshot, snapshot (m)erge failed, suspended snapshot (M)erge
1711 * failed, mapped (d)evice present without tables, mapped device
1712 * present with (i)nactive table, (X) unknown
1715 err = "Invalid snapshot";
1721 split_path_and_filename(elt[p_path], &p, &pnl, &f, &fnl);
1722 Mmsg(d, "%s%s", p, elt[p_origin]);
1724 if ((!arg->device || strcmp(arg->device, d) == 0) ||
1725 (elt2 && strcmp(elt2[p_path], d) == 0))
1727 /* On LVM, the name is LV_SnapshotName, we can strip the LV_ if we find it */
1728 Mmsg(p, "%s_", d); /* /dev/mapper/vg_ssd/test_ */
1729 if (strncmp(p, elt[p_path], strlen(p)) == 0) {
1730 pm_strcpy(f, elt[p_path] + strlen(p));/* test_MySnapshot_2020.. => MySnapshot_2020 */
1733 printf("volume=\"%s\" device=\"%s\" name=\"%s\" createdate=\"%s\" size=\"%s\" "
1734 "status=%d error=\"%s\" type=lvm\n",
1735 elt[p_path], d, f, elt[p_time], elt[p_size], status, err);
1739 free_pool_memory(p);
1740 free_pool_memory(f);
1741 free_pool_memory(d);
1746 /* The simulator is using a simple symlink */
1747 class simulator: public snapshot {
1749 simulator(arguments *arg): snapshot(arg, "simulator") {};
1752 if (!snapshot::mount()) {
1755 split_path_and_filename(arg->volume, &path, &pnl, &fname, &fnl);
1756 printf("status=1 snapmountpoint=\"%s\" snapdirectory=\"%s\"\n",
1762 printf("status=1\n");
1767 if (!snapshot::support()) {
1770 if (access(arg->mountpoint, W_OK) != 0) {
1771 printf("status=0 device=\"%s\" type=simulator "
1772 "error=\"Unable to access mountpoint\"\n",
1776 printf("status=1 device=\"%s\" type=simulator\n", arg->mountpoint);
1784 if (!snapshot::create()) {
1787 Mmsg(path, "%s/%s", arg->mountpoint, arg->snapdir);
1790 Mmsg(cmd, "ln -vsf \"%s\" \"%s\"", arg->mountpoint, path);
1791 if (run_program(cmd, 60, errmsg)) {
1792 Dmsg(10, "Unable to create symlink. ERR=%s\n", errmsg);
1793 strip_quotes(errmsg);
1794 printf("status=%d error=\"Unable to umount the device %s\"\n",
1798 printf("status=1 volume=\"%s\" createtdate=%s type=simulator\n",
1799 path, edit_uint64(now, ed1));
1805 if (!snapshot::del()) {
1808 ret = unlink(arg->volume);
1809 printf("status=%d\n", (ret == 0)? 1 : 0);
1814 snapshot *detect_snapshot_backend(arguments *arg)
1817 if (strcasecmp(arg->type, "btrfs") == 0) {
1818 return new btrfs(arg);
1820 } else if (strcasecmp(arg->type, "lvm") == 0) {
1821 return new lvm(arg);
1823 } else if (strcasecmp(arg->type, "simulator") == 0) {
1824 return new simulator(arg);
1826 } else if (strcasecmp(arg->type, "zfs") == 0) {
1827 return new zfs(arg);
1831 if (strcasecmp(arg->fstype, "btrfs") == 0) {
1832 return new btrfs(arg);
1834 } else if (strcasecmp(arg->fstype, "tmpfs") == 0) {
1835 return new simulator(arg);
1837 /* TODO: Need to find something smarter here */
1838 } else if (strcasecmp(arg->fstype, "ext4") == 0) {
1839 return new lvm(arg);
1841 } else if (strcasecmp(arg->fstype, "xfs") == 0) {
1842 return new lvm(arg);
1844 } else if (strcasecmp(arg->fstype, "ext3") == 0) {
1845 return new lvm(arg);
1847 } else if (strcasecmp(arg->fstype, "zfs") == 0 ||
1848 strcasecmp(arg->fstype, "fuse.zfs") == 0)
1850 return new zfs(arg);
1853 Dmsg(10, "Backend not found\n");
1857 /* defined in jcr.c */
1858 void create_jcr_key();
1860 int main(int argc, char **argv)
1868 set_trace_file("/dev/null");
1869 setlocale(LC_ALL, "");
1870 setenv("LANG", "C", true);
1871 bindtextdomain("bacula", LOCALEDIR);
1872 textdomain("bacula");
1876 my_name_is(argc, argv, "bsnapshot");
1879 while ((ch = getopt(argc, argv, "?d:vc:so:V:T:t")) != -1) {
1881 case 'd': /* set debug level */
1882 debug_level = atoi(optarg);
1883 if (debug_level <= 0) {
1892 case 's': /* use sudo */
1896 case 'c': /* config file */
1897 pm_strcpy(arg.config_file, optarg);
1898 if (stat(optarg, &sp) < 0) {
1899 Pmsg(000, "Unable to access %s. ERR=%s\n",optarg, strerror(errno));
1900 usage(_("Unable to open -p argument for reading"));
1904 case 'o': /* where to send the debug output */
1905 set_trace_file(optarg);
1909 arg.action = (char *)"check";
1912 case 'V': /* set volume name */
1913 arg.volume = optarg;
1916 case 'T': /* device type */
1927 if (!arg.validate()) {
1932 Dmsg(10, "disabled from config file\n");
1936 snap = detect_snapshot_backend(&arg);
1939 printf("status=0 error=\"Unable to detect snapshot backend\"");
1945 if (strcasecmp(arg.action, "mount") == 0) {
1946 ret = snap->mount();
1948 } else if (strcasecmp(arg.action, "support") == 0) {
1949 ret = snap->support();
1951 } else if (strcasecmp(arg.action, "create") == 0) {
1952 ret = snap->create();
1954 } else if (strcasecmp(arg.action, "delete") == 0) {
1957 } else if (strcasecmp(arg.action, "subvolumes") == 0) {
1958 ret = snap->subvolumes();
1960 } else if (strcasecmp(arg.action, "list") == 0) {
1963 } else if (strcasecmp(arg.action, "check") == 0) {
1964 ret = snap->check();
1966 } else if (strcasecmp(arg.action, "unmount") == 0) {
1967 ret = snap->unmount();
1972 close_memory_pool();
1973 lmgr_cleanup_main();
1975 Dmsg(10, "exit code = %d\n", (ret == 1) ? 0 : 1);
1976 return (ret == 1)? 0 : 1;