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