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