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