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