summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pfinet/options.c316
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;
+}