]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mount/configfile.c
Now that only the Section names are case-insensitive
[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
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28
29 #include "xlog.h"
30 #include "conffile.h"
31
32 #define KBYTES(x)     ((x) * (1024))
33 #define MEGABYTES(x)  ((x) * (1048576))
34 #define GIGABYTES(x)  ((x) * (1073741824))
35
36 #ifndef NFSMOUNT_GLOBAL_OPTS
37 #define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
38 #endif
39
40 #ifndef NFSMOUNT_MOUNTPOINT
41 #define NFSMOUNT_MOUNTPOINT "MountPoint"
42 #endif
43
44 #ifndef NFSMOUNT_SERVER
45 #define NFSMOUNT_SERVER "Server"
46 #endif
47
48 #ifndef MOUNTOPTS_CONFFILE
49 #define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
50 #endif
51 char *conf_path = MOUNTOPTS_CONFFILE;
52 enum {
53         MNT_NOARG=0,
54         MNT_INTARG,
55         MNT_STRARG,
56         MNT_SPEC,
57         MNT_UNSET
58 };
59 struct mnt_alias {
60         char *alias;
61         char *opt;
62         int  argtype;
63 } mnt_alias_tab[] = {
64         {"background", "bg", MNT_NOARG},
65         {"foreground", "fg", MNT_NOARG},
66         {"sloppy", "sloppy", MNT_NOARG},
67 };
68 int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
69
70 /*
71  * See if the option is an alias, if so return the 
72  * real mount option along with the argument type.
73  */
74 inline static 
75 char *mountopts_alias(char *opt, int *argtype)
76 {
77         int i;
78
79         *argtype = MNT_UNSET;
80         for (i=0; i < mnt_alias_sz; i++) {
81                 if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
82                         continue;
83                 *argtype = mnt_alias_tab[i].argtype;
84                 return mnt_alias_tab[i].opt;
85         }
86         /* Make option names case-insensitive */
87         upper2lower(opt);
88
89         return opt;
90 }
91 /*
92  * Convert numeric strings that end with 'k', 'm' or 'g'
93  * into numeric strings with the real value. 
94  * Meaning '8k' becomes '8094'.
95  */
96 char *mountopts_convert(char *value)
97 {
98         unsigned long long factor, num;
99         static char buf[64];
100         char *ch;
101
102         ch = &value[strlen(value)-1];
103         switch (tolower(*ch)) {
104         case 'k':
105                 factor = KBYTES(1);
106                 break;
107         case 'm':
108                 factor = MEGABYTES(1);
109                 break;
110         case 'g':
111                 factor = GIGABYTES(1);
112                 break;
113         default:
114                 return value;
115         }
116         *ch = '\0';
117         if (strncmp(value, "0x", 2) == 0) {
118                 num = strtol(value, (char **)NULL, 16);
119         } else if (strncmp(value, "0", 1) == 0) {
120                 num = strtol(value, (char **)NULL, 8);
121         } else {
122                 num = strtol(value, (char **)NULL, 10);
123         }
124         num *= factor;
125         snprintf(buf, 64, "%lld", num);
126
127         return buf;
128 }
129
130 struct entry {
131         SLIST_ENTRY(entry) entries;
132         char *opt;
133 };
134 static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head);
135 static int list_size;
136
137 /*
138  * Add option to the link list
139  */
140 inline static void 
141 add_entry(char *opt)
142 {
143         struct entry *entry;
144
145         entry = calloc(1, sizeof(struct entry));
146         if (entry == NULL) {
147                 xlog_warn("Unable calloc memory for mount configs"); 
148                 return;
149         }
150         entry->opt = strdup(opt);
151         if (entry->opt == NULL) {
152                 xlog_warn("Unable calloc memory for mount opts"); 
153                 free(entry);
154                 return;
155         }
156         SLIST_INSERT_HEAD(&head, entry, entries);
157 }
158 /*
159  * See if the given entry exists if the link list,
160  * if so return that entry
161  */
162 inline static 
163 char *lookup_entry(char *opt)
164 {
165         struct entry *entry;
166
167         SLIST_FOREACH(entry, &head, entries) {
168                 if (strcasecmp(entry->opt, opt) == 0)
169                         return opt;
170         }
171         return NULL;
172 }
173 /*
174  * Free all entries on the link list
175  */
176 inline static 
177 void free_all(void)
178 {
179         struct entry *entry;
180
181         while (!SLIST_EMPTY(&head)) {
182                 entry = SLIST_FIRST(&head);
183                 SLIST_REMOVE_HEAD(&head, entries);
184                 free(entry->opt);
185                 free(entry);
186         }
187 }
188 /*
189  * Parse the given section of the configuration 
190  * file to if there are any mount options set.
191  * If so, added them to link list.
192  */
193 static void 
194 conf_parse_mntopts(char *section, char *arg, char *opts)
195 {
196         struct conf_list *list;
197         struct conf_list_node *node;
198         char buf[BUFSIZ], *value, *field;
199         char *nvalue, *ptr;
200         int argtype;
201
202         list = conf_get_tag_list(section);
203         TAILQ_FOREACH(node, &list->fields, link) {
204                 /*
205                  * Do not overwrite options if already exists 
206                  */
207                 snprintf(buf, BUFSIZ, "%s=", node->field);
208                 if (opts && strcasestr(opts, buf) != NULL)
209                         continue;
210                 if (lookup_entry(node->field) != NULL)
211                         continue;
212                 buf[0] = '\0';
213                 value = conf_get_section(section, arg, node->field);
214                 if (value == NULL)
215                         continue;
216                 field = mountopts_alias(node->field, &argtype);
217                 if (strcasecmp(value, "false") == 0) {
218                         if (argtype != MNT_NOARG)
219                                 snprintf(buf, BUFSIZ, "no%s", field);
220                 } else if (strcasecmp(value, "true") == 0) {
221                         snprintf(buf, BUFSIZ, "%s", field);
222                 } else {
223                         nvalue = strdup(value);
224                         ptr = mountopts_convert(nvalue);
225                         snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
226                         free(nvalue);
227                 }
228                 if (buf[0] == '\0')
229                         continue;
230                 /* 
231                  * Keep a running tally of the list size adding 
232                  * one for the ',' that will be appened later
233                  */
234                 list_size += strlen(buf) + 1;
235                 add_entry(buf);
236         }
237         conf_free_list(list);
238 }
239
240 /*
241  * Concatenate options from the configuration file with the 
242  * given options by building a link list of options from the
243  * different sections in the conf file. Options that exists 
244  * in the either the given options or link list are not 
245  * overwritten so it matter which when each section is
246  * parsed. 
247  */
248 char *conf_get_mntopts(char *spec, char *mount_point, 
249         char *mount_opts)
250 {
251         struct entry *entry;
252         char *ptr, *server, *config_opts;
253         int optlen = 0;
254
255         SLIST_INIT(&head);
256         list_size = 0;
257         /*
258          * First see if there are any mount options relative 
259          * to the mount point.
260          */
261         conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts);
262
263         /* 
264          * Next, see if there are any mount options relative
265          * to the server
266          */
267         server = strdup(spec);
268         if (server == NULL) {
269                 xlog_warn("conf_get_mountops: Unable calloc memory for server"); 
270                 free_all();
271                 return mount_opts;
272         }
273         if ((ptr = strchr(server, ':')) != NULL)
274                 *ptr='\0';
275         conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts);
276         free(server);
277
278         /*
279          * Finally process all the global mount options. 
280          */
281         conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts);
282
283         /*
284          * If no mount options were found in the configuration file
285          * just return what was passed in .
286          */
287         if (SLIST_EMPTY(&head))
288                 return mount_opts;
289
290         /*
291          * Found options in the configuration file. So
292          * concatenate the configuration options with the 
293          * options that were passed in
294          */
295         if (mount_opts)
296                 optlen = strlen(mount_opts);
297
298         /* list_size + optlen + ',' + '\0' */
299         config_opts = calloc(1, (list_size+optlen+2));
300         if (server == NULL) {
301                 xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); 
302                 free_all();
303                 return mount_opts;
304         }
305         if (mount_opts) {
306                 strcpy(config_opts, mount_opts);
307                 strcat(config_opts, ",");
308         }
309         SLIST_FOREACH(entry, &head, entries) {
310                 strcat(config_opts, entry->opt);
311                 strcat(config_opts, ",");
312         }
313         *(strrchr(config_opts, ',')) = '\0';
314
315         free_all();
316         if (mount_opts)
317                 free(mount_opts);
318
319         return config_opts;
320 }