]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/configfile.c
nfsidmap: Added Error Logging
[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 static int 
196 check_vers(char *mopt, char *field)
197 {
198         int i, found=0;
199
200         /*
201          * First check to see if the config setting is one 
202          * of the many version settings
203          */
204         for (i=0; versions[i]; i++) { 
205                 if (strcasestr(field, versions[i]) != NULL) {
206                         found++;
207                         break;
208                 }
209         }
210         if (!found)
211                 return 0;
212         /*
213          * It appears the version is being set, now see
214          * if the version appears on the command 
215          */
216         for (i=0; versions[i]; i++)  {
217                 if (strcasestr(mopt, versions[i]) != NULL)
218                         return 1;
219         }
220
221         return 0;
222 }
223
224 unsigned long config_default_vers;
225 unsigned long config_default_proto;
226 extern sa_family_t config_default_family;
227
228 /*
229  * Check to see if a default value is being set.
230  * If so, set the appropriate global value which will 
231  * be used as the initial value in the server negation.
232  */
233 static int 
234 default_value(char *mopt)
235 {
236         struct mount_options *options = NULL;
237         int dftlen = strlen("default");
238         char *field;
239
240         if (strncasecmp(mopt, "default", dftlen) != 0)
241                 return 0;
242
243         field = mopt + dftlen;
244         if (strncasecmp(field, "proto", strlen("proto")) == 0) {
245                 if ((options = po_split(field)) != NULL) {
246                         if (!nfs_nfs_protocol(options, &config_default_proto)) {
247                                 xlog_warn("Unable to set default protocol : %s", 
248                                         strerror(errno));
249                         }
250                         if (!nfs_nfs_proto_family(options, &config_default_family)) {
251                                 xlog_warn("Unable to set default family : %s", 
252                                         strerror(errno));
253                         }
254                 } else {
255                         xlog_warn("Unable to alloc memory for default protocol");
256                 }
257         } else if (strncasecmp(field, "vers", strlen("vers")) == 0) {
258                 if ((options = po_split(field)) != NULL) {
259                         if (!nfs_nfs_version(options, &config_default_vers)) {
260                                 xlog_warn("Unable to set default version: %s", 
261                                         strerror(errno));
262                                 
263                         }
264                 } else {
265                         xlog_warn("Unable to alloc memory for default version");
266                 }
267         } else 
268                 xlog_warn("Invalid default setting: '%s'", mopt);
269
270         if (options)
271                 po_destroy(options);
272
273         return 1;
274 }
275 /*
276  * Parse the given section of the configuration 
277  * file to if there are any mount options set.
278  * If so, added them to link list.
279  */
280 static void 
281 conf_parse_mntopts(char *section, char *arg, char *opts)
282 {
283         struct conf_list *list;
284         struct conf_list_node *node;
285         char buf[BUFSIZ], *value, *field;
286         char *nvalue, *ptr;
287         int argtype;
288
289         list = conf_get_tag_list(section);
290         TAILQ_FOREACH(node, &list->fields, link) {
291                 /*
292                  * Do not overwrite options if already exists 
293                  */
294                 snprintf(buf, BUFSIZ, "%s=", node->field);
295                 if (opts && strcasestr(opts, buf) != NULL)
296                         continue;
297                 /* 
298                  * Protocol verions can be set in a number of ways
299                  */
300                 if (opts && check_vers(opts, node->field))
301                         continue;
302
303                 if (lookup_entry(node->field) != NULL)
304                         continue;
305                 buf[0] = '\0';
306                 value = conf_get_section(section, arg, node->field);
307                 if (value == NULL)
308                         continue;
309                 field = mountopts_alias(node->field, &argtype);
310                 if (strcasecmp(value, "false") == 0) {
311                         if (argtype != MNT_NOARG)
312                                 snprintf(buf, BUFSIZ, "no%s", field);
313                 } else if (strcasecmp(value, "true") == 0) {
314                         snprintf(buf, BUFSIZ, "%s", field);
315                 } else {
316                         nvalue = strdup(value);
317                         ptr = mountopts_convert(nvalue);
318                         snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
319                         free(nvalue);
320                 }
321                 if (buf[0] == '\0')
322                         continue;
323                 /* 
324                  * Keep a running tally of the list size adding 
325                  * one for the ',' that will be appened later
326                  */
327                 list_size += strlen(buf) + 1;
328                 add_entry(buf);
329         }
330         conf_free_list(list);
331 }
332
333 /*
334  * Concatenate options from the configuration file with the 
335  * given options by building a link list of options from the
336  * different sections in the conf file. Options that exists 
337  * in the either the given options or link list are not 
338  * overwritten so it matter which when each section is
339  * parsed. 
340  */
341 char *conf_get_mntopts(char *spec, char *mount_point, 
342         char *mount_opts)
343 {
344         struct entry *entry;
345         char *ptr, *server, *config_opts;
346         int optlen = 0;
347
348         SLIST_INIT(&head);
349         list_size = 0;
350         /*
351          * First see if there are any mount options relative 
352          * to the mount point.
353          */
354         conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts);
355
356         /* 
357          * Next, see if there are any mount options relative
358          * to the server
359          */
360         server = strdup(spec);
361         if (server == NULL) {
362                 xlog_warn("conf_get_mountops: Unable calloc memory for server"); 
363                 free_all();
364                 return mount_opts;
365         }
366         if ((ptr = strchr(server, ':')) != NULL)
367                 *ptr='\0';
368         conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts);
369         free(server);
370
371         /*
372          * Finally process all the global mount options. 
373          */
374         conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
375
376         /*
377          * If no mount options were found in the configuration file
378          * just return what was passed in .
379          */
380         if (SLIST_EMPTY(&head))
381                 return mount_opts;
382
383         /*
384          * Found options in the configuration file. So
385          * concatenate the configuration options with the 
386          * options that were passed in
387          */
388         if (mount_opts)
389                 optlen = strlen(mount_opts);
390
391         /* list_size + optlen + ',' + '\0' */
392         config_opts = calloc(1, (list_size+optlen+2));
393         if (server == NULL) {
394                 xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); 
395                 free_all();
396                 return mount_opts;
397         }
398
399         if (mount_opts) {
400                 strcpy(config_opts, mount_opts);
401                 strcat(config_opts, ",");
402         }
403         SLIST_FOREACH(entry, &head, entries) {
404                 if (default_value(entry->opt))
405                         continue;
406                 strcat(config_opts, entry->opt);
407                 strcat(config_opts, ",");
408         }
409         if ((ptr = strrchr(config_opts, ',')) != NULL)
410                 *ptr = '\0';
411
412         free_all();
413         if (mount_opts)
414                 free(mount_opts);
415
416         return config_opts;
417 }