diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/ipxe/src/usr | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip | |
Diffstat (limited to 'roms/ipxe/src/usr')
| -rw-r--r-- | roms/ipxe/src/usr/autoboot.c | 587 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/dhcpmgmt.c | 46 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/fcmgmt.c | 116 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/ifmgmt.c | 289 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/imgmgmt.c | 166 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/imgtrust.c | 110 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/ipstat.c | 62 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/iwmgmt.c | 226 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/lotest.c | 275 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/neighmgmt.c | 56 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/nslookup.c | 203 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/pingmgmt.c | 84 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/profstat.c | 44 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/prompt.c | 66 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/pxemenu.c | 380 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/route.c | 44 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/route_ipv4.c | 58 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/route_ipv6.c | 60 | ||||
| -rw-r--r-- | roms/ipxe/src/usr/sync.c | 74 | 
19 files changed, 2946 insertions, 0 deletions
| diff --git a/roms/ipxe/src/usr/autoboot.c b/roms/ipxe/src/usr/autoboot.c new file mode 100644 index 00000000..47476ae4 --- /dev/null +++ b/roms/ipxe/src/usr/autoboot.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/netdevice.h> +#include <ipxe/dhcp.h> +#include <ipxe/settings.h> +#include <ipxe/image.h> +#include <ipxe/sanboot.h> +#include <ipxe/uri.h> +#include <ipxe/open.h> +#include <ipxe/init.h> +#include <ipxe/keys.h> +#include <ipxe/version.h> +#include <ipxe/shell.h> +#include <ipxe/features.h> +#include <ipxe/image.h> +#include <ipxe/timer.h> +#include <usr/ifmgmt.h> +#include <usr/route.h> +#include <usr/imgmgmt.h> +#include <usr/prompt.h> +#include <usr/autoboot.h> +#include <config/general.h> + +/** @file + * + * Automatic booting + * + */ + +/** Link-layer address of preferred autoboot device, if known */ +static uint8_t autoboot_ll_addr[MAX_LL_ADDR_LEN]; + +/** Device location of preferred autoboot device, if known */ +static struct device_description autoboot_desc; + +/** Autoboot device tester */ +static int ( * is_autoboot_device ) ( struct net_device *netdev ); + +/* Disambiguate the various error causes */ +#define ENOENT_BOOT __einfo_error ( EINFO_ENOENT_BOOT ) +#define EINFO_ENOENT_BOOT \ +	__einfo_uniqify ( EINFO_ENOENT, 0x01, "Nothing to boot" ) + +#define NORMAL	"\033[0m" +#define BOLD	"\033[1m" +#define CYAN	"\033[36m" + +/** The "scriptlet" setting */ +const struct setting scriptlet_setting __setting ( SETTING_MISC, scriptlet ) = { +	.name = "scriptlet", +	.description = "Boot scriptlet", +	.tag = DHCP_EB_SCRIPTLET, +	.type = &setting_type_string, +}; + +/** + * Perform PXE menu boot when PXE stack is not available + */ +__weak int pxe_menu_boot ( struct net_device *netdev __unused ) { +	return -ENOTSUP; +} + +/** + * Parse next-server and filename into a URI + * + * @v next_server	Next-server address + * @v filename		Filename + * @ret uri		URI, or NULL on failure + */ +static struct uri * parse_next_server_and_filename ( struct in_addr next_server, +						     const char *filename ) { +	struct uri *uri; + +	/* Parse filename */ +	uri = parse_uri ( filename ); +	if ( ! uri ) +		return NULL; + +	/* Construct a TFTP URI for the filename, if applicable */ +	if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) { +		uri_put ( uri ); +		uri = tftp_uri ( next_server, filename ); +		if ( ! uri ) +			return NULL; +	} + +	return uri; +} + +/** The "keep-san" setting */ +const struct setting keep_san_setting __setting ( SETTING_SANBOOT_EXTRA, +						  keep-san ) = { +	.name = "keep-san", +	.description = "Preserve SAN connection", +	.tag = DHCP_EB_KEEP_SAN, +	.type = &setting_type_int8, +}; + +/** The "skip-san-boot" setting */ +const struct setting skip_san_boot_setting __setting ( SETTING_SANBOOT_EXTRA, +						       skip-san-boot ) = { +	.name = "skip-san-boot", +	.description = "Do not boot from SAN device", +	.tag = DHCP_EB_SKIP_SAN_BOOT, +	.type = &setting_type_int8, +}; + +/** + * Boot from filename and root-path URIs + * + * @v filename		Filename + * @v root_path		Root path + * @v drive		SAN drive (if applicable) + * @v flags		Boot action flags + * @ret rc		Return status code + * + * The somewhat tortuous flow of control in this function exists in + * order to ensure that the "sanboot" command remains identical in + * function to a SAN boot via a DHCP-specified root path, and to + * provide backwards compatibility for the "keep-san" and + * "skip-san-boot" options. + */ +int uriboot ( struct uri *filename, struct uri *root_path, int drive, +	      unsigned int flags ) { +	struct image *image; +	int rc; + +	/* Hook SAN device, if applicable */ +	if ( root_path ) { +		if ( ( rc = san_hook ( root_path, drive ) ) != 0 ) { +			printf ( "Could not open SAN device: %s\n", +				 strerror ( rc ) ); +			goto err_san_hook; +		} +		printf ( "Registered SAN device %#02x\n", drive ); +	} + +	/* Describe SAN device, if applicable */ +	if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_DESCRIBE ) ) { +		if ( ( rc = san_describe ( drive ) ) != 0 ) { +			printf ( "Could not describe SAN device %#02x: %s\n", +				 drive, strerror ( rc ) ); +			goto err_san_describe; +		} +	} + +	/* Allow a root-path-only boot with skip-san enabled to succeed */ +	rc = 0; + +	/* Attempt filename boot if applicable */ +	if ( filename ) { +		if ( ( rc = imgdownload ( filename, 0, &image ) ) != 0 ) +			goto err_download; +		image->flags |= IMAGE_AUTO_UNREGISTER; +		if ( ( rc = image_exec ( image ) ) != 0 ) { +			printf ( "Could not boot image: %s\n", +				 strerror ( rc ) ); +			/* Fall through to (possibly) attempt a SAN boot +			 * as a fallback.  If no SAN boot is attempted, +			 * our status will become the return status. +			 */ +		} else { +			/* Always print an extra newline, because we +			 * don't know where the NBP may have left the +			 * cursor. +			 */ +			printf ( "\n" ); +		} +	} + +	/* Attempt SAN boot if applicable */ +	if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_BOOT ) ) { +		if ( fetch_intz_setting ( NULL, &skip_san_boot_setting) == 0 ) { +			printf ( "Booting from SAN device %#02x\n", drive ); +			rc = san_boot ( drive ); +			printf ( "Boot from SAN device %#02x failed: %s\n", +				 drive, strerror ( rc ) ); +		} else { +			printf ( "Skipping boot from SAN device %#02x\n", +				 drive ); +			/* Avoid overwriting a possible failure status +			 * from a filename boot. +			 */ +		} +	} + + err_download: + err_san_describe: +	/* Unhook SAN device, if applicable */ +	if ( ( drive >= 0 ) && ! ( flags & URIBOOT_NO_SAN_UNHOOK ) ) { +		if ( fetch_intz_setting ( NULL, &keep_san_setting ) == 0 ) { +			san_unhook ( drive ); +			printf ( "Unregistered SAN device %#02x\n", drive ); +		} else { +			printf ( "Preserving SAN device %#02x\n", drive ); +		} +	} + err_san_hook: +	return rc; +} + +/** + * Close all open net devices + * + * Called before a fresh boot attempt in order to free up memory.  We + * don't just close the device immediately after the boot fails, + * because there may still be TCP connections in the process of + * closing. + */ +static void close_all_netdevs ( void ) { +	struct net_device *netdev; + +	for_each_netdev ( netdev ) { +		ifclose ( netdev ); +	} +} + +/** + * Fetch next-server and filename settings into a URI + * + * @v settings		Settings block + * @ret uri		URI, or NULL on failure + */ +struct uri * fetch_next_server_and_filename ( struct settings *settings ) { +	struct in_addr next_server = { 0 }; +	char *raw_filename = NULL; +	struct uri *uri = NULL; +	char *filename; + +	/* If we have a filename, fetch it along with the next-server +	 * setting from the same settings block. +	 */ +	if ( fetch_setting ( settings, &filename_setting, &settings, +			     NULL, NULL, 0 ) >= 0 ) { +		fetch_string_setting_copy ( settings, &filename_setting, +					    &raw_filename ); +		fetch_ipv4_setting ( settings, &next_server_setting, +				     &next_server ); +	} + +	/* Expand filename setting */ +	filename = expand_settings ( raw_filename ? raw_filename : "" ); +	if ( ! filename ) +		goto err_expand; + +	/* Parse next server and filename */ +	if ( next_server.s_addr ) +		printf ( "Next server: %s\n", inet_ntoa ( next_server ) ); +	if ( filename[0] ) +		printf ( "Filename: %s\n", filename ); +	uri = parse_next_server_and_filename ( next_server, filename ); +	if ( ! uri ) +		goto err_parse; + + err_parse: +	free ( filename ); + err_expand: +	free ( raw_filename ); +	return uri; +} + +/** + * Fetch root-path setting into a URI + * + * @v settings		Settings block + * @ret uri		URI, or NULL on failure + */ +static struct uri * fetch_root_path ( struct settings *settings ) { +	struct uri *uri = NULL; +	char *raw_root_path; +	char *root_path; + +	/* Fetch root-path setting */ +	fetch_string_setting_copy ( settings, &root_path_setting, +				    &raw_root_path ); + +	/* Expand filename setting */ +	root_path = expand_settings ( raw_root_path ? raw_root_path : "" ); +	if ( ! root_path ) +		goto err_expand; + +	/* Parse root path */ +	if ( root_path[0] ) +		printf ( "Root path: %s\n", root_path ); +	uri = parse_uri ( root_path ); +	if ( ! uri ) +		goto err_parse; + + err_parse: +	free ( root_path ); + err_expand: +	free ( raw_root_path ); +	return uri; +} + +/** + * Check whether or not we have a usable PXE menu + * + * @ret have_menu	A usable PXE menu is present + */ +static int have_pxe_menu ( void ) { +	struct setting vendor_class_id_setting +		= { .tag = DHCP_VENDOR_CLASS_ID }; +	struct setting pxe_discovery_control_setting +		= { .tag = DHCP_PXE_DISCOVERY_CONTROL }; +	struct setting pxe_boot_menu_setting +		= { .tag = DHCP_PXE_BOOT_MENU }; +	char buf[ 10 /* "PXEClient" + NUL */ ]; +	unsigned int pxe_discovery_control; + +	fetch_string_setting ( NULL, &vendor_class_id_setting, +			       buf, sizeof ( buf ) ); +	pxe_discovery_control = +		fetch_uintz_setting ( NULL, &pxe_discovery_control_setting ); + +	return ( ( strcmp ( buf, "PXEClient" ) == 0 ) && +		 setting_exists ( NULL, &pxe_boot_menu_setting ) && +		 ( ! ( ( pxe_discovery_control & PXEBS_SKIP ) && +		       setting_exists ( NULL, &filename_setting ) ) ) ); +} + +/** + * Boot from a network device + * + * @v netdev		Network device + * @ret rc		Return status code + */ +int netboot ( struct net_device *netdev ) { +	struct uri *filename; +	struct uri *root_path; +	int rc; + +	/* Close all other network devices */ +	close_all_netdevs(); + +	/* Open device and display device status */ +	if ( ( rc = ifopen ( netdev ) ) != 0 ) +		goto err_ifopen; +	ifstat ( netdev ); + +	/* Configure device */ +	if ( ( rc = ifconf ( netdev, NULL ) ) != 0 ) +		goto err_dhcp; +	route(); + +	/* Try PXE menu boot, if applicable */ +	if ( have_pxe_menu() ) { +		printf ( "Booting from PXE menu\n" ); +		rc = pxe_menu_boot ( netdev ); +		goto err_pxe_menu_boot; +	} + +	/* Fetch next server and filename */ +	filename = fetch_next_server_and_filename ( NULL ); +	if ( ! filename ) +		goto err_filename; +	if ( ! uri_has_path ( filename ) ) { +		/* Ignore empty filename */ +		uri_put ( filename ); +		filename = NULL; +	} + +	/* Fetch root path */ +	root_path = fetch_root_path ( NULL ); +	if ( ! root_path ) +		goto err_root_path; +	if ( ! uri_is_absolute ( root_path ) ) { +		/* Ignore empty root path */ +		uri_put ( root_path ); +		root_path = NULL; +	} + +	/* If we have both a filename and a root path, ignore an +	 * unsupported URI scheme in the root path, since it may +	 * represent an NFS root. +	 */ +	if ( filename && root_path && +	     ( xfer_uri_opener ( root_path->scheme ) == NULL ) ) { +		printf ( "Ignoring unsupported root path\n" ); +		uri_put ( root_path ); +		root_path = NULL; +	} + +	/* Check that we have something to boot */ +	if ( ! ( filename || root_path ) ) { +		rc = -ENOENT_BOOT; +		printf ( "Nothing to boot: %s\n", strerror ( rc ) ); +		goto err_no_boot; +	} + +	/* Boot using next server, filename and root path */ +	if ( ( rc = uriboot ( filename, root_path, san_default_drive(), +			      ( root_path ? 0 : URIBOOT_NO_SAN ) ) ) != 0 ) +		goto err_uriboot; + + err_uriboot: + err_no_boot: +	uri_put ( root_path ); + err_root_path: +	uri_put ( filename ); + err_filename: + err_pxe_menu_boot: + err_dhcp: + err_ifopen: +	return rc; +} + +/** + * Test if network device matches the autoboot device bus type and location + * + * @v netdev		Network device + * @ret is_autoboot	Network device matches the autoboot device + */ +static int is_autoboot_busloc ( struct net_device *netdev ) { + +	return ( ( netdev->dev->desc.bus_type == autoboot_desc.bus_type ) && +		 ( netdev->dev->desc.location == autoboot_desc.location ) ); +} + +/** + * Identify autoboot device by bus type and location + * + * @v bus_type		Bus type + * @v location		Location + */ +void set_autoboot_busloc ( unsigned int bus_type, unsigned int location ) { + +	/* Record autoboot device description */ +	autoboot_desc.bus_type = bus_type; +	autoboot_desc.location = location; + +	/* Mark autoboot device as present */ +	is_autoboot_device = is_autoboot_busloc; +} + +/** + * Test if network device matches the autoboot device link-layer address + * + * @v netdev		Network device + * @ret is_autoboot	Network device matches the autoboot device + */ +static int is_autoboot_ll_addr ( struct net_device *netdev ) { + +	return ( memcmp ( netdev->ll_addr, autoboot_ll_addr, +			  netdev->ll_protocol->ll_addr_len ) == 0 ); +} + +/** + * Identify autoboot device by link-layer address + * + * @v ll_addr		Link-layer address + * @v len		Length of link-layer address + */ +void set_autoboot_ll_addr ( const void *ll_addr, size_t len ) { + +	/* Record autoboot link-layer address (truncated if necessary) */ +	if ( len > sizeof ( autoboot_ll_addr ) ) +		len = sizeof ( autoboot_ll_addr ); +	memcpy ( autoboot_ll_addr, ll_addr, len ); + +	/* Mark autoboot device as present */ +	is_autoboot_device = is_autoboot_ll_addr; +} + +/** + * Boot the system + */ +static int autoboot ( void ) { +	struct net_device *netdev; +	int rc = -ENODEV; + +	/* Try booting from each network device.  If we have a +	 * specified autoboot device location, then use only devices +	 * matching that location. +	 */ +	for_each_netdev ( netdev ) { + +		/* Skip any non-matching devices, if applicable */ +		if ( is_autoboot_device && ( ! is_autoboot_device ( netdev ) ) ) +			continue; + +		/* Attempt booting from this device */ +		rc = netboot ( netdev ); +	} + +	printf ( "No more network devices\n" ); +	return rc; +} + +/** + * Prompt for shell entry + * + * @ret	enter_shell	User wants to enter shell + */ +static int shell_banner ( void ) { + +	/* Skip prompt if timeout is zero */ +	if ( BANNER_TIMEOUT <= 0 ) +		return 0; + +	/* Prompt user */ +	printf ( "\n" ); +	return ( prompt ( "Press Ctrl-B for the iPXE command line...", +			  ( ( BANNER_TIMEOUT * TICKS_PER_SEC ) / 10 ), +			  CTRL_B ) == 0 ); +} + +/** + * Main iPXE flow of execution + * + * @v netdev		Network device, or NULL + */ +void ipxe ( struct net_device *netdev ) { +	struct feature *feature; +	struct image *image; +	char *scriptlet; + +	/* +	 * Print welcome banner +	 * +	 * +	 * If you wish to brand this build of iPXE, please do so by +	 * defining the string PRODUCT_NAME in config/general.h. +	 * +	 * While nothing in the GPL prevents you from removing all +	 * references to iPXE or http://ipxe.org, we prefer you not to +	 * do so. +	 * +	 */ +	printf ( NORMAL "\n\n%s\n" BOLD "iPXE %s" +		 NORMAL " -- Open Source Network Boot Firmware -- " +		 CYAN "http://ipxe.org" NORMAL "\n" +		 "Features:", product_name, product_version ); +	for_each_table_entry ( feature, FEATURES ) +		printf ( " %s", feature->name ); +	printf ( "\n" ); + +	/* Boot system */ +	if ( ( image = first_image() ) != NULL ) { +		/* We have an embedded image; execute it */ +		image_exec ( image ); +	} else if ( shell_banner() ) { +		/* User wants shell; just give them a shell */ +		shell(); +	} else { +		fetch_string_setting_copy ( NULL, &scriptlet_setting, +					    &scriptlet ); +		if ( scriptlet ) { +			/* User has defined a scriptlet; execute it */ +			system ( scriptlet ); +			free ( scriptlet ); +		} else { +			/* Try booting.  If booting fails, offer the +			 * user another chance to enter the shell. +			 */ +			if ( netdev ) { +				netboot ( netdev ); +			} else { +				autoboot(); +			} +			if ( shell_banner() ) +				shell(); +		} +	} +} diff --git a/roms/ipxe/src/usr/dhcpmgmt.c b/roms/ipxe/src/usr/dhcpmgmt.c new file mode 100644 index 00000000..23982b19 --- /dev/null +++ b/roms/ipxe/src/usr/dhcpmgmt.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/netdevice.h> +#include <ipxe/dhcp.h> +#include <ipxe/monojob.h> +#include <usr/ifmgmt.h> +#include <usr/dhcpmgmt.h> + +/** @file + * + * DHCP management + * + */ + +int pxebs ( struct net_device *netdev, unsigned int pxe_type ) { +	int rc; + +	/* Perform PXE Boot Server Discovery */ +	printf ( "PXEBS (%s type %d)", netdev->name, pxe_type ); +	if ( ( rc = start_pxebs ( &monojob, netdev, pxe_type ) ) == 0 ) +		rc = monojob_wait ( "", 0 ); + +	return rc; +} diff --git a/roms/ipxe/src/usr/fcmgmt.c b/roms/ipxe/src/usr/fcmgmt.c new file mode 100644 index 00000000..a30f37a7 --- /dev/null +++ b/roms/ipxe/src/usr/fcmgmt.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/fc.h> +#include <ipxe/fcels.h> +#include <ipxe/monojob.h> +#include <usr/fcmgmt.h> + +/** @file + * + * Fibre Channel management + * + */ + +/** + * Print status of Fibre Channel port + * + * @v port		Fibre Channel port + */ +void fcportstat ( struct fc_port *port ) { +	printf ( "%s: %s id %s", port->name, fc_ntoa ( &port->port_wwn ), +		 fc_id_ntoa ( &port->port_id ) ); +	printf ( " node %s\n  [Link:", fc_ntoa ( &port->node_wwn ) ); +	if ( fc_link_ok ( &port->link ) ) { +		printf ( " up, %s", fc_ntoa ( &port->link_port_wwn ) ); +		if ( ( port->flags & FC_PORT_HAS_FABRIC ) ) { +			printf ( " fabric" ); +		} else { +			printf ( " id %s", +				 fc_id_ntoa ( &port->ptp_link_port_id ) ); +		} +		printf ( " node %s]\n", fc_ntoa ( &port->link_node_wwn ) ); +	} else { +		printf ( " down: %s]\n", strerror ( port->link.rc ) ); +	} +} + +/** + * Print status of Fibre Channel peer + * + * @v peer		Fibre Channel peer + */ +void fcpeerstat ( struct fc_peer *peer ) { +	struct fc_ulp *ulp; +	uint8_t *param; +	unsigned int i; + +	printf ( "%s:\n  [Link:", fc_ntoa ( &peer->port_wwn ) ); +	if ( fc_link_ok ( &peer->link ) ) { +		printf ( " up, port %s id %s]\n", peer->port->name, +			 fc_id_ntoa ( &peer->port_id ) ); +	} else { +		printf ( " down: %s]\n", strerror ( peer->link.rc ) ); +	} + +	list_for_each_entry ( ulp, &peer->ulps, list ) { +		printf ( "  [Type %02x link:", ulp->type ); +		if ( fc_link_ok ( &ulp->link ) ) { +			printf ( " up, params" ); +			param = ulp->param; +			for ( i = 0 ; i < ulp->param_len ; i++ ) { +				printf ( "%c%02x", ( ( i == 0 ) ? ' ' : ':' ), +					 param[i] ); +			} +		} else { +			printf ( " down: %s", strerror ( ulp->link.rc ) ); +		} +		printf ( "]\n" ); +	} +} + +/** + * Issue Fibre Channel ELS + * + * @v port		Fibre Channel port + * @v peer_port_id	Peer port ID + * @v handler		ELS handler + * @ret rc		Return status code + */ +int fcels ( struct fc_port *port, struct fc_port_id *peer_port_id, +	    struct fc_els_handler *handler ) { +	int rc; + +	/* Initiate ELS */ +	printf ( "%s %s to %s...", +		 port->name, handler->name, fc_id_ntoa ( peer_port_id ) ); +	if ( ( rc = fc_els_request ( &monojob, port, peer_port_id, +				     handler ) ) != 0 ) { +		printf ( "%s\n", strerror ( rc ) ); +		return rc; +	} + +	/* Wait for ELS to complete */ +	return monojob_wait ( "", 0 ); +} diff --git a/roms/ipxe/src/usr/ifmgmt.c b/roms/ipxe/src/usr/ifmgmt.c new file mode 100644 index 00000000..3d05895c --- /dev/null +++ b/roms/ipxe/src/usr/ifmgmt.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <ipxe/console.h> +#include <ipxe/netdevice.h> +#include <ipxe/device.h> +#include <ipxe/job.h> +#include <ipxe/monojob.h> +#include <ipxe/timer.h> +#include <usr/ifmgmt.h> + +/** @file + * + * Network interface management + * + */ + +/** Default time to wait for link-up */ +#define LINK_WAIT_TIMEOUT ( 15 * TICKS_PER_SEC ) + +/** Default unsuccessful configuration status code */ +#define EADDRNOTAVAIL_CONFIG __einfo_error ( EINFO_EADDRNOTAVAIL_CONFIG ) +#define EINFO_EADDRNOTAVAIL_CONFIG					\ +	__einfo_uniqify ( EINFO_EADDRNOTAVAIL, 0x01,			\ +			  "No configuration methods succeeded" ) + +/** + * Open network device + * + * @v netdev		Network device + * @ret rc		Return status code + */ +int ifopen ( struct net_device *netdev ) { +	int rc; + +	if ( ( rc = netdev_open ( netdev ) ) != 0 ) { +		printf ( "Could not open %s: %s\n", +			 netdev->name, strerror ( rc ) ); +		return rc; +	} + +	return 0; +} + +/** + * Close network device + * + * @v netdev		Network device + */ +void ifclose ( struct net_device *netdev ) { +	netdev_close ( netdev ); +} + +/** + * Print network device error breakdown + * + * @v stats		Network device statistics + * @v prefix		Message prefix + */ +static void ifstat_errors ( struct net_device_stats *stats, +			    const char *prefix ) { +	unsigned int i; + +	for ( i = 0 ; i < ( sizeof ( stats->errors ) / +			    sizeof ( stats->errors[0] ) ) ; i++ ) { +		if ( stats->errors[i].count ) +			printf ( "  [%s: %d x \"%s\"]\n", prefix, +				 stats->errors[i].count, +				 strerror ( stats->errors[i].rc ) ); +	} +} + +/** + * Print status of network device + * + * @v netdev		Network device + */ +void ifstat ( struct net_device *netdev ) { +	printf ( "%s: %s using %s on %s (%s)\n" +		 "  [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n", +		 netdev->name, netdev_addr ( netdev ), +		 netdev->dev->driver_name, netdev->dev->name, +		 ( netdev_is_open ( netdev ) ? "open" : "closed" ), +		 ( netdev_link_ok ( netdev ) ? "up" : "down" ), +		 netdev->tx_stats.good, netdev->tx_stats.bad, +		 netdev->rx_stats.good, netdev->rx_stats.bad ); +	if ( ! netdev_link_ok ( netdev ) ) { +		printf ( "  [Link status: %s]\n", +			 strerror ( netdev->link_rc ) ); +	} +	ifstat_errors ( &netdev->tx_stats, "TXE" ); +	ifstat_errors ( &netdev->rx_stats, "RXE" ); +} + +/** Network device poller */ +struct ifpoller { +	/** Job control interface */ +	struct interface job; +	/** Network device */ +	struct net_device *netdev; +	/** Network device configurator (if applicable) */ +	struct net_device_configurator *configurator; +	/** +	 * Check progress +	 * +	 * @v ifpoller		Network device poller +	 * @ret ongoing_rc	Ongoing job status code (if known) +	 */ +	int ( * progress ) ( struct ifpoller *ifpoller ); +}; + +/** + * Report network device poller progress + * + * @v ifpoller		Network device poller + * @v progress		Progress report to fill in + * @ret ongoing_rc	Ongoing job status code (if known) + */ +static int ifpoller_progress ( struct ifpoller *ifpoller, +			       struct job_progress *progress __unused ) { + +	/* Hand off to current progress checker */ +	return ifpoller->progress ( ifpoller ); +} + +/** Network device poller operations */ +static struct interface_operation ifpoller_job_op[] = { +	INTF_OP ( job_progress, struct ifpoller *, ifpoller_progress ), +}; + +/** Network device poller descriptor */ +static struct interface_descriptor ifpoller_job_desc = +	INTF_DESC ( struct ifpoller, job, ifpoller_job_op ); + +/** + * Poll network device until completion + * + * @v netdev		Network device + * @v configurator	Network device configurator (if applicable) + * @v timeout		Timeout period, in ticks + * @v progress		Method to check progress + * @ret rc		Return status code + */ +static int ifpoller_wait ( struct net_device *netdev, +			   struct net_device_configurator *configurator, +			   unsigned long timeout, +			   int ( * progress ) ( struct ifpoller *ifpoller ) ) { +	static struct ifpoller ifpoller = { +		.job = INTF_INIT ( ifpoller_job_desc ), +	}; + +	ifpoller.netdev = netdev; +	ifpoller.configurator = configurator; +	ifpoller.progress = progress; +	intf_plug_plug ( &monojob, &ifpoller.job ); +	return monojob_wait ( "", timeout ); +} + +/** + * Check link-up progress + * + * @v ifpoller		Network device poller + * @ret ongoing_rc	Ongoing job status code (if known) + */ +static int iflinkwait_progress ( struct ifpoller *ifpoller ) { +	struct net_device *netdev = ifpoller->netdev; +	int ongoing_rc = netdev->link_rc; + +	/* Terminate successfully if link is up */ +	if ( ongoing_rc == 0 ) +		intf_close ( &ifpoller->job, 0 ); + +	/* Otherwise, report link status as ongoing job status */ +	return ongoing_rc; +} + +/** + * Wait for link-up, with status indication + * + * @v netdev		Network device + * @v timeout		Timeout period, in ticks + */ +int iflinkwait ( struct net_device *netdev, unsigned long timeout ) { +	int rc; + +	/* Ensure device is open */ +	if ( ( rc = ifopen ( netdev ) ) != 0 ) +		return rc; + +	/* Return immediately if link is already up */ +	netdev_poll ( netdev ); +	if ( netdev_link_ok ( netdev ) ) +		return 0; + +	/* Wait for link-up */ +	printf ( "Waiting for link-up on %s", netdev->name ); +	return ifpoller_wait ( netdev, NULL, timeout, iflinkwait_progress ); +} + +/** + * Check configuration progress + * + * @v ifpoller		Network device poller + * @ret ongoing_rc	Ongoing job status code (if known) + */ +static int ifconf_progress ( struct ifpoller *ifpoller ) { +	struct net_device *netdev = ifpoller->netdev; +	struct net_device_configurator *configurator = ifpoller->configurator; +	struct net_device_configuration *config; +	int rc; + +	/* Do nothing unless configuration has completed */ +	if ( netdev_configuration_in_progress ( netdev ) ) +		return 0; + +	/* Terminate with appropriate overall return status code */ +	if ( configurator ) { +		config = netdev_configuration ( netdev, configurator ); +		rc = config->rc; +	} else { +		rc = ( netdev_configuration_ok ( netdev ) ? +		       0 : -EADDRNOTAVAIL_CONFIG ); +	} +	intf_close ( &ifpoller->job, rc ); + +	return rc; +} + +/** + * Perform network device configuration + * + * @v netdev		Network device + * @v configurator	Network device configurator, or NULL to use all + * @ret rc		Return status code + */ +int ifconf ( struct net_device *netdev, +	     struct net_device_configurator *configurator ) { +	int rc; + +	/* Ensure device is open and link is up */ +	if ( ( rc = iflinkwait ( netdev, LINK_WAIT_TIMEOUT ) ) != 0 ) +		return rc; + +	/* Start configuration */ +	if ( configurator ) { +		if ( ( rc = netdev_configure ( netdev, configurator ) ) != 0 ) { +			printf ( "Could not configure %s via %s: %s\n", +				 netdev->name, configurator->name, +				 strerror ( rc ) ); +			return rc; +		} +	} else { +		if ( ( rc = netdev_configure_all ( netdev ) ) != 0 ) { +			printf ( "Could not configure %s: %s\n", +				 netdev->name, strerror ( rc ) ); +			return rc; +		} +	} + +	/* Wait for configuration to complete */ +	printf ( "Configuring %s%s%s(%s %s)", +		 ( configurator ? "[" : "" ), +		 ( configurator ? configurator->name : "" ), +		 ( configurator ? "] " : "" ), +		 netdev->name, netdev->ll_protocol->ntoa ( netdev->ll_addr ) ); +	return ifpoller_wait ( netdev, configurator, 0, ifconf_progress ); +} diff --git a/roms/ipxe/src/usr/imgmgmt.c b/roms/ipxe/src/usr/imgmgmt.c new file mode 100644 index 00000000..c9c57164 --- /dev/null +++ b/roms/ipxe/src/usr/imgmgmt.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/image.h> +#include <ipxe/downloader.h> +#include <ipxe/monojob.h> +#include <ipxe/open.h> +#include <ipxe/uri.h> +#include <usr/imgmgmt.h> + +/** @file + * + * Image management + * + */ + +/** + * Download a new image + * + * @v uri		URI + * @v timeout		Download timeout + * @v image		Image to fill in + * @ret rc		Return status code + */ +int imgdownload ( struct uri *uri, unsigned long timeout, +		  struct image **image ) { +	const char *password; +	char *uri_string_redacted; +	int rc; + +	/* Construct redacted URI */ +	password = uri->password; +	if ( password ) +		uri->password = "***"; +	uri_string_redacted = format_uri_alloc ( uri ); +	uri->password = password; +	if ( ! uri_string_redacted ) { +		rc = -ENOMEM; +		goto err_uri_string; +	} + +	/* Resolve URI */ +	uri = resolve_uri ( cwuri, uri ); +	if ( ! uri ) { +		rc = -ENOMEM; +		goto err_resolve_uri; +	} + +	/* Allocate image */ +	*image = alloc_image ( uri ); +	if ( ! *image ) { +		rc = -ENOMEM; +		goto err_alloc_image; +	} + +	/* Create downloader */ +	if ( ( rc = create_downloader ( &monojob, *image ) ) != 0 ) { +		printf ( "Could not start download: %s\n", strerror ( rc ) ); +		goto err_create_downloader; +	} + +	/* Wait for download to complete */ +	if ( ( rc = monojob_wait ( uri_string_redacted, timeout ) ) != 0 ) +		goto err_monojob_wait; + +	/* Register image */ +	if ( ( rc = register_image ( *image ) ) != 0 ) { +		printf ( "Could not register image: %s\n", strerror ( rc ) ); +		goto err_register_image; +	} + + err_register_image: + err_monojob_wait: + err_create_downloader: +	image_put ( *image ); + err_alloc_image: +	uri_put ( uri ); + err_resolve_uri: +	free ( uri_string_redacted ); + err_uri_string: +	return rc; +} + +/** + * Download a new image + * + * @v uri_string	URI string + * @v timeout		Download timeout + * @v image		Image to fill in + * @ret rc		Return status code + */ +int imgdownload_string ( const char *uri_string, unsigned long timeout, +			 struct image **image ) { +	struct uri *uri; +	int rc; + +	if ( ! ( uri = parse_uri ( uri_string ) ) ) +		return -ENOMEM; + +	rc = imgdownload ( uri, timeout, image ); + +	uri_put ( uri ); +	return rc; +} + +/** + * Acquire an image + * + * @v name_uri		Name or URI string + * @v timeout		Download timeout + * @v image		Image to fill in + * @ret rc		Return status code + */ +int imgacquire ( const char *name_uri, unsigned long timeout, +		 struct image **image ) { + +	/* If we already have an image with the specified name, use it */ +	*image = find_image ( name_uri ); +	if ( *image ) +		return 0; + +	/* Otherwise, download a new image */ +	return imgdownload_string ( name_uri, timeout, image ); +} + +/** + * Display status of an image + * + * @v image		Executable/loadable image + */ +void imgstat ( struct image *image ) { +	printf ( "%s : %zd bytes", image->name, image->len ); +	if ( image->type ) +		printf ( " [%s]", image->type->name ); +	if ( image->flags & IMAGE_TRUSTED ) +		printf ( " [TRUSTED]" ); +	if ( image->flags & IMAGE_SELECTED ) +		printf ( " [SELECTED]" ); +	if ( image->flags & IMAGE_AUTO_UNREGISTER ) +		printf ( " [AUTOFREE]" ); +	if ( image->cmdline ) +		printf ( " \"%s\"", image->cmdline ); +	printf ( "\n" ); +} diff --git a/roms/ipxe/src/usr/imgtrust.c b/roms/ipxe/src/usr/imgtrust.c new file mode 100644 index 00000000..da7ff2ef --- /dev/null +++ b/roms/ipxe/src/usr/imgtrust.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <syslog.h> +#include <ipxe/uaccess.h> +#include <ipxe/image.h> +#include <ipxe/cms.h> +#include <ipxe/validator.h> +#include <ipxe/monojob.h> +#include <usr/imgtrust.h> + +/** @file + * + * Image trust management + * + */ + +/** + * Verify image using downloaded signature + * + * @v image		Image to verify + * @v signature		Image containing signature + * @v name		Required common name, or NULL to allow any name + * @ret rc		Return status code + */ +int imgverify ( struct image *image, struct image *signature, +		const char *name ) { +	size_t len; +	void *data; +	struct cms_signature *sig; +	struct cms_signer_info *info; +	time_t now; +	int rc; + +	/* Mark image as untrusted */ +	image_untrust ( image ); + +	/* Copy signature to internal memory */ +	len = signature->len; +	data = malloc ( len ); +	if ( ! data ) { +		rc = -ENOMEM; +		goto err_alloc; +	} +	copy_from_user ( data, signature->data, 0, len ); + +	/* Parse signature */ +	if ( ( rc = cms_signature ( data, len, &sig ) ) != 0 ) +		goto err_parse; + +	/* Free internal copy of signature */ +	free ( data ); +	data = NULL; + +	/* Complete all certificate chains */ +	list_for_each_entry ( info, &sig->info, list ) { +		if ( ( rc = create_validator ( &monojob, info->chain ) ) != 0 ) +			goto err_create_validator; +		if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 ) +			goto err_validator_wait; +	} + +	/* Use signature to verify image */ +	now = time ( NULL ); +	if ( ( rc = cms_verify ( sig, image->data, image->len, +				 name, now, NULL, NULL ) ) != 0 ) +		goto err_verify; + +	/* Drop reference to signature */ +	cms_put ( sig ); +	sig = NULL; + +	/* Mark image as trusted */ +	image_trust ( image ); +	syslog ( LOG_NOTICE, "Image \"%s\" signature OK\n", image->name ); + +	return 0; + + err_verify: + err_validator_wait: + err_create_validator: +	cms_put ( sig ); + err_parse: +	free ( data ); + err_alloc: +	syslog ( LOG_ERR, "Image \"%s\" signature bad: %s\n", +		 image->name, strerror ( rc ) ); +	return rc; +} diff --git a/roms/ipxe/src/usr/ipstat.c b/roms/ipxe/src/usr/ipstat.c new file mode 100644 index 00000000..95ad799d --- /dev/null +++ b/roms/ipxe/src/usr/ipstat.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <ipxe/ipstat.h> +#include <usr/ipstat.h> + +/** @file + * + * IP statistics + * + */ + +/** + * Print IP statistics + * + */ +void ipstat ( void ) { +	struct ip_statistics_family *family; +	struct ip_statistics *stats; + +	for_each_table_entry ( family, IP_STATISTICS_FAMILIES ) { +		stats = family->stats; +		printf ( "IP version %d:\n", family->version ); +		printf ( "  InReceives:%ld InMcastPkts:%ld InBcastPkts:%ld " +			 "InOctets:%ld\n", stats->in_receives, +			 stats->in_mcast_pkts, stats->in_bcast_pkts, +			 stats->in_octets ); +		printf ( "  InHdrErrors:%ld InAddrErrors:%ld " +			 "InUnknownProtos:%ld InTruncatedPkts:%ld\n", +			 stats->in_hdr_errors, stats->in_addr_errors, +			 stats->in_unknown_protos, stats->in_truncated_pkts ); +		printf ( "  ReasmReqds:%ld ReasmOKs:%ld ReasmFails:%ld\n", +			 stats->reasm_reqds, stats->reasm_oks, +			 stats->reasm_fails ); +		printf ( "  InDelivers:%ld OutRequests:%ld OutNoRoutes:%ld\n", +			 stats->in_delivers, stats->out_requests, +			 stats->out_no_routes ); +		printf ( "  OutTransmits:%ld OutMcastPkts:%ld OutBcastPkts:%ld " +			 "OutOctets:%ld\n", stats->out_transmits, +			 stats->out_mcast_pkts, stats->out_bcast_pkts, +			 stats->out_octets ); +	} +} diff --git a/roms/ipxe/src/usr/iwmgmt.c b/roms/ipxe/src/usr/iwmgmt.c new file mode 100644 index 00000000..a486bceb --- /dev/null +++ b/roms/ipxe/src/usr/iwmgmt.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2009 Joshua Oreman <oremanj@rwcr.net>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ipxe/net80211.h> +#include <ipxe/ethernet.h> +#include <usr/ifmgmt.h> +#include <usr/iwmgmt.h> + +/** @file + * + * Wireless network interface management + * + */ + +/** + * Print status of 802.11 device + * + * @v dev	802.11 device + */ +void iwstat ( struct net80211_device *dev ) { + +	ifstat ( dev->netdev ); + +	printf ( "  [802.11 "); +	if ( dev->state & NET80211_ASSOCIATED ) { +		printf ( "SSID '%s', ", dev->essid ); +	} else { +		printf ( "not associated, " ); +	} +	if ( dev->channel < dev->nr_channels && dev->rate < dev->nr_rates ) { +		printf ( "Ch:%d Sig:%d", dev->channels[dev->channel].channel_nr, +			 dev->last_signal ); +		switch ( dev->hw->signal_type ) { +		case NET80211_SIGNAL_NONE: +			printf ( "?" ); +			break; +		case NET80211_SIGNAL_ARBITRARY: +			printf ( "/%d", dev->hw->signal_max ); +			break; +		case NET80211_SIGNAL_DB: +			printf ( "/%d dB", dev->hw->signal_max ); +			break; +		case NET80211_SIGNAL_DBM: +			printf ( " dBm" ); +			break; +		} +		printf ( ", Qual:%d%% Rate:%d Mbps]\n", +			 ( dev->rx_beacon_interval == 0 ? 0 : +			   100 * dev->tx_beacon_interval / +			   dev->rx_beacon_interval ), +			 dev->rates[dev->rate] / 10 ); +	} else { +		printf ( "antenna off]\n" ); +	} + +	if ( dev->state & NET80211_WORKING ) { +		printf ( "  [associating" ); +		if ( dev->associating ) +			printf ( " to '%s'", dev->associating->essid ); +		printf ( "...]\n" ); +	} +} + +/** Identifiers for 802.11 cryptography types, indexed by type number */ +static const char *crypto_types[] = { +	[NET80211_CRYPT_NONE] = "Open", +	[NET80211_CRYPT_WEP] = "WEP ", +	[NET80211_CRYPT_TKIP] = "WPA ", +	[NET80211_CRYPT_CCMP] = "WPA2", +	[NET80211_CRYPT_UNKNOWN] = "UNK ", +}; + +/** Number of 802.11 cryptography types defined */ +#define NR_CRYPTO_TYPES ( sizeof ( crypto_types ) / sizeof ( crypto_types[0] ) ) + +/** Identifiers for 802.11 authentication types, indexed by type number */ +static const char *auth_types[] = { +	[NET80211_SECPROT_NONE] = "", +	[NET80211_SECPROT_PSK] = "PSK", +	[NET80211_SECPROT_EAP] = "802.1X", +	[NET80211_SECPROT_UNKNOWN] = "UNK", +}; + +/** Number of 802.11 authentication types defined */ +#define NR_AUTH_TYPES ( sizeof ( auth_types ) / sizeof ( auth_types[0] ) ) + +/** + * Scan for wireless networks using 802.11 device + * + * @v dev	802.11 device + * @v active	Whether to use active scanning + * + * The list of networks found will be printed in tabular format. + * + * This function is safe to call at all times, whether the 802.11 + * device is open or not, but if called while the auto-association + * task is running it will return an error indication. + */ +int iwlist ( struct net80211_device *dev ) { +	struct net80211_probe_ctx *ctx; +	struct list_head *networks; +	struct net80211_wlan *wlan; +	char ssid_buf[22]; +	int rc; +	unsigned i; +	int was_opened = netdev_is_open ( dev->netdev ); +	int was_channel = dev->channels[dev->channel].channel_nr; + +	if ( ! was_opened ) { +		dev->state |= NET80211_NO_ASSOC; +		rc = netdev_open ( dev->netdev ); +		if ( rc < 0 ) +			goto err; +	} + +	if ( dev->state & NET80211_WORKING ) { +		rc = -EINVAL; +		goto err_close_netdev; +	} + +	if ( ! was_opened ) { +		rc = net80211_prepare_probe ( dev, dev->hw->bands, 0 ); +		if ( rc < 0 ) +			goto err_close_netdev; +	} + +	ctx = net80211_probe_start ( dev, "", 0 ); +	if ( ! ctx ) { +		rc = -ENOMEM; +		goto err_close_netdev; +	} + +	while ( ! ( rc = net80211_probe_step ( ctx ) ) ) { +		step(); +	} + +	networks = net80211_probe_finish_all ( ctx ); + +	if ( list_empty ( networks ) ) { +		goto err_free_networks; +	} + +	rc = 0; + +	printf ( "Networks on %s:\n\n", dev->netdev->name ); + +	/* Output format: +	 * 0         1         2         3         4         5         6 +	 * 0123456789012345678901234567890123456789012345678901234567890 +	 * [Sig] SSID                  BSSID              Ch  Crypt/Auth +	 * ------------------------------------------------------------- +	 * [ 15] abcdefghijklmnopqrst> 00:00:00:00:00:00  11  Open +	 *                                             ... or WPA   PSK etc. +	 */ + +	/* Quoting the dashes and spaces verbatim uses less code space +	   than generating them programmatically. */ +	printf ( "[Sig] SSID                  BSSID              Ch  Crypt/Auth\n" +		 "-------------------------------------------------------------\n" ); + +	list_for_each_entry ( wlan, networks, list ) { + +		/* Format SSID into 22-character string, space-padded, +		   with '>' indicating truncation */ + +		snprintf ( ssid_buf, sizeof ( ssid_buf ), "%s", wlan->essid ); +		for ( i = strlen ( ssid_buf ); i < sizeof ( ssid_buf ) - 1; +		      i++ ) +			ssid_buf[i] = ' '; +		if ( ssid_buf[sizeof ( ssid_buf ) - 2] != ' ' ) +			ssid_buf[sizeof ( ssid_buf ) - 2] = '>'; +		ssid_buf[sizeof ( ssid_buf ) - 1] = 0; + +		/* Sanity check */ +		if ( wlan->crypto >= NR_CRYPTO_TYPES || +		     wlan->handshaking >= NR_AUTH_TYPES ) +			continue; + +		printf ( "[%3d] %s %s  %2d  %s  %s\n", +			 wlan->signal < 0 ? 100 + wlan->signal : wlan->signal, +			 ssid_buf, eth_ntoa ( wlan->bssid ), wlan->channel, +			 crypto_types[wlan->crypto], +			 auth_types[wlan->handshaking] ); +	} +	printf ( "\n" ); + + err_free_networks: +	net80211_free_wlanlist ( networks ); + + err_close_netdev: +	if ( ! was_opened ) { +		dev->state &= ~NET80211_NO_ASSOC; +		netdev_close ( dev->netdev ); +	} else { +		net80211_change_channel ( dev, was_channel ); +	} + +	if ( ! rc ) +		return 0; + + err: +	printf ( "Scanning for networks on %s: %s\n", +		 dev->netdev->name, strerror ( rc ) ); +	return rc; +} diff --git a/roms/ipxe/src/usr/lotest.c b/roms/ipxe/src/usr/lotest.c new file mode 100644 index 00000000..ad7a2fad --- /dev/null +++ b/roms/ipxe/src/usr/lotest.c @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/iobuf.h> +#include <ipxe/netdevice.h> +#include <ipxe/if_ether.h> +#include <ipxe/keys.h> +#include <ipxe/console.h> +#include <usr/ifmgmt.h> +#include <usr/lotest.h> + +/** @file + * + * Loopback testing + * + */ + +/** Current loopback test receiver */ +static struct net_device *lotest_receiver; + +/** Loopback testing received packets */ +static LIST_HEAD ( lotest_queue ); + +/** + * Process received packet + * + * @v iobuf		I/O buffer + * @v netdev		Network device + * @v ll_dest		Link-layer destination address + * @v ll_source		Link-layer source address + * @v flags		Packet flags + * @ret rc		Return status code + */ +static int lotest_rx ( struct io_buffer *iobuf, +		       struct net_device *netdev, +		       const void *ll_dest __unused, +		       const void *ll_source __unused, +		       unsigned int flags __unused ) { + +	/* Add to received packet queue if currently performing a test */ +	if ( netdev == lotest_receiver ) { +		list_add_tail ( &iobuf->list, &lotest_queue ); +	} else { +		free_iob ( iobuf ); +	} + +	return 0; +} + +/** + * Dequeue received packet + * + * @ret iobuf		I/O buffer, or NULL + */ +static struct io_buffer * lotest_dequeue ( void ) { +	struct io_buffer *iobuf; + +	/* Remove first packet (if any) from received packet queue */ +	iobuf = list_first_entry ( &lotest_queue, struct io_buffer, list ); +	if ( ! iobuf ) +		return NULL; +	list_del ( &iobuf->list ); + +	return iobuf; +} + +/** + * Transcribe network-layer address + * + * @v net_addr		Network-layer address + * @ret string		Human-readable transcription of address + */ +static const char * lotest_ntoa ( const void *net_addr __unused ) { +	return "<INVALID>"; +} + +/** + * Loopback test network-layer protocol + * + * Using a dedicated network-layer protocol avoids problems caused by + * cards supporting features such as IPv4 checksum offload trying to + * interpret the (randomly generated) network-layer content. + */ +static struct net_protocol lotest_protocol __net_protocol = { +	.name = "LOTEST", +	.rx = lotest_rx, +	.ntoa = lotest_ntoa, +	.net_proto = htons ( 0x6950 ), /* Not a genuine protocol number */ +	.net_addr_len = 0, +}; + +/** + * Discard all received loopback test packets + * + */ +static void lotest_flush ( void ) { +	struct io_buffer *iobuf; + +	while ( ( iobuf = lotest_dequeue() ) != NULL ) +		free_iob ( iobuf ); +} + +/** + * Wait for packet to be received + * + * @v data		Expected data + * @v len		Expected data length + * @ret rc		Return status code + */ +static int loopback_wait ( void *data, size_t len ) { +	struct io_buffer *iobuf; + +	/* Poll until packet arrives */ +	while ( 1 ) { + +		/* Check for cancellation */ +		if ( iskey() && ( getchar() == CTRL_C ) ) +			return -ECANCELED; + +		/* Poll network devices */ +		net_poll(); + +		/* Dequeue packet, if available */ +		iobuf = lotest_dequeue(); +		if ( ! iobuf ) +			continue; + +		/* Check packet length */ +		if ( iob_len ( iobuf ) != len ) { +			printf ( "\nLength mismatch: sent %zd, received %zd", +				 len, iob_len ( iobuf ) ); +			DBG ( "\nSent:\n" ); +			DBG_HDA ( 0, data, len ); +			DBG ( "Received:\n" ); +			DBG_HDA ( 0, iobuf->data, iob_len ( iobuf ) ); +			free_iob ( iob_disown ( iobuf ) ); +			return -EINVAL; +		} + +		/* Check packet content */ +		if ( memcmp ( iobuf->data, data, len ) != 0 ) { +			printf ( "\nContent mismatch" ); +			DBG ( "\nSent:\n" ); +			DBG_HDA ( 0, data, len ); +			DBG ( "Received:\n" ); +			DBG_HDA ( 0, iobuf->data, iob_len ( iobuf ) ); +			free_iob ( iob_disown ( iobuf ) ); +			return -EINVAL; +		} + +		/* Discard packet and return */ +		free_iob ( iob_disown ( iobuf ) ); +		return 0; +	} +} + +/** + * Perform loopback test between two network devices + * + * @v sender		Sending network device + * @v receiver		Received network device + * @v mtu		Packet size (excluding link-layer headers) + * @ret rc		Return status code + */ +int loopback_test ( struct net_device *sender, struct net_device *receiver, +		    size_t mtu ) { +	uint8_t *buf; +	uint32_t *seq; +	struct io_buffer *iobuf; +	unsigned int i; +	unsigned int successes; +	int rc; + +	/* Open network devices */ +	if ( ( rc = ifopen ( sender ) ) != 0 ) +		return rc; +	if ( ( rc = ifopen ( receiver ) ) != 0 ) +		return rc; + +	/* Wait for link-up */ +	if ( ( rc = iflinkwait ( sender, 0 ) ) != 0 ) +		return rc; +	if ( ( rc = iflinkwait ( receiver, 0 ) ) != 0 ) +		return rc; + +	/* Allocate data buffer */ +	if ( mtu < sizeof ( *seq ) ) +		mtu = sizeof ( *seq ); +	buf = malloc ( mtu ); +	if ( ! buf ) +		return -ENOMEM; +	seq = ( ( void * ) buf ); + +	/* Print initial statistics */ +	printf ( "Performing loopback test from %s to %s with %zd byte MTU\n", +		 sender->name, receiver->name, mtu ); +	ifstat ( sender ); +	ifstat ( receiver ); + +	/* Start loopback test */ +	lotest_flush(); +	lotest_receiver = receiver; + +	/* Perform loopback test */ +	for ( successes = 0 ; ; successes++ ) { + +		/* Print running total */ +		printf ( "\r%d", successes ); + +		/* Generate random packet */ +		*seq = htonl ( successes ); +		for ( i = sizeof ( *seq ) ; i < mtu ; i++ ) +			buf[i] = random(); +		iobuf = alloc_iob ( MAX_LL_HEADER_LEN + mtu ); +		if ( ! iobuf ) { +			printf ( "\nFailed to allocate I/O buffer" ); +			rc = -ENOMEM; +			break; +		} +		iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); +		memcpy ( iob_put ( iobuf, mtu ), buf, mtu ); + +		/* Transmit packet */ +		if ( ( rc = net_tx ( iob_disown ( iobuf ), sender, +				     &lotest_protocol, receiver->ll_addr, +				     sender->ll_addr ) ) != 0 ) { +			printf ( "\nFailed to transmit packet: %s", +				 strerror ( rc ) ); +			break; +		} + +		/* Wait for received packet */ +		if ( ( rc = loopback_wait ( buf, mtu ) ) != 0 ) +			break; +	} + +	printf ( "\n"); + +	/* Stop loopback testing */ +	lotest_receiver = NULL; +	lotest_flush(); + +	/* Dump final statistics */ +	ifstat ( sender ); +	ifstat ( receiver ); + +	/* Free buffer */ +	free ( buf ); + +	return 0; +} diff --git a/roms/ipxe/src/usr/neighmgmt.c b/roms/ipxe/src/usr/neighmgmt.c new file mode 100644 index 00000000..e4d21a20 --- /dev/null +++ b/roms/ipxe/src/usr/neighmgmt.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <ipxe/neighbour.h> +#include <usr/neighmgmt.h> + +/** @file + * + * Neighbour management + * + */ + +/** + * Print neighbour table + * + */ +void nstat ( void ) { +	struct neighbour *neighbour; +	struct net_device *netdev; +	struct ll_protocol *ll_protocol; +	struct net_protocol *net_protocol; + +	list_for_each_entry ( neighbour, &neighbours, list ) { +		netdev = neighbour->netdev; +		ll_protocol = netdev->ll_protocol; +		net_protocol = neighbour->net_protocol; +		printf ( "%s %s %s is %s %s", netdev->name, net_protocol->name, +			 net_protocol->ntoa ( neighbour->net_dest ), +			 ll_protocol->name, +			 ( neighbour_has_ll_dest ( neighbour ) ? +			   ll_protocol->ntoa ( neighbour->ll_dest ) : +			   "(incomplete)" ) ); +		if ( neighbour->discovery ) +			printf ( " (%s)", neighbour->discovery->name ); +		printf ( "\n" ); +	} +} diff --git a/roms/ipxe/src/usr/nslookup.c b/roms/ipxe/src/usr/nslookup.c new file mode 100644 index 00000000..eb2b08b4 --- /dev/null +++ b/roms/ipxe/src/usr/nslookup.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2012 Patrick Plenefisch <phplenefisch@wpi.edu>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ipxe/resolv.h> +#include <ipxe/tcpip.h> +#include <ipxe/monojob.h> +#include <ipxe/settings.h> +#include <usr/nslookup.h> + +/** @file + * + * Standalone name resolution + * + */ + +/** A name resolution request */ +struct nslookup { +	/** Reference count for this object */ +	struct refcnt refcnt; + +	/** Job control interface */ +	struct interface job; +	/** Data transfer interface */ +	struct interface resolver; + +	/** Setting name */ +	char *setting_name; +}; + +/** + * Terminate name resolution + * + * @v nslookup		Name resolution request + * @v rc		Reason for termination + */ +static void nslookup_close ( struct nslookup *nslookup, int rc ) { + +	/* Shut down interfaces */ +	intf_shutdown ( &nslookup->resolver, rc ); +	intf_shutdown ( &nslookup->job, rc ); +} + +/** + * Handle resolved name + * + * @v nslookup		Name resolution request + * @v sa		Completed socket address + */ +static void nslookup_resolv_done ( struct nslookup *nslookup, +				   struct sockaddr *sa ) { +	struct sockaddr_in *sin; +	struct sockaddr_in6 *sin6; +	const struct setting_type *default_type; +	struct settings *settings; +	struct setting setting; +	void *data; +	size_t len; +	int rc; + +	/* Extract address */ +	switch ( sa->sa_family ) { +	case AF_INET: +		sin = ( ( struct sockaddr_in * ) sa ); +		data = &sin->sin_addr; +		len = sizeof ( sin->sin_addr ); +		default_type = &setting_type_ipv4; +		break; +	case AF_INET6: +		sin6 = ( ( struct sockaddr_in6 * ) sa ); +		data = &sin6->sin6_addr; +		len = sizeof ( sin6->sin6_addr ); +		default_type = &setting_type_ipv6; +		break; +	default: +		rc = -ENOTSUP; +		goto err; +	} + +	/* Parse specified setting name */ +	if ( ( rc = parse_setting_name ( nslookup->setting_name, +					 autovivify_child_settings, &settings, +					 &setting ) ) != 0 ) +		goto err; + +	/* Apply default type if necessary */ +	if ( ! setting.type ) +		setting.type = default_type; + +	/* Store in specified setting */ +	if ( ( rc = store_setting ( settings, &setting, data, len ) ) != 0 ) +		goto err; + + err: +	/* Terminate name resolution */ +	nslookup_close ( nslookup, rc ); +} + +/** Name resolution resolver interface operations */ +static struct interface_operation nslookup_resolver_operations[] = { +	INTF_OP ( resolv_done, struct nslookup *, nslookup_resolv_done ), +	INTF_OP ( intf_close, struct nslookup *, nslookup_close ), +}; + +/** Name resolution resolver interface descriptor */ +static struct interface_descriptor nslookup_resolver_desc = +	INTF_DESC_PASSTHRU ( struct nslookup, resolver, +			     nslookup_resolver_operations, job ); + +/** Name resolution job control interface operations */ +static struct interface_operation nslookup_job_operations[] = { +	INTF_OP ( intf_close, struct nslookup *, nslookup_close ), +}; + +/** Name resolution job control interface descriptor */ +static struct interface_descriptor nslookup_job_desc = +	INTF_DESC_PASSTHRU ( struct nslookup, job, +			     nslookup_job_operations, resolver ); + +/** + * Initiate standalone name resolution + * + * @v job		Parent interface + * @v name		Name to resolve + * @v setting_name	Setting name + * @ret rc		Return status code + */ +static int resolv_setting ( struct interface *job, const char *name, +			    const char *setting_name ) { +	struct nslookup *nslookup; +	struct sockaddr sa; +	char *setting_name_copy; +	int rc; + +	/* Allocate and initialise structure */ +	nslookup = zalloc ( sizeof ( *nslookup ) + strlen ( setting_name ) +			    + 1 /* NUL */ ); +	if ( ! nslookup ) +		return -ENOMEM; +	ref_init ( &nslookup->refcnt, NULL ); +	intf_init ( &nslookup->job, &nslookup_job_desc, &nslookup->refcnt ); +	intf_init ( &nslookup->resolver, &nslookup_resolver_desc, +		    &nslookup->refcnt ); +	setting_name_copy = ( ( void * ) ( nslookup + 1 ) ); +	strcpy ( setting_name_copy, setting_name ); +	nslookup->setting_name = setting_name_copy; + +	/* Start name resolution */ +	memset ( &sa, 0, sizeof ( sa ) ); +	if ( ( rc = resolv ( &nslookup->resolver, name, &sa ) ) != 0 ) +		goto err_resolv; + +	/* Attach parent interface, mortalise self, and return */ +	intf_plug_plug ( &nslookup->job, job ); +	ref_put ( &nslookup->refcnt ); +	return 0; + + err_resolv: +	ref_put ( &nslookup->refcnt ); +	return rc; +} + +/** + * Perform (blocking) standalone name resolution + * + * @v name		Name to resolve + * @v setting_name	Setting name + * @ret rc		Return status code + */ +int nslookup ( const char *name, const char *setting_name ) { +	int rc; + +	/* Perform name resolution */ +	if ( ( rc = resolv_setting ( &monojob, name, setting_name ) ) == 0 ) +		rc = monojob_wait ( NULL, 0 ); +	if ( rc != 0 ) { +		printf ( "Could not resolve %s: %s\n", name, strerror ( rc ) ); +		return rc; +	} + +	return 0; +} diff --git a/roms/ipxe/src/usr/pingmgmt.c b/roms/ipxe/src/usr/pingmgmt.c new file mode 100644 index 00000000..16b3ec99 --- /dev/null +++ b/roms/ipxe/src/usr/pingmgmt.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <ipxe/pinger.h> +#include <ipxe/monojob.h> +#include <ipxe/timer.h> +#include <usr/pingmgmt.h> + +/** @file + * + * ICMP ping management + * + */ + +/** + * Display ping result + * + * @v src		Source socket address, or NULL + * @v sequence		Sequence number + * @v len		Payload length + * @v rc		Status code + */ +static void ping_callback ( struct sockaddr *peer, unsigned int sequence, +			    size_t len, int rc ) { + +	/* Display ping response */ +	printf ( "%zd bytes from %s: seq=%d", +		 len, ( peer ? sock_ntoa ( peer ) : "<none>" ), sequence ); +	if ( rc != 0 ) +		printf ( ": %s", strerror ( rc ) ); +	printf ( "\n" ); +} + +/** + * Ping a host + * + * @v hostname		Hostname + * @v timeout		Timeout between pings, in ticks + * @v len		Payload length + * @v count		Number of packets to send (or zero for no limit) + * @v quiet		Inhibit output + * @ret rc		Return status code + */ +int ping ( const char *hostname, unsigned long timeout, size_t len, +	   unsigned int count, int quiet ) { +	int rc; + +	/* Create pinger */ +	if ( ( rc = create_pinger ( &monojob, hostname, timeout, len, count, +				    ( quiet ? NULL : ping_callback ) ) ) != 0 ){ +		printf ( "Could not start ping: %s\n", strerror ( rc ) ); +		return rc; +	} + +	/* Wait for ping to complete */ +	if ( ( rc = monojob_wait ( NULL, 0 ) ) != 0 ) { +		if ( ! quiet ) +			printf ( "Finished: %s\n", strerror ( rc ) ); +		return rc; +	} + +	return 0; +} diff --git a/roms/ipxe/src/usr/profstat.c b/roms/ipxe/src/usr/profstat.c new file mode 100644 index 00000000..99142747 --- /dev/null +++ b/roms/ipxe/src/usr/profstat.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <ipxe/profile.h> +#include <usr/profstat.h> + +/** @file + * + * Profiling + * + */ + +/** + * Print profiling statistics + * + */ +void profstat ( void ) { +	struct profiler *profiler; + +	for_each_table_entry ( profiler, PROFILERS ) { +		printf ( "%s: %ld +/- %ld ticks (%d samples)\n", +			 profiler->name, profile_mean ( profiler ), +			 profile_stddev ( profiler ), profiler->count ); +	} +} diff --git a/roms/ipxe/src/usr/prompt.c b/roms/ipxe/src/usr/prompt.c new file mode 100644 index 00000000..957b4ab3 --- /dev/null +++ b/roms/ipxe/src/usr/prompt.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** @file + * + * Prompt for keypress + * + */ + +#include <errno.h> +#include <stdio.h> +#include <ipxe/console.h> +#include <usr/prompt.h> + +/** + * Prompt for keypress + * + * @v text		Prompt string + * @v timeout		Timeout period, in ticks (0=indefinite) + * @v key		Key to wait for (0=any key) + * @ret rc		Return status code + * + * Returns success if the specified key was pressed within the + * specified timeout period. + */ +int prompt ( const char *text, unsigned long timeout, int key ) { +	int key_pressed; + +	/* Display prompt */ +	printf ( "%s", text ); + +	/* Wait for key */ +	key_pressed = getkey ( timeout ); + +	/* Clear the prompt line */ +	while ( *(text++) ) +		printf ( "\b \b" ); + +	/* Check for timeout */ +	if ( key_pressed < 0 ) +		return -ETIMEDOUT; + +	/* Check for correct key pressed */ +	if ( key && ( key_pressed != key ) ) +		return -ECANCELED; + +	return 0; +} diff --git a/roms/ipxe/src/usr/pxemenu.c b/roms/ipxe/src/usr/pxemenu.c new file mode 100644 index 00000000..b69905df --- /dev/null +++ b/roms/ipxe/src/usr/pxemenu.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <byteswap.h> +#include <curses.h> +#include <ipxe/console.h> +#include <ipxe/dhcp.h> +#include <ipxe/keys.h> +#include <ipxe/timer.h> +#include <ipxe/uri.h> +#include <ipxe/ansicol.h> +#include <usr/dhcpmgmt.h> +#include <usr/autoboot.h> + +/** @file + * + * PXE Boot Menus + * + */ + +/** A PXE boot menu item */ +struct pxe_menu_item { +	/** Boot Server type */ +	unsigned int type; +	/** Description */ +	char *desc; +}; + +/** + * A PXE boot menu + * + * This structure encapsulates the menu information provided via DHCP + * options. + */ +struct pxe_menu { +	/** Prompt string (optional) */ +	const char *prompt; +	/** Timeout (in seconds) +	 * +	 * Negative indicates no timeout (i.e. wait indefinitely) +	 */ +	int timeout; +	/** Number of menu items */ +	unsigned int num_items; +	/** Selected menu item */ +	unsigned int selection; +	/** Menu items */ +	struct pxe_menu_item items[0]; +}; + +/** + * Parse and allocate PXE boot menu + * + * @v menu		PXE boot menu to fill in + * @ret rc		Return status code + * + * It is the callers responsibility to eventually free the allocated + * boot menu. + */ +static int pxe_menu_parse ( struct pxe_menu **menu ) { +	struct setting pxe_boot_menu_prompt_setting = +		{ .tag = DHCP_PXE_BOOT_MENU_PROMPT }; +	struct setting pxe_boot_menu_setting = +		{ .tag = DHCP_PXE_BOOT_MENU }; +	uint8_t raw_menu[256]; +	int raw_prompt_len; +	int raw_menu_len; +	struct dhcp_pxe_boot_menu *raw_menu_item; +	struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt; +	void *raw_menu_end; +	unsigned int num_menu_items; +	unsigned int i; +	int rc; + +	/* Fetch raw menu */ +	memset ( raw_menu, 0, sizeof ( raw_menu ) ); +	if ( ( raw_menu_len = fetch_raw_setting ( NULL, &pxe_boot_menu_setting, +						  raw_menu, +						  sizeof ( raw_menu ) ) ) < 0 ){ +		rc = raw_menu_len; +		DBG ( "Could not retrieve raw PXE boot menu: %s\n", +		      strerror ( rc ) ); +		return rc; +	} +	if ( raw_menu_len >= ( int ) sizeof ( raw_menu ) ) { +		DBG ( "Raw PXE boot menu too large for buffer\n" ); +		return -ENOSPC; +	} +	raw_menu_end = ( raw_menu + raw_menu_len ); + +	/* Fetch raw prompt length */ +	raw_prompt_len = +		fetch_raw_setting ( NULL, &pxe_boot_menu_prompt_setting, +				    NULL, 0 ); +	if ( raw_prompt_len < 0 ) +		raw_prompt_len = 0; + +	/* Count menu items */ +	num_menu_items = 0; +	raw_menu_item = ( ( void * ) raw_menu ); +	while ( 1 ) { +		if ( ( ( ( void * ) raw_menu_item ) + +		       sizeof ( *raw_menu_item ) ) > raw_menu_end ) +			break; +		if ( ( ( ( void * ) raw_menu_item ) + +		       sizeof ( *raw_menu_item ) + +		       raw_menu_item->desc_len ) > raw_menu_end ) +			break; +		num_menu_items++; +		raw_menu_item = ( ( ( void * ) raw_menu_item ) + +				  sizeof ( *raw_menu_item ) + +				  raw_menu_item->desc_len ); +	} + +	/* Allocate space for parsed menu */ +	*menu = zalloc ( sizeof ( **menu ) + +			 ( num_menu_items * sizeof ( (*menu)->items[0] ) ) + +			 raw_menu_len + 1 /* NUL */ + +			 raw_prompt_len + 1 /* NUL */ ); +	if ( ! *menu ) { +		DBG ( "Could not allocate PXE boot menu\n" ); +		return -ENOMEM; +	} + +	/* Fill in parsed menu */ +	(*menu)->num_items = num_menu_items; +	raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) + +			  ( num_menu_items * sizeof ( (*menu)->items[0] ) ) ); +	memcpy ( raw_menu_item, raw_menu, raw_menu_len ); +	for ( i = 0 ; i < num_menu_items ; i++ ) { +		(*menu)->items[i].type = le16_to_cpu ( raw_menu_item->type ); +		(*menu)->items[i].desc = raw_menu_item->desc; +		/* Set type to 0; this ensures that the description +		 * for the previous menu item is NUL-terminated. +		 * (Final item is NUL-terminated anyway.) +		 */ +		raw_menu_item->type = 0; +		raw_menu_item = ( ( ( void * ) raw_menu_item ) + +				  sizeof ( *raw_menu_item ) + +				  raw_menu_item->desc_len ); +	} +	if ( raw_prompt_len ) { +		raw_menu_prompt = ( ( ( void * ) raw_menu_item ) + +				    1 /* NUL */ ); +		fetch_raw_setting ( NULL, &pxe_boot_menu_prompt_setting, +				    raw_menu_prompt, raw_prompt_len ); +		(*menu)->timeout = +			( ( raw_menu_prompt->timeout == 0xff ) ? +			  -1 : raw_menu_prompt->timeout ); +		(*menu)->prompt = raw_menu_prompt->prompt; +	} else { +		(*menu)->timeout = -1; +	} + +	return 0; +} + +/** + * Draw PXE boot menu item + * + * @v menu		PXE boot menu + * @v index		Index of item to draw + * @v selected		Item is selected + */ +static void pxe_menu_draw_item ( struct pxe_menu *menu, +				 unsigned int index, int selected ) { +	char buf[COLS+1]; +	size_t len; +	unsigned int row; + +	/* Prepare space-padded row content */ +	len = snprintf ( buf, sizeof ( buf ), " %c. %s", +			 ( 'A' + index ), menu->items[index].desc ); +	while ( len < ( sizeof ( buf ) - 1 ) ) +		buf[len++] = ' '; +	buf[ sizeof ( buf ) - 1 ] = '\0'; + +	/* Draw row */ +	row = ( LINES - menu->num_items + index ); +	color_set ( ( selected ? CPAIR_PXE : CPAIR_DEFAULT ), NULL ); +	mvprintw ( row, 0, "%s", buf ); +	move ( row, 1 ); +} + +/** + * Make selection from PXE boot menu + * + * @v menu		PXE boot menu + * @ret rc		Return status code + */ +static int pxe_menu_select ( struct pxe_menu *menu ) { +	int key; +	unsigned int key_selection; +	unsigned int i; +	int rc = 0; + +	/* Initialise UI */ +	initscr(); +	start_color(); +	color_set ( CPAIR_DEFAULT, NULL ); + +	/* Draw initial menu */ +	for ( i = 0 ; i < menu->num_items ; i++ ) +		printf ( "\n" ); +	for ( i = 0 ; i < menu->num_items ; i++ ) +		pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 ); + +	while ( 1 ) { + +		/* Highlight currently selected item */ +		pxe_menu_draw_item ( menu, menu->selection, 1 ); + +		/* Wait for keyboard input */ +		key = getkey ( 0 ); + +		/* Unhighlight currently selected item */ +		pxe_menu_draw_item ( menu, menu->selection, 0 ); + +		/* Act upon key */ +		if ( ( key == CR ) || ( key == LF ) ) { +			pxe_menu_draw_item ( menu, menu->selection, 1 ); +			break; +		} else if ( ( key == CTRL_C ) || ( key == ESC ) ) { +			rc = -ECANCELED; +			break; +		} else if ( key == KEY_UP ) { +			if ( menu->selection > 0 ) +				menu->selection--; +		} else if ( key == KEY_DOWN ) { +			if ( menu->selection < ( menu->num_items - 1 ) ) +				menu->selection++; +		} else if ( ( key < KEY_MIN ) && +			    ( ( key_selection = ( toupper ( key ) - 'A' ) ) +			      < menu->num_items ) ) { +			menu->selection = key_selection; +			pxe_menu_draw_item ( menu, menu->selection, 1 ); +			break; +		} +	} + +	/* Shut down UI */ +	endwin(); + +	return rc; +} + +/** + * Prompt for (and make selection from) PXE boot menu + * + * @v menu		PXE boot menu + * @ret rc		Return status code + */ +static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) { +	unsigned long start = currticks(); +	unsigned long now; +	unsigned long elapsed; +	size_t len = 0; +	int key; +	int rc = 0; + +	/* Display menu immediately, if specified to do so */ +	if ( menu->timeout < 0 ) { +		if ( menu->prompt ) +			printf ( "%s\n", menu->prompt ); +		return pxe_menu_select ( menu ); +	} + +	/* Display prompt, if specified */ +	if ( menu->prompt ) +		printf ( "%s", menu->prompt ); + +	/* Wait for timeout, if specified */ +	while ( menu->timeout > 0 ) { +		if ( ! len ) +			len = printf ( " (%d)", menu->timeout ); +		if ( iskey() ) { +			key = getkey ( 0 ); +			if ( key == KEY_F8 ) { +				/* Display menu */ +				printf ( "\n" ); +				return pxe_menu_select ( menu ); +			} else if ( ( key == CTRL_C ) || ( key == ESC ) ) { +				/* Abort */ +				rc = -ECANCELED; +				break; +			} else { +				/* Stop waiting */ +				break; +			} +		} +		now = currticks(); +		elapsed = ( now - start ); +		if ( elapsed >= TICKS_PER_SEC ) { +			menu->timeout -= 1; +			do { +				printf ( "\b \b" ); +			} while ( --len ); +			start = now; +		} +	} + +	/* Return with default option selected */ +	printf ( "\n" ); +	return rc; +} + +/** + * Boot using PXE boot menu + * + * @ret rc		Return status code + * + * Note that a success return status indicates that a PXE boot menu + * item has been selected, and that the DHCP session should perform a + * boot server request/ack. + */ +int pxe_menu_boot ( struct net_device *netdev ) { +	struct pxe_menu *menu; +	unsigned int pxe_type; +	struct settings *pxebs_settings; +	struct uri *uri; +	int rc; + +	/* Parse and allocate boot menu */ +	if ( ( rc = pxe_menu_parse ( &menu ) ) != 0 ) +		return rc; + +	/* Make selection from boot menu */ +	if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) { +		free ( menu ); +		return rc; +	} +	pxe_type = menu->items[menu->selection].type; + +	/* Free boot menu */ +	free ( menu ); + +	/* Return immediately if local boot selected */ +	if ( ! pxe_type ) +		return 0; + +	/* Attempt PXE Boot Server Discovery */ +	if ( ( rc = pxebs ( netdev, pxe_type ) ) != 0 ) +		return rc; + +	/* Fetch next server and filename */ +	pxebs_settings = find_settings ( PXEBS_SETTINGS_NAME ); +	assert ( pxebs_settings ); +	uri = fetch_next_server_and_filename ( pxebs_settings ); +	if ( ! uri ) +		return -ENOMEM; + +	/* Attempt boot */ +	rc = uriboot ( uri, NULL, 0, URIBOOT_NO_SAN ); +	uri_put ( uri ); +	return rc; +} diff --git a/roms/ipxe/src/usr/route.c b/roms/ipxe/src/usr/route.c new file mode 100644 index 00000000..ba4cc322 --- /dev/null +++ b/roms/ipxe/src/usr/route.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <ipxe/netdevice.h> +#include <usr/route.h> + +/** @file + * + * Routing management + * + */ + +/** + * Print routing table + * + */ +void route ( void ) { +	struct net_device *netdev; +	struct routing_family *family; + +	for_each_netdev ( netdev ) { +		for_each_table_entry ( family, ROUTING_FAMILIES ) { +			family->print ( netdev ); +		} +	} +} diff --git a/roms/ipxe/src/usr/route_ipv4.c b/roms/ipxe/src/usr/route_ipv4.c new file mode 100644 index 00000000..b4d1b7bf --- /dev/null +++ b/roms/ipxe/src/usr/route_ipv4.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <ipxe/netdevice.h> +#include <ipxe/ip.h> +#include <usr/route.h> + +/** @file + * + * IPv4 routing management + * + */ + +/** + * Print IPv4 routing table + * + * @v netdev		Network device + */ +static void route_ipv4_print ( struct net_device *netdev ) { +	struct ipv4_miniroute *miniroute; + +	list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { +		if ( miniroute->netdev != netdev ) +			continue; +		printf ( "%s: %s/", netdev->name, +			 inet_ntoa ( miniroute->address ) ); +		printf ( "%s", inet_ntoa ( miniroute->netmask ) ); +		if ( miniroute->gateway.s_addr ) +			printf ( " gw %s", inet_ntoa ( miniroute->gateway ) ); +		if ( ! netdev_is_open ( miniroute->netdev ) ) +			printf ( " (inaccessible)" ); +		printf ( "\n" ); +	} +} + +/** IPv4 routing family */ +struct routing_family ipv4_routing_family __routing_family ( ROUTING_IPV4 ) = { +	.print = route_ipv4_print, +}; diff --git a/roms/ipxe/src/usr/route_ipv6.c b/roms/ipxe/src/usr/route_ipv6.c new file mode 100644 index 00000000..6045f85b --- /dev/null +++ b/roms/ipxe/src/usr/route_ipv6.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdio.h> +#include <ipxe/netdevice.h> +#include <ipxe/ipv6.h> +#include <usr/route.h> + +/** @file + * + * IPv6 routing management + * + */ + +/** + * Print IPv6 routing table + * + * @v netdev		Network device + */ +static void route_ipv6_print ( struct net_device *netdev ) { +	struct ipv6_miniroute *miniroute; + +	list_for_each_entry ( miniroute, &ipv6_miniroutes, list ) { +		if ( miniroute->netdev != netdev ) +			continue; +		printf ( "%s: %s/%d", netdev->name, +			 inet6_ntoa ( &miniroute->address ), +			 miniroute->prefix_len ); +		if ( miniroute->flags & IPV6_HAS_ROUTER ) +			printf ( " gw %s", inet6_ntoa ( &miniroute->router ) ); +		if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) +			printf ( " (no address)" ); +		if ( ! netdev_is_open ( miniroute->netdev ) ) +			printf ( " (inaccessible)" ); +		printf ( "\n" ); +	} +} + +/** IPv6 routing family */ +struct routing_family ipv6_routing_family __routing_family ( ROUTING_IPV6 ) = { +	.print = route_ipv6_print, +}; diff --git a/roms/ipxe/src/usr/sync.c b/roms/ipxe/src/usr/sync.c new file mode 100644 index 00000000..f7a04c44 --- /dev/null +++ b/roms/ipxe/src/usr/sync.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 of the + * License, or 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stddef.h> +#include <ipxe/job.h> +#include <ipxe/monojob.h> +#include <ipxe/pending.h> +#include <usr/sync.h> + +/** @file + * + * Wait for pending operations to complete + * + */ + +/** + * Report progress + * + * @v intf		Interface + * @v progress		Progress report to fill in + * @ret ongoing_rc	Ongoing job status code (if known) + */ +static int sync_progress ( struct interface *intf, +			   struct job_progress *progress __unused ) { + +	/* Terminate successfully if no pending operations remain */ +	if ( ! have_pending() ) +		intf_close ( intf, 0 ); + +	return 0; +} + +/** Synchroniser interface operations */ +static struct interface_operation sync_intf_op[] = { +	INTF_OP ( job_progress, struct interface *, sync_progress ), +}; + +/** Synchroniser interface descriptor */ +static struct interface_descriptor sync_intf_desc = +	INTF_DESC_PURE ( sync_intf_op ); + +/** Synchroniser */ +static struct interface sync_intf = INTF_INIT ( sync_intf_desc ); + +/** + * Wait for pending operations to complete + * + * @v timeout		Timeout period, in ticks (0=indefinite) + * @ret rc		Return status code + */ +int sync ( unsigned long timeout ) { + +	/* Attach synchroniser and wait for completion */ +	intf_plug_plug ( &monojob, &sync_intf ); +	return monojob_wait ( NULL, timeout ); +} | 
