/* Pfinet option parsing Copyright (C) 1996 Free Software Foundation, Inc. Written by Miles Bader 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 #include #include #include #include #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; }