libexport.a: Add helpers to manage DNS lookups
[nfs-utils.git] / utils / exportfs / exportfs.c
1 /*
2  * utils/exportfs/exportfs.c
3  *
4  * Export file systems to knfsd
5  *
6  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
7  *
8  * Extensive changes, 1999, Neil Brown <neilb@cse.unsw.edu.au>
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14
15 #include <sys/vfs.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdarg.h>
21 #include <getopt.h>
22 #include <netdb.h>
23 #include <errno.h>
24 #include "xmalloc.h"
25 #include "misc.h"
26 #include "nfslib.h"
27 #include "exportfs.h"
28 #include "xmalloc.h"
29 #include "xlog.h"
30
31 static void     export_all(int verbose);
32 static void     exportfs(char *arg, char *options, int verbose);
33 static void     unexportfs(char *arg, int verbose);
34 static void     exports_update(int verbose);
35 static void     dump(int verbose);
36 static void     error(nfs_export *exp, int err);
37 static void     usage(void);
38 static void     validate_export(nfs_export *exp);
39
40 int
41 main(int argc, char **argv)
42 {
43         char    *options = NULL;
44         int     f_export = 1;
45         int     f_all = 0;
46         int     f_verbose = 0;
47         int     f_reexport = 0;
48         int     f_ignore = 0;
49         int     i, c;
50         int     new_cache = 0;
51         int     force_flush = 0;
52
53         xlog_open("exportfs");
54
55         export_errno = 0;
56
57         while ((c = getopt(argc, argv, "aio:ruvf")) != EOF) {
58                 switch(c) {
59                 case 'a':
60                         f_all = 1;
61                         break;
62                 case 'i':
63                         f_ignore = 1;
64                         break;
65                 case 'o':
66                         options = optarg;
67                         break;
68                 case 'r':
69                         f_reexport = 1;
70                         f_all = 1;
71                         break;
72                 case 'u':
73                         f_export = 0;
74                         break;
75                 case 'v':
76                         f_verbose = 1;
77                         break;
78                 case 'f':
79                         force_flush = 1;
80                         break;
81                 default:
82                         usage();
83                         break;
84                 }
85         }
86
87         if (optind != argc && f_all) {
88                 fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n");
89                 return 1;
90         }
91         if (f_ignore && (f_all || ! f_export)) {
92                 fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n");
93                 return 1;
94         }
95         if (f_reexport && ! f_export) {
96                 fprintf(stderr, "exportfs: -r and -u are incompatible.\n");
97                 return 1;
98         }
99         new_cache = check_new_cache();
100         if (optind == argc && ! f_all) {
101                 if (force_flush) {
102                         if (new_cache)
103                                 cache_flush(1);
104                         else {
105                                 fprintf(stderr, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n");
106                                 exit(1);
107                         }
108                         return 0;
109                 } else {
110                         xtab_export_read();
111                         dump(f_verbose);
112                         return 0;
113                 }
114         }
115         if (f_export && ! f_ignore)
116                 export_read(_PATH_EXPORTS);
117         if (f_export) {
118                 if (f_all)
119                         export_all(f_verbose);
120                 else
121                         for (i = optind; i < argc ; i++)
122                                 exportfs(argv[i], options, f_verbose);
123         }
124         /* If we are unexporting everything, then
125          * don't care about what should be exported, as that
126          * may require DNS lookups..
127          */
128         if (! ( !f_export && f_all)) {
129                 /* note: xtab_*_read does not update entries if they already exist,
130                  * so this will not lose new options
131                  */
132                 if (!f_reexport)
133                         xtab_export_read();
134                 if (!f_export)
135                         for (i = optind ; i < argc ; i++)
136                                 unexportfs(argv[i], f_verbose);
137                 if (!new_cache)
138                         rmtab_read();
139         }
140         if (!new_cache) {
141                 xtab_mount_read();
142                 exports_update(f_verbose);
143         }
144         xtab_export_write();
145         if (new_cache)
146                 cache_flush(force_flush);
147         if (!new_cache)
148                 xtab_mount_write();
149
150         return export_errno;
151 }
152
153 static void
154 exports_update_one(nfs_export *exp, int verbose)
155 {
156                 /* check mountpoint option */
157         if (exp->m_mayexport &&
158             exp->m_export.e_mountpoint &&
159             !is_mountpoint(exp->m_export.e_mountpoint[0]?
160                            exp->m_export.e_mountpoint:
161                            exp->m_export.e_path)) {
162                 printf("%s not exported as %s not a mountpoint.\n",
163                        exp->m_export.e_path, exp->m_export.e_mountpoint);
164                 exp->m_mayexport = 0;
165         }
166         if (exp->m_mayexport && ((exp->m_exported<1) || exp->m_changed)) {
167                 if (verbose)
168                         printf("%sexporting %s:%s to kernel\n",
169                                exp->m_exported ?"re":"",
170                                exp->m_client->m_hostname,
171                                exp->m_export.e_path);
172                 if (!export_export(exp))
173                         error(exp, errno);
174         }
175         if (exp->m_exported && ! exp->m_mayexport) {
176                 if (verbose)
177                         printf("unexporting %s:%s from kernel\n",
178                                exp->m_client->m_hostname,
179                                exp->m_export.e_path);
180                 if (!export_unexport(exp))
181                         error(exp, errno);
182         }
183 }
184
185
186 /* we synchronise intention with reality.
187  * entries with m_mayexport get exported
188  * entries with m_exported but not m_mayexport get unexported
189  * looking at m_client->m_type == MCL_FQDN and m_client->m_type == MCL_GSS only
190  */
191 static void
192 exports_update(int verbose)
193 {
194         nfs_export      *exp;
195
196         for (exp = exportlist[MCL_FQDN].p_head; exp; exp=exp->m_next) {
197                 exports_update_one(exp, verbose);
198         }
199         for (exp = exportlist[MCL_GSS].p_head; exp; exp=exp->m_next) {
200                 exports_update_one(exp, verbose);
201         }
202 }
203                         
204 /*
205  * export_all finds all entries and
206  *    marks them xtabent and mayexport so that they get exported
207  */
208 static void
209 export_all(int verbose)
210 {
211         nfs_export      *exp;
212         int             i;
213
214         for (i = 0; i < MCL_MAXTYPES; i++) {
215                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
216                         if (verbose)
217                                 printf("exporting %s:%s\n",
218                                        exp->m_client->m_hostname, 
219                                        exp->m_export.e_path);
220                         exp->m_xtabent = 1;
221                         exp->m_mayexport = 1;
222                         exp->m_changed = 1;
223                         exp->m_warned = 0;
224                         validate_export(exp);
225                 }
226         }
227 }
228
229
230 static void
231 exportfs(char *arg, char *options, int verbose)
232 {
233         struct exportent *eep;
234         nfs_export      *exp;
235         struct hostent  *hp = NULL;
236         char            *path;
237         char            *hname = arg;
238         int             htype;
239
240         if ((path = strchr(arg, ':')) != NULL)
241                 *path++ = '\0';
242
243         if (!path || *path != '/') {
244                 fprintf(stderr, "Invalid exporting option: %s\n", arg);
245                 return;
246         }
247
248         if ((htype = client_gettype(hname)) == MCL_FQDN &&
249             (hp = gethostbyname(hname)) != NULL) {
250                 struct hostent *hp2 = hostent_dup (hp);
251                 hp = gethostbyaddr(hp2->h_addr, hp2->h_length,
252                                    hp2->h_addrtype);
253                 if (hp) {
254                         free(hp2);
255                         hp = hostent_dup(hp);
256                 } else
257                         hp = hp2;
258                 exp = export_find(hp, path);
259                 hname = hp->h_name;
260         } else {
261                 exp = export_lookup(hname, path, 0);
262         }
263
264         if (!exp) {
265                 if (!(eep = mkexportent(hname, path, options)) ||
266                     !(exp = export_create(eep, 0))) {
267                         if (hp) free (hp);
268                         return;
269                 }
270         } else if (!updateexportent(&exp->m_export, options)) {
271                 if (hp) free (hp);
272                 return;
273         }
274
275         if (verbose)
276                 printf("exporting %s:%s\n", exp->m_client->m_hostname, 
277                         exp->m_export.e_path);
278         exp->m_xtabent = 1;
279         exp->m_mayexport = 1;
280         exp->m_changed = 1;
281         exp->m_warned = 0;
282         validate_export(exp);
283         if (hp) free (hp);
284 }
285
286 static void
287 unexportfs(char *arg, int verbose)
288 {
289         nfs_export      *exp;
290         struct addrinfo *ai = NULL;
291         char            *path;
292         char            *hname = arg;
293         int             htype;
294
295         if ((path = strchr(arg, ':')) != NULL)
296                 *path++ = '\0';
297
298         if (!path || *path != '/') {
299                 fprintf(stderr, "Invalid unexporting option: %s\n",
300                         arg);
301                 return;
302         }
303
304         if ((htype = client_gettype(hname)) == MCL_FQDN) {
305                 ai = host_addrinfo(hname);
306                 if (ai)
307                         hname = ai->ai_canonname;
308         }
309
310         for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
311                 if (path && strcmp(path, exp->m_export.e_path))
312                         continue;
313                 if (htype != exp->m_client->m_type)
314                         continue;
315                 if (htype == MCL_FQDN
316                     && !matchhostname(exp->m_export.e_hostname,
317                                           hname))
318                         continue;
319                 if (htype != MCL_FQDN
320                     && strcasecmp(exp->m_export.e_hostname, hname))
321                         continue;
322                 if (verbose) {
323 #if 0
324                         if (exp->m_exported) {
325                                 printf("unexporting %s:%s from kernel\n",
326                                        exp->m_client->m_hostname,
327                                        exp->m_export.e_path);
328                         }
329                         else
330 #endif
331                                 printf("unexporting %s:%s\n",
332                                         exp->m_client->m_hostname, 
333                                         exp->m_export.e_path);
334                 }
335 #if 0
336                 if (exp->m_exported && !export_unexport(exp))
337                         error(exp, errno);
338 #endif
339                 exp->m_xtabent = 0;
340                 exp->m_mayexport = 0;
341         }
342
343         freeaddrinfo(ai);
344 }
345
346 static int can_test(void)
347 {
348         int fd;
349         int n;
350         char *setup = "nfsd 0.0.0.0 2147483647 -test-client-\n";
351         fd = open("/proc/net/rpc/auth.unix.ip/channel", O_WRONLY);
352         if ( fd < 0) return 0;
353         n = write(fd, setup, strlen(setup));
354         close(fd);
355         if (n < 0)
356                 return 0;
357         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
358         if ( fd < 0) return 0;
359         close(fd);
360         return 1;
361 }
362
363 static int test_export(char *path, int with_fsid)
364 {
365         char buf[1024];
366         int fd, n;
367
368         sprintf(buf, "-test-client- %s 3 %d -1 -1 0\n",
369                 path,
370                 with_fsid ? NFSEXP_FSID : 0);
371         fd = open("/proc/net/rpc/nfsd.export/channel", O_WRONLY);
372         if (fd < 0)
373                 return 0;
374         n = write(fd, buf, strlen(buf));
375         close(fd);
376         if (n < 0)
377                 return 0;
378         return 1;
379 }
380
381 static void
382 validate_export(nfs_export *exp)
383 {
384         /* Check that the given export point is potentially exportable.
385          * We just give warnings here, don't cause anything to fail.
386          * If a path doesn't exist, or is not a dir or file, give an warning
387          * otherwise trial-export to '-test-client-' and check for failure.
388          */
389         struct stat stb;
390         char *path = exp->m_export.e_path;
391         struct statfs64 stf;
392         int fs_has_fsid = 0;
393
394         if (stat(path, &stb) < 0) {
395                 fprintf(stderr, "exportfs: Warning: %s does not exist\n",
396                         path);
397                 return;
398         }
399         if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
400                 fprintf(stderr, "exportfs: Warning: %s is neither "
401                         "a directory nor a file.\n"
402                         "     remote access will fail\n", path);
403                 return;
404         }
405         if (!can_test())
406                 return;
407
408         if (!statfs64(path, &stf) &&
409             (stf.f_fsid.__val[0] || stf.f_fsid.__val[1]))
410                 fs_has_fsid = 1;
411
412         if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid ||
413             fs_has_fsid) {
414                 if ( !test_export(path, 1)) {
415                         fprintf(stderr, "exportfs: Warning: %s does not "
416                                 "support NFS export.\n",
417                                 path);
418                         return;
419                 }
420         } else if ( ! test_export(path, 0)) {
421                 if (test_export(path, 1))
422                         fprintf(stderr, "exportfs: Warning: %s requires fsid= "
423                                 "for NFS export\n", path);
424                 else
425                         fprintf(stderr, "exportfs: Warning: %s does not "
426                                 "support NFS export.\n",
427                                 path);
428                 return;
429
430         }
431 }
432
433
434 static char
435 dumpopt(char c, char *fmt, ...)
436 {
437         va_list ap;
438
439         va_start(ap, fmt);
440         printf("%c", c);
441         vprintf(fmt, ap);
442         va_end(ap);
443         return ',';
444 }
445
446 static void
447 dump(int verbose)
448 {
449         nfs_export      *exp;
450         struct exportent *ep;
451         int             htype;
452         char            *hname, c;
453
454         for (htype = 0; htype < MCL_MAXTYPES; htype++) {
455                 for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
456                         ep = &exp->m_export;
457                         if (!exp->m_xtabent)
458                             continue; /* neilb */
459                         if (htype == MCL_ANONYMOUS)
460                                 hname = "<world>";
461                         else
462                                 hname = ep->e_hostname;
463                         if (strlen(ep->e_path) > 14)
464                                 printf("%-14s\n\t\t%s", ep->e_path, hname);
465                         else
466                                 printf("%-14s\t%s", ep->e_path, hname);
467                         if (!verbose) {
468                                 printf("\n");
469                                 continue;
470                         }
471                         c = '(';
472                         if (ep->e_flags & NFSEXP_READONLY)
473                                 c = dumpopt(c, "ro");
474                         else
475                                 c = dumpopt(c, "rw");
476                         if (ep->e_flags & NFSEXP_ASYNC)
477                                 c = dumpopt(c, "async");
478                         if (ep->e_flags & NFSEXP_GATHERED_WRITES)
479                                 c = dumpopt(c, "wdelay");
480                         if (ep->e_flags & NFSEXP_NOHIDE)
481                                 c = dumpopt(c, "nohide");
482                         if (ep->e_flags & NFSEXP_CROSSMOUNT)
483                                 c = dumpopt(c, "crossmnt");
484                         if (ep->e_flags & NFSEXP_INSECURE_PORT)
485                                 c = dumpopt(c, "insecure");
486                         if (ep->e_flags & NFSEXP_ROOTSQUASH)
487                                 c = dumpopt(c, "root_squash");
488                         else
489                                 c = dumpopt(c, "no_root_squash");
490                         if (ep->e_flags & NFSEXP_ALLSQUASH)
491                                 c = dumpopt(c, "all_squash");
492                         if (ep->e_flags & NFSEXP_NOSUBTREECHECK)
493                                 c = dumpopt(c, "no_subtree_check");
494                         if (ep->e_flags & NFSEXP_NOAUTHNLM)
495                                 c = dumpopt(c, "insecure_locks");
496                         if (ep->e_flags & NFSEXP_NOACL)
497                                 c = dumpopt(c, "no_acl");
498                         if (ep->e_flags & NFSEXP_FSID)
499                                 c = dumpopt(c, "fsid=%d", ep->e_fsid);
500                         if (ep->e_uuid)
501                                 c = dumpopt(c, "fsid=%s", ep->e_uuid);
502                         if (ep->e_mountpoint)
503                                 c = dumpopt(c, "mountpoint%s%s", 
504                                             ep->e_mountpoint[0]?"=":"", 
505                                             ep->e_mountpoint);
506                         if (ep->e_anonuid != 65534)
507                                 c = dumpopt(c, "anonuid=%d", ep->e_anonuid);
508                         if (ep->e_anongid != 65534)
509                                 c = dumpopt(c, "anongid=%d", ep->e_anongid);
510                         switch(ep->e_fslocmethod) {
511                         case FSLOC_NONE:
512                                 break;
513                         case FSLOC_REFER:
514                                 c = dumpopt(c, "refer=%s", ep->e_fslocdata);
515                                 break;
516                         case FSLOC_REPLICA:
517                                 c = dumpopt(c, "replicas=%s", ep->e_fslocdata);
518                                 break;
519 #ifdef DEBUG
520                         case FSLOC_STUB:
521                                 c = dumpopt(c, "fsloc=stub");
522                                 break;
523 #endif
524                         }
525                         secinfo_show(stdout, ep);
526                         printf("%c\n", (c != '(')? ')' : ' ');
527                 }
528         }
529 }
530
531 static void
532 error(nfs_export *exp, int err)
533 {
534         fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, 
535                 exp->m_export.e_path, strerror(err));
536 }
537
538 static void
539 usage(void)
540 {
541         fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n");
542         exit(1);
543 }