/*
* Copyright (c) 1997-2014 Erez Zadok
* Copyright (c) 1990 Jan-Simon Pendry
* Copyright (c) 1990 Imperial College of Science, Technology & Medicine
* Copyright (c) 1990 The Regents of the University of California.
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry at Imperial College, London.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*
* File: am-utils/amd/conf.c
*
*/
/*
* Functions to handle the configuration file.
*/
/*
* Initialize a map from [global] defaults.
*/
static void
init_cf_map(cf_map_t *cfm)
{
if (!cfm)
return;
/*
* Initialize a regular map's flags and other variables from the
* global ones, so that they are applied to all maps. Of course, each map
* can then override the flags individually.
*
* NOTES:
* (1): Will only work for maps that appear after [global].
* (2): I'm assigning pointers directly from the global map.
*/
/* initialize map_type from [global] */
cfm->cfm_type = gopt.map_type;
/* initialize map_defaults from [global] */
cfm->cfm_defaults = gopt.map_defaults;
/* initialize map_opts from [global] */
cfm->cfm_opts = gopt.map_options;
/* initialize search_path from [global] */
cfm->cfm_search_path = gopt.search_path;
/*
* Initialize flags that are common both to [global] and a local map
* (that is, they could be inherited from the global section).
*/
cfm->cfm_flags = gopt.flags & (CFM_BROWSABLE_DIRS |
CFM_BROWSABLE_DIRS_FULL |
CFM_MOUNT_TYPE_AUTOFS |
CFM_SELECTORS_IN_DEFAULTS |
CFM_SUN_MAP_SYNTAX );
}
/*
* Process configuration file options (called from YACC parser).
* Return 0 if OK, 1 otherwise.
*/
int
set_conf_kv(const char *section, const char *key, const char *val)
{
int ret;
/*
* If global section, process kv pairs one at a time.
*/
if (STREQ(section, "global")) {
/*
* Check if a regular map was configured before "global",
* and warn about it.
*/
if (cur_map && cur_map->cfm_dir) {
static short printed_this_error;
if (!printed_this_error) {
fprintf(stderr, "found regular map \"%s\" before global one.\n",
cur_map->cfm_dir);
printed_this_error = 1;
}
}
/* process the global option first */
ret = process_global_option(key, val);
/* return status from the processing of the global option */
return ret;
}
/*
* Otherwise we found a non-global option: store it after some testing.
*/
/* initialize (static) global list head and current map pointer */
if (!head_map && !cur_map) {
cur_map = CALLOC(cf_map_t);
if (!cur_map) {
perror("calloc");
exit(1);
}
/* initialize first head map from global defaults */
init_cf_map(cur_map);
head_map = cur_map;
}
/* check if we found a new map, then allocate and initialize it */
if (cur_map->cfm_dir && !STREQ(cur_map->cfm_dir, section)) {
/* allocate new map struct */
cf_map_t *tmp_map = CALLOC(cf_map_t);
if (!tmp_map) {
perror("calloc");
exit(1);
}
/* initialize it from global defaults */
init_cf_map(tmp_map);
/* append it to end of linked list */
cur_map->cfm_next = tmp_map;
cur_map = tmp_map;
}
/* now process a single entry of a regular map */
return process_regular_option(section, key, val, cur_map);
}
/*
* Process global section of configuration file options.
* Return 0 upon success, 1 otherwise.
*/
static int
process_global_option(const char *key, const char *val)
{
struct _func_map *gfp;
/* ensure that val is valid */
if (!val || val[0] == '\0')
return 1;
/*
* search for global function.
*/
for (gfp = glob_functable; gfp->name; gfp++)
if (FSTREQ(gfp->name, key))
return (gfp->func)(val);
fprintf(stderr, "conf: unknown global key: \"%s\"\n", key);
return 1; /* failed to match any command */
}
static int
gopt_forced_unmounts(const char *val)
{
if (STREQ(val, "yes")) {
#if !defined(MNT2_GEN_OPT_DETACH) && !defined(MNT2_GEN_OPT_FORCE)
fprintf(stderr, "conf: forced_unmounts unsupported on this system.\n");
return 1;
#else /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */
# ifdef __linux__
/*
* HACK ALERT: Linux has had MNT_FORCE since 2.2, but it hasn't gotten
* stable until 2.4. And it had MNT_DETACH since 2.4, but it hasn't
* gotten stable since 2.6. So alert users if they're trying to use a
* feature that may not work well on their older kernel.
*/
{
struct utsname un;
if (uname(&un) >= 0) {
# ifdef MNT2_GEN_OPT_FORCE
if (strcmp(un.release, "2.4.0") < 0)
fprintf(stderr, "warning: forced-unmounts (MNT_FORCE) may not work well before 2.4.0\n");
# endif /* MNT2_GEN_OPT_FORCE */
# ifdef MNT2_GEN_OPT_DETACH
if (strcmp(un.release, "2.6.0") < 0)
fprintf(stderr, "warning: lazy-unmounts (MNT_DETACH) may not work well before 2.6.0\n");
# endif /* MNT2_GEN_OPT_DETACH */
}
}
# endif /* __linux__ */
gopt.flags |= CFM_FORCED_UNMOUNTS;
return 0;
#endif /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */
} else if (STREQ(val, "no")) {
gopt.flags &= ~CFM_FORCED_UNMOUNTS;
return 0;
}
fprintf(stderr, "conf: unknown value to unmount_on_exit \"%s\"\n", val);
return 1; /* unknown value */
}
static int
gopt_map_reload_interval(const char *val)
{
gopt.map_reload_interval = atoi(val);
if (gopt.map_reload_interval <= 0)
gopt.map_reload_interval = ONE_HOUR;
return 0;
}
static int
gopt_map_type(const char *val)
{
/* check if map type exist */
if (!mapc_type_exists(val)) {
fprintf(stderr, "conf: no such map type \"%s\"\n", val);
return 1;
}
gopt.map_type = xstrdup(val);
return 0;
}
static int
gopt_mount_type(const char *val)
{
if (STREQ(val, "autofs")) {
#ifdef HAVE_FS_AUTOFS
gopt.flags |= CFM_MOUNT_TYPE_AUTOFS;
amd_use_autofs++;
return 0;
#else /* not HAVE_FS_AUTOFS */
fprintf(stderr, "conf: no autofs support available\n");
return 1;
#endif /* not HAVE_FS_AUTOFS */
} else if (STREQ(val, "nfs")) {
gopt.flags &= ~CFM_MOUNT_TYPE_AUTOFS;
return 0;
}
fprintf(stderr, "conf: unknown value to mount_type \"%s\"\n", val);
return 1; /* unknown value */
}
static int
gopt_portmap_program(const char *val)
{
gopt.portmap_program = atol(val);
/*
* allow alternate program numbers to be no more than 10 offset from
* official amd program number (300019).
*/
if (gopt.portmap_program < AMQ_PROGRAM ||
gopt.portmap_program > AMQ_PROGRAM + 10) {
gopt.portmap_program = AMQ_PROGRAM;
set_amd_program_number(gopt.portmap_program);
fprintf(stderr, "conf: illegal amd program number \"%s\"\n", val);
return 1;
}
set_amd_program_number(gopt.portmap_program);
return 0; /* all is OK */
}
static int
gopt_preferred_amq_port(const char *val)
{
gopt.preferred_amq_port = atoi(val);
/*
* No need to check value: preferred_amq_port is an unsigned short and 0
* is a valid number, meaning "any port".
*/
return 0; /* all is OK */
}
static int
gopt_nfs_allow_any_interface(const char *val)
{
if (STREQ(val, "yes")) {
gopt.flags |= CFM_NFS_ANY_INTERFACE;
return 0;
} else if (STREQ(val, "no")) {
gopt.flags &= ~CFM_NFS_ANY_INTERFACE;
return 0;
}
fprintf(stderr, "conf: unknown value to nfs_allow_insecure_port \"%s\"\n", val);
return 1; /* unknown value */
}
static int
gopt_nfs_allow_insecure_port(const char *val)
{
if (STREQ(val, "yes")) {
gopt.flags |= CFM_NFS_INSECURE_PORT;
return 0;
} else if (STREQ(val, "no")) {
gopt.flags &= ~CFM_NFS_INSECURE_PORT;
return 0;
}
fprintf(stderr, "conf: unknown value to nfs_allow_insecure_port \"%s\"\n", val);
return 1; /* unknown value */
}
static int
ropt_map_type(const char *val, cf_map_t *cfm)
{
/* check if map type exist */
if (!mapc_type_exists(val)) {
fprintf(stderr, "conf: no such map type \"%s\"\n", val);
return 1;
}
cfm->cfm_type = xstrdup(val);
return 0;
}
static int
ropt_mount_type(const char *val, cf_map_t *cfm)
{
if (STREQ(val, "autofs")) {
#ifdef HAVE_FS_AUTOFS
cfm->cfm_flags |= CFM_MOUNT_TYPE_AUTOFS;
amd_use_autofs++;
return 0;
#else /* not HAVE_FS_AUTOFS */
fprintf(stderr, "conf: no autofs support available\n");
return 1;
#endif /* not HAVE_FS_AUTOFS */
} else if (STREQ(val, "nfs")) {
cfm->cfm_flags &= ~CFM_MOUNT_TYPE_AUTOFS;
return 0;
}
fprintf(stderr, "conf: unknown value to mount_type \"%s\"\n", val);
return 1; /* unknown value */
}
/*
* Process one collected map.
*/
static int
process_one_regular_map(const cf_map_t *cfm)
{
if (!cfm->cfm_name) {
fprintf(stderr, "conf: map_name must be defined for map \"%s\"\n", cfm->cfm_dir);
return 1;
}
/*
* If map has no tag defined, process the map.
* If no conf_tag was set in amd -T, process all untagged entries.
* If a tag is defined, then process it only if it matches the map tag.
*/
if (!cfm->cfm_tag ||
(conf_tag && STREQ(cfm->cfm_tag, conf_tag))) {
#ifdef DEBUG_CONF
fprintf(stderr, "processing map %s (flags=0x%x)...\n",
cfm->cfm_dir, cfm->cfm_flags);
#endif /* DEBUG_CONF */
root_newmap(cfm->cfm_dir,
cfm->cfm_opts ? cfm->cfm_opts : "",
cfm->cfm_name,
cfm);
} else {
fprintf(stderr, "skipping map %s...\n", cfm->cfm_dir);
}
return 0;
}
/*
* Process all regular maps in conf file (if any)
*/
int
process_all_regular_maps(void)
{
cf_map_t *tmp_map = head_map;
/*
* If the amd.conf file only has a [global] section (pretty useless
* IMHO), there's nothing to process
*/
if (!tmp_map)
return 0;
while (tmp_map) {
if (process_one_regular_map(tmp_map) != 0)
return 1;
tmp_map = tmp_map->cfm_next;
}
return 0;
}
/*
* Find a cf_map_t for a given map name.
* Return NULL if not found.
*/
cf_map_t *
find_cf_map(const char *name)
{
cf_map_t *tmp_map = head_map;
if (!tmp_map || !name)
return NULL;
while (tmp_map) {
if (STREQ(tmp_map->cfm_dir, name)) {
return tmp_map;
}
tmp_map = tmp_map->cfm_next;
}
return NULL;
}