]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/configfile.c
This is the second iteration of this patch. The only difference here
[nfs-utils.git] / utils / mount / configfile.c
1 /*
2  * configfile.c -- mount configuration file manipulation 
3  * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
4  *
5  * - Routines use to create mount options from the mount
6  *   configuration file.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * This program 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  */
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #include "xlog.h"
33 #include "mount.h"
34 #include "parse_opt.h"
35 #include "network.h"
36 #include "conffile.h"
37
38 #define KBYTES(x)     ((x) * (1024))
39 #define MEGABYTES(x)  ((x) * (1048576))
40 #define GIGABYTES(x)  ((x) * (1073741824))
41
42 #ifndef NFSMOUNT_GLOBAL_OPTS
43 #define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
44 #endif
45
46 #ifndef NFSMOUNT_MOUNTPOINT
47 #define NFSMOUNT_MOUNTPOINT "MountPoint"
48 #endif
49
50 #ifndef NFSMOUNT_SERVER
51 #define NFSMOUNT_SERVER "Server"
52 #endif
53
54 #ifndef MOUNTOPTS_CONFFILE
55 #define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
56 #endif
57 char *conf_path = MOUNTOPTS_CONFFILE;
58 enum {
59         MNT_NOARG=0,
60         MNT_INTARG,
61         MNT_STRARG,
62         MNT_SPEC,
63         MNT_UNSET
64 };
65 struct mnt_alias {
66         char *alias;
67         char *opt;
68         int  argtype;
69 } mnt_alias_tab[] = {
70         {"background", "bg", MNT_NOARG},
71         {"foreground", "fg", MNT_NOARG},
72         {"sloppy", "sloppy", MNT_NOARG},
73 };
74 int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
75
76 /*
77  * See if the option is an alias, if so return the 
78  * real mount option along with the argument type.
79  */
80 inline static 
81 char *mountopts_alias(char *opt, int *argtype)
82 {
83         int i;
84
85         *argtype = MNT_UNSET;
86         for (i=0; i < mnt_alias_sz; i++) {
87                 if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
88                         continue;
89                 *argtype = mnt_alias_tab[i].argtype;
90                 return mnt_alias_tab[i].opt;
91         }
92         /* Make option names case-insensitive */
93         upper2lower(opt);
94
95         return opt;
96 }
97 /*
98  * Convert numeric strings that end with 'k', 'm' or 'g'
99  * into numeric strings with the real value. 
100  * Meaning '8k' becomes '8094'.
101  */
102 char *mountopts_convert(char *value)
103 {
104         unsigned long long factor, num;
105         static char buf[64];
106         char *ch;
107
108         ch = &value[strlen(value)-1];
109         switch (tolower(*ch)) {
110         case 'k':
111                 factor = KBYTES(1);
112                 break;
113         case 'm':
114                 factor = MEGABYTES(1);
115                 break;
116         case 'g':
117                 factor = GIGABYTES(1);
118                 break;
119         default:
120                 return value;
121         }
122         *ch = '\0';
123         if (strncmp(value, "0x", 2) == 0) {
124                 num = strtol(value, (char **)NULL, 16);
125         } else if (strncmp(value, "0", 1) == 0) {
126                 num = strtol(value, (char **)NULL, 8);
127         } else {
128                 num = strtol(value, (char **)NULL, 10);
129         }
130         num *= factor;
131         snprintf(buf, 64, "%lld", num);
132
133         return buf;
134 }
135
136 struct entry {
137         SLIST_ENTRY(entry) entries;
138         char *opt;
139 };
140 static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head);
141 static int list_size;
142
143 /*
144  * Add option to the link list
145  */
146 inline static void 
147 add_entry(char *opt)
148 {
149         struct entry *entry;
150
151         entry = calloc(1, sizeof(struct entry));
152         if (entry == NULL) {
153                 xlog_warn("Unable calloc memory for mount configs"); 
154                 return;
155         }
156         entry->opt = strdup(opt);
157         if (entry->opt == NULL) {
158                 xlog_warn("Unable calloc memory for mount opts"); 
159                 free(entry);
160                 return;
161         }
162         SLIST_INSERT_HEAD(&head, entry, entries);
163 }
164 /*
165  * See if the given entry exists if the link list,
166  * if so return that entry
167  */
168 inline static 
169 char *lookup_entry(char *opt)
170 {
171         struct entry *entry;
172
173         SLIST_FOREACH(entry, &head, entries) {
174                 if (strcasecmp(entry->opt, opt) == 0)
175                         return opt;
176         }
177         return NULL;
178 }
179 /*
180  * Free all entries on the link list
181  */
182 inline static 
183 void free_all(void)
184 {
185         struct entry *entry;
186
187         while (!SLIST_EMPTY(&head)) {
188                 entry = SLIST_FIRST(&head);
189                 SLIST_REMOVE_HEAD(&head, entries);
190                 free(entry->opt);
191                 free(entry);
192         }
193 }
194 static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL};
195 int inline check_vers(char *mopt, char *field)
196 {
197         int i, found=0;
198
199         /*
200          * First check to see if the config setting is one 
201          * of the many version settings
202          */
203         for (i=0; versions[i]; i++) { 
204                 if (strcasestr(field, versions[i]) != NULL) {
205                         found++;
206                         break;
207                 }
208         }
209         if (!found)
210                 return 0;
211         /*
212          * It appears the version is being set, now see
213          * if the version appears on the command 
214          */
215         for (i=0; versions[i]; i++)  {
216                 if (strcasestr(mopt, versions[i]) != NULL)
217                         return 1;
218         }
219
220         return 0;
221 }
222
223 unsigned long config_default_vers;
224 unsigned long config_default_proto;
225 extern sa_family_t config_default_family;
226
227 /*
228  * Check to see if a default value is being set.
229  * If so, set the appropriate global value which will 
230  * be used as the initial value in the server negation.
231  */
232 int inline default_value(char *mopt)
233 {
234         struct mount_options *options = NULL;
235         int dftlen = strlen("default");
236         char *field;
237
238         if (strncasecmp(mopt, "default", dftlen) != 0)
239                 return 0;
240
241         field = mopt + dftlen;
242         if (strncasecmp(field, "proto", strlen("proto")) == 0) {
243                 if ((options = po_split(field)) != NULL) {
244                         if (!nfs_nfs_protocol(options, &config_default_proto)) {
245                                 xlog_warn("Unable to set default protocol : %s", 
246                                         strerror(errno));
247                         }
248                         if (!nfs_nfs_proto_family(options, &config_default_family)) {
249                                 xlog_warn("Unable to set default family : %s", 
250                                         strerror(errno));
251                         }
252                 } else {
253                         xlog_warn("Unable to alloc memory for default protocol");
254                 }
255         } else if (strncasecmp(field, "vers", strlen("vers")) == 0) {
256                 if ((options = po_split(field)) != NULL) {
257                         if (!nfs_nfs_version(options, &config_default_vers)) {
258                                 xlog_warn("Unable to set default version: %s", 
259                                         strerror(errno));
260                                 
261                         }
262                 } else {
263                         xlog_warn("Unable to alloc memory for default version");
264                 }
265         } else 
266                 xlog_warn("Invalid default setting: '%s'", mopt);
267
268         if (options)
269                 po_destroy(options);
270
271         return 1;
272 }
273 /*
274  * Parse the given section of the configuration 
275  * file to if there are any mount options set.
276  * If so, added them to link list.
277  */
278 static void 
279 conf_parse_mntopts(char *section, char *arg, char *opts)
280 {
281         struct conf_list *list;
282         struct conf_list_node *node;
283         char buf[BUFSIZ], *value, *field;
284         char *nvalue, *ptr;
285         int argtype;
286
287         list = conf_get_tag_list(section);
288         TAILQ_FOREACH(node, &list->fields, link) {
289                 /*
290                  * Do not overwrite options if already exists 
291                  */
292                 snprintf(buf, BUFSIZ, "%s=", node->field);
293                 if (opts && strcasestr(opts, buf) != NULL)
294                         continue;
295                 /* 
296                  * Protocol verions can be set in a number of ways
297                  */
298                 if (opts && check_vers(opts, node->field))
299                         continue;
300
301                 if (lookup_entry(node->field) != NULL)
302                         continue;
303                 buf[0] = '\0';
304                 value = conf_get_section(section, arg, node->field);
305                 if (value == NULL)
306                         continue;
307                 field = mountopts_alias(node->field, &argtype);
308                 if (strcasecmp(value, "false") == 0) {
309                         if (argtype != MNT_NOARG)
310                                 snprintf(buf, BUFSIZ, "no%s", field);
311                 } else if (strcasecmp(value, "true") == 0) {
312                         snprintf(buf, BUFSIZ, "%s", field);
313                 } else {
314                         nvalue = strdup(value);
315                         ptr = mountopts_convert(nvalue);
316                         snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
317                         free(nvalue);
318                 }
319                 if (buf[0] == '\0')
320                         continue;
321                 /* 
322                  * Keep a running tally of the list size adding 
323                  * one for the ',' that will be appened later
324                  */
325                 list_size += strlen(buf) + 1;
326                 add_entry(buf);
327         }
328         conf_free_list(list);
329 }
330
331 /*
332  * Concatenate options from the configuration file with the 
333  * given options by building a link list of options from the
334  * different sections in the conf file. Options that exists 
335  * in the either the given options or link list are not 
336  * overwritten so it matter which when each section is
337  * parsed. 
338  */
339 char *conf_get_mntopts(char *spec, char *mount_point, 
340         char *mount_opts)
341 {
342         struct entry *entry;
343         char *ptr, *server, *config_opts;
344         int optlen = 0;
345
346         SLIST_INIT(&head);
347         list_size = 0;
348         /*
349          * First see if there are any mount options relative 
350          * to the mount point.
351          */
352         conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts);
353
354         /* 
355          * Next, see if there are any mount options relative
356          * to the server
357          */
358         server = strdup(spec);
359         if (server == NULL) {
360                 xlog_warn("conf_get_mountops: Unable calloc memory for server"); 
361                 free_all();
362                 return mount_opts;
363         }
364         if ((ptr = strchr(server, ':')) != NULL)
365                 *ptr='\0';
366         conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts);
367         free(server);
368
369         /*
370          * Finally process all the global mount options. 
371          */
372         conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
373
374         /*
375          * If no mount options were found in the configuration file
376          * just return what was passed in .
377          */
378         if (SLIST_EMPTY(&head))
379                 return mount_opts;
380
381         /*
382          * Found options in the configuration file. So
383          * concatenate the configuration options with the 
384          * options that were passed in
385          */
386         if (mount_opts)
387                 optlen = strlen(mount_opts);
388
389         /* list_size + optlen + ',' + '\0' */
390         config_opts = calloc(1, (list_size+optlen+2));
391         if (server == NULL) {
392                 xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); 
393                 free_all();
394                 return mount_opts;
395         }
396
397         if (mount_opts) {
398                 strcpy(config_opts, mount_opts);
399                 strcat(config_opts, ",");
400         }
401         SLIST_FOREACH(entry, &head, entries) {
402                 if (default_value(entry->opt))
403                         continue;
404                 strcat(config_opts, entry->opt);
405                 strcat(config_opts, ",");
406         }
407         if ((ptr = strrchr(config_opts, ',')) != NULL)
408                 *ptr = '\0';
409
410         free_all();
411         if (mount_opts)
412                 free(mount_opts);
413
414         return config_opts;
415 }