]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pnfsdsfile/pnfsdsfile.c
Merge commit '850ef5ae11d69ea3381bd310f564f025fc8caea3'
[FreeBSD/FreeBSD.git] / usr.sbin / pnfsdsfile / pnfsdsfile.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2017 Rick Macklem
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28
29 #include <sys/cdefs.h>
30 #include <err.h>
31 #include <getopt.h>
32 #include <netdb.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <sys/param.h>
38 #include <sys/extattr.h>
39 #include <sys/mount.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <fs/nfs/nfskpiport.h>
43 #include <fs/nfs/nfsproto.h>
44 #include <fs/nfs/nfs.h>
45 #include <fs/nfs/nfsrvstate.h>
46
47 static void usage(void) __dead2;
48
49 static struct option longopts[] = {
50         { "changeds",   required_argument,      NULL,   'c'     },
51         { "mirror",     required_argument,      NULL,   'm'     },
52         { "quiet",      no_argument,            NULL,   'q'     },
53         { "zerods",     required_argument,      NULL,   'r'     },
54         { "ds",         required_argument,      NULL,   's'     },
55         { "zerofh",     no_argument,            NULL,   'z'     },
56         { NULL,         0,                      NULL,   0       }
57 };
58
59 /*
60  * This program displays the location information of a data storage file
61  * for a given file on a MetaData Server (MDS) in a pNFS service.  This program
62  * must be run on the MDS and the file argument must be a file in a local
63  * file system that has been exported for the pNFS service.
64  */
65 int
66 main(int argc, char *argv[])
67 {
68         struct addrinfo *res, *ad, *newres;
69         struct sockaddr_in *sin, adsin;
70         struct sockaddr_in6 *sin6, adsin6;
71         char hostn[2 * NI_MAXHOST + 2], *cp;
72         struct pnfsdsfile dsfile[NFSDEV_MAXMIRRORS];
73         int ch, dosetxattr, i, mirrorcnt, mirrorit, quiet, zerods, zerofh;
74         in_port_t tport;
75         ssize_t xattrsize, xattrsize2;
76
77         zerods = 0;
78         zerofh = 0;
79         mirrorit = 0;
80         quiet = 0;
81         dosetxattr = 0;
82         res = NULL;
83         newres = NULL;
84         cp = NULL;
85         while ((ch = getopt_long(argc, argv, "c:m:qr:s:z", longopts, NULL)) !=
86             -1) {
87                 switch (ch) {
88                 case 'c':
89                         /* Replace the first DS server with the second one. */
90                         if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
91                             newres != NULL || res != NULL)
92                                 errx(1, "-c, -m, -r, -s and -z are mutually "
93                                     "exclusive and only can be used once");
94                         strlcpy(hostn, optarg, 2 * NI_MAXHOST + 2);
95                         cp = strchr(hostn, ',');
96                         if (cp == NULL)
97                                 errx(1, "Bad -c argument %s", hostn);
98                         *cp = '\0';
99                         if (getaddrinfo(hostn, NULL, NULL, &res) != 0)
100                                 errx(1, "Can't get IP# for %s", hostn);
101                         *cp++ = ',';
102                         if (getaddrinfo(cp, NULL, NULL, &newres) != 0)
103                                 errx(1, "Can't get IP# for %s", cp);
104                         break;
105                 case 'm':
106                         /* Add 0.0.0.0 entries up to mirror level. */
107                         if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
108                             newres != NULL || res != NULL)
109                                 errx(1, "-c, -m, -r, -s and -z are mutually "
110                                     "exclusive and only can be used once");
111                         mirrorit = atoi(optarg);
112                         if (mirrorit < 2 || mirrorit > NFSDEV_MAXMIRRORS)
113                                 errx(1, "-m %d out of range", mirrorit);
114                         break;
115                 case 'q':
116                         quiet = 1;
117                         break;
118                 case 'r':
119                         /* Reset the DS server in a mirror with 0.0.0.0. */
120                         if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
121                             newres != NULL || res != NULL)
122                                 errx(1, "-c, -m, -r, -s and -z are mutually "
123                                     "exclusive and only can be used once");
124                         zerods = 1;
125                         /* Translate the server name to an IP address. */
126                         if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
127                                 errx(1, "Can't get IP# for %s", optarg);
128                         break;
129                 case 's':
130                         /* Translate the server name to an IP address. */
131                         if (zerods != 0 || mirrorit != 0 || newres != NULL ||
132                             res != NULL)
133                                 errx(1, "-c, -m and -r are mutually exclusive "
134                                     "from use with -s and -z");
135                         if (getaddrinfo(optarg, NULL, NULL, &res) != 0)
136                                 errx(1, "Can't get IP# for %s", optarg);
137                         break;
138                 case 'z':
139                         if (zerofh != 0 || zerods != 0 || mirrorit != 0 ||
140                             newres != NULL)
141                                 errx(1, "-c, -m and -r are mutually exclusive "
142                                     "from use with -s and -z");
143                         zerofh = 1;
144                         break;
145                 default:
146                         usage();
147                 }
148         }
149         argc -= optind;
150         if (argc != 1)
151                 usage();
152         argv += optind;
153
154         /*
155          * The host address and directory where the data storage file is
156          * located is in the extended attribute "pnfsd.dsfile".
157          */
158         xattrsize = extattr_get_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
159             "pnfsd.dsfile", dsfile, sizeof(dsfile));
160         mirrorcnt = xattrsize / sizeof(struct pnfsdsfile);
161         xattrsize2 = mirrorcnt * sizeof(struct pnfsdsfile);
162         if (mirrorcnt < 1 || xattrsize != xattrsize2)
163                 err(1, "Can't get extattr pnfsd.dsfile for %s", *argv);
164
165         if (quiet == 0)
166                 printf("%s:\t", *argv);
167         for (i = 0; i < mirrorcnt; i++) {
168                 if (i > 0 && quiet == 0)
169                         printf("\t");
170                 /* Do the zerofh option. You must be root. */
171                 if (zerofh != 0) {
172                         if (geteuid() != 0)
173                                 errx(1, "Must be root/su to zerofh");
174         
175                         /*
176                          * Do it for the server specified by -s/--ds or all
177                          * servers, if -s/--ds was not specified.
178                          */
179                         sin = &dsfile[i].dsf_sin;
180                         sin6 = &dsfile[i].dsf_sin6;
181                         ad = res;
182                         while (ad != NULL) {
183                                 if (ad->ai_addr->sa_family == AF_INET &&
184                                     sin->sin_family == AF_INET &&
185                                     ad->ai_addrlen >= sizeof(adsin)) {
186                                         memcpy(&adsin, ad->ai_addr,
187                                             sizeof(adsin));
188                                         if (sin->sin_addr.s_addr ==
189                                             adsin.sin_addr.s_addr)
190                                                 break;
191                                 }
192                                 if (ad->ai_addr->sa_family == AF_INET6 &&
193                                     sin6->sin6_family == AF_INET6 &&
194                                     ad->ai_addrlen >= sizeof(adsin6)) {
195                                         memcpy(&adsin6, ad->ai_addr,
196                                             sizeof(adsin6));
197                                         if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
198                                             &adsin6.sin6_addr))
199                                                 break;
200                                 }
201                                 ad = ad->ai_next;
202                         }
203                         if (res == NULL || ad != NULL) {
204                                 memset(&dsfile[i].dsf_fh, 0, sizeof(fhandle_t));
205                                 dosetxattr = 1;
206                         }
207                 }
208         
209                 /* Do the zerods option. You must be root. */
210                 if (zerods != 0 && mirrorcnt > 1) {
211                         if (geteuid() != 0)
212                                 errx(1, "Must be root/su to zerods");
213         
214                         /*
215                          * Do it for the server specified.
216                          */
217                         sin = &dsfile[i].dsf_sin;
218                         sin6 = &dsfile[i].dsf_sin6;
219                         ad = res;
220                         while (ad != NULL) {
221                                 if (ad->ai_addr->sa_family == AF_INET &&
222                                     sin->sin_family == AF_INET &&
223                                     ad->ai_addrlen >= sizeof(adsin)) {
224                                         memcpy(&adsin, ad->ai_addr,
225                                             sizeof(adsin));
226                                         if (sin->sin_addr.s_addr ==
227                                             adsin.sin_addr.s_addr)
228                                                 break;
229                                 }
230                                 if (ad->ai_addr->sa_family == AF_INET6 &&
231                                     sin6->sin6_family == AF_INET6 &&
232                                     ad->ai_addrlen >= sizeof(adsin6)) {
233                                         memcpy(&adsin6, ad->ai_addr,
234                                             sizeof(adsin6));
235                                         if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
236                                             &adsin6.sin6_addr))
237                                                 break;
238                                 }
239                                 ad = ad->ai_next;
240                         }
241                         if (ad != NULL) {
242                                 sin->sin_family = AF_INET;
243                                 sin->sin_len = sizeof(*sin);
244                                 sin->sin_port = 0;
245                                 sin->sin_addr.s_addr = 0;
246                                 dosetxattr = 1;
247                         }
248                 }
249         
250                 /* Do the -c option to replace the DS host address. */
251                 if (newres != NULL) {
252                         if (geteuid() != 0)
253                                 errx(1, "Must be root/su to replace the host"
254                                     " addr");
255         
256                         /*
257                          * Check that the old host address matches.
258                          */
259                         sin = &dsfile[i].dsf_sin;
260                         sin6 = &dsfile[i].dsf_sin6;
261                         ad = res;
262                         while (ad != NULL) {
263                                 if (ad->ai_addr->sa_family == AF_INET &&
264                                     sin->sin_family == AF_INET &&
265                                     ad->ai_addrlen >= sizeof(adsin)) {
266                                         memcpy(&adsin, ad->ai_addr,
267                                             sizeof(adsin));
268                                         if (sin->sin_addr.s_addr ==
269                                             adsin.sin_addr.s_addr)
270                                                 break;
271                                 }
272                                 if (ad->ai_addr->sa_family == AF_INET6 &&
273                                     sin6->sin6_family == AF_INET6 &&
274                                     ad->ai_addrlen >= sizeof(adsin6)) {
275                                         memcpy(&adsin6, ad->ai_addr,
276                                             sizeof(adsin6));
277                                         if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
278                                             &adsin6.sin6_addr))
279                                                 break;
280                                 }
281                                 ad = ad->ai_next;
282                         }
283                         if (ad != NULL) {
284                                 if (sin->sin_family == AF_INET)
285                                         tport = sin->sin_port;
286                                 else
287                                         tport = sin6->sin6_port;
288                                 /*
289                                  * We have a match, so replace it with the first
290                                  * AF_INET or AF_INET6 address in the newres
291                                  * list.
292                                  */
293                                 while (newres->ai_addr->sa_family != AF_INET &&
294                                     newres->ai_addr->sa_family != AF_INET6) {
295                                         newres = newres->ai_next;
296                                         if (newres == NULL)
297                                                 errx(1, "Hostname %s has no"
298                                                     " IP#", cp);
299                                 }
300                                 if (newres->ai_addr->sa_family == AF_INET) {
301                                         memcpy(sin, newres->ai_addr,
302                                             sizeof(*sin));
303                                         sin->sin_port = tport;
304                                 } else if (newres->ai_addr->sa_family ==
305                                     AF_INET6) {
306                                         memcpy(sin6, newres->ai_addr,
307                                             sizeof(*sin6));
308                                         sin6->sin6_port = tport;
309                                 }
310                                 dosetxattr = 1;
311                         }
312                 }
313         
314                 if (quiet == 0) {
315                         /* Translate the IP address to a hostname. */
316                         if (getnameinfo((struct sockaddr *)&dsfile[i].dsf_sin,
317                             dsfile[i].dsf_sin.sin_len, hostn, sizeof(hostn),
318                             NULL, 0, 0) < 0)
319                                 err(1, "Can't get hostname");
320                         printf("%s\tds%d/%s", hostn, dsfile[i].dsf_dir,
321                             dsfile[i].dsf_filename);
322                 }
323         }
324         /* Add entrie(s) with IP address set to 0.0.0.0, as required. */
325         for (i = mirrorcnt; i < mirrorit; i++) {
326                 dsfile[i] = dsfile[0];
327                 dsfile[i].dsf_sin.sin_family = AF_INET;
328                 dsfile[i].dsf_sin.sin_len = sizeof(struct sockaddr_in);
329                 dsfile[i].dsf_sin.sin_addr.s_addr = 0;
330                 dsfile[i].dsf_sin.sin_port = 0;
331                 if (quiet == 0) {
332                         /* Print out the 0.0.0.0 entry. */
333                         printf("\t0.0.0.0\tds%d/%s", dsfile[i].dsf_dir,
334                             dsfile[i].dsf_filename);
335                 }
336         }
337         if (mirrorit > mirrorcnt) {
338                 xattrsize = mirrorit * sizeof(struct pnfsdsfile);
339                 dosetxattr = 1;
340         }
341         if (quiet == 0)
342                 printf("\n");
343
344         if (dosetxattr != 0 && extattr_set_file(*argv, EXTATTR_NAMESPACE_SYSTEM,
345             "pnfsd.dsfile", dsfile, xattrsize) != xattrsize)
346                 err(1, "Can't set pnfsd.dsfile");
347 }
348
349 static void
350 usage(void)
351 {
352
353         fprintf(stderr, "pnfsdsfile [-q/--quiet] [-z/--zerofh] "
354             "[-c/--changeds <old dshostname> <new dshostname>] "
355             "[-r/--zerods <dshostname>] "
356             "[-s/--ds <dshostname>] "
357             "<filename>\n");
358         exit(1);
359 }
360