| /* |
| * configfile.c -- mount configuration file manipulation |
| * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com> |
| * |
| * - Routines use to create mount options from the mount |
| * configuration file. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2, or (at your option) |
| * any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| |
| #include "xlog.h" |
| #include "mount.h" |
| #include "parse_opt.h" |
| #include "network.h" |
| #include "conffile.h" |
| |
| #define KBYTES(x) ((x) * (1024)) |
| #define MEGABYTES(x) ((x) * (1048576)) |
| #define GIGABYTES(x) ((x) * (1073741824)) |
| |
| #ifndef NFSMOUNT_GLOBAL_OPTS |
| #define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options" |
| #endif |
| |
| #ifndef NFSMOUNT_MOUNTPOINT |
| #define NFSMOUNT_MOUNTPOINT "MountPoint" |
| #endif |
| |
| #ifndef NFSMOUNT_SERVER |
| #define NFSMOUNT_SERVER "Server" |
| #endif |
| |
| #ifndef MOUNTOPTS_CONFFILE |
| #define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf" |
| #endif |
| char *conf_path = MOUNTOPTS_CONFFILE; |
| enum { |
| MNT_NOARG=0, |
| MNT_INTARG, |
| MNT_STRARG, |
| MNT_SPEC, |
| MNT_UNSET |
| }; |
| struct mnt_alias { |
| char *alias; |
| char *opt; |
| int argtype; |
| } mnt_alias_tab[] = { |
| {"background", "bg", MNT_NOARG}, |
| {"foreground", "fg", MNT_NOARG}, |
| {"sloppy", "sloppy", MNT_NOARG}, |
| }; |
| int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0])); |
| |
| /* |
| * See if the option is an alias, if so return the |
| * real mount option along with the argument type. |
| */ |
| inline static |
| char *mountopts_alias(char *opt, int *argtype) |
| { |
| int i; |
| |
| *argtype = MNT_UNSET; |
| for (i=0; i < mnt_alias_sz; i++) { |
| if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0) |
| continue; |
| *argtype = mnt_alias_tab[i].argtype; |
| return mnt_alias_tab[i].opt; |
| } |
| /* Make option names case-insensitive */ |
| upper2lower(opt); |
| |
| return opt; |
| } |
| /* |
| * Convert numeric strings that end with 'k', 'm' or 'g' |
| * into numeric strings with the real value. |
| * Meaning '8k' becomes '8094'. |
| */ |
| char *mountopts_convert(char *value) |
| { |
| unsigned long long factor, num; |
| static char buf[64]; |
| char *ch; |
| |
| ch = &value[strlen(value)-1]; |
| switch (tolower(*ch)) { |
| case 'k': |
| factor = KBYTES(1); |
| break; |
| case 'm': |
| factor = MEGABYTES(1); |
| break; |
| case 'g': |
| factor = GIGABYTES(1); |
| break; |
| default: |
| return value; |
| } |
| *ch = '\0'; |
| if (strncmp(value, "0x", 2) == 0) { |
| num = strtol(value, (char **)NULL, 16); |
| } else if (strncmp(value, "0", 1) == 0) { |
| num = strtol(value, (char **)NULL, 8); |
| } else { |
| num = strtol(value, (char **)NULL, 10); |
| } |
| num *= factor; |
| snprintf(buf, 64, "%lld", num); |
| |
| return buf; |
| } |
| |
| struct entry { |
| SLIST_ENTRY(entry) entries; |
| char *opt; |
| }; |
| static SLIST_HEAD(shead, entry) head = SLIST_HEAD_INITIALIZER(head); |
| static int list_size; |
| |
| /* |
| * Add option to the link list |
| */ |
| inline static void |
| add_entry(char *opt) |
| { |
| struct entry *entry; |
| |
| entry = calloc(1, sizeof(struct entry)); |
| if (entry == NULL) { |
| xlog_warn("Unable calloc memory for mount configs"); |
| return; |
| } |
| entry->opt = strdup(opt); |
| if (entry->opt == NULL) { |
| xlog_warn("Unable calloc memory for mount opts"); |
| free(entry); |
| return; |
| } |
| SLIST_INSERT_HEAD(&head, entry, entries); |
| } |
| /* |
| * See if the given entry exists if the link list, |
| * if so return that entry |
| */ |
| inline static |
| char *lookup_entry(char *opt) |
| { |
| struct entry *entry; |
| |
| SLIST_FOREACH(entry, &head, entries) { |
| if (strcasecmp(entry->opt, opt) == 0) |
| return opt; |
| } |
| return NULL; |
| } |
| /* |
| * Free all entries on the link list |
| */ |
| inline static |
| void free_all(void) |
| { |
| struct entry *entry; |
| |
| while (!SLIST_EMPTY(&head)) { |
| entry = SLIST_FIRST(&head); |
| SLIST_REMOVE_HEAD(&head, entries); |
| free(entry->opt); |
| free(entry); |
| } |
| } |
| static char *versions[] = {"v2", "v3", "v4", "vers", "nfsvers", NULL}; |
| int inline check_vers(char *mopt, char *field) |
| { |
| int i, found=0; |
| |
| /* |
| * First check to see if the config setting is one |
| * of the many version settings |
| */ |
| for (i=0; versions[i]; i++) { |
| if (strcasestr(field, versions[i]) != NULL) { |
| found++; |
| break; |
| } |
| } |
| if (!found) |
| return 0; |
| /* |
| * It appears the version is being set, now see |
| * if the version appears on the command |
| */ |
| for (i=0; versions[i]; i++) { |
| if (strcasestr(mopt, versions[i]) != NULL) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| unsigned long config_default_vers; |
| unsigned long config_default_proto; |
| extern sa_family_t config_default_family; |
| |
| /* |
| * Check to see if a default value is being set. |
| * If so, set the appropriate global value which will |
| * be used as the initial value in the server negation. |
| */ |
| int inline default_value(char *mopt) |
| { |
| struct mount_options *options = NULL; |
| int dftlen = strlen("default"); |
| char *field; |
| |
| if (strncasecmp(mopt, "default", dftlen) != 0) |
| return 0; |
| |
| field = mopt + dftlen; |
| if (strncasecmp(field, "proto", strlen("proto")) == 0) { |
| if ((options = po_split(field)) != NULL) { |
| if (!nfs_nfs_protocol(options, &config_default_proto)) { |
| xlog_warn("Unable to set default protocol : %s", |
| strerror(errno)); |
| } |
| if (!nfs_nfs_proto_family(options, &config_default_family)) { |
| xlog_warn("Unable to set default family : %s", |
| strerror(errno)); |
| } |
| } else { |
| xlog_warn("Unable to alloc memory for default protocol"); |
| } |
| } else if (strncasecmp(field, "vers", strlen("vers")) == 0) { |
| if ((options = po_split(field)) != NULL) { |
| if (!nfs_nfs_version(options, &config_default_vers)) { |
| xlog_warn("Unable to set default version: %s", |
| strerror(errno)); |
| |
| } |
| } else { |
| xlog_warn("Unable to alloc memory for default version"); |
| } |
| } else |
| xlog_warn("Invalid default setting: '%s'", mopt); |
| |
| if (options) |
| po_destroy(options); |
| |
| return 1; |
| } |
| /* |
| * Parse the given section of the configuration |
| * file to if there are any mount options set. |
| * If so, added them to link list. |
| */ |
| static void |
| conf_parse_mntopts(char *section, char *arg, char *opts) |
| { |
| struct conf_list *list; |
| struct conf_list_node *node; |
| char buf[BUFSIZ], *value, *field; |
| char *nvalue, *ptr; |
| int argtype; |
| |
| list = conf_get_tag_list(section); |
| TAILQ_FOREACH(node, &list->fields, link) { |
| /* |
| * Do not overwrite options if already exists |
| */ |
| snprintf(buf, BUFSIZ, "%s=", node->field); |
| if (opts && strcasestr(opts, buf) != NULL) |
| continue; |
| /* |
| * Protocol verions can be set in a number of ways |
| */ |
| if (opts && check_vers(opts, node->field)) |
| continue; |
| |
| if (lookup_entry(node->field) != NULL) |
| continue; |
| buf[0] = '\0'; |
| value = conf_get_section(section, arg, node->field); |
| if (value == NULL) |
| continue; |
| field = mountopts_alias(node->field, &argtype); |
| if (strcasecmp(value, "false") == 0) { |
| if (argtype != MNT_NOARG) |
| snprintf(buf, BUFSIZ, "no%s", field); |
| } else if (strcasecmp(value, "true") == 0) { |
| snprintf(buf, BUFSIZ, "%s", field); |
| } else { |
| nvalue = strdup(value); |
| ptr = mountopts_convert(nvalue); |
| snprintf(buf, BUFSIZ, "%s=%s", field, ptr); |
| free(nvalue); |
| } |
| if (buf[0] == '\0') |
| continue; |
| /* |
| * Keep a running tally of the list size adding |
| * one for the ',' that will be appened later |
| */ |
| list_size += strlen(buf) + 1; |
| add_entry(buf); |
| } |
| conf_free_list(list); |
| } |
| |
| /* |
| * Concatenate options from the configuration file with the |
| * given options by building a link list of options from the |
| * different sections in the conf file. Options that exists |
| * in the either the given options or link list are not |
| * overwritten so it matter which when each section is |
| * parsed. |
| */ |
| char *conf_get_mntopts(char *spec, char *mount_point, |
| char *mount_opts) |
| { |
| struct entry *entry; |
| char *ptr, *server, *config_opts; |
| int optlen = 0; |
| |
| SLIST_INIT(&head); |
| list_size = 0; |
| /* |
| * First see if there are any mount options relative |
| * to the mount point. |
| */ |
| conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, mount_opts); |
| |
| /* |
| * Next, see if there are any mount options relative |
| * to the server |
| */ |
| server = strdup(spec); |
| if (server == NULL) { |
| xlog_warn("conf_get_mountops: Unable calloc memory for server"); |
| free_all(); |
| return mount_opts; |
| } |
| if ((ptr = strchr(server, ':')) != NULL) |
| *ptr='\0'; |
| conf_parse_mntopts(NFSMOUNT_SERVER, server, mount_opts); |
| free(server); |
| |
| /* |
| * Finally process all the global mount options. |
| */ |
| conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, mount_opts); |
| |
| /* |
| * If no mount options were found in the configuration file |
| * just return what was passed in . |
| */ |
| if (SLIST_EMPTY(&head)) |
| return mount_opts; |
| |
| /* |
| * Found options in the configuration file. So |
| * concatenate the configuration options with the |
| * options that were passed in |
| */ |
| if (mount_opts) |
| optlen = strlen(mount_opts); |
| |
| /* list_size + optlen + ',' + '\0' */ |
| config_opts = calloc(1, (list_size+optlen+2)); |
| if (server == NULL) { |
| xlog_warn("conf_get_mountops: Unable calloc memory for config_opts"); |
| free_all(); |
| return mount_opts; |
| } |
| |
| if (mount_opts) { |
| strcpy(config_opts, mount_opts); |
| strcat(config_opts, ","); |
| } |
| SLIST_FOREACH(entry, &head, entries) { |
| if (default_value(entry->opt)) |
| continue; |
| strcat(config_opts, entry->opt); |
| strcat(config_opts, ","); |
| } |
| if ((ptr = strrchr(config_opts, ',')) != NULL) |
| *ptr = '\0'; |
| |
| free_all(); |
| if (mount_opts) |
| free(mount_opts); |
| |
| return config_opts; |
| } |