diff options
Diffstat (limited to 'pfinet')
-rw-r--r-- | pfinet/options.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/pfinet/options.c b/pfinet/options.c new file mode 100644 index 00000000..b8c8ac77 --- /dev/null +++ b/pfinet/options.c @@ -0,0 +1,316 @@ +/* Pfinet option parsing + + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This file is part of the GNU Hurd. + + The GNU Hurd 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. + + The GNU Hurd 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdlib.h> +#include <string.h> +#include <hurd.h> +#include <argp.h> +#include <error.h> + +#include "pfinet.h" + +/* Pfinet options. Used for both startup and runtime. */ +static const struct argp_option options[] = { + {"interface", 'i', "DEVICE", 0, "Network interface to use", 1}, + {0,0,0,0,"These apply to a given interface:", 2}, + {"address", 'a', "ADDRESS", 0, "Set the network address"}, + {"netmask", 'm', "MASK", 0, "Set the netmask"}, + {"gateway", 'g', "ADDRESS", 0, "Set the default gateway"}, + {"shutdown", 's', 0, , 0, "Shut it down"} + {0} +}; + +static const char args_doc[] = 0; +static const char doc[] = "Interface-specific options before the first \ +interface specification apply to the first following interface; otherwise \ +they apply to the previously specified interface." + +/* Used to describe a particular interface during argument parsing. */ +struct parse_interface +{ + /* The network interface in question. *? + struct device *device; + + /* New values to apply to it. */ + unsigned long address, netmask, gateway; +}; + +/* Used to hold data during argument parsing. */ +struct parse_hook +{ + /* A list of specified interfaces and their corresponding options. */ + struct parse_interface *interfaces; + size_t num_interfaces; + + /* Interface to which options apply. If the device field isn't filled in + then it should be by the next --interface option. */ + struct parse_interface *curint; +}; + +/* Adds an empty interface slot to H, and sets H's current interface to it, or + returns an error. */ +static error_t +parse_hook_add_interface (struct parse_hook *h) +{ + struct parse_interface *new = realloc (h->interfaces, h->num_interfaces + 1); + if (! new) + return ENOMEM; + h->interfaces = new; + h->num_interfaces++; + h->curint = new + h->num_interfaces - 1; + h->curint->address = INADDR_NONE; + h->curint->netmask = INADDR_NONE; + h->curint->gateway = INADDR_NONE; + return 0; +} + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + error_t err = 0; + struct parse_hook *h = state->hook; + + /* Print a parsing error message and (if exiting is turned off) return the + error code ERR. */ +#define PERR(err, fmt, args...) \ + do { argp_error (state, fmt , ##args); return err; } while (0) + + /* Like PERR but for non-parsing errors. */ +#define FAIL(rerr, status, perr, fmt, args...) \ + do{ argp_failure (state, status, perr, fmt , ##args); return rerr; } while(0) + + /* Parse STR and return the corresponding internet address. If STR is not + a valid internet address, signal an error mentioned TYPE. */ +#define ADDR(str, type) \ + ({ unsigned long addr = inet_addr (str); \ + if (addr == INADDR_NONE) PERR (EINVAL, "Malformed %s", type); \ + addr; }) + + switch (opt) + { + struct parse_interface *in; + + case 'i': + /* An interface. */ + err = 0; + if (h->curint->device) + /* The current interface slot is not available. */ + { + /* First see if a previously specified one is being re-specified. */ + for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) + if (strcmp (in->device.pa_name, arg) == 0) + /* Re-use an old slot. */ + { + h->curint = in; + return 0; + } + + /* Add a new interface entry. */ + err = parse_hook_add_interface (h); + } + in = h->curint; + + if (! err) + err = find_device (arg, &in->device); + if (err) + FAIL (err, 10, err, "%s", arg); + + break; + + case 'a': + h->curint->address = ADDR (arg, "address"); + if (!IN_CLASSA (h->curint->address) + && !IN_CLASSB (h->curint->address) + && !IN_CLASSC (h->curint->address)) + { + if (IN_MULTICAST (h->curint->address)) + FAIL (EINVAL, 1, 0, + "%s: Cannot set interface address to multicast address", + arg); + else + FAIL (EINVAL, 1, 0, + "%s: Illegal or undefined network address", arg); + } + break; + case 'm': + h->curint->netmask = ADDR (arg, "netmask"); break; + case 'g': + h->curint->gateway = ADDR (arg, "gateway"); break; + + case ARGP_KEY_INIT: + /* Initialize our parsing state. */ + h = malloc (sizeof (struct parse_hook)); + if (! h) + FAIL (ENOMEM, 11, ENOMEM, "option parsing"); + + h->interfaces = 0; + h->num_interfaces = 0; + err = parse_hook_add_interface (h); + if (err) + FAIL (err, 12, err, "option parsing"); + + state->hook = h; + break; + + case ARGP_KEY_SUCCESS: + /* Check for problems. */ + if (! h->curint->device) + /* No interface specified; see if there's a single extant one. */ + { + err = find_device (0, &h->curint->device); + if (err) + FAIL (err, 13, 0, "No default interface"); + } + for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) + if (in->address == INADDR_NONE && in->netmask != INADDR_NONE + && in->device->pa_addr != 0) + /* Specifying a netmask for an address-less interface is a no-no. */ + FAIL (EDESTADDREQ, 14, 0, "Cannot set netmask"); + + /* Successfully finished parsing, return a result. */ + for (in = h->interfaces; in < h->interfaces + h->num_interfaces; in++) + { + struct device *dev = in->device; + if (in->address != INADDR_NONE || in->netmask != INADDR_NONE) + { + if (in->device->pa_addr != 0) + /* There's already an addres, delete it. */ + { + in->device->pa_addr = 0; + /* .... */ + } + + if (in->address != INADDR_NONE) + in->device->pa_addr = in->address; + + if (in->netmask != INADDR_NONE) + in->device->pa_mask = in->netmask; + else + { + if (IN_CLASSA (in->address)) + in->device->pa_mask = IN_CLASSA_NET; + else if (IN_CLASSB (in->address)) + in->device->pa_mask = IN_CLASSB_NET; + else if (IS_CLASS_C (in->address)) + in->device->pa_mask = IN_CLASSC_NET; + else + abort (); + } + + in->device->family = AF_INET; + in->device->pa_brdaddr = in->device->pa_addr | ~in->device->pa_mask; + + ip_rt_add (0, in->device->pa_addr & in->device->pa_mask, + in->device->pa_mask, 0, in->device, 0, 0); + } + if (in->gateway != INADDR_NONE) + ip_rt_add (RTF_GATEWAY, 0, 0, in->gateway, in->device, 0, 0); + } + /* Fall through to free hook. */ + + case ARGP_KEY_ERROR: + /* Parsing error occured, free everything. */ + free (h->interfaces); + free (h); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +struct argp +pfinet_argp = { options, parse_opt, args_doc, doc }; + +error_t +trivfs_S_fsys_set_options (struct trivfs_control *cntl, + mach_port_t reply, mach_msg_type_name_t reply_type, + char *data, mach_msg_type_number_t len, + int do_children) +{ + if (! cntl) + return EOPNOTSUPP; + else + { + int argc = argz_count (data, len); + char **argv = alloca (sizeof (char *) * (argc + 1)); + + argz_extract (data, len, argv); + + /* XXX should we should serialize requests here? */ + return + argp_parse (&pfinet_argp, argv, argc, + ARGP_NO_ERRS | ARGP_NO_HELP | ARGP_PARSE_ARGV0, + 0, 0); + } +} + +static error_t +get_opts (char **data, mach_msg_type_number_t *len) +{ + char *argz = 0; + size_t argz_len = 0; + + error_t add_dev_opts (struct device *dev) + { + + } + + err = enumerate_devices (add_dev_opts); + + if (! err) + /* Put ARGZ into vm_alloced memory for the return trip. */ + { + if (*len < argz_len) + err = vm_allocate (mach_task_self (), data, argz_len, 1); + if (! err) + { + if (argz_len) + bcopy (argz, *data, argz_len); + *len = argz_len; + } + } + + if (argz_len > 0) + free (argz); + + return err; +} + +error_t +trivfs_S_fsys_get_options (struct trivfs_control *fsys, + mach_port_t reply, mach_msg_type_name_t reply_type, + char **data, mach_msg_type_number_t *len) +{ + return fsys ? get_opts (data, len) : EOPNOTSUPP; +} + +error_t +trivfs_S_file_get_fs_options (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + char **data, mach_msg_type_number_t *len) +{ + return cred ? get_opts (data, len) : EOPNOTSUPP; +} |