Whamcloud - gitweb
small fix
[fs/lustre-release.git] / lustre / utils / llmount.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (C) 2002 Cluster File Systems, Inc.
5  *   Author: Robert Read <rread@clusterfs.com>
6  *
7  *   This file is part of Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23
24
25 #define _GNU_SOURCE
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <sys/mount.h>
33 #include <mntent.h>
34 #include <getopt.h>
35 #include <sys/utsname.h>
36
37 #include "obdctl.h"
38 #include <lnet/lnetctl.h>
39
40 int          verbose;
41 int          nomtab;
42 int          fake;
43 int          force;
44 int          retry;
45 static char *progname = NULL;
46 #define MAX_RETRIES 99
47
48 void usage(FILE *out)
49 {
50         fprintf(out, "%s v1.%d\n", progname, LMD_MAGIC & 0xFF);
51         fprintf(out, "usage: %s <mdsnode>[,<altmdsnode>]:/<mdsname>/<cfgname>"
52                 " <mountpt> [-fhnv] [-o mntopt]\n", progname);
53         fprintf(out, "\t<mdsnode>: nid of MDS (config) node\n"
54                 "\t<mdsname>: name of MDS service (e.g. mds1)\n"
55                 "\t<cfgname>: name of client config (e.g. client)\n"
56                 "\t<mountpt>: filesystem mountpoint (e.g. /mnt/lustre)\n"
57                 "\t-f|--fake: fake mount (updates /etc/mtab)\n"
58                 "\t--force: force mount even if already in /etc/mtab\n"
59                 "\t-h|--help: print this usage message\n"
60                 "\t-n|--nomtab: do not update /etc/mtab after mount\n"
61                 "\t-v|--verbose: print verbose config settings\n"
62                 "\t-o: filesystem mount options:\n"
63                 "\t\tflock/noflock: enable/disable flock support\n"
64                 "\t\troute=<gw>[-<gw>]:<low>[-<high>]: portal route to MDS\n"
65                 "\t\tuser_xattr/nouser_xattr: enable/disable user extended "
66                 "attributes\n"
67                 );
68         exit(out != stdout);
69 }
70
71 static int check_mtab_entry(char *spec, char *mtpt, char *type)
72 {
73         FILE *fp;
74         struct mntent *mnt;
75
76         if (!force) {
77                 fp = setmntent(MOUNTED, "r");
78                 if (fp == NULL)
79                         return(0);
80
81                 while ((mnt = getmntent(fp)) != NULL) {
82                         if (strcmp(mnt->mnt_fsname, spec) == 0 &&
83                             strcmp(mnt->mnt_dir, mtpt) == 0 &&
84                             strcmp(mnt->mnt_type, type) == 0) {
85                                 fprintf(stderr, "%s: according to %s %s is "
86                                         "already mounted on %s\n",
87                                         progname, MOUNTED, spec, mtpt);
88                                 return(1); /* or should we return an error? */
89                         }
90                 }
91                 endmntent(fp);
92         }
93         return(0);
94 }
95
96 static int
97 update_mtab_entry(char *spec, char *mtpt, char *type, char *opts,
98                   int flags, int freq, int pass)
99 {
100         FILE *fp;
101         struct mntent mnt;
102         int rc = 0;
103
104         mnt.mnt_fsname = spec;
105         mnt.mnt_dir = mtpt;
106         mnt.mnt_type = type;
107         mnt.mnt_opts = opts ? opts : "";
108         mnt.mnt_freq = freq;
109         mnt.mnt_passno = pass;
110
111         fp = setmntent(MOUNTED, "a+");
112         if (fp == NULL) {
113                 fprintf(stderr, "%s: setmntent(%s): %s:",
114                         progname, MOUNTED, strerror (errno));
115                 rc = 16;
116         } else {
117                 if ((addmntent(fp, &mnt)) == 1) {
118                         fprintf(stderr, "%s: addmntent: %s:",
119                                 progname, strerror (errno));
120                         rc = 16;
121                 } else if (verbose > 1) {
122                         fprintf(stderr, "%s: added %s on %s to %s\n",
123                                 progname, spec, mtpt, MOUNTED);
124                 }
125                 endmntent(fp);
126         }
127
128         return rc;
129 }
130
131 int
132 init_options(struct lustre_mount_data *lmd)
133 {
134         memset(lmd, 0, sizeof(*lmd));
135         lmd->lmd_magic = LMD_MAGIC;
136         return 0;
137 }
138
139 int
140 print_options(FILE *out, struct lustre_mount_data *lmd, const char *options)
141 {
142         int i;
143         for (i = 0; i < lmd->lmd_nid_count; i++) {
144                 fprintf(out, "mds nid %d:       %s\n", i,
145                         libcfs_nid2str(lmd->lmd_nid[i]));
146         }
147         fprintf(out, "mds name:        %s\n", lmd->lmd_mds);
148         fprintf(out, "profile:         %s\n", lmd->lmd_profile);
149         fprintf(out, "options:         %s\n", options);
150         fprintf(out, "retry:           %d\n", retry);
151
152         return 0;
153 }
154
155 static int parse_nids(struct lustre_mount_data *lmd, char *nids)
156 {
157         int i = 0;
158         char *tmp = 0;
159         lnet_nid_t nid;
160
161         while ((tmp = strsep(&nids, ",:"))) {
162                 nid = libcfs_str2nid(tmp);
163                 if (nid == LNET_NID_ANY) {
164                         fprintf(stderr, "%s: Can't parse NID '%s'\n",
165                                 progname, tmp);
166                         continue;
167                 }
168                 lmd->lmd_nid[lmd->lmd_nid_count++] = nid;
169                 if (lmd->lmd_nid_count >= MAX_FAILOVER_NIDS) {
170                         fprintf(stderr, "%s: Too many target NIDs: "
171                                 "ignoring nids after %s\n",
172                                 progname, tmp);
173                         break;
174                 }
175         }
176         return (lmd->lmd_nid_count);
177 }
178
179
180 /*****************************************************************************
181  *
182  * This part was cribbed from util-linux/mount/mount.c.  There was no clear
183  * license information, but many other files in the package are identified as
184  * GNU GPL, so it's a pretty safe bet that was their intent.
185  *
186  ****************************************************************************/
187 struct opt_map {
188         const char *opt;        /* option name */
189         int skip;               /* skip in mtab option string */
190         int inv;                /* true if flag value should be inverted */
191         int ms_mask;            /* MS flag mask value */
192         int lmd_mask;           /* LMD flag mask value */
193 };
194
195 static const struct opt_map opt_map[] = {
196   { "defaults", 0, 0, 0, 0         },      /* default options */
197   { "rw",       1, 1, MS_RDONLY, 0 },      /* read-write */
198   { "ro",       0, 0, MS_RDONLY, 0 },      /* read-only */
199   { "exec",     0, 1, MS_NOEXEC, 0 },      /* permit execution of binaries */
200   { "noexec",   0, 0, MS_NOEXEC, 0 },      /* don't execute binaries */
201   { "suid",     0, 1, MS_NOSUID, 0 },      /* honor suid executables */
202   { "nosuid",   0, 0, MS_NOSUID, 0 },      /* don't honor suid executables */
203   { "dev",      0, 1, MS_NODEV,  0 },      /* interpret device files  */
204   { "nodev",    0, 0, MS_NODEV,  0 },      /* don't interpret devices */
205   { "async",    0, 1, MS_SYNCHRONOUS, 0},  /* asynchronous I/O */
206   { "auto",     0, 0, 0, 0         },      /* Can be mounted using -a */
207   { "noauto",   0, 0, 0, 0         },      /* Can only be mounted explicitly */
208   { "nousers",  0, 1, 0, 0         },      /* Forbid ordinary user to mount */
209   { "nouser",   0, 1, 0, 0         },      /* Forbid ordinary user to mount */
210   { "noowner",  0, 1, 0, 0         },      /* Device owner has no special privs */
211   { "_netdev",  0, 0, 0, 0         },      /* Device accessible only via network */
212   { "flock",    0, 0, 0, LMD_FLG_FLOCK},   /* Enable flock support */
213   { "noflock",  1, 1, 0, LMD_FLG_FLOCK},   /* Disable flock support */
214   { "user_xattr", 0, 0, 0, LMD_FLG_USER_XATTR}, /* Enable get/set user xattr */
215   { "nouser_xattr", 1, 1, 0, LMD_FLG_USER_XATTR}, /* Disable user xattr */
216   { "acl",      0, 0, 0, LMD_FLG_ACL},     /* Enable ACL support */
217   { "noacl",    1, 1, 0, LMD_FLG_ACL},     /* Disable ACL support */
218   /* please add new mount options to usage message */
219   { NULL,       0, 0, 0, 0         }
220 };
221 /****************************************************************************/
222
223 static int parse_one_option(const char *check, int *ms_flags, int *lmd_flags)
224 {
225         const struct opt_map *opt;
226
227         for (opt = &opt_map[0]; opt->opt != NULL; opt++) {
228                 if (strcmp(check, opt->opt) == 0) {
229                         if (opt->inv) {
230                                 *ms_flags &= ~(opt->ms_mask);
231                                 *lmd_flags &= ~(opt->lmd_mask);
232                         } else {
233                                 *ms_flags |= opt->ms_mask;
234                                 *lmd_flags |= opt->lmd_mask;
235                         }
236                         return 1;
237                 }
238         }
239         return 0;
240 }
241
242 int parse_options(char *options, struct lustre_mount_data *lmd, int *flagp)
243 {
244         int val;
245         char *opt, *opteq;
246
247         *flagp = 0;
248         /* parsing ideas here taken from util-linux/mount/nfsmount.c */
249         for (opt = strtok(options, ","); opt; opt = strtok(NULL, ",")) {
250                 if ((opteq = strchr(opt, '='))) {
251                         val = atoi(opteq + 1);
252                         *opteq = '\0';
253                         if (!strcmp(opt, "retry")) {
254                                 if (val >= 0 || val < MAX_RETRIES)
255                                         retry = val;
256                                 else
257                                         retry = 0;
258                         } else {
259                                 fprintf(stderr, "%s: unknown option '%s'. "
260                                         "Ignoring.\n", progname, opt);
261                                 /* Ignore old nettype= for now 
262                                 usage(stderr);
263                                 */
264                         }
265                 } else {
266                         if (parse_one_option(opt, flagp, &lmd->lmd_flags))
267                                 continue;
268
269                         fprintf(stderr, "%s: unknown option '%s'\n",
270                                 progname, opt);
271                         usage(stderr);
272                 }
273         }
274         return 0;
275 }
276
277 int
278 build_data(char *source, char *options, struct lustre_mount_data *lmd,
279            int *flagp)
280 {
281         char  buf[1024];
282         char *nid = NULL;
283         char *mds = NULL;
284         char *profile = NULL;
285         char *s;
286         int   rc;
287
288         if (lmd_bad_magic(lmd))
289                 return 4;
290
291         if (strlen(source) >= sizeof(buf)) {
292                 fprintf(stderr, "%s: nid:/mds/profile argument too long\n",
293                         progname);
294                 return 1;
295         }
296         strcpy(buf, source);
297         if ((s = strchr(buf, ':'))) {
298                 nid = buf;
299                 *s = '\0';
300
301                 while (*++s == '/')
302                         ;
303                 mds = s;
304                 if ((s = strchr(mds, '/'))) {
305                         *s = '\0';
306                         profile = s + 1;
307                 } else {
308                         fprintf(stderr, "%s: directory to mount not in "
309                                 "nid:/mds/profile format\n",
310                                 progname);
311                         return(1);
312                 }
313         } else {
314                 fprintf(stderr, "%s: "
315                         "directory to mount not in nid:/mds/profile format\n",
316                         progname);
317                 return(1);
318         }
319
320         rc = parse_options(options, lmd, flagp);
321         if (rc)
322                 return rc;
323
324         if (parse_nids(lmd, nid) == 0) {
325                 fprintf(stderr, "%s: Can't parse any mds nids\n", progname);
326                 return(1);
327         }
328
329         if (strlen(mds) + 1 > sizeof(lmd->lmd_mds)) {
330                 fprintf(stderr, "%s: mds name too long\n", progname);
331                 return(1);
332         }
333         strcpy(lmd->lmd_mds, mds);
334
335         if (strlen(profile) + 1 > sizeof(lmd->lmd_profile)) {
336                 fprintf(stderr, "%s: profile name too long\n", progname);
337                 return(1);
338         }
339         strcpy(lmd->lmd_profile, profile);
340
341         return 0;
342 }
343
344 int main(int argc, char *const argv[])
345 {
346         char *source, *target, *options = "", optbuf[65536] = { '\0' };
347         int i, nargs = 3, opt, rc, flags, buflen = sizeof(optbuf) - 1;
348         struct lustre_mount_data lmd;
349         static struct option long_opt[] = {
350                 {"fake", 0, 0, 'f'},
351                 {"force", 0, 0, 1},
352                 {"help", 0, 0, 'h'},
353                 {"nomtab", 0, 0, 'n'},
354                 {"options", 1, 0, 'o'},
355                 {"verbose", 0, 0, 'v'},
356                 {0, 0, 0, 0}
357         };
358
359         progname = strrchr(argv[0], '/');
360         progname = progname ? progname + 1 : argv[0];
361
362         while ((opt = getopt_long(argc, argv, "fhno:v", long_opt,NULL)) != EOF){
363                 switch (opt) {
364                 case 1:
365                         ++force;
366                         if (verbose)
367                                 printf("force: %d\n", force);
368                         nargs++;
369                         break;
370                 case 'f':
371                         ++fake;
372                         if (verbose)
373                                 printf("fake: %d\n", fake);
374                         nargs++;
375                         break;
376                 case 'h':
377                         usage(stdout);
378                         break;
379                 case 'n':
380                         ++nomtab;
381                         if (verbose)
382                                 printf("nomtab: %d\n", nomtab);
383                         nargs++;
384                         break;
385                 case 'o':
386                         options = optarg;
387                         nargs++;
388                         break;
389                 case 'v':
390                         ++verbose;
391                         printf("verbose: %d\n", verbose);
392                         nargs++;
393                         break;
394                 default:
395                         fprintf(stderr, "%s: unknown option '%c'\n",
396                                 progname, opt);
397                         usage(stderr);
398                         break;
399                 }
400         }
401
402         if (optind + 2 > argc) {
403                 fprintf(stderr, "%s: too few arguments\n", progname);
404                 usage(stderr);
405         }
406
407         source = argv[optind];
408         target = argv[optind + 1];
409
410         if (verbose) {
411                 for (i = 0; i < argc; i++)
412                         printf("arg[%d] = %s\n", i, argv[i]);
413                 printf("source = %s, target = %s\n", source, target);
414         }
415
416         if (!force && check_mtab_entry(source, target, "lustre"))
417                 exit(32);
418
419         init_options(&lmd);
420
421         /* need to copy options, as parse_options->strtok() clobbers it
422          * and we can't use it later to put in /etc/mtab. */
423         if (strlen(options) > buflen)
424                 fprintf(stderr, "%s: options too long (%d > %d), ignore last\n",
425                         progname, strlen(options), buflen);
426         strncpy(optbuf, options, buflen);
427
428         rc = build_data(source, optbuf, &lmd, &flags);
429         if (rc) {
430                 exit(1);
431         }
432
433         if (verbose)
434                 print_options(stdout, &lmd, options);
435
436         rc = access(target, F_OK);
437         if (rc) {
438                 rc = errno;
439                 fprintf(stderr, "%s: %s inaccessible: %s\n", progname, target,
440                         strerror(errno));
441                 return 1;
442         }
443
444         if (!fake) {
445                 FILE *modpipe = popen("/sbin/modprobe -q llite", "r");
446                 if (modpipe != NULL)
447                         pclose(modpipe);
448                 /* use <= to include the initial mount before we retry */
449                 for (i = 0, rc = -EAGAIN; i <= retry && rc != 0; i++)
450                         rc = mount(source, target, "lustre", flags, &lmd);
451         }
452         if (rc) {
453                 fprintf(stderr, "%s: mount(%s, %s) failed: %s\n", progname,
454                         source, target, strerror(errno));
455                 print_options(stderr, &lmd, options);
456                 if (errno == ENODEV) {
457                         struct utsname unamebuf;
458                         char *modfile = "/etc/modutils.conf";
459
460                         if (uname(&unamebuf) == 0 &&
461                             strncmp(unamebuf.release, "2.4", 3) == 0)
462                                 modfile = "/etc/modules.conf";
463
464                         fprintf(stderr, "Are the lustre modules loaded?\n"
465                                 "Check %s and /proc/filesystems\n");
466                 }
467                 rc = 32;
468         } else if (!nomtab) {
469                 rc = update_mtab_entry(source, target, "lustre", options,0,0,0);
470         }
471         return rc;
472 }