Whamcloud - gitweb
LU-13308 mdc: support additional flags for OBD_IOC_CHLG_POLL ioctl
[fs/lustre-release.git] / lustre / utils / lsnapshot.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License version 2 for more details.  A copy is
14  * included in the COPYING file that accompanied this code.
15
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright (c) 2017, Intel Corporation.
24  *
25  * lustre/utils/lsnapshot.c
26  *
27  * Author: Fan, Yong <fan.yong@intel.com>
28  */
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <getopt.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <sys/time.h>
40 #include <sys/file.h>
41 #include <time.h>
42 #include <limits.h>
43 #include <ctype.h>
44
45 #include <libcfs/util/list.h>
46 #include <libcfs/util/ioctl.h>
47 #include <libcfs/util/string.h>
48 #include <linux/lustre/lustre_ioctl.h>
49 #include <linux/lustre/lustre_barrier_user.h>
50
51 #include "obdctl.h"
52
53 #define SNAPSHOT_CONF_DIR       "/etc/lsnapshot"
54 #define LDEV_CONF               "/etc/ldev.conf"
55 #define SNAPSHOT_LOG            "/var/log/lsnapshot.log"
56 #define SNAPSHOT_MAGIC          "0x14F711B9"
57 #define MAX_BUF_SIZE            4096
58
59 enum snapshot_role {
60         SR_MGS  = 0x0001,
61         SR_MDT  = 0x0002,
62         SR_OST  = 0x0004,
63 };
64
65 struct snapshot_target {
66         struct list_head         st_list;
67         /* Target node name. */
68         char                    *st_host;
69         char                    *st_fhost;
70         /* Where the pool is */
71         char                    *st_dir;
72         /* The target pool name on the target node. */
73         char                    *st_pool;
74         /* The backend filesystem name against the target pool. */
75         char                    *st_filesystem;
76         int                      st_role;
77         unsigned int             st_index;
78         unsigned int             st_gen;
79         int                      st_line;
80         int                      st_status;
81         pid_t                    st_pid;
82         bool                     st_ignored;
83 };
84
85 struct snapshot_instance {
86         struct list_head         si_mdts_list;
87         struct list_head         si_osts_list;
88         struct snapshot_target  *si_mgs;
89         struct snapshot_target  *si_mdt0;
90         FILE                    *si_log_fp;
91         char                    *si_rsh;
92         char                    *si_fsname;
93         char                    *si_ssname;
94         char                    *si_new_ssname;
95         char                    *si_comment;
96         int                      si_conf_fd;
97         int                      si_timeout;
98         bool                     si_barrier;
99         bool                     si_detail;
100         bool                     si_force;
101 };
102
103 static const char snapshot_rsh_default[] = "ssh";
104 static char snapshot_path[MAX_BUF_SIZE];
105
106 static char *snapshot_role2name(char *name, enum snapshot_role role,
107                                 __u32 index)
108 {
109         if (role & SR_MDT)
110                 snprintf(name, 8, "MDT%04x", index);
111         else if (role & SR_MGS)
112                 snprintf(name, 4, "MGS");
113         else
114                 snprintf(name, 8, "OST%04x", index);
115
116         return name;
117 }
118
119 #define SNAPSHOT_ADD_LOG(si, format, ...)                               \
120 do {                                                                    \
121         char buf[MAX_BUF_SIZE];                                         \
122         char *ptr;                                                      \
123         time_t tt;                                                      \
124                                                                         \
125         memset(buf, 0, sizeof(buf));                                    \
126         time(&tt);                                                      \
127         snprintf(buf, sizeof(buf) - 1, "%s", ctime(&tt));               \
128         ptr = strrchr(buf, '\n');                                       \
129         if (ptr)                                                        \
130                 *ptr = '\0';                                            \
131                                                                         \
132         fprintf(si->si_log_fp, "%s (%d:%s:%d:%s:%s): "format, buf,      \
133                 getpid(), __func__, __LINE__, si->si_fsname,            \
134                 si->si_rsh, ## __VA_ARGS__);                            \
135 } while (0)
136
137 #define DRSH "%s %s"
138 #define DFSNAME "%s/%s"
139 #define DSSNAME "%s/%s@%s"
140 #define DZFS "%s zfs"
141 #define DIMPORT "%s zpool import -d %s %s > /dev/null 2>&1"
142
143 #define PRSH(si, st, foreign) (si)->si_rsh, \
144                 foreign ? (st)->st_fhost : (st)->st_host
145 #define PFSNAME(st) (st)->st_pool, (st)->st_filesystem
146 #define PSSNAME(si, st) PFSNAME(st), (si)->si_ssname
147 #define PSS_NEW(si, st) PFSNAME(st), (si)->si_new_ssname
148 #define PZFS(st) snapshot_path
149 #define PIMPORT(st) snapshot_path, \
150                 (st)->st_dir ? (st)->st_dir : "/dev -d /tmp", (st)->st_pool
151
152 char *snapshot_fgets(FILE *fp, char *buf, int buflen)
153 {
154         char *ptr;
155
156         memset(buf, 0, buflen);
157         if (fgets(buf, buflen, fp) == NULL)
158                 return NULL;
159
160         ptr = strchr(buf, '\n');
161         if (ptr)
162                 *ptr = '\0';
163
164         return buf;
165 }
166
167 static int snapshot_exec(const char *cmd)
168 {
169         int rc;
170
171         errno = 0;
172
173         /* system() return value depends on both the system() general framework,
174          * such as whether fork()/exec() success or fail, and the real @cmd exec
175          * result. Especially, if the @cmd is remote command, we may cannot know
176          * the real failure. */
177         rc = system(cmd);
178         /* fork()/exec() error */
179         if (rc == -1)
180                 return errno != 0 ? -errno : -1;
181
182         if (WIFEXITED(rc)) {
183                 rc = WEXITSTATUS(rc);
184                 if (rc > 0)
185                         rc = -rc;
186         } else if (WIFSIGNALED(rc)) {
187                 rc = -EINTR;
188         } else {
189                 /* all other known or unknown cases. */
190                 rc = -EFAULT;
191         }
192
193         return rc;
194 }
195
196 static int snapshot_load_conf_ldev(struct snapshot_instance *si, char *buf,
197                                    struct snapshot_target *st, char **role)
198 {
199         char *label = NULL;
200         char *device = NULL;
201         char *ptr;
202         char *ptr1;
203         int len;
204         int rc;
205
206         rc = sscanf(buf, "%ms %ms %ms %ms",
207                     &st->st_host, &st->st_fhost, &label, &device);
208         if (rc < 4) {
209                 rc = -EINVAL;
210                 goto out;
211         }
212         if (strcmp(st->st_fhost, "-") == 0)
213                 st->st_fhost = NULL;
214
215         /* Format of device:
216          * [md|zfs:][pool_dir/]<pool>/<filesystem> */
217         ptr = strchr(device, ':');
218         if (ptr) {
219                 ptr++;
220                 if (strncmp(device, "zfs:", strlen("zfs:")) != 0) {
221                         rc = -EINVAL;
222                         goto out;
223                 }
224         } else {
225                         ptr = device;
226         }
227
228         ptr1 = strrchr(ptr, '/');
229         /* "ptr1 - ptr + 1 == strlen(ptr)" means '/' is at the tail. */
230         if (!ptr1 || ptr1 == ptr || ptr1 - ptr + 1 == strlen(ptr)) {
231                 rc = -EINVAL;
232                 goto out;
233         }
234
235         len = strlen(ptr1);
236         st->st_filesystem = malloc(len);
237         if (!st->st_filesystem) {
238                 rc = -ENOMEM;
239                 goto out;
240         }
241
242         *ptr1 = '\0';
243         strncpy(st->st_filesystem, ptr1 + 1, len - 1);
244         st->st_filesystem[len - 1] = '\0';
245
246         if (*ptr == '/') {
247                 ptr1 = strrchr(ptr, '/');
248                 *ptr1 = '\0';
249                 st->st_dir = strdup(ptr);
250                 if (!st->st_dir) {
251                         rc = -ENOMEM;
252                         goto out;
253                 }
254                 ptr = ptr1 + 1;
255         }
256
257         st->st_pool = strdup(ptr);
258         if (!st->st_pool) {
259                 rc = -ENOMEM;
260                 goto out;
261         }
262
263         /* Format of label:
264          * fsname-<role><index> or <role><index> */
265         ptr = strrchr(label, '-');
266         if (ptr) {
267                 if (strncmp(si->si_fsname, label, ptr - label) != 0) {
268                         /* This line is NOT for current filesystem .*/
269                         rc = -EAGAIN;
270                         goto out;
271                 }
272
273                 ptr++;
274         } else {
275                 ptr = label;
276         }
277
278         if (strlen(ptr) < 3 || strlen(ptr) > 7) {
279                 rc = -EINVAL;
280                 goto out;
281         }
282
283         *role = malloc(4);
284         if (!*role) {
285                 rc = -ENOMEM;
286                 goto out;
287         }
288
289         strncpy(*role, ptr, 3);
290         (*role)[3] = 0;
291         ptr += 3;
292         len = 0;
293         while (isxdigit(ptr[len])) {
294                 if (isdigit(ptr[len]))
295                         st->st_index =
296                                 st->st_index * 16 + ptr[len] - '0';
297                 else if (isupper(ptr[len]))
298                         st->st_index =
299                                 st->st_index * 16 + ptr[len] - 'A' + 10;
300                 else
301                         st->st_index =
302                                 st->st_index * 16 + ptr[len] - 'a' + 10;
303                 len++;
304         }
305
306         if (len == 0) {
307                 if (strncasecmp(*role, "MGS", 3) != 0)
308                         rc = -EINVAL;
309                 else
310                         rc = 0;
311
312                 goto out;
313         }
314
315         if (!isxdigit(ptr[len]) && ptr[len] != '\0') {
316                 rc = -EINVAL;
317                 goto out;
318         }
319
320 out:
321         if (label)
322                 free(label);
323         if (device)
324                 free(device);
325
326         return rc;
327 }
328
329 /**
330  * For old snasphot tools, the configration is in /etc/lsnapshot/${fsname}.conf,
331  * the format is:
332  * <host> <pool_dir> <pool> <local_fsname> <role(,s)> <index>
333  *
334  * For example:
335  *
336  * host-mdt1 /tmp myfs-mdt1 mdt1 MGS,MDT 0
337  * host-mdt2 /tmp myfs-mdt2 mdt2 MDT 1
338  * host-ost1 /tmp myfs-ost1 ost1 OST 0
339  * host-ost2 /tmp myfs-ost2 ost2 OST 1
340  *
341  *
342  * For new snasphot tools, the configration is in /etc/ldev.conf, which is not
343  * only for snapshot, but also for other purpose. The format is:
344  * <host> foreign/- <label> <device> [journal-path]/- [raidtab]
345  *
346  * The format of <label> is:
347  * fsname-<role><index> or <role><index>
348  *
349  * The format of <device> is:
350  * [md|zfs:][pool_dir/]<pool>/<filesystem>
351  *
352  * Snapshot only uses the fields <host>, <label> and <device>.
353  *
354  * For example:
355  *
356  * host-mdt1 - myfs-MDT0000 zfs:/tmp/myfs-mdt1/mdt1
357  *
358  *
359  * \retval       0      for success
360  * \retval      +ve     the line# with which the current line is conflict
361  * \retval      -EAGAIN skip current line
362  * \retval      -ve     other failures
363  */
364 static int snapshot_load_conf_one(struct snapshot_instance *si,
365                                   char *buf, int line_num, bool is_ldev)
366 {
367         struct snapshot_target *st;
368         char *role = NULL;
369         char *path;
370         int rc = 0;
371
372         path = getenv("PATH");
373         if (!path)
374                 return -EINVAL;
375
376         memset(snapshot_path, 0, sizeof(snapshot_path));
377         snprintf(snapshot_path, sizeof(snapshot_path) - 1, "PATH='%s'", path);
378
379         /* filter out space */
380         while (isspace(*buf))
381                 buf++;
382
383         /* skip empty line */
384         if (*buf == '\0')
385                 return 0;
386
387         /* skip comment line */
388         if (*buf == '#')
389                 return 0;
390
391         st = malloc(sizeof(*st));
392         if (!st)
393                 return -ENOMEM;
394
395         memset(st, 0, sizeof(*st));
396         INIT_LIST_HEAD(&st->st_list);
397
398         if (is_ldev) {
399                 rc = snapshot_load_conf_ldev(si, buf, st, &role);
400         } else {
401                 rc = sscanf(buf, "%ms %ms %ms %ms %ms %ms %d",
402                             &st->st_host, &st->st_fhost, &st->st_dir,
403                             &st->st_pool, &st->st_filesystem, &role,
404                             &st->st_index);
405                 if (rc < 7)
406                         rc = -EINVAL;
407
408                 if (strcmp(st->st_fhost, "-") == 0)
409                         st->st_fhost = NULL;
410         }
411
412         if (rc < 0)
413                 goto out;
414         rc = 0;
415
416         if (strncasecmp(role, "MGS", 3) == 0) {
417                 st->st_role = SR_MGS;
418                 if (role[3] == ',') {
419                         /* MGS,MDT */
420                         if (strncasecmp(&role[4], "MDT", 3) != 0) {
421                                 rc = -EINVAL;
422                                 goto out;
423                         }
424
425                         st->st_role |= SR_MDT;
426                 }
427         } else if (strncasecmp(role, "MDT", 3) == 0) {
428                 st->st_role = SR_MDT;
429                 if (role[3] == ',') {
430                         /* MDT,MGS */
431                         if (strncasecmp(&role[4], "MGS", 3) != 0) {
432                                 rc = -EINVAL;
433                                 goto out;
434                         }
435
436                         st->st_role |= SR_MGS;
437                 }
438         } else if (strncasecmp(role, "OST", 3) == 0) {
439                 st->st_role = SR_OST;
440         } else {
441                 rc = -EINVAL;
442                 goto out;
443         }
444
445         st->st_line = line_num;
446         if (st->st_role & SR_MDT) {
447                 /* MGS is the first, MDT0 is just after the MGS
448                  * if they are not combined together. */
449                 if (st->st_role & SR_MGS) {
450                         if (si->si_mgs) {
451                                 rc = si->si_mgs->st_line;
452                                 goto out;
453                         }
454
455                         si->si_mgs = st;
456                         list_add(&st->st_list, &si->si_mdts_list);
457                 }
458
459                 if (st->st_index == 0) {
460                         if (si->si_mdt0) {
461                                 rc = si->si_mdt0->st_line;
462                                 goto out;
463                         }
464
465                         si->si_mdt0 = st;
466                         if (list_empty(&st->st_list)) {
467                                 if (list_empty(&si->si_mdts_list) ||
468                                     !si->si_mgs)
469                                         list_add(&st->st_list,
470                                                  &si->si_mdts_list);
471                                 else
472                                         list_add(&st->st_list,
473                                                  &si->si_mgs->st_list);
474                         }
475                 } else if (list_empty(&st->st_list)) {
476                         list_add_tail(&st->st_list, &si->si_mdts_list);
477                 }
478         } else if (st->st_role & SR_MGS) {
479                 if (si->si_mgs) {
480                         rc = si->si_mgs->st_line;
481                         goto out;
482                 }
483
484                 si->si_mgs = st;
485                 list_add(&st->st_list, &si->si_mdts_list);
486         } else {
487                 list_add_tail(&st->st_list, &si->si_osts_list);
488         }
489
490 out:
491         if (role)
492                 free(role);
493
494         if (rc) {
495                 if (st->st_host)
496                         free(st->st_host);
497                 if (st->st_fhost)
498                         free(st->st_fhost);
499                 if (st->st_dir)
500                         free(st->st_dir);
501                 if (st->st_pool)
502                         free(st->st_pool);
503                 if (st->st_filesystem)
504                         free(st->st_filesystem);
505                 free(st);
506         }
507
508         return rc;
509 }
510
511 static int snapshot_load_conf(struct snapshot_instance *si, int lock_mode)
512 {
513         FILE *fp;
514         char buf[MAX_BUF_SIZE];
515         char conf_name[32];
516         int line_num = 1;
517         int fd = -1;
518         int rc = 0;
519         bool is_ldev = true;
520
521         memset(conf_name, 0, sizeof(conf_name));
522         strncpy(conf_name, LDEV_CONF, sizeof(conf_name) - 1);
523         fd = open(conf_name, O_RDONLY);
524         if (fd < 0) {
525                 if (errno != ENOENT) {
526                         fprintf(stderr,
527                                 "Can't open the snapshot config file %s: %s\n",
528                                 conf_name, strerror(errno));
529
530                         return fd;
531                 }
532
533                 snprintf(conf_name, sizeof(conf_name) - 1, "%s/%s.conf",
534                          SNAPSHOT_CONF_DIR, si->si_fsname);
535                 fd = open(conf_name, O_RDONLY);
536                 if (fd < 0) {
537                         fprintf(stderr,
538                                 "Can't open the snapshot config file %s: %s\n",
539                                 conf_name, strerror(errno));
540
541                         return fd;
542                 }
543
544                 is_ldev = false;
545         }
546
547         rc = flock(fd, lock_mode | LOCK_NB);
548         if (rc < 0) {
549                 fprintf(stderr,
550                         "Can't lock the snapshot config file %s (%d): %s\n",
551                         conf_name, lock_mode, strerror(errno));
552                 close(fd);
553                 return rc;
554         }
555
556         fp = fdopen(fd, "r");
557         if (!fp) {
558                 fprintf(stderr,
559                         "Can't fdopen the snapshot config file %s: %s\n",
560                         conf_name, strerror(errno));
561                 rc = -1;
562                 goto out;
563         }
564
565         while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
566                 rc = snapshot_load_conf_one(si, buf, line_num, is_ldev);
567                 if (rc == -EINVAL) {
568                         fprintf(stderr,
569                                 "Invalid snapshot config file %s at the line "
570                                 "%d '%s'\n", conf_name, line_num, buf);
571                 } else if (rc == -EAGAIN) {
572                         rc = 0;
573                 } else if (rc > 0) {
574                         fprintf(stderr,
575                                 "The config role has been specified repeatedly "
576                                 "at the lines %d/%d in %s\n",
577                                 rc, line_num, conf_name);
578                         rc = -EINVAL;
579                 }
580
581                 if (rc)
582                         goto out;
583
584                 line_num++;
585         }
586
587         if (!si->si_mdt0) {
588                 fprintf(stderr,
589                         "Miss MDT0 in the config file %s\n",
590                         conf_name);
591                 rc = -1;
592                 goto out;
593         }
594
595         /* By default, the MGS is on the MDT0 if it is not specified. */
596         if (!si->si_mgs) {
597                 si->si_mgs = si->si_mdt0;
598                 si->si_mgs->st_role |= SR_MGS;
599         }
600
601         if (list_empty(&si->si_osts_list)) {
602                 fprintf(stderr,
603                         "Miss OST(s) in the config file %s\n",
604                         conf_name);
605                 rc = -1;
606                 goto out;
607         }
608
609 out:
610         if (fd >= 0) {
611                 if (rc < 0) {
612                         flock(fd, LOCK_UN);
613                         close(fd);
614                 } else {
615                         si->si_conf_fd = fd;
616                 }
617         }
618
619         return rc;
620 }
621
622 static void snapshot_unload_conf(struct snapshot_instance *si)
623 {
624         struct snapshot_target *st;
625
626         while (!list_empty(&si->si_mdts_list)) {
627                 st = list_first_entry(&si->si_mdts_list,
628                                       struct snapshot_target, st_list);
629                 list_del(&st->st_list);
630                 free(st->st_host);
631                 free(st->st_fhost);
632                 free(st->st_dir);
633                 free(st->st_pool);
634                 free(st->st_filesystem);
635                 free(st);
636         }
637
638         while (!list_empty(&si->si_osts_list)) {
639                 st = list_first_entry(&si->si_osts_list,
640                                       struct snapshot_target, st_list);
641                 list_del(&st->st_list);
642                 free(st->st_host);
643                 free(st->st_fhost);
644                 free(st->st_dir);
645                 free(st->st_pool);
646                 free(st->st_filesystem);
647                 free(st);
648         }
649
650         si->si_mgs = NULL;
651         si->si_mdt0 = NULL;
652
653         if (si->si_conf_fd >= 0) {
654                 flock(si->si_conf_fd, LOCK_UN);
655                 close(si->si_conf_fd);
656                 si->si_conf_fd = -1;
657         }
658 }
659
660 static int snapshot_handle_string_option(char **dst, const char *option,
661                                          const char *opt_name)
662 {
663         if (*dst && *dst != snapshot_rsh_default) {
664                 fprintf(stderr,
665                         "%s option has been specified repeatedly.\n", opt_name);
666                 return -EINVAL;
667         }
668
669         *dst = strdup(option);
670         if (!*dst)
671                 return -ENOMEM;
672         return 0;
673 }
674
675 static void snapshot_fini(struct snapshot_instance *si)
676 {
677         snapshot_unload_conf(si);
678
679         if (si->si_log_fp)
680                 fclose(si->si_log_fp);
681
682         if (si->si_rsh && si->si_rsh != snapshot_rsh_default)
683                 free(si->si_rsh);
684         if (si->si_fsname)
685                 free(si->si_fsname);
686         if (si->si_ssname)
687                 free(si->si_ssname);
688         if (si->si_new_ssname)
689                 free(si->si_new_ssname);
690         if (si->si_comment)
691                 free(si->si_comment);
692
693         free(si);
694 }
695
696 static struct snapshot_instance *
697 snapshot_init(int argc, char * const argv[], const struct option *longopts,
698               const char *optstring, void (*usage)(void),
699               int lock_mode, int *err)
700 {
701         struct snapshot_instance *si;
702         int idx;
703         int opt;
704
705         *err = 0;
706         si = malloc(sizeof(*si));
707         if (!si) {
708                 fprintf(stderr,
709                         "No enough memory to initialize snapshot instance.\n");
710                 *err = -ENOMEM;
711                 return NULL;
712         }
713
714         memset(si, 0, sizeof(*si));
715         INIT_LIST_HEAD(&si->si_mdts_list);
716         INIT_LIST_HEAD(&si->si_osts_list);
717         si->si_rsh = (char *)snapshot_rsh_default;
718         si->si_conf_fd = -1;
719         si->si_timeout = BARRIER_TIMEOUT_DEFAULT;
720         si->si_barrier = true;
721         si->si_detail = false;
722         si->si_force = false;
723
724         while ((opt = getopt_long(argc, argv, optstring,
725                                   longopts, &idx)) != EOF) {
726                 switch (opt) {
727                 case 'b':
728                         if (!optarg || strcmp(optarg, "on") == 0) {
729                                 si->si_barrier = true;
730                         } else if (strcmp(optarg, "off") == 0) {
731                                 si->si_barrier = false;
732                         } else {
733                                 usage();
734                                 *err = -EINVAL;
735                                 goto out;
736                         }
737                         break;
738                 case 'c':
739                         *err = snapshot_handle_string_option(&si->si_comment,
740                                                              optarg, "comment");
741                         if (*err != 0)
742                                 goto out;
743                         break;
744                 case 'd':
745                         si->si_detail = true;
746                         break;
747                 case 'f':
748                         si->si_force = true;
749                         break;
750                 case 'F':
751                         *err = snapshot_handle_string_option(&si->si_fsname,
752                                                              optarg, "fsname");
753                         if (*err != 0)
754                                 goto out;
755                         break;
756                 case 'n':
757                         *err = snapshot_handle_string_option(&si->si_ssname,
758                                                              optarg, "ssname");
759                         if (*err != 0)
760                                 goto out;
761                         break;
762                 case 'N':
763                         *err = snapshot_handle_string_option(&si->si_new_ssname,
764                                                              optarg,
765                                                              "new ssname");
766                         if (*err != 0)
767                                 goto out;
768                         break;
769                 case 'r':
770                         *err = snapshot_handle_string_option(&si->si_rsh,
771                                                              optarg,
772                                                              "remote shell");
773                         if (*err != 0)
774                                 goto out;
775                         break;
776                 case 't':
777                         si->si_timeout = atoi(optarg);
778                         break;
779                 default:
780                         *err = -EINVAL;
781                         usage();
782                         goto out;
783                 case 'h':
784                         usage();
785                         snapshot_fini(si);
786                         *err = 0;
787                         return NULL;
788                 }
789         }
790
791         if (!si->si_fsname) {
792                 fprintf(stderr, "The fsname must be specified\n");
793                 usage();
794                 *err = -EINVAL;
795                 goto out;
796         }
797
798         if (strlen(si->si_fsname) > 8) {
799                 fprintf(stderr, "Invalid fsname %s\n", si->si_fsname);
800                 *err = -EINVAL;
801                 goto out;
802         }
803
804         si->si_log_fp = fopen(SNAPSHOT_LOG, "a");
805         if (!si->si_log_fp) {
806                 *err = -errno;
807                 fprintf(stderr,
808                         "Can't open the snapshot log file %s: %s\n",
809                         SNAPSHOT_LOG, strerror(errno));
810                 goto out;
811         }
812
813         *err = snapshot_load_conf(si, lock_mode);
814
815 out:
816         if (*err != 0 && si) {
817                 snapshot_fini(si);
818                 si = NULL;
819         }
820
821         return si;
822 }
823
824 static int __snapshot_wait(struct snapshot_instance *si,
825                            struct list_head *head, int *err)
826 {
827         struct snapshot_target *st;
828         int count = 0;
829         int rc = 0;
830
831         list_for_each_entry(st, head, st_list) {
832                 if (st->st_pid == 0)
833                         continue;
834
835                 rc = waitpid(st->st_pid, &st->st_status, 0);
836                 if (rc < 0) {
837                         SNAPSHOT_ADD_LOG(si, "Can't wait child (%d) operation "
838                                          "on the target <%s(%s):%x:%d>: %s\n",
839                                          st->st_pid, st->st_host,
840                                          st->st_fhost ? st->st_fhost : "none",
841                                          st->st_role, st->st_index,
842                                          strerror(errno));
843                         count++;
844                         if (*err == 0)
845                                 *err = rc;
846
847                         st->st_pid = 0;
848                         /* continue to wait for next */
849                         continue;
850                 }
851
852                 if (WIFEXITED(st->st_status)) {
853                         rc = WEXITSTATUS(st->st_status);
854                         if (rc > 0)
855                                 rc -= 256;
856
857                         if (rc == -ESRCH) {
858                                 st->st_ignored = true;
859                         } else if (rc) {
860                                 count++;
861                                 if (*err == 0)
862                                         *err = rc;
863                         }
864                 } else if (WIFSIGNALED(st->st_status)) {
865                         SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
866                                          "target <%s(%s):%x:%d> was killed by "
867                                          "signal %d\n",
868                                          st->st_pid, st->st_host,
869                                          st->st_fhost ? st->st_fhost : "none",
870                                          st->st_role, st->st_index,
871                                          WTERMSIG(st->st_status));
872                         count++;
873                         if (*err == 0)
874                                 *err = -EINTR;
875                 } else {
876                         SNAPSHOT_ADD_LOG(si, "The child (%d) operation on the "
877                                          "target <%s(%s):%x:%d> failed for "
878                                          "unknown reason\n",
879                                          st->st_pid, st->st_host,
880                                          st->st_fhost ? st->st_fhost : "none",
881                                          st->st_role, st->st_index);
882                         count++;
883                         if (*err == 0)
884                                 *err = -EFAULT;
885                 }
886
887                 st->st_pid = 0;
888         }
889
890         return count;
891 }
892
893 static int snapshot_wait(struct snapshot_instance *si, int *err)
894 {
895         int count;
896
897         count = __snapshot_wait(si, &si->si_mdts_list, err);
898         count += __snapshot_wait(si, &si->si_osts_list, err);
899
900         return count;
901 }
902
903 static char *snapshot_first_skip_blank(char *buf)
904 {
905         char *ptr;
906
907         ptr = strchr(buf, ' ');
908         if (!ptr) {
909                 ptr = strchr(buf, '\t');
910                 if (!ptr)
911                         return NULL;
912         }
913
914         while (*ptr == ' ' || *ptr == '\t')
915                 ptr++;
916
917         if (*ptr == '\0')
918                 ptr = NULL;
919
920         return ptr;
921 }
922
923 static int mdt0_is_lustre_snapshot(struct snapshot_instance *si)
924 {
925         struct snapshot_target *st = si->si_mdt0;
926         char buf[MAX_BUF_SIZE * 3];
927         FILE *fp;
928         int rc;
929         int foreign = 0;
930 again:
931         memset(buf, 0, sizeof(buf));
932         snprintf(buf, sizeof(buf) - 1,
933                  DRSH" '"DIMPORT"; "DZFS
934                  " get -H -o value lustre:magic "DSSNAME"' 2>/dev/null",
935                  PRSH(si, st, foreign), PIMPORT(st), PZFS(st), PSSNAME(si, st));
936
937         fp = popen(buf, "r");
938         if (!fp) {
939                 SNAPSHOT_ADD_LOG(si, "Popen fail to check snapshot "
940                                  "on mdt0: %s\n", strerror(errno));
941                 return -errno;
942         }
943
944         if (snapshot_fgets(fp, buf, strlen(SNAPSHOT_MAGIC) + 1) == NULL) {
945                 if (foreign || !st->st_fhost) {
946                         rc = -EINVAL;
947                 } else {
948                         foreign = 1;
949                         pclose(fp);
950                         goto again;
951                 }
952         } else if (strcmp(buf, SNAPSHOT_MAGIC) == 0) {
953                 rc = 0;
954         } else {
955                 fprintf(stderr,
956                         "The target %s is not Lustre snapshot "
957                         "or it does not exists\n", si->si_ssname);
958                 rc = -EPERM;
959         }
960
961         pclose(fp);
962         return rc;
963 }
964
965 static int target_is_mounted(struct snapshot_instance *si,
966                              struct snapshot_target *st, const char *ssname)
967 {
968         char buf[MAX_BUF_SIZE];
969         char fullname[MAX_BUF_SIZE];
970         FILE *fp;
971         char *ptr;
972         int rc = 0;
973         int foreign = 0;
974
975 again:
976         memset(buf, 0, sizeof(buf));
977         snprintf(buf, sizeof(buf) - 1,
978                  DRSH" 'mount' 2>/dev/null",
979                  PRSH(si, st, foreign));
980         fp = popen(buf, "r");
981         if (!fp) {
982                 SNAPSHOT_ADD_LOG(si, "Popen fail to check target mount: %s\n",
983                                  strerror(errno));
984                 return -errno;
985         }
986
987         memset(fullname, 0, sizeof(fullname));
988         if (ssname)
989                 snprintf(fullname, sizeof(fullname) - 1,
990                          DFSNAME"@%s on ",
991                          PFSNAME(st), ssname);
992         else
993                 snprintf(fullname, sizeof(fullname) - 1,
994                          DFSNAME" on ",
995                          PFSNAME(st));
996
997         while (snapshot_fgets(fp, buf, sizeof(buf)) != NULL) {
998                 ptr = strstr(buf, fullname);
999                 if (!ptr)
1000                         continue;
1001
1002                 ptr += strlen(fullname) + 1; /* mount point */
1003                 if (ptr >= buf + strlen(buf))
1004                         continue;
1005
1006                 ptr = strstr(ptr, "type lustre");
1007                 if (ptr) {
1008                         rc = 1;
1009                         break;
1010                 }
1011         }
1012
1013         pclose(fp);
1014         if (!rc && !foreign && st->st_fhost) {
1015                 foreign = 1;
1016                 goto again;
1017         }
1018         return rc;
1019 }
1020
1021 static int snapshot_get_fsname(struct snapshot_instance *si,
1022                                char *fsname, int fslen)
1023 {
1024         struct snapshot_target *st = si->si_mdt0;
1025         char buf[MAX_BUF_SIZE * 3];
1026         FILE *fp;
1027         int rc;
1028         int foreign = 0;
1029 again:
1030         rc = 0;
1031         memset(buf, 0, sizeof(buf));
1032         snprintf(buf, sizeof(buf) - 1,
1033                  DRSH" '"DIMPORT"; "DZFS
1034                  " get -H -o value lustre:fsname "DSSNAME"' 2>/dev/null",
1035                  PRSH(si, st, foreign), PIMPORT(st), PZFS(st),
1036                  PSSNAME(si, st));
1037         fp = popen(buf, "r");
1038         if (!fp) {
1039                 SNAPSHOT_ADD_LOG(si, "Popen fail to get fsname: %s\n",
1040                                  strerror(errno));
1041                 return -errno;
1042         }
1043
1044         if (snapshot_fgets(fp, fsname, fslen) == NULL)
1045                 rc = -EINVAL;
1046
1047         pclose(fp);
1048
1049         if (rc && !foreign && st->st_fhost) {
1050                 foreign = 1;
1051                 goto again;
1052         }
1053         return rc;
1054 }
1055
1056 static int snapshot_get_mgsnode(struct snapshot_instance *si,
1057                                 char *node, int size)
1058 {
1059         char buf[MAX_BUF_SIZE * 2];
1060         struct snapshot_target *st;
1061         FILE *fp;
1062         int rc;
1063         int foreign = 0;
1064
1065         st = list_first_entry(&si->si_osts_list, struct snapshot_target,
1066                               st_list);
1067 again:
1068         rc = 0;
1069         memset(buf, 0, sizeof(buf));
1070         snprintf(buf, sizeof(buf) - 1,
1071                  DRSH" '"DZFS" get -H -o value lustre:mgsnode "DFSNAME"'"
1072                  " 2>/dev/null", PRSH(si, st, foreign), PZFS(st),
1073                  PFSNAME(st));
1074         fp = popen(buf, "r");
1075         if (!fp) {
1076                 SNAPSHOT_ADD_LOG(si, "Popen fail to get mgsnode: %s\n",
1077                                  strerror(errno));
1078                 return -errno;
1079         }
1080
1081         if (snapshot_fgets(fp, node, size) == NULL)
1082                 rc = -EINVAL;
1083
1084         pclose(fp);
1085
1086         if (rc && !foreign && st->st_fhost) {
1087                 foreign = 1;
1088                 goto again;
1089         }
1090
1091         return rc;
1092 }
1093
1094 static int snapshot_exists_check(struct snapshot_instance *si,
1095                                  struct snapshot_target *st)
1096 {
1097         char buf[MAX_BUF_SIZE * 2];
1098         FILE *fp;
1099         int rc = 0;
1100         int foreign = 0;
1101 again:
1102         memset(buf, 0, sizeof(buf));
1103         snprintf(buf, sizeof(buf) - 1,
1104                  DRSH" '"DZFS" list "DSSNAME"' 2>/dev/null",
1105                  PRSH(si, st, foreign), PZFS(st), PSSNAME(si, st));
1106         fp = popen(buf, "r");
1107         if (!fp) {
1108                 SNAPSHOT_ADD_LOG(si, "Popen fail to create check: %s\n",
1109                                  strerror(errno));
1110                 return -errno;
1111         }
1112
1113         if (snapshot_fgets(fp, buf, sizeof(buf)) != NULL)
1114                 rc = -EEXIST;
1115
1116         pclose(fp);
1117
1118         if (rc != -EEXIST && !foreign && st->st_fhost) {
1119                 foreign = 1;
1120                 goto again;
1121         }
1122         return rc;
1123 }
1124
1125 static int snapshot_general_check(struct snapshot_instance *si)
1126 {
1127         return mdt0_is_lustre_snapshot(si);
1128 }
1129
1130 static void snapshot_create_usage(void)
1131 {
1132         fprintf(stderr,
1133                 "Create snapshot for the given filesystem.\n"
1134                 "Usage:\n"
1135                 "snapshot_create [-b | --barrier [on | off]] "
1136                                 "[-c | --comment comment] "
1137                                 "<-F | --fsname fsname> "
1138                                 "[-h | --help] <-n | --name ssname> "
1139                                 "[-r | --rsh remote_shell]"
1140                                 "[-t | --timeout timeout]\n"
1141                 "Options:\n"
1142                 "-b: set write barrier before creating snapshot, "
1143                         "the default value is 'on'.\n"
1144                 "-c: describe what the snapshot is for, and so on.\n"
1145                 "-F: the filesystem name.\n"
1146                 "-h: for help information.\n"
1147                 "-n: the snapshot's name.\n"
1148                 "-r: the remote shell used for communication with remote "
1149                         "target, the default value is 'ssh'.\n"
1150                 "-t: the life cycle (seconds) for write barrier, "
1151                         "the default value is %d seconds.\n",
1152                 BARRIER_TIMEOUT_DEFAULT);
1153 }
1154
1155 static int snapshot_create_check(struct snapshot_instance *si)
1156 {
1157         int rc;
1158
1159         rc = snapshot_exists_check(si, si->si_mdt0);
1160         if (rc == -EEXIST)
1161                 fprintf(stderr, "The snapshot %s exists\n", si->si_ssname);
1162
1163         return rc;
1164 }
1165
1166 static int snapshot_inherit_prop(struct snapshot_instance *si,
1167                                  struct snapshot_target *st,
1168                                  char *cmd, int size)
1169 {
1170         char buf[MAX_BUF_SIZE * 3];
1171         FILE *fp;
1172         int len = 0;
1173         int rc = 0;
1174         int foreign = 0;
1175 again:
1176         memset(buf, 0, sizeof(buf));
1177         snprintf(buf, sizeof(buf) - 1,
1178                  DRSH" \""DIMPORT"; "DZFS
1179                  " get all "DFSNAME" | grep lustre: | grep local$ | "
1180                  "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
1181                  "sed -e 's/^ //'\" 2>/dev/null",
1182                  PRSH(si, st, foreign), PIMPORT(st), PZFS(st), PFSNAME(st));
1183         fp = popen(buf, "r");
1184         if (!fp) {
1185                 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
1186                                  strerror(errno));
1187                 return -errno;
1188         }
1189
1190         while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
1191                 char *ptr;
1192                 char *end;
1193                 foreign = 1;
1194                 if (strncmp(buf, "lustre:fsname",
1195                             strlen("lustre:fsname")) == 0)
1196                         continue;
1197
1198                 if (strncmp(buf, "lustre:magic",
1199                             strlen("lustre:magic")) == 0)
1200                         continue;
1201
1202                 if (strncmp(buf, "lustre:ctime",
1203                             strlen("lustre:ctime")) == 0)
1204                         continue;
1205
1206                 if (strncmp(buf, "lustre:mtime",
1207                             strlen("lustre:mtime")) == 0)
1208                         continue;
1209
1210                 if (strncmp(buf, "lustre:comment",
1211                             strlen("lustre:comment")) == 0)
1212                         continue;
1213
1214                 if (strncmp(buf, "lustre:svname",
1215                             strlen("lustre:svname")) == 0)
1216                         continue;
1217
1218                 if (strncmp(buf, "lustre:mgsnode",
1219                             strlen("lustre:mgsnode")) == 0)
1220                         continue;
1221
1222                 ptr = strchr(buf, ' ');
1223                 if (!ptr) {
1224                         ptr = strchr(buf, '\t');
1225                         if (!ptr)
1226                                 continue;
1227                 }
1228
1229                 *ptr = '\0';
1230                 ptr++;
1231                 while (*ptr == ' ' || *ptr == '\t')
1232                         ptr++;
1233
1234                 if (*ptr == '\0')
1235                         continue;
1236
1237                 end = strchr(ptr, ' ');
1238                 if (!end)
1239                         end = strchr(buf, '\t');
1240                 if (end)
1241                         *end = '\0';
1242
1243                 rc = scnprintf(cmd + len, size - len - 1,
1244                                "-o %s=\"%s\" ", buf, ptr);
1245                 if (rc <= 0)
1246                         return -EOVERFLOW;
1247
1248                 len += rc;
1249         }
1250
1251         pclose(fp);
1252         if (!foreign && st->st_fhost) {
1253                 foreign = 1;
1254                 goto again;
1255         }
1256         return len;
1257 }
1258
1259 static int __snapshot_create(struct snapshot_instance *si,
1260                              struct list_head *head, const char *fsname,
1261                              const char *mgsnode, __u64 xtime)
1262 {
1263         struct snapshot_target *st;
1264         pid_t pid;
1265         int rc;
1266
1267         list_for_each_entry(st, head, st_list) {
1268                 st->st_status = 0;
1269                 st->st_ignored = 0;
1270                 st->st_pid = 0;
1271
1272                 pid = fork();
1273                 if (pid < 0) {
1274                         SNAPSHOT_ADD_LOG(si, "Can't fork for create snapshot "
1275                                          "(%s@%s <%s>) on the target "
1276                                          "(%s(%s):%x:%d): %s\n",
1277                                          fsname, si->si_ssname,
1278                                          si->si_comment, st->st_host,
1279                                          st->st_fhost ? st->st_fhost : "none",
1280                                          st->st_role, st->st_index,
1281                                          strerror(errno));
1282                         return pid;
1283                 }
1284
1285                 /* child */
1286                 if (pid == 0) {
1287                         char cmd[MAX_BUF_SIZE];
1288                         int len;
1289                         int foreign = 0;
1290 again:
1291                         memset(cmd, 0, sizeof(cmd));
1292                         len = scnprintf(cmd, sizeof(cmd) - 1,
1293                                         DRSH" '"DZFS" snapshot "
1294                                         "-o lustre:fsname=%s "
1295                                         "-o lustre:magic=%s "
1296                                         "-o lustre:ctime=%llu "
1297                                         "-o lustre:mtime=%llu ",
1298                                         PRSH(si, st, foreign), PZFS(st),
1299                                         fsname, SNAPSHOT_MAGIC,
1300                                         (unsigned long long)xtime,
1301                                         (unsigned long long)xtime);
1302                         if (len <= 0)
1303                                 exit(-EOVERFLOW);
1304
1305                         if (si->si_comment) {
1306                                 rc = scnprintf(cmd + len, sizeof(cmd) - len - 1,
1307                                                "-o lustre:comment=\"%s\" ",
1308                                                si->si_comment);
1309                                 if (rc <= 0)
1310                                         exit(-EOVERFLOW);
1311
1312                                 len += rc;
1313                         }
1314
1315                         /* Make the inherited properties as local ones,
1316                          * then even if others changed (or removed) the
1317                          * property of the parent dataset, the snapshot
1318                          * will not be affected. */
1319                         rc = snapshot_inherit_prop(si, st, cmd + len,
1320                                                    MAX_BUF_SIZE - len - 1);
1321                         if (rc < 0) {
1322                                 SNAPSHOT_ADD_LOG(si, "Can't filter property on "
1323                                                  "target (%s:%x:%d): rc = %d\n",
1324                                                  foreign ? st->st_fhost :
1325                                                  st->st_host, st->st_role,
1326                                                  st->st_index, rc);
1327                                 exit(rc);
1328                         }
1329
1330                         len += rc;
1331                         if (st->st_role & SR_OST)
1332                                 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1333                                               "-o lustre:svname=%s-OST%04x "
1334                                               "-o lustre:mgsnode=%s "DSSNAME"'"
1335                                               " 2>/dev/null",
1336                                               fsname, st->st_index, mgsnode,
1337                                               PSSNAME(si, st));
1338                         else if (!(st->st_role & SR_MGS) ||
1339                                 /* MGS is on MDT0 */
1340                                  si->si_mdt0 == si->si_mgs)
1341                                 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1342                                               "-o lustre:svname=%s-MDT%04x "
1343                                               "-o lustre:mgsnode=%s "DSSNAME"'"
1344                                               " 2>/dev/null",
1345                                               fsname, st->st_index, mgsnode,
1346                                               PSSNAME(si, st));
1347                         else
1348                                 /* separated MGS */
1349                                 rc = snprintf(cmd + len, sizeof(cmd) - len - 1,
1350                                               DSSNAME"' 2>/dev/null",
1351                                               PSSNAME(si, st));
1352                         if (rc <= 0)
1353                                 exit(-EOVERFLOW);
1354
1355                         rc = snapshot_exec(cmd);
1356                         if (rc) {
1357                                 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1358                                                  "target (%s:%x:%d): rc = %d\n",
1359                                                  cmd, foreign ? st->st_fhost :
1360                                                  st->st_host, st->st_role,
1361                                                  st->st_index, rc);
1362                                 if (!foreign && st->st_fhost) {
1363                                         foreign = 1;
1364                                         goto again;
1365                                 }
1366                         }
1367
1368                         exit(rc);
1369                 } /* end of child */
1370
1371                 /* parent continue to run more snapshot commands in parallel. */
1372                 st->st_pid = pid;
1373         }
1374
1375         return 0;
1376 }
1377
1378 static int __snapshot_destroy(struct snapshot_instance *si,
1379                               struct list_head *head);
1380
1381 static int snapshot_create(struct snapshot_instance *si)
1382 {
1383         char *__argv[3];
1384         char buf[MAX_BUF_SIZE];
1385         struct timeval tv;
1386         char new_fsname[9];
1387         int rc = 0;
1388         int rc1 = 0;
1389         int rc2 = 0;
1390
1391         rc = snapshot_create_check(si);
1392         if (rc)
1393                 return rc;
1394
1395         rc = gettimeofday(&tv, NULL);
1396         if (rc)
1397                 return rc;
1398
1399         srandom(tv.tv_usec);
1400         snprintf(new_fsname, sizeof(new_fsname), "%08x", (__u32)random());
1401
1402         rc = snapshot_get_mgsnode(si, buf, sizeof(buf));
1403         if (rc)
1404                 return rc;
1405
1406         __argv[1] = si->si_fsname;
1407         /* 1. Get barrier */
1408         if (si->si_barrier) {
1409                 char tbuf[8];
1410
1411                 memset(tbuf, 0, sizeof(tbuf));
1412                 snprintf(tbuf, 7, "%u", si->si_timeout);
1413                 __argv[0] = "barrier_freeze";
1414                 __argv[2] = tbuf;
1415                 rc = jt_barrier_freeze(3, __argv);
1416                 if (rc) {
1417                         SNAPSHOT_ADD_LOG(si, "Can't set barrier within %u "
1418                                          "seconds on %s: rc = %d\n",
1419                                          si->si_timeout, si->si_fsname, rc);
1420
1421                         return rc;
1422                 }
1423         }
1424
1425         /* 2. Fork config llog on MGS */
1426         __argv[0] = "fork_lcfg";
1427         __argv[2] = new_fsname;
1428         rc = jt_lcfg_fork(3, __argv);
1429         if (rc) {
1430                 SNAPSHOT_ADD_LOG(si, "Can't fork config log for create "
1431                                  "snapshot %s from %s to %s: rc = %d\n",
1432                                  si->si_ssname, si->si_fsname, new_fsname, rc);
1433                 goto out;
1434         }
1435
1436         /* 3.1 Create snapshot on every MDT */
1437         rc = __snapshot_create(si, &si->si_mdts_list, new_fsname, buf,
1438                                tv.tv_sec);
1439         if (!rc)
1440                 /* 3.2 Create snapshot on every OST */
1441                 rc = __snapshot_create(si, &si->si_osts_list, new_fsname, buf,
1442                                        tv.tv_sec);
1443
1444         /* 4. Wait for all children, even though part of them maybe failed */
1445         snapshot_wait(si, &rc1);
1446
1447 out:
1448         /* 5. Put barrier */
1449         if (si->si_barrier) {
1450                 if (!rc && !rc1) {
1451                         struct barrier_ctl bc;
1452
1453                         rc = __jt_barrier_stat(__argv[1], &bc);
1454                         if (rc) {
1455                                 SNAPSHOT_ADD_LOG(si, "Can't get barrier status "
1456                                                  "on %s: rc = %d\n",
1457                                                  si->si_fsname, rc);
1458                         } else if (bc.bc_status != BS_FROZEN ||
1459                                    bc.bc_timeout <= 0) {
1460                                 SNAPSHOT_ADD_LOG(si, "The barrier expired "
1461                                                  "on %s\n", si->si_fsname);
1462                                 rc = -ETIMEDOUT;
1463                         }
1464                 }
1465
1466                 __argv[0] = "barrier_thaw";
1467                 rc2 = jt_barrier_thaw(2, __argv);
1468                 if (rc2)
1469                         SNAPSHOT_ADD_LOG(si, "Can't release barrier on %s: "
1470                                          "rc = %d\n", si->si_fsname, rc2);
1471         }
1472
1473         /* cleanup */
1474         if (rc || rc1) {
1475                 si->si_force = true;
1476                 __snapshot_destroy(si, &si->si_osts_list);
1477                 __snapshot_destroy(si, &si->si_mdts_list);
1478                 snapshot_wait(si, &rc2);
1479
1480                 __argv[0] = "erase_lcfg";
1481                 __argv[1] = new_fsname;
1482                 __argv[2] = "-q";
1483                 jt_lcfg_erase(3, __argv);
1484         }
1485
1486         return rc ? rc : (rc1 ? rc1 : rc2);
1487 }
1488
1489 int jt_snapshot_create(int argc, char **argv)
1490 {
1491         struct snapshot_instance *si;
1492         struct option long_opts[] = {
1493         { .val = 'b',   .name = "barrier",      .has_arg = optional_argument },
1494         { .val = 'c',   .name = "comment",      .has_arg = required_argument },
1495         { .val = 'F',   .name = "fsname",       .has_arg = required_argument },
1496         { .val = 'h',   .name = "help",         .has_arg = no_argument },
1497         { .val = 'n',   .name = "name",         .has_arg = required_argument },
1498         { .val = 'r',   .name = "rsh",          .has_arg = required_argument },
1499         { .val = 't',   .name = "timeout",      .has_arg = required_argument },
1500         { .name = NULL } };
1501         int rc = 0;
1502
1503         si = snapshot_init(argc, argv, long_opts, "b::c:F:hn:r:t:",
1504                            snapshot_create_usage, LOCK_EX, &rc);
1505         if (!si)
1506                 return rc;
1507
1508         if (!si->si_ssname) {
1509                 fprintf(stderr,
1510                         "Miss the snapshot name to be created\n");
1511                 snapshot_create_usage();
1512                 snapshot_fini(si);
1513                 return -EINVAL;
1514         }
1515
1516         rc = snapshot_create(si);
1517         if (rc) {
1518                 fprintf(stderr,
1519                         "Can't create the snapshot %s\n", si->si_ssname);
1520                 SNAPSHOT_ADD_LOG(si, "Can't create snapshot %s with "
1521                                  "comment <%s> barrier <%s>, timeout "
1522                                  "<%d>: %d\n",
1523                                  si->si_ssname, si->si_comment,
1524                                  si->si_barrier ? "enable" : "disable",
1525                                  si->si_barrier ? si->si_timeout : -1, rc);
1526         } else {
1527                 SNAPSHOT_ADD_LOG(si, "Create snapshot %s successfully "
1528                                  "with comment <%s>, barrier <%s>, "
1529                                  "timeout <%d>\n",
1530                                  si->si_ssname, si->si_comment,
1531                                  si->si_barrier ? "enable" : "disable",
1532                                  si->si_barrier ? si->si_timeout : -1);
1533         }
1534
1535         snapshot_fini(si);
1536         return rc;
1537 }
1538
1539 static void snapshot_destroy_usage(void)
1540 {
1541         fprintf(stderr,
1542                 "Destroy the specified snapshot.\n"
1543                 "Usage:\n"
1544                 "snapshot_destroy [-f | --force] "
1545                                  "<-F | --fsname fsname> [-h | --help] "
1546                                  "<-n | --name ssname> "
1547                                  "[-r | --rsh remote_shell]\n"
1548                 "Options:\n"
1549                 "-f: destroy the snapshot by force.\n"
1550                 "-F: the filesystem name.\n"
1551                 "-h: for help information.\n"
1552                 "-n: the snapshot's name.\n"
1553                 "-r: the remote shell used for communication with remote "
1554                         "target, the default value is 'ssh'.\n");
1555 }
1556
1557 static int snapshot_destroy_check(struct snapshot_instance *si)
1558 {
1559         struct list_head *head = &si->si_osts_list;
1560         struct snapshot_target *st;
1561         pid_t pid;
1562         int rc = 0;
1563
1564 again1:
1565         list_for_each_entry(st, head, st_list) {
1566                 st->st_status = 0;
1567                 st->st_ignored = 0;
1568                 st->st_pid = 0;
1569
1570                 pid = fork();
1571                 if (pid < 0) {
1572                         SNAPSHOT_ADD_LOG(si, "Can't fork for check snapshot "
1573                                          "%s on the target (%s(%s):%x:%d): "
1574                                          "%s\n",
1575                                          si->si_ssname, st->st_host,
1576                                          st->st_fhost ? st->st_fhost : "none",
1577                                          st->st_role, st->st_index,
1578                                          strerror(errno));
1579                         return pid;
1580                 }
1581
1582                 /* child */
1583                 if (pid == 0) {
1584                         rc = snapshot_exists_check(si, st);
1585                         if (!rc)
1586                                 /* The snapshot piece does not exist */
1587                                 exit(-ESRCH);
1588
1589                         exit(rc == -EEXIST ? 0: rc);
1590                 } /* end of child */
1591
1592                 /* parent continue to run more snapshot commands in parallel. */
1593                 st->st_pid = pid;
1594         }
1595
1596         if (head == &si->si_osts_list) {
1597                 head = &si->si_mdts_list;
1598                 goto again1;
1599         }
1600
1601         snapshot_wait(si, &rc);
1602         if (rc)
1603                 return rc;
1604
1605         head = &si->si_osts_list;
1606
1607 again2:
1608         list_for_each_entry(st, head, st_list) {
1609                 if (st->st_ignored && !si->si_force) {
1610                         char name[8];
1611
1612                         snapshot_role2name(name, st->st_role, st->st_index);
1613                         fprintf(stderr,
1614                                 "Miss snapshot piece on the %s. Use '-f' "
1615                                 "option if want to destroy it by force.\n",
1616                                 name);
1617
1618                         return -ENOENT;
1619                 }
1620         }
1621
1622         if (head == &si->si_osts_list) {
1623                 head = &si->si_mdts_list;
1624                 goto again2;
1625         }
1626
1627         if (!si->si_force)
1628                 rc = snapshot_general_check(si);
1629
1630         return rc;
1631 }
1632
1633 static int __snapshot_destroy(struct snapshot_instance *si,
1634                               struct list_head *head)
1635 {
1636         struct snapshot_target *st;
1637         pid_t pid;
1638         int rc;
1639
1640         list_for_each_entry(st, head, st_list) {
1641                 if (st->st_ignored)
1642                         continue;
1643
1644                 st->st_status = 0;
1645                 st->st_pid = 0;
1646
1647                 pid = fork();
1648                 if (pid < 0) {
1649                         SNAPSHOT_ADD_LOG(si, "Can't fork for destroy snapshot "
1650                                          "%s on the target (%s(%s):%x:%d): "
1651                                          "%s\n",
1652                                          si->si_ssname, st->st_host,
1653                                          st->st_fhost ? st->st_fhost : "none",
1654                                          st->st_role, st->st_index,
1655                                          strerror(errno));
1656                         return pid;
1657                 }
1658
1659                 /* child */
1660                 if (pid == 0) {
1661                         char cmd[MAX_BUF_SIZE * 2];
1662                         int foreign = 0;
1663 again:
1664                         memset(cmd, 0, sizeof(cmd));
1665                         if (si->si_force)
1666                                 snprintf(cmd, sizeof(cmd) - 1,
1667                                          DRSH" 'umount -f "DSSNAME
1668                                          " > /dev/null 2>&1; "DZFS
1669                                          " destroy -f "DSSNAME"'"
1670                                          " 2>/dev/null",
1671                                          PRSH(si, st, foreign),
1672                                          PSSNAME(si, st), PZFS(st),
1673                                          PSSNAME(si, st));
1674                         else
1675                                 snprintf(cmd, sizeof(cmd) - 1,
1676                                          DRSH" '"DZFS" destroy "DSSNAME"'"
1677                                          " 2>/dev/null",
1678                                          PRSH(si, st, foreign), PZFS(st),
1679                                          PSSNAME(si, st));
1680                         rc = snapshot_exec(cmd);
1681                         if (rc) {
1682                                 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1683                                                  "target (%s:%x:%d): rc = %d\n",
1684                                                  cmd, foreign ? st->st_fhost :
1685                                                  st->st_host, st->st_role,
1686                                                  st->st_index, rc);
1687                                 if (!foreign && st->st_fhost) {
1688                                         foreign = 1;
1689                                         goto again;
1690                                 }
1691                         }
1692
1693                         exit(rc);
1694                 } /* end of child */
1695
1696                 /* parent continue to run more snapshot commands in parallel. */
1697                 st->st_pid = pid;
1698         }
1699
1700         return 0;
1701 }
1702
1703 static int snapshot_destroy(struct snapshot_instance *si)
1704 {
1705         char fsname[9];
1706         int rc = 0;
1707         int rc1 = 0;
1708         int rc2 = 0;
1709         int rc3 = 0;
1710
1711         rc = snapshot_destroy_check(si);
1712         if (rc)
1713                 return rc;
1714
1715         rc = snapshot_get_fsname(si, fsname, sizeof(fsname));
1716         if (rc)
1717                 return rc;
1718
1719         /* 1.1 Destroy snapshot on every OST */
1720         rc = __snapshot_destroy(si, &si->si_osts_list);
1721         if (!si->si_force) {
1722                 if (rc)
1723                         return rc;
1724
1725                 __snapshot_wait(si, &si->si_osts_list, &rc);
1726                 if (rc)
1727                         return rc;
1728         }
1729
1730         /* 1.2 Destroy snapshot on every MDT */
1731         rc1 = __snapshot_destroy(si, &si->si_mdts_list);
1732
1733         /* 2 Wait for all children, even though part of them maybe failed */
1734         snapshot_wait(si, &rc2);
1735         if (rc2 == -ENOENT && si->si_force)
1736                 rc2 = 0;
1737
1738         /* 3. Erase config llog from MGS */
1739         if ((!rc && !rc1 && !rc2) || si->si_force) {
1740                 char *__argv[3];
1741
1742                 __argv[0] = "erase_lcfg";
1743                 __argv[1] = fsname;
1744                 __argv[2] = "-q";
1745                 rc3 = jt_lcfg_erase(3, __argv);
1746                 if (rc3 && errno == ENOENT)
1747                         rc3 = 0;
1748                 if (rc3)
1749                         SNAPSHOT_ADD_LOG(si, "Can't erase config for destroy "
1750                                          "snapshot %s, fsname %s: rc = %d\n",
1751                                          si->si_ssname, fsname, rc3);
1752         }
1753
1754         return rc ? rc : (rc1 ? rc1 : (rc2 ? rc2 : rc3));
1755 }
1756
1757 int jt_snapshot_destroy(int argc, char **argv)
1758 {
1759         struct snapshot_instance *si;
1760         struct option long_opts[] = {
1761         { .val = 'f',   .name = "force",        .has_arg = no_argument },
1762         { .val = 'F',   .name = "fsname",       .has_arg = required_argument },
1763         { .val = 'h',   .name = "help",         .has_arg = no_argument },
1764         { .val = 'n',   .name = "name",         .has_arg = required_argument },
1765         { .val = 'r',   .name = "rsh",          .has_arg = required_argument },
1766         { .name = NULL } };
1767         int rc = 0;
1768
1769         si = snapshot_init(argc, argv, long_opts, "fF:hn:r:",
1770                            snapshot_destroy_usage, LOCK_EX, &rc);
1771         if (!si)
1772                 return rc;
1773
1774         if (!si->si_ssname) {
1775                 fprintf(stderr,
1776                         "Miss the snapshot name to be destroyed\n");
1777                 snapshot_destroy_usage();
1778                 snapshot_fini(si);
1779                 return -EINVAL;
1780         }
1781
1782         rc = snapshot_destroy(si);
1783         if (rc) {
1784                 fprintf(stderr,
1785                         "Can't destroy the snapshot %s\n", si->si_ssname);
1786                 SNAPSHOT_ADD_LOG(si, "Can't destroy snapshot %s with "
1787                                  "force <%s>: %d\n", si->si_ssname,
1788                                  si->si_force ? "enable" : "disable", rc);
1789         } else {
1790                 SNAPSHOT_ADD_LOG(si, "Destroy snapshot %s successfully "
1791                                  "with force <%s>\n", si->si_ssname,
1792                                  si->si_force ? "enable" : "disable");
1793         }
1794
1795         snapshot_fini(si);
1796         return rc;
1797 }
1798
1799 static void snapshot_modify_usage(void)
1800 {
1801         fprintf(stderr,
1802                 "Change the specified snapshot's name and/or comment.\n"
1803                 "Usage:\n"
1804                 "snapshot_modify [-c | --comment comment] "
1805                                 "<-F | --fsname fsname> [-h | --help] "
1806                                 "<-n | --name ssname> [-N | --new new_ssname] "
1807                                 "[-r | --rsh remote_shell]\n"
1808                 "Options:\n"
1809                 "-c: update the snapshot's comment.\n"
1810                 "-F: the filesystem name.\n"
1811                 "-h: for help information.\n"
1812                 "-n: the snapshot's name.\n"
1813                 "-N: rename the snapshot's name as the new_ssname.\n"
1814                 "-r: the remote shell used for communication with remote "
1815                         "target, the default value is 'ssh'.\n");
1816 }
1817
1818 static int snapshot_modify_check(struct snapshot_instance *si)
1819 {
1820         int rc;
1821
1822         if (si->si_new_ssname &&
1823             strcmp(si->si_ssname, si->si_new_ssname) == 0) {
1824                 fprintf(stderr, "The new snapshot's name is the same as "
1825                         "the old one %s %s.\n",
1826                         si->si_ssname, si->si_new_ssname);
1827                 return -EPERM;
1828         }
1829
1830         if (!si->si_new_ssname && !si->si_comment) {
1831                 fprintf(stderr, "Miss options, nothing to be changed.\n");
1832                 return -EINVAL;
1833         }
1834
1835         rc = mdt0_is_lustre_snapshot(si);
1836         if (!rc && si->si_new_ssname) {
1837                 rc = target_is_mounted(si, si->si_mdt0, si->si_ssname);
1838                 if (rc > 0) {
1839                         fprintf(stderr,
1840                                 "snapshot %s is mounted, can't be renamed.\n",
1841                                 si->si_ssname);
1842                         rc = -EBUSY;
1843                 }
1844         }
1845
1846         return rc;
1847 }
1848
1849 static int __snapshot_modify(struct snapshot_instance *si,
1850                              struct list_head *head, __u64 xtime)
1851 {
1852         struct snapshot_target *st;
1853         pid_t pid;
1854         int rc;
1855
1856         list_for_each_entry(st, head, st_list) {
1857                 st->st_status = 0;
1858                 st->st_ignored = 0;
1859                 st->st_pid = 0;
1860
1861                 pid = fork();
1862                 if (pid < 0) {
1863                         SNAPSHOT_ADD_LOG(si, "Can't fork for modify snapshot "
1864                                          "(%s|%s, <%s>) on the target "
1865                                          "(%s(%s):%x:%d): %s\n",
1866                                          si->si_ssname, si->si_new_ssname,
1867                                          si->si_comment, st->st_host,
1868                                          st->st_fhost ? st->st_fhost : "none",
1869                                          st->st_role, st->st_index,
1870                                          strerror(errno));
1871                         return pid;
1872                 }
1873
1874                 /* child */
1875                 if (pid == 0) {
1876                         char cmd[MAX_BUF_SIZE * 5];
1877                         int foreign = 0;
1878 again:
1879                         memset(cmd, 0, sizeof(cmd));
1880                         if (si->si_new_ssname && si->si_comment)
1881                                 snprintf(cmd, sizeof(cmd) - 1,
1882                                          DRSH" '"DIMPORT"; "DZFS" rename "
1883                                          DSSNAME" "DSSNAME" && "DZFS
1884                                          " set lustre:comment=\"%s\" "DSSNAME
1885                                          " && "DZFS
1886                                          " set lustre:mtime=%llu "DSSNAME"'"
1887                                          " 2>/dev/null",
1888                                          PRSH(si, st, foreign), PIMPORT(st),
1889                                          PZFS(st), PSSNAME(si, st),
1890                                          PSS_NEW(si, st), PZFS(st),
1891                                          si->si_comment, PSS_NEW(si, st),
1892                                          PZFS(st), (unsigned long long)xtime,
1893                                          PSS_NEW(si, st));
1894                         else if (si->si_new_ssname)
1895                                 snprintf(cmd, sizeof(cmd) - 1,
1896                                          DRSH" '"DIMPORT"; "DZFS
1897                                          " rename "DSSNAME" "DSSNAME" && "DZFS
1898                                          " set lustre:mtime=%llu "DSSNAME"'"
1899                                          " 2>/dev/null",
1900                                          PRSH(si, st, foreign), PIMPORT(st),
1901                                          PZFS(st), PSSNAME(si, st),
1902                                          PSS_NEW(si, st), PZFS(st),
1903                                          (unsigned long long)xtime,
1904                                          PSS_NEW(si, st));
1905                         else if (si->si_comment)
1906                                 snprintf(cmd, sizeof(cmd) - 1,
1907                                          DRSH" '"DIMPORT"; "DZFS
1908                                          " set lustre:comment=\"%s\" "DSSNAME
1909                                          " && "DZFS " set lustre:mtime=%llu "
1910                                          DSSNAME"' 2>/dev/null",
1911                                          PRSH(si, st, foreign), PIMPORT(st),
1912                                          PZFS(st), si->si_comment,
1913                                          PSSNAME(si, st), PZFS(st),
1914                                          (unsigned long long)xtime,
1915                                          PSSNAME(si, st));
1916                         else
1917                                 exit(-EINVAL);
1918
1919                         rc = snapshot_exec(cmd);
1920                         if (rc) {
1921                                 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on "
1922                                                  "target (%s:%x:%d): rc = %d\n",
1923                                                  cmd, foreign ? st->st_fhost :
1924                                                  st->st_host, st->st_role,
1925                                                  st->st_index, rc);
1926                                 if (!foreign && st->st_fhost) {
1927                                         foreign = 1;
1928                                         goto again;
1929                                 }
1930                         }
1931
1932                         exit(rc);
1933                 } /* end of child */
1934
1935                 /* parent continue to run more snapshot commands in parallel. */
1936                 st->st_pid = pid;
1937         }
1938
1939         return 0;
1940 }
1941
1942 static int snapshot_modify(struct snapshot_instance *si)
1943 {
1944         time_t tt;
1945         int rc = 0;
1946         int rc1 = 0;
1947
1948         rc = snapshot_modify_check(si);
1949         if (rc)
1950                 return rc;
1951
1952         time(&tt);
1953
1954         /* Modify snapshot on every MDT */
1955         rc = __snapshot_modify(si, &si->si_mdts_list, (__u64)tt);
1956         if (!rc)
1957                 /* Modify snapshot on every OST */
1958                 rc = __snapshot_modify(si, &si->si_osts_list, (__u64)tt);
1959
1960         /* Wait for all children, even though part of them maybe failed */
1961         snapshot_wait(si, &rc1);
1962
1963         return rc ? rc : rc1;
1964 }
1965
1966 int jt_snapshot_modify(int argc, char **argv)
1967 {
1968         struct snapshot_instance *si;
1969         struct option long_opts[] = {
1970         { .val = 'c',   .name = "comment",      .has_arg = required_argument },
1971         { .val = 'F',   .name = "fsname",       .has_arg = required_argument },
1972         { .val = 'h',   .name = "help",         .has_arg = no_argument },
1973         { .val = 'n',   .name = "name",         .has_arg = required_argument },
1974         { .val = 'N',   .name = "new",          .has_arg = required_argument },
1975         { .val = 'r',   .name = "rsh",          .has_arg = required_argument },
1976         { .name = NULL } };
1977         int rc = 0;
1978
1979         si = snapshot_init(argc, argv, long_opts, "c:F:hn:N:r:",
1980                            snapshot_modify_usage, LOCK_EX, &rc);
1981         if (!si)
1982                 return rc;
1983
1984         if (!si->si_ssname) {
1985                 fprintf(stderr,
1986                         "Miss the snapshot name to be modified\n");
1987                 snapshot_modify_usage();
1988                 snapshot_fini(si);
1989                 return -EINVAL;
1990         }
1991
1992         rc = snapshot_modify(si);
1993         if (rc) {
1994                 fprintf(stderr,
1995                         "Can't modify the snapshot %s\n", si->si_ssname);
1996                 SNAPSHOT_ADD_LOG(si, "Can't modify snapshot %s with "
1997                                  "name <%s>, comment <%s>: %d\n",
1998                                  si->si_ssname, si->si_new_ssname,
1999                                  si->si_comment, rc);
2000         } else {
2001                 SNAPSHOT_ADD_LOG(si, "Modify snapshot %s successfully "
2002                                  "with name <%s>, comment <%s>\n",
2003                                  si->si_ssname, si->si_new_ssname,
2004                                  si->si_comment);
2005         }
2006
2007         snapshot_fini(si);
2008         return rc;
2009 }
2010
2011 static void snapshot_list_usage(void)
2012 {
2013         fprintf(stderr,
2014                 "List the specified snapshot or all snapshots.\n"
2015                 "Usage:\n"
2016                 "snapshot_list [-d | --detail] "
2017                               "<-F | --fsname fsname> [-h | --help] "
2018                               "[-n | --name ssname] [-r | --rsh remote_shell]\n"
2019                 "Options:\n"
2020                 "-d: list every piece for the specified snapshot.\n"
2021                 "-F: the filesystem name.\n"
2022                 "-h: for help information.\n"
2023                 "-n: the snapshot's name.\n"
2024                 "-r: the remote shell used for communication with remote "
2025                         "target, the default value is 'ssh'.\n");
2026 }
2027
2028 static int snapshot_list_one(struct snapshot_instance *si,
2029                              struct snapshot_target *st)
2030 {
2031         char buf[MAX_BUF_SIZE * 3];
2032         FILE *fp;
2033         int rc;
2034         int foreign = 0;
2035
2036 again:
2037         memset(buf, 0, sizeof(buf));
2038         snprintf(buf, sizeof(buf) - 1,
2039                  DRSH" \""DIMPORT"; "DZFS
2040                  " get all "DSSNAME" | grep lustre: | grep local$ | "
2041                  "awk '{ \\$1=\\\"\\\"; \\$NF=\\\"\\\"; print \\$0 }' | "
2042                  "sed -e 's/^ //'\" 2>/dev/null",
2043                  PRSH(si, st, foreign), PIMPORT(st), PZFS(st),
2044                  PSSNAME(si, st));
2045         fp = popen(buf, "r");
2046         if (!fp) {
2047                 SNAPSHOT_ADD_LOG(si, "Popen fail to list one: %s\n",
2048                                  strerror(errno));
2049                 return -errno;
2050         }
2051
2052         if (si->si_detail && !foreign) {
2053                 char name[8];
2054
2055                 snapshot_role2name(name, st->st_role, st->st_index);
2056                 printf("\nsnapshot_role: %s\n", name);
2057         }
2058
2059         while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2060                 __u64 xtime;
2061                 char *ptr;
2062                 foreign = 1;
2063
2064                 if (strncmp(buf, "lustre:fsname",
2065                             strlen("lustre:fsname")) == 0) {
2066                         ptr = snapshot_first_skip_blank(buf);
2067                         if (ptr)
2068                                 printf("snapshot_fsname: %s\n", ptr);
2069                         continue;
2070                 }
2071
2072                 if (strncmp(buf, "lustre:comment",
2073                             strlen("lustre:comment")) == 0) {
2074                         ptr = snapshot_first_skip_blank(buf);
2075                         if (ptr)
2076                                 printf("comment: %s\n", ptr);
2077                         continue;
2078                 }
2079
2080                 if (strncmp(buf, "lustre:ctime",
2081                             strlen("lustre:ctime")) == 0) {
2082                         ptr = snapshot_first_skip_blank(buf);
2083                         if (ptr) {
2084                                 xtime = (__u64)strtoull(ptr, NULL, 10);
2085                                 printf("create_time: %s",
2086                                        ctime((time_t *)&xtime));
2087                         }
2088                         continue;
2089                 }
2090
2091                 if (strncmp(buf, "lustre:mtime",
2092                             strlen("lustre:mtime")) == 0) {
2093                         ptr = snapshot_first_skip_blank(buf);
2094                         if (ptr) {
2095                                 xtime = (__u64)strtoull(ptr, NULL, 10);
2096                                 printf("modify_time: %s",
2097                                        ctime((time_t *)&xtime));
2098                         }
2099                         continue;
2100                 }
2101         }
2102
2103         pclose(fp);
2104         if (!foreign && st->st_fhost) {
2105                 foreign = 1;
2106                 goto again;
2107         }
2108         rc = target_is_mounted(si, st, si->si_ssname);
2109         if (rc < 0)
2110                 printf("status: unknown\n");
2111         else if (!rc)
2112                 printf("status: not mount\n");
2113         else
2114                 printf("status: mounted\n");
2115
2116         return rc;
2117 }
2118
2119 static int __snapshot_list(struct snapshot_instance *si,
2120                            struct list_head *head)
2121 {
2122         struct snapshot_target *st;
2123         int rc = 0;
2124
2125         list_for_each_entry(st, head, st_list) {
2126                 int rc1;
2127
2128                 rc1 = snapshot_list_one(si, st);
2129                 if (rc1 < 0 || rc >= 0)
2130                         rc = rc1;
2131         }
2132
2133         return rc;
2134 }
2135
2136 static int snapshot_list(struct snapshot_instance *si)
2137 {
2138         int rc = 0;
2139
2140         rc = snapshot_general_check(si);
2141         if (rc)
2142                 return rc;
2143
2144         printf("\nfilesystem_name: %s\nsnapshot_name: %s\n",
2145                si->si_fsname, si->si_ssname);
2146
2147         if (!si->si_detail) {
2148                 rc = snapshot_list_one(si, si->si_mdt0);
2149         } else {
2150                 int rc1;
2151
2152                 rc = __snapshot_list(si, &si->si_mdts_list);
2153                 rc1 = __snapshot_list(si, &si->si_osts_list);
2154                 if (rc >= 0)
2155                         rc = rc1;
2156         }
2157
2158         return rc < 0 ? rc : 0;
2159 }
2160
2161 static int snapshot_list_all(struct snapshot_instance *si)
2162 {
2163         struct snapshot_target *st = si->si_mdt0;
2164         struct list_sub_item {
2165                 struct list_head lsi_list;
2166                 char lsi_ssname[0];
2167         };
2168
2169         struct list_head list_sub_items;
2170         struct list_sub_item *lsi;
2171         char buf[MAX_BUF_SIZE * 2];
2172         FILE *fp;
2173         int rc = 0;
2174         int foreign = 0;
2175
2176         INIT_LIST_HEAD(&list_sub_items);
2177 again:
2178         memset(buf, 0, sizeof(buf));
2179         snprintf(buf, sizeof(buf) - 1,
2180                  DRSH" \""DZFS" get -H -r lustre:magic "DFSNAME
2181                  " | grep %s | awk '{ print \\$1 }' | cut -d@ -f2\" "
2182                  " 2>/dev/null",
2183                  PRSH(si, st, foreign), PZFS(st), PFSNAME(st),
2184                  SNAPSHOT_MAGIC);
2185         fp = popen(buf, "r");
2186         if (!fp) {
2187                 SNAPSHOT_ADD_LOG(si, "Popen fail to list ssnames: %s\n",
2188                                  strerror(errno));
2189                 return -errno;
2190         }
2191
2192         while (snapshot_fgets(fp, buf, MAX_BUF_SIZE) != NULL) {
2193                 int len = strlen(buf);
2194                 foreign = 1;
2195
2196                 lsi = malloc(len + 1 + sizeof(struct list_sub_item));
2197                 if (!lsi) {
2198                         SNAPSHOT_ADD_LOG(si, "NOT enough memory\n");
2199                         rc = -ENOMEM;
2200                         break;
2201                 }
2202
2203                 memcpy(lsi->lsi_ssname, buf, len + 1);
2204                 list_add(&lsi->lsi_list, &list_sub_items);
2205         }
2206
2207         pclose(fp);
2208         if (!foreign && st->st_fhost) {
2209                 foreign = 1;
2210                 goto again;
2211         }
2212         while (!list_empty(&list_sub_items)) {
2213                 lsi = list_first_entry(&list_sub_items,
2214                                        struct list_sub_item, lsi_list);
2215                 list_del(&lsi->lsi_list);
2216                 if (!rc) {
2217                         si->si_ssname = lsi->lsi_ssname;
2218                         rc = snapshot_list(si);
2219                         si->si_ssname = NULL;
2220                 }
2221
2222                 free(lsi);
2223         }
2224
2225         return rc;
2226 }
2227
2228 int jt_snapshot_list(int argc, char **argv)
2229 {
2230         struct snapshot_instance *si;
2231         struct option long_opts[] = {
2232         { .val = 'd',   .name = "detail",       .has_arg = no_argument },
2233         { .val = 'F',   .name = "fsname",       .has_arg = required_argument },
2234         { .val = 'h',   .name = "help",         .has_arg = no_argument },
2235         { .val = 'n',   .name = "name",         .has_arg = required_argument },
2236         { .val = 'r',   .name = "rsh",          .has_arg = required_argument },
2237         { .name = NULL } };
2238         int rc = 0;
2239
2240         si = snapshot_init(argc, argv, long_opts, "dF:hn:r:",
2241                            snapshot_list_usage, LOCK_SH, &rc);
2242         if (!si)
2243                 return rc;
2244
2245         if (si->si_ssname)
2246                 rc = snapshot_list(si);
2247         else
2248                 rc = snapshot_list_all(si);
2249
2250         if (rc) {
2251                 fprintf(stderr,
2252                         "Can't list the snapshot %s\n", si->si_ssname);
2253                 SNAPSHOT_ADD_LOG(si, "Can't list snapshot %s with detail "
2254                                  "<%s>: %d\n", si->si_ssname,
2255                                  si->si_detail ? "yes" : "no", rc);
2256         }
2257
2258         snapshot_fini(si);
2259         return rc;
2260 }
2261
2262 static void snapshot_mount_usage(void)
2263 {
2264         fprintf(stderr,
2265                 "Mount the specified snapshot.\n"
2266                 "Usage:\n"
2267                 "snapshot_mount <-F | --fsname fsname> [-h | --help] "
2268                                "<-n | --name ssname> "
2269                                "[-r | --rsh remote_shell]\n"
2270                 "Options:\n"
2271                 "-F: the filesystem name.\n"
2272                 "-h: for help information.\n"
2273                 "-n: the snapshot's name.\n"
2274                 "-r: the remote shell used for communication with remote "
2275                         "target, the default value is 'ssh'.\n");
2276 }
2277
2278 static int snapshot_mount_check(struct snapshot_instance *si, char *fsname,
2279                                 int fslen, bool *mgs_running)
2280 {
2281         int rc;
2282
2283         rc = mdt0_is_lustre_snapshot(si);
2284         if (rc)
2285                 return rc;
2286
2287         rc = snapshot_get_fsname(si, fsname, fslen);
2288         if (rc < 0)
2289                 return rc;
2290
2291         rc = target_is_mounted(si, si->si_mgs, NULL);
2292         if (rc > 0) {
2293                 *mgs_running = true;
2294                 rc = 0;
2295         }
2296
2297         return rc;
2298 }
2299
2300 static int snapshot_mount_target(struct snapshot_instance *si,
2301                                  struct snapshot_target *st, const char *optstr)
2302 {
2303         char cmd[MAX_BUF_SIZE * 2];
2304         char name[8];
2305         int rc;
2306         int foreign = 0;
2307
2308         rc = target_is_mounted(si, st, si->si_ssname);
2309         if (rc < 0)
2310                 return rc;
2311
2312         if (rc > 0)
2313                 return -ESRCH;
2314
2315         memset(name, 0, sizeof(name));
2316         snapshot_role2name(name, st->st_role, st->st_index);
2317 again:
2318         memset(cmd, 0, sizeof(cmd));
2319         snprintf(cmd, sizeof(cmd) - 1,
2320                  DRSH" '"DIMPORT"; mkdir -p /mnt/%s_%s && mount -t lustre "
2321                  "-o rdonly_dev%s "DSSNAME" /mnt/%s_%s' 2>/dev/null",
2322                  PRSH(si, st, foreign), PIMPORT(st), si->si_ssname, name,
2323                  st != si->si_mdt0 ? "" : optstr, PSSNAME(si, st),
2324                  si->si_ssname, name);
2325         rc = snapshot_exec(cmd);
2326         if (rc) {
2327                 SNAPSHOT_ADD_LOG(si, "Can't execute \"%s\" on the target "
2328                                  "(%s:%x:%d): rc = %d\n",
2329                                  cmd, foreign ? st->st_fhost : st->st_host,
2330                                  st->st_role, st->st_index, rc);
2331                 if (!foreign && st->st_fhost) {
2332                         foreign = 1;
2333                         goto again;
2334                 }
2335         }
2336
2337         return rc;
2338 }
2339
2340 static int __snapshot_mount(struct snapshot_instance *si,
2341                             struct list_head *head)
2342 {
2343         struct snapshot_target *st;
2344         pid_t pid;
2345         int rc;
2346
2347         list_for_each_entry(st, head, st_list) {
2348                 if (st == si->si_mgs || st == si->si_mdt0)
2349                         continue;
2350
2351                 st->st_status = 0;
2352                 st->st_ignored = 0;
2353                 st->st_pid = 0;
2354
2355                 pid = fork();
2356                 if (pid < 0) {
2357                         SNAPSHOT_ADD_LOG(si, "Can't fork for mount snapshot "
2358                                          "%s on target (%s(%s):%x:%d): %s\n",
2359                                          si->si_ssname, st->st_host,
2360                                          st->st_fhost ? st->st_fhost : "none",
2361                                          st->st_role, st->st_index,
2362                                          strerror(errno));
2363                         return pid;
2364                 }
2365
2366                 /* child */
2367                 if (pid == 0) {
2368                         rc = snapshot_mount_target(si, st, "");
2369                         exit(rc);
2370                 }
2371
2372                 /* parent continue to run more snapshot commands in parallel. */
2373                 st->st_pid = pid;
2374         }
2375
2376         return 0;
2377 }
2378
2379 static int __snapshot_umount(struct snapshot_instance *si,
2380                              struct list_head *head);
2381
2382 static int snapshot_mount(struct snapshot_instance *si)
2383 {
2384         struct snapshot_target *st;
2385         int needed = 0;
2386         int failed = 0;
2387         int rc = 0;
2388         int rc1 = 0;
2389         char fsname[9];
2390         bool mdt0_mounted = false;
2391         bool mgs_running = false;
2392
2393         rc = snapshot_mount_check(si, fsname, sizeof(fsname), &mgs_running);
2394         if (rc < 0) {
2395                 fprintf(stderr,
2396                         "Can't mount the snapshot %s: %s\n",
2397                         si->si_ssname, strerror(-rc));
2398                 return rc;
2399         }
2400
2401         /* 1. MGS is not mounted yet, mount the MGS firstly */
2402         si->si_mgs->st_ignored = 0;
2403         si->si_mgs->st_pid = 0;
2404         if (!mgs_running) {
2405                 rc = snapshot_mount_target(si, si->si_mgs, "");
2406                 if (rc == -ESRCH) {
2407                         si->si_mgs->st_ignored = 1;
2408                         rc = 0;
2409                 }
2410
2411                 if (rc < 0) {
2412                         fprintf(stderr,
2413                                 "Can't mount the snapshot %s: %s\n",
2414                                 si->si_ssname, strerror(-rc));
2415                         return rc;
2416                 }
2417
2418                 if (si->si_mgs == si->si_mdt0)
2419                         mdt0_mounted = true;
2420         } else {
2421                 si->si_mgs->st_ignored = 1;
2422         }
2423
2424         /* 2. Mount MDT0 if it is not combined with the MGS. */
2425         if (!mdt0_mounted) {
2426                 si->si_mdt0->st_ignored = 0;
2427                 si->si_mdt0->st_pid = 0;
2428                 rc = snapshot_mount_target(si, si->si_mdt0, ",nomgs");
2429                 if (rc == -ESRCH) {
2430                         si->si_mdt0->st_ignored = 1;
2431                         rc = 0;
2432                 }
2433                 if (rc)
2434                         goto cleanup;
2435         }
2436
2437         /* 3.1 Mount other MDTs in parallel */
2438         rc = __snapshot_mount(si, &si->si_mdts_list);
2439         if (!rc)
2440                 /* 3.2 Mount OSTs in parallel */
2441                 rc = __snapshot_mount(si, &si->si_osts_list);
2442
2443         /* Wait for all children, even though part of them maybe failed */
2444         failed = snapshot_wait(si, &rc1);
2445
2446         list_for_each_entry(st, &si->si_mdts_list, st_list) {
2447                 if (!st->st_ignored)
2448                         needed++;
2449         }
2450
2451         list_for_each_entry(st, &si->si_osts_list, st_list) {
2452                 if (!st->st_ignored)
2453                         needed++;
2454         }
2455
2456 cleanup:
2457         if (rc || rc1) {
2458                 int rc2 = 0;
2459
2460                 __snapshot_umount(si, &si->si_mdts_list);
2461                 __snapshot_umount(si, &si->si_osts_list);
2462                 snapshot_wait(si, &rc2);
2463
2464                 if (rc)
2465                         fprintf(stderr,
2466                                 "Can't mount the snapshot %s: %s\n",
2467                                 si->si_ssname, strerror(-rc));
2468                 else
2469                         fprintf(stderr,
2470                                 "%d of %d pieces of the snapshot %s "
2471                                 "can't be mounted: %s\n",
2472                                 failed, needed, si->si_ssname, strerror(-rc1));
2473
2474                 return rc ? rc : rc1;
2475         }
2476
2477         if (needed == 0) {
2478                 fprintf(stderr,
2479                         "The snapshot %s has already been mounted by other\n",
2480                         si->si_ssname);
2481                 return -EALREADY;
2482         }
2483
2484         fprintf(stdout, "mounted the snapshot %s with fsname %s\n",
2485                 si->si_ssname, fsname);
2486
2487         return 0;
2488 }
2489
2490 int jt_snapshot_mount(int argc, char **argv)
2491 {
2492         struct snapshot_instance *si;
2493         struct option long_opts[] = {
2494         { .val = 'F',   .name = "fsname",       .has_arg = required_argument },
2495         { .val = 'h',   .name = "help",         .has_arg = no_argument },
2496         { .val = 'n',   .name = "name",         .has_arg = required_argument },
2497         { .val = 'r',   .name = "rsh",          .has_arg = required_argument },
2498         { .name = NULL } };
2499         int rc = 0;
2500
2501         si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2502                            snapshot_mount_usage, LOCK_EX, &rc);
2503         if (!si)
2504                 return rc;
2505
2506         if (!si->si_ssname) {
2507                 fprintf(stderr,
2508                         "Miss the snapshot name to be mounted\n");
2509                 snapshot_mount_usage();
2510                 snapshot_fini(si);
2511                 return -EINVAL;
2512         }
2513
2514         rc = snapshot_mount(si);
2515         if (rc)
2516                 SNAPSHOT_ADD_LOG(si, "Can't mount snapshot %s: %d\n",
2517                                  si->si_ssname, rc);
2518         else
2519                 SNAPSHOT_ADD_LOG(si, "The snapshot %s is mounted\n",
2520                                  si->si_ssname);
2521
2522         snapshot_fini(si);
2523         return rc;
2524
2525 }
2526
2527 static void snapshot_umount_usage(void)
2528 {
2529         fprintf(stderr,
2530                 "Umount the specified snapshot.\n"
2531                 "Usage:\n"
2532                 "snapshot_umount <-F | --fsname fsname> [-h | --help] "
2533                                 "<-n | --name ssname> "
2534                                 "[-r | --rsh remote_shell]\n"
2535                 "Options:\n"
2536                 "-F: the filesystem name.\n"
2537                 "-h: for help information.\n"
2538                 "-n: the snapshot's name.\n"
2539                 "-r: the remote shell used for communication with remote "
2540                         "target, the default value is 'ssh'.\n");
2541 }
2542
2543 static int __snapshot_umount(struct snapshot_instance *si,
2544                              struct list_head *head)
2545 {
2546         struct snapshot_target *st;
2547         pid_t pid;
2548         int rc;
2549
2550         list_for_each_entry(st, head, st_list) {
2551                 st->st_status = 0;
2552                 st->st_ignored = 0;
2553                 st->st_pid = 0;
2554
2555                 pid = fork();
2556                 if (pid < 0) {
2557                         SNAPSHOT_ADD_LOG(si, "Can't fork for umount snapshot "
2558                                          "%s on target (%s(%s):%x:%d): %s\n",
2559                                          si->si_ssname, st->st_host,
2560                                          st->st_fhost ? st->st_fhost : "none",
2561                                          st->st_role, st->st_index,
2562                                          strerror(errno));
2563                         return pid;
2564                 }
2565
2566                 /* child */
2567                 if (pid == 0) {
2568                         char cmd[MAX_BUF_SIZE];
2569                         int foreign = 0;
2570
2571                         rc = target_is_mounted(si, st, si->si_ssname);
2572                         if (rc < 0)
2573                                 exit(rc);
2574
2575                         if (!rc)
2576                                 exit(-ESRCH);
2577 again:
2578                         memset(cmd, 0, sizeof(cmd));
2579                         snprintf(cmd, sizeof(cmd) - 1,
2580                                  DRSH" 'umount "DSSNAME"' 2>/dev/null",
2581                                  PRSH(si, st, foreign), PSSNAME(si, st));
2582                         rc = snapshot_exec(cmd);
2583                         if (rc && !foreign && st->st_fhost) {
2584                                 foreign = 1;
2585                                 goto again;
2586                         }
2587
2588                         exit(rc);
2589                 }
2590
2591                 /* parent continue to run more snapshot commands in parallel. */
2592                 st->st_pid = pid;
2593         }
2594
2595         return 0;
2596 }
2597
2598 static int snapshot_umount(struct snapshot_instance *si)
2599 {
2600         struct snapshot_target *st;
2601         int needed = 0;
2602         int failed;
2603         int rc = 0;
2604         int rc1 = 0;
2605         int rc2 = 0;
2606
2607         rc = snapshot_general_check(si);
2608         if (rc < 0) {
2609                 fprintf(stderr,
2610                         "Can't umount the snapshot %s: %s\n",
2611                         si->si_ssname, strerror(-rc));
2612                 return rc;
2613         }
2614
2615         rc = __snapshot_umount(si, &si->si_mdts_list);
2616         rc1 = __snapshot_umount(si, &si->si_osts_list);
2617         failed = snapshot_wait(si, &rc2);
2618
2619         list_for_each_entry(st, &si->si_mdts_list, st_list) {
2620                 if (!st->st_ignored)
2621                         needed++;
2622         }
2623
2624         list_for_each_entry(st, &si->si_osts_list, st_list) {
2625                 if (!st->st_ignored)
2626                         needed++;
2627         }
2628
2629         if (needed == 0) {
2630                 fprintf(stderr,
2631                         "The snapshot %s has not been mounted\n",
2632                         si->si_ssname);
2633                 return -EALREADY;
2634         }
2635
2636         if (failed != 0)
2637                 fprintf(stderr,
2638                         "%d of %d pieces of the snapshot %s "
2639                         "can't be umounted: %s\n",
2640                         failed, needed, si->si_ssname, strerror(-rc2));
2641
2642         return rc ? rc : (rc1 ? rc1 : rc2);
2643 }
2644
2645 int jt_snapshot_umount(int argc, char **argv)
2646 {
2647         struct snapshot_instance *si;
2648         struct option long_opts[] = {
2649         { .val = 'F',   .name = "fsname",       .has_arg = required_argument },
2650         { .val = 'h',   .name = "help",         .has_arg = no_argument },
2651         { .val = 'n',   .name = "name",         .has_arg = required_argument },
2652         { .val = 'r',   .name = "rsh",          .has_arg = required_argument },
2653         { .name = NULL } };
2654         int rc = 0;
2655
2656         si = snapshot_init(argc, argv, long_opts, "F:hn:r:",
2657                            snapshot_umount_usage, LOCK_EX, &rc);
2658         if (!si)
2659                 return rc;
2660
2661         if (!si->si_ssname) {
2662                 fprintf(stderr,
2663                         "Miss the snapshot name to be umounted\n");
2664                 snapshot_umount_usage();
2665                 snapshot_fini(si);
2666                 return -EINVAL;
2667         }
2668
2669         rc = snapshot_umount(si);
2670         if (rc < 0)
2671                 SNAPSHOT_ADD_LOG(si, "Can't umount snapshot %s: %d\n",
2672                                  si->si_ssname, rc);
2673         else
2674                 SNAPSHOT_ADD_LOG(si, "the snapshot %s have been umounted\n",
2675                                  si->si_ssname);
2676
2677         snapshot_fini(si);
2678         return rc;
2679 }