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