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