diff --git a/minix/Makefile.fetchgnu b/minix/Makefile.fetchgnu deleted file mode 100644 index e6dd8a7eb..000000000 --- a/minix/Makefile.fetchgnu +++ /dev/null @@ -1,71 +0,0 @@ -# MINIX fetch rules -# -# Rules used to fetch a GNU package. Expects GNUHOSTDIST to be set -# and to be previously .include'd. -# -# New interface: -# -# * When using the `cleandir' target, defining CLEANFETCHED=yes will -# additionally remove all the fetched files. -# This is particularly useful when a GNU package is updated. -# -# The rest should be operations hidden to the normal programmers. -# -# How to use: (maintainers manual) -# -# * put a fetch.sh script one directory below the GNUHOSTDIST; -# -# * .include "path/to/minix/Makefile.fetchgnu", after having -# defined GNUHOSTDIST and before any use of the variable below; -# -# * insert ${fetch_done} as source before performing any operation -# on the files under GPL license which are usually found -# within NetBSD src/ tree; -# -# * rinse and repeat for every target which assumes the presence of -# these files, and for every Makefile operating upon them. -# -# -# TODO: does not handle correctly the cases where there are more than -# one package downloaded by fetch.sh (e.g.gnu/dist with texinfo+gmake): -# .gitignore only "protects" the first package which triggers. - -.if !defined(__MINIX) || !defined(GNUHOSTDIST) -.error Bad logic in Makefiles. -.endif - -.if !defined(_MINIX_FETCHGNU_MK_) -_MINIX_FETCHGNU_MK_=1 - -# MINIX /usr/src does not have the sources for the GNU utilities -# in-tree, for licensing reasons. So to successfully use them while -# cross-compiling, we have to fetch them. The success of that operation -# is indicated by the presence of a .gitignore file in the corresponding -# source parent directory, which also conveniently hides from git. -.if exists(${GNUHOSTDIST:H}/fetch.sh) -${GNUHOSTDIST:H}/.gitignore: ${GNUHOSTDIST:H}/fetch.sh - SED=${TOOL_SED} ${HOST_SH} ${GNUHOSTDIST:H}/fetch.sh - @test -e ${GNUHOSTDIST}/configure - @echo "${MODULE:U${.CURDIR:T}:C,gcc[0-9]*,gcc,:C,gmake*,make,}-*.tar.*z*" >> $@ - @echo ${GNUHOSTDIST:T} >> $@ - -# Do the fetching as an extra step, to force serialization -.fetch_done: ${GNUHOSTDIST:H}/.gitignore - @touch $@ -fetch_done=.fetch_done - -# Special target for MINIX, reset the source tree as pristine -# Note it does NOT remove the downloaded tarball -.if ${CLEANFETCHED:Uno} == "yes" -cleandir: clean_gnu_src -clean_gnu_src: - -rm -r -f ${GNUHOSTDIST} ${GNUHOSTDIST:H}/.gitignore -.endif # CLEANFETCHED == yes - -clean: clean.fetchgnu -clean.fetchgnu: - -@rm -f .fetch_done - -.endif # exists(GNUHOSTDIST:H/fetch.sh) on MINIX - -.endif # !defined(_MINIX_FETCHGNU_MK_) diff --git a/minix/commands/devmand/Makefile b/minix/commands/devmand/Makefile deleted file mode 100644 index 02dc7a834..000000000 --- a/minix/commands/devmand/Makefile +++ /dev/null @@ -1,20 +0,0 @@ - -PROG = devmand -MAN = - -SRCS = main.c usb_scan.l usb.y -CPPFLAGS+= -I${.CURDIR} - -YFLAGS:= -d -CLEANFILES+= y.tab.h - -usb_scan.l: usb.y - -.include - -# LSC: Seems that this file is implicitly taken into account by NetBSD's make, -# still seems to be ignored / not found currently. -# It's a sad story, as it has default rules to manage yacc / lex files. So for -# a happy ending here it is explicitly included: -.include - diff --git a/minix/commands/fetch/fetch.1 b/minix/commands/fetch/fetch.1 deleted file mode 100644 index c767c1a0e..000000000 --- a/minix/commands/fetch/fetch.1 +++ /dev/null @@ -1,265 +0,0 @@ -.\" $NetBSD: fetch.1,v 1.4 2009/02/07 15:25:51 wiz Exp $ -.\"- -.\" Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav -.\" All rights reserved. -.\" Portions Copyright (c) 1999 Massachusetts Institute of Technology; used -.\" by permission. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer -.\" in this position and unchanged. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. The name of the author may not be used to endorse or promote products -.\" derived from this software without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.Dd February 5, 2009 -.Dt FETCH 1 -.Os -.Sh NAME -.Nm fetch -.Nd retrieve a file by Uniform Resource Locator -.Sh SYNOPSIS -.Nm -.Op Fl 146AFMRUadilmnqrsv -.Op Fl B Ar bytes -.Op Fl N Ar file -.Op Fl o Ar file -.Op Fl S Ar bytes -.Op Fl T Ar seconds -.Op Fl w Ar seconds -.Op Ar URL ... -.Sh DESCRIPTION -The -.Nm -utility provides a command-line interface to the -.Xr fetch 3 -library. -Its purpose is to retrieve the file(s) pointed to by the URL(s) on the -command line. -.Pp -The following options are available: -.Bl -tag -width Fl -.It Fl 1 -Stop and return exit code 0 at the first successfully retrieved file. -.It Fl 4 -Forces -.Nm -to use IPv4 addresses only. -.It Fl 6 -Forces -.Nm -to use IPv6 addresses only. -.It Fl A -Do not automatically follow -.Dq temporary -(302) redirects. -Some broken Web sites will return a redirect instead of a not-found -error when the requested object does not exist. -.It Fl a -Automatically retry the transfer upon soft failures. -.It Fl B Ar bytes -Specify the read buffer size in bytes. -The default is 4096 bytes. -Attempts to set a buffer size lower than this will be silently -ignored. -The number of reads actually performed is reported at verbosity level -two or higher (see the -.Fl v -flag). -.It Fl d -Use a direct connection even if a proxy is configured. -.It Fl F -In combination with the -.Fl r -flag, forces a restart even if the local and remote files have -different modification times. -Implies -.Fl R . -.It Fl i -Only fetch if it the output file is older than the referenced URL. -This option is overriden by -.Fl o Ar - . -.It Fl l -If the target is a file-scheme URL, make a symbolic link to the target -rather than trying to copy it. -.It Fl M -.It Fl m -Mirror mode: if the file already exists locally and has the same size -and modification time as the remote file, it will not be fetched. -Note that the -.Fl m -and -.Fl r -flags are mutually exclusive. -.It Fl N Ar file -Use -.Ar file -instead of -.Pa ~/.netrc -to look up login names and passwords for FTP sites. -See -.Xr ftp 1 -for a description of the file format. -This feature is experimental. -.It Fl n -Do not preserve the modification time of the transferred file. -.It Fl o Ar file -Set the output file name to -.Ar file . -By default, a -.Dq pathname -is extracted from the specified URI, and -its basename is used as the name of the output file. -A -.Ar file -argument of -.Sq Li \&- -indicates that results are to be directed to the standard output. -If the -.Ar file -argument is a directory, fetched file(s) will be placed within the -directory, with name(s) selected as in the default behaviour. -.It Fl q -Quiet mode. -.It Fl R -The output files are precious, and should not be deleted under any -circumstances, even if the transfer failed or was incomplete. -.It Fl r -Restart a previously interrupted transfer. -Note that the -.Fl m -and -.Fl r -flags are mutually exclusive. -.It Fl S Ar bytes -Require the file size reported by the server to match the specified -value. -If it does not, a message is printed and the file is not fetched. -If the server does not support reporting file sizes, this option is -ignored and the file is fetched unconditionally. -.It Fl s -Print the size in bytes of each requested file, without fetching it. -.It Fl T Ar seconds -Set timeout value to -.Ar seconds . -Overrides the environment variables -.Ev FTP_TIMEOUT -for FTP transfers or -.Ev HTTP_TIMEOUT -for HTTP transfers if set. -.It Fl U -When using passive FTP, allocate the port for the data connection from -the low (default) port range. -See -.Xr ip 4 -for details on how to specify which port range this corresponds to. -.It Fl v -Increase verbosity level. -.It Fl w Ar seconds -When the -.Fl a -flag is specified, wait this many seconds between successive retries. -.El -.Pp -If -.Nm -receives a -.Dv SIGINFO -signal (see the -.Cm status -argument for -.Xr stty 1 ) , -the current transfer rate statistics will be written to the -standard error output, in the same format as the standard completion -message. -.Sh EXIT STATUS -The -.Nm -command returns zero on success, or one on failure. -If multiple URLs are listed on the command line, -.Nm -will attempt to retrieve each one of them in turn, and will return -zero only if they were all successfully retrieved. -.Sh ENVIRONMENT -.Bl -tag -width HTTP_TIMEOUT -.It Ev FTP_TIMEOUT -Maximum time, in seconds, to wait before aborting an FTP connection. -.It Ev HTTP_TIMEOUT -Maximum time, in seconds, to wait before aborting an HTTP connection. -.El -.Pp -See -.Xr fetch 3 -for a description of additional environment variables, including -.Ev FETCH_BIND_ADDRESS , -.Ev FTP_LOGIN , -.Ev FTP_PASSIVE_MODE , -.Ev FTP_PASSWORD , -.Ev FTP_PROXY , -.Ev ftp_proxy , -.Ev HTTP_AUTH , -.Ev HTTP_PROXY , -.Ev http_proxy , -.Ev HTTP_PROXY_AUTH , -.Ev HTTP_REFERER , -.Ev HTTP_USER_AGENT , -.Ev NETRC , -.Ev NO_PROXY -and -.Ev no_proxy . -.Sh SEE ALSO -.Xr fetch 3 -.Sh HISTORY -The -.Nm -command appeared in -.Fx 2.1.5 . -This implementation first appeared in -.Fx 4.1 . -.Sh AUTHORS -.An -nosplit -The original implementation of -.Nm -was done by -.An Jean-Marc Zucconi Aq jmz@FreeBSD.org . -It was extensively re-worked for -.Fx 2.2 -by -.An Garrett Wollman Aq wollman@FreeBSD.org , -and later completely rewritten to use the -.Xr fetch 3 -library by -.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org . -.Sh NOTES -The -.Fl b -and -.Fl t -options are no longer supported and will generate warnings. -They were workarounds for bugs in other OSes which this implementation -does not trigger. -.Pp -One cannot both use the -.Fl h , -.Fl c , -and -.Fl f -options and specify URLs on the command line. diff --git a/minix/commands/netconf/netconf.8 b/minix/commands/netconf/netconf.8 deleted file mode 100644 index 5e574b2ae..000000000 --- a/minix/commands/netconf/netconf.8 +++ /dev/null @@ -1,37 +0,0 @@ -.Dd February 22, 2017 -.Dt NETCONF 8 -.Os -.Sh NAME -.Nm netconf -.Nd configure the network interactively -.Sh SYNOPSIS -.Nm -.Op Fl lh -.Op Fl p Ar prefix -.Sh DESCRIPTION -The -.Nm -utility allows the root user to configure the network in an interactive mode. -.Sh OPTIONS -.Bl -tag -width XpXprefixXX -.It Fl l -Print a list of configurable interfaces. -.It Fl h -Print usage. -.It Fl p Ar prefix -Set a path prefix for configuration files (e.g., /mnt). -.El -.Sh SEE ALSO -.Xr ifconfig.if 5 , -.Xr resolv.conf 5 , -.Xr ifconfig 8 , -.Xr route 8 , -.Xr dhcpcd 8 -.Sh BUGS -IPv6 support is limited at this time. -.Sh AUTHORS -Original version by Thomas Veerman . -.Pp -NetBSD networking rewrite by David van Moolenbroek . -.Pp -Original manual page by Leith Brandeland . diff --git a/minix/commands/netconf/netconf.sh b/minix/commands/netconf/netconf.sh deleted file mode 100644 index 826603460..000000000 --- a/minix/commands/netconf/netconf.sh +++ /dev/null @@ -1,415 +0,0 @@ -#!/bin/sh -# -# netconf 0.2 - Configure network -# -# Changes: -# v0.2: rewrite for NetBSD network infrastructure -# - the primary choice is now for an interface, not a network card; -# - manual driver configuration is now an exception; -# - the menu transition system is slightly more solid; -# - all non-interactive functionality has been removed. -# - -# Get system config -. /etc/rc.conf - -LOCALRC=/usr/etc/rc.local -IFCONF=/etc/ifconfig. -RESOLVCONF=/etc/resolv.conf -HOSTNAME=/etc/hostname.file -USRKBFILE=/.usrkb - -prefix="" -cd="no" # running from cd? -changed="no" # have any ifconfig.if(5) files been changed? - -usage() -{ - cat >&2 <<'EOF' -Usage: - - netconf [-lh] [-p ] - - flags: - -l Print a list of configurable interfaces - -h Print this help file - -p Set a path prefix for all configuration files (e.g., /mnt) -EOF - exit 1 -} - -backup_file() -{ - # Do not make backups if we're running from CD. - if [ "$cd" != "yes" -a -f "$1" ]; then - mv "$1" "$1~" || exit 1 - echo - echo "Backed up $1 to $1~" - fi -} - -select_number() -{ - while true; do - echo -n "$4 [$3] " - read input - case "$input" in - '') - return $3 - ;; - *[!0-9]*) - ;; - *) - [ $input -ge $1 -a $input -le $2 ] && return $input - ;; - esac - done -} - -interfaces() -{ - # Get a list of interfaces that are not virtual (i.e., cloners). There - # is always one virtual interface type ("lo", loopback). - cloners_regex='^('`ifconfig -C | sed 's/ /[0-9]|/g'`'[0-9])' - iflist=`ifconfig -l | tr ' ' '\n' | grep -vE "$cloners_regex"` - - ifcount=0 - ifunconf=0 # the first interface with no configuration file, or 0 - if [ -z "$iflist" ]; then - echo " No network hardware interfaces detected!" - else - for if in $iflist; do - ifcount=$(($ifcount + 1)) - if [ -r $IFCONF$if ]; then - info="($1)" - else - [ $ifunconf -eq 0 ] && ifunconf=$ifcount - info="" - fi - printf "%2d. %-8s %s\n" $ifcount "$if" "$info" - done - fi -} - -do_step1() -{ - echo " -The following network interfaces are available for configuration. These are -interfaces corresponding to network drivers that are currently running. If no -interface is listed for your network card here, then either MINIX 3 does not -support your card, or, if it is not a plug-and-play device, it may require -manual configuration first. - -Please choose the interface you would like to configure, or another option. -" - interfaces "already configured" - echo - manual_choice=$(($ifcount + 1)) - quit_choice=$(($ifcount + 2)) - printf "%2d. Manually configure an ethernet driver\n" $manual_choice - printf "%2d. Quit\n\n" $quit_choice - - default_choice=$ifunconf - [ $default_choice -eq 0 ] && default_choice=$quit_choice - - select_number 1 $quit_choice $default_choice "Interface choice?" - choice=$? - - case $choice in - $manual_choice) - step=do_stepM - ;; - $quit_choice) - ;; - *) - ifchoice="$(echo $iflist | cut -d' ' -f$choice)" - step=do_step2 - esac -} - -do_stepM() -{ - # TODO: it would be nice if this list changed on a per-platform basis.. - echo " -MINIX 3 has drivers for a limited number of older cards that require manual -configuration. They are shown below. Please choose one of the listed options. - - 1. 3Com 501 or 3Com 509 based ISA card (i386) - 2. NE2000, 3Com 503, or WD based ISA card (i386) (emulated by Bochs, Qemu) - - 3. Go back to interface selection - 4. Quit -" - - select_number 1 4 4 "Card choice?" - - case $? in - 1) - driver=dpeth - driverargs="#dpeth_args='DPETH0=port:irq:memory'" - echo " -Note: After installing, edit $LOCALRC to the right configuration." - ;; - 2) - driver=dp8390 - driverargs="dp8390_args='DPETH0=300:9'" - echo " -Note: After installing, edit $LOCALRC to the right configuration. -You may then also have to edit /etc/system.conf.d/dp8390 to match. -For now, the defaults for emulation by Bochs/Qemu have been set." - ;; - 3) - step=do_step1 - return - ;; - 4) - return - ;; - esac - - backup_file "$LOCALRC" - echo "# Generated by netconf(8). Edit as necessary." > $LOCALRC - echo "netdriver='"$driver"'" >> $LOCALRC - echo "$driverargs" >> $LOCALRC - - # $LOCALRC typically expands to /mnt/usr/etc/rc.local, so leave room.. - echo " -A template to start the driver has been written to $LOCALRC . As -noted above, you may have to edit it. Once you are done editing, reboot the -system, after which the driver will be started. Once the driver is running, -you can run 'netconf' to configure the corresponding network interface." -} - -do_step2() -{ - iffile="$IFCONF$ifchoice" - - echo " -Configure interface $ifchoice using DHCP or manually? - -For now, the choice here is primarily about IPv4. With DHCP it is possible to -enable IPv6 as well. Even if the local network has no IPv6 facilities, enabling -IPv6 should do no harm. For IPv6-only mode or any other configuration that is -not supported here, you will have to edit $iffile yourself. - - 1. Automatically using DHCP (IPv4 + IPv6) - 2. Automatically using DHCP (IPv4 only) - 3. Manually (IPv4 only)" - - if [ -r "$iffile" ]; then - echo " 4. Remove current configuration" - remove_choice=4 - goback_choice=5 - quit_choice=6 - else - remove_choice=X - goback_choice=4 - quit_choice=5 - fi - - echo - printf "%2d. Go back to interface selection\n" $goback_choice - printf "%2d. Quit\n\n" $quit_choice - - select_number 1 $quit_choice 1 "Configuration choice?" - - case $? in - 1) - backup_file "$iffile" - echo 'up' > $iffile - echo '!dhcpcd -qM $int' >> $iffile - - echo - echo "Interface $ifchoice configured for DHCP (IPv4 + IPv6)." - step=do_step3 - ;; - 2) - backup_file "$iffile" - echo 'up' > $iffile - echo '!dhcpcd -qM -4 $int' >> $iffile - - echo - echo "Interface $ifchoice configured for DHCP (IPv4 only)." - step=do_step3 - ;; - 3) - # Query user for settings - # - # Some of these settings (hostname, nameservers) do not apply - # to just the selected interface. Still, they are what one has - # to specify for a complete manual configuration. In order to - # make manual configuration of multiple interfaces less - # frustrating in this regard, offer defaults that match what - # may just have been set already. - - echo - - # Hostname - if [ -r $HOSTNAME ]; then - hostname_default=$(cat $HOSTNAME) - else - hostname_default="minix" - fi - echo -n "Hostname [$hostname_default]: " - read hostname - if [ -z "$hostname" ]; then - hostname="$hostname_default" - fi - - # IP address - ip="" - while [ -z "$ip" ]; do - echo -n "IP address []: " - read ip - done - - # Netmask - echo -n "Netmask (optional) []: " - read netmask - [ -n "$netmask" ] && netmask=" netmask $netmask" - - # Gateway (no gateway is fine for local networking) - echo -n "Gateway (optional) []: " - read gateway - - # DNS Servers - dns1_default="$(grep '^nameserver' $RESOLVCONF 2>/dev/null | \ - sed '1q' | awk '{print $2}')" - dns2_default="$(grep '^nameserver' $RESOLVCONF 2>/dev/null | \ - sed '2q;d' | awk '{print $2}')" - - echo -n "Primary DNS Server [$dns1_default]: " - read dns1 - [ -z "$dns1" ] && dns1="$dns1_default" - - if [ -n "$dns1" ]; then - echo -n "Secondary DNS Server (optional) [$dns2_default]: " - read dns2 - [ -z "$dns2" ] && dns2="$dns2_default" - else - dns2="" - fi - - backup_file "$HOSTNAME" - echo "$hostname" > $HOSTNAME - hostname "$hostname" - - backup_file "$iffile" - echo 'up' > $iffile - echo "inet $ip$netmask" >> $iffile - if [ -n "$gateway" ]; then - echo "!route -q add default $gateway" >> $iffile - fi - - if [ -n "$dns1" ]; then - backup_file "$RESOLVCONF" - echo "nameserver $dns1" > $RESOLVCONF - if [ -n "$dns2" ]; then - echo "nameserver $dns2" >> $RESOLVCONF - fi - fi - - echo - echo "Interface $ifchoice configured manually." - step=do_step3 - ;; - $remove_choice) - backup_file "$iffile" - rm -f "$iffile" - echo - echo "Removed configuration for interface $ifchoice." - step=do_step3 - ;; - $goback_choice) - step=do_step1 - ;; - esac -} - -do_step3() -{ - - # We get here only if one of the ifconfig.if(5) files have changed. - changed="yes" - - echo " -Do you want to configure additional interfaces? - -You can also invoke the 'netconf' command as root at any later time. - - 1. Go back to interface selection - 2. Quit -" - - # Note that "quit" is deliberately the default choice: most people will - # want to configure at most one interface, and keep pressing Enter in - # order to make it through the setup procedure as easily as possible. - select_number 1 2 2 "Menu choice?" - - [ $? -eq 1 ] && step=do_step1 -} - -# Parse options -while getopts "p:hl" arg; do - case "$arg" in - p) prefix=$OPTARG; ;; - h) usage ;; - l) echo "The following network hardware interfaces are detected:" - echo - interfaces "configured" - exit 0 - ;; - \?) echo "Unknown option -$OPTARG"; usage ;; - :) echo "Missing required argument for -$OPTARG"; usage ;; - *) usage ;; - esac -done - -if [ -n "$prefix" ] ; then - if [ ! -d $prefix ]; then - echo -e "It seems the supplied prefix (\`$prefix') is invalid." - exit 1 - fi - LOCALRC=$prefix$LOCALRC - IFCONF=$prefix$IFCONF - RESOLVCONF=$prefix$RESOLVCONF - HOSTNAME=$prefix$HOSTNAME -fi - -if [ `whoami` != root ] ; then - echo "Please run netconf as root." - exit 1 -fi - -if ! ifconfig -l >/dev/null 2>&1; then - echo "Unable to obtain a list of interfaces. Is the LWIP service running?" - exit 1 -fi - -# Are we running from CD? -if [ -f "$USRKBFILE" ] ; then - cd="yes" # We are running from CD -fi - -# The interactive program. -step=do_step1 -while [ $step != exit ]; do - proc=$step - step=exit - $proc -done - -# Skip printing this last bit of information if it will not actually work. The -# fact that it will not work on the CD (i.e. from the setup program) at least -# yet, is also the reason why we do not simply issue the command ourselves -# right now. We might reconsider this later. -if [ "$changed" = "yes" -a -z "$prefix" ]; then - echo - echo "One or more of the interface configuration files have been changed." - echo "You can use the command 'service network restart' to reload them now." -fi - -# Aesthetics. -echo - -exit 0 diff --git a/minix/drivers/bus/i2c/arch/earm/omap_i2c.c b/minix/drivers/bus/i2c/arch/earm/omap_i2c.c deleted file mode 100644 index cc571c509..000000000 --- a/minix/drivers/bus/i2c/arch/earm/omap_i2c.c +++ /dev/null @@ -1,876 +0,0 @@ -/* - * This file implements support for i2c on the BeagleBone and BeagleBoard-xM - */ - -/* kernel headers */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* device headers */ -#include - -/* system headers */ -#include -#include -#include - -/* usr headers */ -#include -#include -#include - -/* local headers */ -#include "omap_i2c.h" - -/* - * defines the set of register - * - * Warning: always use the 16-bit variants of read/write/set from mmio.h - * to access these registers. The DM37XX TRM Section 17.6 warns that 32-bit - * accesses can corrupt the register contents. - */ -typedef struct omap_i2c_registers -{ - vir_bytes I2C_REVNB_LO; /* AM335X Only */ - vir_bytes I2C_REVNB_HI; /* AM335X Only */ - vir_bytes I2C_REV; /* DM37XX Only */ - vir_bytes I2C_IE; /* DM37XX Only */ - vir_bytes I2C_STAT; /* DM37XX Only */ - vir_bytes I2C_SYSC; - vir_bytes I2C_IRQSTATUS_RAW; /* AM335X Only */ - vir_bytes I2C_IRQSTATUS; /* AM335X Only */ - vir_bytes I2C_IRQENABLE_SET; /* AM335X Only */ - vir_bytes I2C_IRQENABLE_CLR; /* AM335X Only */ - vir_bytes I2C_WE; - vir_bytes I2C_DMARXENABLE_SET; /* AM335X Only */ - vir_bytes I2C_DMATXENABLE_SET; /* AM335X Only */ - vir_bytes I2C_DMARXENABLE_CLR; /* AM335X Only */ - vir_bytes I2C_DMATXENABLE_CLR; /* AM335X Only */ - vir_bytes I2C_DMARXWAKE_EN; /* AM335X Only */ - vir_bytes I2C_DMATXWAKE_EN; /* AM335X Only */ - vir_bytes I2C_SYSS; - vir_bytes I2C_BUF; - vir_bytes I2C_CNT; - vir_bytes I2C_DATA; - vir_bytes I2C_CON; - vir_bytes I2C_OA; /* AM335X Only */ - vir_bytes I2C_OA0; /* DM37XX Only */ - vir_bytes I2C_SA; - vir_bytes I2C_PSC; - vir_bytes I2C_SCLL; - vir_bytes I2C_SCLH; - vir_bytes I2C_SYSTEST; - vir_bytes I2C_BUFSTAT; - vir_bytes I2C_OA1; - vir_bytes I2C_OA2; - vir_bytes I2C_OA3; - vir_bytes I2C_ACTOA; - vir_bytes I2C_SBLOCK; -} omap_i2c_regs_t; - -/* generic definition an i2c bus */ - -typedef struct omap_i2c_bus -{ - enum bus_types - { AM335X_I2C_BUS, DM37XX_I2C_BUS} bus_type; - phys_bytes mr_base; - phys_bytes mr_size; - vir_bytes mapped_addr; - omap_i2c_regs_t *regs; - uint32_t functional_clock; - uint32_t module_clock; - uint32_t bus_speed; - uint16_t major; - uint16_t minor; - int irq; - int irq_hook_id; - int irq_hook_kernel_id; -} omap_i2c_bus_t; - -/* Define the registers for each chip */ - -static omap_i2c_regs_t am335x_i2c_regs = { - .I2C_REVNB_LO = AM335X_I2C_REVNB_LO, - .I2C_REVNB_HI = AM335X_I2C_REVNB_HI, - .I2C_SYSC = AM335X_I2C_SYSC, - .I2C_IRQSTATUS_RAW = AM335X_I2C_IRQSTATUS_RAW, - .I2C_IRQSTATUS = AM335X_I2C_IRQSTATUS, - .I2C_IRQENABLE_SET = AM335X_I2C_IRQENABLE_SET, - .I2C_IRQENABLE_CLR = AM335X_I2C_IRQENABLE_CLR, - .I2C_WE = AM335X_I2C_WE, - .I2C_DMARXENABLE_SET = AM335X_I2C_DMARXENABLE_SET, - .I2C_DMATXENABLE_SET = AM335X_I2C_DMATXENABLE_SET, - .I2C_DMARXENABLE_CLR = AM335X_I2C_DMARXENABLE_CLR, - .I2C_DMATXENABLE_CLR = AM335X_I2C_DMATXENABLE_CLR, - .I2C_DMARXWAKE_EN = AM335X_I2C_DMARXWAKE_EN, - .I2C_DMATXWAKE_EN = AM335X_I2C_DMATXWAKE_EN, - .I2C_SYSS = AM335X_I2C_SYSS, - .I2C_BUF = AM335X_I2C_BUF, - .I2C_CNT = AM335X_I2C_CNT, - .I2C_DATA = AM335X_I2C_DATA, - .I2C_CON = AM335X_I2C_CON, - .I2C_OA = AM335X_I2C_OA, - .I2C_SA = AM335X_I2C_SA, - .I2C_PSC = AM335X_I2C_PSC, - .I2C_SCLL = AM335X_I2C_SCLL, - .I2C_SCLH = AM335X_I2C_SCLH, - .I2C_SYSTEST = AM335X_I2C_SYSTEST, - .I2C_BUFSTAT = AM335X_I2C_BUFSTAT, - .I2C_OA1 = AM335X_I2C_OA1, - .I2C_OA2 = AM335X_I2C_OA2, - .I2C_OA3 = AM335X_I2C_OA3, - .I2C_ACTOA = AM335X_I2C_ACTOA, - .I2C_SBLOCK = AM335X_I2C_SBLOCK -}; - -static omap_i2c_regs_t dm37xx_i2c_regs = { - .I2C_REV = DM37XX_I2C_REV, - .I2C_IE = DM37XX_I2C_IE, - .I2C_STAT = DM37XX_I2C_STAT, - .I2C_WE = DM37XX_I2C_WE, - .I2C_SYSS = DM37XX_I2C_SYSS, - .I2C_BUF = DM37XX_I2C_BUF, - .I2C_CNT = DM37XX_I2C_CNT, - .I2C_DATA = DM37XX_I2C_DATA, - .I2C_SYSC = DM37XX_I2C_SYSC, - .I2C_CON = DM37XX_I2C_CON, - .I2C_OA0 = DM37XX_I2C_OA0, - .I2C_SA = DM37XX_I2C_SA, - .I2C_PSC = DM37XX_I2C_PSC, - .I2C_SCLL = DM37XX_I2C_SCLL, - .I2C_SCLH = DM37XX_I2C_SCLH, - .I2C_SYSTEST = DM37XX_I2C_SYSTEST, - .I2C_BUFSTAT = DM37XX_I2C_BUFSTAT, - .I2C_OA1 = DM37XX_I2C_OA1, - .I2C_OA2 = DM37XX_I2C_OA2, - .I2C_OA3 = DM37XX_I2C_OA3, - .I2C_ACTOA = DM37XX_I2C_ACTOA, - .I2C_SBLOCK = DM37XX_I2C_SBLOCK -}; - -/* Define the buses available on each chip */ - -static omap_i2c_bus_t am335x_i2c_buses[] = { - {AM335X_I2C_BUS, AM335X_I2C0_BASE, AM335X_I2C0_SIZE, 0, &am335x_i2c_regs, - AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, - BUS_SPEED_400KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, - AM335X_I2C0_IRQ, 1, 1}, - {AM335X_I2C_BUS, AM335X_I2C1_BASE, AM335X_I2C1_SIZE, 0, &am335x_i2c_regs, - AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, - BUS_SPEED_100KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, - AM335X_I2C1_IRQ, 2, 3}, - {AM335X_I2C_BUS, AM335X_I2C2_BASE, AM335X_I2C2_SIZE, 0, &am335x_i2c_regs, - AM335X_FUNCTIONAL_CLOCK, AM335X_MODULE_CLOCK, - BUS_SPEED_100KHz, AM335X_REV_MAJOR, AM335X_REV_MINOR, - AM335X_I2C2_IRQ, 3, 3} -}; - -#define AM335X_OMAP_NBUSES (sizeof(am335x_i2c_buses) / sizeof(omap_i2c_bus_t)) - -static omap_i2c_bus_t dm37xx_i2c_buses[] = { - {DM37XX_I2C_BUS, DM37XX_I2C0_BASE, DM37XX_I2C0_SIZE, 0, &dm37xx_i2c_regs, - DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, - BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, - DM37XX_I2C0_IRQ, 1, 1}, - {DM37XX_I2C_BUS, DM37XX_I2C1_BASE, DM37XX_I2C1_SIZE, 0, &dm37xx_i2c_regs, - DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, - BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, - DM37XX_I2C1_IRQ, 2, 2}, - {DM37XX_I2C_BUS, DM37XX_I2C2_BASE, DM37XX_I2C2_SIZE, 0, &dm37xx_i2c_regs, - DM37XX_FUNCTIONAL_CLOCK, DM37XX_MODULE_CLOCK, - BUS_SPEED_100KHz, DM37XX_REV_MAJOR, DM37XX_REV_MINOR, - DM37XX_I2C2_IRQ, 3, 3} -}; - -#define DM37XX_OMAP_NBUSES (sizeof(dm37xx_i2c_buses) / sizeof(omap_i2c_bus_t)) - -/* Globals */ - -static omap_i2c_bus_t *omap_i2c_buses; /* all available buses for this SoC */ -static omap_i2c_bus_t *omap_i2c_bus; /* the bus selected at start-up */ -static int omap_i2c_nbuses; /* number of buses supported by SoC */ - -/* logging - use with log_warn(), log_info(), log_debug(), log_trace() */ -static struct log log = { - .name = "i2c", - .log_level = LEVEL_INFO, - .log_func = default_log -}; - -/* Local Function Prototypes */ - -/* Implementation of Generic I2C Interface using Bus Specific Code */ -static int omap_i2c_process(minix_i2c_ioctl_exec_t * m); - -/* Bus Specific Code */ -static void omap_i2c_flush(void); -static uint16_t omap_i2c_poll(uint16_t mask); -static int omap_i2c_bus_is_free(void); -static int omap_i2c_soft_reset(void); -static void omap_i2c_bus_init(void); -static void omap_i2c_padconf(int i2c_bus_id); -static void omap_i2c_clkconf(int i2c_bus_id); -static void omap_i2c_intr_enable(void); -static uint16_t omap_i2c_read_status(void); -static void omap_i2c_write_status(uint16_t mask); -static int omap_i2c_read(i2c_addr_t addr, uint8_t * buf, size_t buflen, - int dostop); -static int omap_i2c_write(i2c_addr_t addr, const uint8_t * buf, size_t buflen, - int dostop); - -/* - * Performs the action in minix_i2c_ioctl_exec_t. - */ -static int -omap_i2c_process(minix_i2c_ioctl_exec_t * ioctl_exec) -{ - int r; - - /* - * Zero data bytes transfers are not allowed. The controller treats - * I2C_CNT register value of 0x0 as 65536. This is true for both the - * am335x and dm37xx. Full details in the TRM on the I2C_CNT page. - */ - if (ioctl_exec->iie_buflen == 0) { - return EINVAL; - } - - omap_i2c_flush(); /* clear any garbage in the fifo */ - - /* Check bus busy flag before using the bus */ - r = omap_i2c_bus_is_free(); - if (r == 0) { - log_warn(&log, "Bus is busy\n"); - return EBUSY; - } - - if (ioctl_exec->iie_cmdlen > 0) { - r = omap_i2c_write(ioctl_exec->iie_addr, ioctl_exec->iie_cmd, - ioctl_exec->iie_cmdlen, - !(I2C_OP_READ_P(ioctl_exec->iie_op))); - if (r != OK) { - omap_i2c_soft_reset(); - omap_i2c_bus_init(); - return r; - } - } - - if (I2C_OP_READ_P(ioctl_exec->iie_op)) { - r = omap_i2c_read(ioctl_exec->iie_addr, ioctl_exec->iie_buf, - ioctl_exec->iie_buflen, I2C_OP_STOP_P(ioctl_exec->iie_op)); - } else { - r = omap_i2c_write(ioctl_exec->iie_addr, ioctl_exec->iie_buf, - ioctl_exec->iie_buflen, I2C_OP_STOP_P(ioctl_exec->iie_op)); - } - - if (r != OK) { - omap_i2c_soft_reset(); - omap_i2c_bus_init(); - return r; - } - - return OK; -} - -/* - * Drain the incoming FIFO. - * - * Usually called to clear any garbage that may be in the buffer before - * doing a read. - */ -static void -omap_i2c_flush(void) -{ - int tries; - int status; - - for (tries = 0; tries < 1000; tries++) { - status = omap_i2c_poll(1 << RRDY); - if ((status & (1 << RRDY)) != 0) { /* bytes available for reading */ - - /* consume the byte and throw it away */ - (void) read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA); - - /* clear the read ready flag */ - omap_i2c_write_status(1 << RRDY); - - } else { - break; /* buffer drained */ - } - } -} - -/* - * Poll the status register checking the bits set in 'mask'. - * Returns the status if any bits set or 0x0000 when the timeout is reached. - */ -static uint16_t -omap_i2c_poll(uint16_t mask) -{ - spin_t spin; - uint16_t status; - - /* poll for up to 1 s */ - spin_init(&spin, 1000000); - do { - status = omap_i2c_read_status(); - if ((status & mask) != 0) { /* any bits in mask set */ - return status; - } - - } while (spin_check(&spin)); - - return status; /* timeout reached, abort */ -} - -/* - * Poll Bus Busy Flag until the bus becomes free (return 1) or the timeout - * expires (return 0). - */ -static int -omap_i2c_bus_is_free(void) -{ - spin_t spin; - uint16_t status; - - /* wait for up to 1 second for the bus to become free */ - spin_init(&spin, 1000000); - do { - - status = omap_i2c_read_status(); - if ((status & (1 << BB)) == 0) { - return 1; /* bus is free */ - } - - } while (spin_check(&spin)); - - return 0; /* timeout expired */ -} - -static void -omap_i2c_clkconf(int i2c_bus_id) -{ - clkconf_init(); - - if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { - - clkconf_set(CM_ICLKEN1_CORE, BIT((15 + i2c_bus_id)), - 0xffffffff); - clkconf_set(CM_FCLKEN1_CORE, BIT((15 + i2c_bus_id)), - 0xffffffff); - - } else if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { - - switch (i2c_bus_id) { - case 0: - clkconf_set(CM_WKUP_I2C0_CLKCTRL, BIT(1), 0xffffffff); - break; - case 1: - clkconf_set(CM_PER_I2C1_CLKCTRL, BIT(1), 0xffffffff); - break; - case 2: - clkconf_set(CM_PER_I2C2_CLKCTRL, BIT(1), 0xffffffff); - break; - default: - log_warn(&log, "Invalid i2c_bus_id\n"); - break; - } - } - - clkconf_release(); -} - -static void -omap_i2c_padconf(int i2c_bus_id) -{ - int r; - u32_t pinopts; - - if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { - - /* use the options suggested in starterware driver */ - pinopts = - CONTROL_CONF_SLEWCTRL | CONTROL_CONF_RXACTIVE | - CONTROL_CONF_PUTYPESEL; - - switch (i2c_bus_id) { - case 0: - pinopts |= CONTROL_CONF_MUXMODE(0); - - r = sys_padconf(CONTROL_CONF_I2C0_SDA, 0xffffffff, - pinopts); - if (r != OK) { - log_warn(&log, "padconf failed (r=%d)\n", r); - } - - r = sys_padconf(CONTROL_CONF_I2C0_SCL, 0xffffffff, - pinopts); - if (r != OK) { - log_warn(&log, "padconf failed (r=%d)\n", r); - } - - log_debug(&log, "pinopts=0x%x\n", pinopts); - break; - - case 1: - pinopts |= CONTROL_CONF_MUXMODE(2); - - r = sys_padconf(CONTROL_CONF_SPI0_CS0, 0xffffffff, - pinopts); - if (r != OK) { - log_warn(&log, "padconf failed (r=%d)\n", r); - } - - r = sys_padconf(CONTROL_CONF_SPI0_D1, 0xffffffff, - pinopts); - if (r != OK) { - log_warn(&log, "padconf failed (r=%d)\n", r); - } - log_debug(&log, "pinopts=0x%x\n", pinopts); - break; - - case 2: - pinopts |= CONTROL_CONF_MUXMODE(3); - - r = sys_padconf(CONTROL_CONF_UART1_CTSN, 0xffffffff, - pinopts); - if (r != OK) { - log_warn(&log, "padconf failed (r=%d)\n", r); - } - - r = sys_padconf(CONTROL_CONF_UART1_RTSN, - 0xffffffff, pinopts); - if (r != OK) { - log_warn(&log, "padconf failed (r=%d)\n", r); - } - - log_debug(&log, "pinopts=0x%x\n", pinopts); - break; - - default: - log_warn(&log, "Invalid i2c_bus_id\n"); - break; - } - } - - /* nothing to do for the DM37XX */ -} - -static int -omap_i2c_soft_reset(void) -{ - spin_t spin; - - /* Disable to do soft reset */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, 0); - micro_delay(50000); - - /* Do a soft reset */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SYSC, (1 << SRST)); - - /* Have to temporarily enable I2C to read RDONE */ - set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, (1<mapped_addr + omap_i2c_bus->regs->I2C_SYSS) & (1 << RDONE)) { - return OK; - } - - } while (spin_check(&spin)); - - log_warn(&log, "Tried soft reset, but bus never came back.\n"); - return EIO; -} - -static void -omap_i2c_intr_enable(void) -{ - int r; - uint16_t intmask; - static int policy_set = 0; - static int enabled = 0; - - if (!policy_set) { - r = sys_irqsetpolicy(omap_i2c_bus->irq, 0, - &omap_i2c_bus->irq_hook_kernel_id); - if (r == OK) { - policy_set = 1; - } else { - log_warn(&log, "Couldn't set irq policy\n"); - } - } - - if (policy_set && !enabled) { - r = sys_irqenable(&omap_i2c_bus->irq_hook_kernel_id); - if (r == OK) { - enabled = 1; - } else { - log_warn(&log, "Couldn't enable irq %d (hooked)\n", - omap_i2c_bus->irq); - } - } - - /* According to NetBSD driver and u-boot, these are needed even - * if just using polling (i.e. non-interrupt driver programming). - */ - intmask = 0; - intmask |= (1 << ROVR); - intmask |= (1 << AERR); - intmask |= (1 << XRDY); - intmask |= (1 << RRDY); - intmask |= (1 << ARDY); - intmask |= (1 << NACK); - intmask |= (1 << AL); - - if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQENABLE_SET, intmask); - } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IE, intmask); - } else { - log_warn(&log, "Don't know how to enable interrupts.\n"); - } -} - -static void -omap_i2c_bus_init(void) -{ - - /* Ensure i2c module is disabled before setting prescalar & bus speed */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, 0); - micro_delay(50000); - - /* Disable autoidle */ - set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SYSC, (1<mapped_addr + omap_i2c_bus->regs->I2C_PSC, - ((omap_i2c_bus->functional_clock / omap_i2c_bus->module_clock) - - 1)); - - /* Set the bus speed */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SCLL, - ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - - 7)); - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SCLH, - ((omap_i2c_bus->module_clock / (2 * omap_i2c_bus->bus_speed)) - - 5)); - - /* Set own I2C address */ - if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_OA, I2C_OWN_ADDRESS); - } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_OA0, I2C_OWN_ADDRESS); - } else { - log_warn(&log, "Don't know how to set own address.\n"); - } - - /* Set TX/RX Threshold to 1 and disable I2C DMA */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_BUF, 0x0000); - - /* Bring the i2c module out of reset */ - set16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, (1<bus_type == AM335X_I2C_BUS) { - /* TRM says to use RAW for polling for events */ - status = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQSTATUS_RAW); - } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { - status = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_STAT); - } else { - log_warn(&log, "Don't know how to read i2c bus status.\n"); - } - - return status; -} - -static void -omap_i2c_write_status(uint16_t mask) -{ - if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { - /* write 1's to IRQSTATUS (not RAW) to clear the bits */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_IRQSTATUS, mask); - } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_STAT, mask); - } else { - log_warn(&log, "Don't know how to clear i2c bus status.\n"); - } -} - -static int -omap_i2c_read(i2c_addr_t addr, uint8_t * buf, size_t buflen, int dostop) -{ - int r, i; - uint16_t conopts; - uint16_t pollmask; - uint16_t errmask; - - /* Set address of slave device */ - conopts = 0; - addr &= MAX_I2C_SA_MASK; /* sanitize address (10-bit max) */ - if (addr > 0x7f) { - /* 10-bit extended address in use, need to set XSA */ - conopts |= (1 << XSA); - } - - errmask = 0; - errmask |= (1 << ROVR); - errmask |= (1 << AERR); - errmask |= (1 << NACK); - errmask |= (1 << AL); - - pollmask = 0; - pollmask |= (1 << RRDY); - - /* Set bytes to read and slave address */ - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CNT, buflen); - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SA, addr); - - /* Set control register */ - conopts |= (1 << I2C_EN); /* enabled */ - conopts |= (1 << MST); /* master mode */ - conopts |= (1 << STT); /* start condition */ - - if (dostop != 0) { - conopts |= (1 << STP); /* stop condition */ - } - - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, conopts); - - for (i = 0; i < buflen; i++) { - /* Data to read? */ - r = omap_i2c_poll(pollmask | errmask); - if ((r & errmask) != 0) { - /* only debug log level because i2cscan trigers this */ - log_debug(&log, "Read Error! Status=%x\n", r); - return EIO; - } else if ((r & pollmask) == 0) { - log_warn(&log, "No RRDY Interrupt. Status=%x\n", r); - log_warn(&log, - "Likely cause: bad pinmux or no devices on bus\n"); - return EBUSY; - } - - /* read a byte */ - buf[i] = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA) & 0xff; - - /* clear the read ready flag */ - omap_i2c_write_status(pollmask); - } - - r = omap_i2c_read_status(); - if ((r & (1 << NACK)) != 0) { - log_warn(&log, "NACK\n"); - return EIO; - } - - /* Wait for operation to complete */ - pollmask = (1< 0x7f) { - /* 10-bit extended address in use, need to set XSA */ - conopts |= (1 << XSA); - } - - pollmask = 0; - pollmask |= (1 << XRDY); - - errmask = 0; - errmask |= (1 << ROVR); - errmask |= (1 << AERR); - errmask |= (1 << NACK); - errmask |= (1 << AL); - - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CNT, buflen); - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_SA, addr); - - /* Set control register */ - conopts |= (1 << I2C_EN); /* enabled */ - conopts |= (1 << MST); /* master mode */ - conopts |= (1 << TRX); /* TRX mode */ - conopts |= (1 << STT); /* start condition */ - - if (dostop != 0) { - conopts |= (1 << STP); /* stop condition */ - } - - omap_i2c_write_status(0x7fff); - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_CON, conopts); - - for (i = 0; i < buflen; i++) { - - /* Ready to write? */ - r = omap_i2c_poll(pollmask | errmask); - if ((r & errmask) != 0) { - log_warn(&log, "Write Error! Status=%x\n", r); - return EIO; - } else if ((r & pollmask) == 0) { - log_warn(&log, "Not ready for write? Status=%x\n", r); - return EBUSY; - } - - write16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_DATA, buf[i]); - - /* clear the write ready flag */ - omap_i2c_write_status(pollmask); - } - - r = omap_i2c_read_status(); - if ((r & (1 << NACK)) != 0) { - log_warn(&log, "NACK\n"); - return EIO; - } - - /* Wait for operation to complete */ - pollmask = (1<= omap_i2c_nbuses) { - return EINVAL; - } - - /* select the bus to operate on */ - omap_i2c_bus = &omap_i2c_buses[i2c_bus_id]; - - /* Configure Pins */ - omap_i2c_padconf(i2c_bus_id); - - /* - * Map I2C Registers - */ - - /* Configure memory access */ - mr.mr_base = omap_i2c_bus->mr_base; /* start addr */ - mr.mr_limit = mr.mr_base + omap_i2c_bus->mr_size; /* end addr */ - - /* ask for privileges to access the I2C memory range */ - if (sys_privctl(SELF, SYS_PRIV_ADD_MEM, &mr) != OK) { - panic("Unable to obtain i2c memory range privileges"); - } - - /* map the memory into this process */ - omap_i2c_bus->mapped_addr = (vir_bytes) vm_map_phys(SELF, - (void *) omap_i2c_bus->mr_base, omap_i2c_bus->mr_size); - - if (omap_i2c_bus->mapped_addr == (vir_bytes) MAP_FAILED) { - panic("Unable to map i2c registers"); - } - - /* Enable Clocks */ - omap_i2c_clkconf(i2c_bus_id); - - /* Perform a soft reset of the I2C module to ensure a fresh start */ - r = omap_i2c_soft_reset(); - if (r != OK) { - /* module didn't come back up :( */ - return r; - } - - /* Bring up I2C module */ - omap_i2c_bus_init(); - - /* Get I2C Revision */ - if (omap_i2c_bus->bus_type == AM335X_I2C_BUS) { - /* I2C_REVLO revision: major (bits 10-8), minor (bits 5-0) */ - i2c_rev = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_REVNB_LO); - major = (i2c_rev >> 8) & 0x07; - minor = i2c_rev & 0x3f; - - } else if (omap_i2c_bus->bus_type == DM37XX_I2C_BUS) { - /* I2C_REV revision: major (bits 7-4), minor (bits 3-0) */ - i2c_rev = read16(omap_i2c_bus->mapped_addr + omap_i2c_bus->regs->I2C_REV); - major = (i2c_rev >> 4) & 0x0f; - minor = i2c_rev & 0x0f; - } else { - panic("Don't know how to read i2c revision."); - } - - if (major != omap_i2c_bus->major || minor != omap_i2c_bus->minor) { - log_warn(&log, "Unrecognized value in I2C_REV register.\n"); - log_warn(&log, "Read: 0x%x.0x%x | Expected: 0x%x.0x%x\n", - major, minor, omap_i2c_bus->major, omap_i2c_bus->minor); - } - - /* display i2c revision information for debugging purposes */ - log_debug(&log, "i2c_%d: I2C rev 0x%x.0x%x\n", (i2c_bus_id + 1), - major, minor); - - return OK; -} diff --git a/minix/drivers/bus/pci/pci.h b/minix/drivers/bus/pci/pci.h deleted file mode 100644 index 110a73e8b..000000000 --- a/minix/drivers/bus/pci/pci.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -pci.h - -Created: Jan 2000 by Philip Homburg -*/ - -struct pci_isabridge -{ - u16_t vid; - u16_t did; - int checkclass; - int type; -}; - -struct pci_acl -{ - int inuse; - struct rs_pci acl; -}; - -#define NR_DRIVERS NR_SYS_PROCS - -#define PCI_IB_PIIX 1 /* Intel PIIX compatible ISA bridge */ -#define PCI_IB_VIA 2 /* VIA compatible ISA bridge */ -#define PCI_IB_AMD 3 /* AMD compatible ISA bridge */ -#define PCI_IB_SIS 4 /* SIS compatible ISA bridge */ - -#define PCI_PPB_STD 1 /* Standard PCI-to-PCI bridge */ -#define PCI_PPB_CB 2 /* Cardbus bridge */ -/* Still needed? */ -#define PCI_AGPB_VIA 3 /* VIA compatible AGP bridge */ - -extern int debug; - -extern struct pci_isabridge pci_isabridge[]; -extern struct pci_acl pci_acl[NR_DRIVERS]; - -/* Function prototypes. */ -int sef_cb_init(int type, sef_init_info_t *info); -int map_service(struct rprocpub *rpub); - -int _pci_grant_access(int devind, endpoint_t proc); -int _pci_reserve(int devind, endpoint_t proc, struct rs_pci *aclp); -void _pci_release(endpoint_t proc); - -int _pci_first_dev(struct rs_pci *aclp, int *devindp, u16_t *vidp, - u16_t *didp); -int _pci_next_dev(struct rs_pci *aclp, int *devindp, u16_t *vidp, u16_t - *didp); -int _pci_find_dev(u8_t bus, u8_t dev, u8_t func, int *devindp); - -void _pci_rescan_bus(u8_t busnr); -const char *_pci_dev_name(u16_t vid, u16_t did); - - -int _pci_get_bar(int devind, int port, u32_t *base, u32_t *size, int - *ioflag); -int _pci_slot_name(int devind, char **cpp); -int _pci_ids(int devind, u16_t *vidp, u16_t *didp); - -/* PCI Config Read functions */ -int _pci_attr_r8(int devind, int port, u8_t *vp); -int _pci_attr_r16(int devind, int port, u16_t *vp); -int _pci_attr_r32(int devind, int port, u32_t *vp); - -/* PCI Config Write functions */ -int _pci_attr_w8(int devind, int port, u8_t value); -int _pci_attr_w16(int devind, int port, u16_t value); -int _pci_attr_w32(int devind, int port, u32_t value); - -/* minix hooks into NetBSD PCI IDS DB */ -typedef uint32_t pcireg_t; -const char *pci_baseclass_name(pcireg_t reg); -const char *pci_subclass_name(pcireg_t reg); diff --git a/minix/drivers/storage/fbd/Makefile b/minix/drivers/storage/fbd/Makefile deleted file mode 100644 index 45bb655d1..000000000 --- a/minix/drivers/storage/fbd/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Makefile for the Faulty Block Device (FBD) - -.include - -PROG= fbd -SRCS= fbd.c rule.c action.c - -DPADD+= ${LIBBLOCKDRIVER} ${LIBSYS} -LDADD+= -lblockdriver -lsys -CPPFLAGS+= -DDEBUG=0 - -# The FBD driver requires NetBSD libc. - -.include diff --git a/minix/drivers/storage/mmc/sdhcreg.h b/minix/drivers/storage/mmc/sdhcreg.h deleted file mode 100644 index df6f33125..000000000 --- a/minix/drivers/storage/mmc/sdhcreg.h +++ /dev/null @@ -1,202 +0,0 @@ -/* $NetBSD: sdhcreg.h,v 1.6 2012/03/02 18:20:34 nonaka Exp $ */ -/* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */ - -/* - * Copyright (c) 2006 Uwe Stuehler - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SDHCREG_H_ -#define _SDHCREG_H_ - -/* Host standard register set */ -#define SDHC_DMA_ADDR 0x00 -#define SDHC_BLOCK_SIZE 0x04 -#define SDHC_BLOCK_COUNT 0x06 -#define SDHC_BLOCK_COUNT_MAX 512 -#define SDHC_ARGUMENT 0x08 -#define SDHC_TRANSFER_MODE 0x0c -#define SDHC_MULTI_BLOCK_MODE (1<<5) -#define SDHC_READ_MODE (1<<4) -#define SDHC_AUTO_CMD12_ENABLE (1<<2) -#define SDHC_BLOCK_COUNT_ENABLE (1<<1) -#define SDHC_DMA_ENABLE (1<<0) -#define SDHC_COMMAND 0x0e -/* 14-15 reserved */ -#define SDHC_COMMAND_INDEX_SHIFT 8 -#define SDHC_COMMAND_INDEX_MASK 0x3f -#define SDHC_COMMAND_TYPE_ABORT (3<<6) -#define SDHC_COMMAND_TYPE_RESUME (2<<6) -#define SDHC_COMMAND_TYPE_SUSPEND (1<<6) -#define SDHC_COMMAND_TYPE_NORMAL (0<<6) -#define SDHC_DATA_PRESENT_SELECT (1<<5) -#define SDHC_INDEX_CHECK_ENABLE (1<<4) -#define SDHC_CRC_CHECK_ENABLE (1<<3) -/* 2 reserved */ -#define SDHC_RESP_LEN_48_CHK_BUSY (3<<0) -#define SDHC_RESP_LEN_48 (2<<0) -#define SDHC_RESP_LEN_136 (1<<0) -#define SDHC_NO_RESPONSE (0<<0) -#define SDHC_RESPONSE 0x10 /* - 0x1f */ -#define SDHC_DATA 0x20 -#define SDHC_PRESENT_STATE 0x24 -/* 25-31 reserved */ -#define SDHC_CMD_LINE_SIGNAL_LEVEL (1<<24) -#define SDHC_DAT3_LINE_LEVEL (1<<23) -#define SDHC_DAT2_LINE_LEVEL (1<<22) -#define SDHC_DAT1_LINE_LEVEL (1<<21) -#define SDHC_DAT0_LINE_LEVEL (1<<20) -#define SDHC_WRITE_PROTECT_SWITCH (1<<19) -#define SDHC_CARD_DETECT_PIN_LEVEL (1<<18) -#define SDHC_CARD_STATE_STABLE (1<<17) -#define SDHC_CARD_INSERTED (1<<16) -/* 12-15 reserved */ -#define SDHC_BUFFER_READ_ENABLE (1<<11) -#define SDHC_BUFFER_WRITE_ENABLE (1<<10) -#define SDHC_READ_TRANSFER_ACTIVE (1<<9) -#define SDHC_WRITE_TRANSFER_ACTIVE (1<<8) -/* 3-7 reserved */ -#define SDHC_DAT_ACTIVE (1<<2) -#define SDHC_CMD_INHIBIT_DAT (1<<1) -#define SDHC_CMD_INHIBIT_CMD (1<<0) -#define SDHC_CMD_INHIBIT_MASK 0x0003 -#define SDHC_HOST_CTL 0x28 -#define SDHC_HIGH_SPEED (1<<2) -#define SDHC_ESDHC_8BIT_MODE (1<<2) /* eSDHC */ -#define SDHC_4BIT_MODE (1<<1) -#define SDHC_LED_ON (1<<0) -#define SDHC_POWER_CTL 0x29 -#define SDHC_VOLTAGE_SHIFT 1 -#define SDHC_VOLTAGE_MASK 0x07 -#define SDHC_VOLTAGE_3_3V 0x07 -#define SDHC_VOLTAGE_3_0V 0x06 -#define SDHC_VOLTAGE_1_8V 0x05 -#define SDHC_BUS_POWER (1<<0) -#define SDHC_BLOCK_GAP_CTL 0x2a -#define SDHC_WAKEUP_CTL 0x2b -#define SDHC_CLOCK_CTL 0x2c -#define SDHC_SDCLK_DIV_SHIFT 8 -#define SDHC_SDCLK_DIV_MASK 0xff -#define SDHC_SDCLK_XDIV_SHIFT 6 -#define SDHC_SDCLK_XDIV_MASK 0x3 -#define SDHC_SDCLK_CGM (1<<5) -#define SDHC_SDCLK_DVS_SHIFT 4 -#define SDHC_SDCLK_DVS_MASK 0xf -#define SDHC_SDCLK_ENABLE (1<<2) -#define SDHC_INTCLK_STABLE (1<<1) -#define SDHC_INTCLK_ENABLE (1<<0) -#define SDHC_TIMEOUT_CTL 0x2e -#define SDHC_TIMEOUT_MAX 0x0e -#define SDHC_SOFTWARE_RESET 0x2f -#define SDHC_INIT_ACTIVE (1<<3) -#define SDHC_RESET_MASK 0x5 -#define SDHC_RESET_DAT (1<<2) -#define SDHC_RESET_CMD (1<<1) -#define SDHC_RESET_ALL (1<<0) -#define SDHC_NINTR_STATUS 0x30 -#define SDHC_ERROR_INTERRUPT (1<<15) -#define SDHC_CARD_INTERRUPT (1<<8) -#define SDHC_CARD_REMOVAL (1<<7) -#define SDHC_CARD_INSERTION (1<<6) -#define SDHC_BUFFER_READ_READY (1<<5) -#define SDHC_BUFFER_WRITE_READY (1<<4) -#define SDHC_DMA_INTERRUPT (1<<3) -#define SDHC_BLOCK_GAP_EVENT (1<<2) -#define SDHC_TRANSFER_COMPLETE (1<<1) -#define SDHC_COMMAND_COMPLETE (1<<0) -#define SDHC_NINTR_STATUS_MASK 0x81ff -#define SDHC_EINTR_STATUS 0x32 -#define SDHC_DMA_ERROR (1<<12) -#define SDHC_AUTO_CMD12_ERROR (1<<8) -#define SDHC_CURRENT_LIMIT_ERROR (1<<7) -#define SDHC_DATA_END_BIT_ERROR (1<<6) -#define SDHC_DATA_CRC_ERROR (1<<5) -#define SDHC_DATA_TIMEOUT_ERROR (1<<4) -#define SDHC_CMD_INDEX_ERROR (1<<3) -#define SDHC_CMD_END_BIT_ERROR (1<<2) -#define SDHC_CMD_CRC_ERROR (1<<1) -#define SDHC_CMD_TIMEOUT_ERROR (1<<0) -#define SDHC_EINTR_STATUS_MASK 0x01ff /* excluding vendor signals */ -#define SDHC_NINTR_STATUS_EN 0x34 -#define SDHC_EINTR_STATUS_EN 0x36 -#define SDHC_NINTR_SIGNAL_EN 0x38 -#define SDHC_NINTR_SIGNAL_MASK 0x01ff -#define SDHC_EINTR_SIGNAL_EN 0x3a -#define SDHC_EINTR_SIGNAL_MASK 0x01ff /* excluding vendor signals */ -#define SDHC_CMD12_ERROR_STATUS 0x3c -#define SDHC_CAPABILITIES 0x40 -#define SDHC_VOLTAGE_SUPP_1_8V (1<<26) -#define SDHC_VOLTAGE_SUPP_3_0V (1<<25) -#define SDHC_VOLTAGE_SUPP_3_3V (1<<24) -#define SDHC_DMA_SUPPORT (1<<22) -#define SDHC_HIGH_SPEED_SUPP (1<<21) -#define SDHC_MAX_BLK_LEN_512 0 -#define SDHC_MAX_BLK_LEN_1024 1 -#define SDHC_MAX_BLK_LEN_2048 2 -#define SDHC_MAX_BLK_LEN_4096 3 -#define SDHC_MAX_BLK_LEN_SHIFT 16 -#define SDHC_MAX_BLK_LEN_MASK 0x3 -#define SDHC_BASE_FREQ_SHIFT 8 -#define SDHC_BASE_FREQ_MASK 0x3f -#define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */ -#define SDHC_TIMEOUT_FREQ_SHIFT 0 -#define SDHC_TIMEOUT_FREQ_MASK 0x1f -#define SDHC_MAX_CAPABILITIES 0x48 -#define SDHC_HOST_VER 0xFC -#define SDHC_VVN_MASK 0x0f -#define SDHC_VVN_SHIFT 0x04 -#define SDHC_SVN_MASK 0x0f -#define SDHC_SVN_SHIFT 0x00 -#define SDHC_SLOT_INTR_STATUS 0xfc -#define SDHC_HOST_CTL_VERSION 0xfe -#define SDHC_SPEC_VERS_SHIFT 0 -#define SDHC_SPEC_VERS_MASK 0xff -#define SDHC_VENDOR_VERS_SHIFT 8 -#define SDHC_VENDOR_VERS_MASK 0xff -#define SDHC_DMA_CTL 0x40c /* eSDHC */ -#define SDHC_DMA_SNOOP 0x40 - -/* SDHC_SPEC_VERS */ -#define SDHC_SPEC_VERS_100 0x00 -#define SDHC_SPEC_VERS_200 0x01 -#define SDHC_SPEC_VERS_300 0x02 - -/* SDHC_CAPABILITIES decoding */ -#define SDHC_BASE_FREQ_KHZ(cap) \ - ((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK) * 1000) -#define SDHC_TIMEOUT_FREQ(cap) \ - (((cap) >> SDHC_TIMEOUT_FREQ_SHIFT) & SDHC_TIMEOUT_FREQ_MASK) -#define SDHC_TIMEOUT_FREQ_KHZ(cap) \ - (((cap) & SDHC_TIMEOUT_FREQ_UNIT) ? \ - SDHC_TIMEOUT_FREQ(cap) * 1000: \ - SDHC_TIMEOUT_FREQ(cap)) - -/* SDHC_HOST_CTL_VERSION decoding */ -#define SDHC_SPEC_VERSION(hcv) \ - (((hcv) >> SDHC_SPEC_VERS_SHIFT) & SDHC_SPEC_VERS_MASK) -#define SDHC_VENDOR_VERSION(hcv) \ - (((hcv) >> SDHC_VENDOR_VERS_SHIFT) & SDHC_VENDOR_VERS_MASK) - -#define SDHC_PRESENT_STATE_BITS \ - "\20\31CL\30D3L\27D2L\26D1L\25D0L\24WPS\23CD\22CSS\21CI" \ - "\14BRE\13BWE\12RTA\11WTA\3DLA\2CID\1CIC" -#define SDHC_NINTR_STATUS_BITS \ - "\20\20ERROR\11CARD\10REMOVAL\7INSERTION\6READ\5WRITE" \ - "\4DMA\3GAP\2XFER\1CMD" -#define SDHC_EINTR_STATUS_BITS \ - "\20\11ACMD12\10CL\7DEB\6DCRC\5DT\4CI\3CEB\2CCRC\1CT" -#define SDHC_CAPABILITIES_BITS \ - "\20\33Vdd1.8V\32Vdd3.0V\31Vdd3.3V\30SUSPEND\27DMA\26HIGHSPEED" - -#endif /* _SDHCREG_H_ */ diff --git a/minix/drivers/storage/mmc/sdmmcreg.h b/minix/drivers/storage/mmc/sdmmcreg.h deleted file mode 100644 index d9f3e1c91..000000000 --- a/minix/drivers/storage/mmc/sdmmcreg.h +++ /dev/null @@ -1,355 +0,0 @@ -/* $NetBSD: sdmmcreg.h,v 1.8 2012/01/27 03:07:21 matt Exp $ */ -/* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */ - -/* - * Copyright (c) 2006 Uwe Stuehler - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SDMMCREG_H_ -#define _SDMMCREG_H_ - -/* MMC commands */ /* response type */ -#define MMC_GO_IDLE_STATE 0 /* R0 */ -#define MMC_SEND_OP_COND 1 /* R3 */ -#define MMC_ALL_SEND_CID 2 /* R2 */ -#define MMC_SET_RELATIVE_ADDR 3 /* R1 */ -#define MMC_SWITCH 6 /* R1b */ -#define MMC_SELECT_CARD 7 /* R1 */ -#define MMC_SEND_EXT_CSD 8 /* R1 */ -#define MMC_SEND_CSD 9 /* R2 */ -#define MMC_SEND_CID 10 /* R2 */ -#define MMC_STOP_TRANSMISSION 12 /* R1b */ -#define MMC_SEND_STATUS 13 /* R1 */ -#define MMC_INACTIVE_STATE 15 /* R0 */ -#define MMC_SET_BLOCKLEN 16 /* R1 */ -#define MMC_READ_BLOCK_SINGLE 17 /* R1 */ -#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ -#define MMC_SET_BLOCK_COUNT 23 /* R1 */ -#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ -#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ -#define MMC_PROGRAM_CSD 27 /* R1 */ -#define MMC_SET_WRITE_PROT 28 /* R1b */ -#define MMC_SET_CLR_WRITE_PROT 29 /* R1b */ -#define MMC_SET_SEND_WRITE_PROT 30 /* R1 */ -#define MMC_TAG_SECTOR_START 32 /* R1 */ -#define MMC_TAG_SECTOR_END 33 /* R1 */ -#define MMC_UNTAG_SECTOR 34 /* R1 */ -#define MMC_TAG_ERASE_GROUP_START 35 /* R1 */ -#define MMC_TAG_ERASE_GROUP_END 36 /* R1 */ -#define MMC_UNTAG_ERASE_GROUP 37 /* R1 */ -#define MMC_ERASE 38 /* R1b */ -#define MMC_LOCK_UNLOCK 42 /* R1b */ -#define MMC_APP_CMD 55 /* R1 */ -#define MMC_READ_OCR 58 /* R3 */ - -/* SD commands */ /* response type */ -#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ -#define SD_SEND_SWITCH_FUNC 6 /* R1 */ -#define SD_SEND_IF_COND 8 /* R7 */ - -/* SD application commands */ /* response type */ -#define SD_APP_SET_BUS_WIDTH 6 /* R1 */ -#define SD_APP_OP_COND 41 /* R3 */ -#define SD_APP_SEND_SCR 51 /* R1 */ - -/* OCR bits */ -#define MMC_OCR_MEM_READY (1U<<31)/* memory power-up status bit */ -#define MMC_OCR_HCS (1<<30) -#define MMC_OCR_3_5V_3_6V (1<<23) -#define MMC_OCR_3_4V_3_5V (1<<22) -#define MMC_OCR_3_3V_3_4V (1<<21) -#define MMC_OCR_3_2V_3_3V (1<<20) -#define MMC_OCR_3_1V_3_2V (1<<19) -#define MMC_OCR_3_0V_3_1V (1<<18) -#define MMC_OCR_2_9V_3_0V (1<<17) -#define MMC_OCR_2_8V_2_9V (1<<16) -#define MMC_OCR_2_7V_2_8V (1<<15) -#define MMC_OCR_2_6V_2_7V (1<<14) -#define MMC_OCR_2_5V_2_6V (1<<13) -#define MMC_OCR_2_4V_2_5V (1<<12) -#define MMC_OCR_2_3V_2_4V (1<<11) -#define MMC_OCR_2_2V_2_3V (1<<10) -#define MMC_OCR_2_1V_2_2V (1<<9) -#define MMC_OCR_2_0V_2_1V (1<<8) -#define MMC_OCR_1_9V_2_0V (1<<7) -#define MMC_OCR_1_8V_1_9V (1<<6) -#define MMC_OCR_1_7V_1_8V (1<<5) -#define MMC_OCR_1_6V_1_7V (1<<4) - -/* R1 response type bits */ -#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ -#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */ - -/* 48-bit response decoding (32 bits w/o CRC) */ -#define MMC_R1(resp) ((resp)[0]) -#define MMC_R3(resp) ((resp)[0]) -#define SD_R6(resp) ((resp)[0]) -#define MMC_R7(resp) ((resp)[0]) -#define MMC_SPI_R1(resp) ((resp)[0]) -#define MMC_SPI_R7(resp) ((resp)[1]) - -/* RCA argument and response */ -#define MMC_ARG_RCA(rca) ((rca) << 16) -#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16) - -/* bus width argument */ -#define SD_ARG_BUS_WIDTH_1 0 -#define SD_ARG_BUS_WIDTH_4 2 - -/* EXT_CSD fields */ -#define EXT_CSD_BUS_WIDTH 183 /* WO */ -#define EXT_CSD_HS_TIMING 185 /* R/W */ -#define EXT_CSD_REV 192 /* RO */ -#define EXT_CSD_STRUCTURE 194 /* RO */ -#define EXT_CSD_CARD_TYPE 196 /* RO */ - -/* EXT_CSD field definitions */ -#define EXT_CSD_CMD_SET_NORMAL (1U << 0) -#define EXT_CSD_CMD_SET_SECURE (1U << 1) -#define EXT_CSD_CMD_SET_CPSECURE (1U << 2) - -/* EXT_CSD_BUS_WIDTH */ -#define EXT_CSD_BUS_WIDTH_1 0 /* 1 bit mode */ -#define EXT_CSD_BUS_WIDTH_4 1 /* 4 bit mode */ -#define EXT_CSD_BUS_WIDTH_8 2 /* 8 bit mode */ - -/* EXT_CSD_STRUCTURE */ -#define EXT_CSD_STRUCTURE_VER_1_0 0 /* CSD Version No.1.0 */ -#define EXT_CSD_STRUCTURE_VER_1_1 1 /* CSD Version No.1.1 */ -#define EXT_CSD_STRUCTURE_VER_1_2 2 /* Version 4.1-4.2-4.3 */ - -/* EXT_CSD_CARD_TYPE */ -#define EXT_CSD_CARD_TYPE_26M (1 << 0) -#define EXT_CSD_CARD_TYPE_52M (1 << 1) - -/* MMC_SWITCH access mode */ -#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ -#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */ -#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */ -#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ - -/* SPI mode reports R1/R2(SEND_STATUS) status. */ -#define R1_SPI_IDLE (1 << 0) -#define R1_SPI_ERASE_RESET (1 << 1) -#define R1_SPI_ILLEGAL_COMMAND (1 << 2) -#define R1_SPI_COM_CRC (1 << 3) -#define R1_SPI_ERASE_SEQ (1 << 4) -#define R1_SPI_ADDRESS (1 << 5) -#define R1_SPI_PARAMETER (1 << 6) -/* R1 bit 7 is always zero */ -#define R2_SPI_CARD_LOCKED (1 << 8) -#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ -#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP -#define R2_SPI_ERROR (1 << 10) -#define R2_SPI_CC_ERROR (1 << 11) -#define R2_SPI_CARD_ECC_ERROR (1 << 12) -#define R2_SPI_WP_VIOLATION (1 << 13) -#define R2_SPI_ERASE_PARAM (1 << 14) -#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ -#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE - -/* MMC R2 response (CSD) */ -#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) -#define MMC_CSD_CSDVER_1_0 0 -#define MMC_CSD_CSDVER_1_1 1 -#define MMC_CSD_CSDVER_1_2 2 /* MMC 4.1 - 4.2 - 4.3 */ -#define MMC_CSD_CSDVER_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ -#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) -#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */ -#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */ -#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */ -#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */ -#define MMC_CSD_MMCVER_4_0 4 /* MMC 4.1 - 4.2 - 4.3 */ -#define MMC_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) -#define MMC_CSD_TAAC_MANT(resp) MMC_RSP_BITS((resp), 115, 4) -#define MMC_CSD_TAAC_EXP(resp) MMC_RSP_BITS((resp), 112, 3) -#define MMC_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) -#define MMC_CSD_TRAN_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) -#define MMC_CSD_TRAN_SPEED_MANT(resp) MMC_RSP_BITS((resp), 99, 4) -#define MMC_CSD_TRAN_SPEED_EXP(resp) MMC_RSP_BITS((resp), 96, 3) -#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) -#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) -#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \ - (MMC_CSD_C_SIZE_MULT((resp))+2)) -#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) -#define MMC_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) -#define MMC_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) - -/* MMC v1 R2 response (CID) */ -#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24) -#define MMC_CID_PNM_V1_CPY(resp, pnm) \ - do { \ - (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ - (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ - (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ - (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ - (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ - (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ - (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \ - (pnm)[7] = '\0'; \ - } while (/*CONSTCOND*/0) -#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8) -#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24) -#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8) - -/* MMC v2 R2 response (CID) */ -#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8) -#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16) -#define MMC_CID_PNM_V2_CPY(resp, pnm) \ - do { \ - (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ - (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ - (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ - (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ - (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ - (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ - (pnm)[6] = '\0'; \ - } while (/*CONSTCOND*/0) -#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32) - -/* SD R2 response (CSD) */ -#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) -#define SD_CSD_CSDVER_1_0 0 -#define SD_CSD_CSDVER_2_0 1 -#define SD_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) -#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) -#define SD_CSD_TAAC_EXP(resp) MMC_RSP_BITS((resp), 115, 4) -#define SD_CSD_TAAC_MANT(resp) MMC_RSP_BITS((resp), 112, 3) -#define SD_CSD_TAAC_1_5_MSEC 0x26 -#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) -#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) -#define SD_CSD_SPEED_MANT(resp) MMC_RSP_BITS((resp), 99, 4) -#define SD_CSD_SPEED_EXP(resp) MMC_RSP_BITS((resp), 96, 3) -#define SD_CSD_SPEED_25_MHZ 0x32 -#define SD_CSD_SPEED_50_MHZ 0x5a -#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) -#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ -#define SD_CSD_CCC_BR (1 << 2) /* block read */ -#define SD_CSD_CCC_BW (1 << 4) /* block write */ -#define SD_CSD_CCC_ERACE (1 << 5) /* erase */ -#define SD_CSD_CCC_WP (1 << 6) /* write protection */ -#define SD_CSD_CCC_LC (1 << 7) /* lock card */ -#define SD_CSD_CCC_AS (1 << 8) /*application specific*/ -#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */ -#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */ -#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) -#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) -#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1) -#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1) -#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1) -#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) -#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \ - (SD_CSD_C_SIZE_MULT((resp))+2)) -#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3) -#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3) -#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3) -#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3) -#define SD_CSD_VDD_RW_CURR_100mA 0x7 -#define SD_CSD_VDD_RW_CURR_80mA 0x6 -#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22) -#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10) -#define SD_CSD_V2_BL_LEN 0x9 /* 512 */ -#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) -#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1) -#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */ -#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */ -#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1) -#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) -#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) -#define SD_CSD_RW_BL_LEN_2G 0xa -#define SD_CSD_RW_BL_LEN_1G 0x9 -#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1) -#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1) -#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1) -#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1) -#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1) -#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2) - -/* SD R2 response (CID) */ -#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8) -#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16) -#define SD_CID_PNM_CPY(resp, pnm) \ - do { \ - (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ - (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ - (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ - (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ - (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ - (pnm)[5] = '\0'; \ - } while (/*CONSTCOND*/0) -#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8) -#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32) -#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12) - -/* SCR (SD Configuration Register) */ -#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4) -#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */ -#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4) -#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */ -#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */ -#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */ -#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1) -#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3) -#define SCR_SD_SECURITY_NONE 0 /* no security */ -#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */ -#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */ -#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4) -#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */ -#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */ -#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 32, 16) -#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) - -/* Status of Switch Function */ -#define SFUNC_STATUS_GROUP(status, group) \ - be16toh(__bitfield((uint32_t *)(status), (7 - (group)) << 4, 16)) - -/* Might be slow, but it should work on big and little endian systems. */ - - -/* The macro used to do a (start)-8 to work around a bug in hardware see sdhc.c - * of the openbsd mmc code driver. - */ -#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start), (len)) -static inline int -__bitfield(uint32_t *src, int start, int len) -{ - uint8_t *sp; - uint32_t dst, mask; - int shift, bs, bc; - - if (start < 0 || len < 0 || len > 32) - return 0; - - dst = 0; - mask = len % 32 ? UINT_MAX >> (32 - (len % 32)) : UINT_MAX; - shift = 0; - - while (len > 0) { - sp = (uint8_t *)src + start / 8; - bs = start % 8; - bc = 8 - bs; - if (bc > len) - bc = len; - dst |= (*sp >> bs) << shift; - shift += bc; - start += bc; - len -= bc; - } - - dst &= mask; - return (int)dst; -} - -#endif /* _SDMMCREG_H_ */ diff --git a/minix/drivers/storage/ramdisk/rc b/minix/drivers/storage/ramdisk/rc deleted file mode 100644 index fad33a8a6..000000000 --- a/minix/drivers/storage/ramdisk/rc +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/sh -set -e - -PATH=/sbin:/usr/sbin:/bin:/usr/bin - -FSCK=/bin/fsck_mfs -ACPI=/service/acpi - -if [ X`sysenv arch` = Xi386 ] -then if [ -e $ACPI -a -n "`sysenv acpi`" ] - then - minix-service -c up $ACPI - fi - minix-service -c up /service/pci -dev /dev/pci - - minix-service -c up /service/input -dev /dev/kbdmux - minix-service -c up /service/pckbd || : - - # Start procfs so we can access /proc/pci - mount -t procfs none /proc >/dev/null - - # Do we want to use the virtio block device? - # If not specified, default to yes if the device is found. - if sysenv virtio_blk >/dev/null - then virtio_blk="`sysenv virtio_blk`" - elif grep '^[^ ]* [^ ]* 1AF4:1001[^ ]* ' /proc/pci >/dev/null - then echo "virtio_blk not set, defaulting to using found virtio device." - virtio_blk=yes - fi - - minix-service -cn up /service/floppy -dev /dev/fd0 - if [ X`sysenv ahci` = Xyes ] - then - # this is here temporarily, for testing purposes - minix-service -c up /service/ahci -dev /dev/c0d0 -label ahci_0 -args instance=0 - elif [ X"$virtio_blk" = Xyes ] - then - minix-service -c up /service/virtio_blk -dev /dev/c0d0 -label virtio_blk_0 -args instance=0 - else - minix-service -c up /service/at_wini -dev /dev/c0d0 -label at_wini_0 - minix-service -cr up /service/at_wini -dev /dev/c1d0 -label at_wini_1 -args instance=1 2>/dev/null || : - fi - umount /proc >/dev/null -fi - -if [ X`sysenv arch` = Xearm ] -then echo Starting the mmc driver - minix-service -c up /service/mmc -dev /dev/c0d0 -fi - -# Load ProcFS from the ramdisk to minimize the chance of a desync with the boot -# image services from which it obtains data structures directly. As we move to -# the MIB service, this will eventually become obsolete. -minix-service up /service/procfs || echo "WARNING: couldn't start procfs" - -if sysenv rootdevname >/dev/null -then rootdevname=/dev/`sysenv rootdevname` -else - if ! sysenv cdproberoot >/dev/null && ! sysenv bootramdisk >/dev/null - then echo "rootdevname not set" - exit 1 - fi -fi - -if [ "`sysenv bin_img`" = 1 ] -then - bin_img="-i " -fi - -if sysenv cdproberoot >/dev/null -then - echo - echo 'Looking for boot CD. This may take a minute.' - echo 'Please ignore any error messages.' - echo - rootdevname=$(cdprobe) || { echo 'No CD found'; exit 1; } - export rootdevname -elif [ "$rootdevname" = "/dev/ram" ] -then - ramimagename=/dev/`sysenv ramimagename` - echo "Loading ramdisk from $ramimagename" - loadramdisk "$ramimagename" || echo "WARNING: loadramdisk failed" -fi - -if sysenv bootramdisk >/dev/null -then - rootdevname=imgrd -fi - -echo "Root device name is $rootdevname" - -if ! sysenv cdproberoot >/dev/null -then - if [ -e $FSCK ] - then $FSCK -p $rootdevname - fi -fi - -# Change root from temporary boot ramdisk to the configure root device -if ! sysenv bootramdisk >/dev/null; then - mount -n $bin_img"$rootdevname" / - - # Reopen standard file descriptors, so that we can unmount the ramdisk. - # That is essentially a VFS shortcoming that should be fixed, though.. - exec >/dev/log - exec 2>/dev/log - exec /dev/null; then - exec sh /etc/rc "$@" -fi diff --git a/minix/drivers/storage/vnd/NOTES b/minix/drivers/storage/vnd/NOTES deleted file mode 100644 index 69d0f83aa..000000000 --- a/minix/drivers/storage/vnd/NOTES +++ /dev/null @@ -1,90 +0,0 @@ -Development notes regarding VND. Original document by David van Moolenbroek. - - -DESIGN DECISIONS - -As simple as the VND driver implementation looks, several important decisions -had to be made in the design process. These decisions are listed here. - -Multiple instances instead of a single instance: The decision to spawn a -separate driver instance for each VND unit was not ideologically inspired, but -rather based on a practical issue. Namely, users may reasonably expect to be -able to set up a VND using a backing file that resides on a file system hosted -on another VND. If one single driver instance were to host both VND units, its -implementation would have to perform all its backcalls to VFS asynchronously, -so as to be able to process another incoming request that was initiated as part -of such an ongoing backcall. As of writing, MINIX3 does not support any form of -asynchronous I/O, but this would not even be sufficient: the asynchrony would -have to extend even to the close(2) call that takes place during device -unconfiguration, as this call could spark I/O to another VND device. -Ultimately, using one driver instance per VND unit avoids these complications -altogether, thus making nesting possible with a maximum depth of the number of -VFS threads. Of course, this comes at the cost of having more VND driver -processes; in order to avoid this cost in the common case, driver instances are -dynamically started and stopped by vndconfig(8). - -copyfd(2) instead of openas(2): Compared to the NetBSD interface, the MINIX3 -VND API requires that the user program configuring a device pass in a file -descriptor in the vnd_ioctl structure instead of a pointer to a path name. -While binary compatibility with NetBSD would be impossible anyway (MINIX3 can -not support pointers in IOCTL data structures), providing a path name buffer -would be closer to what NetBSD does. There are two reasons behind the choice to -pass in a file descriptor instead. First, performing an open(2)-like call as -a driver backcall is tricky in terms of avoiding deadlocks in VFS, since it -would by nature violate the VFS locking order. On top of that, special -provisions would have to be added to support opening a file in the context of -another process so that chrooted processes would be supported, for example. -In contrast, copying a file descriptor to a remote process is relatively easy -because there is only one potential deadlock case to cover - that of the given -file descriptor identifying the VFS filp object used to control the very same -device - and VFS need only implement a procedure that very much resembles -sending a file descriptor across a UNIX domain socket. Second, since passing a -file descriptor is effectively passing an object capability, it is easier to -improve the isolation of the VND drivers in the future, as described below. - -No separate control device: The driver uses the same minor (block) device for -configuration and for actual (whole-disk) I/O, instead of exposing a separate -device that exists only for the purpose of configuring the device. The reason -for this is that such a control device simply does not fit the NetBSD -opendisk(3) API. While MINIX3 may at some point implement support for NetBSD's -notion of raw devices, such raw devices are still expected to support I/O, and -that means they cannot be control-only. In this regard, it should be mentioned -that the entire VND infrastructure relies on block caches being invalidated -properly upon (un)configuration of VND units, and that such invalidation -(through the REQ_FLUSH file system request) is currently initiated only by -closing block devices. Support for configuration or I/O through character -devices would thus require more work on that side first. In any case, the -primary downside of not having a separate control device is that handling -access permissions on device open is a bit of a hack in order to keep the -MINIX3 userland happy. - - -FUTURE IMPROVEMENTS - -Currently, the VND driver instances are run as root just and only because the -copyfd(2) call requires root. Obviously, nonroot user processes should never -be able to copy file descriptors from arbitrary processes, and thus, some -security check is required there. However, an access control list for VFS calls -would be a much better solution: in that case, VND driver processes can be -given exclusive rights to the use of the copyfd(2) call, while they can be -given a normal driver UID at the same time. - -In MINIX3's dependability model, drivers are generally not considered to be -malicious. However, the VND case is interesting because it is possible to -isolate individual driver instances to the point of actual "least authority". -The copyfd(2) call currently allows any file descriptor to be copied, but it -would be possible to extend the scheme to let user processes (and vndconfig(8) -in particular) mark the file descriptors that may be the target of a copyfd(2) -call. One of several schemes may be implemented in VFS for this purpose. For -example, each process could be allowed to mark one of its file descriptors as -"copyable" using a new VFS call, and VFS would then allow copyfd(2) only on a -"copyable" file descriptor from a process blocked on a call to the driver that -invoked copyfd(2). This approach precludes hiding a VND driver behind a RAID -or FBD (etc) driver, but more sophisticated approaches can solve that as well. -Regardless of the scheme, the end result would be a situation where the VND -drivers are strictly limited to operating on the resources given to them. - -Note that copyfd(2) was originally called dupfrom(2), and then extended to copy -file descriptors *to* remote processes as well. The latter is not as security -sensitive, but may have to be restricted in a similar way. If this is not -possible, copyfd(2) can always be split into multiple calls. diff --git a/minix/drivers/video/fb/Makefile b/minix/drivers/video/fb/Makefile deleted file mode 100644 index 54bcdff85..000000000 --- a/minix/drivers/video/fb/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -# Makefile for the framebuffer driver. -PROG= fb - -.include "arch/${MACHINE_ARCH}/Makefile.inc" - -SRCS+= fb_edid.c fb.c - -# re-use EDID parsing/validation code from NetBSD. -.PATH: ${NETBSDSRCDIR}/sys/dev/videomode -SRCS+= edid.c pickmode.c videomode.c vesagtf.c - -# Put this dir and the EDID headers (dev/videomode/*.h) in the search path. -CPPFLAGS+= -I${.CURDIR} -I${NETBSDSRCDIR}/sys - -DPADD+= ${LIBCHARDRIVER} ${LIBSYS} -LDADD+= -lchardriver -lsys - -.include diff --git a/minix/include/arch/earm/include/multiboot.h b/minix/include/arch/earm/include/multiboot.h deleted file mode 100644 index 753d49365..000000000 --- a/minix/include/arch/earm/include/multiboot.h +++ /dev/null @@ -1,351 +0,0 @@ -/* $NetBSD: multiboot.h,v 1.8 2009/02/22 18:05:42 ahoka Exp $ */ - -/*- - * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Julio M. Merino Vidal. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef __MULTIBOOT_H__ -#define __MULTIBOOT_H__ - -#if !defined(_KERNEL) && defined(_STANDALONE) - -/* --------------------------------------------------------------------- */ - -/* - * Multiboot header structure. - */ -#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 -#define MULTIBOOT_HEADER_MODS_ALIGNED 0x00000001 -#define MULTIBOOT_HEADER_WANT_MEMORY 0x00000002 -#define MULTIBOOT_HEADER_HAS_VBE 0x00000004 -#define MULTIBOOT_HEADER_HAS_ADDR 0x00010000 - -#if !defined(_LOCORE) -struct multiboot_header { - uint32_t mh_magic; - uint32_t mh_flags; - uint32_t mh_checksum; - - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ - paddr_t mh_header_addr; - paddr_t mh_load_addr; - paddr_t mh_load_end_addr; - paddr_t mh_bss_end_addr; - paddr_t mh_entry_addr; - - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. */ - uint32_t mh_mode_type; - uint32_t mh_width; - uint32_t mh_height; - uint32_t mh_depth; -}; -#endif /* !defined(_LOCORE) */ - -/* - * Symbols defined in locore.S. - */ -#if !defined(_LOCORE) && defined(_KERNEL) -extern struct multiboot_header *Multiboot_Header; -#endif /* !defined(_LOCORE) && defined(_KERNEL) */ - -/* --------------------------------------------------------------------- */ - -/* - * Multiboot information structure. - */ -#define MULTIBOOT_INFO_MAGIC 0x2BADB002 -#define MULTIBOOT_INFO_HAS_MEMORY 0x00000001 -#define MULTIBOOT_INFO_HAS_BOOT_DEVICE 0x00000002 -#define MULTIBOOT_INFO_HAS_CMDLINE 0x00000004 -#define MULTIBOOT_INFO_HAS_MODS 0x00000008 -#define MULTIBOOT_INFO_HAS_AOUT_SYMS 0x00000010 -#define MULTIBOOT_INFO_HAS_ELF_SYMS 0x00000020 -#define MULTIBOOT_INFO_HAS_MMAP 0x00000040 -#define MULTIBOOT_INFO_HAS_DRIVES 0x00000080 -#define MULTIBOOT_INFO_HAS_CONFIG_TABLE 0x00000100 -#define MULTIBOOT_INFO_HAS_LOADER_NAME 0x00000200 -#define MULTIBOOT_INFO_HAS_APM_TABLE 0x00000400 -#define MULTIBOOT_INFO_HAS_VBE 0x00000800 - -#if !defined(_LOCORE) -struct multiboot_info { - uint32_t mi_flags; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MEMORY. */ - uint32_t mi_mem_lower; - uint32_t mi_mem_upper; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_BOOT_DEVICE. */ - uint8_t mi_boot_device_part3; - uint8_t mi_boot_device_part2; - uint8_t mi_boot_device_part1; - uint8_t mi_boot_device_drive; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CMDLINE. */ - char * mi_cmdline; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MODS. */ - uint32_t mi_mods_count; - vaddr_t mi_mods_addr; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_{AOUT,ELF}_SYMS. */ - uint32_t mi_elfshdr_num; - uint32_t mi_elfshdr_size; - vaddr_t mi_elfshdr_addr; - uint32_t mi_elfshdr_shndx; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_MMAP. */ - uint32_t mi_mmap_length; - vaddr_t mi_mmap_addr; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_DRIVES. */ - uint32_t mi_drives_length; - vaddr_t mi_drives_addr; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_CONFIG_TABLE. */ - void * unused_mi_config_table; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_LOADER_NAME. */ - char * mi_loader_name; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_APM. */ - void * unused_mi_apm_table; - - /* Valid if mi_flags sets MULTIBOOT_INFO_HAS_VBE. */ - void * unused_mi_vbe_control_info; - void * unused_mi_vbe_mode_info; - paddr_t unused_mi_vbe_interface_seg; - paddr_t unused_mi_vbe_interface_off; - uint32_t unused_mi_vbe_interface_len; -}; - -/* --------------------------------------------------------------------- */ - -/* - * Drive information. This describes an entry in the drives table as - * pointed to by mi_drives_addr. - */ -struct multiboot_drive { - uint32_t md_length; - uint8_t md_number; - uint8_t md_mode; - uint16_t md_cylinders; - uint8_t md_heads; - uint8_t md_sectors; - - /* The variable-sized 'ports' field comes here, so this structure - * can be longer. */ -}; - -/* --------------------------------------------------------------------- */ - -/* - * Memory mapping. This describes an entry in the memory mappings table - * as pointed to by mi_mmap_addr. - * - * Be aware that mm_size specifies the size of all other fields *except* - * for mm_size. In order to jump between two different entries, you - * have to count mm_size + 4 bytes. - */ -struct multiboot_mmap { - uint32_t mm_size; - uint64_t mm_base_addr; - uint64_t mm_length; - uint32_t mm_type; -}; - -/* - * Modules. This describes an entry in the modules table as pointed - * to by mi_mods_addr. - */ - -struct multiboot_module { - uint32_t mmo_start; - uint32_t mmo_end; - char * mmo_string; - uint32_t mmo_reserved; -}; - -#endif /* !defined(_LOCORE) */ - -/* --------------------------------------------------------------------- */ - -/* - * Prototypes for public functions defined in multiboot.c. - */ -#if !defined(_LOCORE) && defined(_KERNEL) -void multiboot_pre_reloc(struct multiboot_info *); -void multiboot_post_reloc(void); -void multiboot_print_info(void); -bool multiboot_ksyms_addsyms_elf(void); -#endif /* !defined(_LOCORE) */ - -/* --------------------------------------------------------------------- */ -#else /* !defined(_KERNEL) && defined(_STANDALONE) */ -/* LSC FIXME: OLD MINIX DEFINITION: should be removed and replace with - definition above... */ -#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 - -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 - -/* Must pass memory information to OS. */ -#define MULTIBOOT_PAGE_ALIGN 0x00000001 - -#define MULTIBOOT_MEMORY_INFO 0x00000002 - -#define MULTIBOOT_VIDEO_MODE 0x00000004 - -#define MULTIBOOT_AOUT_KLUDGE 0x00010000 - -/* consts used for Multiboot pre-init */ - -#define MULTIBOOT_VIDEO_MODE_EGA 1 - -#define MULTIBOOT_VIDEO_BUFFER 0xB8000 - -/* Usable lower memory chunk has a upper bound */ -#define MULTIBOOT_LOWER_MEM_MAX 0x7f800 - -#define MULTIBOOT_CONSOLE_LINES 25 -#define MULTIBOOT_CONSOLE_COLS 80 - -#define MULTIBOOT_VIDEO_BUFFER_BYTES \ - (MULTIBOOT_CONSOLE_LINES*MULTIBOOT_CONSOLE_COLS*2) - -#define MULTIBOOT_STACK_SIZE 4096 -#define MULTIBOOT_PARAM_BUF_SIZE 1024 - -#define MULTIBOOT_MAX_MODS 20 - -/* Flags to be set in the ’flags’ member of the multiboot info structure. */ - -#define MULTIBOOT_INFO_MEMORY 0x00000001 -#define MULTIBOOT_INFO_MEM_MAP 0x00000040 - -/* Is there a boot device set? */ -#define MULTIBOOT_INFO_BOOTDEV 0x00000002 - -/* Is the command-line defined? */ -#define MULTIBOOT_INFO_CMDLINE 0x00000004 - -/* Are there modules to do something with? */ -#define MULTIBOOT_INFO_MODS 0x00000008 - -#define MULTIBOOT_HIGH_MEM_BASE 0x100000 - -#ifndef __ASSEMBLY__ - -#include -/* The symbol table for a.out. */ -struct multiboot_aout_symbol_table -{ - u32_t tabsize; - u32_t strsize; - u32_t addr; - u32_t reserved; -}; -/* The section header table for ELF. */ -struct multiboot_elf_section_header_table -{ - u32_t num; - u32_t size; - u32_t addr; - u32_t shndx; -}; - -typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; -typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; - -struct multiboot_info -{ - /* Multiboot info version number */ - u32_t flags; - /* Available memory from BIOS */ - u32_t mem_lower_unused; /* minix uses memmap instead */ - u32_t mem_upper_unused; - /* "root" partition */ - u32_t boot_device; - /* Kernel command line */ - u32_t cmdline; - /* Boot-Module list */ - u32_t mi_mods_count; - u32_t mods_addr; - union - { - multiboot_aout_symbol_table_t aout_sym; - multiboot_elf_section_header_table_t elf_sec; - } u; - /* Memory Mapping buffer */ - u32_t mmap_length; - u32_t mmap_addr; - /* Drive Info buffer */ - u32_t drives_length; - u32_t drives_addr; - /* ROM configuration table */ - u32_t config_table; - /* Boot Loader Name */ - u32_t boot_loader_name; - /* APM table */ - u32_t apm_table; - /* Video */ - u32_t vbe_control_info; - u32_t vbe_mode_info; - u16_t vbe_mode; - u16_t vbe_interface_seg; - u16_t vbe_interface_off; - u16_t vbe_interface_len; -}; -typedef struct multiboot_info multiboot_info_t; - -struct multiboot_mod_list -{ - /* Memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ - u32_t mod_start; - u32_t mod_end; - /* Module command line */ - u32_t cmdline; - /* Pad struct to 16 bytes (must be zero) */ - u32_t pad; -}; -typedef struct multiboot_mod_list multiboot_module_t; - -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 -struct multiboot_mmap_entry -{ - u32_t size; - u64_t mm_base_addr; - u64_t mm_length; -#define MULTIBOOT_MEMORY_AVAILABLE 1 -#define MULTIBOOT_MEMORY_RESERVED 2 - u32_t type; -} __attribute__((packed)); -typedef struct multiboot_mmap_entry multiboot_memory_map_t; - -#endif /* __ASSEMBLY__ */ -#endif /* !defined(_KERNEL) && defined(_STANDALONE) */ -#endif /* __MULTIBOOT_H__ */ diff --git a/minix/include/lib.h b/minix/include/lib.h deleted file mode 100644 index 81687ecdc..000000000 --- a/minix/include/lib.h +++ /dev/null @@ -1,52 +0,0 @@ -/* The header is the master header used by the library. - * All the C files in the lib subdirectories include it. - */ - -#ifndef _LIB_H -#define _LIB_H - -/* First come the defines. */ -#include /* tell headers to include NetBSD stuff. */ - -/* The following are so basic, all the lib files get them automatically. */ -#include /* must be first */ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct minix_kerninfo *get_minix_kerninfo(void); - -vir_bytes minix_get_user_sp(void); - -struct ps_strings; /* forward declaration for minix_stack_fill. */ - -void minix_stack_params(const char *path, char * const *argv, - char * const *envp, size_t *stack_size, char *overflow, int *argc, - int *envc); -void minix_stack_fill(const char *path, int argc, char * const *argv, - int envc, char * const *envp, size_t stack_size, char *frame, - int *vsp, struct ps_strings **psp); - -int __execve(const char *_path, char *const _argv[], char *const - _envp[], int _nargs, int _nenvps); -int _syscall(endpoint_t _who, int _syscallnr, message *_msgptr); -void _loadname(const char *_name, message *_msgptr); -int _len(const char *_s); -void _begsig(int _dummy); - -#define _VECTORIO_READ 1 -#define _VECTORIO_WRITE 2 -ssize_t _vectorio_setup(const struct iovec * iov, int iovcnt, char ** ptr, - int op); -void _vectorio_cleanup(const struct iovec * iov, int iovcnt, char * buffer, - ssize_t r, int op); - -#endif /* _LIB_H */ diff --git a/minix/include/minix/bpf.h b/minix/include/minix/bpf.h deleted file mode 100644 index acc38206a..000000000 --- a/minix/include/minix/bpf.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _MINIX_BPF_H -#define _MINIX_BPF_H - -#include - -/* - * MINIX3-specific extensions to the NetBSD Berkeley Packet Filter header. - * These extensions are necessary because NetBSD BPF uses a few ioctl(2) - * structure formats that contain pointers--something that MINIX3 has to avoid, - * due to its memory granting mechanisms. Thus, those ioctl(2) calls have to - * be converted from NetBSD to MINIX3 format. We currently do that in libc. - * This header specifies the numbers and formats for the MINIX3 versions. - * - * See for details on how things work here. - */ - -/* BIOCSETF: set BPF filter program. */ -/* - * This ioctl is an exception, as it is write-only, so we do not need the - * original structure. Also, the size of this structure is currently slightly - * over 4KB, which makes it too big for a regular ioctl call. Thus, we have to - * use a big ioctl call. Note that future changes of BPF_MAXINSNS will - * unfortunately (necessarily) change the ioctl call number. - */ -struct minix_bpf_program { - u_int mbf_len; - struct bpf_insn mbf_insns[BPF_MAXINSNS]; -}; - -#define MINIX_BIOCSETF _IOW_BIG(2, struct minix_bpf_program) - -/* BIOCGDLTLIST: retrieve list of possible data link types. */ -#define MINIX_BPF_MAXDLT 256 - -struct minix_bpf_dltlist { - struct bpf_dltlist mbfl_dltlist; /* MUST be first */ - u_int mbfl_list[MINIX_BPF_MAXDLT]; -}; - -#define MINIX_BIOCGDLTLIST _IOWR('B', 119, struct minix_bpf_dltlist) - -#endif /* !_MINIX_BPF_H */ diff --git a/minix/include/minix/i2c.h b/minix/include/minix/i2c.h deleted file mode 100644 index 75dfb9c37..000000000 --- a/minix/include/minix/i2c.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __MINIX_I2C_H -#define __MINIX_I2C_H - -/* - * Minix I2C /dev Interface. - * - * Same as NetBSD/OpenBSD interface but with a flat struct (i.e. no pointers). - * The NetBSD/OpenBSD interface can still be used on i2c device files. The - * ioctl(2) function will translate to/from the Minix version of the struct. - */ - -#include -#include -#include - -typedef struct minix_i2c_ioctl_exec { - i2c_op_t iie_op; /* operation to perform */ - i2c_addr_t iie_addr; /* address of device */ - uint8_t iie_cmd[I2C_EXEC_MAX_CMDLEN]; /* pointer to command */ - size_t iie_cmdlen; /* length of command */ - uint8_t iie_buf[I2C_EXEC_MAX_BUFLEN]; /* pointer to data buffer */ - size_t iie_buflen; /* length of data buffer */ -} minix_i2c_ioctl_exec_t; - -#define MINIX_I2C_IOCTL_EXEC _IOWR('I', 1, minix_i2c_ioctl_exec_t) - -#endif /* __MINIX_I2C_H */ diff --git a/minix/include/minix/if.h b/minix/include/minix/if.h deleted file mode 100644 index 7a22df45a..000000000 --- a/minix/include/minix/if.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef _MINIX_IF_H -#define _MINIX_IF_H - -#include -#include - -/* - * MINIX3-specific extensions to the network interface headers. These - * extensions are necessary because NetBSD IF uses a few ioctl(2) structure - * formats that contain pointers--something that MINIX3 has to avoid, due to - * its memory granting mechanisms. Thus, those ioctl(2) calls have to be - * converted from NetBSD to MINIX3 format. We currently do that in libc. - * This header specifies the numbers and formats for the MINIX3 versions. - * - * The general idea is that we rewrite the ioctl request data to include both - * the original structure and a buffer for the array of values to which the - * original structure uses a pointer. Important: in those cases, the original - * structure is expected to be the first element of the replacement structure. - * - * There is typically no configured upper bound for the maximum number of - * values in the array, and so we pick size values that are hopefully always - * oversized and yet keep the ioctl sizes within the range of regular ioctls - * (4095 bytes, as per sys/ioccom.h). If there may be larger amounts of data, - * we have to use "big" ioctls. - * - * For the replacement ioctl codes, we use the original ioctl class and number - * with a different size. That should virtually eliminate the possibility of - * accidental collisions. - */ - -/* SIOCGIFMEDIA: retrieve interface media status and types. */ -#define MINIX_IF_MAXMEDIA 256 - -struct minix_ifmediareq { - struct ifmediareq mifm_ifm; /* MUST be first */ - int mifm_list[MINIX_IF_MAXMEDIA]; -}; - -#define MINIX_SIOCGIFMEDIA _IOWR('i', 54, struct minix_ifmediareq) - -/* SIOCIFGCLONERS: retrieve interface "cloners" (virtual types). */ -#define MINIX_IF_MAXCLONERS 128 - -struct minix_if_clonereq { - struct if_clonereq mifcr_ifcr; /* MUST be first */ - char mifcr_buffer[MINIX_IF_MAXCLONERS * IFNAMSIZ]; -}; - -#define MINIX_SIOCIFGCLONERS _IOWR('i', 120, struct minix_if_clonereq) - -#endif /* !_MINIX_IF_H */ diff --git a/minix/include/minix/rmib.h b/minix/include/minix/rmib.h deleted file mode 100644 index 35ea2c079..000000000 --- a/minix/include/minix/rmib.h +++ /dev/null @@ -1,188 +0,0 @@ -#ifndef _MINIX_RMIB_H -#define _MINIX_RMIB_H - -/* - * This header file is for use by services that use the remote MIB (RMIB) - * functionality of libsys. RMIB allows services to mount and handle certain - * subtrees of the MIB service's sysctl tree. - */ - -#include - -/* - * The maximum number of I/O vector elements that can be passed to the - * rmib_vcopyout function. - */ -#define RMIB_IOV_MAX SCPVEC_NR - -/* - * This structure contains a number of less heavily used parameters for handler - * functions, mainly to provide extensibility while limiting argument clutter. - */ -struct rmib_call { - endpoint_t call_endpt; /* endpoint of the user process */ - const int *call_oname; /* original full name of the request */ - const int *call_name; /* remaining part of the name */ - unsigned int call_namelen; /* length of the remaining name part */ - unsigned int call_flags; /* RMIB_FLAG_ call flags */ - uint32_t call_rootver; /* version of all nodes in subtree */ - uint32_t call_treever; /* version of the entire MIB tree */ -}; - -/* - * Call flags. - * - * TODO: this is effectively a flag used on the wire. This should be turned - * into a proper definition shared with the MIB service. As long as we have - * only one flag anyway, this is not exactly urgent though. - */ -#define RMIB_FLAG_AUTH 0x1 /* user has superuser privileges */ - -struct rmib_node; -struct rmib_oldp; -struct rmib_newp; - -typedef ssize_t (*rmib_func_ptr)(struct rmib_call *, struct rmib_node *, - struct rmib_oldp *, struct rmib_newp *); - -/* - * Indirect node, used for sparse nodes. Sparse nodes are node-type nodes with - * the CTLFLAG_SPARSE flag set. A sparse node points not to an array of child - * nodes (using rnode_cptr), but to a array of {id,child pointer} elements - * (using rnode_icptr). At the cost of O(n) lookups, sparse nodes save memory. - * Currently for presentation reasons only, indirect lists must be sorted - * ascending by node identifiers. They may also not have ID duplicates, may not - * have NULL node pointers, and may not point to nodes with zero flags fields. - */ -#define CTLFLAG_SPARSE CTLFLAG_ROOT /* overloaded NetBSD flag */ - -struct rmib_indir { - unsigned int rindir_id; /* node identifier */ - struct rmib_node *rindir_node; /* pointer to actual node */ -}; - -/* - * The central structure for remote MIB nodes. This is essentially a somewhat - * cut-down version of the node structure used within the MIB service. See the - * source code of that service for several details that apply here as well. - * The 'rnode_' prefix makes it possible to include both this header file and - * the MIB service's internal header file at once--neat if useless. - */ -struct rmib_node { - uint32_t rnode_flags; /* CTLTYPE_ type and CTLFLAG_ flags */ - size_t rnode_size; /* size of associated data */ - union ixfer_rnode_val_u { - bool rvu_bool; /* immediate boolean */ - int rvu_int; /* immediate integer */ - u_quad_t rvu_quad; /* immediate quad */ - uint32_t rvu_clen; /* number of actual children */ - } rnode_val_u; - union pxfer_rnode_ptr_u { - void *rpu_data; /* struct or string data pointer */ - struct rmib_node *rpu_cptr; /* child node array */ - struct rmib_indir *rpu_icptr; /* indirect child node array */ - } rnode_ptr_u; - rmib_func_ptr rnode_func; /* handler function */ - const char *rnode_name; /* node name string */ - const char *rnode_desc; /* node description (may be NULL) */ -}; -#define rnode_bool rnode_val_u.rvu_bool -#define rnode_int rnode_val_u.rvu_int -#define rnode_quad rnode_val_u.rvu_quad -#define rnode_clen rnode_val_u.rvu_clen -#define rnode_data rnode_ptr_u.rpu_data -#define rnode_cptr rnode_ptr_u.rpu_cptr -#define rnode_icptr rnode_ptr_u.rpu_icptr - -/* Various macros to initialize nodes at compile time. */ -#define RMIB_NODE(f,t,n,d) { \ - .rnode_flags = CTLTYPE_NODE | CTLFLAG_READONLY | \ - CTLFLAG_PERMANENT | f, \ - .rnode_size = __arraycount(t), \ - .rnode_cptr = t, \ - .rnode_name = n, \ - .rnode_desc = d \ -} -#define RMIB_SNODE(f,t,n,d) { \ - .rnode_flags = CTLTYPE_NODE | CTLFLAG_READONLY | \ - CTLFLAG_PERMANENT | CTLFLAG_SPARSE | f, \ - .rnode_size = 0, \ - .rnode_icptr = t, \ - .rnode_name = n, \ - .rnode_desc = d \ -} -#define RMIB_FUNC(f,s,fp,n,d) { \ - .rnode_flags = CTLFLAG_PERMANENT | f, \ - .rnode_size = s, \ - .rnode_func = fp, \ - .rnode_name = n, \ - .rnode_desc = d \ -} -#define RMIB_BOOL(f,b,n,d) { \ - .rnode_flags = CTLTYPE_BOOL | CTLFLAG_PERMANENT | \ - CTLFLAG_IMMEDIATE | f, \ - .rnode_size = sizeof(bool), \ - .rnode_bool = b, \ - .rnode_name = n, \ - .rnode_desc = d \ -} -#define RMIB_INT(f,i,n,d) { \ - .rnode_flags = CTLTYPE_INT | CTLFLAG_PERMANENT | \ - CTLFLAG_IMMEDIATE | f, \ - .rnode_size = sizeof(int), \ - .rnode_int = i, \ - .rnode_name = n, \ - .rnode_desc = d \ -} -#define RMIB_QUAD(f,q,n,d) { \ - .rnode_flags = CTLTYPE_QUAD | CTLFLAG_PERMANENT | \ - CTLFLAG_IMMEDIATE | f, \ - .rnode_size = sizeof(u_quad_t), \ - .rnode_quad = q, \ - .rnode_name = n, \ - .rnode_desc = d \ -} -#define _RMIB_DATA(f,s,p,n,d) { \ - .rnode_flags = CTLFLAG_PERMANENT | f, \ - .rnode_size = s, \ - .rnode_data = __UNCONST(p), \ - .rnode_name = n, \ - .rnode_desc = d \ -} -/* - * The following macros really require a pointer to the proper data type; weird - * casts may not trigger compiler warnings but do allow for memory corruption. - * The first three need to be passed a pointer to a bool, int, and u_quad_t, - * respectively. RMIB_STRING needs a pointer to a character array, so that - * sizeof(array) yields the proper size. Since RMIB_STRUCT may be given a - * pointer to either a structure or an array, it must also be given a size. - */ -#define RMIB_BOOLPTR(f,p,n,d) _RMIB_DATA(CTLTYPE_BOOL | f, sizeof(*p), p, n, d) -#define RMIB_INTPTR(f,p,n,d) _RMIB_DATA(CTLTYPE_INT | f, sizeof(*p), p, n, d) -#define RMIB_QUADPTR(f,p,n,d) _RMIB_DATA(CTLTYPE_QUAD | f, sizeof(*p), p, n, d) -#define RMIB_STRING(f,p,n,d) \ - _RMIB_DATA(CTLTYPE_STRING | f, sizeof(p), p, n, d) -#define RMIB_STRUCT(f,s,p,n,d) _RMIB_DATA(CTLTYPE_STRUCT | f, s, p, n, d) - -/* Shortcut flag macros. */ -#define RMIB_RO CTLFLAG_READONLY /* shortcut for read-only nodes */ -#define RMIB_RW CTLFLAG_READWRITE /* shortcut for read-write nodes */ - -/* Function prototypes. */ -int rmib_register(const int * name, unsigned int namelen, struct rmib_node *); -int rmib_deregister(struct rmib_node *); -void rmib_reregister(void); -void rmib_reset(void); -void rmib_process(const message *, int); - -int rmib_inrange(struct rmib_oldp *, size_t); -size_t rmib_getoldlen(struct rmib_oldp *); -ssize_t rmib_copyout(struct rmib_oldp *, size_t, const void * __restrict, - size_t); -ssize_t rmib_vcopyout(struct rmib_oldp *, size_t, const iovec_t *, - unsigned int); -int rmib_copyin(struct rmib_newp * __restrict, void * __restrict, size_t); -ssize_t rmib_readwrite(struct rmib_call *, struct rmib_node *, - struct rmib_oldp *, struct rmib_newp *); - -#endif /* !_MINIX_RMIB_H */ diff --git a/minix/include/minix/rs.h b/minix/include/minix/rs.h deleted file mode 100644 index f2e6db637..000000000 --- a/minix/include/minix/rs.h +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef RS_H -#define RS_H - -/* -minix/rs.h - -Interface to the reincarnation server -*/ - -#include -#include -#include - -#define SERVICE_LOGIN "service" /* passwd file entry for services */ - -/* The following definition should be kept in sync with the actual - * /etc/master.passwd value for SERVICE_LOGIN for now, and removed altogether - * once we are able to obtain its value dynamically everywhere. The value has - * been chosen so as to avoid creating conflicts with future NetBSD additions - * to the password files, although one can never be sure. - */ -#define SERVICE_UID 999 /* user ID for services */ - -/* RSS definitions. */ -#define RSS_NR_IRQ 16 -#define RSS_NR_IO 16 -#define RSS_IRQ_ALL (RSS_NR_IRQ+1) -#define RSS_IO_ALL (RSS_NR_IO+1) -#define RSS_IPC_ALL "IPC_ALL" -#define RSS_IPC_ALL_SYS "IPC_ALL_SYS" - -/* RSS flags. */ -#define RSS_COPY 0x01 /* keep an in-memory copy of the binary */ -#define RSS_REUSE 0x04 /* Try to reuse previously copied binary */ -#define RSS_NOBLOCK 0x08 /* unblock caller immediately */ -#define RSS_REPLICA 0x10 /* keep a replica of the service */ -#define RSS_BATCH 0x20 /* batch mode */ -#define RSS_SELF_LU 0x40 /* perform self update */ -#define RSS_ASR_LU 0x80 /* perform ASR update */ -#define RSS_FORCE_SELF_LU 0x0100 /* force self update */ -#define RSS_PREPARE_ONLY_LU 0x0200 /* request prepare-only update */ -#define RSS_FORCE_INIT_CRASH 0x0400 /* force crash at initialization time (for debugging) */ -#define RSS_FORCE_INIT_FAIL 0x0800 /* force failure at initialization time (for debugging) */ -#define RSS_FORCE_INIT_TIMEOUT 0x1000 /* force timeout at initialization time (for debugging) */ -#define RSS_FORCE_INIT_DEFCB 0x2000 /* force default cb at initialization time (for debugging) */ -#define RSS_SYS_BASIC_CALLS 0x4000 /* include basic kernel calls */ -#define RSS_VM_BASIC_CALLS 0x8000 /* include basic vm calls */ -#define RSS_NOMMAP_LU 0x10000 /* don't inherit mmapped regions */ -#define RSS_DETACH 0x20000 /* detach on update/restart */ -#define RSS_NORESTART 0x40000 /* don't restart */ -#define RSS_FORCE_INIT_ST 0x80000 /* force state transfer at initialization time */ -#define RSS_NO_BIN_EXP 0x100000 /* suppress binary exponential offset */ - -/* Common definitions. */ -#define RS_NR_CONTROL 8 -#define RS_NR_PCI_DEVICE 32 -#define RS_NR_PCI_CLASS 4 -#define RS_MAX_LABEL_LEN 16 -#define RS_MAX_IPCF_STR_LEN (RS_MAX_LABEL_LEN+12) -#define RS_MAX_IPC_FILTERS 4 - -/* CPU special values */ -#define RS_CPU_DEFAULT -1 /* use the default cpu or do not change the current one */ -#define RS_CPU_BSP -2 /* use the bootstrap cpu */ - -/* Labels are copied over separately. */ -struct rss_label -{ - char *l_addr; - size_t l_len; -}; - -struct rs_pci_id { - u16_t vid; - u16_t did; - u16_t sub_vid; - u16_t sub_did; -}; -#define NO_SUB_VID 0xffff -#define NO_SUB_DID 0xffff - -struct rs_pci_class { - u32_t pciclass; - u32_t mask; -}; - -/* State-related data. */ -struct rs_ipc_filter_el { - int flags; - char m_label[RS_MAX_LABEL_LEN]; - int m_type; -}; -struct rs_state_data { - size_t size; - void *ipcf_els; - size_t ipcf_els_size; - int ipcf_els_gid; - void *eval_addr; - size_t eval_len; - int eval_gid; -}; - -/* Arguments needed to start a new driver or server */ -struct rs_start -{ - unsigned rss_flags; - char *rss_cmd; - size_t rss_cmdlen; - uid_t rss_uid; - endpoint_t rss_sigmgr; - endpoint_t rss_scheduler; - int rss_priority; - int rss_quantum; - int rss_major; - long rss_period; - char *rss_script; - size_t rss_scriptlen; - long rss_asr_count; - long rss_restarts; - long rss_heap_prealloc_bytes; - long rss_map_prealloc_bytes; - int rss_nr_irq; - int rss_irq[RSS_NR_IRQ]; - int rss_nr_io; - struct { unsigned base; unsigned len; } rss_io[RSS_NR_IO]; - int rss_nr_pci_id; - struct rs_pci_id rss_pci_id[RS_NR_PCI_DEVICE]; - int rss_nr_pci_class; - struct rs_pci_class rss_pci_class[RS_NR_PCI_CLASS]; - bitchunk_t rss_system[SYS_CALL_MASK_SIZE]; - struct rss_label rss_label; - struct rss_label rss_trg_label; - char *rss_ipc; - size_t rss_ipclen; - bitchunk_t rss_vm[VM_CALL_MASK_SIZE]; - int rss_nr_control; - struct rss_label rss_control[RS_NR_CONTROL]; - struct rs_state_data rss_state_data; - int devman_id; - char *rss_progname; - size_t rss_prognamelen; - int rss_nr_domain; - int rss_domain[NR_DOMAIN]; - /* - * SMP specific data - * - * must be at the end of the structure for binary compatibility with - * non-smp sysytems - */ - int rss_cpu; -}; - -/* ACL information for access to PCI devices */ -struct rs_pci -{ - char rsp_label[RS_MAX_LABEL_LEN]; - int rsp_endpoint; - int rsp_nr_device; - struct rs_pci_id rsp_device[RS_NR_PCI_DEVICE]; - int rsp_nr_class; - struct rs_pci_class rsp_class[RS_NR_PCI_CLASS]; -}; - -/* Definition of a public entry of the system process table. */ -struct rprocpub { - short in_use; /* set when the entry is in use */ - unsigned sys_flags; /* sys flags */ - endpoint_t endpoint; /* process endpoint number */ - endpoint_t old_endpoint; /* old instance endpoint number (for VM, when updating) */ - endpoint_t new_endpoint; /* new instance endpoint number (for VM, when updating) */ - - devmajor_t dev_nr; /* major device number or NO_DEV */ - int nr_domain; /* number of socket driver domains */ - int domain[NR_DOMAIN]; /* set of socket driver domains */ - - char label[RS_MAX_LABEL_LEN]; /* label of this service */ - char proc_name[RS_MAX_LABEL_LEN]; /* process name of this service */ - - bitchunk_t vm_call_mask[VM_CALL_MASK_SIZE]; /* vm call mask */ - - struct rs_pci pci_acl; /* pci acl */ - int devman_id; -}; - -/* Return whether the given boot process is a user process, as opposed to a - * system process. Only usable by core services during SEF initialization. - */ -#define IS_RPUB_BOOT_USR(rpub) ((rpub)->endpoint == INIT_PROC_NR) - -/* Sys flag values. */ -#define SF_CORE_SRV 0x001 /* set for core system services */ -#define SF_SYNCH_BOOT 0X002 /* set when process needs synch boot init */ -#define SF_NEED_COPY 0x004 /* set when process needs copy to start */ -#define SF_USE_COPY 0x008 /* set when process has a copy in memory */ -#define SF_NEED_REPL 0x010 /* set when process needs replica to start */ -#define SF_USE_REPL 0x020 /* set when process has a replica */ -#define SF_VM_UPDATE 0x040 /* set when process needs vm update */ -#define SF_VM_ROLLBACK 0x080 /* set when vm update is a rollback */ -#define SF_VM_NOMMAP 0x100 /* set when vm update ignores mmapped regions */ -#define SF_USE_SCRIPT 0x200 /* set when process has restart script */ -#define SF_DET_RESTART 0x400 /* set when process detaches on restart */ -#define SF_NORESTART 0x800 /* set when process should not be restarted */ -#define SF_NO_BIN_EXP 0x1000 /* set when we should ignore binary exp. offset */ - -#define IMM_SF \ - (SF_NO_BIN_EXP | SF_CORE_SRV | SF_SYNCH_BOOT | SF_NEED_COPY | SF_NEED_REPL) /* immutable */ - -int minix_rs_lookup(const char *name, endpoint_t *value); - -#endif diff --git a/minix/include/minix/sysctl.h b/minix/include/minix/sysctl.h deleted file mode 100644 index 39cfb2387..000000000 --- a/minix/include/minix/sysctl.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef _MINIX_SYSCTL_H -#define _MINIX_SYSCTL_H - -/* MINIX3-specific sysctl(2) extensions. */ - -#include -#include - -/* Special values. */ -#define SYSCTL_NODE_FN ((sysctlfn)0x1) /* node is function-driven */ - -/* - * The top-level MINIX3 identifier is quite a bit beyond the last top-level - * identifier in use by NetBSD, because NetBSD may add more later, and we do - * not want conflicts: this definition is part of the MINIX3 ABI. - */ -#define CTL_MINIX 32 - -#if CTL_MAXID > CTL_MINIX -#error "CTL_MAXID has grown too large!" -#endif - -/* - * The identifiers below follow the standard sysctl naming scheme, which means - * care should be taken not to introduce clashes with other definitions - * elsewhere. On the upside, not many places need to include this header file. - */ -#define MINIX_TEST 0 -#define MINIX_MIB 1 -#define MINIX_PROC 2 -#define MINIX_LWIP 3 - -/* - * These identifiers, under MINIX_TEST, are used by test87 to test the MIB - * service. - */ -#define TEST_INT 0 -#define TEST_BOOL 1 -#define TEST_QUAD 2 -#define TEST_STRING 3 -#define TEST_STRUCT 4 -#define TEST_PRIVATE 5 -#define TEST_ANYWRITE 6 -#define TEST_DYNAMIC 7 -#define TEST_SECRET 8 -#define TEST_PERM 9 -#define TEST_DESTROY1 10 -#define TEST_DESTROY2 11 - -#define SECRET_VALUE 0 - -/* Identifiers for subnodes of MINIX_MIB. */ -#define MIB_NODES 1 -#define MIB_OBJECTS 2 -#define MIB_REMOTES 3 - -/* Identifiers for subnodes of MINIX_PROC. */ -#define PROC_LIST 1 -#define PROC_DATA 2 - -/* Structure used for PROC_LIST. Not part of the ABI. Used by ProcFS only. */ -struct minix_proc_list { - uint32_t mpl_flags; /* process flags (MPLF_) */ - pid_t mpl_pid; /* process PID */ - uid_t mpl_uid; /* effective user ID */ - gid_t mpl_gid; /* effective group ID */ -}; -#define MPLF_IN_USE 0x01 /* process slot is in use */ -#define MPLF_ZOMBIE 0x02 /* process is a zombie */ - -/* Structure used for PROC_DATA. Not part of the ABI. Used by ProcFS only. */ -struct minix_proc_data { - endpoint_t mpd_endpoint; /* process endpoint */ - uint32_t mpd_flags; /* procses flags (MPDF_) */ - endpoint_t mpd_blocked_on; /* blocked on other process, or NONE */ - uint32_t mpd_priority; /* current process priority */ - uint32_t mpd_user_time; /* user time, in clock ticks */ - uint32_t mpd_sys_time; /* system time, in clock ticks */ - uint64_t mpd_cycles; /* cycles spent by the process */ - uint64_t mpd_kipc_cycles; /* cycles spent on kernel IPC */ - uint64_t mpd_kcall_cycles; /* cycles spent on kernel calls */ - uint32_t mpd_nice; /* nice value */ - char mpd_name[16]; /* short process name */ -}; -#define MPDF_SYSTEM 0x01 /* process is a system service */ -#define MPDF_ZOMBIE 0x02 /* process is a zombie */ -#define MPDF_RUNNABLE 0x04 /* process is runnable */ -#define MPDF_STOPPED 0x08 /* process is stopped */ - -/* - * Expose sysctl(2) to system services (ProcFS in particular), so as to avoid - * including the CTL_USER subtree handling of sysctl(3) as well. - */ -int __sysctl(const int *, unsigned int, void *, size_t *, const void *, - size_t); - -#endif /* !_MINIX_SYSCTL_H */ diff --git a/minix/include/minix/usb_ch9.h b/minix/include/minix/usb_ch9.h deleted file mode 100644 index 6d994a940..000000000 --- a/minix/include/minix/usb_ch9.h +++ /dev/null @@ -1,217 +0,0 @@ -#ifndef MINIX_USB_CH9_H -#define MINIX_USB_CH9_H -/* - * Copyright (c) 1998 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Lennart Augustsson (lennart@augustsson.net) at - * Carlstedt Research & Technology. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the NetBSD - * Foundation, Inc. and its contributors. - * 4. Neither the name of The NetBSD Foundation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/* USB DESCRIPTORS */ -/* - * The USB records contain some unaligned little-endian word - * components. The U[SG]ETW macros take care of both the alignment - * and endian problem and should always be used to access non-byte - * values. - */ - -#include - -typedef u8_t uByte; -typedef u8_t uWord[2]; -typedef u8_t uDWord[4]; - -#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) - -#if 1 -#define UGETW(w) ((w)[0] | ((w)[1] << 8)) -#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) -#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24)) -#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \ - (w)[1] = (u_int8_t)((v) >> 8), \ - (w)[2] = (u_int8_t)((v) >> 16), \ - (w)[3] = (u_int8_t)((v) >> 24)) -#else -/* - * On little-endian machines that can handle unanliged accesses - * (e.g. i386) these macros can be replaced by the following. - */ -#define UGETW(w) (*(u_int16_t *)(w)) -#define USETW(w,v) (*(u_int16_t *)(w) = (v)) -#define UGETDW(w) (*(u_int32_t *)(w)) -#define USETDW(w,v) (*(u_int32_t *)(w) = (v)) -#endif - -#define UPACKED __attribute__((__packed__)) - -/* Requests */ -#define UR_GET_STATUS 0x00 -#define UR_CLEAR_FEATURE 0x01 -#define UR_SET_FEATURE 0x03 -#define UR_SET_ADDRESS 0x05 -#define UR_GET_DESCRIPTOR 0x06 -#define UDESC_DEVICE 0x01 -#define UDESC_CONFIG 0x02 -#define UDESC_STRING 0x03 -#define USB_LANGUAGE_TABLE 0x00 /* language ID string index */ -#define UDESC_INTERFACE 0x04 -#define UDESC_ENDPOINT 0x05 -#define UDESC_DEVICE_QUALIFIER 0x06 -#define UDESC_OTHER_SPEED_CONFIGURATION 0x07 -#define UDESC_INTERFACE_POWER 0x08 -#define UDESC_OTG 0x09 -#define UDESC_DEBUG 0x0A -#define UDESC_IFACE_ASSOC 0x0B /* interface association */ -#define UDESC_BOS 0x0F /* binary object store */ -#define UDESC_DEVICE_CAPABILITY 0x10 -#define UDESC_CS_DEVICE 0x21 /* class specific */ -#define UDESC_CS_CONFIG 0x22 -#define UDESC_CS_STRING 0x23 -#define UDESC_CS_INTERFACE 0x24 -#define UDESC_CS_ENDPOINT 0x25 -#define UDESC_HUB 0x29 -#define UDESC_ENDPOINT_SS_COMP 0x30 /* super speed */ -#define UR_SET_DESCRIPTOR 0x07 -#define UR_GET_CONFIG 0x08 -#define UR_SET_CONFIG 0x09 -#define UR_GET_INTERFACE 0x0a -#define UR_SET_INTERFACE 0x0b -#define UR_SYNCH_FRAME 0x0c -#define UR_SET_SEL 0x30 -#define UR_ISOCH_DELAY 0x31 - - - -typedef struct { - uByte bLength; - uByte bDescriptorType; - uByte bDescriptorSubtype; -} UPACKED usb_descriptor_t; - - -typedef struct { - uByte bLength; - uByte bDescriptorType; - uWord bcdUSB; -#define UD_USB_2_0 0x0200 -#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) - uByte bDeviceClass; - uByte bDeviceSubClass; - uByte bDeviceProtocol; - uByte bMaxPacketSize; - /* The fields below are not part of the initial descriptor. */ - uWord idVendor; - uWord idProduct; - uWord bcdDevice; - uByte iManufacturer; - uByte iProduct; - uByte iSerialNumber; - uByte bNumConfigurations; -} UPACKED usb_device_descriptor_t; -#define USB_DEVICE_DESCRIPTOR_SIZE 18 - -typedef struct { - uByte bLength; - uByte bDescriptorType; - uWord wTotalLength; - uByte bNumInterface; - uByte bConfigurationValue; - uByte iConfiguration; - uByte bmAttributes; -#define UC_BUS_POWERED 0x80 -#define UC_SELF_POWERED 0x40 -#define UC_REMOTE_WAKEUP 0x20 - uByte bMaxPower; /* max current in 2 mA units */ -#define UC_POWER_FACTOR 2 -} UPACKED usb_config_descriptor_t; -#define USB_CONFIG_DESCRIPTOR_SIZE 9 - -typedef struct { - uByte bLength; - uByte bDescriptorType; - uByte bInterfaceNumber; - uByte bAlternateSetting; - uByte bNumEndpoints; - uByte bInterfaceClass; - uByte bInterfaceSubClass; - uByte bInterfaceProtocol; - uByte iInterface; -} UPACKED usb_interface_descriptor_t; -#define USB_INTERFACE_DESCRIPTOR_SIZE 9 - -typedef struct { - uByte bLength; - uByte bDescriptorType; - uByte bEndpointAddress; -#define UE_GET_DIR(a) ((a) & 0x80) -#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7)) -#define UE_DIR_IN 0x80 -#define UE_DIR_OUT 0x00 -#define UE_ADDR 0x0f -#define UE_GET_ADDR(a) ((a) & UE_ADDR) - uByte bmAttributes; -#define UE_XFERTYPE 0x03 -#define UE_CONTROL 0x00 -#define UE_ISOCHRONOUS 0x01 -#define UE_BULK 0x02 -#define UE_INTERRUPT 0x03 -#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE) -#define UE_ISO_TYPE 0x0c -#define UE_ISO_ASYNC 0x04 -#define UE_ISO_ADAPT 0x08 -#define UE_ISO_SYNC 0x0c -#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE) - uWord wMaxPacketSize; - uByte bInterval; -} UPACKED usb_endpoint_descriptor_t; -#define USB_ENDPOINT_DESCRIPTOR_SIZE 7 - -typedef struct { - uByte bLength; - uByte bDescriptorType; - uWord bString[127]; -} UPACKED usb_string_descriptor_t; - -#define USB_MAX_STRING_LEN 128 -#define USB_MAX_ENCODED_STRING_LEN (USB_MAX_STRING_LEN * 3) /* UTF8 */ - -struct usb_device_request { - uByte bmRequestType; - uByte bRequest; - uWord wValue; - uWord wIndex; - uWord wLength; -} UPACKED; -typedef struct usb_device_request usb_device_request_t; - - -#endif diff --git a/minix/include/sys/elf_common.h b/minix/include/sys/elf_common.h deleted file mode 100644 index 3ba9cafc2..000000000 --- a/minix/include/sys/elf_common.h +++ /dev/null @@ -1,961 +0,0 @@ -/*- - * Copyright (c) 1998 John D. Polstra. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _SYS_ELF_COMMON_H_ -#define _SYS_ELF_COMMON_H_ 1 - -/* - * ELF definitions that are independent of architecture or word size. - */ - -/* - * Note header. The ".note" section contains an array of notes. Each - * begins with this header, aligned to a word boundary. Immediately - * following the note header is n_namesz bytes of name, padded to the - * next word boundary. Then comes n_descsz bytes of descriptor, again - * padded to a word boundary. The values of n_namesz and n_descsz do - * not include the padding. - */ - -typedef struct { - uint32_t n_namesz; /* Length of name. */ - uint32_t n_descsz; /* Length of descriptor. */ - uint32_t n_type; /* Type of this note. */ -} Elf_Note; - -/* - * The header for GNU-style hash sections. - */ - -typedef struct { - uint32_t gh_nbuckets; /* Number of hash buckets. */ - uint32_t gh_symndx; /* First visible symbol in .dynsym. */ - uint32_t gh_maskwords; /* #maskwords used in bloom filter. */ - uint32_t gh_shift2; /* Bloom filter shift count. */ -} Elf_GNU_Hash_Header; - -/* Indexes into the e_ident array. Keep synced with - http://www.sco.com/developers/gabi/latest/ch4.eheader.html */ -#define EI_MAG0 0 /* Magic number, byte 0. */ -#define EI_MAG1 1 /* Magic number, byte 1. */ -#define EI_MAG2 2 /* Magic number, byte 2. */ -#define EI_MAG3 3 /* Magic number, byte 3. */ -#define EI_CLASS 4 /* Class of machine. */ -#define EI_DATA 5 /* Data format. */ -#define EI_VERSION 6 /* ELF format version. */ -#define EI_OSABI 7 /* Operating system / ABI identification */ -#define EI_ABIVERSION 8 /* ABI version */ -#define OLD_EI_BRAND 8 /* Start of architecture identification. */ -#define EI_PAD 9 /* Start of padding (per SVR4 ABI). */ -#define EI_NIDENT 16 /* Size of e_ident array. */ - -/* Values for the magic number bytes. */ -#define ELFMAG0 0x7f -#define ELFMAG1 'E' -#define ELFMAG2 'L' -#define ELFMAG3 'F' -#define ELFMAG "\177ELF" /* magic string */ -#define SELFMAG 4 /* magic string size */ - -/* Values for e_ident[EI_VERSION] and e_version. */ -#define EV_NONE 0 -#define EV_CURRENT 1 - -/* Values for e_ident[EI_CLASS]. */ -#define ELFCLASSNONE 0 /* Unknown class. */ -#define ELFCLASS32 1 /* 32-bit architecture. */ -#define ELFCLASS64 2 /* 64-bit architecture. */ - -/* Values for e_ident[EI_DATA]. */ -#define ELFDATANONE 0 /* Unknown data format. */ -#define ELFDATA2LSB 1 /* 2's complement little-endian. */ -#define ELFDATA2MSB 2 /* 2's complement big-endian. */ - -/* Values for e_ident[EI_OSABI]. */ -#define ELFOSABI_NONE 0 /* UNIX System V ABI */ -#define ELFOSABI_HPUX 1 /* HP-UX operating system */ -#define ELFOSABI_NETBSD 2 /* NetBSD */ -#define ELFOSABI_LINUX 3 /* GNU/Linux */ -#define ELFOSABI_HURD 4 /* GNU/Hurd */ -#define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */ -#define ELFOSABI_SOLARIS 6 /* Solaris */ -#define ELFOSABI_AIX 7 /* AIX */ -#define ELFOSABI_IRIX 8 /* IRIX */ -#define ELFOSABI_FREEBSD 9 /* FreeBSD */ -#define ELFOSABI_TRU64 10 /* TRU64 UNIX */ -#define ELFOSABI_MODESTO 11 /* Novell Modesto */ -#define ELFOSABI_OPENBSD 12 /* OpenBSD */ -#define ELFOSABI_OPENVMS 13 /* Open VMS */ -#define ELFOSABI_NSK 14 /* HP Non-Stop Kernel */ -#define ELFOSABI_AROS 15 /* Amiga Research OS */ -#define ELFOSABI_ARM 97 /* ARM */ -#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ - -#define ELFOSABI_SYSV ELFOSABI_NONE /* symbol used in old spec */ -#define ELFOSABI_MONTEREY ELFOSABI_AIX /* Monterey */ - -/* e_ident */ -#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ - (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ - (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ - (ehdr).e_ident[EI_MAG3] == ELFMAG3) - -/* Values for e_type. */ -#define ET_NONE 0 /* Unknown type. */ -#define ET_REL 1 /* Relocatable. */ -#define ET_EXEC 2 /* Executable. */ -#define ET_DYN 3 /* Shared object. */ -#define ET_CORE 4 /* Core file. */ -#define ET_LOOS 0xfe00 /* First operating system specific. */ -#define ET_HIOS 0xfeff /* Last operating system-specific. */ -#define ET_LOPROC 0xff00 /* First processor-specific. */ -#define ET_HIPROC 0xffff /* Last processor-specific. */ - -/* Values for e_machine. */ -#define EM_NONE 0 /* Unknown machine. */ -#define EM_M32 1 /* AT&T WE32100. */ -#define EM_SPARC 2 /* Sun SPARC. */ -#define EM_386 3 /* Intel i386. */ -#define EM_68K 4 /* Motorola 68000. */ -#define EM_88K 5 /* Motorola 88000. */ -#define EM_860 7 /* Intel i860. */ -#define EM_MIPS 8 /* MIPS R3000 Big-Endian only. */ -#define EM_S370 9 /* IBM System/370. */ -#define EM_MIPS_RS3_LE 10 /* MIPS R3000 Little-Endian. */ -#define EM_PARISC 15 /* HP PA-RISC. */ -#define EM_VPP500 17 /* Fujitsu VPP500. */ -#define EM_SPARC32PLUS 18 /* SPARC v8plus. */ -#define EM_960 19 /* Intel 80960. */ -#define EM_PPC 20 /* PowerPC 32-bit. */ -#define EM_PPC64 21 /* PowerPC 64-bit. */ -#define EM_S390 22 /* IBM System/390. */ -#define EM_V800 36 /* NEC V800. */ -#define EM_FR20 37 /* Fujitsu FR20. */ -#define EM_RH32 38 /* TRW RH-32. */ -#define EM_RCE 39 /* Motorola RCE. */ -#define EM_ARM 40 /* ARM. */ -#define EM_SH 42 /* Hitachi SH. */ -#define EM_SPARCV9 43 /* SPARC v9 64-bit. */ -#define EM_TRICORE 44 /* Siemens TriCore embedded processor. */ -#define EM_ARC 45 /* Argonaut RISC Core. */ -#define EM_H8_300 46 /* Hitachi H8/300. */ -#define EM_H8_300H 47 /* Hitachi H8/300H. */ -#define EM_H8S 48 /* Hitachi H8S. */ -#define EM_H8_500 49 /* Hitachi H8/500. */ -#define EM_IA_64 50 /* Intel IA-64 Processor. */ -#define EM_MIPS_X 51 /* Stanford MIPS-X. */ -#define EM_COLDFIRE 52 /* Motorola ColdFire. */ -#define EM_68HC12 53 /* Motorola M68HC12. */ -#define EM_MMA 54 /* Fujitsu MMA. */ -#define EM_PCP 55 /* Siemens PCP. */ -#define EM_NCPU 56 /* Sony nCPU. */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor. */ -#define EM_STARCORE 58 /* Motorola Star*Core processor. */ -#define EM_ME16 59 /* Toyota ME16 processor. */ -#define EM_ST100 60 /* STMicroelectronics ST100 processor. */ -#define EM_TINYJ 61 /* Advanced Logic Corp. TinyJ processor. */ -#define EM_X86_64 62 /* Advanced Micro Devices x86-64 */ -#define EM_AMD64 EM_X86_64 /* Advanced Micro Devices x86-64 (compat) */ -#define EM_PDSP 63 /* Sony DSP Processor. */ -#define EM_FX66 66 /* Siemens FX66 microcontroller. */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 - microcontroller. */ -#define EM_ST7 68 /* STmicroelectronics ST7 8-bit - microcontroller. */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller. */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller. */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller. */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller. */ -#define EM_SVX 73 /* Silicon Graphics SVx. */ -#define EM_ST19 74 /* STMicroelectronics ST19 8-bit mc. */ -#define EM_VAX 75 /* Digital VAX. */ -#define EM_CRIS 76 /* Axis Communications 32-bit embedded - processor. */ -#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded - processor. */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor. */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor. */ -#define EM_MMIX 80 /* Donald Knuth's educational 64-bit proc. */ -#define EM_HUANY 81 /* Harvard University machine-independent - object files. */ -#define EM_PRISM 82 /* SiTera Prism. */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller. */ -#define EM_FR30 84 /* Fujitsu FR30. */ -#define EM_D10V 85 /* Mitsubishi D10V. */ -#define EM_D30V 86 /* Mitsubishi D30V. */ -#define EM_V850 87 /* NEC v850. */ -#define EM_M32R 88 /* Mitsubishi M32R. */ -#define EM_MN10300 89 /* Matsushita MN10300. */ -#define EM_MN10200 90 /* Matsushita MN10200. */ -#define EM_PJ 91 /* picoJava. */ -#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor. */ -#define EM_ARC_A5 93 /* ARC Cores Tangent-A5. */ -#define EM_XTENSA 94 /* Tensilica Xtensa Architecture. */ -#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore processor. */ -#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose - Processor. */ -#define EM_NS32K 97 /* National Semiconductor 32000 series. */ -#define EM_TPC 98 /* Tenor Network TPC processor. */ -#define EM_SNP1K 99 /* Trebia SNP 1000 processor. */ -#define EM_ST200 100 /* STMicroelectronics ST200 microcontroller. */ -#define EM_IP2K 101 /* Ubicom IP2xxx microcontroller family. */ -#define EM_MAX 102 /* MAX Processor. */ -#define EM_CR 103 /* National Semiconductor CompactRISC - microprocessor. */ -#define EM_F2MC16 104 /* Fujitsu F2MC16. */ -#define EM_MSP430 105 /* Texas Instruments embedded microcontroller - msp430. */ -#define EM_BLACKFIN 106 /* Analog Devices Blackfin (DSP) processor. */ -#define EM_SE_C33 107 /* S1C33 Family of Seiko Epson processors. */ -#define EM_SEP 108 /* Sharp embedded microprocessor. */ -#define EM_ARCA 109 /* Arca RISC Microprocessor. */ -#define EM_UNICORE 110 /* Microprocessor series from PKU-Unity Ltd. - and MPRC of Peking University */ - -/* Non-standard or deprecated. */ -#define EM_486 6 /* Intel i486. */ -#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ -#define EM_ALPHA_STD 41 /* Digital Alpha (standard value). */ -#define EM_ALPHA 0x9026 /* Alpha (written in the absence of an ABI) */ - -/* Special section indexes. */ -#define SHN_UNDEF 0 /* Undefined, missing, irrelevant. */ -#define SHN_LORESERVE 0xff00 /* First of reserved range. */ -#define SHN_LOPROC 0xff00 /* First processor-specific. */ -#define SHN_HIPROC 0xff1f /* Last processor-specific. */ -#define SHN_LOOS 0xff20 /* First operating system-specific. */ -#define SHN_HIOS 0xff3f /* Last operating system-specific. */ -#define SHN_ABS 0xfff1 /* Absolute values. */ -#define SHN_COMMON 0xfff2 /* Common data. */ -#define SHN_XINDEX 0xffff /* Escape -- index stored elsewhere. */ -#define SHN_HIRESERVE 0xffff /* Last of reserved range. */ - -/* sh_type */ -#define SHT_NULL 0 /* inactive */ -#define SHT_PROGBITS 1 /* program defined information */ -#define SHT_SYMTAB 2 /* symbol table section */ -#define SHT_STRTAB 3 /* string table section */ -#define SHT_RELA 4 /* relocation section with addends */ -#define SHT_HASH 5 /* symbol hash table section */ -#define SHT_DYNAMIC 6 /* dynamic section */ -#define SHT_NOTE 7 /* note section */ -#define SHT_NOBITS 8 /* no space section */ -#define SHT_REL 9 /* relocation section - no addends */ -#define SHT_SHLIB 10 /* reserved - purpose unknown */ -#define SHT_DYNSYM 11 /* dynamic symbol table section */ -#define SHT_INIT_ARRAY 14 /* Initialization function pointers. */ -#define SHT_FINI_ARRAY 15 /* Termination function pointers. */ -#define SHT_PREINIT_ARRAY 16 /* Pre-initialization function ptrs. */ -#define SHT_GROUP 17 /* Section group. */ -#define SHT_SYMTAB_SHNDX 18 /* Section indexes (see SHN_XINDEX). */ -#define SHT_LOOS 0x60000000 /* First of OS specific semantics */ -#define SHT_LOSUNW 0x6ffffff4 -#define SHT_SUNW_dof 0x6ffffff4 -#define SHT_SUNW_cap 0x6ffffff5 -#define SHT_SUNW_SIGNATURE 0x6ffffff6 -#define SHT_GNU_HASH 0x6ffffff6 -#define SHT_SUNW_ANNOTATE 0x6ffffff7 -#define SHT_SUNW_DEBUGSTR 0x6ffffff8 -#define SHT_SUNW_DEBUG 0x6ffffff9 -#define SHT_SUNW_move 0x6ffffffa -#define SHT_SUNW_COMDAT 0x6ffffffb -#define SHT_SUNW_syminfo 0x6ffffffc -#define SHT_SUNW_verdef 0x6ffffffd -#define SHT_GNU_verdef 0x6ffffffd /* Symbol versions provided */ -#define SHT_SUNW_verneed 0x6ffffffe -#define SHT_GNU_verneed 0x6ffffffe /* Symbol versions required */ -#define SHT_SUNW_versym 0x6fffffff -#define SHT_GNU_versym 0x6fffffff /* Symbol version table */ -#define SHT_HISUNW 0x6fffffff -#define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */ -#define SHT_LOPROC 0x70000000 /* reserved range for processor */ -#define SHT_AMD64_UNWIND 0x70000001 /* unwind information */ -#define SHT_HIPROC 0x7fffffff /* specific section header types */ -#define SHT_LOUSER 0x80000000 /* reserved range for application */ -#define SHT_HIUSER 0xffffffff /* specific indexes */ - -/* Flags for sh_flags. */ -#define SHF_WRITE 0x1 /* Section contains writable data. */ -#define SHF_ALLOC 0x2 /* Section occupies memory. */ -#define SHF_EXECINSTR 0x4 /* Section contains instructions. */ -#define SHF_MERGE 0x10 /* Section may be merged. */ -#define SHF_STRINGS 0x20 /* Section contains strings. */ -#define SHF_INFO_LINK 0x40 /* sh_info holds section index. */ -#define SHF_LINK_ORDER 0x80 /* Special ordering requirements. */ -#define SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required. */ -#define SHF_GROUP 0x200 /* Member of section group. */ -#define SHF_TLS 0x400 /* Section contains TLS data. */ -#define SHF_MASKOS 0x0ff00000 /* OS-specific semantics. */ -#define SHF_MASKPROC 0xf0000000 /* Processor-specific semantics. */ - -/* Values for p_type. */ -#define PT_NULL 0 /* Unused entry. */ -#define PT_LOAD 1 /* Loadable segment. */ -#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ -#define PT_INTERP 3 /* Pathname of interpreter. */ -#define PT_NOTE 4 /* Auxiliary information. */ -#define PT_SHLIB 5 /* Reserved (not used). */ -#define PT_PHDR 6 /* Location of program header itself. */ -#define PT_TLS 7 /* Thread local storage segment */ -#define PT_LOOS 0x60000000 /* First OS-specific. */ -#define PT_SUNW_UNWIND 0x6464e550 /* amd64 UNWIND program header */ -#define PT_GNU_EH_FRAME 0x6474e550 -#define PT_LOSUNW 0x6ffffffa -#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ -#define PT_SUNWSTACK 0x6ffffffb /* describes the stack segment */ -#define PT_SUNWDTRACE 0x6ffffffc /* private */ -#define PT_SUNWCAP 0x6ffffffd /* hard/soft capabilities segment */ -#define PT_HISUNW 0x6fffffff -#define PT_HIOS 0x6fffffff /* Last OS-specific. */ -#define PT_LOPROC 0x70000000 /* First processor-specific type. */ -#define PT_HIPROC 0x7fffffff /* Last processor-specific type. */ - -/* Values for p_flags. */ -#define PF_X 0x1 /* Executable. */ -#define PF_W 0x2 /* Writable. */ -#define PF_R 0x4 /* Readable. */ -#define PF_MASKOS 0x0ff00000 /* Operating system-specific. */ -#define PF_MASKPROC 0xf0000000 /* Processor-specific. */ - -/* Extended program header index. */ -#define PN_XNUM 0xffff - -/* Values for d_tag. */ -#define DT_NULL 0 /* Terminating entry. */ -#define DT_NEEDED 1 /* String table offset of a needed shared - library. */ -#define DT_PLTRELSZ 2 /* Total size in bytes of PLT relocations. */ -#define DT_PLTGOT 3 /* Processor-dependent address. */ -#define DT_HASH 4 /* Address of symbol hash table. */ -#define DT_STRTAB 5 /* Address of string table. */ -#define DT_SYMTAB 6 /* Address of symbol table. */ -#define DT_RELA 7 /* Address of ElfNN_Rela relocations. */ -#define DT_RELASZ 8 /* Total size of ElfNN_Rela relocations. */ -#define DT_RELAENT 9 /* Size of each ElfNN_Rela relocation entry. */ -#define DT_STRSZ 10 /* Size of string table. */ -#define DT_SYMENT 11 /* Size of each symbol table entry. */ -#define DT_INIT 12 /* Address of initialization function. */ -#define DT_FINI 13 /* Address of finalization function. */ -#define DT_SONAME 14 /* String table offset of shared object - name. */ -#define DT_RPATH 15 /* String table offset of library path. [sup] */ -#define DT_SYMBOLIC 16 /* Indicates "symbolic" linking. [sup] */ -#define DT_REL 17 /* Address of ElfNN_Rel relocations. */ -#define DT_RELSZ 18 /* Total size of ElfNN_Rel relocations. */ -#define DT_RELENT 19 /* Size of each ElfNN_Rel relocation. */ -#define DT_PLTREL 20 /* Type of relocation used for PLT. */ -#define DT_DEBUG 21 /* Reserved (not used). */ -#define DT_TEXTREL 22 /* Indicates there may be relocations in - non-writable segments. [sup] */ -#define DT_JMPREL 23 /* Address of PLT relocations. */ -#define DT_BIND_NOW 24 /* [sup] */ -#define DT_INIT_ARRAY 25 /* Address of the array of pointers to - initialization functions */ -#define DT_FINI_ARRAY 26 /* Address of the array of pointers to - termination functions */ -#define DT_INIT_ARRAYSZ 27 /* Size in bytes of the array of - initialization functions. */ -#define DT_FINI_ARRAYSZ 28 /* Size in bytes of the array of - terminationfunctions. */ -#define DT_RUNPATH 29 /* String table offset of a null-terminated - library search path string. */ -#define DT_FLAGS 30 /* Object specific flag values. */ -#define DT_ENCODING 32 /* Values greater than or equal to DT_ENCODING - and less than DT_LOOS follow the rules for - the interpretation of the d_un union - as follows: even == 'd_ptr', even == 'd_val' - or none */ -#define DT_PREINIT_ARRAY 32 /* Address of the array of pointers to - pre-initialization functions. */ -#define DT_PREINIT_ARRAYSZ 33 /* Size in bytes of the array of - pre-initialization functions. */ -#define DT_MAXPOSTAGS 34 /* number of positive tags */ -#define DT_LOOS 0x6000000d /* First OS-specific */ -#define DT_SUNW_AUXILIARY 0x6000000d /* symbol auxiliary name */ -#define DT_SUNW_RTLDINF 0x6000000e /* ld.so.1 info (private) */ -#define DT_SUNW_FILTER 0x6000000f /* symbol filter name */ -#define DT_SUNW_CAP 0x60000010 /* hardware/software */ -#define DT_HIOS 0x6ffff000 /* Last OS-specific */ - -/* - * DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the - * Dyn.d_un.d_val field of the Elf*_Dyn structure. - */ -#define DT_VALRNGLO 0x6ffffd00 -#define DT_CHECKSUM 0x6ffffdf8 /* elf checksum */ -#define DT_PLTPADSZ 0x6ffffdf9 /* pltpadding size */ -#define DT_MOVEENT 0x6ffffdfa /* move table entry size */ -#define DT_MOVESZ 0x6ffffdfb /* move table size */ -#define DT_FEATURE_1 0x6ffffdfc /* feature holder */ -#define DT_POSFLAG_1 0x6ffffdfd /* flags for DT_* entries, effecting */ - /* the following DT_* entry. */ - /* See DF_P1_* definitions */ -#define DT_SYMINSZ 0x6ffffdfe /* syminfo table size (in bytes) */ -#define DT_SYMINENT 0x6ffffdff /* syminfo entry size (in bytes) */ -#define DT_VALRNGHI 0x6ffffdff - -/* - * DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the - * Dyn.d_un.d_ptr field of the Elf*_Dyn structure. - * - * If any adjustment is made to the ELF object after it has been - * built, these entries will need to be adjusted. - */ -#define DT_ADDRRNGLO 0x6ffffe00 -#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table */ -#define DT_CONFIG 0x6ffffefa /* configuration information */ -#define DT_DEPAUDIT 0x6ffffefb /* dependency auditing */ -#define DT_AUDIT 0x6ffffefc /* object auditing */ -#define DT_PLTPAD 0x6ffffefd /* pltpadding (sparcv9) */ -#define DT_MOVETAB 0x6ffffefe /* move table */ -#define DT_SYMINFO 0x6ffffeff /* syminfo table */ -#define DT_ADDRRNGHI 0x6ffffeff - -#define DT_VERSYM 0x6ffffff0 /* Address of versym section. */ -#define DT_RELACOUNT 0x6ffffff9 /* number of RELATIVE relocations */ -#define DT_RELCOUNT 0x6ffffffa /* number of RELATIVE relocations */ -#define DT_FLAGS_1 0x6ffffffb /* state flags - see DF_1_* defs */ -#define DT_VERDEF 0x6ffffffc /* Address of verdef section. */ -#define DT_VERDEFNUM 0x6ffffffd /* Number of elems in verdef section */ -#define DT_VERNEED 0x6ffffffe /* Address of verneed section. */ -#define DT_VERNEEDNUM 0x6fffffff /* Number of elems in verneed section */ - -#define DT_LOPROC 0x70000000 /* First processor-specific type. */ -#define DT_DEPRECATED_SPARC_REGISTER 0x7000001 -#define DT_AUXILIARY 0x7ffffffd /* shared library auxiliary name */ -#define DT_USED 0x7ffffffe /* ignored - same as needed */ -#define DT_FILTER 0x7fffffff /* shared library filter name */ -#define DT_HIPROC 0x7fffffff /* Last processor-specific type. */ - -/* Values for DT_FLAGS */ -#define DF_ORIGIN 0x0001 /* Indicates that the object being loaded may - make reference to the $ORIGIN substitution - string */ -#define DF_SYMBOLIC 0x0002 /* Indicates "symbolic" linking. */ -#define DF_TEXTREL 0x0004 /* Indicates there may be relocations in - non-writable segments. */ -#define DF_BIND_NOW 0x0008 /* Indicates that the dynamic linker should - process all relocations for the object - containing this entry before transferring - control to the program. */ -#define DF_STATIC_TLS 0x0010 /* Indicates that the shared object or - executable contains code using a static - thread-local storage scheme. */ - -/* Values for DT_FLAGS_1 */ -#define DF_1_BIND_NOW 0x00000001 /* Same as DF_BIND_NOW */ -#define DF_1_GLOBAL 0x00000002 /* Set the RTLD_GLOBAL for object */ -#define DF_1_NODELETE 0x00000008 /* Set the RTLD_NODELETE for object */ -#define DF_1_NOOPEN 0x00000040 /* Do not allow loading on dlopen() */ -#define DF_1_ORIGIN 0x00000080 /* Process $ORIGIN */ - -/* Values for n_type. Used in core files. */ -#define NT_PRSTATUS 1 /* Process status. */ -#define NT_FPREGSET 2 /* Floating point registers. */ -#define NT_PRPSINFO 3 /* Process state info. */ - -/* Symbol Binding - ELFNN_ST_BIND - st_info */ -#define STB_LOCAL 0 /* Local symbol */ -#define STB_GLOBAL 1 /* Global symbol */ -#define STB_WEAK 2 /* like global - lower precedence */ -#define STB_LOOS 10 /* Reserved range for operating system */ -#define STB_HIOS 12 /* specific semantics. */ -#define STB_LOPROC 13 /* reserved range for processor */ -#define STB_HIPROC 15 /* specific semantics. */ - -/* Symbol type - ELFNN_ST_TYPE - st_info */ -#define STT_NOTYPE 0 /* Unspecified type. */ -#define STT_OBJECT 1 /* Data object. */ -#define STT_FUNC 2 /* Function. */ -#define STT_SECTION 3 /* Section. */ -#define STT_FILE 4 /* Source file. */ -#define STT_COMMON 5 /* Uninitialized common block. */ -#define STT_TLS 6 /* TLS object. */ -#define STT_NUM 7 -#define STT_LOOS 10 /* Reserved range for operating system */ -#define STT_HIOS 12 /* specific semantics. */ -#define STT_LOPROC 13 /* reserved range for processor */ -#define STT_HIPROC 15 /* specific semantics. */ - -/* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */ -#define STV_DEFAULT 0x0 /* Default visibility (see binding). */ -#define STV_INTERNAL 0x1 /* Special meaning in relocatable objects. */ -#define STV_HIDDEN 0x2 /* Not visible. */ -#define STV_PROTECTED 0x3 /* Visible but not preemptible. */ -#define STV_EXPORTED 0x4 -#define STV_SINGLETON 0x5 -#define STV_ELIMINATE 0x6 - -/* Special symbol table indexes. */ -#define STN_UNDEF 0 /* Undefined symbol index. */ - -/* Symbol versioning flags. */ -#define VER_DEF_CURRENT 1 -#define VER_DEF_IDX(x) VER_NDX(x) - -#define VER_FLG_BASE 0x01 -#define VER_FLG_WEAK 0x02 - -#define VER_NEED_CURRENT 1 -#define VER_NEED_WEAK (1u << 15) -#define VER_NEED_HIDDEN VER_NDX_HIDDEN -#define VER_NEED_IDX(x) VER_NDX(x) - -#define VER_NDX_LOCAL 0 -#define VER_NDX_GLOBAL 1 -#define VER_NDX_GIVEN 2 - -#define VER_NDX_HIDDEN (1u << 15) -#define VER_NDX(x) ((x) & ~(1u << 15)) - -#define CA_SUNW_NULL 0 -#define CA_SUNW_HW_1 1 /* first hardware capabilities entry */ -#define CA_SUNW_SF_1 2 /* first software capabilities entry */ - -/* - * Syminfo flag values - */ -#define SYMINFO_FLG_DIRECT 0x0001 /* symbol ref has direct association */ - /* to object containing defn. */ -#define SYMINFO_FLG_PASSTHRU 0x0002 /* ignored - see SYMINFO_FLG_FILTER */ -#define SYMINFO_FLG_COPY 0x0004 /* symbol is a copy-reloc */ -#define SYMINFO_FLG_LAZYLOAD 0x0008 /* object containing defn should be */ - /* lazily-loaded */ -#define SYMINFO_FLG_DIRECTBIND 0x0010 /* ref should be bound directly to */ - /* object containing defn. */ -#define SYMINFO_FLG_NOEXTDIRECT 0x0020 /* don't let an external reference */ - /* directly bind to this symbol */ -#define SYMINFO_FLG_FILTER 0x0002 /* symbol ref is associated to a */ -#define SYMINFO_FLG_AUXILIARY 0x0040 /* standard or auxiliary filter */ - -/* - * Syminfo.si_boundto values. - */ -#define SYMINFO_BT_SELF 0xffff /* symbol bound to self */ -#define SYMINFO_BT_PARENT 0xfffe /* symbol bound to parent */ -#define SYMINFO_BT_NONE 0xfffd /* no special symbol binding */ -#define SYMINFO_BT_EXTERN 0xfffc /* symbol defined as external */ -#define SYMINFO_BT_LOWRESERVE 0xff00 /* beginning of reserved entries */ - -/* - * Syminfo version values. - */ -#define SYMINFO_NONE 0 /* Syminfo version */ -#define SYMINFO_CURRENT 1 -#define SYMINFO_NUM 2 - -/* - * Relocation types. - * - * All machine architectures are defined here to allow tools on one to - * handle others. - */ - -#define R_386_NONE 0 /* No relocation. */ -#define R_386_32 1 /* Add symbol value. */ -#define R_386_PC32 2 /* Add PC-relative symbol value. */ -#define R_386_GOT32 3 /* Add PC-relative GOT offset. */ -#define R_386_PLT32 4 /* Add PC-relative PLT offset. */ -#define R_386_COPY 5 /* Copy data from shared object. */ -#define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */ -#define R_386_JMP_SLOT 7 /* Set GOT entry to code address. */ -#define R_386_RELATIVE 8 /* Add load address of shared object. */ -#define R_386_GOTOFF 9 /* Add GOT-relative symbol address. */ -#define R_386_GOTPC 10 /* Add PC-relative GOT table address. */ -#define R_386_TLS_TPOFF 14 /* Negative offset in static TLS block */ -#define R_386_TLS_IE 15 /* Absolute address of GOT for -ve static TLS */ -#define R_386_TLS_GOTIE 16 /* GOT entry for negative static TLS block */ -#define R_386_TLS_LE 17 /* Negative offset relative to static TLS */ -#define R_386_TLS_GD 18 /* 32 bit offset to GOT (index,off) pair */ -#define R_386_TLS_LDM 19 /* 32 bit offset to GOT (index,zero) pair */ -#define R_386_TLS_GD_32 24 /* 32 bit offset to GOT (index,off) pair */ -#define R_386_TLS_GD_PUSH 25 /* pushl instruction for Sun ABI GD sequence */ -#define R_386_TLS_GD_CALL 26 /* call instruction for Sun ABI GD sequence */ -#define R_386_TLS_GD_POP 27 /* popl instruction for Sun ABI GD sequence */ -#define R_386_TLS_LDM_32 28 /* 32 bit offset to GOT (index,zero) pair */ -#define R_386_TLS_LDM_PUSH 29 /* pushl instruction for Sun ABI LD sequence */ -#define R_386_TLS_LDM_CALL 30 /* call instruction for Sun ABI LD sequence */ -#define R_386_TLS_LDM_POP 31 /* popl instruction for Sun ABI LD sequence */ -#define R_386_TLS_LDO_32 32 /* 32 bit offset from start of TLS block */ -#define R_386_TLS_IE_32 33 /* 32 bit offset to GOT static TLS offset entry */ -#define R_386_TLS_LE_32 34 /* 32 bit offset within static TLS block */ -#define R_386_TLS_DTPMOD32 35 /* GOT entry containing TLS index */ -#define R_386_TLS_DTPOFF32 36 /* GOT entry containing TLS offset */ -#define R_386_TLS_TPOFF32 37 /* GOT entry of -ve static TLS offset */ - -#define R_ARM_NONE 0 /* No relocation. */ -#define R_ARM_PC24 1 -#define R_ARM_ABS32 2 -#define R_ARM_REL32 3 -#define R_ARM_PC13 4 -#define R_ARM_ABS16 5 -#define R_ARM_ABS12 6 -#define R_ARM_THM_ABS5 7 -#define R_ARM_ABS8 8 -#define R_ARM_SBREL32 9 -#define R_ARM_THM_PC22 10 -#define R_ARM_THM_PC8 11 -#define R_ARM_AMP_VCALL9 12 -#define R_ARM_SWI24 13 -#define R_ARM_THM_SWI8 14 -#define R_ARM_XPC25 15 -#define R_ARM_THM_XPC22 16 -#define R_ARM_COPY 20 /* Copy data from shared object. */ -#define R_ARM_GLOB_DAT 21 /* Set GOT entry to data address. */ -#define R_ARM_JUMP_SLOT 22 /* Set GOT entry to code address. */ -#define R_ARM_RELATIVE 23 /* Add load address of shared object. */ -#define R_ARM_GOTOFF 24 /* Add GOT-relative symbol address. */ -#define R_ARM_GOTPC 25 /* Add PC-relative GOT table address. */ -#define R_ARM_GOT32 26 /* Add PC-relative GOT offset. */ -#define R_ARM_PLT32 27 /* Add PC-relative PLT offset. */ -#define R_ARM_GNU_VTENTRY 100 -#define R_ARM_GNU_VTINHERIT 101 -#define R_ARM_RSBREL32 250 -#define R_ARM_THM_RPC22 251 -#define R_ARM_RREL32 252 -#define R_ARM_RABS32 253 -#define R_ARM_RPC24 254 -#define R_ARM_RBASE 255 - -/* Name Value Field Calculation */ -#define R_IA_64_NONE 0 /* None */ -#define R_IA_64_IMM14 0x21 /* immediate14 S + A */ -#define R_IA_64_IMM22 0x22 /* immediate22 S + A */ -#define R_IA_64_IMM64 0x23 /* immediate64 S + A */ -#define R_IA_64_DIR32MSB 0x24 /* word32 MSB S + A */ -#define R_IA_64_DIR32LSB 0x25 /* word32 LSB S + A */ -#define R_IA_64_DIR64MSB 0x26 /* word64 MSB S + A */ -#define R_IA_64_DIR64LSB 0x27 /* word64 LSB S + A */ -#define R_IA_64_GPREL22 0x2a /* immediate22 @gprel(S + A) */ -#define R_IA_64_GPREL64I 0x2b /* immediate64 @gprel(S + A) */ -#define R_IA_64_GPREL32MSB 0x2c /* word32 MSB @gprel(S + A) */ -#define R_IA_64_GPREL32LSB 0x2d /* word32 LSB @gprel(S + A) */ -#define R_IA_64_GPREL64MSB 0x2e /* word64 MSB @gprel(S + A) */ -#define R_IA_64_GPREL64LSB 0x2f /* word64 LSB @gprel(S + A) */ -#define R_IA_64_LTOFF22 0x32 /* immediate22 @ltoff(S + A) */ -#define R_IA_64_LTOFF64I 0x33 /* immediate64 @ltoff(S + A) */ -#define R_IA_64_PLTOFF22 0x3a /* immediate22 @pltoff(S + A) */ -#define R_IA_64_PLTOFF64I 0x3b /* immediate64 @pltoff(S + A) */ -#define R_IA_64_PLTOFF64MSB 0x3e /* word64 MSB @pltoff(S + A) */ -#define R_IA_64_PLTOFF64LSB 0x3f /* word64 LSB @pltoff(S + A) */ -#define R_IA_64_FPTR64I 0x43 /* immediate64 @fptr(S + A) */ -#define R_IA_64_FPTR32MSB 0x44 /* word32 MSB @fptr(S + A) */ -#define R_IA_64_FPTR32LSB 0x45 /* word32 LSB @fptr(S + A) */ -#define R_IA_64_FPTR64MSB 0x46 /* word64 MSB @fptr(S + A) */ -#define R_IA_64_FPTR64LSB 0x47 /* word64 LSB @fptr(S + A) */ -#define R_IA_64_PCREL60B 0x48 /* immediate60 form1 S + A - P */ -#define R_IA_64_PCREL21B 0x49 /* immediate21 form1 S + A - P */ -#define R_IA_64_PCREL21M 0x4a /* immediate21 form2 S + A - P */ -#define R_IA_64_PCREL21F 0x4b /* immediate21 form3 S + A - P */ -#define R_IA_64_PCREL32MSB 0x4c /* word32 MSB S + A - P */ -#define R_IA_64_PCREL32LSB 0x4d /* word32 LSB S + A - P */ -#define R_IA_64_PCREL64MSB 0x4e /* word64 MSB S + A - P */ -#define R_IA_64_PCREL64LSB 0x4f /* word64 LSB S + A - P */ -#define R_IA_64_LTOFF_FPTR22 0x52 /* immediate22 @ltoff(@fptr(S + A)) */ -#define R_IA_64_LTOFF_FPTR64I 0x53 /* immediate64 @ltoff(@fptr(S + A)) */ -#define R_IA_64_LTOFF_FPTR32MSB 0x54 /* word32 MSB @ltoff(@fptr(S + A)) */ -#define R_IA_64_LTOFF_FPTR32LSB 0x55 /* word32 LSB @ltoff(@fptr(S + A)) */ -#define R_IA_64_LTOFF_FPTR64MSB 0x56 /* word64 MSB @ltoff(@fptr(S + A)) */ -#define R_IA_64_LTOFF_FPTR64LSB 0x57 /* word64 LSB @ltoff(@fptr(S + A)) */ -#define R_IA_64_SEGREL32MSB 0x5c /* word32 MSB @segrel(S + A) */ -#define R_IA_64_SEGREL32LSB 0x5d /* word32 LSB @segrel(S + A) */ -#define R_IA_64_SEGREL64MSB 0x5e /* word64 MSB @segrel(S + A) */ -#define R_IA_64_SEGREL64LSB 0x5f /* word64 LSB @segrel(S + A) */ -#define R_IA_64_SECREL32MSB 0x64 /* word32 MSB @secrel(S + A) */ -#define R_IA_64_SECREL32LSB 0x65 /* word32 LSB @secrel(S + A) */ -#define R_IA_64_SECREL64MSB 0x66 /* word64 MSB @secrel(S + A) */ -#define R_IA_64_SECREL64LSB 0x67 /* word64 LSB @secrel(S + A) */ -#define R_IA_64_REL32MSB 0x6c /* word32 MSB BD + A */ -#define R_IA_64_REL32LSB 0x6d /* word32 LSB BD + A */ -#define R_IA_64_REL64MSB 0x6e /* word64 MSB BD + A */ -#define R_IA_64_REL64LSB 0x6f /* word64 LSB BD + A */ -#define R_IA_64_LTV32MSB 0x74 /* word32 MSB S + A */ -#define R_IA_64_LTV32LSB 0x75 /* word32 LSB S + A */ -#define R_IA_64_LTV64MSB 0x76 /* word64 MSB S + A */ -#define R_IA_64_LTV64LSB 0x77 /* word64 LSB S + A */ -#define R_IA_64_PCREL21BI 0x79 /* immediate21 form1 S + A - P */ -#define R_IA_64_PCREL22 0x7a /* immediate22 S + A - P */ -#define R_IA_64_PCREL64I 0x7b /* immediate64 S + A - P */ -#define R_IA_64_IPLTMSB 0x80 /* function descriptor MSB special */ -#define R_IA_64_IPLTLSB 0x81 /* function descriptor LSB speciaal */ -#define R_IA_64_SUB 0x85 /* immediate64 A - S */ -#define R_IA_64_LTOFF22X 0x86 /* immediate22 special */ -#define R_IA_64_LDXMOV 0x87 /* immediate22 special */ -#define R_IA_64_TPREL14 0x91 /* imm14 @tprel(S + A) */ -#define R_IA_64_TPREL22 0x92 /* imm22 @tprel(S + A) */ -#define R_IA_64_TPREL64I 0x93 /* imm64 @tprel(S + A) */ -#define R_IA_64_TPREL64MSB 0x96 /* word64 MSB @tprel(S + A) */ -#define R_IA_64_TPREL64LSB 0x97 /* word64 LSB @tprel(S + A) */ -#define R_IA_64_LTOFF_TPREL22 0x9a /* imm22 @ltoff(@tprel(S+A)) */ -#define R_IA_64_DTPMOD64MSB 0xa6 /* word64 MSB @dtpmod(S + A) */ -#define R_IA_64_DTPMOD64LSB 0xa7 /* word64 LSB @dtpmod(S + A) */ -#define R_IA_64_LTOFF_DTPMOD22 0xaa /* imm22 @ltoff(@dtpmod(S+A)) */ -#define R_IA_64_DTPREL14 0xb1 /* imm14 @dtprel(S + A) */ -#define R_IA_64_DTPREL22 0xb2 /* imm22 @dtprel(S + A) */ -#define R_IA_64_DTPREL64I 0xb3 /* imm64 @dtprel(S + A) */ -#define R_IA_64_DTPREL32MSB 0xb4 /* word32 MSB @dtprel(S + A) */ -#define R_IA_64_DTPREL32LSB 0xb5 /* word32 LSB @dtprel(S + A) */ -#define R_IA_64_DTPREL64MSB 0xb6 /* word64 MSB @dtprel(S + A) */ -#define R_IA_64_DTPREL64LSB 0xb7 /* word64 LSB @dtprel(S + A) */ -#define R_IA_64_LTOFF_DTPREL22 0xba /* imm22 @ltoff(@dtprel(S+A)) */ - -#define R_MIPS_NONE 0 /* No reloc */ -#define R_MIPS_16 1 /* Direct 16 bit */ -#define R_MIPS_32 2 /* Direct 32 bit */ -#define R_MIPS_REL32 3 /* PC relative 32 bit */ -#define R_MIPS_26 4 /* Direct 26 bit shifted */ -#define R_MIPS_HI16 5 /* High 16 bit */ -#define R_MIPS_LO16 6 /* Low 16 bit */ -#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ -#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ -#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ -#define R_MIPS_PC16 10 /* PC relative 16 bit */ -#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ -#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ -#define R_MIPS_GOTHI16 21 /* GOT HI 16 bit */ -#define R_MIPS_GOTLO16 22 /* GOT LO 16 bit */ -#define R_MIPS_CALLHI16 30 /* upper 16 bit GOT entry for function */ -#define R_MIPS_CALLLO16 31 /* lower 16 bit GOT entry for function */ - -#define R_PPC_NONE 0 /* No relocation. */ -#define R_PPC_ADDR32 1 -#define R_PPC_ADDR24 2 -#define R_PPC_ADDR16 3 -#define R_PPC_ADDR16_LO 4 -#define R_PPC_ADDR16_HI 5 -#define R_PPC_ADDR16_HA 6 -#define R_PPC_ADDR14 7 -#define R_PPC_ADDR14_BRTAKEN 8 -#define R_PPC_ADDR14_BRNTAKEN 9 -#define R_PPC_REL24 10 -#define R_PPC_REL14 11 -#define R_PPC_REL14_BRTAKEN 12 -#define R_PPC_REL14_BRNTAKEN 13 -#define R_PPC_GOT16 14 -#define R_PPC_GOT16_LO 15 -#define R_PPC_GOT16_HI 16 -#define R_PPC_GOT16_HA 17 -#define R_PPC_PLTREL24 18 -#define R_PPC_COPY 19 -#define R_PPC_GLOB_DAT 20 -#define R_PPC_JMP_SLOT 21 -#define R_PPC_RELATIVE 22 -#define R_PPC_LOCAL24PC 23 -#define R_PPC_UADDR32 24 -#define R_PPC_UADDR16 25 -#define R_PPC_REL32 26 -#define R_PPC_PLT32 27 -#define R_PPC_PLTREL32 28 -#define R_PPC_PLT16_LO 29 -#define R_PPC_PLT16_HI 30 -#define R_PPC_PLT16_HA 31 -#define R_PPC_SDAREL16 32 -#define R_PPC_SECTOFF 33 -#define R_PPC_SECTOFF_LO 34 -#define R_PPC_SECTOFF_HI 35 -#define R_PPC_SECTOFF_HA 36 - -/* - * 64-bit relocations - */ -#define R_PPC64_ADDR64 38 -#define R_PPC64_ADDR16_HIGHER 39 -#define R_PPC64_ADDR16_HIGHERA 40 -#define R_PPC64_ADDR16_HIGHEST 41 -#define R_PPC64_ADDR16_HIGHESTA 42 -#define R_PPC64_UADDR64 43 -#define R_PPC64_REL64 44 -#define R_PPC64_PLT64 45 -#define R_PPC64_PLTREL64 46 -#define R_PPC64_TOC16 47 -#define R_PPC64_TOC16_LO 48 -#define R_PPC64_TOC16_HI 49 -#define R_PPC64_TOC16_HA 50 -#define R_PPC64_TOC 51 -#define R_PPC64_DTPMOD64 68 -#define R_PPC64_TPREL64 73 -#define R_PPC64_DTPREL64 78 - -/* - * TLS relocations - */ -#define R_PPC_TLS 67 -#define R_PPC_DTPMOD32 68 -#define R_PPC_TPREL16 69 -#define R_PPC_TPREL16_LO 70 -#define R_PPC_TPREL16_HI 71 -#define R_PPC_TPREL16_HA 72 -#define R_PPC_TPREL32 73 -#define R_PPC_DTPREL16 74 -#define R_PPC_DTPREL16_LO 75 -#define R_PPC_DTPREL16_HI 76 -#define R_PPC_DTPREL16_HA 77 -#define R_PPC_DTPREL32 78 -#define R_PPC_GOT_TLSGD16 79 -#define R_PPC_GOT_TLSGD16_LO 80 -#define R_PPC_GOT_TLSGD16_HI 81 -#define R_PPC_GOT_TLSGD16_HA 82 -#define R_PPC_GOT_TLSLD16 83 -#define R_PPC_GOT_TLSLD16_LO 84 -#define R_PPC_GOT_TLSLD16_HI 85 -#define R_PPC_GOT_TLSLD16_HA 86 -#define R_PPC_GOT_TPREL16 87 -#define R_PPC_GOT_TPREL16_LO 88 -#define R_PPC_GOT_TPREL16_HI 89 -#define R_PPC_GOT_TPREL16_HA 90 - -/* - * The remaining relocs are from the Embedded ELF ABI, and are not in the - * SVR4 ELF ABI. - */ - -#define R_PPC_EMB_NADDR32 101 -#define R_PPC_EMB_NADDR16 102 -#define R_PPC_EMB_NADDR16_LO 103 -#define R_PPC_EMB_NADDR16_HI 104 -#define R_PPC_EMB_NADDR16_HA 105 -#define R_PPC_EMB_SDAI16 106 -#define R_PPC_EMB_SDA2I16 107 -#define R_PPC_EMB_SDA2REL 108 -#define R_PPC_EMB_SDA21 109 -#define R_PPC_EMB_MRKREF 110 -#define R_PPC_EMB_RELSEC16 111 -#define R_PPC_EMB_RELST_LO 112 -#define R_PPC_EMB_RELST_HI 113 -#define R_PPC_EMB_RELST_HA 114 -#define R_PPC_EMB_BIT_FLD 115 -#define R_PPC_EMB_RELSDA 116 - -#define R_SPARC_NONE 0 -#define R_SPARC_8 1 -#define R_SPARC_16 2 -#define R_SPARC_32 3 -#define R_SPARC_DISP8 4 -#define R_SPARC_DISP16 5 -#define R_SPARC_DISP32 6 -#define R_SPARC_WDISP30 7 -#define R_SPARC_WDISP22 8 -#define R_SPARC_HI22 9 -#define R_SPARC_22 10 -#define R_SPARC_13 11 -#define R_SPARC_LO10 12 -#define R_SPARC_GOT10 13 -#define R_SPARC_GOT13 14 -#define R_SPARC_GOT22 15 -#define R_SPARC_PC10 16 -#define R_SPARC_PC22 17 -#define R_SPARC_WPLT30 18 -#define R_SPARC_COPY 19 -#define R_SPARC_GLOB_DAT 20 -#define R_SPARC_JMP_SLOT 21 -#define R_SPARC_RELATIVE 22 -#define R_SPARC_UA32 23 -#define R_SPARC_PLT32 24 -#define R_SPARC_HIPLT22 25 -#define R_SPARC_LOPLT10 26 -#define R_SPARC_PCPLT32 27 -#define R_SPARC_PCPLT22 28 -#define R_SPARC_PCPLT10 29 -#define R_SPARC_10 30 -#define R_SPARC_11 31 -#define R_SPARC_64 32 -#define R_SPARC_OLO10 33 -#define R_SPARC_HH22 34 -#define R_SPARC_HM10 35 -#define R_SPARC_LM22 36 -#define R_SPARC_PC_HH22 37 -#define R_SPARC_PC_HM10 38 -#define R_SPARC_PC_LM22 39 -#define R_SPARC_WDISP16 40 -#define R_SPARC_WDISP19 41 -#define R_SPARC_GLOB_JMP 42 -#define R_SPARC_7 43 -#define R_SPARC_5 44 -#define R_SPARC_6 45 -#define R_SPARC_DISP64 46 -#define R_SPARC_PLT64 47 -#define R_SPARC_HIX22 48 -#define R_SPARC_LOX10 49 -#define R_SPARC_H44 50 -#define R_SPARC_M44 51 -#define R_SPARC_L44 52 -#define R_SPARC_REGISTER 53 -#define R_SPARC_UA64 54 -#define R_SPARC_UA16 55 -#define R_SPARC_TLS_GD_HI22 56 -#define R_SPARC_TLS_GD_LO10 57 -#define R_SPARC_TLS_GD_ADD 58 -#define R_SPARC_TLS_GD_CALL 59 -#define R_SPARC_TLS_LDM_HI22 60 -#define R_SPARC_TLS_LDM_LO10 61 -#define R_SPARC_TLS_LDM_ADD 62 -#define R_SPARC_TLS_LDM_CALL 63 -#define R_SPARC_TLS_LDO_HIX22 64 -#define R_SPARC_TLS_LDO_LOX10 65 -#define R_SPARC_TLS_LDO_ADD 66 -#define R_SPARC_TLS_IE_HI22 67 -#define R_SPARC_TLS_IE_LO10 68 -#define R_SPARC_TLS_IE_LD 69 -#define R_SPARC_TLS_IE_LDX 70 -#define R_SPARC_TLS_IE_ADD 71 -#define R_SPARC_TLS_LE_HIX22 72 -#define R_SPARC_TLS_LE_LOX10 73 -#define R_SPARC_TLS_DTPMOD32 74 -#define R_SPARC_TLS_DTPMOD64 75 -#define R_SPARC_TLS_DTPOFF32 76 -#define R_SPARC_TLS_DTPOFF64 77 -#define R_SPARC_TLS_TPOFF32 78 -#define R_SPARC_TLS_TPOFF64 79 - -#define R_X86_64_NONE 0 /* No relocation. */ -#define R_X86_64_64 1 /* Add 64 bit symbol value. */ -#define R_X86_64_PC32 2 /* PC-relative 32 bit signed sym value. */ -#define R_X86_64_GOT32 3 /* PC-relative 32 bit GOT offset. */ -#define R_X86_64_PLT32 4 /* PC-relative 32 bit PLT offset. */ -#define R_X86_64_COPY 5 /* Copy data from shared object. */ -#define R_X86_64_GLOB_DAT 6 /* Set GOT entry to data address. */ -#define R_X86_64_JMP_SLOT 7 /* Set GOT entry to code address. */ -#define R_X86_64_RELATIVE 8 /* Add load address of shared object. */ -#define R_X86_64_GOTPCREL 9 /* Add 32 bit signed pcrel offset to GOT. */ -#define R_X86_64_32 10 /* Add 32 bit zero extended symbol value */ -#define R_X86_64_32S 11 /* Add 32 bit sign extended symbol value */ -#define R_X86_64_16 12 /* Add 16 bit zero extended symbol value */ -#define R_X86_64_PC16 13 /* Add 16 bit signed extended pc relative symbol value */ -#define R_X86_64_8 14 /* Add 8 bit zero extended symbol value */ -#define R_X86_64_PC8 15 /* Add 8 bit signed extended pc relative symbol value */ -#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ -#define R_X86_64_DTPOFF64 17 /* Offset in TLS block */ -#define R_X86_64_TPOFF64 18 /* Offset in static TLS block */ -#define R_X86_64_TLSGD 19 /* PC relative offset to GD GOT entry */ -#define R_X86_64_TLSLD 20 /* PC relative offset to LD GOT entry */ -#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ -#define R_X86_64_GOTTPOFF 22 /* PC relative offset to IE GOT entry */ -#define R_X86_64_TPOFF32 23 /* Offset in static TLS block */ - - -#endif /* !_SYS_ELF_COMMON_H_ */ diff --git a/minix/include/sys/ioc_net.h b/minix/include/sys/ioc_net.h deleted file mode 100644 index dcd88eda7..000000000 --- a/minix/include/sys/ioc_net.h +++ /dev/null @@ -1,99 +0,0 @@ -/* sys/ioc_net.h - NetBSD-friendly version of Minix net/ioctl.h - */ -/* net/ioctl.h - Network ioctl() command codes. Author: Kees J. Bot - * 23 Nov 2002 - * - */ - -#ifndef _SYS_IOC_NET_H_ -#define _SYS_IOC_NET_H_ - -#include -#include - -#include -#ifndef socklen_t -typedef __socklen_t socklen_t; -#define socklen_t __socklen_t -#endif - -#define MSG_CONTROL_MAX (1024 - sizeof(socklen_t)) -struct msg_control -{ - char msg_control[MSG_CONTROL_MAX]; - socklen_t msg_controllen; -}; - - -/* Network ioctls. */ -#define NWIOSETHOPT _IOW('n', 16, struct nwio_ethopt) -#define NWIOGETHOPT _IOR('n', 17, struct nwio_ethopt) -#define NWIOGETHSTAT _IOR('n', 18, struct nwio_ethstat) - -#define NWIOARPGIP _IOWR('n',20, struct nwio_arp) -#define NWIOARPGNEXT _IOWR('n',21, struct nwio_arp) -#define NWIOARPSIP _IOW ('n',22, struct nwio_arp) -#define NWIOARPDIP _IOW ('n',23, struct nwio_arp) - -#define NWIOSIPCONF2 _IOW('n', 32, struct nwio_ipconf2) -#define NWIOSIPCONF _IOW('n', 32, struct nwio_ipconf) -#define NWIOGIPCONF2 _IOR('n', 33, struct nwio_ipconf2) -#define NWIOGIPCONF _IOR('n', 33, struct nwio_ipconf) -#define NWIOSIPOPT _IOW('n', 34, struct nwio_ipopt) -#define NWIOGIPOPT _IOR('n', 35, struct nwio_ipopt) - -#define NWIOGIPOROUTE _IOWR('n', 40, struct nwio_route) -#define NWIOSIPOROUTE _IOW ('n', 41, struct nwio_route) -#define NWIODIPOROUTE _IOW ('n', 42, struct nwio_route) -#define NWIOGIPIROUTE _IOWR('n', 43, struct nwio_route) -#define NWIOSIPIROUTE _IOW ('n', 44, struct nwio_route) -#define NWIODIPIROUTE _IOW ('n', 45, struct nwio_route) - -#define NWIOSTCPCONF _IOW('n', 48, struct nwio_tcpconf) -#define NWIOGTCPCONF _IOR('n', 49, struct nwio_tcpconf) -#define NWIOTCPCONN _IOW('n', 50, struct nwio_tcpcl) -#define NWIOTCPLISTEN _IOW('n', 51, struct nwio_tcpcl) -#define NWIOTCPATTACH _IOW('n', 52, struct nwio_tcpatt) -#define NWIOTCPSHUTDOWN _IO ('n', 53) -#define NWIOSTCPOPT _IOW('n', 54, struct nwio_tcpopt) -#define NWIOGTCPOPT _IOR('n', 55, struct nwio_tcpopt) -#define NWIOTCPPUSH _IO ('n', 56) -#define NWIOTCPLISTENQ _IOW('n', 57, int) -#define NWIOGTCPCOOKIE _IOR('n', 58, struct tcp_cookie) -#define NWIOTCPACCEPTTO _IOW('n', 59, struct tcp_cookie) -#define NWIOTCPGERROR _IOR('n', 60, int) - -#define NWIOSUDPOPT _IOW('n', 64, struct nwio_udpopt) -#define NWIOGUDPOPT _IOR('n', 65, struct nwio_udpopt) -#define NWIOUDPPEEK _IOR('n', 66, struct udp_io_hdr) - -#define NWIOGUDSFADDR _IOR ('n', 67, struct sockaddr_un) /* recvfrom() */ -#define NWIOSUDSTADDR _IOW ('n', 68, struct sockaddr_un) /* sendto() */ -#define NWIOSUDSADDR _IOW ('n', 69, struct sockaddr_un) /* bind() */ -#define NWIOGUDSADDR _IOR ('n', 70, struct sockaddr_un) /* getsockname() */ -#define NWIOGUDSPADDR _IOR ('n', 71, struct sockaddr_un) /* getpeername() */ -#define NWIOSUDSTYPE _IOW ('n', 72, int) /* socket() */ -#define NWIOSUDSBLOG _IOW ('n', 73, int) /* listen() */ -#define NWIOSUDSCONN _IOW ('n', 74, struct sockaddr_un) /* connect() */ -#define NWIOSUDSSHUT _IOW ('n', 75, int) /* shutdown() */ -#define NWIOSUDSPAIR _IOW ('n', 76, dev_t) /* socketpair() */ -#define NWIOSUDSACCEPT _IOW ('n', 77, struct sockaddr_un) /* accept() */ -#define NWIOSUDSCTRL _IOW ('n', 78, struct msg_control) /* sendmsg() */ -#define NWIOGUDSCTRL _IOWR('n', 79, struct msg_control) /* recvmsg() */ - -#define NWIOSPSIPOPT _IOW('n', 80, struct nwio_psipopt) -#define NWIOGPSIPOPT _IOR('n', 81, struct nwio_psipopt) - -/* setsockopt/setsockopt for unix domain sockets */ -#define NWIOGUDSSOTYPE _IOR('n', 90, int) /* SO_TYPE */ -#define NWIOGUDSPEERCRED _IOR('n', 91, struct uucred) /* SO_PEERCRED */ -#define NWIOGUDSSNDBUF _IOR('n', 92, size_t) /* SO_SNDBUF */ -#define NWIOSUDSSNDBUF _IOW('n', 93, size_t) /* SO_SNDBUF */ -#define NWIOGUDSRCVBUF _IOR('n', 94, size_t) /* SO_RCVBUF */ -#define NWIOSUDSRCVBUF _IOW('n', 95, size_t) /* SO_RCVBUF */ - -#endif /* _NET__IOCTL_H */ - -/* - * $PchId: ioctl.h,v 1.2 2003/07/25 14:34:03 philip Exp $ - */ diff --git a/minix/include/varargs.h b/minix/include/varargs.h deleted file mode 100644 index 8d33ca060..000000000 --- a/minix/include/varargs.h +++ /dev/null @@ -1,45 +0,0 @@ -/* $NetBSD: varargs.h,v 1.3 2008/04/28 20:22:54 martin Exp $ */ - -/*- - * Copyright (c) 2003 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Matthias Scheler. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _VARARGS_H_ -#define _VARARGS_H_ - -#include - -#if __GNUC_PREREQ__(3, 3) -#error "GCC 3.3 and newer no longer implements ." -#error "Revise your code to use ." -#else -#include -#endif - -#endif /* !_VARARGS_H_ */ - diff --git a/minix/kernel/arch/earm/phys_memset.S b/minix/kernel/arch/earm/phys_memset.S deleted file mode 100644 index b3645fb31..000000000 --- a/minix/kernel/arch/earm/phys_memset.S +++ /dev/null @@ -1,304 +0,0 @@ -/* $NetBSD: memset.S,v 1.7 2013/12/02 21:21:33 joerg Exp $ */ - -/* - * Copyright 2003 Wasabi Systems, Inc. - * All rights reserved. - * - * Written by Steve C. Woodford for Wasabi Systems, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed for the NetBSD Project by - * Wasabi Systems, Inc. - * 4. The name of Wasabi Systems, Inc. may not be used to endorse - * or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright (c) 1995 Mark Brinicombe. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Mark Brinicombe. - * 4. The name of the company nor the name of the author may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include - -#if 0 && defined(__minix) -#if defined(__ARM_EABI__) && !defined(_BZERO) -ENTRY(__aeabi_memset) - mov r3, r1 - mov r1, r2 - mov r2, r3 - b memset -END(__aeabi_memset) -STRONG_ALIAS(__aeabi_memset4, __aeabi_memset) -STRONG_ALIAS(__aeabi_memset8, __aeabi_memset) - -ENTRY(__aeabi_memclr) - mov r2, r1 - mov r1, #0 - b memset -END(__aeabi_memclr) -STRONG_ALIAS(__aeabi_memclr4, __aeabi_memclr) -STRONG_ALIAS(__aeabi_memclr8, __aeabi_memclr) -#endif -#endif /* #if 0 && defined(__minix) */ - -/* - * memset: Sets a block of memory to the specified value - * - * On entry: - * r0 - dest address - * r1 - byte to write - * r2 - number of bytes to write - * - * On exit: - * r0 - dest address - */ -#ifdef _BZERO -/* LINTSTUB: Func: void bzero(void *, size_t) */ -ENTRY(bzero) - mov r3, #0x00 -#else -#if defined(__minix) -/* LINTSTUB: Func: void *phys_memset(void *, int, size_t) */ -ENTRY(phys_memset) -#else -/* LINTSTUB: Func: void *memset(void *, int, size_t) */ -ENTRY(memset) -#endif - and r3, r1, #0xff /* We deal with bytes */ - mov r1, r2 -#endif - cmp r1, #0x04 /* Do we have less than 4 bytes */ - mov ip, r0 - blt .Lmemset_lessthanfour - - /* Ok first we will word align the address */ - ands r2, ip, #0x03 /* Get the bottom two bits */ - bne .Lmemset_wordunaligned /* The address is not word aligned */ - - /* We are now word aligned */ -.Lmemset_wordaligned: -#ifndef _BZERO - orr r3, r3, r3, lsl #8 /* Extend value to 16-bits */ -#endif -#ifdef _ARM_ARCH_DWORD_OK - tst ip, #0x04 /* Quad-align for Xscale */ -#else - cmp r1, #0x10 -#endif -#ifndef _BZERO - orr r3, r3, r3, lsl #16 /* Extend value to 32-bits */ -#endif -#ifdef _ARM_ARCH_DWORD_OK - subne r1, r1, #0x04 /* Quad-align if necessary */ - strne r3, [ip], #0x04 - cmp r1, #0x10 -#endif - blt .Lmemset_loop4 /* If less than 16 then use words */ - mov r2, r3 /* Duplicate data */ - cmp r1, #0x80 /* If < 128 then skip the big loop */ - blt .Lmemset_loop32 - - /* Do 128 bytes at a time */ -.Lmemset_loop128: - subs r1, r1, #0x80 -#ifdef _ARM_ARCH_DWORD_OK - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 -#else - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} -#endif - bgt .Lmemset_loop128 -#if defined(__minix) - moveq r0, #0 -#endif - RETc(eq) /* Zero length so just exit */ - - add r1, r1, #0x80 /* Adjust for extra sub */ - - /* Do 32 bytes at a time */ -.Lmemset_loop32: - subs r1, r1, #0x20 -#ifdef _ARM_ARCH_DWORD_OK - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 -#else - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} -#endif - bgt .Lmemset_loop32 -#if defined(__minix) - moveq r0, #0 -#endif - RETc(eq) /* Zero length so just exit */ - - adds r1, r1, #0x10 /* Partially adjust for extra sub */ - - /* Deal with 16 bytes or more */ -#ifdef _ARM_ARCH_DWORD_OK - strdge r2, r3, [ip], #0x08 - strdge r2, r3, [ip], #0x08 -#else - stmiage ip!, {r2-r3} - stmiage ip!, {r2-r3} -#endif -#if defined(__minix) - moveq r0, #0 -#endif - RETc(eq) /* Zero length so just exit */ - - addlt r1, r1, #0x10 /* Possibly adjust for extra sub */ - - /* We have at least 4 bytes so copy as words */ -.Lmemset_loop4: - subs r1, r1, #0x04 - strge r3, [ip], #0x04 - bgt .Lmemset_loop4 -#if defined(__minix) - moveq r0, #0 -#endif - RETc(eq) /* Zero length so just exit */ - -#ifdef _ARM_ARCH_DWORD_OK - /* Compensate for 64-bit alignment check */ - adds r1, r1, #0x04 -#if defined(__minix) - moveq r0, #0 -#endif - RETc(eq) - cmp r1, #2 -#else - cmp r1, #-2 -#endif - - strb r3, [ip], #0x01 /* Set 1 byte */ - strbge r3, [ip], #0x01 /* Set another byte */ - strbgt r3, [ip] /* and a third */ -#if defined(__minix) - mov r0, #0 -#endif - RET /* Exit */ - -.Lmemset_wordunaligned: - rsb r2, r2, #0x004 - strb r3, [ip], #0x01 /* Set 1 byte */ - cmp r2, #0x02 - strbge r3, [ip], #0x01 /* Set another byte */ - sub r1, r1, r2 - strbgt r3, [ip], #0x01 /* and a third */ - cmp r1, #0x04 /* More than 4 bytes left? */ - bge .Lmemset_wordaligned /* Yup */ - -.Lmemset_lessthanfour: - cmp r1, #0x00 -#if defined(__minix) - moveq r0, #0 -#endif - RETc(eq) /* Zero length so exit */ - strb r3, [ip], #0x01 /* Set 1 byte */ - cmp r1, #0x02 - strbge r3, [ip], #0x01 /* Set another byte */ - strbgt r3, [ip] /* and a third */ -#if defined(__minix) - mov r0, #0 -#endif - RET /* Exit */ -#ifdef _BZERO -END(bzero) -#else -#if !defined(__minix) -END(memset) -#else -END(phys_memset) -#endif -#endif - -#if defined(__minix) -LABEL(memset_fault) /* kernel can send us here */ - mov r0, #0 - RET - -LABEL(memset_fault_in_kernel) /* kernel can send us here */ - mrc p15, 0, r0, c6, c0, 0 /* Read DFAR */ - RET -#endif diff --git a/minix/lib/libc/sys/poll.c b/minix/lib/libc/sys/poll.c deleted file mode 100644 index 0c375f480..000000000 --- a/minix/lib/libc/sys/poll.c +++ /dev/null @@ -1,102 +0,0 @@ -/* $NetBSD: poll.c,v 1.3 2008/04/29 05:46:08 martin Exp $ */ - -/*- - * Copyright (c) 2003 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Charles Blundell. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "namespace.h" -#include - -#include -#include -#include -#include -#include - -int -poll(struct pollfd *p, nfds_t nfds, int timout) -{ - fd_set rd, wr, except; - struct timeval tv; - nfds_t i; - int highfd, rval; - - /* - * select cannot tell us much wrt POLL*BAND, POLLPRI, POLLHUP or - * POLLNVAL. - */ - FD_ZERO(&rd); - FD_ZERO(&wr); - FD_ZERO(&except); - - highfd = -1; - for (i = 0; i < nfds; i++) { - p[i].revents = 0; - if (p[i].fd < 0) - continue; - if (p[i].fd >= FD_SETSIZE) { - errno = EINVAL; - return -1; - } - if (p[i].fd > highfd) - highfd = p[i].fd; - - if (p[i].events & (POLLIN|POLLRDNORM)) - FD_SET(p[i].fd, &rd); - if (p[i].events & (POLLOUT|POLLWRNORM|POLLWRBAND)) - FD_SET(p[i].fd, &wr); - if (p[i].events & (POLLRDBAND|POLLPRI)) - FD_SET(p[i].fd, &except); - } - - tv.tv_sec = timout / 1000; - tv.tv_usec = (timout % 1000) * 1000; - - rval = select(highfd + 1, &rd, &wr, &except, - timout == -1 ? NULL : &tv); - if (rval <= 0) - return rval; - - rval = 0; - for (i = 0; i < nfds; i++) { - if (p[i].fd < 0) - continue; - if (FD_ISSET(p[i].fd, &rd)) - p[i].revents |= p[i].events & (POLLIN|POLLRDNORM); - if (FD_ISSET(p[i].fd, &wr)) - p[i].revents |= - p[i].events & (POLLOUT|POLLWRNORM|POLLWRBAND); - if (FD_ISSET(p[i].fd, &except)) - p[i].revents |= p[i].events & (POLLRDBAND|POLLPRI); - /* XXX: POLLERR/POLLHUP/POLLNVAL? */ - if (p[i].revents != 0) - rval++; - } - return rval; -} diff --git a/minix/lib/libsockevent/sockevent.c b/minix/lib/libsockevent/sockevent.c deleted file mode 100644 index 31dbf2574..000000000 --- a/minix/lib/libsockevent/sockevent.c +++ /dev/null @@ -1,2590 +0,0 @@ -/* Socket event dispatching library - by D.C. van Moolenbroek */ - -#include -#include -#include -#include - -#include "sockevent_proc.h" - -#define US 1000000UL /* microseconds per second */ - -#define SOCKHASH_SLOTS 256 /* # slots in ID-to-sock hash table */ - -static SLIST_HEAD(, sock) sockhash[SOCKHASH_SLOTS]; - -static SLIST_HEAD(, sock) socktimer; - -static minix_timer_t sockevent_timer; - -static SIMPLEQ_HEAD(, sock) sockevent_pending; - -static sockevent_socket_cb_t sockevent_socket_cb = NULL; - -static int sockevent_working; - -static void socktimer_del(struct sock * sock); -static void sockevent_cancel_send(struct sock * sock, - struct sockevent_proc * spr, int err); -static void sockevent_cancel_recv(struct sock * sock, - struct sockevent_proc * spr, int err); - -/* - * Initialize the hash table of sock objects. - */ -static void -sockhash_init(void) -{ - unsigned int slot; - - for (slot = 0; slot < __arraycount(sockhash); slot++) - SLIST_INIT(&sockhash[slot]); -} - -/* - * Given a socket identifier, return a hash table slot number. - */ -static unsigned int -sockhash_slot(sockid_t id) -{ - - /* - * The idea of the shift is that a socket driver may offer multiple - * classes of sockets, and put the class in the higher bits. The shift - * aims to prevent that all classes' first sockets end up in the same - * hash slot. - */ - return (id + (id >> 16)) % SOCKHASH_SLOTS; -} - -/* - * Obtain a sock object from the hash table using its unique identifier. - * Return a pointer to the object if found, or NULL otherwise. - */ -static struct sock * -sockhash_get(sockid_t id) -{ - struct sock *sock; - unsigned int slot; - - slot = sockhash_slot(id); - - SLIST_FOREACH(sock, &sockhash[slot], sock_hash) { - if (sock->sock_id == id) - return sock; - } - - return NULL; -} - -/* - * Add a sock object to the hash table. The sock object must have a valid ID - * in its 'sock_id' field, and must not be in the hash table already. - */ -static void -sockhash_add(struct sock * sock) -{ - unsigned int slot; - - slot = sockhash_slot(sock->sock_id); - - SLIST_INSERT_HEAD(&sockhash[slot], sock, sock_hash); -} - -/* - * Remove a sock object from the hash table. The sock object must be in the - * hash table. - */ -static void -sockhash_del(struct sock * sock) -{ - unsigned int slot; - - slot = sockhash_slot(sock->sock_id); - - /* This macro is O(n). */ - SLIST_REMOVE(&sockhash[slot], sock, sock, sock_hash); -} - -/* - * Reset a socket object to a proper initial state, with a particular socket - * identifier, a SOCK_ type, and a socket operations table. The socket is - * added to the ID-to-object hash table. This function always succeeds. - */ -static void -sockevent_reset(struct sock * sock, sockid_t id, int domain, int type, - const struct sockevent_ops * ops) -{ - - assert(sock != NULL); - - memset(sock, 0, sizeof(*sock)); - - sock->sock_id = id; - sock->sock_domain = domain; - sock->sock_type = type; - - sock->sock_slowat = 1; - sock->sock_rlowat = 1; - - sock->sock_ops = ops; - sock->sock_proc = NULL; - sock->sock_select.ss_endpt = NONE; - - sockhash_add(sock); -} - -/* - * Initialize a new socket that will serve as an accepted socket on the given - * listening socket 'sock'. The new socket is given as 'newsock', and its new - * socket identifier is given as 'newid'. This function always succeeds. - */ -void -sockevent_clone(struct sock * sock, struct sock * newsock, sockid_t newid) -{ - - sockevent_reset(newsock, newid, (int)sock->sock_domain, - sock->sock_type, sock->sock_ops); - - /* These are the settings that are currently inherited. */ - newsock->sock_opt = sock->sock_opt & ~SO_ACCEPTCONN; - newsock->sock_linger = sock->sock_linger; - newsock->sock_stimeo = sock->sock_stimeo; - newsock->sock_rtimeo = sock->sock_rtimeo; - newsock->sock_slowat = sock->sock_slowat; - newsock->sock_rlowat = sock->sock_rlowat; - - newsock->sock_flags |= SFL_CLONED; -} - -/* - * A new socket has just been accepted. The corresponding listening socket is - * given as 'sock'. The new socket has ID 'newid', and if it had not already - * been added to the hash table through sockevent_clone() before, 'newsock' is - * a non-NULL pointer which identifies the socket object to clone into. - */ -static void -sockevent_accepted(struct sock * sock, struct sock * newsock, sockid_t newid) -{ - - if (newsock == NULL) { - if ((newsock = sockhash_get(newid)) == NULL) - panic("libsockdriver: socket driver returned unknown " - "ID %d from accept callback", newid); - } else - sockevent_clone(sock, newsock, newid); - - assert(newsock->sock_flags & SFL_CLONED); - newsock->sock_flags &= ~SFL_CLONED; -} - -/* - * Allocate a sock object, by asking the socket driver for one. On success, - * return OK, with a pointer to the new object stored in 'sockp'. This new - * object has all its fields set to initial values, in part based on the given - * parameters. On failure, return an error code. Failure has two typical - * cause: either the given domain, type, protocol combination is not supported, - * or the socket driver is out of sockets (globally or for this combination). - */ -static int -sockevent_alloc(int domain, int type, int protocol, endpoint_t user_endpt, - struct sock ** sockp) -{ - struct sock *sock; - const struct sockevent_ops *ops; - sockid_t r; - - /* - * Verify that the given domain is sane. Unlike the type and protocol, - * the domain is already verified by VFS, so we do not limit ourselves - * here. The result is that we can store the domain in just a byte. - */ - if (domain < 0 || domain > UINT8_MAX) - return EAFNOSUPPORT; - - /* Make sure that the library has actually been initialized. */ - if (sockevent_socket_cb == NULL) - panic("libsockevent: not initialized"); - - sock = NULL; - ops = NULL; - - /* - * Ask the socket driver to create a socket for the given combination - * of domain, type, and protocol. If so, let it return a new sock - * object, a unique socket identifier for that object, and an - * operations table for it. - */ - if ((r = sockevent_socket_cb(domain, type, protocol, user_endpt, &sock, - &ops)) < 0) - return r; - - assert(sock != NULL); - assert(ops != NULL); - - sockevent_reset(sock, r, domain, type, ops); - - *sockp = sock; - return OK; -} - -/* - * Free a previously allocated sock object. - */ -static void -sockevent_free(struct sock * sock) -{ - const struct sockevent_ops *ops; - - assert(sock->sock_proc == NULL); - - socktimer_del(sock); - - sockhash_del(sock); - - /* - * Invalidate the operations table on the socket, before freeing the - * socket. This allows us to detect cases where sockevent functions - * are called on sockets that have already been freed. - */ - ops = sock->sock_ops; - sock->sock_ops = NULL; - - assert(ops != NULL); - assert(ops->sop_free != NULL); - - ops->sop_free(sock); -} - -/* - * Create a new socket. - */ -static sockid_t -sockevent_socket(int domain, int type, int protocol, endpoint_t user_endpt) -{ - struct sock *sock; - int r; - - if ((r = sockevent_alloc(domain, type, protocol, user_endpt, - &sock)) != OK) - return r; - - return sock->sock_id; -} - -/* - * Create a pair of connected sockets. - */ -static int -sockevent_socketpair(int domain, int type, int protocol, endpoint_t user_endpt, - sockid_t id[2]) -{ - struct sock *sock1, *sock2; - int r; - - if ((r = sockevent_alloc(domain, type, protocol, user_endpt, - &sock1)) != OK) - return r; - - /* Creating socket pairs is not always supported. */ - if (sock1->sock_ops->sop_pair == NULL) { - sockevent_free(sock1); - - return EOPNOTSUPP; - } - - if ((r = sockevent_alloc(domain, type, protocol, user_endpt, - &sock2)) != OK) { - sockevent_free(sock1); - - return r; - } - - assert(sock1->sock_ops == sock2->sock_ops); - - r = sock1->sock_ops->sop_pair(sock1, sock2, user_endpt); - - if (r != OK) { - sockevent_free(sock2); - sockevent_free(sock1); - - return r; - } - - id[0] = sock1->sock_id; - id[1] = sock2->sock_id; - return OK; -} - -/* - * A send request returned EPIPE. If desired, send a SIGPIPE signal to the - * user process that issued the request. - */ -static void -sockevent_sigpipe(struct sock * sock, endpoint_t user_endpt, int flags) -{ - - /* - * POSIX says that pipe signals should be generated for SOCK_STREAM - * sockets. Linux does just this, NetBSD raises signals for all socket - * types. - */ - if (sock->sock_type != SOCK_STREAM) - return; - - /* - * Why would there be fewer than four ways to do the same thing? - * O_NOSIGPIPE, MSG_NOSIGNAL, SO_NOSIGPIPE, and of course blocking - * SIGPIPE. VFS already sets MSG_NOSIGNAL for calls on sockets with - * O_NOSIGPIPE. The fact that SO_NOSIGPIPE is a thing, is also the - * reason why we cannot let VFS handle signal generation altogether. - */ - if (flags & MSG_NOSIGNAL) - return; - if (sock->sock_opt & SO_NOSIGPIPE) - return; - - /* - * Send a SIGPIPE signal to the user process. Unfortunately we cannot - * guarantee that the SIGPIPE reaches the user process before the send - * call returns. Usually, the scheduling priorities of system services - * are such that the signal is likely to arrive first anyway, but if - * timely arrival of the signal is required, a more fundamental change - * to the system would be needed. - */ - sys_kill(user_endpt, SIGPIPE); -} - -/* - * Suspend a request without data, that is, a bind, connect, accept, or close - * request. - */ -static void -sockevent_suspend(struct sock * sock, unsigned int event, - const struct sockdriver_call * __restrict call, endpoint_t user_endpt) -{ - struct sockevent_proc *spr, **sprp; - - /* There is one slot for each process, so this should never fail. */ - if ((spr = sockevent_proc_alloc()) == NULL) - panic("libsockevent: too many suspended processes"); - - spr->spr_next = NULL; - spr->spr_event = event; - spr->spr_timer = FALSE; - spr->spr_call = *call; - spr->spr_endpt = user_endpt; - - /* - * Add the request to the tail of the queue. This operation is O(n), - * but the number of suspended requests per socket is expected to be - * low at all times. - */ - for (sprp = &sock->sock_proc; *sprp != NULL; - sprp = &(*sprp)->spr_next); - *sprp = spr; -} - -/* - * Suspend a request with data, that is, a send or receive request. - */ -static void -sockevent_suspend_data(struct sock * sock, unsigned int event, int timer, - const struct sockdriver_call * __restrict call, endpoint_t user_endpt, - const struct sockdriver_data * __restrict data, size_t len, size_t off, - const struct sockdriver_data * __restrict ctl, socklen_t ctl_len, - socklen_t ctl_off, int flags, int rflags, clock_t time) -{ - struct sockevent_proc *spr, **sprp; - - /* There is one slot for each process, so this should never fail. */ - if ((spr = sockevent_proc_alloc()) == NULL) - panic("libsockevent: too many suspended processes"); - - spr->spr_next = NULL; - spr->spr_event = event; - spr->spr_timer = timer; - spr->spr_call = *call; - spr->spr_endpt = user_endpt; - sockdriver_pack_data(&spr->spr_data, call, data, len); - spr->spr_datalen = len; - spr->spr_dataoff = off; - sockdriver_pack_data(&spr->spr_ctl, call, ctl, ctl_len); - spr->spr_ctllen = ctl_len; - spr->spr_ctloff = ctl_off; - spr->spr_flags = flags; - spr->spr_rflags = rflags; - spr->spr_time = time; - - /* - * Add the request to the tail of the queue. This operation is O(n), - * but the number of suspended requests per socket is expected to be - * low at all times. - */ - for (sprp = &sock->sock_proc; *sprp != NULL; - sprp = &(*sprp)->spr_next); - *sprp = spr; -} - -/* - * Return TRUE if there are any suspended requests on the given socket's queue - * that match any of the events in the given event mask, or FALSE otherwise. - */ -static int -sockevent_has_suspended(struct sock * sock, unsigned int mask) -{ - struct sockevent_proc *spr; - - for (spr = sock->sock_proc; spr != NULL; spr = spr->spr_next) - if (spr->spr_event & mask) - return TRUE; - - return FALSE; -} - -/* - * Check whether the given call is on the given socket's queue of suspended - * requests. If so, remove it from the queue and return a pointer to the - * suspension data structure. The caller is then responsible for freeing that - * data structure using sockevent_proc_free(). If the call was not found, the - * function returns NULL. - */ -static struct sockevent_proc * -sockevent_unsuspend(struct sock * sock, const struct sockdriver_call * call) -{ - struct sockevent_proc *spr, **sprp; - - /* Find the suspended request being canceled. */ - for (sprp = &sock->sock_proc; (spr = *sprp) != NULL; - sprp = &spr->spr_next) { - if (spr->spr_call.sc_endpt == call->sc_endpt && - spr->spr_call.sc_req == call->sc_req) { - /* Found; remove and return it. */ - *sprp = spr->spr_next; - - return spr; - } - } - - return NULL; -} - -/* - * Attempt to resume the given suspended request for the given socket object. - * Return TRUE if the suspended request has been fully resumed and can be - * removed from the queue of suspended requests, or FALSE if it has not been - * fully resumed and should stay on the queue. In the latter case, no - * resumption will be attempted for other suspended requests of the same type. - */ -static int -sockevent_resume(struct sock * sock, struct sockevent_proc * spr) -{ - struct sock *newsock; - struct sockdriver_data data, ctl; - char addr[SOCKADDR_MAX]; - socklen_t addr_len; - size_t len, min; - sockid_t r; - - switch (spr->spr_event) { - case SEV_CONNECT: - /* - * If the connect call was suspended for the purpose of - * intercepting resumption, simply remove it from the queue. - */ - if (spr->spr_call.sc_endpt == NONE) - return TRUE; - - /* FALLTHROUGH */ - case SEV_BIND: - if ((r = sock->sock_err) != OK) - sock->sock_err = OK; - - sockdriver_reply_generic(&spr->spr_call, r); - - return TRUE; - - case SEV_ACCEPT: - /* - * A previous accept call may not have blocked on a socket that - * was not in listening mode. - */ - assert(sock->sock_opt & SO_ACCEPTCONN); - - addr_len = 0; - newsock = NULL; - - /* - * This call is suspended, which implies that the call table - * pointer has already tested to be non-NULL. - */ - if ((r = sock->sock_ops->sop_accept(sock, - (struct sockaddr *)&addr, &addr_len, spr->spr_endpt, - &newsock)) == SUSPEND) - return FALSE; - - if (r >= 0) { - assert(addr_len <= sizeof(addr)); - - sockevent_accepted(sock, newsock, r); - } - - sockdriver_reply_accept(&spr->spr_call, r, - (struct sockaddr *)&addr, addr_len); - - return TRUE; - - case SEV_SEND: - if (sock->sock_err != OK || (sock->sock_flags & SFL_SHUT_WR)) { - if (spr->spr_dataoff > 0 || spr->spr_ctloff > 0) - r = (int)spr->spr_dataoff; - else if ((r = sock->sock_err) != OK) - sock->sock_err = OK; - else - r = EPIPE; - } else { - sockdriver_unpack_data(&data, &spr->spr_call, - &spr->spr_data, spr->spr_datalen); - sockdriver_unpack_data(&ctl, &spr->spr_call, - &spr->spr_ctl, spr->spr_ctllen); - - len = spr->spr_datalen - spr->spr_dataoff; - - min = sock->sock_slowat; - if (min > len) - min = len; - - /* - * As mentioned elsewhere, we do not save the address - * upon suspension so we cannot supply it anymore here. - */ - r = sock->sock_ops->sop_send(sock, &data, len, - &spr->spr_dataoff, &ctl, - spr->spr_ctllen - spr->spr_ctloff, - &spr->spr_ctloff, NULL, 0, spr->spr_endpt, - spr->spr_flags, min); - - assert(r <= 0); - - if (r == SUSPEND) - return FALSE; - - /* - * If an error occurred but some data were already - * sent, return the progress rather than the error. - * Note that if the socket driver detects an - * asynchronous error during the send, it itself must - * perform this check and call sockevent_set_error() as - * needed, to make sure the error does not get lost. - */ - if (spr->spr_dataoff > 0 || spr->spr_ctloff > 0) - r = spr->spr_dataoff; - } - - if (r == EPIPE) - sockevent_sigpipe(sock, spr->spr_endpt, - spr->spr_flags); - - sockdriver_reply_generic(&spr->spr_call, r); - - return TRUE; - - case SEV_RECV: - addr_len = 0; - - if (sock->sock_flags & SFL_SHUT_RD) - r = SOCKEVENT_EOF; - else { - len = spr->spr_datalen - spr->spr_dataoff; - - if (sock->sock_err == OK) { - min = sock->sock_rlowat; - if (min > len) - min = len; - } else - min = 0; - - sockdriver_unpack_data(&data, &spr->spr_call, - &spr->spr_data, spr->spr_datalen); - sockdriver_unpack_data(&ctl, &spr->spr_call, - &spr->spr_ctl, spr->spr_ctllen); - - r = sock->sock_ops->sop_recv(sock, &data, len, - &spr->spr_dataoff, &ctl, - spr->spr_ctllen - spr->spr_ctloff, - &spr->spr_ctloff, (struct sockaddr *)&addr, - &addr_len, spr->spr_endpt, spr->spr_flags, min, - &spr->spr_rflags); - - /* - * If the call remains suspended but a socket error is - * pending, return the pending socket error instead. - */ - if (r == SUSPEND) { - if (sock->sock_err == OK) - return FALSE; - - r = SOCKEVENT_EOF; - } - - assert(addr_len <= sizeof(addr)); - } - - /* - * If the receive call reported success, or if some data were - * already received, return the (partial) result. Otherwise, - * return a pending error if any, or otherwise a regular error - * or 0 for EOF. - */ - if (r == OK || spr->spr_dataoff > 0 || spr->spr_ctloff > 0) - r = (int)spr->spr_dataoff; - else if (sock->sock_err != OK) { - r = sock->sock_err; - - sock->sock_err = OK; - } else if (r == SOCKEVENT_EOF) - r = 0; /* EOF */ - - sockdriver_reply_recv(&spr->spr_call, r, spr->spr_ctloff, - (struct sockaddr *)&addr, addr_len, spr->spr_rflags); - - return TRUE; - - case SEV_CLOSE: - sockdriver_reply_generic(&spr->spr_call, OK); - - return TRUE; - - default: - panic("libsockevent: process suspended on unknown event 0x%x", - spr->spr_event); - } -} - -/* - * Return TRUE if the given socket is ready for reading for a select call, or - * FALSE otherwise. - */ -static int -sockevent_test_readable(struct sock * sock) -{ - int r; - - /* - * The meaning of "ready-to-read" depends on whether the socket is a - * listening socket or not. For the former, it is a test on whether - * there are any new sockets to accept. However, shutdown flags take - * precedence in both cases. - */ - if (sock->sock_flags & SFL_SHUT_RD) - return TRUE; - - if (sock->sock_err != OK) - return TRUE; - - /* - * Depending on whether this is a listening-mode socket, test whether - * either accepts or receives would block. - */ - if (sock->sock_opt & SO_ACCEPTCONN) { - if (sock->sock_ops->sop_test_accept == NULL) - return TRUE; - - r = sock->sock_ops->sop_test_accept(sock); - } else { - if (sock->sock_ops->sop_test_recv == NULL) - return TRUE; - - r = sock->sock_ops->sop_test_recv(sock, sock->sock_rlowat, - NULL); - } - - return (r != SUSPEND); -} - -/* - * Return TRUE if the given socket is ready for writing for a select call, or - * FALSE otherwise. - */ -static int -sockevent_test_writable(struct sock * sock) -{ - int r; - - if (sock->sock_err != OK) - return TRUE; - - if (sock->sock_flags & SFL_SHUT_WR) - return TRUE; - - if (sock->sock_ops->sop_test_send == NULL) - return TRUE; - - /* - * Test whether sends would block. The low send watermark is relevant - * for stream-type sockets only. - */ - r = sock->sock_ops->sop_test_send(sock, sock->sock_slowat); - - return (r != SUSPEND); -} - -/* - * Test whether any of the given select operations are ready on the given - * socket. Return the subset of ready operations; zero if none. - */ -static unsigned int -sockevent_test_select(struct sock * sock, unsigned int ops) -{ - unsigned int ready_ops; - - assert(!(ops & ~(SDEV_OP_RD | SDEV_OP_WR | SDEV_OP_ERR))); - - /* - * We do not support the "bind in progress" case here. If a blocking - * bind call is in progress, the file descriptor should not be ready - * for either reading or writing. Currently, socket drivers will have - * to cover this case themselves. Otherwise we would have to check the - * queue of suspended calls, or create a custom flag for this. - */ - - ready_ops = 0; - - if ((ops & SDEV_OP_RD) && sockevent_test_readable(sock)) - ready_ops |= SDEV_OP_RD; - - if ((ops & SDEV_OP_WR) && sockevent_test_writable(sock)) - ready_ops |= SDEV_OP_WR; - - /* TODO: OOB receive support. */ - - return ready_ops; -} - -/* - * Fire the given mask of events on the given socket object now. - */ -static void -sockevent_fire(struct sock * sock, unsigned int mask) -{ - struct sockevent_proc *spr, **sprp; - unsigned int r, flag, ops; - - /* - * A completed connection attempt (successful or not) also always - * implies that the socket becomes writable. For convenience we - * enforce this rule here, because it is easy to forget. Note that in - * any case, a suspended connect request should be the first in the - * list, so we do not risk returning 0 from a connect call as a result - * of sock_err getting eaten by another resumed call. - */ - if (mask & SEV_CONNECT) - mask |= SEV_SEND; - - /* - * First try resuming regular system calls. - */ - for (sprp = &sock->sock_proc; (spr = *sprp) != NULL; ) { - flag = spr->spr_event; - - if ((mask & flag) && sockevent_resume(sock, spr)) { - *sprp = spr->spr_next; - - sockevent_proc_free(spr); - } else { - mask &= ~flag; - - sprp = &spr->spr_next; - } - } - - /* - * Then see if we can satisfy pending select queries. - */ - if ((mask & (SEV_ACCEPT | SEV_SEND | SEV_RECV)) && - sock->sock_select.ss_endpt != NONE) { - assert(sock->sock_selops != 0); - - /* - * Only retest select operations that, based on the given event - * mask, could possibly be satisfied now. - */ - ops = sock->sock_selops; - if (!(mask & (SEV_ACCEPT | SEV_RECV))) - ops &= ~SDEV_OP_RD; - if (!(mask & SEV_SEND)) - ops &= ~SDEV_OP_WR; - if (!(0)) /* TODO: OOB receive support */ - ops &= ~SDEV_OP_ERR; - - /* Are there any operations to test? */ - if (ops != 0) { - /* Test those operations. */ - r = sockevent_test_select(sock, ops); - - /* Were any satisfied? */ - if (r != 0) { - /* Let the caller know. */ - sockdriver_reply_select(&sock->sock_select, - sock->sock_id, r); - - sock->sock_selops &= ~r; - - /* Are there any saved operations left now? */ - if (sock->sock_selops == 0) - sock->sock_select.ss_endpt = NONE; - } - } - } - - /* - * Finally, a SEV_CLOSE event unconditionally frees the sock object. - * This event should be fired only for sockets that are either not yet, - * or not anymore, in use by userland. - */ - if (mask & SEV_CLOSE) { - assert(sock->sock_flags & (SFL_CLONED | SFL_CLOSING)); - - sockevent_free(sock); - } -} - -/* - * Process all pending events. Events must still be blocked, so that if - * handling one event generates a new event, that event is handled from here - * rather than immediately. - */ -static void -sockevent_pump(void) -{ - struct sock *sock; - unsigned int mask; - - assert(sockevent_working); - - while (!SIMPLEQ_EMPTY(&sockevent_pending)) { - sock = SIMPLEQ_FIRST(&sockevent_pending); - SIMPLEQ_REMOVE_HEAD(&sockevent_pending, sock_next); - - mask = sock->sock_events; - assert(mask != 0); - sock->sock_events = 0; - - sockevent_fire(sock, mask); - /* - * At this point, the sock object may already have been readded - * to the event list, or even be deallocated altogether. - */ - } -} - -/* - * Return TRUE if any events are pending on any sockets, or FALSE otherwise. - */ -static int -sockevent_has_events(void) -{ - - return (!SIMPLEQ_EMPTY(&sockevent_pending)); -} - -/* - * Raise the given bitwise-OR'ed set of events on the given socket object. - * Depending on the context of the call, they events may or may not be - * processed immediately. - */ -void -sockevent_raise(struct sock * sock, unsigned int mask) -{ - - assert(sock->sock_ops != NULL); - - /* - * Handle SEV_CLOSE first. This event must not be deferred, so as to - * let socket drivers recycle sock objects as they are needed. For - * example, a user-closed TCP socket may stay open to transmit the - * remainder of its send buffer, until the TCP driver runs out of - * sockets, in which case the connection is aborted. The driver would - * then raise SEV_CLOSE on the sock object so as to clean it up, and - * immediately reuse it afterward. If the close event were to be - * deferred, this immediate reuse would not be possible. - * - * The sop_free() callback routine may not raise new events, and thus, - * the state of 'sockevent_working' need not be checked or set here. - */ - if (mask & SEV_CLOSE) { - assert(mask == SEV_CLOSE); - - sockevent_fire(sock, mask); - - return; - } - - /* - * If we are currently processing a socket message, store the event for - * later. If not, this call is not coming from inside libsockevent, - * and we must handle the event immediately. - */ - if (sockevent_working) { - assert(mask != 0); - assert(mask <= UCHAR_MAX); /* sock_events field size check */ - - if (sock->sock_events == 0) - SIMPLEQ_INSERT_TAIL(&sockevent_pending, sock, - sock_next); - - sock->sock_events |= mask; - } else { - sockevent_working = TRUE; - - sockevent_fire(sock, mask); - - if (sockevent_has_events()) - sockevent_pump(); - - sockevent_working = FALSE; - } -} - -/* - * Set a pending error on the socket object, and wake up any suspended - * operations that are affected by this. - */ -void -sockevent_set_error(struct sock * sock, int err) -{ - - assert(err < 0); - assert(sock->sock_ops != NULL); - - /* If an error was set already, it will be overridden. */ - sock->sock_err = err; - - sockevent_raise(sock, SEV_BIND | SEV_CONNECT | SEV_SEND | SEV_RECV); -} - -/* - * Initialize timer-related data structures. - */ -static void -socktimer_init(void) -{ - - SLIST_INIT(&socktimer); - - init_timer(&sockevent_timer); -} - -/* - * Check whether the given socket object has any suspended requests that have - * now expired. If so, cancel them. Also, if the socket object has any - * suspended requests with a timeout that has not yet expired, return the - * earliest (relative) timeout of all of them, or TMR_NEVER if no such requests - * are present. - */ -static clock_t -sockevent_expire(struct sock * sock, clock_t now) -{ - struct sockevent_proc *spr, **sprp; - clock_t lowest, left; - int r; - - /* - * First handle the case that the socket is closed. In this case, - * there may be a linger timer, although the socket may also simply - * still be on the timer list because of a request that did not time - * out right before the socket was closed. - */ - if (sock->sock_flags & SFL_CLOSING) { - /* Was there a linger timer and has it expired? */ - if ((sock->sock_opt & SO_LINGER) && - tmr_is_first(sock->sock_linger, now)) { - assert(sock->sock_ops->sop_close != NULL); - - /* - * Whatever happens next, we must now resume the - * pending close operation, if it was not canceled - * earlier. As before, we return OK rather than the - * standardized EWOULDBLOCK, to ensure that the user - * process knows the file descriptor has been closed. - */ - if ((spr = sock->sock_proc) != NULL) { - assert(spr->spr_event == SEV_CLOSE); - assert(spr->spr_next == NULL); - - sock->sock_proc = NULL; - - sockdriver_reply_generic(&spr->spr_call, OK); - - sockevent_proc_free(spr); - } - - /* - * Tell the socket driver that closing the socket is - * now a bit more desired than the last time we asked. - */ - r = sock->sock_ops->sop_close(sock, TRUE /*force*/); - - assert(r == OK || r == SUSPEND); - - /* - * The linger timer fires once. After that, the socket - * driver is free to decide that it still will not - * close the socket. If it does, do not fire the - * linger timer again. - */ - if (r == SUSPEND) - sock->sock_opt &= ~SO_LINGER; - else - sockevent_free(sock); - } - - return TMR_NEVER; - } - - /* - * Then see if any send and/or receive requests have expired. Also see - * if there are any send and/or receive requests left that have not yet - * expired but do have a timeout, so that we can return the lowest of - * those timeouts. - */ - lowest = TMR_NEVER; - - for (sprp = &sock->sock_proc; (spr = *sprp) != NULL; ) { - /* Skip requests without a timeout. */ - if (spr->spr_timer == 0) { - sprp = &spr->spr_next; - - continue; - } - - assert(spr->spr_event == SEV_SEND || - spr->spr_event == SEV_RECV); - - /* - * If the request has expired, cancel it and remove it from the - * list. Otherwise, see if the request has the lowest number - * of ticks until its timeout so far. - */ - if (tmr_is_first(spr->spr_time, now)) { - *sprp = spr->spr_next; - - if (spr->spr_event == SEV_SEND) - sockevent_cancel_send(sock, spr, EWOULDBLOCK); - else - sockevent_cancel_recv(sock, spr, EWOULDBLOCK); - - sockevent_proc_free(spr); - } else { - left = spr->spr_time - now; - - if (lowest == TMR_NEVER || lowest > left) - lowest = left; - - sprp = &spr->spr_next; - } - } - - return lowest; -} - -/* - * The socket event alarm went off. Go through the set of socket objects with - * timers, and see if any of their requests have now expired. Set a new alarm - * as necessary. - */ -static void -socktimer_expire(int arg __unused) -{ - SLIST_HEAD(, sock) oldtimer; - struct sock *sock, *tsock; - clock_t now, lowest, left; - int working; - - /* - * This function may or may not be called from a context where we are - * already deferring events, so we have to cover both cases here. - */ - if ((working = sockevent_working) == FALSE) - sockevent_working = TRUE; - - /* Start a new list. */ - memcpy(&oldtimer, &socktimer, sizeof(oldtimer)); - SLIST_INIT(&socktimer); - - now = getticks(); - lowest = TMR_NEVER; - - /* - * Go through all sockets that have or had a request with a timeout, - * canceling any expired requests and building a new list of sockets - * that still have requests with timeouts as we go. - */ - SLIST_FOREACH_SAFE(sock, &oldtimer, sock_timer, tsock) { - assert(sock->sock_flags & SFL_TIMER); - sock->sock_flags &= ~SFL_TIMER; - - left = sockevent_expire(sock, now); - /* - * The sock object may already have been deallocated now. - * If 'next' is TMR_NEVER, do not touch 'sock' anymore. - */ - - if (left != TMR_NEVER) { - if (lowest == TMR_NEVER || lowest > left) - lowest = left; - - SLIST_INSERT_HEAD(&socktimer, sock, sock_timer); - - sock->sock_flags |= SFL_TIMER; - } - } - - /* If there is a new lowest timeout at all, set a new timer. */ - if (lowest != TMR_NEVER) - set_timer(&sockevent_timer, lowest, socktimer_expire, 0); - - if (!working) { - /* If any new events were raised, process them now. */ - if (sockevent_has_events()) - sockevent_pump(); - - sockevent_working = FALSE; - } -} - -/* - * Set a timer for the given (relative) number of clock ticks, adding the - * associated socket object to the set of socket objects with timers, if it was - * not already in that set. Set a new alarm if necessary, and return the - * absolute timeout for the timer. Since the timers list is maintained lazily, - * the caller need not take the object off the set if the call was canceled - * later; see also socktimer_del(). - */ -static clock_t -socktimer_add(struct sock * sock, clock_t ticks) -{ - clock_t now; - - /* - * Relative time comparisons require that any two times are no more - * than half the comparison space (clock_t, unsigned long) apart. - */ - assert(ticks <= TMRDIFF_MAX); - - /* If the socket was not already on the timers list, put it on. */ - if (!(sock->sock_flags & SFL_TIMER)) { - SLIST_INSERT_HEAD(&socktimer, sock, sock_timer); - - sock->sock_flags |= SFL_TIMER; - } - - /* - * (Re)set the timer if either it was not running at all or this new - * timeout will occur sooner than the currently scheduled alarm. Note - * that setting a timer that was already set is allowed. - */ - now = getticks(); - - if (!tmr_is_set(&sockevent_timer) || - tmr_is_first(now + ticks, tmr_exp_time(&sockevent_timer))) - set_timer(&sockevent_timer, ticks, socktimer_expire, 0); - - /* Return the absolute timeout. */ - return now + ticks; -} - -/* - * Remove a socket object from the set of socket objects with timers. Since - * the timer list is maintained lazily, this needs to be done only right before - * the socket object is freed. - */ -static void -socktimer_del(struct sock * sock) -{ - - if (sock->sock_flags & SFL_TIMER) { - /* This macro is O(n). */ - SLIST_REMOVE(&socktimer, sock, sock, sock_timer); - - sock->sock_flags &= ~SFL_TIMER; - } -} - -/* - * Bind a socket to a local address. - */ -static int -sockevent_bind(sockid_t id, const struct sockaddr * __restrict addr, - socklen_t addr_len, endpoint_t user_endpt, - const struct sockdriver_call * __restrict call) -{ - struct sock *sock; - int r; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (sock->sock_ops->sop_bind == NULL) - return EOPNOTSUPP; - - /* Binding a socket in listening mode is never supported. */ - if (sock->sock_opt & SO_ACCEPTCONN) - return EINVAL; - - r = sock->sock_ops->sop_bind(sock, addr, addr_len, user_endpt); - - if (r == SUSPEND) { - if (call == NULL) - return EINPROGRESS; - - sockevent_suspend(sock, SEV_BIND, call, user_endpt); - } - - return r; -} - -/* - * Connect a socket to a remote address. - */ -static int -sockevent_connect(sockid_t id, const struct sockaddr * __restrict addr, - socklen_t addr_len, endpoint_t user_endpt, - const struct sockdriver_call * call) -{ - struct sockdriver_call fakecall; - struct sockevent_proc *spr; - struct sock *sock; - int r; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (sock->sock_ops->sop_connect == NULL) - return EOPNOTSUPP; - - /* Connecting a socket in listening mode is never supported. */ - if (sock->sock_opt & SO_ACCEPTCONN) - return EOPNOTSUPP; - - /* - * The upcoming connect call may fire an accept event for which the - * handler may in turn fire a connect event on this socket. Since we - * delay event processing until after processing calls, this would - * create the problem that even if the connection is accepted right - * away, non-blocking connect requests would return EINPROGRESS. For - * UDS, this is undesirable behavior. To remedy this, we use a hack: - * we temporarily suspend the connect even if non-blocking, then - * process events, and then cancel the connect request again. If the - * connection was accepted immediately, the cancellation will have no - * effect, since the request has already been replied to. In order not - * to violate libsockdriver rules with this hack, we fabricate a fake - * 'conn' object. - */ - r = sock->sock_ops->sop_connect(sock, addr, addr_len, user_endpt); - - if (r == SUSPEND) { - if (call != NULL || sockevent_has_events()) { - if (call == NULL) { - fakecall.sc_endpt = NONE; - - call = &fakecall; - } - - assert(!sockevent_has_suspended(sock, - SEV_SEND | SEV_RECV)); - - sockevent_suspend(sock, SEV_CONNECT, call, user_endpt); - - if (call == &fakecall) { - /* Process any pending events first now. */ - sockevent_pump(); - - /* - * If the connect request has not been resumed - * yet now, we must remove it from the queue - * again, and return EINPROGRESS ourselves. - * Otherwise, return OK or a pending error. - */ - spr = sockevent_unsuspend(sock, call); - if (spr != NULL) { - sockevent_proc_free(spr); - - r = EINPROGRESS; - } else if ((r = sock->sock_err) != OK) - sock->sock_err = OK; - } - } else - r = EINPROGRESS; - } - - if (r == OK) { - /* - * A completed connection attempt also always implies that the - * socket becomes writable. For convenience we enforce this - * rule here, because it is easy to forget. - */ - sockevent_raise(sock, SEV_SEND); - } - - return r; -} - -/* - * Put a socket in listening mode. - */ -static int -sockevent_listen(sockid_t id, int backlog) -{ - struct sock *sock; - int r; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (sock->sock_ops->sop_listen == NULL) - return EOPNOTSUPP; - - /* - * Perform a general adjustment on the backlog value, applying the - * customary BSD "fudge factor" of 1.5x. Keep the value within bounds - * though. POSIX imposes that a negative backlog value is equal to a - * backlog value of zero. A backlog value of zero, in turn, may mean - * anything; we take it to be one. POSIX also imposes that all socket - * drivers accept up to at least SOMAXCONN connections on the queue. - */ - if (backlog < 0) - backlog = 0; - if (backlog < SOMAXCONN) - backlog += 1 + ((unsigned int)backlog >> 1); - if (backlog > SOMAXCONN) - backlog = SOMAXCONN; - - r = sock->sock_ops->sop_listen(sock, backlog); - - /* - * On success, the socket is now in listening mode. As part of that, - * a select(2) ready-to-read condition now indicates that a connection - * may be accepted on the socket, rather than that data may be read. - * Since libsockevent is responsible for this distinction, we keep - * track of the listening mode at this level. Conveniently, there is a - * socket option for this, which we support out of the box as a result. - */ - if (r == OK) { - sock->sock_opt |= SO_ACCEPTCONN; - - /* - * For the extremely unlikely case that right after the socket - * is put into listening mode, it has a connection ready to - * accept, we retest blocked ready-to-read select queries now. - */ - sockevent_raise(sock, SEV_ACCEPT); - } - - return r; -} - -/* - * Accept a connection on a listening socket, creating a new socket. - */ -static sockid_t -sockevent_accept(sockid_t id, struct sockaddr * __restrict addr, - socklen_t * __restrict addr_len, endpoint_t user_endpt, - const struct sockdriver_call * __restrict call) -{ - struct sock *sock, *newsock; - sockid_t r; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (sock->sock_ops->sop_accept == NULL) - return EOPNOTSUPP; - - /* - * Attempt to accept a connection. The socket driver is responsible - * for allocating a sock object (and identifier) on success. It may - * already have done so before, in which case it should leave newsock - * filled with NULL; otherwise, the returned sock object is cloned from - * the listening socket. The socket driver is also responsible for - * failing the call if the socket is not in listening mode, because it - * must specify the error to return: EOPNOTSUPP or EINVAL. - */ - newsock = NULL; - - if ((r = sock->sock_ops->sop_accept(sock, addr, addr_len, user_endpt, - &newsock)) == SUSPEND) { - assert(sock->sock_opt & SO_ACCEPTCONN); - - if (call == NULL) - return EWOULDBLOCK; - - sockevent_suspend(sock, SEV_ACCEPT, call, user_endpt); - - return SUSPEND; - } - - if (r >= 0) - sockevent_accepted(sock, newsock, r); - - return r; -} - -/* - * Send regular and/or control data. - */ -static int -sockevent_send(sockid_t id, const struct sockdriver_data * __restrict data, - size_t len, const struct sockdriver_data * __restrict ctl_data, - socklen_t ctl_len, const struct sockaddr * __restrict addr, - socklen_t addr_len, endpoint_t user_endpt, int flags, - const struct sockdriver_call * __restrict call) -{ - struct sock *sock; - clock_t time; - size_t min, off; - socklen_t ctl_off; - int r, timer; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - /* - * The order of the following checks is not necessarily fixed, and may - * be changed later. As far as applicable, they should match the order - * of the checks during call resumption, though. - */ - if ((r = sock->sock_err) != OK) { - sock->sock_err = OK; - - return r; - } - - if (sock->sock_flags & SFL_SHUT_WR) { - sockevent_sigpipe(sock, user_endpt, flags); - - return EPIPE; - } - - /* - * Translate the sticky SO_DONTROUTE option to a per-request - * MSG_DONTROUTE flag. This achieves two purposes: socket drivers have - * to check only one flag, and socket drivers that do not support the - * flag will fail send requests in a consistent way. - */ - if (sock->sock_opt & SO_DONTROUTE) - flags |= MSG_DONTROUTE; - - /* - * Check if this is a valid send request as far as the socket driver is - * concerned. We do this separately from sop_send for the reason that - * this send request may immediately be queued behind other pending - * send requests (without a call to sop_send), which means even invalid - * requests would be queued and not return failure until much later. - */ - if (sock->sock_ops->sop_pre_send != NULL && - (r = sock->sock_ops->sop_pre_send(sock, len, ctl_len, addr, - addr_len, user_endpt, - flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL))) != OK) - return r; - - if (sock->sock_ops->sop_send == NULL) - return EOPNOTSUPP; - - off = 0; - ctl_off = 0; - - /* - * Sending out-of-band data is treated differently from regular data: - * - * - sop_send is called immediately, even if a partial non-OOB send - * operation is currently suspended (TODO: it may have to be aborted - * in order to maintain atomicity guarantees - that should be easy); - * - sop_send must not return SUSPEND; instead, if it cannot process - * the OOB data immediately, it must return an appropriate error; - * - the send low watermark is ignored. - * - * Given that none of the current socket drivers support OOB data at - * all, more sophisticated approaches would have no added value now. - */ - if (flags & MSG_OOB) { - r = sock->sock_ops->sop_send(sock, data, len, &off, ctl_data, - ctl_len, &ctl_off, addr, addr_len, user_endpt, flags, 0); - - if (r == SUSPEND) - panic("libsockevent: MSG_OOB send calls may not be " - "suspended"); - - return (r == OK) ? (int)off : r; - } - - /* - * Only call the actual sop_send function now if no other send calls - * are suspended already. - * - * Call sop_send with 'min' set to the minimum of the request size and - * the socket's send low water mark, but only if the call is non- - * blocking. For stream-oriented sockets, this should have the effect - * that non-blocking calls fail with EWOULDBLOCK if not at least that - * much can be sent immediately. For consistency, we choose to apply - * the same threshold to blocking calls. For datagram-oriented - * sockets, the minimum is not a factor to be considered. - */ - if (!sockevent_has_suspended(sock, SEV_SEND)) { - min = sock->sock_slowat; - if (min > len) - min = len; - - r = sock->sock_ops->sop_send(sock, data, len, &off, ctl_data, - ctl_len, &ctl_off, addr, addr_len, user_endpt, flags, min); - } else - r = SUSPEND; - - if (r == SUSPEND) { - /* - * We do not store the target's address on suspension, because - * that would add significantly to the per-process suspension - * state. As a result, we disallow socket drivers from - * suspending send calls with addresses, because we would no - * longer have the address for proper call resumption. - * However, we do not know here whether the socket is in - * connection-oriented mode; if it is, the address is to be - * ignored altogether. Therefore, there is no test on 'addr' - * here. Resumed calls will get a NULL address pointer, and - * the socket driver is expected to do the right thing. - */ - - /* - * For non-blocking socket calls, return an error only if we - * were not able to send anything at all. If only control data - * were sent, the return value is therefore zero. - */ - if (call != NULL) { - if (sock->sock_stimeo != 0) { - timer = TRUE; - time = socktimer_add(sock, sock->sock_stimeo); - } else { - timer = FALSE; - time = 0; - } - - sockevent_suspend_data(sock, SEV_SEND, timer, call, - user_endpt, data, len, off, ctl_data, ctl_len, - ctl_off, flags, 0, time); - } else - r = (off > 0 || ctl_off > 0) ? OK : EWOULDBLOCK; - } else if (r == EPIPE) - sockevent_sigpipe(sock, user_endpt, flags); - - return (r == OK) ? (int)off : r; -} - -/* - * The inner part of the receive request handler. An error returned from here - * may be overridden by an error pending on the socket, although data returned - * from here trumps such pending errors. - */ -static int -sockevent_recv_inner(struct sock * sock, - const struct sockdriver_data * __restrict data, - size_t len, size_t * __restrict off, - const struct sockdriver_data * __restrict ctl_data, - socklen_t ctl_len, socklen_t * __restrict ctl_off, - struct sockaddr * __restrict addr, - socklen_t * __restrict addr_len, endpoint_t user_endpt, - int * __restrict flags, const struct sockdriver_call * __restrict call) -{ - clock_t time; - size_t min; - int r, oob, inflags, timer; - - /* - * Check if this is a valid receive request as far as the socket driver - * is concerned. We do this separately from sop_recv for the reason - * that this receive request may immediately be queued behind other - * pending receive requests (without a call to sop_recv), which means - * even invalid requests would be queued and not return failure until - * much later. - */ - inflags = *flags; - *flags = 0; - - if (sock->sock_ops->sop_pre_recv != NULL && - (r = sock->sock_ops->sop_pre_recv(sock, user_endpt, - inflags & ~(MSG_DONTWAIT | MSG_NOSIGNAL))) != OK) - return r; - - /* - * The order of the following checks is not necessarily fixed, and may - * be changed later. As far as applicable, they should match the order - * of the checks during call resumption, though. - */ - if (sock->sock_flags & SFL_SHUT_RD) - return SOCKEVENT_EOF; - - if (sock->sock_ops->sop_recv == NULL) - return EOPNOTSUPP; - - /* - * Receiving out-of-band data is treated differently from regular data: - * - * - sop_recv is called immediately, even if a partial non-OOB receive - * operation is currently suspended (TODO: it may have to be aborted - * in order to maintain atomicity guarantees - that should be easy); - * - sop_recv must not return SUSPEND; instead, if it cannot return any - * the OOB data immediately, it must return an appropriate error; - * - the receive low watermark is ignored. - * - * Given that none of the current socket drivers support OOB data at - * all, more sophisticated approaches would have no added value now. - */ - oob = (inflags & MSG_OOB); - - if (oob && (sock->sock_opt & SO_OOBINLINE)) - return EINVAL; - - /* - * Only call the actual sop_recv function now if no other receive - * calls are suspended already. - * - * Call sop_recv with 'min' set to the minimum of the request size and - * the socket's socket's low water mark, unless there is a pending - * error. As a result, blocking calls will block, and non-blocking - * calls will yield EWOULDBLOCK, if at least that much can be received, - * unless another condition (EOF or that pending error) prevents more - * from being received anyway. For datagram-oriented sockets, the - * minimum is not a factor to be considered. - */ - if (oob || !sockevent_has_suspended(sock, SEV_RECV)) { - if (!oob && sock->sock_err == OK) { - min = sock->sock_rlowat; - if (min > len) - min = len; - } else - min = 0; /* receive even no-data segments */ - - r = sock->sock_ops->sop_recv(sock, data, len, off, ctl_data, - ctl_len, ctl_off, addr, addr_len, user_endpt, inflags, min, - flags); - } else - r = SUSPEND; - - assert(r <= 0 || r == SOCKEVENT_EOF); - - if (r == SUSPEND) { - if (oob) - panic("libsockevent: MSG_OOB receive calls may not be " - "suspended"); - - /* - * For non-blocking socket calls, return EWOULDBLOCK only if we - * did not receive anything at all. If only control data were - * received, the return value is therefore zero. Suspension - * implies that there is nothing to read. For the purpose of - * the calling wrapper function, never suspend a call when - * there is a pending error. - */ - if (call != NULL && sock->sock_err == OK) { - if (sock->sock_rtimeo != 0) { - timer = TRUE; - time = socktimer_add(sock, sock->sock_rtimeo); - } else { - timer = FALSE; - time = 0; - } - - sockevent_suspend_data(sock, SEV_RECV, timer, call, - user_endpt, data, len, *off, ctl_data, - ctl_len, *ctl_off, inflags, *flags, time); - } else - r = EWOULDBLOCK; - } - - return r; -} - -/* - * Receive regular and/or control data. - */ -static int -sockevent_recv(sockid_t id, const struct sockdriver_data * __restrict data, - size_t len, const struct sockdriver_data * __restrict ctl_data, - socklen_t * __restrict ctl_len, struct sockaddr * __restrict addr, - socklen_t * __restrict addr_len, endpoint_t user_endpt, - int * __restrict flags, const struct sockdriver_call * __restrict call) -{ - struct sock *sock; - size_t off; - socklen_t ctl_inlen; - int r; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - /* - * This function is a wrapper around the actual receive functionality. - * The reason for this is that receiving data should take precedence - * over a pending socket error, while a pending socket error should - * take precedence over both regular errors as well as EOF. In other - * words: if there is a pending error, we must try to receive anything - * at all; if receiving does not work, we must fail the call with the - * pending error. However, until we call the receive callback, we have - * no way of telling whether any data can be received. So we must try - * that before we can decide whether to return a pending error. - */ - off = 0; - ctl_inlen = *ctl_len; - *ctl_len = 0; - - /* - * Attempt to perform the actual receive call. - */ - r = sockevent_recv_inner(sock, data, len, &off, ctl_data, ctl_inlen, - ctl_len, addr, addr_len, user_endpt, flags, call); - - /* - * If the receive request succeeded, or it failed but yielded a partial - * result, then return the (partal) result. Otherwise, if an error is - * pending, return that error. Otherwise, return either a regular - * error or 0 for EOF. - */ - if (r == OK || (r != SUSPEND && (off > 0 || *ctl_len > 0))) - r = (int)off; - else if (sock->sock_err != OK) { - assert(r != SUSPEND); - - r = sock->sock_err; - - sock->sock_err = OK; - } else if (r == SOCKEVENT_EOF) - r = 0; - - return r; -} - -/* - * Process an I/O control call. - */ -static int -sockevent_ioctl(sockid_t id, unsigned long request, - const struct sockdriver_data * __restrict data, endpoint_t user_endpt, - const struct sockdriver_call * __restrict call __unused) -{ - struct sock *sock; - size_t size; - int r, val; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - /* We handle a very small subset of generic IOCTLs here. */ - switch (request) { - case FIONREAD: - size = 0; - if (!(sock->sock_flags & SFL_SHUT_RD) && - sock->sock_ops->sop_test_recv != NULL) - (void)sock->sock_ops->sop_test_recv(sock, 0, &size); - - val = (int)size; - - return sockdriver_copyout(data, 0, &val, sizeof(val)); - } - - if (sock->sock_ops->sop_ioctl == NULL) - return ENOTTY; - - r = sock->sock_ops->sop_ioctl(sock, request, data, user_endpt); - - /* - * Suspending IOCTL requests is not currently supported by this - * library, even though the VFS protocol and libsockdriver do support - * it. The reason is that IOCTLs do not match our proces suspension - * model: they could be neither queued nor repeated. For now, it seems - * that this feature is not needed by the socket drivers either. Thus, - * even though there are possible solutions, we defer implementing them - * until we know what exactly is needed. - */ - if (r == SUSPEND) - panic("libsockevent: socket driver suspended IOCTL 0x%lx", - request); - - return r; -} - -/* - * Set socket options. - */ -static int -sockevent_setsockopt(sockid_t id, int level, int name, - const struct sockdriver_data * data, socklen_t len) -{ - struct sock *sock; - struct linger linger; - struct timeval tv; - clock_t secs, ticks; - int r, val; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (level == SOL_SOCKET) { - /* - * Handle a subset of the socket-level options here. For most - * of them, this means that the socket driver itself need not - * handle changing or returning the options, but still needs to - * implement the correct behavior based on them where needed. - * A few of them are handled exclusively in this library: - * SO_ACCEPTCONN, SO_NOSIGPIPE, SO_ERROR, SO_TYPE, SO_LINGER, - * SO_SNDLOWAT, SO_RCVLOWAT, SO_SNDTIMEO, and SO_RCVTIMEO. - * The SO_USELOOPBACK option is explicitly absent, as it is - * valid for routing sockets only and is set by default there. - */ - switch (name) { - case SO_DEBUG: - case SO_REUSEADDR: - case SO_KEEPALIVE: - case SO_DONTROUTE: - case SO_BROADCAST: - case SO_OOBINLINE: - case SO_REUSEPORT: - case SO_NOSIGPIPE: - case SO_TIMESTAMP: - /* - * Simple on-off options. Changing them does not - * involve the socket driver. - */ - if ((r = sockdriver_copyin_opt(data, &val, sizeof(val), - len)) != OK) - return r; - - if (val) - sock->sock_opt |= (unsigned int)name; - else - sock->sock_opt &= ~(unsigned int)name; - - /* - * In priciple these on-off options are maintained in - * this library, but some socket drivers may need to - * apply the options elsewhere, so we notify them that - * something has changed. Using the sop_setsockopt - * callback would be inconvenient for this for two - * reasons: multiple value copy-ins and default errors. - */ - if (sock->sock_ops->sop_setsockmask != NULL) - sock->sock_ops->sop_setsockmask(sock, - sock->sock_opt); - - /* - * The inlining of OOB data may make new data available - * through regular receive calls. Thus, see if we can - * wake up any suspended receive calls now. - */ - if (name == SO_OOBINLINE && val) - sockevent_raise(sock, SEV_RECV); - - return OK; - - case SO_LINGER: - /* The only on-off option with an associated value. */ - if ((r = sockdriver_copyin_opt(data, &linger, - sizeof(linger), len)) != OK) - return r; - - if (linger.l_onoff) { - if (linger.l_linger < 0) - return EINVAL; - /* EDOM is the closest applicable error.. */ - secs = (clock_t)linger.l_linger; - if (secs >= TMRDIFF_MAX / sys_hz()) - return EDOM; - - sock->sock_opt |= SO_LINGER; - sock->sock_linger = secs * sys_hz(); - } else { - sock->sock_opt &= ~SO_LINGER; - sock->sock_linger = 0; - } - - return OK; - - case SO_SNDLOWAT: - case SO_RCVLOWAT: - if ((r = sockdriver_copyin_opt(data, &val, sizeof(val), - len)) != OK) - return r; - - if (val <= 0) - return EINVAL; - - /* - * Setting these values may allow suspended operations - * (send, recv, select) to be resumed, so recheck. - */ - if (name == SO_SNDLOWAT) { - sock->sock_slowat = (size_t)val; - - sockevent_raise(sock, SEV_SEND); - } else { - sock->sock_rlowat = (size_t)val; - - sockevent_raise(sock, SEV_RECV); - } - - return OK; - - case SO_SNDTIMEO: - case SO_RCVTIMEO: - if ((r = sockdriver_copyin_opt(data, &tv, sizeof(tv), - len)) != OK) - return r; - - if (tv.tv_sec < 0 || tv.tv_usec < 0 || - (unsigned long)tv.tv_usec >= US) - return EINVAL; - if (tv.tv_sec >= TMRDIFF_MAX / sys_hz()) - return EDOM; - - ticks = tv.tv_sec * sys_hz() + - (tv.tv_usec * sys_hz() + US - 1) / US; - - if (name == SO_SNDTIMEO) - sock->sock_stimeo = ticks; - else - sock->sock_rtimeo = ticks; - - /* - * The timeouts for any calls already in progress for - * this socket are left as is. - */ - return OK; - - case SO_ACCEPTCONN: - case SO_ERROR: - case SO_TYPE: - /* These options may be retrieved but not set. */ - return ENOPROTOOPT; - - default: - /* - * The remaining options either cannot be handled in a - * generic way, or are not recognized altogether. Pass - * them to the socket driver, which should handle what - * it knows and reject the rest. - */ - break; - } - } - - if (sock->sock_ops->sop_setsockopt == NULL) - return ENOPROTOOPT; - - /* - * The socket driver must return ENOPROTOOPT for all options it does - * not recognize. - */ - return sock->sock_ops->sop_setsockopt(sock, level, name, data, len); -} - -/* - * Retrieve socket options. - */ -static int -sockevent_getsockopt(sockid_t id, int level, int name, - const struct sockdriver_data * __restrict data, - socklen_t * __restrict len) -{ - struct sock *sock; - struct linger linger; - struct timeval tv; - clock_t ticks; - int val; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (level == SOL_SOCKET) { - /* - * As with setting, handle a subset of the socket-level options - * here. The rest is to be taken care of by the socket driver. - */ - switch (name) { - case SO_DEBUG: - case SO_ACCEPTCONN: - case SO_REUSEADDR: - case SO_KEEPALIVE: - case SO_DONTROUTE: - case SO_BROADCAST: - case SO_OOBINLINE: - case SO_REUSEPORT: - case SO_NOSIGPIPE: - case SO_TIMESTAMP: - val = !!(sock->sock_opt & (unsigned int)name); - - return sockdriver_copyout_opt(data, &val, sizeof(val), - len); - - case SO_LINGER: - linger.l_onoff = !!(sock->sock_opt & SO_LINGER); - linger.l_linger = sock->sock_linger / sys_hz(); - - return sockdriver_copyout_opt(data, &linger, - sizeof(linger), len); - - case SO_ERROR: - if ((val = -sock->sock_err) != OK) - sock->sock_err = OK; - - return sockdriver_copyout_opt(data, &val, sizeof(val), - len); - - case SO_TYPE: - val = sock->sock_type; - - return sockdriver_copyout_opt(data, &val, sizeof(val), - len); - - case SO_SNDLOWAT: - val = (int)sock->sock_slowat; - - return sockdriver_copyout_opt(data, &val, sizeof(val), - len); - - case SO_RCVLOWAT: - val = (int)sock->sock_rlowat; - - return sockdriver_copyout_opt(data, &val, sizeof(val), - len); - - case SO_SNDTIMEO: - case SO_RCVTIMEO: - if (name == SO_SNDTIMEO) - ticks = sock->sock_stimeo; - else - ticks = sock->sock_rtimeo; - - tv.tv_sec = ticks / sys_hz(); - tv.tv_usec = (ticks % sys_hz()) * US / sys_hz(); - - return sockdriver_copyout_opt(data, &tv, sizeof(tv), - len); - - default: - break; - } - } - - if (sock->sock_ops->sop_getsockopt == NULL) - return ENOPROTOOPT; - - /* - * The socket driver must return ENOPROTOOPT for all options it does - * not recognize. - */ - return sock->sock_ops->sop_getsockopt(sock, level, name, data, len); -} - -/* - * Retrieve a socket's local address. - */ -static int -sockevent_getsockname(sockid_t id, struct sockaddr * __restrict addr, - socklen_t * __restrict addr_len) -{ - struct sock *sock; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - if (sock->sock_ops->sop_getsockname == NULL) - return EOPNOTSUPP; - - return sock->sock_ops->sop_getsockname(sock, addr, addr_len); -} - -/* - * Retrieve a socket's remote address. - */ -static int -sockevent_getpeername(sockid_t id, struct sockaddr * __restrict addr, - socklen_t * __restrict addr_len) -{ - struct sock *sock; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - /* Listening-mode sockets cannot possibly have a peer address. */ - if (sock->sock_opt & SO_ACCEPTCONN) - return ENOTCONN; - - if (sock->sock_ops->sop_getpeername == NULL) - return EOPNOTSUPP; - - return sock->sock_ops->sop_getpeername(sock, addr, addr_len); -} - -/* - * Mark the socket object as shut down for sending and/or receiving. The flags - * parameter may be a bitwise-OR'ed combination of SFL_SHUT_RD and SFL_SHUT_WR. - * This function will wake up any suspended requests affected by this change, - * but it will not invoke the sop_shutdown() callback function on the socket. - * The function may in fact be called from sop_shutdown() before completion to - * mark the socket as shut down as reflected by sockevent_is_shutdown(). - */ -void -sockevent_set_shutdown(struct sock * sock, unsigned int flags) -{ - unsigned int mask; - - assert(sock->sock_ops != NULL); - assert(!(flags & ~(SFL_SHUT_RD | SFL_SHUT_WR))); - - /* Look at the newly set flags only. */ - flags &= ~(unsigned int)sock->sock_flags; - - if (flags != 0) { - sock->sock_flags |= flags; - - /* - * Wake up any blocked calls that are affected by the shutdown. - * Shutting down listening sockets causes ongoing accept calls - * to be rechecked. - */ - mask = 0; - if (flags & SFL_SHUT_RD) - mask |= SEV_RECV; - if (flags & SFL_SHUT_WR) - mask |= SEV_SEND; - if (sock->sock_opt & SO_ACCEPTCONN) - mask |= SEV_ACCEPT; - - assert(mask != 0); - sockevent_raise(sock, mask); - } -} - -/* - * Shut down socket send and receive operations. - */ -static int -sockevent_shutdown(sockid_t id, int how) -{ - struct sock *sock; - unsigned int flags; - int r; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - /* Convert the request to a set of flags. */ - flags = 0; - if (how == SHUT_RD || how == SHUT_RDWR) - flags |= SFL_SHUT_RD; - if (how == SHUT_WR || how == SHUT_RDWR) - flags |= SFL_SHUT_WR; - - if (sock->sock_ops->sop_shutdown != NULL) - r = sock->sock_ops->sop_shutdown(sock, flags); - else - r = OK; - - /* On success, update our internal state as well. */ - if (r == OK) - sockevent_set_shutdown(sock, flags); - - return r; -} - -/* - * Close a socket. - */ -static int -sockevent_close(sockid_t id, const struct sockdriver_call * call) -{ - struct sock *sock; - int r, force; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - assert(sock->sock_proc == NULL); - sock->sock_select.ss_endpt = NONE; - - /* - * There are several scenarios when it comes to closing sockets. First - * of all, we never actually force the socket driver to close a socket. - * The driver may always suspend the close call and take as long as it - * wants. After a suspension, it signals its completion of the close - * through the SEV_CLOSE socket event. - * - * With that said, we offer two levels of urgency regarding the close - * request: regular and forced. The former allows for a graceful - * close; the latter urges the socket driver to close the socket as - * soon as possible. A socket that has been requested to be closed - * gracefully can, as long as it is still open (i.e., no SEV_CLOSE was - * fired yet), later be requested to be closed forcefully. This is how - * SO_LINGER with a nonzero timeout is implemented. If SO_LINGER is - * set with a zero timeout, the socket is force-closed immediately. - * Finally, if SO_LINGER is not set, the socket will be closed normally - * and never be forced--akin to SO_LINGER with an infinite timeout. - * - * The return value of the caller's close(2) may only ever be either - * OK or EINPROGRESS, to ensure that the caller knows that the file - * descriptor is freed up, as per Austin Group Defect #529. In fact, - * EINPROGRESS is to be returned only on signal interruption (i.e., - * cancel). For that reason, this function only ever returns OK. - */ - force = ((sock->sock_opt & SO_LINGER) && sock->sock_linger == 0); - - if (sock->sock_ops->sop_close != NULL) - r = sock->sock_ops->sop_close(sock, force); - else - r = OK; - - assert(r == OK || r == SUSPEND); - - if (r == SUSPEND) { - sock->sock_flags |= SFL_CLOSING; - - /* - * If we were requested to force-close the socket immediately, - * but the socket driver needs more time anyway, then tell the - * caller that the socket was closed right away. - */ - if (force) - return OK; - - /* - * If we are to force-close the socket only after a specific - * linger timeout, set the timer for that now, even if the call - * is non-blocking. This also means that we cannot associate - * the linger timeout with the close call. Instead, we convert - * the sock_linger value from a (relative) duration to an - * (absolute) timeout time, and use the SFL_CLOSING flag (along - * with SFL_TIMER) to tell the difference. Since the socket is - * otherwise unreachable from userland at this point, the - * conversion is never visible in any way. - * - * The socket may already be in the timers list, so we must - * always check the SO_LINGER flag before checking sock_linger. - * - * If SO_LINGER is not set, we must never suspend the call. - */ - if (sock->sock_opt & SO_LINGER) { - sock->sock_linger = - socktimer_add(sock, sock->sock_linger); - } else - call = NULL; - - /* - * A non-blocking close is completed asynchronously. The - * caller is not told about this with EWOULDBLOCK as usual, for - * the reasons mentioned above. - */ - if (call != NULL) - sockevent_suspend(sock, SEV_CLOSE, call, NONE); - else - r = OK; - } else if (r == OK) - sockevent_free(sock); - - return r; -} - -/* - * Cancel a suspended send request. - */ -static void -sockevent_cancel_send(struct sock * sock, struct sockevent_proc * spr, int err) -{ - int r; - - /* - * If any regular or control data were sent, return the number of data - * bytes sent--possibly zero. Otherwise return the given error code. - */ - if (spr->spr_dataoff > 0 || spr->spr_ctloff > 0) - r = (int)spr->spr_dataoff; - else - r = err; - - sockdriver_reply_generic(&spr->spr_call, r); - - /* - * In extremely rare circumstances, one send may be queued behind - * another send even though the former can actually be sent on the - * socket right away. For this reason, we retry sending when canceling - * a send. We need to do this only when the first send in the queue - * was canceled, but multiple blocked sends on a single socket should - * be rare anyway. - */ - sockevent_raise(sock, SEV_SEND); -} - -/* - * Cancel a suspended receive request. - */ -static void -sockevent_cancel_recv(struct sock * sock, struct sockevent_proc * spr, int err) -{ - int r; - - /* - * If any regular or control data were received, return the number of - * data bytes received--possibly zero. Otherwise return the given - * error code. - */ - if (spr->spr_dataoff > 0 || spr->spr_ctloff > 0) - r = (int)spr->spr_dataoff; - else - r = err; - - /* - * Also return any flags set for the data received so far, e.g. - * MSG_CTRUNC. Do not return an address: receive calls on unconnected - * sockets must never block after receiving some data--instead, they - * are supposed to return MSG_TRUNC if not all data were copied out. - */ - sockdriver_reply_recv(&spr->spr_call, r, spr->spr_ctloff, NULL, 0, - spr->spr_rflags); - - /* - * The same story as for sends (see above) applies to receives, - * although this case should be even more rare in practice. - */ - sockevent_raise(sock, SEV_RECV); -} - -/* - * Cancel a previous request that may currently be suspended. The cancel - * operation itself does not have a reply. Instead, if the given request was - * found to be suspended, that request must be aborted and an appropriate reply - * must be sent for the request. If no matching request was found, no reply - * must be sent at all. - */ -static void -sockevent_cancel(sockid_t id, const struct sockdriver_call * call) -{ - struct sockevent_proc *spr; - struct sock *sock; - - /* - * Due to asynchronous close(2) operations, not even the sock object - * may be found. If this (entirely legitimate) case, do not send any - * reply. - */ - if ((sock = sockhash_get(id)) == NULL) - return; - - /* - * The request may already have completed by the time we receive the - * cancel request, in which case we can not find it. In this (entirely - * legitimate) case, do not send any reply. - */ - if ((spr = sockevent_unsuspend(sock, call)) == NULL) - return; - - /* - * We found the operation. Cancel it according to its call type. - * Then, once fully done with it, free the suspension data structure. - * - * Note that we have to use the call structure from the suspension data - * structure rather than the given 'call' pointer: only the former - * includes all the information necessary to resume the request! - */ - switch (spr->spr_event) { - case SEV_BIND: - case SEV_CONNECT: - assert(spr->spr_call.sc_endpt != NONE); - - sockdriver_reply_generic(&spr->spr_call, EINTR); - - break; - - case SEV_ACCEPT: - sockdriver_reply_accept(&spr->spr_call, EINTR, NULL, 0); - - break; - - case SEV_SEND: - sockevent_cancel_send(sock, spr, EINTR); - - break; - - case SEV_RECV: - sockevent_cancel_recv(sock, spr, EINTR); - - break; - - case SEV_CLOSE: - /* - * Return EINPROGRESS rather than EINTR, so that the user - * process can tell from the close(2) result that the file - * descriptor has in fact been closed. - */ - sockdriver_reply_generic(&spr->spr_call, EINPROGRESS); - - /* - * Do not free the sock object here: the socket driver will - * complete the close in the background, and fire SEV_CLOSE - * once it is done. Only then is the sock object freed. - */ - break; - - default: - panic("libsockevent: process suspended on unknown event 0x%x", - spr->spr_event); - } - - sockevent_proc_free(spr); -} - -/* - * Process a select request. - */ -static int -sockevent_select(sockid_t id, unsigned int ops, - const struct sockdriver_select * sel) -{ - struct sock *sock; - unsigned int r, notify; - - if ((sock = sockhash_get(id)) == NULL) - return EINVAL; - - notify = (ops & SDEV_NOTIFY); - ops &= (SDEV_OP_RD | SDEV_OP_WR | SDEV_OP_ERR); - - /* - * See if any of the requested select operations can be satisfied - * immediately. - */ - r = sockevent_test_select(sock, ops); - - /* - * If select operations were pending, the new results must not indicate - * that any of those were satisfied, as that would indicate an internal - * logic error: the socket driver is supposed to update its state - * proactively, and thus, discovering that things have changed here is - * not something that should ever happen. - */ - assert(!(sock->sock_selops & r)); - - /* - * If any select operations are not satisfied immediately, and we are - * asked to notify the caller when they are satisfied later, save them - * for later retesting. - */ - ops &= ~r; - - if (notify && ops != 0) { - /* - * For now, we support only one caller when it comes to select - * queries: VFS. If we want to support a networked file system - * (or so) directly calling select as well, this library will - * have to be extended accordingly (should not be too hard). - */ - if (sock->sock_select.ss_endpt != NONE) { - if (sock->sock_select.ss_endpt != sel->ss_endpt) { - printf("libsockevent: no support for multiple " - "select callers yet\n"); - - return EIO; - } - - /* - * If a select query was already pending for this - * caller, we must simply merge in the new operations. - */ - sock->sock_selops |= ops; - } else { - assert(sel->ss_endpt != NONE); - - sock->sock_select = *sel; - sock->sock_selops = ops; - } - } - - return r; -} - -/* - * An alarm has triggered. Expire any timers. Socket drivers that do not pass - * clock notification messages to libsockevent must call expire_timers(3) - * themselves instead. - */ -static void -sockevent_alarm(clock_t now) -{ - - expire_timers(now); -} - -static const struct sockdriver sockevent_tab = { - .sdr_socket = sockevent_socket, - .sdr_socketpair = sockevent_socketpair, - .sdr_bind = sockevent_bind, - .sdr_connect = sockevent_connect, - .sdr_listen = sockevent_listen, - .sdr_accept = sockevent_accept, - .sdr_send = sockevent_send, - .sdr_recv = sockevent_recv, - .sdr_ioctl = sockevent_ioctl, - .sdr_setsockopt = sockevent_setsockopt, - .sdr_getsockopt = sockevent_getsockopt, - .sdr_getsockname = sockevent_getsockname, - .sdr_getpeername = sockevent_getpeername, - .sdr_shutdown = sockevent_shutdown, - .sdr_close = sockevent_close, - .sdr_cancel = sockevent_cancel, - .sdr_select = sockevent_select, - .sdr_alarm = sockevent_alarm -}; - -/* - * Initialize the socket event library. - */ -void -sockevent_init(sockevent_socket_cb_t socket_cb) -{ - - sockhash_init(); - - socktimer_init(); - - sockevent_proc_init(); - - SIMPLEQ_INIT(&sockevent_pending); - - assert(socket_cb != NULL); - sockevent_socket_cb = socket_cb; - - /* Announce we are up. */ - sockdriver_announce(); - - sockevent_working = FALSE; -} - -/* - * Process a socket driver request message. - */ -void -sockevent_process(const message * m_ptr, int ipc_status) -{ - - /* Block events until after we have processed the request. */ - assert(!sockevent_working); - sockevent_working = TRUE; - - /* Actually process the request. */ - sockdriver_process(&sockevent_tab, m_ptr, ipc_status); - - /* - * If any events were fired while processing the request, they will - * have been queued for later. Go through them now. - */ - if (sockevent_has_events()) - sockevent_pump(); - - sockevent_working = FALSE; -} diff --git a/minix/lib/libsys/cpuavg.c b/minix/lib/libsys/cpuavg.c deleted file mode 100644 index 796e3be33..000000000 --- a/minix/lib/libsys/cpuavg.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Routines to maintain a decaying average of per-process CPU utilization, in a - * way that results in numbers that are (hopefully) similar to those produced - * by NetBSD. Once a second, NetBSD performs the following basic computation - * for each process: - * - * avg = ccpu * avg + (1 - ccpu) * (run / hz) - * - * In this formula, 'avg' is the running average, 'hz' is the number of clock - * ticks per second, 'run' is the number of ticks during which the process was - * found running in the last second, and 'ccpu' is a decay value chosen such - * that only 5% of the original average remains after 60 seconds: e**(-1/20). - * - * Here, the idea is that we update the average lazily, namely, only when the - * process is running when the kernel processes a clock tick - no matter how - * long it had not been running before that. The result is that at any given - * time, the average may be out of date. For that reason, this code is shared - * between the kernel and the MIB service: the latter occasionally obtains the - * raw kernel process table, for example because a user runs ps(1), and it then - * needs to bring the values up to date. The kernel could do that itself just - * before copying out the process table, but the MIB service is equally capable - * of doing it post-copy - while also being preemptible during the computation. - * There is more to be said about this, but the summary is that it is not clear - * which of the two options is better in practice. We simply chose this one. - * - * In addition, we deliberately delay updating the actual average by one - * second, keeping the last second's number of process run ticks in a separate - * variable 'last'. This allows us to produce an estimate of short-term - * activity of the process as well. We use this to generate a "CPU estimate" - * value. BSD generates such a value for the purpose of scheduling, but we - * have no actual use for that, and generating the value just for userland is - * a bit too costly in our case. Our inaccurate value should suffice for most - * practical purposes though (e.g., comparisons between active processes). - * - * Overall, in terms of overhead, our approach should produce the same values - * as NetBSD while having only the same overhead as NetBSD in the very worst - * case, and much less overhead on average. Even in the worst case, in our - * case, the computation is spread out across each second, rather than all done - * at once. In terms of implementation, since this code is running in the - * kernel, we make use of small tables of precomputed values, and we try to - * save on computation as much as possible. We copy much of the NetBSD - * approach of avoiding divisions using FSCALE. - * - * Another difference with NetBSD is that our kernel does not actually call - * this function from its clock interrupt handler, but rather when a process - * has spent a number of CPU cycles that adds up to one clock tick worth of - * execution time. The result is better accuracy (no process can escape - * accounting by yielding just before each clock interrupt), but due to the - * inaccuracy of converting CPU cycles to clock ticks, a process may end up - * using more than 'hz' clock ticks per second. We could correct for this; - * however, it has not yet shown to be a problem. - * - * Zooming out a bit again, the current average is fairly accurate but not - * very precise. There are two reasons for this. First, the accounting is in - * clock tick fractions, which means that a per-second CPU usage below 1/hz - * cannot be measured. Second, the NetBSD FSCALE and ccpu values are such that - * (FSCALE - ccpu) equals 100, which means that a per-second CPU usage below - * 1/100 cannot be measured either. Both issues can be resolved by switching - * to a CPU cycle based accounting approach, which requires 64-bit arithmetic - * and a MINIX3-specific FSCALE value. For now, this is just not worth doing. - * - * Finally, it should be noted that in terms of overall operating system - * functionality, the CPU averages feature is entirely optional; as of writing, - * the produced values are only used in the output of utilities such as ps(1). - * If computing the CPU average becomes too burdensome in terms of either - * performance or maintenance, it can simply be removed again. - * - * Original author: David van Moolenbroek - */ - -#include "sysutil.h" -#include - -#define CCPUTAB_SHIFT 3 /* 2**3 == 8 */ -#define CCPUTAB_MASK ((1 << CCPUTAB_SHIFT) - 1) - -#define F(n) ((uint32_t)((n) * FSCALE)) - -/* e**(-1/20*n)*FSCALE for n=1..(2**CCPUTAB_SHIFT-1) */ -static const uint32_t ccpu_low[CCPUTAB_MASK] = { - F(0.951229424501), F(0.904837418036), F(0.860707976425), - F(0.818730753078), F(0.778800783071), F(0.740818220682), - F(0.704688089719) -}; -#define ccpu (ccpu_low[0]) - -/* e**(-1/20*8*n)*FSCALE for n=1.. until the value is zero (for FSCALE=2048) */ -static const uint32_t ccpu_high[] = { - F(0.670320046036), F(0.449328964117), F(0.301194211912), - F(0.201896517995), F(0.135335283237), F(0.090717953289), - F(0.060810062625), F(0.040762203978), F(0.027323722447), - F(0.018315638889), F(0.012277339903), F(0.008229747049), - F(0.005516564421), F(0.003697863716), F(0.002478752177), - F(0.001661557273), F(0.001113775148), F(0.000746585808), - F(0.000500451433) -}; - -/* - * Initialize the per-process CPU average structure. To be called when the - * process is started, that is, as part of a fork call. - */ -void -cpuavg_init(struct cpuavg * ca) -{ - - ca->ca_base = 0; - ca->ca_run = 0; - ca->ca_last = 0; - ca->ca_avg = 0; -} - -/* - * Return a new CPU usage average value, resulting from decaying the old value - * by the given number of seconds, using the formula (avg * ccpu**secs). - * We use two-level lookup tables to limit the computational expense to two - * multiplications while keeping the tables themselves relatively small. - */ -static uint32_t -cpuavg_decay(uint32_t avg, uint32_t secs) -{ - unsigned int slot; - - /* - * The ccpu_high table is set up such that with the default FSCALE, the - * values of any array entries beyond the end would be zero. That is, - * the average would be decayed to a value that, if represented in - * FSCALE units, would be zero. Thus, if it has been that long ago - * that we updated the average, we can just reset it to zero. - */ - if (secs > (__arraycount(ccpu_high) << CCPUTAB_SHIFT)) - return 0; - - if (secs > CCPUTAB_MASK) { - slot = (secs >> CCPUTAB_SHIFT) - 1; - - avg = (ccpu_high[slot] * avg) >> FSHIFT; /* decay #3 */ - - secs &= CCPUTAB_MASK; - } - - if (secs > 0) - avg = (ccpu_low[secs - 1] * avg) >> FSHIFT; /* decay #4 */ - - return avg; -} - -/* - * Update the CPU average value, either because the kernel is processing a - * clock tick, or because the MIB service updates obtained averages. We - * perform the decay in at most four computation steps (shown as "decay #n"), - * and thus, this algorithm is O(1). - */ -static void -cpuavg_update(struct cpuavg * ca, clock_t now, clock_t hz) -{ - clock_t delta; - uint32_t secs; - - delta = now - ca->ca_base; - - /* - * If at least a second elapsed since we last updated the average, we - * must do so now. If not, we need not do anything for now. - */ - if (delta < hz) - return; - - /* - * Decay the average by one second, and merge in the run fraction of - * the previous second, as though that second only just ended - even - * though the real time is at least one whole second ahead. By doing - * so, we roll the statistics time forward by one virtual second. - */ - ca->ca_avg = (ccpu * ca->ca_avg) >> FSHIFT; /* decay #1 */ - ca->ca_avg += (FSCALE - ccpu) * (ca->ca_last / hz) >> FSHIFT; - - ca->ca_last = ca->ca_run; /* move 'run' into 'last' */ - ca->ca_run = 0; - - ca->ca_base += hz; /* move forward by a second */ - delta -= hz; - - if (delta < hz) - return; - - /* - * At least a whole second more elapsed since the start of the recorded - * second. That means that our current 'run' counter (now moved into - * 'last') is also outdated, and we need to merge it in as well, before - * performing the next decay steps. - */ - ca->ca_avg = (ccpu * ca->ca_avg) >> FSHIFT; /* decay #2 */ - ca->ca_avg += (FSCALE - ccpu) * (ca->ca_last / hz) >> FSHIFT; - - ca->ca_last = 0; /* 'run' is already zero now */ - - ca->ca_base += hz; /* move forward by a second */ - delta -= hz; - - if (delta < hz) - return; - - /* - * If additional whole seconds elapsed since the start of the last - * second slot, roll forward in time by that many whole seconds, thus - * decaying the value properly while maintaining alignment to whole- - * second slots. The decay takes up to another two computation steps. - */ - secs = delta / hz; - - ca->ca_avg = cpuavg_decay(ca->ca_avg, secs); - - ca->ca_base += secs * hz; /* move forward by whole seconds */ -} - -/* - * The clock ticked, and this last clock tick is accounted to the process for - * which the CPU average statistics are stored in 'ca'. Update the statistics - * accordingly, decaying the average as necessary. The current system uptime - * must be given as 'now', and the number of clock ticks per second must be - * given as 'hz'. - */ -void -cpuavg_increment(struct cpuavg * ca, clock_t now, clock_t hz) -{ - - if (ca->ca_base == 0) - ca->ca_base = now; - else - cpuavg_update(ca, now, hz); - - /* - * Register that the process was running at this clock tick. We could - * avoid one division above by precomputing (FSCALE/hz), but this is - * typically not a clean division and would therefore result in (more) - * loss of accuracy. - */ - ca->ca_run += FSCALE; -} - -/* - * Retrieve the decaying CPU utilization average (as return value), the number - * of CPU run ticks in the current second so far (stored in 'cpticks'), and an - * opaque CPU utilization estimate (stored in 'estcpu'). The caller must - * provide the CPU average structure ('ca_orig'), which will not be modified, - * as well as the current uptime in clock ticks ('now') and the number of clock - * ticks per second ('hz'). - */ -uint32_t -cpuavg_getstats(const struct cpuavg * ca_orig, uint32_t * cpticks, - uint32_t * estcpu, clock_t now, clock_t hz) -{ - struct cpuavg ca; - - ca = *ca_orig; - - /* Update the average as necessary. */ - cpuavg_update(&ca, now, hz); - - /* Merge the last second into the average. */ - ca.ca_avg = (ccpu * ca.ca_avg) >> FSHIFT; - ca.ca_avg += (FSCALE - ccpu) * (ca.ca_last / hz) >> FSHIFT; - - *cpticks = ca.ca_run >> FSHIFT; - - /* - * In traditional BSD kernels, the 'estcpu' value determines a - * scheduling queue and decays to 10% in 5*(the current load average) - * seconds. MINIX reports the process's percentage of CPU usage in the - * last second instead, yielding a value in the range 0..100 with a - * decay of 100% after one second. This should be good enough for most - * practical purposes. - */ - *estcpu = (ca.ca_last / hz * 100) >> FSHIFT; - - return ca.ca_avg; -} - -/* - * Return the ccpu decay value, in FSCALE units. - */ -uint32_t -cpuavg_getccpu(void) -{ - - return ccpu; -} diff --git a/minix/lib/libsys/rmib.c b/minix/lib/libsys/rmib.c deleted file mode 100644 index e4b686389..000000000 --- a/minix/lib/libsys/rmib.c +++ /dev/null @@ -1,1089 +0,0 @@ -/* Service support for remote MIB subtrees - by D.C. van Moolenbroek */ -/* - * In effect, this is a lightweight version of the MIB service's main and tree - * code. Some parts of the code have even been copied almost as is, even - * though the copy here operates on slightly different data structures in order - * to keep the implementation more lightweight. For clarification on many - * aspects of the source code here, see the source code of the MIB service. - * One unique feature here is support for sparse nodes, which is needed for - * net.inet/inet6 as those are using subtrees with protocol-based identifiers. - * - * There is no way for this module to get to know about MIB service deaths - * without possibly interfering with the main code of the service this module - * is a part of. As a result, re-registration of mount points after a MIB - * service restart is not automatic. Instead, the main service code should - * provide detection of MIB service restarts, and call rmib_reregister() after - * such a restart in order to remount any previously mounted subtrees. - */ - -#include -#include -#include - -/* Structures for outgoing and incoming data, deliberately distinctly named. */ -struct rmib_oldp { - cp_grant_id_t oldp_grant; - size_t oldp_len; -}; - -struct rmib_newp { - cp_grant_id_t newp_grant; - size_t newp_len; -}; - -/* - * The maximum field size, in bytes, for which updates (i.e., writes) to the - * field do not require dynamic memory allocation. By policy, non-root users - * may not update fields exceeding this size at all. For strings, this size - * includes an extra byte for adding a null terminator if missing. As the name - * indicates, a buffer of this size is placed on the stack. - */ -#define RMIB_STACKBUF 257 - -/* - * The maximum number of subtrees that this service can mount. This value can - * be increased without any problems, but it is already quite high in practice. - */ -#define RMIB_MAX_SUBTREES 16 - -/* - * The array of subtree root nodes. Each root node's array index is the root - * identifier used in communication with the MIB service. - */ -static struct { - struct rmib_node *rno_node; - unsigned int rno_namelen; - int rno_name[CTL_SHORTNAME]; -} rnodes[RMIB_MAX_SUBTREES] = { { NULL, 0, { 0 } } }; - -/* - * Return TRUE or FALSE indicating whether the given offset is within the range - * of data that is to be copied out. This call can be used to test whether - * certain bits of data need to be prepared for copying at all. - */ -int -rmib_inrange(struct rmib_oldp * oldp, size_t off) -{ - - if (oldp == NULL) - return FALSE; - - return (off < oldp->oldp_len); -} - -/* - * Return the total length of the requested data. This should not be used - * directly except in highly unusual cases, such as particular node requests - * where the request semantics blatantly violate overall sysctl(2) semantics. - */ -size_t -rmib_getoldlen(struct rmib_oldp * oldp) -{ - - if (oldp == NULL) - return 0; - - return oldp->oldp_len; -} - -/* - * Copy out (partial) data to the user. The copy is automatically limited to - * the range of data requested by the user. Return the requested length on - * success (for the caller's convenience) or an error code on failure. - */ -ssize_t -rmib_copyout(struct rmib_oldp * __restrict oldp, size_t off, - const void * __restrict buf, size_t size) -{ - size_t len; - int r; - - len = size; - assert(len <= SSIZE_MAX); - - if (oldp == NULL || off >= oldp->oldp_len) - return size; /* nothing to do */ - - if (len > oldp->oldp_len - off) - len = oldp->oldp_len - off; - - if ((r = sys_safecopyto(MIB_PROC_NR, oldp->oldp_grant, off, - (vir_bytes)buf, len)) != OK) - return r; - - return size; -} - -/* - * Copy out (partial) data to the user, from a vector of up to RMIB_IOV_MAX - * local buffers. The copy is automatically limited to the range of data - * requested by the user. Return the total requested length on success or an - * error code on failure. - */ -ssize_t -rmib_vcopyout(struct rmib_oldp * oldp, size_t off, const iovec_t * iov, - unsigned int iovcnt) -{ - static struct vscp_vec vec[RMIB_IOV_MAX]; - size_t size, chunk; - unsigned int i; - ssize_t r; - - assert(iov != NULL); - assert(iovcnt <= __arraycount(vec)); - - /* Take a shortcut for single-vector elements, saving a kernel copy. */ - if (iovcnt == 1) - return rmib_copyout(oldp, off, (const void *)iov->iov_addr, - iov->iov_size); - - /* - * Iterate through the full vector even if we cannot copy out all of - * it, because we need to compute the total length. - */ - for (size = i = 0; iovcnt > 0; iov++, iovcnt--) { - if (oldp != NULL && off < oldp->oldp_len) { - chunk = oldp->oldp_len - off; - if (chunk > iov->iov_size) - chunk = iov->iov_size; - - vec[i].v_from = SELF; - vec[i].v_to = MIB_PROC_NR; - vec[i].v_gid = oldp->oldp_grant; - vec[i].v_offset = off; - vec[i].v_addr = iov->iov_addr; - vec[i].v_bytes = chunk; - - off += chunk; - i++; - } - - size += iov->iov_size; - } - - /* Perform the copy, if there is anything to copy, that is. */ - if (i > 0 && (r = sys_vsafecopy(vec, i)) != OK) - return r; - - return size; -} - -/* - * Copy in data from the user. The given length must match exactly the length - * given by the user. Return OK or an error code. - */ -int -rmib_copyin(struct rmib_newp * __restrict newp, void * __restrict buf, - size_t len) -{ - - if (newp == NULL || len != newp->newp_len) - return EINVAL; - - if (len == 0) - return OK; - - return sys_safecopyfrom(MIB_PROC_NR, newp->newp_grant, 0, - (vir_bytes)buf, len); -} - -/* - * Copy out a node to userland, using the exchange format for nodes (namely, - * a sysctlnode structure). Return the size of the object that is (or, if the - * node falls outside the requested data range, would be) copied out on - * success, or a negative error code on failure. - */ -static ssize_t -rmib_copyout_node(struct rmib_call * call, struct rmib_oldp * oldp, - ssize_t off, unsigned int id, const struct rmib_node * rnode) -{ - struct sysctlnode scn; - int visible; - - if (!rmib_inrange(oldp, off)) - return sizeof(scn); /* nothing to do */ - - memset(&scn, 0, sizeof(scn)); - - /* - * We use CTLFLAG_SPARSE internally only. NetBSD uses these flags for - * different purposes. Either way, do not expose it to userland. - * hide any of them from the user. - */ - scn.sysctl_flags = SYSCTL_VERSION | - (rnode->rnode_flags & ~CTLFLAG_SPARSE); - scn.sysctl_num = id; - strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name)); - scn.sysctl_ver = call->call_rootver; - scn.sysctl_size = rnode->rnode_size; - - /* Some information is only visible if the user can access the node. */ - visible = (!(rnode->rnode_flags & CTLFLAG_PRIVATE) || - (call->call_flags & RMIB_FLAG_AUTH)); - - /* - * For immediate types, store the immediate value in the resulting - * structure, unless the caller is not authorized to obtain the value. - */ - if ((rnode->rnode_flags & CTLFLAG_IMMEDIATE) && visible) { - switch (SYSCTL_TYPE(rnode->rnode_flags)) { - case CTLTYPE_BOOL: - scn.sysctl_bdata = rnode->rnode_bool; - break; - case CTLTYPE_INT: - scn.sysctl_idata = rnode->rnode_int; - break; - case CTLTYPE_QUAD: - scn.sysctl_qdata = rnode->rnode_quad; - break; - } - } - - /* Special rules apply to parent nodes. */ - if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) { - /* Report the node size the way NetBSD does, just in case. */ - scn.sysctl_size = sizeof(scn); - - /* - * For real parent nodes, report child information, but only if - * the node itself is accessible by the caller. For function- - * driven nodes, set a nonzero function address, for trace(1). - */ - if (rnode->rnode_func == NULL && visible) { - scn.sysctl_csize = rnode->rnode_size; - scn.sysctl_clen = rnode->rnode_clen; - } else if (rnode->rnode_func != NULL) - scn.sysctl_func = SYSCTL_NODE_FN; - } - - /* Copy out the resulting node. */ - return rmib_copyout(oldp, off, &scn, sizeof(scn)); -} - -/* - * Given a query on a non-leaf (parent) node, provide the user with an array of - * this node's children. - */ -static ssize_t -rmib_query(struct rmib_call * call, struct rmib_node * rparent, - struct rmib_oldp * oldp, struct rmib_newp * newp) -{ - struct sysctlnode scn; - struct rmib_node *rnode; - unsigned int i, id; - ssize_t r, off; - - /* If the user passed in version numbers, check them. */ - if (newp != NULL) { - if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK) - return r; - - if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) - return EINVAL; - - /* - * If a node version number is given, it must match the version - * of the subtree or the root of the entire MIB version. - */ - if (scn.sysctl_ver != 0 && - scn.sysctl_ver != call->call_rootver && - scn.sysctl_ver != call->call_treever) - return EINVAL; - } - - /* Enumerate the child nodes of the given parent node. */ - off = 0; - - for (i = 0; i < rparent->rnode_size; i++) { - if (rparent->rnode_flags & CTLFLAG_SPARSE) { - id = rparent->rnode_icptr[i].rindir_id; - rnode = rparent->rnode_icptr[i].rindir_node; - } else { - id = i; - rnode = &rparent->rnode_cptr[i]; - - if (rnode->rnode_flags == 0) - continue; - } - - if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0) - return r; - off += r; - } - - return off; -} - -/* - * Copy out a node description to userland, using the exchange format for node - * descriptions (namely, a sysctldesc structure). Return the size of the - * object that is (or, if the description falls outside the requested data - * range, would be) copied out on success, or a negative error code on failure. - * The function may return 0 to indicate that nothing was copied out after all. - */ -static ssize_t -rmib_copyout_desc(struct rmib_call * call, struct rmib_oldp * oldp, - ssize_t off, unsigned int id, const struct rmib_node * rnode) -{ - struct sysctldesc scd; - size_t len, size; - ssize_t r; - - /* Descriptions of private nodes are considered private too. */ - if ((rnode->rnode_flags & CTLFLAG_PRIVATE) && - !(call->call_flags & RMIB_FLAG_AUTH)) - return 0; - - /* - * Unfortunately, we do not have a scratch buffer here. Instead, copy - * out the description structure and the actual description string - * separately. This is more costly, but remote subtrees are already - * not going to give the best performance ever. We do optimize for the - * case that there is no description, because that is relatively easy. - */ - /* The description length includes the null terminator. */ - if (rnode->rnode_desc != NULL) - len = strlen(rnode->rnode_desc) + 1; - else - len = 1; - - memset(&scd, 0, sizeof(scd)); - scd.descr_num = id; - scd.descr_ver = call->call_rootver; - scd.descr_len = len; - - size = offsetof(struct sysctldesc, descr_str); - - if (len == 1) { - scd.descr_str[0] = '\0'; /* superfluous */ - size++; - } - - /* Copy out the structure, possibly including a null terminator. */ - if ((r = rmib_copyout(oldp, off, &scd, size)) < 0) - return r; - - if (len > 1) { - /* Copy out the description itself. */ - if ((r = rmib_copyout(oldp, off + size, rnode->rnode_desc, - len)) < 0) - return r; - - size += len; - } - - /* - * By aligning just the size, we may leave garbage between the entries - * copied out, which is fine because it is userland's own data. - */ - return roundup2(size, sizeof(int32_t)); -} - -/* - * Look up a child node given a parent node and a child node identifier. - * Return a pointer to the child node if found, or NULL otherwise. The lookup - * procedure differs based on whether the parent node is sparse or not. - */ -static struct rmib_node * -rmib_lookup(struct rmib_node * rparent, unsigned int id) -{ - struct rmib_node *rnode; - struct rmib_indir *rindir; - unsigned int i; - - if (rparent->rnode_flags & CTLFLAG_SPARSE) { - rindir = rparent->rnode_icptr; - for (i = 0; i < rparent->rnode_size; i++, rindir++) - if (rindir->rindir_id == id) - return rindir->rindir_node; - } else { - if (id >= rparent->rnode_size) - return NULL; - rnode = &rparent->rnode_cptr[id]; - if (rnode->rnode_flags != 0) - return rnode; - } - - return NULL; -} - -/* - * Retrieve node descriptions in bulk, or retrieve a particular node's - * description. - */ -static ssize_t -rmib_describe(struct rmib_call * call, struct rmib_node * rparent, - struct rmib_oldp * oldp, struct rmib_newp * newp) -{ - struct sysctlnode scn; - struct rmib_node *rnode; - unsigned int i, id; - ssize_t r, off; - - if (newp != NULL) { - if ((r = rmib_copyin(newp, &scn, sizeof(scn))) != OK) - return r; - - if (SYSCTL_VERS(scn.sysctl_flags) != SYSCTL_VERSION) - return EINVAL; - - /* Locate the child node. */ - if ((rnode = rmib_lookup(rparent, scn.sysctl_num)) == NULL) - return ENOENT; - - /* Descriptions of private nodes are considered private too. */ - if ((rnode->rnode_flags & CTLFLAG_PRIVATE) && - !(call->call_flags & RMIB_FLAG_AUTH)) - return EPERM; - - /* - * If a description pointer was given, this is a request to - * set the node's description. We do not allow this, nor would - * we be able to support it, since we cannot access the data. - */ - if (scn.sysctl_desc != NULL) - return EPERM; - - /* - * Copy out the requested node's description. At this point we - * should be sure that this call does not return zero. - */ - return rmib_copyout_desc(call, oldp, 0, scn.sysctl_num, rnode); - } - - /* Describe the child nodes of the given parent node. */ - off = 0; - - for (i = 0; i < rparent->rnode_size; i++) { - if (rparent->rnode_flags & CTLFLAG_SPARSE) { - id = rparent->rnode_icptr[i].rindir_id; - rnode = rparent->rnode_icptr[i].rindir_node; - } else { - id = i; - rnode = &rparent->rnode_cptr[i]; - - if (rnode->rnode_flags == 0) - continue; - } - - if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0) - return r; - off += r; - } - - return off; -} - -/* - * Return a pointer to the data associated with the given node, or NULL if the - * node has no associated data. Actual calls to this function should never - * result in NULL - as long as the proper rules are followed elsewhere. - */ -static void * -rmib_getptr(struct rmib_node * rnode) -{ - - switch (SYSCTL_TYPE(rnode->rnode_flags)) { - case CTLTYPE_BOOL: - if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) - return &rnode->rnode_bool; - break; - case CTLTYPE_INT: - if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) - return &rnode->rnode_int; - break; - case CTLTYPE_QUAD: - if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) - return &rnode->rnode_quad; - break; - case CTLTYPE_STRING: - case CTLTYPE_STRUCT: - if (rnode->rnode_flags & CTLFLAG_IMMEDIATE) - return NULL; - break; - default: - return NULL; - } - - return rnode->rnode_data; -} - -/* - * Read current (old) data from a regular data node, if requested. Return the - * old data length. - */ -static ssize_t -rmib_read(struct rmib_node * rnode, struct rmib_oldp * oldp) -{ - void *ptr; - size_t oldlen; - int r; - - if ((ptr = rmib_getptr(rnode)) == NULL) - return EINVAL; - - if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_STRING) - oldlen = strlen(rnode->rnode_data) + 1; - else - oldlen = rnode->rnode_size; - - if (oldlen > SSIZE_MAX) - return EINVAL; - - /* Copy out the current data, if requested at all. */ - if (oldp != NULL && (r = rmib_copyout(oldp, 0, ptr, oldlen)) < 0) - return r; - - /* Return the current length in any case. */ - return (ssize_t)oldlen; -} - -/* - * Write new data into a regular data node, if requested. - */ -static int -rmib_write(struct rmib_call * call, struct rmib_node * rnode, - struct rmib_newp * newp) -{ - bool b[(sizeof(bool) == sizeof(char)) ? 1 : -1]; /* for sanitizing */ - char *src, *dst, buf[RMIB_STACKBUF]; - size_t newlen; - int r; - - if (newp == NULL) - return OK; /* nothing to do */ - - /* - * When setting a new value, we cannot risk doing an in-place update: - * the copy from userland may fail halfway through, in which case an - * in-place update could leave the node value in a corrupted state. - * Thus, we must first fetch any new data into a temporary buffer. - */ - newlen = newp->newp_len; - - if ((dst = rmib_getptr(rnode)) == NULL) - return EINVAL; - - switch (SYSCTL_TYPE(rnode->rnode_flags)) { - case CTLTYPE_BOOL: - case CTLTYPE_INT: - case CTLTYPE_QUAD: - case CTLTYPE_STRUCT: - /* Non-string types must have an exact size match. */ - if (newlen != rnode->rnode_size) - return EINVAL; - break; - case CTLTYPE_STRING: - /* - * Strings must not exceed their buffer size. There is a - * second check further below, because we allow userland to - * give us an unterminated string. In that case we terminate - * it ourselves, but then the null terminator must fit as well. - */ - if (newlen > rnode->rnode_size) - return EINVAL; - break; - default: - return EINVAL; - } - - /* - * If we cannot fit the data in the small stack buffer, then allocate a - * temporary buffer. We add one extra byte so that we can add a null - * terminator at the end of strings in case userland did not supply - * one. Either way, we must free the temporary buffer later! - */ - if (newlen + 1 > sizeof(buf)) { - /* - * For regular users, we do not want to perform dynamic memory - * allocation. Thus, for CTLTYPE_ANYWRITE nodes, only the - * superuser may set values exceeding the small buffer in size. - */ - if (!(call->call_flags & RMIB_FLAG_AUTH)) - return EPERM; - - /* Do not return ENOMEM on allocation failure. */ - if ((src = malloc(newlen + 1)) == NULL) - return EINVAL; - } else - src = buf; - - /* Copy in the data. Note that the given new length may be zero. */ - if ((r = rmib_copyin(newp, src, newlen)) == OK) { - /* Check and, if acceptable, store the new value. */ - switch (SYSCTL_TYPE(rnode->rnode_flags)) { - case CTLTYPE_BOOL: - /* Sanitize booleans. See the MIB code for details. */ - b[0] = (bool)src[0]; - memcpy(dst, &b[0], sizeof(b[0])); - break; - case CTLTYPE_INT: - case CTLTYPE_QUAD: - case CTLTYPE_STRUCT: - memcpy(dst, src, rnode->rnode_size); - break; - case CTLTYPE_STRING: - if (newlen == rnode->rnode_size && - src[newlen - 1] != '\0') { - /* Our null terminator does not fit! */ - r = EINVAL; - break; - } - src[newlen] = '\0'; - strlcpy(dst, src, rnode->rnode_size); - break; - default: - r = EINVAL; - } - } - - if (src != buf) - free(src); - - return r; -} - -/* - * Read and/or write the value of a regular data node. A regular data node is - * a leaf node. Typically, a leaf node has no associated function, in which - * case this function will be used instead. In addition, this function may be - * used from handler functions as part of their functionality. - */ -ssize_t -rmib_readwrite(struct rmib_call * call, struct rmib_node * rnode, - struct rmib_oldp * oldp, struct rmib_newp * newp) -{ - ssize_t len; - int r; - - /* Copy out old data, if requested. Always get the old data length. */ - if ((r = len = rmib_read(rnode, oldp)) < 0) - return r; - - /* Copy in new data, if requested. */ - if ((r = rmib_write(call, rnode, newp)) != OK) - return r; - - /* Return the old data length. */ - return len; -} - -/* - * Handle a sysctl(2) call from a user process, relayed by the MIB service to - * us. If the call succeeds, return the old length. The MIB service will - * perform a check against the given old length and return ENOMEM to the caller - * when applicable, so we do not have to do that here. If the call fails, - * return a negative error code. - */ -static ssize_t -rmib_call(const message * m_in) -{ - struct rmib_node *rnode, *rparent; - struct rmib_call call; - struct rmib_oldp oldp_data, *oldp; - struct rmib_newp newp_data, *newp; - unsigned int root_id, prefixlen, namelen; - int r, id, is_leaf, has_func, name[CTL_MAXNAME]; - - /* - * Look up the root of the subtree that is the subject of the call. If - * the call is for a subtree that is not registered, return ERESTART to - * indicate to the MIB service that it should deregister the subtree it - * thinks we have. This case may occur in practice if a deregistration - * request from us crosses a sysctl call request from the MIB service. - */ - root_id = m_in->m_mib_lsys_call.root_id; - if (root_id >= __arraycount(rnodes) || - (rnode = rnodes[root_id].rno_node) == NULL) - return ERESTART; - - /* - * Use the name of the mounted subtree as prefix to the given name, so - * that call_oname will point to the complete name of the node. This - * is necessary for the few queries that make use of call_oname. - */ - prefixlen = rnodes[root_id].rno_namelen; - memcpy(name, rnodes[root_id].rno_name, prefixlen * sizeof(name[0])); - - /* - * Set up all data structures that we need to use while handling the - * call processing. Start by copying in the remainder of the MIB name. - */ - /* A zero name length is valid and should always yield EISDIR. */ - namelen = m_in->m_mib_lsys_call.name_len; - if (prefixlen + namelen > __arraycount(name)) - return EINVAL; - - if (namelen > 0) { - r = sys_safecopyfrom(m_in->m_source, - m_in->m_mib_lsys_call.name_grant, 0, - (vir_bytes)&name[prefixlen], sizeof(name[0]) * namelen); - if (r != OK) - return r; - } - - oldp_data.oldp_grant = m_in->m_mib_lsys_call.oldp_grant; - oldp_data.oldp_len = m_in->m_mib_lsys_call.oldp_len; - oldp = (GRANT_VALID(oldp_data.oldp_grant)) ? &oldp_data : NULL; - - newp_data.newp_grant = m_in->m_mib_lsys_call.newp_grant; - newp_data.newp_len = m_in->m_mib_lsys_call.newp_len; - newp = (GRANT_VALID(newp_data.newp_grant)) ? &newp_data : NULL; - - call.call_endpt = m_in->m_mib_lsys_call.user_endpt; - call.call_oname = name; - call.call_name = &name[prefixlen]; - call.call_namelen = namelen; - call.call_flags = m_in->m_mib_lsys_call.flags; - call.call_rootver = m_in->m_mib_lsys_call.root_ver; - call.call_treever = m_in->m_mib_lsys_call.tree_ver; - - /* - * Dispatch the call. - */ - for (rparent = rnode; call.call_namelen > 0; rparent = rnode) { - id = call.call_name[0]; - call.call_name++; - call.call_namelen--; - - assert(SYSCTL_TYPE(rparent->rnode_flags) == CTLTYPE_NODE); - - /* Check for meta-identifiers. */ - if (id < 0) { - /* - * A meta-identifier must always be the last name - * component. - */ - if (call.call_namelen > 0) - return EINVAL; - - switch (id) { - case CTL_QUERY: - return rmib_query(&call, rparent, oldp, newp); - case CTL_DESCRIBE: - return rmib_describe(&call, rparent, oldp, - newp); - case CTL_CREATE: - case CTL_DESTROY: - /* We support fully static subtrees only. */ - return EPERM; - default: - return EOPNOTSUPP; - } - } - - /* Locate the child node. */ - if ((rnode = rmib_lookup(rparent, id)) == NULL) - return ENOENT; - - /* Check if access is permitted at this level. */ - if ((rnode->rnode_flags & CTLFLAG_PRIVATE) && - !(call.call_flags & RMIB_FLAG_AUTH)) - return EPERM; - - /* - * Is this a leaf node, and/or is this node handled by a - * function? If either is true, resolution ends at this level. - */ - is_leaf = (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE); - has_func = (rnode->rnode_func != NULL); - - /* - * The name may be longer only if the node is not a leaf. That - * also applies to leaves with functions, so check this first. - */ - if (is_leaf && call.call_namelen > 0) - return ENOTDIR; - - /* - * If resolution indeed ends here, and the user supplied new - * data, check if writing is allowed. - */ - if ((is_leaf || has_func) && newp != NULL) { - if (!(rnode->rnode_flags & CTLFLAG_READWRITE)) - return EPERM; - - if (!(rnode->rnode_flags & CTLFLAG_ANYWRITE) && - !(call.call_flags & RMIB_FLAG_AUTH)) - return EPERM; - } - - /* If this node has a handler function, let it do the work. */ - if (has_func) - return rnode->rnode_func(&call, rnode, oldp, newp); - - /* For regular data leaf nodes, handle generic access. */ - if (is_leaf) - return rmib_readwrite(&call, rnode, oldp, newp); - - /* No function and not a leaf? Descend further. */ - } - - /* If we get here, the name refers to a node array. */ - return EISDIR; -} - -/* - * Initialize the given node and recursively all its node-type children, - * assigning the proper child length value to each of them. - */ -static void -rmib_init(struct rmib_node * rparent) -{ - struct rmib_node *rnode; - unsigned int i; - - for (i = 0; i < rparent->rnode_size; i++) { - if (rparent->rnode_flags & CTLFLAG_SPARSE) { - /* Indirect lists must be sorted ascending by ID. */ - assert(i == 0 || rparent->rnode_icptr[i].rindir_id > - rparent->rnode_icptr[i - 1].rindir_id); - - rnode = rparent->rnode_icptr[i].rindir_node; - } else { - rnode = &rparent->rnode_cptr[i]; - - if (rnode->rnode_flags == 0) - continue; - } - - rparent->rnode_clen++; - - if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) - rmib_init(rnode); /* recurse */ - } -} - -/* - * Request that the MIB service (re)mount the subtree identified by the given - * identifier. This is a one-way request, so we never hear whether mounting - * succeeds. There is not that much we can do if it fails anyway though. - */ -static void -rmib_send_reg(int id) -{ - message m; - int r; - - memset(&m, 0, sizeof(m)); - - m.m_type = MIB_REGISTER; - m.m_lsys_mib_register.root_id = id; - m.m_lsys_mib_register.flags = SYSCTL_VERSION | - (rnodes[id].rno_node->rnode_flags & ~CTLFLAG_SPARSE); - m.m_lsys_mib_register.csize = rnodes[id].rno_node->rnode_size; - m.m_lsys_mib_register.clen = rnodes[id].rno_node->rnode_clen; - m.m_lsys_mib_register.miblen = rnodes[id].rno_namelen; - memcpy(m.m_lsys_mib_register.mib, rnodes[id].rno_name, - sizeof(rnodes[id].rno_name[0]) * rnodes[id].rno_namelen); - - if ((r = asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY)) != OK) - panic("asynsend3 call to MIB service failed: %d", r); -} - -/* - * Register a MIB subtree. Initialize the subtree, add it to the local set, - * and send a registration request for it to the MIB service. - */ -int -rmib_register(const int * name, unsigned int namelen, struct rmib_node * rnode) -{ - unsigned int id, free_id; - - /* A few basic sanity checks. */ - if (namelen == 0 || namelen >= __arraycount(rnodes[0].rno_name)) - return EINVAL; - if (SYSCTL_TYPE(rnode->rnode_flags) != CTLTYPE_NODE) - return EINVAL; - - /* Make sure this is a new subtree, and find a free slot for it. */ - for (id = free_id = 0; id < __arraycount(rnodes); id++) { - if (rnodes[id].rno_node == rnode) - return EEXIST; - else if (rnodes[id].rno_node == NULL && - rnodes[free_id].rno_node != NULL) - free_id = id; - } - - if (rnodes[free_id].rno_node != NULL) - return ENOMEM; - - rnodes[free_id].rno_node = rnode; - rnodes[free_id].rno_namelen = namelen; - memcpy(rnodes[free_id].rno_name, name, sizeof(name[0]) * namelen); - - /* - * Initialize the entire subtree. This will also compute rnode_clen - * for the given rnode, so do this before sending the message. - */ - rmib_init(rnode); - - /* Send the registration request to the MIB service. */ - rmib_send_reg(free_id); - - return OK; -} - -/* - * Deregister a previously registered subtree, both internally and with the MIB - * service. Return OK if the deregistration procedure has been started, in - * which case the given subtree is guaranteed to no longer be accessed. Return - * a negative error code on failure. - */ -int -rmib_deregister(struct rmib_node * rnode) -{ - message m; - unsigned int id; - - for (id = 0; id < __arraycount(rnodes); id++) - if (rnodes[id].rno_node == rnode) - break; - - if (id == __arraycount(rnodes)) - return ENOENT; - - rnodes[id].rno_node = NULL; - - /* - * Request that the MIB service unmount the subtree. We completely - * ignore failure here, because the caller would not be able to do - * anything about it anyway. We may also still receive sysctl call - * requests for the node we just deregistered, but this is caught - * during request processing. Reuse of the rnodes[] slot could be a - * potential problem though. We could use sequence numbers in the root - * identifiers to resolve that problem if it ever occurs in reality. - */ - memset(&m, 0, sizeof(m)); - - m.m_type = MIB_DEREGISTER; - m.m_lsys_mib_register.root_id = id; - - (void)asynsend3(MIB_PROC_NR, &m, AMF_NOREPLY); - - return OK; -} - -/* - * Reregister all previously registered subtrees. This routine should be - * called after the main program has determined that the MIB service has been - * restarted. - */ -void -rmib_reregister(void) -{ - unsigned int id; - - for (id = 0; id < __arraycount(rnodes); id++) - if (rnodes[id].rno_node != NULL) - rmib_send_reg(id); -} - -/* - * Reset all registrations, without involving MIB communication. This routine - * exists for testing purposes only, and may disappear in the future. - */ -void -rmib_reset(void) -{ - - memset(rnodes, 0, sizeof(rnodes)); -} - -/* - * Process a request from the MIB service for information about the root node - * of a subtree, specifically its name and description. - */ -static int -rmib_info(const message * m_in) -{ - struct rmib_node *rnode; - unsigned int id; - const char *ptr; - size_t size; - int r; - - id = m_in->m_mib_lsys_info.root_id; - if (id >= __arraycount(rnodes) || rnodes[id].rno_node == NULL) - return ENOENT; - rnode = rnodes[id].rno_node; - - /* The name must fit. If it does not, the service writer messed up. */ - size = strlen(rnode->rnode_name) + 1; - if (size > m_in->m_mib_lsys_info.name_size) - return ENAMETOOLONG; - - r = sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.name_grant, 0, - (vir_bytes)rnode->rnode_name, size); - if (r != OK) - return r; - - /* If there is no (optional) description, copy out an empty string. */ - ptr = (rnode->rnode_desc != NULL) ? rnode->rnode_desc : ""; - size = strlen(ptr) + 1; - - if (size > m_in->m_mib_lsys_info.desc_size) - size = m_in->m_mib_lsys_info.desc_size; - - return sys_safecopyto(m_in->m_source, m_in->m_mib_lsys_info.desc_grant, - 0, (vir_bytes)ptr, size); -} - -/* - * Process a request from the MIB service. The given message should originate - * from the MIB service and have one of the COMMON_MIB_ requests as type. - */ -void -rmib_process(const message * m_in, int ipc_status) -{ - message m_out; - uint32_t req_id; - ssize_t r; - - /* Only the MIB service may issue these requests. */ - if (m_in->m_source != MIB_PROC_NR) - return; - - /* Process the actual request. */ - switch (m_in->m_type) { - case COMMON_MIB_INFO: - req_id = m_in->m_mib_lsys_info.req_id; - - r = rmib_info(m_in); - - break; - - case COMMON_MIB_CALL: - req_id = m_in->m_mib_lsys_call.req_id; - - r = rmib_call(m_in); - - break; - - default: - /* - * HACK: assume that for all current and future requests, the - * request ID field is in the same place. We could create a - * m_mib_lsys_unknown pseudo message type for this, but, eh. - */ - req_id = m_in->m_mib_lsys_info.req_id; - - r = ENOSYS; - } - - /* Construct and send a reply message to the MIB service. */ - memset(&m_out, 0, sizeof(m_out)); - - m_out.m_type = COMMON_MIB_REPLY; - m_out.m_lsys_mib_reply.req_id = req_id; - m_out.m_lsys_mib_reply.status = r; - - if (IPC_STATUS_CALL(ipc_status) == SENDREC) - r = ipc_sendnb(m_in->m_source, &m_out); - else - r = asynsend3(m_in->m_source, &m_out, AMF_NOREPLY); - - if (r != OK) - printf("lsys:rmib: unable to send reply to %d: %zd\n", - m_in->m_source, r); -} diff --git a/minix/llvm/generate_gold_plugin.sh b/minix/llvm/generate_gold_plugin.sh deleted file mode 100755 index a022e160b..000000000 --- a/minix/llvm/generate_gold_plugin.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/sh - -cd $(dirname $0) - -: ${NETBSDSRCDIR=${PWD}/../..} -: ${LLVMSRCDIR=${NETBSDSRCDIR}/external/bsd/llvm/dist} -: ${ARCH=i386} -: ${JOBS=1} -: ${OBJ_LLVM=${NETBSDSRCDIR}/../obj_llvm.${ARCH}} -: ${OBJ=${NETBSDSRCDIR}/../obj.${ARCH}} -: ${CROSS_TOOLS=${OBJ}/"tooldir.`uname -s`-`uname -r`-`uname -m`"/bin} -: ${MAKE=make} - -echo ${NETBSDSRCDIR} -echo ${LLVMSRCDIR} -echo ${OBJ_LLVM} -echo ${OBJ} -echo ${CROSS_TOOLS} - -# Retrieve all the GPL sources -cd ${NETBSDSRCDIR} -find . -name fetch.sh -exec '{}' \; - -# Build LLVM manually -mkdir -p ${OBJ_LLVM} -cd ${OBJ_LLVM} - -${LLVMSRCDIR}/llvm/configure \ - --enable-targets=x86 \ - --with-c-include-dirs=/usr/include/clang-3.6:/usr/include \ - --disable-timestamps \ - --prefix=/usr \ - --sysconfdir=/etc/llvm \ - --with-clang-srcdir=${LLVMSRCDIR}/clang \ - --host=i586-elf32-minix \ - --with-binutils-include=${NETBSDSRCDIR}/external/gpl3/binutils/dist/include \ - --disable-debug-symbols \ - --enable-assertions \ - --enable-bindings=none \ - llvm_cv_gnu_make_command=${MAKE} \ - ac_cv_path_CIRCO="echo circo" \ - ac_cv_path_DOT="echo dot" \ - ac_cv_path_DOTTY="echo dotty" \ - ac_cv_path_FDP="echo fdp" \ - ac_cv_path_NEATO="echo neato" \ - ac_cv_path_TWOPI="echo twopi" \ - ac_cv_path_XDOT="echo xdot" \ - --enable-optimized - -${MAKE} -j ${JOBS} - -# Copy the gold plugin where the NetBSD build system expects it. -mkdir -p ${NETBSDSRCDIR}/minix/llvm/bin/ -cp ${OBJ_LLVM}/./Release+Asserts/lib/libLTO.so ${NETBSDSRCDIR}/minix/llvm/bin/ -cp ${OBJ_LLVM}/./Release+Asserts/lib/LLVMgold.so ${NETBSDSRCDIR}/minix/llvm/bin/ - -# Copy useful LLVM tools -mkdir -p ${CROSS_TOOLS} -cp ${OBJ_LLVM}/./Release+Asserts/bin/llc ${CROSS_TOOLS} -cp ${OBJ_LLVM}/./Release+Asserts/bin/opt ${CROSS_TOOLS} -cp ${OBJ_LLVM}/./Release+Asserts/bin/llvm-* ${CROSS_TOOLS} - -# Generate and Install default MINIX passes -cd ${NETBSDSRCDIR}/minix/llvm/passes/WeakAliasModuleOverride -${MAKE} install - -cd ${NETBSDSRCDIR}/minix/llvm/passes/hello -${MAKE} install - -cd ${NETBSDSRCDIR}/minix/llvm/passes/sectionify -${MAKE} install - -cd ${NETBSDSRCDIR}/minix/llvm/passes/magic -${MAKE} install - -cd ${NETBSDSRCDIR}/minix/llvm/passes/asr -${MAKE} install diff --git a/minix/man/man5/pkg_install.conf.5 b/minix/man/man5/pkg_install.conf.5 deleted file mode 100644 index bba6a96b7..000000000 --- a/minix/man/man5/pkg_install.conf.5 +++ /dev/null @@ -1,215 +0,0 @@ -.\" $NetBSD: pkg_install.conf.5.in,v 1.14 2010/06/16 23:02:49 joerg Exp $ -.\" -.\" Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. -.\" All rights reserved. -.\" -.\" This code is derived from software contributed to The NetBSD Foundation -.\" by Thomas Klausner. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -.\" POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd June 16, 2010 -.Dt PKG_INSTALL.CONF 5 -.Os -.Sh NAME -.Nm pkg_install.conf -.Nd configuration file for package installation tools -.Sh DESCRIPTION -The file -.Nm -contains system defaults for the package installation tools -as a list of variable-value pairs. -Each line has the format -.Ev VARIABLE=VALUE . -If the value consists of more than one line, each line is prefixed with -.Ev VARIABLE= . -.Pp -The current value of a variable can be checked by running -.Dl Ic pkg_admin config-var VARIABLE -.Pp -Some variables are overriden by environmental variables of the same name. -Those are marked by (*). -.Pp -The following variables are supported: -.Bl -tag -width indent -.It Dv ACCEPTABLE_LICENSES -List of licenses packages are allowed to carry. -License names are case-sensitive. -.It Dv ACTIVE_FTP -Force the use of active FTP. -.It Dv CACHE_INDEX -Cache directory listenings in memory. -This avoids retransfers of the large directory index for HTTP and is -enabled by default. -.It Dv CERTIFICATE_ANCHOR_PKGS -Path to the file containing the certificates used for validating -binary packages. -A package is trusted when a certificate chain ends in one of the -certificates contained in this file. -The certificates must be PEM-encoded. -.It Dv CERTIFICATE_ANCHOR_PKGVULN -Analogous to -.Dv CERTIFICATE_ANCHOR_PKGS . -The -.Pa pkg-vulnerabilities -is trusted when a certificate chain ends in one of the certificates -contained in this file. -.It Dv CERTIFICATE_CHAIN -Path to a file containing additional certificates that can be used -for completing certificate chains when validating binary packages or -pkg-vulnerabilities files. -.It Dv CHECK_LICENSE -Check the license conditions of packages before installing them. -Supported values are: -.Bl -tag -width interactiveXX -.It Dv no -The check is not performed. -.It Dv yes -The check is performed if the package has license conditions set. -.It Dv always -Passing the license check is required. -Missing license conditions are considered an error. -.El -.It Dv CHECK_END_OF_FILE -During vulnerability checks, consider packages that have reached end-of-life -as vulnerable. -This option is enabled by default. -.It Dv CHECK_VULNERABILITIES -Check for vulnerabilities when installing packages. -Supported values are: -.Bl -tag -width interactiveXX -.It Dv never -No check is performed. -.It Dv always -Passing the vulnerability check is required. -A missing pkg-vulnerabilities file is considered an error. -.It Dv interactive -The user is always asked to confirm installation of vulnerable packages. -.El -.It Dv CONFIG_CACHE_CONNECTIONS -Limit the global connection cache to this value. -For FTP this is the number of sessions without active command. -For HTTP this is the number of connections open with keep-alive. -.It Dv CONFIG_CACHE_CONNECTIONS_HOST -Like -.Dv CONFIG_CACHE_CONNECTIONS , -but limit the number of connections to the host as well. -See -.Xr fetch 3 -for further details -.It Dv DEFAULT_ACCEPTABLE_LICENSES -List of common Free and Open Source licenses packages are allowed to carry. -The default value contains all OSI approved licenses in pkgsrc on the date -pkg_install was released. -License names are case-sensitive. -.It Dv GPG -Path to -.Xr gpg 1 , -which can be used to verify the signature in the -.Pa pkg-vulnerabilities -file when running -.Dl Ic pkg_admin check-pkg-vulnerabilities -s -or -.Dl Ic pkg_admin fetch-pkg-vulnerabilities -s -It can also be used to verify and sign binary packages. -.It Dv GPG_KEYRING_PKGVULN -Non-default keyring to use for verifying GPG signatures of -.Pa pkg-vulnerabilities . -.It Dv GPG_KEYRING_SIGN -Non-default keyring to use for signing packages with GPG. -.It Dv GPG_KEYRING_VERIFY -Non-default keyring to use for verifying GPG signature of packages. -.It Dv GPG_SIGN_AS -User-id to use for signing packages. -.It Dv IGNORE_PROXY -Use direct connections and ignore -.Ev FTP_PROXY -and -.Ev HTTP_PROXY . -.It Dv IGNORE_URL -One line per advisory which should be ignored when running -.Dl Ic pkg_admin audit -The URL from the -.Pa pkg-vulnerabilities -file should be used as value. -.It Dv PKG_DBDIR (*) -Location of the packages database. -This option is always overriden by the argument of the -.Fl K -option. -.It Dv PKG_PATH (*) -Search path for packages. -The entries are separated by semicolon. -Each entry specifies a directory or URL to search for packages. -.It Dv PKG_REFCOUNT_DBDIR (*) -Location of the package reference counts database directory. -The default value is -.Pa ${PKG_DBDIR}.refcount . -.It Dv PKGVULNDIR -Directory name in which the -.Pa pkg-vulnerabilities -file resides. -Default is -.Pa ${PKG_DBDIR} . -.It Dv PKGVULNURL -URL which is used for updating the local -.Pa pkg-vulnerabilities -file when running -.Dl Ic pkg_admin fetch-pkg-vulnerabilities -The default location is ftp.minix3.org using HTTP. -.Em Note : -Usually, only the compression type should be changed. -Currently supported are uncompressed files and files compressed by -.Xr bzip2 1 -.Pq Pa .bz2 -or -.Xr gzip 1 -.Pq Pa .gz . -.It Dv VERBOSE_NETIO -Log details of network IO to stderr. -.It Dv VERIFIED_INSTALLATION -Set trust level used when installation. -Supported values are: -.Bl -tag -width interactiveXX -.It Dv never -No signature checks are performed. -.It Dv always -A valid signature is required. -If the binary package can not be verified, the installation is terminated -.It Dv trusted -A valid signature is required. -If the binary package can not be verified, the user is asked interactively. -.It Dv interactive -The user is always asked interactively when installing a package. -.El -.El -.Sh FILES -.Bl -tag -width ".Pa /etc/pkg_install.conf" -.It Pa /etc/pkg_install.conf -Default location for the file described in this manual page. -.El -.Sh SEE ALSO -.Xr pkg_add 1 , -.Xr pkg_admin 1 -.Xr pkg_create 1 , -.Xr pkg_delete 1 , -.Xr pkg_info 1 diff --git a/minix/man/man5/pkg_summary.5 b/minix/man/man5/pkg_summary.5 deleted file mode 100644 index 08aece1c2..000000000 --- a/minix/man/man5/pkg_summary.5 +++ /dev/null @@ -1,133 +0,0 @@ -.\" $NetBSD: pkg_summary.5,v 1.9 2009/05/02 16:14:37 reed Exp $ -.\" -.\" Copyright (c) 2006 The NetBSD Foundation -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the NetBSD Foundation nor the names of its -.\" contributors may be used to endorse or promote products derived from -.\" this software without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION AND ITS CONTRIBUTORS -.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT -.\" NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND -.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT -.\" SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.Dd April 11, 2009 -.Dt PKG_SUMMARY 5 -.Os -.Sh NAME -.Nm pkg_summary -.Nd summary of binary package repository -.Sh DESCRIPTION -The file -.Nm -contains information about each package in a binary package -repository as a list of variable-value pairs. -The variables describing different packages are separated by one empty -line. -Each line has the format -.Ev VARIABLE=VALUE . -If the value consists of more than one line, each line is prefixed with -.Ev VARIABLE= . -Multi-line variables are guaranteed to be in consecutive lines. -.Pp -The following variables are used: -.Bl -tag -width indent -.It Ev BUILD_DATE -(required) The date and time when the package was built. -.It Ev CATEGORIES -(required) A list of categories which this package fits in, separated by -space. -.It Ev COMMENT -(required) A one-line description of the package. -.It Ev CONFLICTS -(optional) A list of dewey patterns of packages the package conflicts -with, one per line. -If missing, this package has no conflicts. -.It Ev DEPENDS -(optional) A list of dewey patterns of packages the package depends -on, one per line. -If missing, this package has no dependencies. -.It Ev DESCRIPTION -(required) A more detailed description of the package. -.\" DIGEST -.It Ev FILE_NAME -(optional) The name of the binary package file. -If not given, -.Pa PKGNAME.tgz -can be assumed. -.It Ev FILE_SIZE -(optional) The size of the binary package file, in bytes. -.It Ev HOMEPAGE -(optional) A URL where more information about the package can be found. -.It Ev LICENSE -(optional) The type of license this package is distributed under. -If empty or missing, it is OSI-approved. -.It Ev MACHINE_ARCH -(required) The architecture on which the package was compiled. -.It Ev OPSYS -(required) The operating system on which the package was compiled. -.It Ev OS_VERSION -(required) The version of the operating system on which the package -was compiled. -.It Ev PKG_OPTIONS -(optional) Any options selected to compile this package. -If missing, the package does not support options. -.It Ev PKGNAME -(required) The name of the package. -.It Ev PKGPATH -(required) The path of the package directory within pkgsrc. -.It Ev PKGTOOLS_VERSION -(required) The version of the package tools used to create the package. -.It Ev PREV_PKGPATH -(optional) The previous path of the package directory within pkgsrc when -a package was moved. -(See -.Ev SUPERSEDES -below for a renamed package.) -.It Ev PROVIDES -(optional) A list of shared libraries provided by the package, -including major version number, one per line. -If missing, this package does not provide shared libraries. -.It Ev REQUIRES -(optional) A list of shared libraries needed by the package, including -major version number, one per line. -If missing, this package does not require shared libraries. -.It Ev SIZE_PKG -(required) The size of the package when installed, in bytes. -.It Ev SUPERSEDES -(optional) A list of dewey patterns of previous packages this -package replaces, one per line. -This is used for package renaming. -.El -.Pp -The -.Nm pkg_summary -file can be generated using the -.Xr pkg_info 1 -.Fl X -option. -For example, the following will list this data for all installed packages: -.Pp -.Dl "pkg_info -X -a" -.Sh SEE ALSO -.Xr pkg_info 1 -.Sh HISTORY -The -.Nm pkg_summary -format was first officially documented in April 2006. diff --git a/minix/man/man5/statvfs.5 b/minix/man/man5/statvfs.5 deleted file mode 100644 index 0f1ee9d29..000000000 --- a/minix/man/man5/statvfs.5 +++ /dev/null @@ -1,89 +0,0 @@ -.\" $NetBSD: statvfs.5,v 1.10 2008/06/23 04:22:36 dholland Exp $ -.\" -.\" Copyright (c) 1989, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)statfs.2 8.5 (Berkeley) 5/24/95 -.\" -.Dd June 23, 2008 -.Dt STATVFS 5 -.Os -.Sh NAME -.Nm statvfs -.Nd file system statistics -.Sh SYNOPSIS -.In sys/types.h -.In sys/statvfs.h -.Sh DESCRIPTION -The -.Aq Pa sys/statvfs.h -header defines the structures and functions that -return information about a mounted file system. -The -.Nm statvfs -structure is defined as follows: -.Bd -literal - -struct statvfs { - unsigned long f_bsize; /* system block size */ - unsigned long f_frsize; /* system fragment size */ - - fsblkcnt_t f_blocks; /* number of blocks in file system */ - fsblkcnt_t f_bfree; /* free blocks avail in file system */ - fsblkcnt_t f_bavail; /* free blocks avail to non-root */ - - fsfilcnt_t f_files; /* total file nodes in file system */ - fsfilcnt_t f_ffree; /* free file nodes in file system */ - fsfilcnt_t f_favail; /* free file nodes avail to non-root */ - - unsigned long f_fsid; /* File system ID */ - - unsigned long f_flag; /* flags */ - - unsigned long f_namemax;/* maximum filename length */ -}; -.Ed -.Pp -The -.Fa f_flag -argument can have the following bits set: -.Bl -tag -width ST_SYNCHRONOUS -.It Dv ST_RDONLY -The filesystem is mounted read-only; -Even the super-user may not write on it. -.It Dv ST_NOSUID -Setuid and setgid bits on files are not honored when they are executed. -.It Dv ST_NOTRUNC -File names longer than NAME_MAX are not truncated. -.El -.Sh SEE ALSO -.Xr statvfs 2 -.Sh HISTORY -The -.Aq Pa sys/statvfs.h -header first appeared in -.Nx 3.0 . diff --git a/minix/man/man7/pkgsrc.7 b/minix/man/man7/pkgsrc.7 deleted file mode 100644 index c4444c2b3..000000000 --- a/minix/man/man7/pkgsrc.7 +++ /dev/null @@ -1,54 +0,0 @@ -.\" $NetBSD: pkgsrc.7,v 1.2 2007/10/07 12:59:14 kano Exp $ -.\" -.\" Copyright (c) 2007 The NetBSD Foundation, Inc. -.\" All rights reserved. -.\" -.\" This code is derived from software contributed to The NetBSD Foundation -.\" by Thomas Klausner. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS -.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS -.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -.\" POSSIBILITY OF SUCH DAMAGE. -.\" -.Dd March 2, 2007 -.Dt PKGSRC 7 -.Os -.Sh NAME -.Nm pkgsrc -.Nd MINIX packages collection (framework for third-party software) -.Sh DESCRIPTION -.The -.Mx -Packages Collection (pkgsrc) is a framework for building and -maintaining third-party software on -.Mx -and other -.Ux Ns -like -systems. -It is used to enable freely available software to be configured -and built easily on supported platforms. -.Pp -Tools are available to install ready-to-use packages and to perform -various administrative tasks for the package system. -.Sh SEE ALSO -.Xr pkg_add 1 , -.Xr pkg_delete 1 , -.Xr pkg_info 1 , -.Pa https://wiki.minix3.org/doku.php?id=developersguide:pkgsrc diff --git a/minix/tests/t84_h_spawn.c b/minix/tests/t84_h_spawn.c deleted file mode 100644 index 5c4dc24cf..000000000 --- a/minix/tests/t84_h_spawn.c +++ /dev/null @@ -1,51 +0,0 @@ -/* $NetBSD: h_spawn.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */ - -/*- - * Copyright (c) 2012 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Charles Zhang and - * Martin Husemann . - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -int -main(int argc, char **argv) -{ - unsigned long ret; - char *endp; - - if (argc < 2) { - fprintf(stderr, "usage:\n\t%s (retcode)\n", getprogname()); - exit(255); - } - ret = strtoul(argv[1], &endp, 10); -#if DEBUG - fprintf(stderr, "%s exiting with status %lu\n", getprogname(), ret); -#endif - return ret; -} diff --git a/minix/tests/t84_h_spawnattr.c b/minix/tests/t84_h_spawnattr.c deleted file mode 100644 index 377a71c21..000000000 --- a/minix/tests/t84_h_spawnattr.c +++ /dev/null @@ -1,94 +0,0 @@ -/* $NetBSD: h_spawnattr.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */ - -/*- - * Copyright (c) 2012 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Charles Zhang and - * Martin Husemann . - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include - -/* - * Helper to test the hardcoded assumptions from t_spawnattr.c - * Exit with apropriate exit status and print diagnostics to - * stderr explaining what is wrong. - */ -int -main(int argc, char **argv) -{ - int parent_pipe, res = EXIT_SUCCESS; - sigset_t sig; - struct sigaction act; - ssize_t rd; - char tmp; - - sigemptyset(&sig); - if (sigprocmask(0, NULL, &sig) < 0) { - fprintf(stderr, "%s: sigprocmask error\n", getprogname()); - res = EXIT_FAILURE; - } - if (!sigismember(&sig, SIGUSR1)) { - fprintf(stderr, "%s: SIGUSR not in procmask\n", getprogname()); - res = EXIT_FAILURE; - } - if (sigaction(SIGUSR1, NULL, &act) < 0) { - fprintf(stderr, "%s: sigaction error\n", getprogname()); - res = EXIT_FAILURE; - } - if (act.sa_sigaction != (void *)SIG_DFL) { - fprintf(stderr, "%s: SIGUSR1 action != SIG_DFL\n", - getprogname()); - res = EXIT_FAILURE; - } - - if (argc >= 2) { - parent_pipe = atoi(argv[1]); - if (parent_pipe > 2) { -#if DEBUG - printf("%s: waiting for command from parent on pipe " - "%d\n", getprogname(), parent_pipe); -#endif - rd = read(parent_pipe, &tmp, 1); - if (rd == 1) { -#if DEBUG - printf("%s: got command %c from parent\n", - getprogname(), tmp); -#endif - } else if (rd == -1) { - printf("%s: %d is no pipe, errno %d\n", - getprogname(), parent_pipe, errno); - res = EXIT_FAILURE; - } - } - } - - return res; -} diff --git a/minix/tests/test48.c b/minix/tests/test48.c deleted file mode 100644 index 8b8d2458f..000000000 --- a/minix/tests/test48.c +++ /dev/null @@ -1,607 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -int max_error = 3; -#include "common.h" - -#define err() e(__LINE__) - -static void printstr(const char *s) -{ - if (s) - printf("\"%s\"", s); - else - printf("NULL"); -} - -static void test_getaddrinfo_err( - int n, - const char *nodename, - const char *servname, - int passhints, - int flags, - int family, - int socktype, - const char *exp_result, - const char *result) -{ - printf("error %d: getaddrinfo(", n); - printstr(nodename); - printf(", "); - printstr(servname); - printf(", "); - if (passhints) - printf("{ 0x%x, %d, %d }", flags, family, socktype); - else - printf("NULL"); - - printf("); result: "); - printstr(result); - printf("; expected: "); - printstr(exp_result); - printf("\n"); - err(); -} - -/* yes, this is ugly, but not as ugly as repeating it all every time */ -#define TEST_GETADDRINFO_ERR_PARAMS \ - nodename, servname, passhints, flags, family, socktype - -static void test_getaddrinfo_err_nr( - int n, - const char *nodename, - const char *servname, - int passhints, - int flags, - int family, - int socktype, - int exp_result, - int result) -{ - char exp_result_s[23], result_s[23]; - - /* convert result to string */ - snprintf(exp_result_s, sizeof(exp_result_s), "%d/0x%x", - exp_result, exp_result); - snprintf(result_s, sizeof(result_s), "%d/0x%x", result, result); - test_getaddrinfo_err(n, TEST_GETADDRINFO_ERR_PARAMS, - exp_result_s, result_s); -} - -static void test_getnameinfo_err( - int n, - unsigned long ipaddr, - unsigned short port, - socklen_t nodelen, - socklen_t servicelen, - int flags, - const char *exp_result, - const char *result) -{ - printf("error %d: getnameinfo(0x%.8x, %d, %d, %d, 0x%x); result: ", - n, ntohl(ipaddr), ntohs(port), nodelen, servicelen, flags); - printstr(result); - printf("; expected: "); - printstr(exp_result); - printf("\n"); - err(); -} - -/* yes, this is ugly, but not as ugly as repeating it all every time */ -#define TEST_GETNAMEINFO_ERR_PARAMS ipaddr, port, nodelen, servicelen, flags - -static void test_getnameinfo_err_nr( - int n, - unsigned long ipaddr, - unsigned short port, - socklen_t nodelen, - socklen_t servicelen, - int flags, - int exp_result, - int result) -{ - char exp_result_s[23], result_s[23]; - - /* convert result to string */ - snprintf(exp_result_s, sizeof(exp_result_s), "%d/0x%x", - exp_result, exp_result); - snprintf(result_s, sizeof(result_s), "%d/0x%x", result, result); - test_getnameinfo_err(n, TEST_GETNAMEINFO_ERR_PARAMS, - exp_result_s, result_s); -} - -static void test_getaddrinfo( - const char *nodename, - int nodename_numerical, - const char *servname, - int servname_numerical, - int passhints, - int flags, - int family, - int socktype, - int exp_results, - unsigned long exp_ip, - int exp_canonname, - unsigned short exp_port) -{ - struct addrinfo *ai, *ai_cur; - struct addrinfo hints; - struct sockaddr_in *sockaddr_in; - int ai_count_dgram, ai_count_stream, r; - - /* some parameters are only meaningful with hints */ - assert(passhints || !flags); - assert(passhints || family == AF_UNSPEC); - assert(passhints || !socktype); - - /* a combination of parameters don't make sense to test */ - if (nodename == NULL && servname == NULL) return; - if (nodename == NULL && (flags & AI_NUMERICHOST)) return; - if (servname == NULL && (flags & AI_NUMERICSERV)) return; - - /* initialize hints */ - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = flags; - hints.ai_family = family; - hints.ai_socktype = socktype; - - /* perform query and test result */ - ai = (struct addrinfo *) 0xDEADBEEF; - r = getaddrinfo(nodename, servname, passhints ? &hints : NULL, &ai); - if (r < 0 || r >= 32 || !((1 << r) & exp_results)) - test_getaddrinfo_err_nr(1, TEST_GETADDRINFO_ERR_PARAMS, exp_results, r); - - if (r) - return; - - /* the function succeeded; do the results make sense? */ - ai_cur = ai; - ai_count_dgram = 0; - ai_count_stream = 0; - while (ai_cur) - { - /* - * TODO: this test was written for IPv4. For now, skip IPv6 - * results altogether. Later, we should add additional code - * for IPv6. However, since this test now largely exercises - * NetBSD code, it is not as important as it once was. - */ - if (ai_cur->ai_family == AF_INET6) { - ai_cur = ai_cur->ai_next; - continue; - } - - /* test result fields */ - if (ai_cur->ai_family != AF_INET) - test_getaddrinfo_err_nr(2, TEST_GETADDRINFO_ERR_PARAMS, - AF_INET, ai_cur->ai_family); - - if (socktype && ai_cur->ai_socktype != socktype) - test_getaddrinfo_err_nr(3, TEST_GETADDRINFO_ERR_PARAMS, - socktype, ai_cur->ai_socktype); - - switch (ai_cur->ai_socktype) - { - case SOCK_DGRAM: ai_count_dgram++; break; - case SOCK_STREAM: ai_count_stream++; break; - } - - /* do address and port match? */ - if (ai_cur->ai_addrlen != sizeof(struct sockaddr_in)) - test_getaddrinfo_err_nr(4, TEST_GETADDRINFO_ERR_PARAMS, - sizeof(struct sockaddr_in), - ai_cur->ai_addrlen); - else - { - sockaddr_in = (struct sockaddr_in *) ai_cur->ai_addr; - if (sockaddr_in->sin_addr.s_addr != exp_ip) - test_getaddrinfo_err_nr(5, - TEST_GETADDRINFO_ERR_PARAMS, - ntohl(exp_ip), - ntohl(sockaddr_in->sin_addr.s_addr)); - - if (sockaddr_in->sin_port != exp_port) - test_getaddrinfo_err_nr(6, - TEST_GETADDRINFO_ERR_PARAMS, - ntohs(exp_port), - ntohs(sockaddr_in->sin_port)); - } - - /* If a hostname is numeric, there can't be a canonical name. - * Instead, the returned canonname (if requested) will be - * identical to the supplied hostname */ - if (nodename != NULL && nodename_numerical && - (flags & AI_CANONNAME)) { - if (strncmp(ai_cur->ai_canonname, nodename, - strlen(nodename))) - test_getaddrinfo_err(11, - TEST_GETADDRINFO_ERR_PARAMS, - nodename, ai_cur->ai_canonname); - } else { - /* is canonical supplied? */ - if (exp_canonname && nodename && - (!ai_cur->ai_canonname || !*ai_cur->ai_canonname)) - test_getaddrinfo_err(7, - TEST_GETADDRINFO_ERR_PARAMS, - "(anything)", ai_cur->ai_canonname); - - if (!exp_canonname && ai_cur->ai_canonname) - test_getaddrinfo_err(8, - TEST_GETADDRINFO_ERR_PARAMS, - NULL, ai_cur->ai_canonname); - - } - /* move to next result */ - ai_cur = ai_cur->ai_next; - } - - /* If socket type is non-zero, make sure we got what we wanted. Else - * any result is okay. */ - if (socktype) { - if (ai_count_dgram != ((socktype == SOCK_STREAM) ? 0 : 1)) - test_getaddrinfo_err_nr(9, TEST_GETADDRINFO_ERR_PARAMS, - (socktype == SOCK_STREAM) ? 0 : 1, ai_count_dgram); - - if (ai_count_stream != ((socktype == SOCK_DGRAM) ? 0 : 1)) - test_getaddrinfo_err_nr(10, TEST_GETADDRINFO_ERR_PARAMS, - (socktype == SOCK_DGRAM) ? 0 : 1, ai_count_stream); - } - - /* clean up */ - freeaddrinfo(ai); -} - -static void memsetl(void *s, unsigned long c, size_t n) -{ - unsigned char *p = (unsigned char *) s; - size_t i; - - for (i = 0; i < n; i++) - p[i] = c >> (8 * (i % sizeof(c))); -} - -static void test_getnameinfo( - unsigned long ipaddr, - unsigned short port, - const char *exp_node, - socklen_t nodelen, - const char *exp_service, - socklen_t servicelen, - int flags, - int exp_results) -{ - struct sockaddr_in sockaddr; - char node[256], service[256]; - int r; - - /* avoid buffer overflows */ - assert(nodelen <= sizeof(node)); - assert(servicelen <= sizeof(service)); - - /* perform query and test result */ - sockaddr.sin_family = AF_INET; - sockaddr.sin_addr.s_addr = ipaddr; - sockaddr.sin_port = port; - memsetl(node, 0xDEADBEEF, nodelen); - memsetl(service, 0xDEADBEEF, servicelen); - r = getnameinfo((struct sockaddr *) &sockaddr, sizeof(sockaddr), - node, nodelen, service, servicelen, flags); - - if (r < 0 || r >= 32 || !((1 << r) & exp_results)) - test_getnameinfo_err_nr(1, TEST_GETNAMEINFO_ERR_PARAMS, - exp_results, r); - - if (r) - return; - - /* check results */ - if (nodelen && strcmp(exp_node, node) != 0) - test_getnameinfo_err(2, TEST_GETNAMEINFO_ERR_PARAMS, - exp_node, node); - - if (servicelen && strcmp(exp_service, service) != 0) - test_getnameinfo_err(2, TEST_GETNAMEINFO_ERR_PARAMS, - exp_service, service); -} - -static struct -{ - const char *nodename; - unsigned long ipaddr; - int numeric; - int canonname; - int need_network; - int exp_result; -} hosts[] = { - { NULL, 0x7f000001, 1, 1, 0, 0 }, - { "0.0.0.0", 0x00000000, 1, 0, 0, 0 }, - { "0.0.0.255", 0x000000ff, 1, 0, 0, 0 }, - { "0.0.255.0", 0x0000ff00, 1, 0, 0, 0 }, - { "0.255.0.0", 0x00ff0000, 1, 0, 0, 0 }, - { "255.0.0.0", 0xff000000, 1, 0, 0, 0 }, - { "127.0.0.1", 0x7f000001, 1, 0, 0, 0 }, - { "localhost", 0x7f000001, 0, 1, 0, 0, }, - { "test48.minix3.org", 0x7f010203, 0, 1, 1, 0, }, - { "minix3.example.com", 0x00000000, 0, 0, 1, (1< 1 && flagcount < 4) continue; - - /* skip tests that need but cannot use network */ - if (!use_network && hosts[i].need_network) - continue; - - /* determine flags */ - flags = (flag_PASSIVE ? AI_PASSIVE : 0) | - (flag_CANONNAME ? AI_CANONNAME : 0) | - (flag_NUMERICHOST ? AI_NUMERICHOST : 0) | - (flag_NUMERICSERV ? AI_NUMERICSERV : 0); - - /* some options require hints */ - if (families[k].value != AF_UNSPEC || - socktypes[l].value != 0 || flags) { - passhints = 1; - } - - /* flags may influence IP address */ - ipaddr = hosts[i].ipaddr; - if (!hosts[i].nodename && flag_PASSIVE) - ipaddr = INADDR_ANY; - - /* determine expected result */ - exp_results = - hosts[i].exp_result | - services[j].exp_result | - families[k].exp_result | - socktypes[l].exp_result; - if (!hosts[i].nodename && !services[j].servname) - exp_results |= (1 << EAI_NONAME); - - if (flag_NUMERICHOST && !hosts[i].numeric) - exp_results |= (1 << EAI_NONAME); - - if (flag_NUMERICSERV && !services[j].numeric) - exp_results |= (1 << EAI_NONAME); - - /* When we don't pass hints, getaddrinfo will find suitable - * settings for us. If we do pass hints, there might be - * conflicts. - */ - if (passhints) { - /* Can't have conflicting socket types */ - if (services[j].socktype && - socktypes[l].value && - socktypes[l].value != services[j].socktype) { - exp_results |= (1 << EAI_SERVICE); - } - } - - /* with no reason for failure, we demand success */ - if (!exp_results) - exp_results |= (1 << 0); - - /* test getaddrinfo function */ - test_getaddrinfo( - hosts[i].nodename, - hosts[i].numeric, - services[j].servname, - services[j].numeric, - passhints, - flags, - families[k].value, - socktypes[l].value, - exp_results, - htonl(ipaddr), - flag_CANONNAME && hosts[i].canonname, - htons(services[j].port)); - } -} - -static struct -{ - const char *nodename; - const char *nodenum; - unsigned long ipaddr; - int havename; -} ipaddrs[] = { - { "0.0.0.0", "0.0.0.0", 0x00000000, 0 }, - { "0.0.0.255", "0.0.0.255", 0x000000ff, 0 }, - { "0.0.255.0", "0.0.255.0", 0x0000ff00, 0 }, - { "0.255.0.0", "0.255.0.0", 0x00ff0000, 0 }, - { "255.0.0.0", "255.0.0.0", 0xff000000, 0 }, - { "localhost", "127.0.0.1", 0x7f000001, 1 }, - /* no reverse DNS unfortunately */ - /* { "minix3.org", "130.37.20.20", 0x82251414, 1 } */}; - -static struct -{ - const char *servname; - const char *servnum; - unsigned short port; - int socktype; - struct servent *se_tcp; /* getservbyport() s_name on this port with "tcp" */ - struct servent *se_udp; /* getservbyport() s_name on this port with "udp" */ -} ports[] = { - { "0", "0", 0, 0 }, - { "tcpmux", "1", 1, SOCK_STREAM }, - { "32767", "32767", 32767, 0 }, - { "32768", "32768", 32768, 0 }, - { "65535", "65535", 65535, 0 }, - { "echo", "7", 7, 0 }, - { "ftp", "21", 21, SOCK_STREAM }, - { "tftp", "69", 69, SOCK_DGRAM }}; - -static int buflens[] = { 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 255 }; - -static void test_getnameinfo_all(void) -{ - int flag_NUMERICHOST, flag_NAMEREQD, flag_NUMERICSERV, flag_DGRAM; - int exp_results, flagcount, flags, i, j, k, l; - const char *nodename, *servname; - - /* set ports servent structs */ - for (j = 0; j < LENGTH(ports); j++) { - struct servent *se_tcp, *se_udp; - - se_tcp = getservbyport(htons(ports[j].port), "tcp"); - ports[j].se_tcp = se_tcp; - - if(ports[j].se_tcp) { - ports[j].se_tcp = malloc(sizeof(struct servent)); - memcpy(ports[j].se_tcp, se_tcp, sizeof(*se_tcp)); - assert(ports[j].se_tcp->s_name); - ports[j].se_tcp->s_name = strdup(ports[j].se_tcp->s_name); - assert(ports[j].se_tcp->s_name); - } - - se_udp = getservbyport(htons(ports[j].port), "udp"); - ports[j].se_udp = se_udp; - - if(ports[j].se_udp) { - ports[j].se_udp = malloc(sizeof(struct servent)); - memcpy(ports[j].se_udp, se_udp, sizeof(*se_udp)); - assert(ports[j].se_udp->s_name); - ports[j].se_udp->s_name = strdup(ports[j].se_udp->s_name); - assert(ports[j].se_udp->s_name); - } - } - - /* loop through various parameter values */ - for (i = 0; i < LENGTH(ipaddrs); i++) - for (j = 0; j < LENGTH(ports); j++) - for (k = 0; k < LENGTH(buflens); k++) - for (l = 0; l < LENGTH(buflens); l++) - for (flag_NUMERICHOST = 0; flag_NUMERICHOST < 2; flag_NUMERICHOST++) - for (flag_NAMEREQD = 0; flag_NAMEREQD < 2; flag_NAMEREQD++) - for (flag_NUMERICSERV = 0; flag_NUMERICSERV < 2; flag_NUMERICSERV++) - for (flag_DGRAM = 0; flag_DGRAM < 2; flag_DGRAM++) - { - /* skip some redundant combinations */ - flagcount = flag_NUMERICHOST + flag_NAMEREQD + - flag_NUMERICSERV + flag_DGRAM; - if (flagcount > 1 && flagcount < 4) continue; - if (k > 1 && k < LENGTH(buflens) - 2 && - l > 1 && l < LENGTH(buflens) - 2) continue; - - /* determine flags */ - flags = (flag_NUMERICHOST ? NI_NUMERICHOST : 0) | - (flag_NAMEREQD ? NI_NAMEREQD : 0) | - (flag_NUMERICSERV ? NI_NUMERICSERV : 0) | - (flag_DGRAM ? NI_DGRAM : 0); - - /* determine expected result */ - exp_results = 0; - - nodename = flag_NUMERICHOST ? ipaddrs[i].nodenum : ipaddrs[i].nodename; - if (buflens[k] > 0 && buflens[k] <= strlen(nodename)) - exp_results |= (1 << EAI_OVERFLOW) | (1 << EAI_MEMORY); - - struct servent *se = flag_DGRAM ? ports[j].se_udp : ports[j].se_tcp; - - servname = (flag_NUMERICSERV) ? - ports[j].servnum : (se ? se->s_name : ports[j].servname); - - if (buflens[l] > 0 && buflens[l] <= strlen(servname)) - exp_results |= (1 << EAI_OVERFLOW) | (1 << EAI_MEMORY); - - if (flag_NAMEREQD && (!ipaddrs[i].havename || flag_NUMERICHOST) && buflens[k]) - exp_results |= (1 << EAI_NONAME); - - /* with no reason for failure, we demand success */ - if (!exp_results) - exp_results |= (1 << 0); - - /* perform the test */ - test_getnameinfo( - htonl(ipaddrs[i].ipaddr), - htons(ports[j].port), - nodename, - buflens[k], - servname, - buflens[l], - flags, - exp_results); - } -} - -int main(void) -{ - int use_network; - - start(48); - - use_network = get_setting_use_network(); - test_getaddrinfo_all(use_network); - test_getnameinfo_all(); - - quit(); - return 0; -} diff --git a/minix/tests/test56.c b/minix/tests/test56.c deleted file mode 100644 index d7a211b00..000000000 --- a/minix/tests/test56.c +++ /dev/null @@ -1,1733 +0,0 @@ -/* - * Test Program for Unix Domain Sockets - * - * Overview: This program tests Unix Domain Sockets. It attempts - * to exercise the functions associated with Unix Domain Sockets. - * It also attempts to make sure all of the functions which handle - * file/socket descriptors work correctly when given a socket - * descriptor for a Unix domain socket. It also implicitly checks - * for the existance of constants like AF_UNIX and structures like - * sockaddr_un (it won't compile if they aren't defined). Besides - * checking that the sockets work properly, this test program also - * checks that the errors returned conform to the POSIX 2008 - * standards. Some tests are omitted as they could adversely affect - * the operation of the host system. For example, implementing a test - * for socket() failing with errno = ENFILE would require using up all - * of the file descriptors supported by the OS (defined in - * /proc/sys/fs/file-max on Linux); this could cause problems for - * daemons and other processes running on the system. Some tests are - * omitted because they would require changes to libc or the kernel. - * For example, getting EINTR would require delaying the system call - * execution time long enough to raise a signal to interupt it. Some - * tests were omitted because the particular errors cannot occur when - * using Unix domain sockets. For example, write() will never fail with - * ENETDOWN because Unix domain sockets don't use network interfaces. - * - * Structure: Some functions can be tested or partially tested without - * making a connection, socket() for example. These have test - * functions like test_NAME(). The functionality that needs two way - * communication is contained within test_xfer(). - * - * Functions Tested: accept(), bind(), close(), connect(), dup(), - * dup2(), fstat(), getpeername(), getsockname(), getsockopt(), - * listen(), read(), readv(), recv(), recvfrom(), recvmsg(), select(), - * send(), sendmsg(), sendto(), setsockopt(), shutdown(), socket(), - * socketpair(), write(), writev() - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Maximum number of errors that we'll allow to occur before this test - * program gives us and quits. - */ -int max_error = 4; -#include "common.h" -#include "common-socket.h" - - -/* Use the common testing code instead of reinventing the wheel. */ - -/* path of the unix domain socket */ -#define TEST_SUN_PATH "test.sock" -#define TEST_SUN_PATHB "testb.sock" - -/* filenames for symlinks -- we link these to each other to test ELOOP .*/ -#define TEST_SYM_A "test.a" -#define TEST_SYM_B "test.b" - -/* text file and test phrase for testing file descriptor passing */ -#define TEST_TXT_FILE "test.txt" -#define MSG "This raccoon loves to eat bugs.\n" - -/* socket types supported */ -static int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}; - -static void test_header(void) -{ - struct sockaddr_un sun; - debug("entering test_header()"); - - sun.sun_family = AF_UNIX; - sun.sun_path[0] = 'x'; - sun.sun_path[1] = 'x'; - sun.sun_path[2] = 'x'; - sun.sun_path[3] = '\0'; - - if (SUN_LEN(&sun) != 5) { - test_fail("SUN_LEN(&sun) should be 5"); - } - - if (PF_UNIX != PF_LOCAL || PF_UNIX != AF_UNIX) { - test_fail("PF_UNIX, PF_LOCAL and AF_UNIX"); - } -} - -static void test_socketpair(void) -{ - char buf[128]; - struct sockaddr_un addr; - int socket_vector[2]; - int rc; - int i; - - debug("entering test_socketpair()"); - - UNLINK(TEST_SUN_PATH); - memset(&addr, '\0', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); - - debug("Testing socketpair() success"); - - rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector); - if (rc == -1) { - test_fail("socketpair() should have worked"); - } - - debug("Testing a simple read/write using sockets from socketpair()"); - memset(buf, '\0', sizeof(buf)); - - strncpy(buf, "Howdy Partner", sizeof(buf) - 1); - - rc = write(socket_vector[0], buf, sizeof(buf)); - if (rc == -1) { - test_fail("write(sd, buf, sizeof(buf)) failed unexpectedly"); - } - - memset(buf, '\0', sizeof(buf)); - - rc = read(socket_vector[1], buf, sizeof(buf)); - if (rc == -1) { - test_fail("read() failed unexpectedly"); - } - - if (strncmp(buf, "Howdy Partner", strlen("Howdy Partner")) != 0) { - test_fail("We did not read what we wrote"); - } - - CLOSE(socket_vector[0]); - CLOSE(socket_vector[1]); - - debug("Test socketpair() with all FDs open by this process"); - - for (i = 3; i < getdtablesize(); i++) { - rc = open("/dev/null", O_RDONLY); - if (rc == -1) { - test_fail("we couldn't open /dev/null for read"); - } - } - - rc = socketpair(PF_UNIX, SOCK_STREAM, 0, socket_vector); - if (!(rc == -1 && errno == EMFILE)) { - test_fail("socketpair() should have failed with EMFILE"); - } - - for (i = 3; i < getdtablesize(); i++) { - CLOSE(i); - } - - rc = socketpair(PF_UNIX, SOCK_STREAM, 4, socket_vector); - if (!(rc == -1 && errno == EPROTONOSUPPORT)) { - test_fail("socketpair() should have failed"); - } - - debug("leaving test_socketpair()"); -} - -static void test_ucred(void) -{ - struct unpcbid credentials; - socklen_t ucred_length; - uid_t euid = geteuid(); - gid_t egid = getegid(); - int sv[2]; - int rc; - - debug("Test peer credentials"); - - ucred_length = sizeof(credentials); - - rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); - if (rc == -1) { - test_fail("socketpair(PF_UNIX, SOCK_STREAM, 0, sv) failed"); - } - - memset(&credentials, '\0', ucred_length); - rc = getsockopt(sv[0], 0, LOCAL_PEEREID, &credentials, - &ucred_length); - if (rc == -1) { - test_fail("getsockopt(LOCAL_PEEREID) failed"); - } else if (credentials.unp_pid != getpid() || - credentials.unp_euid != geteuid() || - credentials.unp_egid != getegid()) { - printf("%d=%d %d=%d %d=%d",credentials.unp_pid, getpid(), - credentials.unp_euid, geteuid(), - credentials.unp_egid, getegid()); - test_fail("Credential passing gave us the wrong cred"); - } - - rc = getpeereid(sv[0], &euid, &egid); - if (rc == -1) { - test_fail("getpeereid(sv[0], &euid, &egid) failed"); - } else if (credentials.unp_euid != euid || - credentials.unp_egid != egid) { - test_fail("getpeereid() didn't give the correct euid/egid"); - } - - CLOSE(sv[0]); - CLOSE(sv[1]); -} - -static void callback_check_sockaddr(const struct sockaddr *sockaddr, - socklen_t sockaddrlen, const char *callname, int addridx) { - char buf[256]; - const char *path; - const struct sockaddr_un *sockaddr_un = - (const struct sockaddr_un *) sockaddr; - - switch (addridx) { - case 1: path = TEST_SUN_PATH; break; - case 2: path = TEST_SUN_PATHB; break; - default: - fprintf(stderr, "error: invalid addridx %d in " - "callback_check_sockaddr\n", addridx); - abort(); - } - - if (!(sockaddr_un->sun_family == AF_UNIX && - strncmp(sockaddr_un->sun_path, - path, - sizeof(sockaddr_un->sun_path) - 1) == 0)) { - - snprintf(buf, sizeof(buf), "%s() didn't return the right addr", - callname); - test_fail(buf); - fprintf(stderr, "exp: '%s' | got: '%s'\n", path, - sockaddr_un->sun_path); - } -} - -static void callback_cleanup(void) { - UNLINK(TEST_SUN_PATH); - UNLINK(TEST_SUN_PATHB); - UNLINK(TEST_SYM_A); - UNLINK(TEST_SYM_B); -} - -static void test_bind_unix(void) -{ - struct sockaddr_un addr; - int sd; - int rc; - - debug("entering test_bind_unix()"); - UNLINK(TEST_SUN_PATH); - memset(&addr, '\0', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); - - debug("Test bind() with an empty sun_path"); - - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - memset(addr.sun_path, '\0', sizeof(addr.sun_path)); - errno = 0; - - rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); - if (!(rc == -1 && errno == ENOENT)) { - test_fail("bind() should have failed with ENOENT"); - } - CLOSE(sd); - - debug("Test bind() using a symlink loop"); - - UNLINK(TEST_SUN_PATH); - UNLINK(TEST_SYM_A); - UNLINK(TEST_SYM_B); - - SYMLINK(TEST_SYM_B, TEST_SYM_A); - - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - - strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1); - errno = 0; - rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); - if (!((rc == -1) && (errno == EADDRINUSE))) { - test_fail("bind() should have failed with EADDRINUSE"); - } - CLOSE(sd); - - SYMLINK(TEST_SYM_A, TEST_SYM_B); - - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - - strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1); - strlcat(addr.sun_path, "/x", sizeof(addr.sun_path)); - errno = 0; - rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); - if (!((rc == -1) && (errno == ELOOP))) { - test_fail("bind() should have failed with ELOOP"); - } - CLOSE(sd); - - UNLINK(TEST_SUN_PATH); - UNLINK(TEST_SYM_A); - UNLINK(TEST_SYM_B); - - /* Test bind with garbage in sockaddr_un */ - memset(&addr, '?', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - addr.sun_path[0] = 'f'; - addr.sun_path[1] = 'o'; - addr.sun_path[2] = 'o'; - addr.sun_path[3] = '\0'; - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - rc = bind(sd, (struct sockaddr *) &addr, - offsetof(struct sockaddr_un, sun_path) + strlen(addr.sun_path) + - 1); - if (rc == -1) { - test_fail("bind() should have worked"); - } - CLOSE(sd); - UNLINK("foo"); - - debug("leaving test_bind_unix()"); -} - -static void callback_xfer_prepclient(void) { - debug("Creating symlink to TEST_SUN_PATH"); - - SYMLINK(TEST_SUN_PATH, TEST_SYM_A); -} - -static void callback_xfer_peercred(int sd) { - struct unpcbid credentials; - int rc; - socklen_t ucred_length; - - ucred_length = sizeof(credentials); - - debug("Test obtaining the peer credentials"); - - memset(&credentials, '\0', ucred_length); - rc = getsockopt(sd, 0, LOCAL_PEEREID, &credentials, &ucred_length); - - if (rc == -1) { - test_fail("[client] getsockopt() failed"); - } else if (credentials.unp_euid != geteuid() || - credentials.unp_egid != getegid()) { - printf("%d=* %d=%d %d=%d", credentials.unp_pid, - credentials.unp_euid, geteuid(), - credentials.unp_egid, getegid()); - test_fail("[client] Credential passing gave us a bad UID/GID"); - } -} - -static void -callback_set_listen_opt(int sd) -{ - int val; - - /* - * Several of the tests assume that a new connection to a server will - * not be established (i.e., go from "connecting" to "connected" state) - * until the server actually accepts the connection with an accept(2) - * call. With the new UDS implementation, this is no longer true: to - * match the behavior of other systems, UDS now preemptively connects - * the socket in anticipation of the accept(2) call. We can change - * back to the old behavior by setting LOCAL_CONNWAIT however, and - * since the test effectively tests a larger set of socket transitions - * that way, that is what we do for these tests. - */ - val = 1; - if (setsockopt(sd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) - test_fail("setsockopt(LOCAL_CONNWAIT)"); -} - -static void test_vectorio(int type) -{ - int sv[2]; - int rc; - struct iovec iov[3]; - char buf1[BUFSIZE]; - char buf2[BUFSIZE]; - char buf3[BUFSIZE]; - char buf4[BUFSIZE*3]; - const struct iovec *iovp = iov; - - debug("begin vectorio tests"); - - memset(buf1, '\0', BUFSIZE); - strncpy(buf1, "HELLO ", BUFSIZE - 1); - - memset(buf2, '\0', BUFSIZE); - strncpy(buf2, "WORLD", BUFSIZE - 1); - - memset(buf3, '\0', BUFSIZE); - - rc = socketpair(PF_UNIX, type, 0, sv); - if (rc == -1) { - test_fail("socketpair"); - } - - iov[0].iov_base = buf1; - iov[0].iov_len = strlen(buf1); - iov[1].iov_base = buf2; - iov[1].iov_len = strlen(buf2); - iov[2].iov_base = buf3; - iov[2].iov_len = 1; - - rc = writev(sv[0], iovp, 3); - if (rc == -1) { - test_fail("writev"); - } - - memset(buf4, '\0', BUFSIZE*3); - - rc = read(sv[1], buf4, BUFSIZE*3); - if (rc == -1) { - test_fail("read"); - } - - if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) { - test_fail("the string we read was not 'HELLO WORLD'"); - } - - memset(buf1, '\0', BUFSIZE); - strncpy(buf1, "Unit Test Time", BUFSIZE - 1); - - rc = write(sv[1], buf1, strlen(buf1) + 1); - if (rc == -1) { - test_fail("write"); - } - - memset(buf2, '\0', BUFSIZE); - memset(buf3, '\0', BUFSIZE); - memset(buf4, '\0', BUFSIZE*3); - - iov[0].iov_base = buf2; - iov[0].iov_len = 5; - iov[1].iov_base = buf3; - iov[1].iov_len = 5; - iov[2].iov_base = buf4; - iov[2].iov_len = 32; - - rc = readv(sv[0], iovp, 3); - if (rc == -1) { - test_fail("readv"); - } - - if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) || - strncmp(buf4, "Time", 4)) { - test_fail("readv"); - } - - rc = close(sv[0]); - if (rc == -1) { - test_fail("close"); - } - - rc = close(sv[1]); - if (rc == -1) { - test_fail("close"); - } - - debug("done vector io tests"); -} - -static void test_msg(int type) -{ - int sv[2]; - int rc; - struct msghdr msg1; - struct msghdr msg2; - struct iovec iov[3]; - char buf1[BUFSIZE]; - char buf2[BUFSIZE]; - char buf3[BUFSIZE]; - char buf4[BUFSIZE*3]; - - debug("begin sendmsg/recvmsg tests"); - - memset(buf1, '\0', BUFSIZE); - strncpy(buf1, "HELLO ", BUFSIZE - 1); - - memset(buf2, '\0', BUFSIZE); - strncpy(buf2, "WORLD", BUFSIZE - 1); - - memset(buf3, '\0', BUFSIZE); - - rc = socketpair(PF_UNIX, type, 0, sv); - if (rc == -1) { - test_fail("socketpair"); - } - - iov[0].iov_base = buf1; - iov[0].iov_len = strlen(buf1); - iov[1].iov_base = buf2; - iov[1].iov_len = strlen(buf2); - iov[2].iov_base = buf3; - iov[2].iov_len = 1; - - memset(&msg1, '\0', sizeof(struct msghdr)); - msg1.msg_name = NULL; - msg1.msg_namelen = 0; - msg1.msg_iov = iov; - msg1.msg_iovlen = 3; - msg1.msg_control = NULL; - msg1.msg_controllen = 0; - msg1.msg_flags = 0; - - rc = sendmsg(sv[0], &msg1, 0); - if (rc == -1) { - test_fail("writev"); - } - - memset(buf4, '\0', BUFSIZE*3); - - rc = read(sv[1], buf4, BUFSIZE*3); - if (rc == -1) { - test_fail("read"); - } - - if (strncmp(buf4, "HELLO WORLD", strlen("HELLO WORLD"))) { - test_fail("the string we read was not 'HELLO WORLD'"); - } - - memset(buf1, '\0', BUFSIZE); - strncpy(buf1, "Unit Test Time", BUFSIZE - 1); - - rc = write(sv[1], buf1, strlen(buf1) + 1); - if (rc == -1) { - test_fail("write"); - } - - memset(buf2, '\0', BUFSIZE); - memset(buf3, '\0', BUFSIZE); - memset(buf4, '\0', BUFSIZE*3); - - iov[0].iov_base = buf2; - iov[0].iov_len = 5; - iov[1].iov_base = buf3; - iov[1].iov_len = 5; - iov[2].iov_base = buf4; - iov[2].iov_len = 32; - - memset(&msg2, '\0', sizeof(struct msghdr)); - msg2.msg_name = NULL; - msg2.msg_namelen = 0; - msg2.msg_iov = iov; - msg2.msg_iovlen = 3; - msg2.msg_control = NULL; - msg2.msg_controllen = 0; - msg2.msg_flags = 0; - - rc = recvmsg(sv[0], &msg2, 0); - if (rc == -1) { - test_fail("readv"); - } - - if (strncmp(buf2, "Unit ", 5) || strncmp(buf3, "Test ", 5) || - strncmp(buf4, "Time", 4)) { - test_fail("readv"); - } - - rc = close(sv[0]); - if (rc == -1) { - test_fail("close"); - } - - rc = close(sv[1]); - if (rc == -1) { - test_fail("close"); - } -} - -static void test_scm_credentials(void) -{ - int rc; - int src; - int dst; - int one; - union { - struct sockcred cred; - char buf[SOCKCREDSIZE(NGROUPS_MAX)]; - } cred; - struct cmsghdr *cmsg = NULL; - struct sockaddr_un addr; - struct iovec iov[3]; - struct msghdr msg1; - struct msghdr msg2; - char buf1[BUFSIZE]; - char buf2[BUFSIZE]; - char buf3[BUFSIZE]; - char ctrl[BUFSIZE]; - socklen_t len, addrlen = sizeof(struct sockaddr_un); - - debug("test_scm_credentials"); - - UNLINK(TEST_SUN_PATH); - UNLINK(TEST_SUN_PATHB); - - debug("creating src socket"); - - src = socket(PF_UNIX, SOCK_DGRAM, 0); - if (src == -1) { - test_fail("socket"); - } - - debug("creating dst socket"); - - dst = socket(PF_UNIX, SOCK_DGRAM, 0); - if (dst == -1) { - test_fail("socket"); - } - - debug("binding src socket"); - - memset(&addr, '\0', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, TEST_SUN_PATHB, sizeof(addr.sun_path) - 1); - rc = bind(src, (struct sockaddr *) &addr, addrlen); - if (rc == -1) { - test_fail("bind"); - } - - debug("binding dst socket"); - - memset(&addr, '\0', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); - - rc = bind(dst, (struct sockaddr *) &addr, addrlen); - if (rc == -1) { - test_fail("bind"); - } - - debug("request credential passing"); - - one = 1; - rc = setsockopt(dst, 0, LOCAL_CREDS, &one, sizeof(one)); - if (rc == -1) { - test_fail("setsockopt(LOCAL_CREDS)"); - } - - debug("sending msg1"); - - memset(&buf1, '\0', BUFSIZE); - memset(&buf2, '\0', BUFSIZE); - memset(&buf3, '\0', BUFSIZE); - memset(&ctrl, '\0', BUFSIZE); - - strncpy(buf1, "Minix ", BUFSIZE-1); - strncpy(buf2, "is ", BUFSIZE-1); - strncpy(buf3, "great!", BUFSIZE-1); - - iov[0].iov_base = buf1; - iov[0].iov_len = 6; - iov[1].iov_base = buf2; - iov[1].iov_len = 3; - iov[2].iov_base = buf3; - iov[2].iov_len = 32; - - memset(&msg1, '\0', sizeof(struct msghdr)); - msg1.msg_name = &addr; - msg1.msg_namelen = addrlen; - msg1.msg_iov = iov; - msg1.msg_iovlen = 3; - msg1.msg_control = NULL; - msg1.msg_controllen = 0; - msg1.msg_flags = 0; - - rc = sendmsg(src, &msg1, 0); - if (rc == -1) { - test_fail("sendmsg"); - } - - memset(&buf1, '\0', BUFSIZE); - memset(&buf2, '\0', BUFSIZE); - memset(&buf3, '\0', BUFSIZE); - memset(&ctrl, '\0', BUFSIZE); - - iov[0].iov_base = buf1; - iov[0].iov_len = 9; - iov[1].iov_base = buf2; - iov[1].iov_len = 32; - - memset(&addr, '\0', sizeof(struct sockaddr_un)); - memset(&msg2, '\0', sizeof(struct msghdr)); - msg2.msg_name = &addr; - msg2.msg_namelen = sizeof(struct sockaddr_un); - msg2.msg_iov = iov; - msg2.msg_iovlen = 2; - msg2.msg_control = ctrl; - msg2.msg_controllen = BUFSIZE; - msg2.msg_flags = 0; - - debug("recv msg2"); - - rc = recvmsg(dst, &msg2, 0); - if (rc == -1) { - test_fail("recvmsg"); - } - - debug("checking results"); - - if (strncmp(buf1, "Minix is ", 9) || strncmp(buf2, "great!", 6)) { - test_fail("recvmsg"); - } - - /* we need to use the full path "/usr/src/test/DIR_56/testb.sock" - * because that is what is returned by recvmsg(). - */ - if (addr.sun_family != AF_UNIX || strcmp(addr.sun_path, - TEST_SUN_PATHB)) { - test_fail("recvmsg"); - } - - debug("looking for credentials"); - - len = 0; - - memset(&cred, 'x', sizeof(cred)); - for (cmsg = CMSG_FIRSTHDR(&msg2); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msg2, cmsg)) { - - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDS) { - /* Great, this alignment business! But then at least - * give me a macro to compute the actual data length.. - */ - len = cmsg->cmsg_len - (socklen_t) - ((char *)CMSG_DATA(cmsg) - (char *)cmsg); - - if (len < sizeof(struct sockcred)) - test_fail("credentials too small"); - else if (len > sizeof(cred)) - test_fail("credentials too large"); - memcpy(cred.buf, CMSG_DATA(cmsg), len); - break; - } - } - - if (len == 0) - test_fail("no credentials found"); - - if (len != SOCKCREDSIZE(cred.cred.sc_ngroups)) - test_fail("wrong credentials size"); - - /* - * TODO: check supplementary groups. This whole test is pretty much - * pointless since we're running with very standard credentials anyway. - */ - if (cred.cred.sc_uid != getuid() || - cred.cred.sc_euid != geteuid() || - cred.cred.sc_gid != getgid() || - cred.cred.sc_egid != getegid() || - cred.cred.sc_ngroups < 0 || cred.cred.sc_ngroups > NGROUPS_MAX) { - test_fail("did no receive the proper credentials"); - } - - rc = close(dst); - if (rc == -1) { - test_fail("close"); - } - - rc = close(src); - if (rc == -1) { - test_fail("close"); - } - - UNLINK(TEST_SUN_PATH); - UNLINK(TEST_SUN_PATHB); -} - -static void test_connect(const struct socket_test_info *info) -{ - int i, sd, sds[2], rc; - - /* connect() is already tested throughout test56, but - * in most cases the client and server end up on /dev/uds - * minor 0 and minor 1. This test opens some sockets first and - * then calls test_simple_client_server(). This forces the - * client and server minor numbers higher in the descriptor table. - */ - - debug("starting test_connect()"); - - sd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (sd == -1) { - test_fail("couldn't create a socket"); - } - - rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sds); - if (rc == -1) { - test_fail("couldn't create a socketpair"); - } - - for (i = 0; i < 3; i++) { - test_simple_client_server(info, types[i]); - } - - rc = close(sds[1]); - if (rc == -1) { - test_fail("close() failed"); - } - - rc = close(sds[0]); - if (rc == -1) { - test_fail("close() failed"); - } - - rc = close(sd); - if (rc == -1) { - test_fail("close() failed"); - } - - debug("exiting test_connect()"); -} - -static int test_multiproc_read(void) -{ -/* test that when we fork() a process with an open socket descriptor, - * the descriptor in each process points to the same thing. - */ - - pid_t pid; - int sds[2]; - int rc, status; - char buf[3]; - - debug("entering test_multiproc_read()"); - - rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sds); - if (rc == -1) { - test_fail("socketpair"); - return 1; - } - - memset(buf, '\0', 3); - - - /* the signal handler is only used by the client, but we have to - * install it now. if we don't the server may signal the client - * before the handler is installed. - */ - debug("installing signal handler"); - if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) { - test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed"); - return 1; - } - - debug("signal handler installed"); - - server_ready = 0; - - pid = fork(); - - if (pid == -1) { - - test_fail("fork"); - return 1; - - } else if (pid == 0) { - - while (server_ready == 0) { - debug("waiting for SIGUSR1 from parent"); - sleep(1); - } - - rc = read(sds[1], buf, 2); - if (rc == -1) { - test_fail("read"); - exit(1); - } - - if (!(buf[0] == 'X' && buf[1] == '3')) { - test_fail("Didn't read X3"); - exit(1); - } - - exit(0); - } else { - - rc = write(sds[0], "MNX3", 4); - if (rc == -1) { - test_fail("write"); - } - - rc = read(sds[1], buf, 2); - if (rc == -1) { - test_fail("read"); - } - - if (!(buf[0] == 'M' && buf[1] == 'N')) { - test_fail("Didn't read MN"); - } - - /* time to tell the client to start the test */ - kill(pid, SIGUSR1); - - do { - rc = waitpid(pid, &status, 0); - } while (rc == -1 && errno == EINTR); - - /* we use the exit status to get its error count */ - errct += WEXITSTATUS(status); - } - - return 0; -} - -static int test_multiproc_write(void) -{ -/* test that when we fork() a process with an open socket descriptor, - * the descriptor in each process points to the same thing. - */ - - pid_t pid; - int sds[2]; - int rc, status; - char buf[7]; - - debug("entering test_multiproc_write()"); - - rc = socketpair(PF_UNIX, SOCK_STREAM, 0, sds); - if (rc == -1) { - test_fail("socketpair"); - return 1; - } - - memset(buf, '\0', 7); - - - /* the signal handler is only used by the client, but we have to - * install it now. if we don't the server may signal the client - * before the handler is installed. - */ - debug("installing signal handler"); - if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) { - test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed"); - return 1; - } - - debug("signal handler installed"); - - server_ready = 0; - - pid = fork(); - - if (pid == -1) { - - test_fail("fork"); - return 1; - - } else if (pid == 0) { - - while (server_ready == 0) { - debug("waiting for SIGUSR1 from parent"); - sleep(1); - } - - rc = write(sds[1], "IX3", 3); - if (rc == -1) { - test_fail("write"); - exit(1); - } - - rc = read(sds[0], buf, 6); - if (rc == -1) { - test_fail("read"); - exit(1); - } - - if (strcmp(buf, "MINIX3") != 0) { - test_fail("didn't read MINIX3"); - exit(1); - } - - exit(0); - } else { - - rc = write(sds[1], "MIN", 3); - if (rc == -1) { - test_fail("write"); - } - - /* time to tell the client to start the test */ - kill(pid, SIGUSR1); - - do { - rc = waitpid(pid, &status, 0); - } while (rc == -1 && errno == EINTR); - - /* we use the exit status to get its error count */ - errct += WEXITSTATUS(status); - } - - return 0; -} - -static void test_fd_passing_child(int sd) -{ - int fd, rc; - char x = 'x'; - struct msghdr msghdr; - struct cmsghdr *cmsg; - struct iovec iov; - char buf[BUFSIZE]; - - memset(buf, '\0', BUFSIZE); - - fd = open(TEST_TXT_FILE, O_CREAT|O_TRUNC|O_RDWR); - if (fd == -1) { - test_fail("could not open test.txt"); - } - - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - - iov.iov_base = &x; - iov.iov_len = 1; - msghdr.msg_iov = &iov; - msghdr.msg_iovlen = 1; - - msghdr.msg_control = buf; - msghdr.msg_controllen = CMSG_SPACE(sizeof(int)); - - msghdr.msg_flags = 0; - - cmsg = CMSG_FIRSTHDR(&msghdr); - cmsg->cmsg_len = CMSG_SPACE(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - - ((int *) CMSG_DATA(cmsg))[0] = fd; - - rc = sendmsg(sd, &msghdr, 0); - if (rc == -1) { - test_fail("could not send message"); - } - - memset(buf, '\0', BUFSIZE); - rc = read(sd, buf, BUFSIZE); - if (rc == -1) { - test_fail("could not read from socket"); - } - - if (strcmp(buf, "done") != 0) { - test_fail("we didn't read the right message"); - } - - memset(buf, '\0', BUFSIZE); - rc = lseek(fd, 0, SEEK_SET); - if (rc == -1) { - test_fail("could not seek to start of test.txt"); - } - - rc = read(fd, buf, BUFSIZE); - if (rc == -1) { - test_fail("could not read from test.txt"); - } - - if (strcmp(buf, MSG) != 0) { - test_fail("other process didn't write MSG to test.txt"); - } - - rc = close(fd); - if (rc == -1) { - test_fail("could not close test.txt"); - } - - rc = close(sd); - if (rc == -1) { - test_fail("could not close socket"); - } - - rc = unlink(TEST_TXT_FILE); - if (rc == -1) { - test_fail("could not unlink test.txt"); - } - - exit(errct); -} - -static void test_fd_passing_parent(int sd) -{ - int rc, fd; - char x; - struct msghdr msghdr; - struct cmsghdr *cmsg; - struct iovec iov; - char buf[BUFSIZE]; - - memset(buf, '\0', BUFSIZE); - - msghdr.msg_name = NULL; - msghdr.msg_namelen = 0; - - iov.iov_base = &x; - iov.iov_len = 1; - msghdr.msg_iov = &iov; - msghdr.msg_iovlen = 1; - - msghdr.msg_iov = &iov; - msghdr.msg_iovlen = 1; - - msghdr.msg_control = buf; - msghdr.msg_controllen = BUFSIZE; - - msghdr.msg_flags = 0; - - rc = recvmsg(sd, &msghdr, 0); - if (rc == -1) { - test_fail("could not recv message."); - } - - cmsg = CMSG_FIRSTHDR(&msghdr); - fd = ((int *) CMSG_DATA(cmsg))[0]; - - rc = write(fd, MSG, strlen(MSG)); - if (rc != strlen(MSG)) { - test_fail("could not write the full message to test.txt"); - } - - rc = close(fd); - if (rc == -1) { - test_fail("could not close test.txt"); - } - - memset(buf, '\0', BUFSIZE); - strcpy(buf, "done"); - rc = write(sd, buf, BUFSIZE); - if (rc == -1) { - test_fail("could not write to socket"); - } - - rc = close(sd); - if (rc == -1) { - test_fail("could not close socket"); - } -} - -static void test_permissions(void) { - /* Test bind and connect for permission verification - * - * After creating a UDS socket we change user credentials. At that - * point we should not be allowed to bind or connect to the UDS socket - */ - - pid_t pid; - int sd, rc, status; - struct sockaddr_un addr; - - memset(&addr, '\0', sizeof(struct sockaddr_un)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1); - - UNLINK(TEST_SUN_PATH); - - pid = fork(); - if (pid < 0) test_fail("unable to fork"); - else if (pid == 0) { - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - if (setuid(999) != 0) test_fail("unable to chance uid"); - rc = bind(sd, (struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); - if (rc != -1) { - test_fail("bind() should not have worked"); - } - exit(errct); - } else { - rc = waitpid(pid, &status, 0); - errct += WEXITSTATUS(status); - } - - /* the signal handler is only used by the client, but we have to - * install it now. if we don't the server may signal the client - * before the handler is installed. - */ - debug("installing signal handler"); - if (signal(SIGUSR1, test_xfer_sighdlr) == SIG_ERR) { - test_fail("signal(SIGUSR1, test_xfer_sighdlr) failed"); - } - - debug("signal handler installed"); - - server_ready = 0; - - pid = fork(); - if (pid < 0) test_fail("unable to fork"); - else if (pid == 0) { - while (server_ready == 0) { - debug("[client] waiting for the server to signal"); - sleep(1); - } - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - if (setuid(999) != 0) test_fail("unable to chance uid"); - rc = connect(sd, (struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); - if (rc != -1) - test_fail("connect should not have worked"); - exit(errct); - } else { - SOCKET(sd, PF_UNIX, SOCK_STREAM, 0); - rc = bind(sd, (struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); - if (rc == -1) { - test_fail("bind() should have worked"); - } - - rc = listen(sd, 8); - if (rc == -1) { - test_fail("listen(sd, 8) should have worked"); - } - kill(pid, SIGUSR1); - sleep(1); - CLOSE(sd); - - rc = waitpid(pid, &status, 0); - errct += WEXITSTATUS(status); - } - - UNLINK(TEST_SUN_PATH); -} - -static void test_fd_passing(void) { - int status; - int sv[2]; - pid_t pid; - int rc; - - rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); - if (rc == -1) { - test_fail("socketpair failed"); - } - - pid = fork(); - if (pid == -1) { - test_fail("fork() failed"); - - rc = close(sv[0]); - if (rc == -1) { - test_fail("could not close sv[0]"); - } - - rc = close(sv[1]); - if (rc == -1) { - test_fail("could not close sv[1]"); - } - - exit(0); - } else if (pid == 0) { - rc = close(sv[0]); - if (rc == -1) { - test_fail("could not close sv[0]"); - } - - test_fd_passing_child(sv[1]); - test_fail("should never get here"); - exit(1); - } else { - rc = close(sv[1]); - if (rc == -1) { - test_fail("could not close sv[1]"); - } - - test_fd_passing_parent(sv[0]); - - /* wait for client to exit */ - do { - errno = 0; - rc = waitpid(pid, &status, 0); - } while (rc == -1 && errno == EINTR); - - /* we use the exit status to get its error count */ - errct += WEXITSTATUS(status); - } -} - -static void test_select(void) -{ - int nfds = -1; - int socks[2]; - fd_set readfds, writefds; - struct timeval tv; - int res = 0; - char buf[1]; - - FD_ZERO(&readfds); - FD_ZERO(&writefds); - - tv.tv_sec = 2; - tv.tv_usec = 0; /* 2 sec time out */ - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) { - test_fail("Can't open socket pair."); - } - FD_SET(socks[0], &readfds); - nfds = socks[0] + 1; - - /* Close the write end of the socket to generate EOF on read end */ - if ((res = shutdown(socks[1], SHUT_WR)) != 0) { - test_fail("shutdown failed\n"); - } - - res = select(nfds, &readfds, NULL, NULL, &tv); - if (res != 1) { - test_fail("select should've returned 1 ready fd\n"); - } - if (!(FD_ISSET(socks[0], &readfds))) { - test_fail("The server didn't respond within 2 seconds"); - } - /* Now try to read from empty, closed pipe */ - if (read(socks[0], buf, sizeof(buf)) != 0) { - test_fail("reading from empty, closed pipe should return EOF"); - } - - close(socks[0]); - - /* Try again the other way around: create a socketpair, close the - * read end, and try to write. This should cause an EPIPE */ - - tv.tv_sec = 2; - tv.tv_usec = 0; /* 2 sec time out */ - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) { - test_fail("Can't open socket pair."); - } - FD_SET(socks[1], &writefds); - nfds = socks[1] + 1; - - /* kill the read end of the socket to generate EPIPE on write end */ - if ((res = shutdown(socks[0], SHUT_RD)) != 0) { - test_fail("shutdown failed\n"); - } - - res = select(nfds, NULL, &writefds, NULL, &tv); - if (res != 1) { - test_fail("select should've returned 1 ready fd\n"); - } - if (!(FD_ISSET(socks[1], &writefds))) { - test_fail("The server didn't respond within 2 seconds"); - } - - /* Now try to write to closed pipe */ - errno = 0; - if ((res = write(socks[1], buf, sizeof(buf))) != -1) { - printf("write res = %d\n", res); - test_fail("writing to empty, closed pipe should fail"); - } - if (errno != EPIPE) { - printf("errno = %d\n", errno); - test_fail("writing to closed pipe should return EPIPE\n"); - } - - close(socks[1]); -} - -static void test_select_close(void) -{ - int res, socks[2]; - fd_set readfds; - struct timeval tv; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) { - test_fail("Can't open socket pair."); - } - - switch (fork()) { - case 0: - sleep(1); - - exit(0); - case -1: - test_fail("Can't fork."); - default: - break; - } - - close(socks[1]); - - FD_ZERO(&readfds); - FD_SET(socks[0], &readfds); - tv.tv_sec = 2; - tv.tv_usec = 0; /* 2 sec time out */ - - res = select(socks[0] + 1, &readfds, NULL, NULL, &tv); - if (res != 1) { - test_fail("select should've returned 1 ready fd\n"); - } - if (!(FD_ISSET(socks[0], &readfds))) { - test_fail("The server didn't respond within 2 seconds"); - } - - wait(NULL); - - close(socks[0]); -} - -static void test_fchmod(void) -{ - int socks[2]; - struct stat st1, st2; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0) { - test_fail("Can't open socket pair."); - } - - if (fstat(socks[0], &st1) < 0 || fstat(socks[1], &st2) < 0) { - test_fail("fstat failed."); - } - - if ((st1.st_mode & (S_IRUSR|S_IWUSR)) == S_IRUSR && - (st2.st_mode & (S_IRUSR|S_IWUSR)) == S_IWUSR) { - test_fail("fstat failed."); - } - - if (fchmod(socks[0], S_IRUSR) < 0 || - fstat(socks[0], &st1) < 0 || - (st1.st_mode & (S_IRUSR|S_IWUSR)) != S_IRUSR) { - test_fail("fchmod/fstat mode set/check failed (1)."); - } - - if (fchmod(socks[1], S_IWUSR) < 0 || fstat(socks[1], &st2) < 0 || - (st2.st_mode & (S_IRUSR|S_IWUSR)) != S_IWUSR) { - test_fail("fchmod/fstat mode set/check failed (2)."); - } - - close(socks[0]); - close(socks[1]); -} - -/* - * Test various aspects related to the socket files on the file system. - * This subtest is woefully incomplete and currently only attempts to test - * aspects that have recently been affected by code changes. In the future, - * there should be tests for the entire range of file system path and access - * related error codes (TODO). - */ -static void -test_file(void) -{ - struct sockaddr_un addr, saddr, saddr2; - char buf[1]; - socklen_t len; - struct stat st; - mode_t omask; - int sd, sd2, csd, fd; - - /* - * If the provided socket path exists on the file system, the bind(2) - * call must fail, regardless of whether there is a socket that is - * bound to that address. - */ - UNLINK(TEST_SUN_PATH); - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strlcpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path)); - - if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) - test_fail("Can't bind socket"); - - if ((sd2 = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != -1) - test_fail("Binding socket unexpectedly succeeded"); - if (errno != EADDRINUSE) - test_fail("Binding socket failed with wrong error"); - - CLOSE(sd); - - if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != -1) - test_fail("Binding socket unexpectedly succeeded"); - if (errno != EADDRINUSE) - test_fail("Binding socket failed with wrong error"); - - CLOSE(sd2); - - UNLINK(TEST_SUN_PATH); - - /* - * If the socket is removed from the file system while there is still a - * socket bound to it, it should be possible to bind a new socket to - * the address. The old socket should then become unreachable in terms - * of connections and data directed to the address, even though it - * should still appear to be bound to the same address. - */ - if ((sd = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) - test_fail("Can't bind socket"); - memset(&saddr, 0, sizeof(saddr)); - len = sizeof(saddr); - if (getsockname(sd, (struct sockaddr *)&saddr, &len) != 0) - test_fail("Can't get socket address"); - - if ((csd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) - test_fail("Can't open client socket"); - - memset(buf, 'X', sizeof(buf)); - if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, - sizeof(addr)) != sizeof(buf)) - test_fail("Can't send to socket"); - if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf)) - test_fail("Can't receive from socket"); - if (buf[0] != 'X') - test_fail("Transmission failure"); - - if (unlink(TEST_SUN_PATH) != 0) - test_fail("Can't unlink socket"); - - if ((sd2 = socket(PF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd2, (struct sockaddr *)&addr, sizeof(addr)) != 0) - test_fail("Can't bind socket"); - memset(&saddr2, 0, sizeof(saddr2)); - len = sizeof(saddr2); - if (getsockname(sd2, (struct sockaddr *)&saddr2, &len) != 0) - test_fail("Can't get socket address"); - if (memcmp(&saddr, &saddr2, sizeof(saddr))) - test_fail("Unexpected socket address"); - - memset(buf, 'Y', sizeof(buf)); - if (sendto(csd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, - sizeof(addr)) != sizeof(buf)) - test_fail("Can't send to socket"); - if (recvfrom(sd, buf, sizeof(buf), 0, NULL, 0) != -1) - test_fail("Unexpectedly received from old socket"); - if (errno != EWOULDBLOCK) - test_fail("Wrong receive failure from old socket"); - if (recvfrom(sd2, buf, sizeof(buf), 0, NULL, 0) != sizeof(buf)) - test_fail("Can't receive from new socket"); - if (buf[0] != 'Y') - test_fail("Transmission failure"); - - len = sizeof(saddr2); - if (getsockname(sd, (struct sockaddr *)&saddr2, &len) != 0) - test_fail("Can't get old socket address"); - if (memcmp(&saddr, &saddr2, sizeof(saddr))) - test_fail("Unexpected old socket address"); - - if (unlink(TEST_SUN_PATH) != 0) - test_fail("Can't unlink socket"); - - CLOSE(sd); - CLOSE(sd2); - CLOSE(csd); - - /* - * If the socket path identifies a file that is not a socket, bind(2) - * should still fail with EADDRINUSE, but connect(2) and sendto(2) - * should fail with ENOTSOCK (the latter is not specified by POSIX, so - * we follow NetBSD here). - */ - if ((fd = open(TEST_SUN_PATH, O_WRONLY | O_CREAT | O_EXCL, 0700)) < 0) - test_fail("Can't create regular file"); - CLOSE(fd); - - if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != -1) - test_fail("Binding socket unexpectedly succeeded"); - if (errno != EADDRINUSE) - test_fail("Binding socket failed with wrong error"); - if (sendto(sd, buf, sizeof(buf), 0, (struct sockaddr *)&addr, - sizeof(addr)) != -1) - test_fail("Sending to socket unexpectedly succeeded"); - if (errno != ENOTSOCK) - test_fail("Sending to socket failed with wrong error"); - CLOSE(sd); - - if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) - test_fail("Can't open socket"); - if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != -1) - test_fail("Connecting socket unexpectedly succeeded"); - if (errno != ENOTSOCK) - test_fail("Connecting socket failed with wrong error"); - CLOSE(sd); - - UNLINK(TEST_SUN_PATH); - - /* - * The created socket file should have an access mode of 0777 corrected - * with the calling process's file mode creation mask. - */ - omask = umask(045123); - - if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) - test_fail("Can't bind socket"); - - if (stat(TEST_SUN_PATH, &st) != 0) - test_fail("Can't stat socket"); - if (!S_ISSOCK(st.st_mode)) - test_fail("File is not a socket"); - if ((st.st_mode & ALLPERMS) != (ACCESSPERMS & ~0123)) - test_fail("Unexpected file permission mask"); - - CLOSE(sd); - UNLINK(TEST_SUN_PATH); - - umask(omask); - - /* - * Only socket(2), socketpair(2), and accept(2) may be used to obtain - * new file descriptors to sockets (or "sockets"); open(2) on a socket - * file is expected to fail with EOPNOTSUPP (Austin Group Issue #943), - * regardless of whether the socket is in use. - */ - if ((sd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) - test_fail("Can't open socket"); - if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) - test_fail("Can't bind socket"); - - if (open(TEST_SUN_PATH, O_RDWR) != -1) - test_fail("Unexpectedly opened socket file"); - if (errno != EOPNOTSUPP) - test_fail("Open failed with wrong error"); - - CLOSE(sd); - - if (open(TEST_SUN_PATH, O_RDONLY) != -1) - test_fail("Unexpectedly opened socket file"); - if (errno != EOPNOTSUPP) - test_fail("Open failed with wrong error"); - - UNLINK(TEST_SUN_PATH); -} - -int main(int argc, char *argv[]) -{ - int i; - struct sockaddr_un clientaddr = { - .sun_family = AF_UNIX, - .sun_path = TEST_SUN_PATH, - }; - struct sockaddr_un clientaddr2 = { - .sun_family = AF_UNIX, - .sun_path = TEST_SUN_PATHB, - }; - struct sockaddr_un clientaddrsym = { - .sun_family = AF_UNIX, - .sun_path = TEST_SYM_A, - }; - const struct socket_test_info info = { - .clientaddr = (struct sockaddr *) &clientaddr, - .clientaddrlen = sizeof(clientaddr), - .clientaddr2 = (struct sockaddr *) &clientaddr2, - .clientaddr2len = sizeof(clientaddr2), - .clientaddrsym = (struct sockaddr *) &clientaddrsym, - .clientaddrsymlen = sizeof(clientaddrsym), - .domain = PF_UNIX, - .expected_rcvbuf = 32768 - 5, /* no constants: */ - .expected_sndbuf = 32768 - 5, /* UDS internals */ - .serveraddr = (struct sockaddr *) &clientaddr, - .serveraddrlen = sizeof(clientaddr), - .serveraddr2 = (struct sockaddr *) &clientaddr2, - .serveraddr2len = sizeof(clientaddr2), - .type = SOCK_STREAM, - .types = types, - .typecount = 3, - .callback_check_sockaddr = callback_check_sockaddr, - .callback_cleanup = callback_cleanup, - .callback_xfer_prepclient = callback_xfer_prepclient, - .callback_xfer_peercred = callback_xfer_peercred, - .callback_set_listen_opt = callback_set_listen_opt, - }; - - debug("entering main()"); - - start(56); - - /* This test was written before UDS started supporting SIGPIPE. */ - signal(SIGPIPE, SIG_IGN); - - test_socket(&info); - test_bind(&info); - test_bind_unix(); - test_listen(&info); - test_getsockname(&info); - test_header(); - test_shutdown(&info); - test_close(&info); - test_permissions(); - test_dup(&info); - test_dup2(&info); - test_socketpair(); - test_shutdown(&info); - test_read(&info); - test_write(&info); - test_sockopts(&info); - test_ucred(); - test_xfer(&info); - - for (i = 0; i < 3; i++) { - test_simple_client_server(&info, types[i]); - if (types[i] != SOCK_DGRAM) test_vectorio(types[i]); - if (types[i] != SOCK_DGRAM) test_msg(types[i]); - } - - test_abort_client_server(&info, 1); - test_abort_client_server(&info, 2); - test_msg_dgram(&info); - test_connect(&info); - test_multiproc_read(); - test_multiproc_write(); - test_scm_credentials(); - test_fd_passing(); - test_select(); - test_select_close(); - test_fchmod(); - test_nonblock(&info); - test_connect_nb(&info); - test_intr(&info); - test_connect_close(&info); - test_listen_close(&info); - test_listen_close_nb(&info); - test_file(); - - quit(); - - return -1; /* we should never get here */ -} diff --git a/minix/tests/test68.c b/minix/tests/test68.c deleted file mode 100644 index d9e3bab65..000000000 --- a/minix/tests/test68.c +++ /dev/null @@ -1,373 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int max_error = 5; -#include "common.h" - - -void copy_subtests(void); -void test_pipe_cloexec(void); -void test_pipe_flag_setting(void); -void test_pipe_nonblock(void); -void test_pipe_normal(void); -void test_pipe_nosigpipe(void); -void alarm_handler(int sig); -void pipe_handler(int sig); - -static int seen_pipe_signal = 0; -static int seen_alarm_signal = 0; - -void -alarm_handler(int sig) -{ - if (seen_pipe_signal == 0) - seen_pipe_signal = -1; - seen_alarm_signal = 1; -} - -void -pipe_handler(int sig) -{ - seen_pipe_signal = 1; -} - -void -copy_subtests() -{ - char *subtests[] = { "t68a", "t68b" }; - char copy_cmd[8 + PATH_MAX + 1]; - int i, no_tests; - - no_tests = sizeof(subtests) / sizeof(char *); - - for (i = 0; i < no_tests; i++) { - snprintf(copy_cmd, 8 + PATH_MAX, "cp ../%s .", subtests[i]); - system(copy_cmd); - } -} - -void -test_pipe_normal() -{ -/* Verify pipe2 creates pipes that behave like a normal pipe */ - - int pipes[2]; - char buf_in[1], buf_out[1]; - pid_t pid; - - subtest = 2; - - if (pipe2(pipes, 0) != 0) e(1); - - buf_out[0] = 'T'; - if (write(pipes[1], buf_out, sizeof(buf_out)) != sizeof(buf_out)) e(2); - if (read(pipes[0], buf_in, sizeof(buf_in)) != sizeof(buf_in)) e(3); - if (buf_out[0] != buf_in[0]) e(4); - - /* When we close the write end, reading should fail */ - if (close(pipes[1]) != 0) e(5); - if (read(pipes[0], buf_in, sizeof(buf_in)) != 0) e(6); - - /* Let's retry that experiment the other way around. Install a signal - * handler to catch SIGPIPE. Install an alarm handler to make sure - * this test finishes in finite time. */ - if (pipe2(pipes, 0) != 0) e(7); - signal(SIGPIPE, pipe_handler); - signal(SIGALRM, alarm_handler); - seen_pipe_signal = 0; - seen_alarm_signal = 0; - alarm(1); - if (close(pipes[0]) != 0) e(8); - if (write(pipes[1], buf_out, sizeof(buf_out)) != -1) e(9); - while (seen_pipe_signal == 0) - ; - if (seen_pipe_signal != 1) e(10); - if (close(pipes[1]) != 0) e(11); - - /* Collect alarm signal */ - while (seen_alarm_signal == 0) - ; - - if (pipe2(pipes, 0) != 0) e(12); - - /* Now fork and verify we can write to the pipe */ - pid = fork(); - if (pid < 0) e(13); - if (pid == 0) { - /* We're the child */ - char fd_buf[2]; - - /* Verify we can still write a byte into the pipe */ - if (write(pipes[1], buf_out, sizeof(buf_out)) != 1) e(14); - - snprintf(fd_buf, sizeof(fd_buf), "%d", pipes[1]); - execl("./t68a", "t68a", fd_buf, NULL); - - exit(1); /* Should not be reached */ - } else { - /* We're the parent */ - int result; - - if (waitpid(pid, &result, 0) == -1) e(15); - if (WEXITSTATUS(result) != 0) e(16); - } - - if (close(pipes[0]) != 0) e(17); - if (close(pipes[1]) != 0) e(18); -} - -void -test_pipe_cloexec() -{ -/* Open a pipe with O_CLOEXEC */ - int flags; - int pipes[2]; - pid_t pid; - char buf_in[1], buf_out[1]; - - subtest = 3; - - if (pipe2(pipes, O_CLOEXEC) != 0) e(1); - - /* Verify O_CLOEXEC flag is set */ - flags = fcntl(pipes[0], F_GETFD); - if (flags < 0) e(2); - if (!(flags & FD_CLOEXEC)) e(3); - - pid = fork(); - if (pid < 0) e(4); - if (pid == 0) { - /* We're the child */ - char fd_buf[2]; - - /* Verify we can still write a byte into the pipe */ - buf_in[0] = 0; - buf_out[0] = 'T'; - if (write(pipes[1], buf_out, sizeof(buf_out)) != 1) e(5); - if (read(pipes[0], buf_in, sizeof(buf_in)) != 1) e(6); - if (buf_out[0] != buf_in[0]) e(7); - - /* Verify FD_CLOEXEC flag is still set */ - flags = fcntl(pipes[0], F_GETFD); - if (flags < 0) e(8); - if (!(flags & FD_CLOEXEC)) e(9); - - snprintf(fd_buf, sizeof(fd_buf), "%d", pipes[0]); - execl("./t68b", "t68b", fd_buf, NULL); - - exit(1); /* Should not be reached */ - } else { - /* We're the parent */ - int result; - - if (waitpid(pid, &result, 0) == -1) e(10); - if (WEXITSTATUS(result) != 0) e(11); - } - - /* Eventhough our child's pipe should've been closed upon exec, our - * pipe should still be functioning. - */ - buf_in[0] = 0; - buf_out[0] = 't'; - if (write(pipes[1], buf_out, sizeof(buf_out)) != sizeof(buf_out)) e(12); - if (read(pipes[0], buf_in, sizeof(buf_in)) != sizeof(buf_in)) e(13); - if (buf_out[0] != buf_in[0]) e(14); - - if (close(pipes[0]) != 0) e(15); - if (close(pipes[1]) != 0) e(16); -} - -void -test_pipe_nonblock() -{ -/* Open a pipe with O_NONBLOCK */ - char *buf_in, *buf_out; - int pipes[2]; - size_t pipe_size; - - subtest = 4; - - if (pipe2(pipes, O_NONBLOCK) != 0) e(1); - if ((pipe_size = fpathconf(pipes[0], _PC_PIPE_BUF)) == -1) e(2); - buf_in = calloc(2, pipe_size); /* Allocate twice the buffer size */ - if (buf_in == NULL) e(3); - buf_out = calloc(2, pipe_size); /* Idem dito for output buffer */ - if (buf_out == NULL) e(4); - - /* According to POSIX, a pipe with O_NONBLOCK set shall never block. - * When we attempt to write PIPE_BUF or less bytes, and there is - * sufficient space available, write returns nbytes. Else write will - * return -1 and not transfer any data. - */ - if (write(pipes[1], buf_out, 1) != 1) e(5); /* Write 1 byte */ - if (write(pipes[1], buf_out, pipe_size) != -1) e(6); /* Can't fit */ - if (errno != EAGAIN) e(7); - - /* When writing more than PIPE_BUF bytes and when at least 1 byte can - * be tranferred, return the number of bytes written. We've written 1 - * byte, so there are PIPE_BUF - 1 bytes left. */ - if (write(pipes[1], buf_out, pipe_size + 1) != pipe_size - 1) e(8); - - /* Read out all data and try again. This time we should be able to - * write PIPE_BUF bytes. */ - if (read(pipes[0], buf_in, pipe_size) != pipe_size) e(9); - if (read(pipes[0], buf_in, 1) != -1) e(10); /* Empty, can't read */ - if (errno != EAGAIN) e(11); - if (write(pipes[1], buf_out, pipe_size + 1) != pipe_size) e(12); - if (close(pipes[0]) != 0) e(13); - if (close(pipes[1]) != 0) e(14); - free(buf_in); - free(buf_out); -} - -void -test_pipe_nosigpipe(void) -{ -/* Let's retry the writing to pipe without readers experiment. This time we set - * the O_NOSIGPIPE flag to prevent getting a signal. */ - int pipes[2]; - char buf_out[1]; - - subtest = 5; - - if (pipe2(pipes, O_NOSIGPIPE) != 0) e(7); - signal(SIGPIPE, pipe_handler); - signal(SIGALRM, alarm_handler); - seen_pipe_signal = 0; - seen_alarm_signal = 0; - alarm(1); - if (close(pipes[0]) != 0) e(8); - if (write(pipes[1], buf_out, sizeof(buf_out)) != -1) e(9); - - /* Collect alarm signal */ - while (seen_alarm_signal == 0) - ; - if (errno != EPIPE) e(10); - if (seen_pipe_signal != -1) e(11); /* Alarm sig handler set it to -1 */ - if (close(pipes[1]) != 0) e(12); -} - -void -test_pipe_flag_setting() -{ - int pipes[2]; - - subtest = 1; - - /* Create standard pipe with no flags and verify they're off */ - if (pipe2(pipes, 0) != 0) e(1); - if (fcntl(pipes[0], F_GETFD) != 0) e(2); - if (fcntl(pipes[1], F_GETFD) != 0) e(3); - if (fcntl(pipes[0], F_GETFL) & O_NONBLOCK) e(4); - if (fcntl(pipes[1], F_GETFL) & O_NONBLOCK) e(5); - if (fcntl(pipes[0], F_GETNOSIGPIPE) != 0) e(6); - if (fcntl(pipes[1], F_GETNOSIGPIPE) != 0) e(7); - if (close(pipes[0]) != 0) e(8); - if (close(pipes[1]) != 0) e(9); - - /* Create pipe with all flags and verify they're on */ - if (pipe2(pipes, O_CLOEXEC|O_NONBLOCK|O_NOSIGPIPE) != 0) e(10); - if (fcntl(pipes[0], F_GETFD) != FD_CLOEXEC) e(11); - if (fcntl(pipes[1], F_GETFD) != FD_CLOEXEC) e(12); - if (!(fcntl(pipes[0], F_GETFL) & O_NONBLOCK)) e(13); - if (!(fcntl(pipes[1], F_GETFL) & O_NONBLOCK)) e(14); - if (fcntl(pipes[0], F_GETNOSIGPIPE) == 0) e(15); - if (fcntl(pipes[1], F_GETNOSIGPIPE) == 0) e(16); - if (fcntl(pipes[0], F_SETNOSIGPIPE, 0) != 0) e(17); - if (fcntl(pipes[0], F_GETNOSIGPIPE) != 0) e(18); - if (fcntl(pipes[0], F_SETNOSIGPIPE, 1) != 0) e(19); - if (fcntl(pipes[0], F_GETNOSIGPIPE) == 0) e(20); - if (close(pipes[0]) != 0) e(21); - if (close(pipes[1]) != 0) e(22); -} - -/* - * Test the behavior of a large pipe write that achieves partial progress - * before the reader end is closed. The write call is expected to return EPIPE - * and generate a SIGPIPE signal, and otherwise leave the system in good order. - */ -static void -test_pipe_partial_write(void) -{ - char buf[PIPE_BUF + 2]; - int pfd[2], status; - - signal(SIGPIPE, pipe_handler); - - if (pipe(pfd) < 0) e(1); - - switch (fork()) { - case 0: - close(pfd[1]); - - sleep(1); /* let the parent block on the write(2) */ - - /* - * This one-byte read raises the question whether the write - * should return partial progress or not, since consumption of - * part of its data is now clearly visible. NetBSD chooses - * *not* to return partial progress, and MINIX3 follows suit. - */ - if (read(pfd[0], buf, 1) != 1) e(2); - - sleep(1); /* let VFS retry satisfying the write(2) */ - - exit(errct); /* close the reader side of the pipe */ - - case -1: - e(3); - - default: - break; - } - - close(pfd[0]); - - seen_pipe_signal = 0; - - /* - * The following large write should block until the child exits, as - * that is when the read end closes, thus making eventual completion of - * the write impossible. - */ - if (write(pfd[1], buf, sizeof(buf)) != -1) e(4); - if (errno != EPIPE) e(5); - if (seen_pipe_signal != 1) e(6); - - seen_pipe_signal = 0; - - /* A subsequent write used to cause a system crash. */ - if (write(pfd[1], buf, 1) != -1) e(7); - if (errno != EPIPE) e(8); - if (seen_pipe_signal != 1) e(9); - - /* Clean up. */ - close(pfd[1]); - - if (wait(&status) <= 0) e(10); - if (!WIFEXITED(status)) e(11); - errct += WEXITSTATUS(status); -} - -int -main(int argc, char *argv[]) -{ - start(68); - copy_subtests(); - test_pipe_flag_setting(); - test_pipe_normal(); - test_pipe_cloexec(); - test_pipe_nonblock(); - test_pipe_nosigpipe(); - test_pipe_partial_write(); - quit(); - return(-1); /* Unreachable */ -} - diff --git a/minix/tests/test77.c b/minix/tests/test77.c deleted file mode 100644 index 2ad246159..000000000 --- a/minix/tests/test77.c +++ /dev/null @@ -1,1148 +0,0 @@ -/* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */ -/* - * As of the introduction of Unix98 PTY support, this test set actually relies - * on the ability to create Unix98 PTYs. The system still supports old-style - * PTYs but there is no way to force openpty(3) to use them. However, part of - * this test set can still be used to test old-style PTYs: first disable Unix98 - * PTYs, for example by unmounting PTYFS or temporarily removing /dev/ptmx, and - * then run the a-f subtests from this test set as root. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ITERATIONS 10 - -#define MIN_PTYS 4 - -#include "common.h" - -static int sighups; /* number of SIGHUP signals received */ - -/* - * Signal handler for SIGHUP and SIGUSR1. - */ -static void -signal_handler(int sig) -{ - if (sig == SIGHUP) - sighups++; -} - -/* - * Set the slave side of the pseudo terminal to raw mode. This simplifies - * testing communication. - */ -static void -make_raw(int slavefd) -{ - struct termios tios; - - if (tcgetattr(slavefd, &tios) < 0) e(0); - - cfmakeraw(&tios); - - if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(0); -} - -/* - * See if the given pseudo terminal can successfully perform basic - * communication between master and slave. - */ -static void -test_comm(int masterfd, int slavefd) -{ - char c; - - make_raw(slavefd); - - c = 'A'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'A') e(0); - - c = 'B'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'B') e(0); - - c = 'C'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'C') e(0); - - c = 'D'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'D') e(0); -} - -/* - * Obtain a pseudo terminal. The master end is opened and its file descriptor - * stored in 'pfd'. The slave path name is stored in 'tname'. For old-style - * PTYs, the function returns 1 and stores the master name in 'pname' if not - * NULL. For Unix98 PTYs, the function returns 0, in which case no master name - * is available. For old-style PTYs, the caller may close and reopen the - * master. In that case, we make the assumption that nobody snatches the pair - * while we are running. For Unix98 PTYs, the master must be kept open. - */ -static int -get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX]) -{ - char *name; - int len, masterfd, slavefd; - - /* - * First try Unix98 PTY allocation, mainly to avoid opening the slave - * end immediately. If this fails, try openpty(3) as well. - */ - if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) != -1) { - if (grantpt(masterfd) != -1 && unlockpt(masterfd) != -1 && - (name = ptsname(masterfd)) != NULL) { - *pfd = masterfd; - strlcpy(tname, name, PATH_MAX); - - return 0; - } - if (close(masterfd) < 0) e(0); - } - - if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0); - - test_comm(masterfd, slavefd); - - *pfd = masterfd; - - if (close(slavefd) < 0) e(0); - - /* - * openpty(3) gives us only the slave name, but we also want the master - * name. - */ - len = strlen(_PATH_DEV); - if (strncmp(tname, _PATH_DEV, len)) e(0); - - if (strncmp(&tname[len], "tty", 3)) - return 0; /* Unix98 after all? Well okay, whatever.. */ - - if (pname != NULL) { - strlcpy(pname, tname, PATH_MAX); - pname[len] = 'p'; - } - - return 1; -} - -/* - * Test various orders of opening and closing the master and slave sides of a - * pseudo terminal, as well as opening/closing one side without ever opening - * the other. This test is meaningful mainly for old-style pseudoterminals. - */ -static void -test77a(void) -{ - struct sigaction act, oact; - char pname[PATH_MAX], tname[PATH_MAX]; - int oldstyle, masterfd, slavefd; - - subtest = 1; - - /* We do not want to get SIGHUP signals in this test. */ - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - - /* Obtain a pseudo terminal. */ - oldstyle = get_pty(&masterfd, pname, tname); - - if (oldstyle) { - /* Try closing the master. */ - if (close(masterfd) < 0) e(0); - - /* See if we can reopen the master. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - } - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - /* In the meantime, test different closing orders. This is order A. */ - if (close(slavefd) < 0) e(0); - if (close(masterfd) < 0) e(0); - - /* Now try opening the pair (or a new pair) again. */ - if (!oldstyle) - oldstyle = get_pty(&masterfd, pname, tname); - else - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - - /* - * Try reopening the slave after closing it. It is not very important - * that this works, but the TTY driver should currently support it. - */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - /* This is closing order B. This may or may not cause a SIGHUP. */ - if (close(masterfd) < 0) e(0); - if (close(slavefd) < 0) e(0); - - /* Try the normal open procedure. */ - if (!oldstyle) - oldstyle = get_pty(&masterfd, pname, tname); - else - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - if (close(masterfd) < 0) e(0); - - /* - * Try reopening and closing the slave, without opening the master. - * This should work on old-style PTYS, but not on Unix98 PTYs. - */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) { - if (!oldstyle) e(0); - - if (close(slavefd) < 0) e(0); - } else - if (oldstyle) e(0); - - /* Again, try the normal open procedure. */ - if (!oldstyle) - oldstyle = get_pty(&masterfd, pname, tname); - else - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - if (close(masterfd) < 0) e(0); - - /* - * Finally, try opening the slave first. This does not work with - * Unix98 PTYs. - */ - if (oldstyle) { - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - if (close(masterfd) < 0) e(0); - } - - if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); -} - -/* - * Test opening a single side multiple times. - */ -static void -test77b(void) -{ - char pname[PATH_MAX], tname[PATH_MAX]; - int oldstyle, masterfd, slavefd, extrafd; - - subtest = 2; - - /* Obtain a pseudo terminal. */ - oldstyle = get_pty(&masterfd, pname, tname); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - /* - * It must not be possible to open the master multiple times. Doing so - * is possible only if we have a named master, i.e., an old-style PTY. - */ - test_comm(masterfd, slavefd); - - if (oldstyle) { - if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0); - if (errno != EIO) e(0); - } - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - if (close(masterfd) < 0) e(0); - - /* The slave can be opened multiple times, though. */ - oldstyle = get_pty(&masterfd, pname, tname); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, slavefd); - - if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - test_comm(masterfd, extrafd); - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - if (close(extrafd) < 0) e(0); - if (close(masterfd) < 0) e(0); -} - -/* - * Test communication on half-open pseudo terminals. - */ -static void -test77c(void) -{ - struct sigaction act, oact; - char pname[PATH_MAX], tname[PATH_MAX]; - int oldstyle, masterfd, slavefd; - char c; - - subtest = 3; - - /* We do not want to get SIGHUP signals in this test. */ - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - - /* Obtain a pseudo terminal. */ - oldstyle = get_pty(&masterfd, pname, tname); - - /* - * For old-style pseudo terminals, we have just opened and closed the - * slave end, which alters the behavior we are testing below. Close - * and reopen the master to start fresh. - */ - if (oldstyle) { - if (close(masterfd) < 0) e(0); - - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - } - - /* Writes to the master should be buffered until there is a slave. */ - c = 'E'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - make_raw(slavefd); - - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'E') e(0); - - /* Discard the echo on the master. */ - if (tcflush(slavefd, TCOFLUSH) != 0) e(0); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - - /* Writes to the master after the slave has been closed should fail. */ - if (write(masterfd, &c, sizeof(c)) >= 0) e(0); - if (errno != EIO) e(0); - - if (oldstyle) - if (close(masterfd) < 0) e(0); - - /* - * Writes to the slave should be buffered until there is a master. - * This applies to old-style PTYs only. - */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if (oldstyle) { - make_raw(slavefd); - - c = 'F'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'F') e(0); - } - - test_comm(masterfd, slavefd); - - if (close(masterfd) < 0) e(0); - - if (write(slavefd, &c, sizeof(c)) >= 0) e(0); - if (errno != EIO) e(0); - - /* Reads from the slave should return EOF if the master is gone. */ - if (read(slavefd, &c, sizeof(c)) != 0) e(0); - - if (close(slavefd) < 0) e(0); - - if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); -} - -/* - * Wait for a child process to terminate. Return 0 if the child exited without - * errors, -1 otherwise. - */ -static int -waitchild(void) -{ - int status; - - if (wait(&status) <= 0) return -1; - if (!WIFEXITED(status)) return -1; - if (WEXITSTATUS(status) != 0) return -1; - - return 0; -} - -/* - * Test opening the slave side with and without the O_NOCTTY flag. - */ -static void -test77d(void) -{ - char pname[PATH_MAX], tname[PATH_MAX]; - int masterfd, slavefd; - - subtest = 4; - - /* Make ourselves process group leader if we aren't already. */ - (void)setsid(); - - /* Obtain a pseudo terminal. */ - (void)get_pty(&masterfd, NULL, tname); - - /* - * Opening the slave with O_NOCTTY should not change its controlling - * terminal. - */ - switch (fork()) { - case 0: - if (close(masterfd) < 0) e(0); - - if (setsid() < 0) e(0); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if (open("/dev/tty", O_RDWR) >= 0) e(0); - if (errno != ENXIO) e(0); - - exit(errct); - case -1: - e(0); - default: - break; - } - - if (waitchild() < 0) e(0); - - if (close(masterfd) < 0) e(0); - - (void)get_pty(&masterfd, pname, tname); - - /* - * Opening the slave without O_NOCTTY should change its controlling - * terminal, though. - */ - switch (fork()) { - case 0: - if (close(masterfd) < 0) e(0); - - if (setsid() < 0) e(0); - - if ((slavefd = open(tname, O_RDWR)) < 0) e(0); - - if (open("/dev/tty", O_RDWR) < 0) e(0); - - exit(errct); - case -1: - e(0); - default: - break; - } - - if (waitchild() < 0) e(0); - - if (close(masterfd) < 0) e(0); -} - -/* - * Test receiving of SIGHUP on master hang-up. All of the tests so far have - * ignored SIGHUP, and probably would not have received one anyway, since the - * process was not its own session leader. Time to test this aspect. - */ -static void -test77e(void) -{ - struct sigaction act, hup_oact, usr_oact; - sigset_t set, oset; - char tname[PATH_MAX]; - int masterfd, slavefd; - - subtest = 5; - - memset(&act, 0, sizeof(act)); - act.sa_handler = signal_handler; - if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(0); - - memset(&act, 0, sizeof(act)); - act.sa_handler = signal_handler; - if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(0); - - sigemptyset(&set); - sigaddset(&set, SIGHUP); - sigaddset(&set, SIGUSR1); - if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(0); - - sighups = 0; - - /* Make ourselves process group leader if we aren't already. */ - (void)setsid(); - - /* Obtain a pseudo terminal. */ - (void)get_pty(&masterfd, NULL, tname); - - switch (fork()) { - case 0: - if (close(masterfd) < 0) e(0); - - /* Become session leader. */ - if (setsid() < 0) e(0); - - if ((slavefd = open(tname, O_RDWR)) < 0) e(0); - - /* Tell the parent we are ready. */ - kill(getppid(), SIGUSR1); - - /* We should now get a SIGHUP. */ - set = oset; - if (sigsuspend(&set) >= 0) e(0); - - if (sighups != 1) e(0); - - exit(errct); - case -1: - e(0); - default: - break; - } - - /* Wait for SIGUSR1 from the child. */ - set = oset; - if (sigsuspend(&set) >= 0) e(0); - - /* Closing the master should now raise a SIGHUP signal in the child. */ - if (close(masterfd) < 0) e(0); - - if (waitchild() < 0) e(0); - - if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(0); - - if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(0); - if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(0); -} - -/* - * Test basic select functionality on /dev/tty. While this test should not be - * part of this test set, we already have all the infrastructure we need here. - */ -static void -test77f(void) -{ - struct sigaction act, oact; - char c, tname[PATH_MAX]; - struct timeval tv; - fd_set fd_set; - int fd, maxfd, masterfd, slavefd; - - subtest = 6; - - /* We do not want to get SIGHUP signals in this test. */ - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - - /* Obtain a pseudo terminal. */ - (void)get_pty(&masterfd, NULL, tname); - - switch (fork()) { - case 0: - if (close(masterfd) < 0) e(0); - - if (setsid() < 0) e(0); - - if ((slavefd = open(tname, O_RDWR)) < 0) e(0); - - if ((fd = open("/dev/tty", O_RDWR)) < 0) e(0); - - make_raw(fd); - - /* Without slave input, /dev/tty is not ready for reading. */ - FD_ZERO(&fd_set); - FD_SET(fd, &fd_set); - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fd_set)) e(0); - - FD_SET(fd, &fd_set); - tv.tv_sec = 0; - tv.tv_usec = 10000; - - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fd_set)) e(0); - - /* It will be ready for writing, though. */ - FD_SET(fd, &fd_set); - - if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fd_set)) e(0); - - /* Test mixing file descriptors to the same terminal. */ - FD_ZERO(&fd_set); - FD_SET(fd, &fd_set); - FD_SET(slavefd, &fd_set); - tv.tv_sec = 0; - tv.tv_usec = 10000; - - maxfd = fd > slavefd ? fd : slavefd; - if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fd_set)) e(0); - if (FD_ISSET(slavefd, &fd_set)) e(0); - - /* The delayed echo on the master must wake up our select. */ - c = 'A'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - - FD_ZERO(&fd_set); - FD_SET(fd, &fd_set); - - if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fd_set)) e(0); - - /* Select must now still flag readiness for reading. */ - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(0); - if (!FD_ISSET(fd, &fd_set)) e(0); - - /* That is, until we read the byte. */ - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'B') e(0); - - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fd_set)) e(0); - - /* Ask the parent to close the master. */ - c = 'C'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - - FD_SET(fd, &fd_set); - - /* The closure must cause an EOF condition on the slave. */ - if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fd_set)) e(0); - - if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fd_set)) e(0); - - if (read(slavefd, &c, sizeof(c)) != 0) e(0); - - exit(errct); - case -1: - e(0); - default: - /* Wait for the child to write something to the slave. */ - FD_ZERO(&fd_set); - FD_SET(masterfd, &fd_set); - - if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1) - e(0); - if (!FD_ISSET(masterfd, &fd_set)) e(0); - - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'A') e(0); - - /* Write a reply once the child is blocked in its select. */ - tv.tv_sec = 1; - tv.tv_usec = 0; - if (select(masterfd + 1, &fd_set, NULL, NULL, &tv) != 0) - e(0); - - c = 'B'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - - /* Wait for the child to request closing the master. */ - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if (c != 'C') e(0); - - /* Close the master once the child is blocked in its select. */ - sleep(1); - - close(masterfd); - - break; - } - - if (waitchild() < 0) e(0); - - if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); -} - -/* - * See if the directory contents of /dev/pts are as we expect. We have to keep - * in mind that other programs may have pseudo terminals open while we are - * running, although we assume that those programs do not open or close PTYs - * while we are running. - */ -static void -test_getdents(int nindex, int array[3], int present[3]) -{ - struct group *group; - DIR *dirp; - struct dirent *dp; - struct stat buf; - char path[PATH_MAX], *endp; - gid_t tty_gid; - int i, n, seen_dot, seen_dotdot, seen_index[3], *seen; - - seen_dot = seen_dotdot = 0; - for (i = 0; i < nindex; i++) - seen_index[i] = 0; - - if ((group = getgrnam("tty")) == NULL) e(0); - tty_gid = group->gr_gid; - - if ((dirp = opendir(_PATH_DEV_PTS)) == NULL) e(0); - - while ((dp = readdir(dirp)) != NULL) { - snprintf(path, sizeof(path), _PATH_DEV_PTS "%s", dp->d_name); - if (stat(path, &buf) < 0) e(0); - - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { - seen = - (dp->d_name[1] == '.') ? &seen_dot : &seen_dotdot; - if (*seen) e(0); - *seen = 1; - - /* Check basic dirent and stat fields. */ - if (dp->d_type != DT_DIR) e(0); - if (dp->d_name[1] == '\0' && - buf.st_ino != dp->d_fileno) e(0); - if (!S_ISDIR(buf.st_mode)) e(0); - if (buf.st_nlink < 2) e(0); - } else { - /* The file name must be a number. */ - errno = 0; - n = strtol(dp->d_name, &endp, 10); - if (errno != 0) e(0); - if (dp->d_name[0] == '\0' || *endp != '\0') e(0); - if (n < 0) e(0); - - /* Check basic dirent and stat fields. */ - if (dp->d_type != DT_CHR) e(0); - if (buf.st_ino != dp->d_fileno) e(0); - if (!S_ISCHR(buf.st_mode)) e(0); - if (buf.st_nlink != 1) e(0); - if (buf.st_size != 0) e(0); - if (buf.st_rdev == 0) e(0); - - /* Is this one of the PTYs we created? */ - for (i = 0; i < nindex; i++) { - if (array[i] == n) { - if (seen_index[i]) e(0); - seen_index[i] = 1; - - break; - } - } - - /* If so, perform some extra tests. */ - if (i < nindex) { - if ((buf.st_mode & ALLPERMS) != 0620) e(0); - if (buf.st_uid != getuid()) e(0); - if (buf.st_gid != tty_gid) e(0); - } - } - } - - if (closedir(dirp) < 0) e(0); - - if (!seen_dot) e(0); - if (!seen_dotdot) e(0); - for (i = 0; i < nindex; i++) - if (seen_index[i] != present[i]) e(0); -} - -/* - * Obtain a Unix98 PTY. Return an open file descriptor for the master side, - * and store the name of the slave side in 'tptr'. - */ -static int -get_unix98_pty(char ** tptr) -{ - int masterfd; - - if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0) e(0); - - if (grantpt(masterfd) < 0) e(0); - - /* This call is a no-op on MINIX3. */ - if (unlockpt(masterfd) < 0) e(0); - - if ((*tptr = ptsname(masterfd)) == NULL) e(0); - - return masterfd; -} - -/* - * Test for Unix98 PTY support and PTYFS. - */ -static void -test77g(void) -{ - char *tname; - struct stat buf; - size_t len; - int i, masterfd, slavefd, fd[3], array[3], present[3]; - - subtest = 7; - - /* - * Test basic operation, and verify that the slave node disappears - * after both sides of the pseudo terminal have been closed. We check - * different combinations of open master and slave ends (with 'i'): - * 0) opening and closing the master only, 1) closing a slave before - * the master, and 2) closing the slave after the master. - */ - for (i = 0; i <= 2; i++) { - masterfd = get_unix98_pty(&tname); - - if (access(tname, R_OK | W_OK) < 0) e(0); - - if (i > 0) { - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) - e(0); - - if (access(tname, R_OK | W_OK) < 0) e(0); - - if (i > 1) { - if (close(masterfd) < 0) e(0); - - masterfd = slavefd; /* ugly but saving code */ - } else - if (close(slavefd) < 0) e(0); - } - - if (access(tname, R_OK | W_OK) < 0) e(0); - - if (close(masterfd) < 0) e(0); - - if (access(tname, R_OK | W_OK) == 0) e(0); - } - - /* - * Test whether we can open multiple pseudo terminals. We need to be - * able to open three PTYs. Verify that they are properly listed in - * the /dev/pts directory contents, and have proper attributes set. - */ - test_getdents(0, NULL, NULL); - - for (i = 0; i < 3; i++) { - fd[i] = get_unix98_pty(&tname); - - /* Figure out the slave index number. */ - len = strlen(_PATH_DEV_PTS); - if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); - array[i] = atoi(&tname[len]); - present[i] = 1; - } - - test_getdents(3, array, present); - - if (close(fd[0]) < 0) e(0); - present[0] = 0; - - test_getdents(3, array, present); - - if (close(fd[2]) < 0) e(0); - present[2] = 0; - - test_getdents(3, array, present); - - if (close(fd[1]) < 0) e(0); - present[1] = 0; - - test_getdents(3, array, present); - - /* - * Test chmod(2) on a slave node, and multiple calls to grantpt(3). - * The first grantpt(3) call should create the slave node (we currently - * can not test this: the slave node may be created earlier as well, - * but we do not know its name), whereas subsequent grantpt(3) calls - * should reset its mode, uid, and gid. Testing the latter two and - * chown(2) on the slave node requires root, so we skip that part. - * - * Finally, NetBSD revokes access to existing slave file descriptors - * upon a call to grantpt(3). This is not a POSIX requirement, but - * NetBSD needs this for security reasons because it already creates - * the slave node when the master is opened (and it does not lock the - * slave until a call to unlockpt(3)). MINIX3 does not implement - * revocation this way, because the slave node is created only upon the - * call to grantpt(3), thus leaving no insecure window for the slave - * side between posix_openpt(3) and grantpt(3). While this behavior - * may be changed later, we test for the lack of revocation here now. - */ - masterfd = get_unix98_pty(&tname); - - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if (stat(tname, &buf) != 0) e(0); - if (buf.st_mode != (S_IFCHR | 0620)) e(0); - - if (chmod(tname, S_IFCHR | 0630) != 0) e(0); - - if (stat(tname, &buf) != 0) e(0); - if (buf.st_mode != (S_IFCHR | 0630)) e(0); - - if (grantpt(masterfd) != 0) e(0); - - if (stat(tname, &buf) != 0) e(0); - if (buf.st_mode != (S_IFCHR | 0620)) e(0); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(0); - if (close(masterfd) < 0) e(0); - - test_getdents(0, NULL, NULL); -} - -/* - * Check that the given PTY index, which is in use for an old-style PTY, is not - * allocated through Unix98 PTY allocation. This test is not foolproof, but it - * does the job well enough. - */ -static void -test_overlap(int m) -{ - char *tname; - size_t len; - int i, n, fd[MIN_PTYS]; - - for (i = 0; i < MIN_PTYS; i++) { - if ((fd[i] = posix_openpt(O_RDWR | O_NOCTTY)) < 0) - break; /* out of PTYs */ - if (grantpt(fd[i]) < 0) e(0); - if (unlockpt(fd[i]) < 0) e(0); - if ((tname = ptsname(fd[i])) == NULL) e(0); - - len = strlen(_PATH_DEV_PTS); - if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); - n = atoi(&tname[len]); - if (n < 0 || n > 9) e(0); - - if (m == n) e(0); - } - - for (i--; i >= 0; i--) - if (close(fd[i]) < 0) e(0); -} - -/* - * Test for mixing access to old-style and Unix98 PTYs. Since the PTY service - * internally shares the set of pseudo terminals between the two types, it has - * to implement checks to prevent that a PTY opened as one type is also - * accessed through the other type. We test some of those checks here. - */ -static void -test77h(void) -{ - char *tname, ptest[PATH_MAX], ttest[PATH_MAX]; - struct sigaction act, oact; - size_t len; - int i, n, masterfd, slavefd; - - subtest = 8; - - /* We do not want to get SIGHUP signals in this test. */ - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - - /* - * Check that Unix98 PTYs cannot be accessed through old-style device - * nodes. We check different combinations of open master and - * slave ends for the Unix98 side (with 'i'): 0) opening and closing - * the master only, 1) closing a slave before the master, and 2) - * closing the slave after the master. - * - * This test relies on the implementation aspect that /dev/ttypN and - * /dev/pts/N (with N in the range 0..9) map to the same PTY. It also - * relies on lack of concurrent PTY allocation outside the test. - */ - for (i = 0; i <= 2; i++) { - /* Open a Unix98 PTY and get the slave name. */ - masterfd = get_unix98_pty(&tname); - - /* Figure out the slave index number. */ - len = strlen(_PATH_DEV_PTS); - if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); - n = atoi(&tname[len]); - if (n < 0 || n > 9) e(0); - - /* Use this index number to create old-style device names. */ - snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n); - snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n); - - /* - * Now make sure that opening the old-style master and slave - * fails as long as either side of the Unix98 PTY is open. - */ - if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - - if (i > 0) { - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) - e(0); - - if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - - if (close(slavefd) < 0) e(0); - - if (i > 1) { - if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - - if ((slavefd = - open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - - if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - - if (close(masterfd) < 0) e(0); - - masterfd = slavefd; /* ugly but saving code */ - } - - if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); - if (errno != EACCES && errno != EIO) e(0); - } - - if (close(masterfd) < 0) e(0); - - /* - * Once both Unix98 sides are closed, the pseudo terminal can - * be reused. Thus, opening the old-style master should now - * succeed. However, it is possible that we do not have - * permission to open the master at all. - */ - if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) < 0 && - errno != EACCES) e(0); - - if (masterfd >= 0 && close(masterfd) < 0) e(0); - } - - /* - * The reverse test, which would check that old-style PTYs cannot be - * accessed through Unix98 device nodes, is impossible to perform - * properly without root privileges, as we would have to create device - * nodes manually with mknod(2). All we can do here is ensure that if - * an old-style PTY is opened, it will not also be allocated as a - * Unix98 PTY. We do a rather basic check, but only if we can open an - * old-style master at all. We check two closing orders (with 'i'): - * 0) the slave first, 1) the master first. Here, we make the hard - * assumption that the system supports at least four pseudo terminals, - * of which at least one is currently free. - */ - for (i = 0; i <= 1; i++) { - for (n = 0; n < MIN_PTYS; n++) { - snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n); - - if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) >= 0) - break; - } - - if (n >= MIN_PTYS) - break; - - test_overlap(n); - - snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n); - - /* We can do part of the test only if we can open the slave. */ - if ((slavefd = open(ttest, O_RDWR | O_NOCTTY)) >= 0) { - test_overlap(n); - - if (i > 0) { - if (close(masterfd) < 0) e(0); - - masterfd = slavefd; /* again, ugly */ - } else - if (close(slavefd) < 0) e(0); - - test_overlap(n); - } - - if (close(masterfd) < 0) e(0); - } - - if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); -} - -int -main(int argc, char **argv) -{ - int i, m; - - start(77); - - if (argc == 2) - m = atoi(argv[1]); - else - m = 0xFF; - - for (i = 0; i < ITERATIONS; i++) { - if (m & 0x01) test77a(); - if (m & 0x02) test77b(); - if (m & 0x04) test77c(); - if (m & 0x08) test77d(); - if (m & 0x10) test77e(); - if (m & 0x20) test77f(); - if (m & 0x40) test77g(); - if (m & 0x80) test77h(); - } - - quit(); -} diff --git a/minix/tests/test84.c b/minix/tests/test84.c deleted file mode 100644 index 68f571f3e..000000000 --- a/minix/tests/test84.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Based off tests/lib/libc/gen/posix_spawn/t_spawn.c - */ - -/* $NetBSD: t_spawn.c,v 1.1 2012/02/13 21:03:08 martin Exp $ */ - -/*- - * Copyright (c) 2012 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Charles Zhang and - * Martin Husemann . - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -/* reroute stdout to /dev/null while returning another fd for the old stdout */ -/* this is just for aesthetics: we don't want to see the output of 'ls' */ -static int -sink_stdout(void) -{ - int fd, fd2; - - if ((fd = fcntl(1, F_DUPFD, 3)) == -1 || close(1) == -1) { - e(0); - quit(); - } - - if ((fd2 = open("/dev/null", O_WRONLY)) != 1) { - if (fd2 == -1 || dup2(fd2, 1) != 1) { - dup2(fd, 1); - e(0); - quit(); - } - } - - return fd; -} - -/* restore stdout */ -static void -restore_stdout(int fd) -{ - - dup2(fd, 1); - close(fd); -} - -/* tests a simple posix_spawn executing /bin/ls */ -static void -test_posix_spawn_ls(void) -{ - char * const args[] = { "ls", "-la", NULL }; - int err; - - err = posix_spawn(NULL, "/bin/ls", NULL, NULL, args, NULL); - if (err != 0) - e(1); -} - -/* tests a simple posix_spawnp executing ls via $PATH */ -static void -test_posix_spawnp_ls(void) -{ - char * const args[] = { "ls", "-la", NULL }; - int err; - - err = posix_spawnp(NULL, "ls", NULL, NULL, args, NULL); - if(err != 0) - e(2); -} - -/* posix_spawn a non existant binary */ -static void -test_posix_spawn_missing(void) -{ - char * const args[] = { "t84_h_nonexist", NULL }; - int err; - - err = posix_spawn(NULL, "../t84_h_nonexist", NULL, NULL, args, NULL); - if (err != ENOENT) - e(4); -} - -/* posix_spawn a script with non existing interpreter */ -static void -test_posix_spawn_nonexec(void) -{ - char * const args[] = { "t84_h_nonexec", NULL }; - int err; - - err = posix_spawn(NULL, "../t84_h_nonexec", NULL, NULL, args, NULL); - if (err != ENOENT) - e(5); -} - -/* posix_spawn a child and get it's return code */ -static void -test_posix_spawn_child(void) -{ - char * const args0[] = { "t84_h_spawn", "0", NULL }; - char * const args1[] = { "t84_h_spawn", "1", NULL }; - char * const args7[] = { "t84_h_spawn", "7", NULL }; - int err, status; - pid_t pid; - - err = posix_spawn(&pid, "../t84_h_spawn", NULL, NULL, args0, NULL); - if (err != 0 || pid < 1) - e(1); - waitpid(pid, &status, 0); - if (! (WIFEXITED(status) && WEXITSTATUS(status) == 0)) - e(2); - - err = posix_spawn(&pid, "../t84_h_spawn", NULL, NULL, args1, NULL); - if (err != 0 || pid < 1) - e(3); - waitpid(pid, &status, 0); - if (! (WIFEXITED(status) && WEXITSTATUS(status) == 1)) - e(4); - - err = posix_spawn(&pid, "../t84_h_spawn", NULL, NULL, args7, NULL); - if (err != 0 || pid < 1) - e(5); - waitpid(pid, &status, 0); - if (! (WIFEXITED(status) && WEXITSTATUS(status) == 7)) - e(6); -} - -/* test spawn attributes */ -static void -test_posix_spawnattr(void) -{ - int pid, status, err, pfd[2]; - char helper_arg[128]; - char * const args[] = { "t84_h_spawnattr", helper_arg, NULL }; - sigset_t sig; - posix_spawnattr_t attr; - - /* - * create a pipe to controll the child - */ - err = pipe(pfd); - if (err != 0) - e(1); - sprintf(helper_arg, "%d", pfd[0]); - - posix_spawnattr_init(&attr); - - sigemptyset(&sig); - sigaddset(&sig, SIGUSR1); - - posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSCHEDULER | - POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETPGROUP | - POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | - POSIX_SPAWN_SETSIGDEF); - posix_spawnattr_setpgroup(&attr, 0); -#if 0 - posix_spawnattr_setschedparam(&attr, &sp); - posix_spawnattr_setschedpolicy(&attr, scheduler); -#endif - posix_spawnattr_setsigmask(&attr, &sig); - posix_spawnattr_setsigdefault(&attr, &sig); - - err = posix_spawn(&pid, "../t84_h_spawnattr", NULL, &attr, args, NULL); - if (err != 0) - e(2); - - /* ready, let child go */ - write(pfd[1], "q", 1); - close(pfd[0]); - close(pfd[1]); - - /* wait and check result from child */ - waitpid(pid, &status, 0); - if (! (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)) - e(3); - - posix_spawnattr_destroy(&attr); -} - -/* tests a simple posix_spawn executing /bin/ls with file actions */ -static void -test_posix_spawn_file_actions(void) -{ - char * const args[] = { "ls", "-la", NULL }; - int err; - posix_spawn_file_actions_t file_actions; - - /* - * Just do a bunch of random operations which should leave console - * output intact. - */ - posix_spawn_file_actions_init(&file_actions); - posix_spawn_file_actions_adddup2(&file_actions, 1, 3); - posix_spawn_file_actions_adddup2(&file_actions, 1, 4); - posix_spawn_file_actions_adddup2(&file_actions, 1, 6); - posix_spawn_file_actions_adddup2(&file_actions, 1, 5); - posix_spawn_file_actions_addclose(&file_actions, 3); - posix_spawn_file_actions_addclose(&file_actions, 4); - posix_spawn_file_actions_addclose(&file_actions, 6); - posix_spawn_file_actions_addclose(&file_actions, 5); - - posix_spawn_file_actions_addclose(&file_actions, 0); - posix_spawn_file_actions_addclose(&file_actions, 2); - posix_spawn_file_actions_addopen(&file_actions, 0, "/dev/null", - O_RDONLY, 0); - posix_spawn_file_actions_adddup2(&file_actions, 1, 2); - posix_spawn_file_actions_addclose(&file_actions, 1); - posix_spawn_file_actions_adddup2(&file_actions, 2, 1); - - err = posix_spawn(NULL, "/bin/ls", &file_actions, NULL, args, NULL); - posix_spawn_file_actions_destroy(&file_actions); - - if (err != 0) - e(1); -} - -/* tests failures with file actions */ -static void -test_posix_spawn_file_actions_failures(void) -{ - char * const args[] = { "ls", "-la", NULL }; - int err, i; - posix_spawn_file_actions_t file_actions; - - /* Test bogus open */ - posix_spawn_file_actions_init(&file_actions); - posix_spawn_file_actions_addclose(&file_actions, 0); - posix_spawn_file_actions_addopen(&file_actions, 0, "t84_h_nonexist", - O_RDONLY, 0); - - err = posix_spawn(NULL, "/bin/ls", &file_actions, NULL, args, NULL); - posix_spawn_file_actions_destroy(&file_actions); - - if (err == 0) - e(1); - - /* Test bogus dup2 */ - for (i = 3; i < 10; i++) { - posix_spawn_file_actions_init(&file_actions); - posix_spawn_file_actions_adddup2(&file_actions, i, i+1); - - err = posix_spawn(NULL, "/bin/ls", &file_actions, NULL, args, - NULL); - posix_spawn_file_actions_destroy(&file_actions); - - if (err == 0) - e(i-2); - } - - /* - * Test bogus exec with dup2 (to mess with the pipe error reporting in - * posix_spawn.c) - */ - posix_spawn_file_actions_init(&file_actions); - posix_spawn_file_actions_adddup2(&file_actions, 1, 3); - posix_spawn_file_actions_adddup2(&file_actions, 1, 4); - posix_spawn_file_actions_adddup2(&file_actions, 1, 6); - posix_spawn_file_actions_adddup2(&file_actions, 1, 5); - posix_spawn_file_actions_adddup2(&file_actions, 1, 7); - - err = posix_spawn(NULL, "t84_h_nonexist", &file_actions, NULL, args, - NULL); - posix_spawn_file_actions_destroy(&file_actions); - - if (err == 0) - e(9); -} - -int -main(void) -{ - int fd; - - start(84); - - subtest = 1; - fd = sink_stdout(); - test_posix_spawn_ls(); - test_posix_spawnp_ls(); - restore_stdout(fd); - - test_posix_spawn_missing(); - test_posix_spawn_nonexec(); - - subtest = 2; - test_posix_spawn_child(); - - subtest = 3; - test_posix_spawnattr(); - subtest = 4; - fd = sink_stdout(); - test_posix_spawn_file_actions(); - restore_stdout(fd); - subtest = 5; - test_posix_spawn_file_actions_failures(); - - /* TODO: Write/port more tests */ - - quit(); - - /* Not reached */ - return -1; -} diff --git a/minix/tests/test87.c b/minix/tests/test87.c deleted file mode 100644 index 02e043f02..000000000 --- a/minix/tests/test87.c +++ /dev/null @@ -1,3662 +0,0 @@ -/* Tests for sysctl(2) and the MIB service - by D.C. van Moolenbroek */ -/* This test needs to run as root: many sysctl(2) calls are privileged. */ -#include -#include -#include -#include -#include -#include -#include -#include - -#define ITERATIONS 2 - -#include "common.h" - -#define NONROOT_USER "bin" /* name of any unprivileged user */ - -#define NEXT_VER(n) (((n) + 1 == 0) ? 1 : ((n) + 1)) /* node version + 1 */ - -static void *bad_ptr; /* a pointer to unmapped memory */ -static unsigned int nodes, objects; /* stats for pre/post test check */ - -/* - * Spawn a child process that drops privileges and then executes the given - * procedure. The returned PID value is of the dead, cleaned-up child, and - * should be used only to check whether the child could store its own PID. - */ -static pid_t -test_nonroot(void (* proc)(void)) -{ - struct passwd *pw; - pid_t pid; - int status; - - pid = fork(); - - switch (pid) { - case -1: - e(0); - break; - case 0: - errct = 0; - - if ((pw = getpwnam(NONROOT_USER)) == NULL) e(0); - - if (setuid(pw->pw_uid) != 0) e(0); - - proc(); - - exit(errct); - default: - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status)) e(0); - if (WEXITSTATUS(status) != 0) e(0); - } - - return pid; -} - -/* - * Test basic operations from an unprivileged process. - */ -static void -sub87a(void) -{ - size_t oldlen; - pid_t pid; - bool b; - int i, mib[4]; - - pid = getpid(); - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - - /* Regular reads should succeed. */ - mib[2] = TEST_INT; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != 0x01020304) e(0); - - mib[2] = TEST_BOOL; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - /* Regular writes should fail. */ - b = true; - if (sysctl(mib, 3, NULL, NULL, &b, sizeof(b)) != -1) e(0); - if (errno != EPERM) e(0); - - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - /* Privileged reads and writes should fail. */ - mib[2] = TEST_PRIVATE; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); - - oldlen = sizeof(i); - i = 1; - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); - if (i != 1) e(0); - - if (sysctl(mib, 3, NULL, NULL, &i, sizeof(i)) != -1) e(0); - if (errno != EPERM) e(0); - - mib[2] = TEST_SECRET; - mib[3] = SECRET_VALUE; - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 4, &i, &oldlen, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); - if (i == 12345) e(0); - - mib[3]++; - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 4, &i, &oldlen, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); - - /* Free-for-all writes should succeed. */ - mib[2] = TEST_ANYWRITE; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - - i = pid; - if (sysctl(mib, 3, NULL, NULL, &i, sizeof(i)) != 0) e(0); - - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != pid) e(0); -} - -/* - * Test the basic sysctl(2) interface. - */ -static void -test87a(void) -{ - char buf[32]; - size_t len, oldlen; - pid_t pid; - u_quad_t q; - bool b, b2; - int i, va[2], lastva = -1 /*gcc*/, mib[CTL_MAXNAME + 1]; - - subtest = 0; - - mib[0] = INT_MAX; /* some root-level identifier that does not exist */ - for (i = 1; i <= CTL_MAXNAME; i++) - mib[i] = i; - - /* - * We cannot test for invalid 'name' and 'oldlenp' pointers, because - * those may be accessed directly by the libc system call stub. The - * NetBSD part of the stub even accesses name[0] without checking - * namelen first. - */ - if (sysctl(mib, 0, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, INT_MAX, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, UINT_MAX, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - for (i = 1; i <= CTL_MAXNAME; i++) { - if (sysctl(mib, i, NULL, NULL, NULL, 0) != -1) e(i); - if (errno != ENOENT) e(i); - } - if (sysctl(mib, i, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - /* Test names that are too short, right, and too long. */ - mib[0] = CTL_MINIX; - if (sysctl(mib, 1, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EISDIR) e(0); - mib[1] = MINIX_TEST; - if (sysctl(mib, 2, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EISDIR) e(0); - mib[2] = TEST_INT; - if (sysctl(mib, 3, NULL, NULL, NULL, 0) != 0) e(0); - mib[3] = 0; - if (sysctl(mib, 4, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != ENOTDIR) e(0); - - /* Do some tests with meta-identifiers (special keys). */ - mib[3] = CTL_QUERY; - if (sysctl(mib, 4, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = CTL_QUERY; - mib[3] = 0; - if (sysctl(mib, 4, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - mib[2] = CTL_EOL; /* a known-invalid meta-identifier */ - if (sysctl(mib, 3, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EOPNOTSUPP) e(0); - - /* This case returns EINVAL now but might as well return EOPNOTSUPP. */ - mib[3] = 0; - if (sysctl(mib, 4, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EOPNOTSUPP && errno != EINVAL) e(0); - - /* Make sure the given oldlen value is ignored when unused. */ - mib[2] = TEST_INT; - oldlen = 0; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(int)) e(0); - oldlen = 1; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(int)) e(0); - oldlen = SSIZE_MAX; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(int)) e(0); - oldlen = SIZE_MAX; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(int)) e(0); - - /* Test retrieval with the exact length. */ - oldlen = sizeof(va[0]); - va[0] = va[1] = -1; - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (va[0] != 0x01020304) e(0); - if (va[1] != -1) e(0); - - /* Test retrieval with a length that is too short. */ - for (i = 0; i < sizeof(va[0]); i++) { - va[0] = -1; - oldlen = i; - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (i == 0 && va[0] != -1) e(0); - if (i > 0 && va[0] >= lastva) e(0); - if (va[1] != -1) e(0); - lastva = va[0]; - } - - /* Test retrieval with a length that is too long. */ - oldlen = sizeof(va[0]) + 1; - va[0] = -1; - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (va[0] != 0x01020304) e(0); - if (va[1] != -1) e(0); - - oldlen = SSIZE_MAX; - va[0] = -1; - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (va[0] != 0x01020304) e(0); - if (va[1] != -1) e(0); - - oldlen = SIZE_MAX; - va[0] = -1; - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (va[0] != 0x01020304) e(0); - if (va[1] != -1) e(0); - - /* - * Ensure that we cannot overwrite this read-only integer. A write - * request must have both a pointer and a nonzero length, though. - */ - va[0] = 0x05060708; - if (sysctl(mib, 3, NULL, NULL, NULL, 1) != 0) e(0); - if (sysctl(mib, 3, NULL, NULL, va, 0) != 0) e(0); - if (sysctl(mib, 3, NULL, NULL, va, sizeof(va[0])) != -1) e(0); - if (errno != EPERM) e(0); - - oldlen = sizeof(va[0]); - va[0] = -1; - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (va[0] != 0x01020304) e(0); - if (va[1] != -1) e(0); - - /* Test retrieval into a bad pointer. */ - oldlen = sizeof(int); - if (sysctl(mib, 3, bad_ptr, &oldlen, NULL, 0) != -1) e(0); - if (errno != EFAULT) e(0); - - /* - * Test reading and writing booleans. Booleans may actually be an int, - * a char, or just one bit of a char. As a result, the MIB service can - * not test properly for non-bool values being passed in bool fields, - * and we can not do effective testing on this either, because in both - * cases our efforts may simply be optimized away, and result in - * unexpected success. - */ - mib[2] = TEST_BOOL; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false && b != true) e(0); - - b = true; - if (sysctl(mib, 3, NULL, &oldlen, &b, sizeof(b)) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - - b = false; - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != true) e(0); - - b = false; - b2 = false; - oldlen = sizeof(b2); - if (sysctl(mib, 3, &b2, &oldlen, &b, sizeof(b)) != 0) e(0); - if (oldlen != sizeof(b2)) e(0); - if (b != false) e(0); - if (b2 != true) e(0); - - if (sysctl(mib, 3, NULL, NULL, &b, sizeof(b) + 1) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * The MIB service does not support value swaps. If we pass in the - * same buffer for old and new data, we expect that the old data stays. - */ - b = true; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, &b, sizeof(b)) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - b = true; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - /* Test reading and writing a quad. */ - mib[2] = TEST_QUAD; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - - q = 0x1234567890abcdefULL; - if (sysctl(mib, 3, NULL, NULL, &q, sizeof(q)) != 0) e(0); - - q = 0ULL; - oldlen = sizeof(q); - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != 0x1234567890abcdefULL) e(0); - - q = ~0ULL; - if (sysctl(mib, 3, NULL, NULL, &q, sizeof(q)) != 0) e(0); - - /* Test writing with a bad pointer. The value must stay. */ - if (sysctl(mib, 3, NULL, NULL, bad_ptr, sizeof(q)) != -1) e(0); - if (errno != EFAULT) e(0); - - q = 0ULL; - oldlen = sizeof(q); - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != ~0ULL) e(0); - - q = 0ULL; - if (sysctl(mib, 3, NULL, NULL, &q, sizeof(q)) != 0) e(0); - - q = 1ULL; - oldlen = sizeof(q); - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != 0ULL) e(0); - - /* Test reading and writing a string. */ - mib[2] = TEST_STRING; - strlcpy(buf, "test", sizeof(buf)); - len = strlen(buf); - if (sysctl(mib, 3, NULL, NULL, buf, len + 1) != 0) e(0); - - oldlen = sizeof(buf); - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - - memset(buf, 0x07, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (strcmp(buf, "test")) e(0); - if (oldlen != len + 1) e(0); - if (buf[len + 1] != 0x07) e(0); - - strlcpy(buf, "abc123", sizeof(buf)); - oldlen = 2; - if (sysctl(mib, 3, NULL, &oldlen, buf, strlen(buf) + 1) != 0) e(0); - if (oldlen != len + 1) e(0); - len = strlen(buf); - - memset(buf, 0x07, sizeof(buf)); - oldlen = len - 1; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != len + 1) e(0); - if (strncmp(buf, "abc12", len - 1)) e(0); - if (buf[len - 1] != 0x07 || buf[len] != 0x07) e(0); - - memset(buf, 0x07, sizeof(buf)); - oldlen = len + 1; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - if (strcmp(buf, "abc123")) e(0); - - /* - * Now put in a shorter string, without null terminator. The string - * must be accepted; the null terminator must be added automatically. - */ - strlcpy(buf, "foolproof", sizeof(buf)); - len = strlen("foo"); - if (sysctl(mib, 3, NULL, NULL, buf, len) != 0) e(0); - - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - - memset(buf, 0x07, sizeof(buf)); - oldlen = len; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != len + 1) e(0); - if (strncmp(buf, "foo", len)) e(0); - if (buf[len] != 0x07) e(0); - - memset(buf, 0x07, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - if (strcmp(buf, "foo")) e(0); - if (buf[len + 1] != 0x07) e(0); - - /* - * Passing in more data after the string is fine, but whatever comes - * after the first null terminator is disregarded. - */ - strlcpy(buf, "barbapapa", sizeof(buf)); - len = strlen(buf); - buf[3] = '\0'; - if (sysctl(mib, 3, NULL, NULL, buf, len + 1)) e(0); - len = strlen(buf); - - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - - memset(buf, 0x07, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - if (strcmp(buf, "bar")) e(0); - if (buf[len + 1] != 0x07) e(0); - - /* Test the maximum string length. */ - strlcpy(buf, "0123456789abcdef", sizeof(buf)); - len = strlen(buf); - if (sysctl(mib, 3, NULL, NULL, buf, len + 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, buf, len) != -1) e(0); - if (errno != EINVAL) e(0); - - buf[--len] = '\0'; - if (sysctl(mib, 3, NULL, NULL, buf, len + 1) != 0) e(0); - memset(buf, 0x07, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - if (strcmp(buf, "0123456789abcde")) e(0); - if (buf[len + 1] != 0x07) e(0); - - /* - * Clearing out the field with zero-length data is not possible, - * because zero-length updates are disregarded at a higher level. - */ - if (sysctl(mib, 3, NULL, NULL, "", 0) != 0) e(0); - memset(buf, 0x07, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len + 1) e(0); - if (strcmp(buf, "0123456789abcde")) e(0); - - /* To clear the field, the null terminator is required. */ - if (sysctl(mib, 3, NULL, NULL, "", 1) != 0) e(0); - memset(buf, 0x07, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 1) e(0); - if (buf[0] != '\0') e(0); - if (buf[1] != 0x07) e(0); - - /* - * Test reading and writing structures. Structures are just blobs of - * data, with no special handling by default. They can only be read - * and written all at once. - */ - mib[2] = TEST_STRUCT; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 12) e(0); - len = oldlen; - - for (i = 0; i < len + 1; i++) - buf[i] = i + 1; - if (sysctl(mib, 3, NULL, NULL, buf, len - 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, buf, len + 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, buf, len) != 0) e(0); - - memset(buf, 0x7f, sizeof(buf)); - oldlen = len - 1; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != len) e(0); - for (i = 0; i < len - 1; i++) - if (buf[i] != i + 1) e(0); - if (buf[i] != 0x7f) e(0); - - memset(buf, 0x7f, sizeof(buf)); - oldlen = len + 1; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len) e(0); - for (i = 0; i < len; i++) - if (buf[i] != i + 1) e(0); - if (buf[i] != 0x7f) e(0); - - memset(buf, 0x7f, sizeof(buf)); - oldlen = len; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - for (i = 0; i < len; i++) - if (buf[i] != i + 1) e(0); - if (buf[len] != 0x7f) e(0); - - /* Null characters are not treated in any special way. */ - for (i = 0; i < len; i++) - buf[i] = !!i; - if (sysctl(mib, 3, NULL, NULL, buf, len) != 0) e(0); - - oldlen = len; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len) e(0); - for (i = 0; i < len; i++) - if (buf[i] != !!i) e(0); - if (buf[len] != 0x7f) e(0); - - memset(buf, 0, len); - if (sysctl(mib, 3, NULL, NULL, buf, len) != 0) e(0); - - oldlen = len; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len) e(0); - for (i = 0; i < len; i++) - if (buf[i] != 0) e(0); - if (buf[len] != 0x7f) e(0); - - /* - * Test private read and free-for-all write operations. For starters, - * this test should run with superuser privileges, and thus should be - * able to read and write private fields. - */ - mib[2] = TEST_PRIVATE; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (va[0] != -5375) e(0); - if (sysctl(mib, 3, NULL, NULL, va, sizeof(va[0])) != 0) e(0); - - mib[2] = TEST_SECRET; - mib[3] = SECRET_VALUE; - oldlen = sizeof(va[0]); - if (sysctl(mib, 4, va, &oldlen, NULL, 0) != 0) e(0); - if (va[0] != 12345) e(0); - if (sysctl(mib, 4, NULL, NULL, va, sizeof(va[0])) != -1) e(0); - if (errno != EPERM) e(0); - - mib[3]++; - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 4, &i, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOENT) e(0); - - /* Use a child process to test operations without root privileges. */ - pid = test_nonroot(sub87a); - - /* The change made by the child should be visible to the parent. */ - mib[2] = TEST_ANYWRITE; - va[0] = 0; - oldlen = sizeof(va[0]); - if (sysctl(mib, 3, va, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(va[0])) e(0); - if (va[0] != pid) e(0); -} - -/* - * Test queries from an unprivileged process. - */ -static void -sub87b(void) -{ - struct sysctlnode scn[32]; - unsigned int count; - size_t oldlen; - int i, mib[4]; - - /* Query minix.test and make sure we do not get privileged values. */ - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_QUERY; - - oldlen = sizeof(scn); - if (sysctl(mib, 3, scn, &oldlen, NULL, 0) != 0) e(0); - if (oldlen % sizeof(scn[0])) e(0); - count = oldlen / sizeof(scn[0]); - if (count < 8) e(0); - - /* - * Do not bother doing the entire check again, but test enough to - * inspire confidence that only the right values are hidden. - */ - if (scn[0].sysctl_num != TEST_INT) e(0); - if (SYSCTL_TYPE(scn[0].sysctl_flags) != CTLTYPE_INT) e(0); - if ((SYSCTL_FLAGS(scn[0].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READONLY | CTLFLAG_IMMEDIATE | CTLFLAG_HEX)) e(0); - if (SYSCTL_VERS(scn[0].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[0].sysctl_name, "int")) e(0); - if (scn[0].sysctl_ver == 0) e(0); - if (scn[0].sysctl_size != sizeof(int)) e(0); - if (scn[0].sysctl_idata != 0x01020304) e(0); - - for (i = 0; i < count; i++) - if (scn[i].sysctl_num == TEST_PRIVATE) - break; - if (i == count) e(0); - if (SYSCTL_TYPE(scn[i].sysctl_flags) != CTLTYPE_INT) e(0); - if ((SYSCTL_FLAGS(scn[i].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READWRITE | CTLFLAG_PRIVATE | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(scn[i].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[i].sysctl_name, "private")) e(0); - if (scn[i].sysctl_size != sizeof(int)) e(0); - if (scn[i].sysctl_idata != 0) e(0); /* private */ - - for (i = 0; i < count; i++) - if (scn[i].sysctl_num == TEST_SECRET) - break; - if (i == count) e(0); - if (SYSCTL_TYPE(scn[i].sysctl_flags) != CTLTYPE_NODE) e(0); - if ((SYSCTL_FLAGS(scn[i].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READONLY | CTLFLAG_PRIVATE)) e(0); - if (SYSCTL_VERS(scn[i].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[i].sysctl_name, "secret")) e(0); - if (scn[i].sysctl_ver == 0) e(0); - if (scn[i].sysctl_size != sizeof(scn[0])) e(0); - if (scn[i].sysctl_csize != 0) e(0); /* private */ - if (scn[i].sysctl_clen != 0) e(0); /* private */ - - /* Make sure that a query on minix.test.secret fails. */ - mib[2] = TEST_SECRET; - mib[3] = CTL_QUERY; - if (sysctl(mib, 4, NULL, &oldlen, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); -} - -/* - * Test sysctl(2) queries. - */ -static void -test87b(void) -{ - struct sysctlnode scn[32]; - unsigned int count; - size_t len, oldlen; - u_quad_t q; - bool b; - int i, mib[4]; - - subtest = 1; - - /* We should be able to query the root key. */ - mib[0] = CTL_QUERY; - - oldlen = 0; - if (sysctl(mib, 1, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen <= sizeof(scn[0])) e(0); - if (oldlen % sizeof(scn[0])) e(0); - - oldlen = sizeof(scn[0]); - if (sysctl(mib, 1, scn, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOMEM); - if (oldlen <= sizeof(scn[0])) e(0); - if (oldlen % sizeof(scn[0])) e(0); - - /* - * We assume that the root node's first child is always CTL_KERN, which - * must be read-only and may have only the CTLFLAG_PERMANENT flag set. - */ - if (scn[0].sysctl_num != CTL_KERN) e(0); - if (SYSCTL_TYPE(scn[0].sysctl_flags) != CTLTYPE_NODE) e(0); - if ((SYSCTL_FLAGS(scn[0].sysctl_flags) & ~CTLFLAG_PERMANENT) != - CTLFLAG_READONLY) e(0); - if (SYSCTL_VERS(scn[0].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[0].sysctl_name, "kern")) e(0); - if (scn[0].sysctl_ver == 0) e(0); - if (scn[0].sysctl_size != sizeof(scn[0])) e(0); - if ((int)scn[0].sysctl_csize <= 0) e(0); - if ((int)scn[0].sysctl_clen <= 0) e(0); - if (scn[0].sysctl_csize < scn[0].sysctl_clen) e(0); - - /* Now do a more complete test on the minix.test subtree. */ - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - - /* - * Initialize a few immediate fields to nonzero so that we can test - * that their values are returned as a result of the query. - */ - mib[2] = TEST_BOOL; - b = true; - if (sysctl(mib, 3, NULL, NULL, &b, sizeof(b)) != 0) e(0); - - mib[2] = TEST_QUAD; - q = ~0; - if (sysctl(mib, 3, NULL, NULL, &q, sizeof(q)) != 0) e(0); - - mib[2] = CTL_QUERY; - - oldlen = 1; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen % sizeof(scn[0])) e(0); - if (oldlen >= sizeof(scn)) e(0); - len = oldlen; - count = len / sizeof(scn[0]); - if (count < 8) e(0); - - memset(scn, 0x7e, sizeof(scn)); - if (sysctl(mib, 3, scn, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len) e(0); - if (scn[count].sysctl_name[0] != 0x7e) e(0); - - /* - * Again, we rely on the MIB service returning entries in ascending - * order for at least the static nodes. We do not make assumptions - * about whether dynamic nodes are merged in or (as is the case as of - * writing) returned after the static nodes. At this point there - * should be no dynamic nodes here yet anyway. We mostly ignore - * CTLFLAG_PERMANENT in order to facilitate running this test on a - * remotely mounted subtree. - */ - if (scn[0].sysctl_num != TEST_INT) e(0); - if (SYSCTL_TYPE(scn[0].sysctl_flags) != CTLTYPE_INT) e(0); - if ((SYSCTL_FLAGS(scn[0].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READONLY | CTLFLAG_IMMEDIATE | CTLFLAG_HEX)) e(0); - if (SYSCTL_VERS(scn[0].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[0].sysctl_name, "int")) e(0); - if (scn[0].sysctl_ver == 0) e(0); - if (scn[0].sysctl_size != sizeof(int)) e(0); - if (scn[0].sysctl_idata != 0x01020304) e(0); - - if (scn[1].sysctl_num != TEST_BOOL) e(0); - if (SYSCTL_TYPE(scn[1].sysctl_flags) != CTLTYPE_BOOL) e(0); - if ((SYSCTL_FLAGS(scn[1].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READWRITE | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(scn[1].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[1].sysctl_name, "bool")) e(0); - if (scn[1].sysctl_ver == 0) e(0); - if (scn[1].sysctl_size != sizeof(bool)) e(0); - if (scn[1].sysctl_bdata != true) e(0); - - if (scn[2].sysctl_num != TEST_QUAD) e(0); - if (SYSCTL_TYPE(scn[2].sysctl_flags) != CTLTYPE_QUAD) e(0); - if ((SYSCTL_FLAGS(scn[2].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READWRITE | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(scn[2].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[2].sysctl_name, "quad")) e(0); - if (scn[2].sysctl_ver == 0) e(0); - if (scn[2].sysctl_size != sizeof(u_quad_t)) e(0); - if (scn[2].sysctl_qdata != q) e(0); - - if (scn[3].sysctl_num != TEST_STRING) e(0); - if (SYSCTL_TYPE(scn[3].sysctl_flags) != CTLTYPE_STRING) e(0); - if ((SYSCTL_FLAGS(scn[3].sysctl_flags) & ~CTLFLAG_PERMANENT) != - CTLFLAG_READWRITE) e(0); - if (SYSCTL_VERS(scn[3].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[3].sysctl_name, "string")) e(0); - if (scn[3].sysctl_ver == 0) e(0); - if (scn[3].sysctl_size != 16) e(0); - - if (scn[4].sysctl_num != TEST_STRUCT) e(0); - if (SYSCTL_TYPE(scn[4].sysctl_flags) != CTLTYPE_STRUCT) e(0); - if ((SYSCTL_FLAGS(scn[4].sysctl_flags) & ~CTLFLAG_PERMANENT) != - CTLFLAG_READWRITE) e(0); - if (SYSCTL_VERS(scn[4].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[4].sysctl_name, "struct")) e(0); - if (scn[4].sysctl_ver == 0) e(0); - if (scn[4].sysctl_size != 12) e(0); - - if (scn[5].sysctl_num != TEST_PRIVATE) e(0); - if (SYSCTL_TYPE(scn[5].sysctl_flags) != CTLTYPE_INT) e(0); - if ((SYSCTL_FLAGS(scn[5].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READWRITE | CTLFLAG_PRIVATE | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(scn[5].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[5].sysctl_name, "private")) e(0); - if (scn[5].sysctl_ver == 0) e(0); - if (scn[5].sysctl_size != sizeof(int)) e(0); - if (scn[5].sysctl_idata != -5375) e(0); - - if (scn[6].sysctl_num != TEST_ANYWRITE) e(0); - if (SYSCTL_TYPE(scn[6].sysctl_flags) != CTLTYPE_INT) e(0); - if ((SYSCTL_FLAGS(scn[6].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READWRITE | CTLFLAG_ANYWRITE | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(scn[6].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[6].sysctl_name, "anywrite")) e(0); - if (scn[6].sysctl_ver == 0) e(0); - if (scn[6].sysctl_size != sizeof(int)) e(0); - - i = (scn[7].sysctl_num == TEST_DYNAMIC) ? 8 : 7; - - if (scn[i].sysctl_num != TEST_SECRET) e(0); - if (SYSCTL_TYPE(scn[i].sysctl_flags) != CTLTYPE_NODE) e(0); - if ((SYSCTL_FLAGS(scn[i].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READONLY | CTLFLAG_PRIVATE)) e(0); - if (SYSCTL_VERS(scn[i].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[i].sysctl_name, "secret")) e(0); - if (scn[i].sysctl_ver == 0) e(0); - if (scn[i].sysctl_size != sizeof(scn[0])) e(0); - if (scn[i].sysctl_csize != 1) e(0); - if (scn[i].sysctl_clen != 1) e(0); - - /* - * Now that we know how many entries there are in minix.test, also look - * at whether the right child length is returned in a query on its - * parent. While doing that, see whether data structure versioning - * works as expected as well. MINIX_TEST is hardcoded to zero so we - * expect it to be the first entry returned from a query. - */ - mib[1] = CTL_QUERY; - - memset(scn, 0, sizeof(scn)); - scn[1].sysctl_flags = SYSCTL_VERS_0; - if (sysctl(mib, 2, NULL, &oldlen, &scn[1], sizeof(scn[1])) != -1) e(0); - if (errno != EINVAL) e(0); - scn[1].sysctl_flags = SYSCTL_VERS_1; - if (sysctl(mib, 2, NULL, &oldlen, &scn[1], sizeof(scn[1]) - 1) != -1) - e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 2, NULL, &oldlen, &scn[1], sizeof(scn[1]) + 1) != -1) - e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 2, NULL, &oldlen, &scn[1], sizeof(scn[1])) != 0) e(0); - if (oldlen == 0) e(0); - if (oldlen % sizeof(scn[0])) e(0); - - oldlen = sizeof(scn[0]); - scn[1].sysctl_flags = SYSCTL_VERS_0; - if (sysctl(mib, 2, scn, &oldlen, &scn[1], sizeof(scn[1])) != -1) e(0); - if (errno != EINVAL) e(0); - oldlen = sizeof(scn[0]); - scn[1].sysctl_flags = SYSCTL_VERS_1; - if (sysctl(mib, 2, scn, &oldlen, &scn[1], sizeof(scn[1])) != 0 && - errno != ENOMEM) e(0); - if (oldlen == 0) e(0); - if (oldlen % sizeof(scn[0])) e(0); - - if (scn[0].sysctl_num != MINIX_TEST) e(0); - if (SYSCTL_TYPE(scn[0].sysctl_flags) != CTLTYPE_NODE) e(0); - if ((SYSCTL_FLAGS(scn[0].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READWRITE | CTLFLAG_HIDDEN)) e(0); - if (SYSCTL_VERS(scn[0].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[0].sysctl_name, "test")) e(0); - if (scn[0].sysctl_ver == 0) e(0); - if (scn[0].sysctl_size != sizeof(scn[0])) e(0); - if ((int)scn[0].sysctl_clen != count) e(0); - if (scn[0].sysctl_csize < scn[0].sysctl_clen) e(0); - - /* - * Test querying minix.test.secret, which should have exactly one node. - * At the same time, test bad pointers. - */ - mib[1] = MINIX_TEST; - mib[2] = TEST_SECRET; - mib[3] = CTL_QUERY; - oldlen = sizeof(scn); - if (sysctl(mib, 4, NULL, &oldlen, bad_ptr, sizeof(scn[0])) != -1) e(0); - if (errno != EFAULT) e(0); - - oldlen = sizeof(scn[0]) * 2; - if (sysctl(mib, 4, bad_ptr, &oldlen, NULL, 0) != -1) e(0); - if (errno != EFAULT) e(0); - - memset(scn, 0x7, sizeof(scn[0]) * 2); - oldlen = sizeof(scn[0]) * 2; - if (sysctl(mib, 4, scn, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(scn[0])) e(0); - - if (scn[0].sysctl_num != SECRET_VALUE) e(0); - if (SYSCTL_TYPE(scn[0].sysctl_flags) != CTLTYPE_INT) e(0); - if ((SYSCTL_FLAGS(scn[0].sysctl_flags) & ~CTLFLAG_PERMANENT) != - (CTLFLAG_READONLY | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(scn[0].sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(scn[0].sysctl_name, "value")) e(0); - if (scn[0].sysctl_ver == 0) e(0); - if (scn[0].sysctl_size != sizeof(int)) e(0); - if (scn[0].sysctl_idata != 12345) e(0); - if (scn[1].sysctl_name[0] != 0x07) e(0); - - /* Use a child process to test queries without root privileges. */ - (void)test_nonroot(sub87b); - - /* Do some more path-related error code tests unrelated to the rest. */ - mib[1] = INT_MAX; - mib[2] = CTL_QUERY; - oldlen = sizeof(scn[0]); - if (sysctl(mib, 3, &scn, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOENT) e(0); - - mib[1] = MINIX_TEST; - mib[2] = TEST_INT; - mib[3] = CTL_QUERY; - oldlen = sizeof(scn[0]); - if (sysctl(mib, 4, &scn, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOTDIR) e(0); /* ..and not EPERM (_INT is read-only) */ - - mib[2] = TEST_BOOL; - oldlen = sizeof(scn[0]); - if (sysctl(mib, 4, &scn, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOTDIR) e(0); /* (_BOOL is read-write) */ - - mib[2] = CTL_QUERY; - oldlen = sizeof(scn[0]); - if (sysctl(mib, 4, &scn, &oldlen, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); -} - -/* - * Attempt to create a node, using a given node template, identifier, and name - * string. If other_id is nonnegative, the creation is expected to fail due to - * a collision with an existing node, which should have the ID other_id and the - * name string in other_name. Otherwise, the creation may succeed or fail, and - * the caller must perform the appropriate checks. On success, return the new - * node identifier. On failure, return -1, with errno set. - */ -static int -create_node(const int * path, unsigned int pathlen, struct sysctlnode * tmpscn, - int id, const char * name, int other_id, const char * other_name) -{ - struct sysctlnode scn, oldscn; - size_t oldlen; - int r, mib[CTL_MAXNAME]; - - assert(pathlen < CTL_MAXNAME); - memcpy(mib, path, sizeof(mib[0]) * pathlen); - mib[pathlen] = CTL_CREATE; - - memcpy(&scn, tmpscn, sizeof(scn)); - scn.sysctl_num = id; - strlcpy(scn.sysctl_name, name, sizeof(scn.sysctl_name)); - oldlen = sizeof(oldscn); - r = sysctl(mib, pathlen + 1, &oldscn, &oldlen, &scn, sizeof(scn)); - if (other_id >= 0) { /* conflict expected */ - if (oldlen != sizeof(oldscn)) e(0); - if (r != -1) e(0); - if (errno != EEXIST) e(0); - if (oldscn.sysctl_num != other_id) e(0); - if (strcmp(oldscn.sysctl_name, other_name)) e(0); - return -1; - } else { - if (r != 0) - return r; - if (oldlen != sizeof(oldscn)) e(0); - return oldscn.sysctl_num; - } -} - -/* - * Destroy a node by identifier in the given named node directory. Return 0 on - * success. Return -1 on failure, with errno set. - */ -static int -destroy_node(const int * path, unsigned int pathlen, int id) -{ - struct sysctlnode scn; - int mib[CTL_MAXNAME]; - - assert(pathlen < CTL_MAXNAME); - memcpy(mib, path, sizeof(mib[0]) * pathlen); - mib[pathlen] = CTL_DESTROY; - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = id; - - return sysctl(mib, pathlen + 1, NULL, NULL, &scn, sizeof(scn)); -} - -/* - * Obtain the node data for one particular node in a node directory, by its - * parent path and identifier. Return 0 on success, with the node details - * stored in 'scn', or -1 on failure. - */ -static int -query_node(const int * path, unsigned int pathlen, int id, - struct sysctlnode * scn) -{ - struct sysctlnode scnset[32]; - size_t oldlen; - unsigned int i; - int r, mib[CTL_MAXNAME]; - - assert(pathlen < CTL_MAXNAME); - memcpy(mib, path, sizeof(mib[0]) * pathlen); - mib[pathlen] = CTL_QUERY; - - oldlen = sizeof(scnset); - if ((r = sysctl(mib, pathlen + 1, scnset, &oldlen, NULL, 0)) != 0 && - errno != ENOMEM) e(0); - if (oldlen == 0 || oldlen % sizeof(scnset[0])) e(0); - for (i = 0; i < oldlen / sizeof(scnset[0]); i++) - if (scnset[i].sysctl_num == id) - break; - if (i == oldlen / sizeof(scnset[0])) { - if (r != 0) e(0); /* if this triggers, make scnset[] bigger! */ - return -1; - } - memcpy(scn, &scnset[i], sizeof(*scn)); - return 0; -} - -/* - * Test unprivileged node creation. - */ -static void -sub87c(void) -{ - struct sysctlnode scn; - int mib[4]; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = TEST_DYNAMIC; - mib[3] = CTL_CREATE; - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_IMMEDIATE | - CTLFLAG_READONLY | CTLTYPE_INT; - scn.sysctl_size = sizeof(int); - scn.sysctl_num = CTL_CREATE; - scn.sysctl_idata = 777; - strlcpy(scn.sysctl_name, "nonroot", sizeof(scn.sysctl_name)); - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); - - mib[0] = CTL_CREATE; - scn.sysctl_num = CTL_MINIX + 1; - if (sysctl(mib, 1, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); -} - -/* - * Test sysctl(2) node creation. - */ -static void -test87c(void) -{ - static const uint32_t badflags[] = { - SYSCTL_VERS_MASK, SYSCTL_TYPEMASK, CTLFLAG_PERMANENT, - CTLFLAG_ROOT, CTLFLAG_ANYNUMBER, CTLFLAG_ALIAS, CTLFLAG_MMAP, - CTLFLAG_OWNDESC - }; - static const size_t badintsizes[] = { - 0, 1, sizeof(int) - 1, sizeof(int) + 1, sizeof(int) * 2, - sizeof(int) * 4, SSIZE_MAX, SIZE_MAX - }; - static const char *goodnames[] = { - "_", "a", "test_name", "_____foo", "bar_0_1_2_3", "_2bornot2b", - "abcdefghijklmnopqrstuvwxyz12345", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ67890", - }; - static const char *badnames[] = { - "", "0", "test.name", "2bornot2b", "@a", "b[", "c`d", "{", - "\n", "\xff", "dir/name", "foo:bar", - "abcdefghijklmnopqrstuvwxyz123456" - }; - struct sysctlnode scn, pscn, oldscn, newscn, tmpscn, scnset[32]; - size_t oldlen, len; - char buf[32], seen[5]; - bool b; - u_quad_t q; - int i, mib[CTL_MAXNAME], id[3]; - - subtest = 2; - - /* - * On the first run of this test, this call with actually destroy a - * static node. On subsequent runs, it may clean up the most likely - * leftover from a previous failed test. - */ - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - (void)destroy_node(mib, 2, TEST_DYNAMIC); - - /* Get child statistics about the parent node, for later comparison. */ - if (query_node(mib, 1, MINIX_TEST, &pscn) != 0) e(0); - if (pscn.sysctl_clen == 0) e(0); - if (pscn.sysctl_csize <= pscn.sysctl_clen) e(0); - - /* Start by testing if we can actually create a node at all. */ - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_IMMEDIATE | - CTLFLAG_READONLY | CTLTYPE_INT; - scn.sysctl_size = sizeof(int); - scn.sysctl_num = TEST_DYNAMIC; - scn.sysctl_idata = 777; - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - oldlen = sizeof(newscn); - if (sysctl(mib, 3, &newscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(newscn)) e(0); - - memcpy(&tmpscn, &scn, sizeof(scn)); - - if (newscn.sysctl_num != TEST_DYNAMIC) e(0); - if (SYSCTL_TYPE(newscn.sysctl_flags) != CTLTYPE_INT) e(0); - if (SYSCTL_FLAGS(newscn.sysctl_flags) != - (CTLFLAG_READONLY | CTLFLAG_IMMEDIATE)) e(0); - if (SYSCTL_VERS(newscn.sysctl_flags) != SYSCTL_VERSION) e(0); - if (strcmp(newscn.sysctl_name, "dynamic")) e(0); - if (newscn.sysctl_ver == 0) e(0); - if (newscn.sysctl_size != sizeof(int)) e(0); - if (newscn.sysctl_idata != 777) e(0); - - /* Can we also read its value? */ - mib[2] = TEST_DYNAMIC; - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != 777) e(0); - - /* For now, we assume that basic node destruction works. */ - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Try some variants of invalid new node data. */ - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn) - 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn) + 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, bad_ptr, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - /* Try with an invalid flags field. */ - scn.sysctl_flags = - (scn.sysctl_flags & ~SYSCTL_VERS_MASK) | SYSCTL_VERS_0; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags &= ~SYSCTL_TYPEMASK; /* type 0 does not exist */ - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - for (i = 0; i < __arraycount(badflags); i++) { - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= badflags[i]; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(i); - if (errno != EINVAL) e(i); - } - - /* Try successful creation (and destruction) once more. */ - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Try a combination of most valid flags. */ - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags &= ~CTLFLAG_READONLY; /* noop */ - scn.sysctl_flags |= CTLFLAG_READWRITE | CTLFLAG_ANYWRITE | - CTLFLAG_PRIVATE | CTLFLAG_HEX | CTLFLAG_HIDDEN | CTLFLAG_UNSIGNED; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Try invalid integer sizes. We will get to other types in a bit. */ - for (i = 0; i < __arraycount(badintsizes); i++) { - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_size = badintsizes[i]; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(i); - if (errno != EINVAL) e(i); - } - - /* - * For the value, we can supply IMMEDIATE, OWNDATA, or neither. For - * IMMEDIATE, the integer value is taken directly from sysctl_idata. - * If OWNDATA is set, sysctl_data may be set, in which case the integer - * value is copied in from there. If sysctl_data is NULL, the integer - * is initalized to zero. If neither flag is set, sysctl_data must be - * NULL, since we do not support kernel addresses, and the integer will - * similarly be initialized to zero. If both flags are set, the call - * fails with EINVAL. - */ - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_OWNDATA; /* both flags are now set */ - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags &= ~(CTLFLAG_IMMEDIATE | CTLFLAG_OWNDATA); - scn.sysctl_data = &i; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = NULL; - oldlen = sizeof(newscn); - if (sysctl(mib, 3, &newscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(newscn)) e(0); - if (newscn.sysctl_flags & CTLFLAG_IMMEDIATE) e(0); - if (!(newscn.sysctl_flags & CTLFLAG_OWNDATA)) e(0); /* auto-set */ - if (newscn.sysctl_idata != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - scn.sysctl_flags |= CTLFLAG_OWNDATA; - scn.sysctl_data = NULL; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - i = -1; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - scn.sysctl_data = bad_ptr; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - i = 999; - scn.sysctl_data = (void *)&i; - oldlen = sizeof(newscn); - if (sysctl(mib, 3, &newscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(newscn)) e(0); - if ((newscn.sysctl_flags & (CTLFLAG_IMMEDIATE | CTLFLAG_OWNDATA)) != - CTLFLAG_OWNDATA) e(0); - if (newscn.sysctl_idata != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != 999) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* The user may never supply a function pointer or a parent. */ - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_func = (sysctlfn)test87c; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_parent = &scn; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* Test some good and bad node names. */ - for (i = 0; i < __arraycount(goodnames); i++) { - memcpy(&scn, &tmpscn, sizeof(scn)); - len = strlen(goodnames[i]); - memcpy(scn.sysctl_name, goodnames[i], len); - memset(&scn.sysctl_name[len], 0, SYSCTL_NAMELEN - len); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(i); - } - - for (i = 0; i < __arraycount(badnames); i++) { - memcpy(&scn, &tmpscn, sizeof(scn)); - len = strlen(badnames[i]); - memcpy(scn.sysctl_name, badnames[i], len); - memset(&scn.sysctl_name[len], 0, SYSCTL_NAMELEN - len); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(i); - if (errno != EINVAL) e(i); - } - - /* - * Check for ID and name conflicts with existing nodes, starting with - * the basics. - */ - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EEXIST) e(0); - - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EEXIST) e(0); - if (oldlen != sizeof(oldscn)) e(0); - if (oldscn.sysctl_ver == 0) e(0); - oldscn.sysctl_ver = 0; - if (memcmp(&oldscn, &tmpscn, sizeof(oldscn))) e(0); - - oldlen = sizeof(oldscn) - 1; - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EEXIST) e(0); /* ..we should not get ENOMEM now */ - if (oldlen != sizeof(oldscn)) e(0); - - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, bad_ptr, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EEXIST) e(0); /* ..we should not get EFAULT now */ - if (oldlen != 0) e(0); /* this is arguably an implementation detail */ - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test ID and name conflicts against static nodes. */ - if (create_node(mib, 2, &tmpscn, TEST_INT, "dynamic", TEST_INT, - "int") != -1) e(0); - if (create_node(mib, 2, &tmpscn, TEST_SECRET, "dynamic", TEST_SECRET, - "secret") != -1) e(0); - if (create_node(mib, 2, &tmpscn, TEST_DYNAMIC, "quad", TEST_QUAD, - "quad") != -1) e(0); - - if (create_node(mib, 2, &tmpscn, TEST_DYNAMIC, "dynamic", -1, - NULL) != TEST_DYNAMIC) e(0); - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test unique ID generation and LL back insertion. */ - if ((id[0] = create_node(mib, 2, &tmpscn, CTL_CREATE, "id0", -1, - NULL)) == -1) e(0); - if ((id[1] = create_node(mib, 2, &tmpscn, CTL_CREATE, "id1", -1, - NULL)) == -1) e(0); - if ((id[2] = create_node(mib, 2, &tmpscn, CTL_CREATE, "id2", -1, - NULL)) == -1) e(0); - if (id[0] < CREATE_BASE || id[1] < CREATE_BASE || id[2] < CREATE_BASE) - e(0); - if (id[0] == id[1] || id[1] == id[2] || id[0] == id[2]) e(0); - - if (destroy_node(mib, 2, id[1]) != 0) e(0); - - /* Test ID and name conflicts against dynamic nodes. */ - if (create_node(mib, 2, &tmpscn, id[0], "id1", id[0], - "id0") != -1) e(0); - if (create_node(mib, 2, &tmpscn, id[2], "id1", id[2], - "id2") != -1) e(0); - if (create_node(mib, 2, &tmpscn, id[1], "id0", id[0], - "id0") != -1) e(0); - if (create_node(mib, 2, &tmpscn, id[1], "id2", id[2], - "id2") != -1) e(0); - - /* Test name conflicts before and after LL insertion point. */ - if (create_node(mib, 2, &tmpscn, CTL_CREATE, "id0", id[0], - "id0") != -1) e(0); - if (create_node(mib, 2, &tmpscn, CTL_CREATE, "id2", id[2], - "id2") != -1) e(0); - - /* Test recreation by ID and LL middle insertion. */ - if (create_node(mib, 2, &tmpscn, id[1], "id1", -1, NULL) == -1) e(0); - if (destroy_node(mib, 2, id[1]) != 0) e(0); - - /* Test dynamic recreation and more LL middle insertion. */ - if ((id[1] = create_node(mib, 2, &tmpscn, CTL_CREATE, "id1", -1, - NULL)) == -1) e(0); - if (id[1] < CREATE_BASE) e(0); - if (id[1] == id[0] || id[1] == id[2]) e(0); - - /* Test LL front insertion. */ - if (create_node(mib, 2, &tmpscn, TEST_DYNAMIC, "dynamic", -1, - NULL) == -1) e(0); - - /* Ensure that all dynamic nodes show up in a query. */ - mib[2] = CTL_QUERY; - oldlen = sizeof(scnset); - memset(seen, 0, sizeof(seen)); - memset(scnset, 0, sizeof(scnset)); - if (sysctl(mib, 3, scnset, &oldlen, NULL, 0) != 0) e(0); - if (oldlen % sizeof(scn)) e(0); - for (i = 0; (unsigned int)i < oldlen / sizeof(scn); i++) { - if (scnset[i].sysctl_num == TEST_INT) { - if (strcmp(scnset[i].sysctl_name, "int")) e(0); - seen[0]++; - } else if (scnset[i].sysctl_num == TEST_DYNAMIC) { - if (strcmp(scnset[i].sysctl_name, "dynamic")) e(0); - seen[1]++; - } else if (scnset[i].sysctl_num == id[0]) { - if (strcmp(scnset[i].sysctl_name, "id0")) e(0); - seen[2]++; - } else if (scnset[i].sysctl_num == id[1]) { - if (strcmp(scnset[i].sysctl_name, "id1")) e(0); - seen[3]++; - } else if (scnset[i].sysctl_num == id[2]) { - if (strcmp(scnset[i].sysctl_name, "id2")) e(0); - seen[4]++; - } - } - for (i = 0; i < 5; i++) - if (seen[i] != 1) e(i); - - /* Compare the parent's statistics with those obtained earlier. */ - if (query_node(mib, 1, MINIX_TEST, &scn) != 0) e(0); - if (scn.sysctl_clen != pscn.sysctl_clen + 4) e(0); - if (scn.sysctl_csize != pscn.sysctl_csize + 4) e(0); - - /* Clean up. */ - if (destroy_node(mib, 2, id[0]) != 0) e(0); - if (destroy_node(mib, 2, id[1]) != 0) e(0); - if (destroy_node(mib, 2, id[2]) != 0) e(0); - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Copy-out errors should not result in the node not being created. */ - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - oldlen = sizeof(newscn) - 1; - if (sysctl(mib, 3, &newscn, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != sizeof(newscn)) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - oldlen = sizeof(newscn); - if (sysctl(mib, 3, bad_ptr, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - oldlen = sizeof(newscn) + 1; - if (sysctl(mib, 3, &newscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(newscn)) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * Now that we are done with the integer template, try other data - * types, starting with booleans. A big part of these tests is that - * the creation results in a usable node, regardless of the way its - * contents were initialized. - */ - tmpscn.sysctl_flags = - SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_BOOL; - tmpscn.sysctl_size = sizeof(b); - tmpscn.sysctl_data = NULL; - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - b = true; - if (sysctl(mib, 3, NULL, NULL, &b, sizeof(b)) != 0) e(0); - - oldlen = sizeof(b); - b = false; - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != true) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_IMMEDIATE; - scn.sysctl_bdata = true; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != true) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - scn.sysctl_bdata = false; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_data = &b; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags |= CTLFLAG_OWNDATA; - scn.sysctl_size++; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_size--; - scn.sysctl_data = bad_ptr; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - b = true; - scn.sysctl_data = &b; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != true) e(0); - - b = false; - oldlen = sizeof(b); - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != true) e(0); - - b = false; - if (sysctl(mib, 3, NULL, NULL, &b, sizeof(b)) != 0) e(0); - - oldlen = sizeof(b); - b = true; - if (sysctl(mib, 3, &b, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(b)) e(0); - if (b != false) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test quads next. */ - tmpscn.sysctl_flags = - SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_QUAD; - tmpscn.sysctl_size = sizeof(q); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(q); - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != 0) e(0); - - q = ~0ULL; - if (sysctl(mib, 3, NULL, NULL, &q, sizeof(q)) != 0) e(0); - - oldlen = sizeof(q); - q = 0; - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != ~0ULL) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_IMMEDIATE; - scn.sysctl_qdata = 1ULL << 48; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(q); - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != (1ULL << 48)) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_data = &q; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags |= CTLFLAG_OWNDATA; - scn.sysctl_size <<= 1; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_size >>= 1; - scn.sysctl_data = bad_ptr; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - q = 123ULL << 31; - scn.sysctl_data = &q; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(q); - if (sysctl(mib, 3, &q, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(q)) e(0); - if (q != (123ULL << 31)) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test strings. */ - tmpscn.sysctl_flags = - SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_STRING; - tmpscn.sysctl_size = 7; - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_data = buf; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = NULL; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 1) e(0); - if (buf[0] != '\0') e(0); - if (buf[1] != 0x7f) e(0); - - if (sysctl(mib, 3, NULL, NULL, "woobie!", 8) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, "woobie!", 7) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, "woobie", 7) != 0) e(0); - - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 7) e(0); - if (strcmp(buf, "woobie")) e(0); - if (buf[7] != 0x7f) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_IMMEDIATE; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_size = 0; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = buf; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_size = (size_t)SSIZE_MAX + 1; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_OWNDATA; - scn.sysctl_data = bad_ptr; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_OWNDATA; - scn.sysctl_data = "abc123?"; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = "abc123"; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 7) e(0); - if (strcmp(buf, "abc123")) e(0); - if (buf[7] != 0x7f) e(0); - - if (sysctl(mib, 3, NULL, NULL, "", 1) != 0) e(0); - - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 1) e(0); - if (buf[0] != '\0') e(0); - if (buf[1] != 0x7f) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - scn.sysctl_data = ""; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 1) e(0); - if (buf[0] != '\0') e(0); - if (buf[7] != 0x7f) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * For strings, a zero node size means that the string length - * determines the buffer size. - */ - mib[2] = CTL_CREATE; - scn.sysctl_size = 0; - scn.sysctl_data = NULL; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = bad_ptr; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - scn.sysctl_data = "This is a string initializer."; /* size 29+1 */ - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != strlen(scn.sysctl_data) + 1) e(0); - if (buf[oldlen - 1] != '\0') e(0); - if (buf[oldlen] != 0x7f) e(0); - - if (query_node(mib, 2, TEST_DYNAMIC, &newscn) != 0) e(0); - if (newscn.sysctl_size != strlen(scn.sysctl_data) + 1) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test structs. */ - tmpscn.sysctl_flags = - SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_STRUCT; - tmpscn.sysctl_size = 21; - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_data = buf; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = NULL; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 21) e(0); - for (i = 0; i < 21; i++) - if (buf[i] != 0) e(i); - if (buf[i] != 0x7f) e(0); - - memset(buf, 'x', 32); - if (sysctl(mib, 3, NULL, NULL, buf, 20) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, buf, 22) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, buf, 21) != 0) e(0); - - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 21) e(0); - for (i = 0; i < 21; i++) - if (buf[i] != 'x') e(i); - if (buf[i] != 0x7f) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_IMMEDIATE; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_size = 0; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_data = buf; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_size = (size_t)SSIZE_MAX + 1; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_OWNDATA; - scn.sysctl_data = bad_ptr; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_OWNDATA; - for (i = 0; i < sizeof(buf); i++) - buf[i] = i; - scn.sysctl_data = buf; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(buf, 0x7f, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 21) e(0); - for (i = 0; i < 21; i++) - if (buf[i] != i) e(i); - if (buf[i] != 0x7f) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Finally, test node-type nodes. */ - tmpscn.sysctl_flags = - SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_NODE; - tmpscn.sysctl_size = 0; - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - scn.sysctl_flags |= CTLFLAG_IMMEDIATE; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_IMMEDIATE; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_size = sizeof(scn); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags &= ~CTLFLAG_IMMEDIATE; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags |= CTLFLAG_OWNDATA; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_csize = 8; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_clen = 1; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_child = &scn; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_parent = &scn; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_func = (sysctlfn)test87c; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (query_node(mib, 2, TEST_DYNAMIC, &scn) != 0) e(0); - if (scn.sysctl_csize != 0) e(0); - if (scn.sysctl_clen != 0) e(0); - - mib[2] = TEST_DYNAMIC; - - for (i = 3; i < CTL_MAXNAME; i++) { - memcpy(&scn, &tmpscn, sizeof(scn)); - if (i % 2) - scn.sysctl_num = i - 3; - else - scn.sysctl_num = CTL_CREATE; - /* - * Test both names with different length (depthN vs depthNN) - * and cross-directory name duplicates (depth7.depth7). - */ - snprintf(scn.sysctl_name, sizeof(scn.sysctl_name), "depth%u", - 7 + i / 2); - mib[i] = CTL_CREATE; - - oldlen = sizeof(newscn); - if (sysctl(mib, i + 1, &newscn, &oldlen, &scn, - sizeof(scn)) != 0) e(0); - mib[i] = newscn.sysctl_num; - } - - id[0] = mib[i - 1]; - mib[i - 1] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_READONLY | - CTLFLAG_OWNDATA | CTLTYPE_STRING; - scn.sysctl_num = id[0] + 1; - scn.sysctl_data = "bar"; - scn.sysctl_size = strlen(scn.sysctl_data) + 1; - strlcpy(scn.sysctl_name, "foo", sizeof(scn.sysctl_name)); - if (sysctl(mib, i, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - mib[i - 1] = id[0] + 1; - - oldlen = sizeof(buf); - if (sysctl(mib, i, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != strlen(scn.sysctl_data) + 1) e(0); - if (strcmp(buf, scn.sysctl_data)) e(0); - - if (query_node(mib, i - 2, mib[i - 2], &scn) != 0) e(0); - if (scn.sysctl_csize != 2) e(0); - if (scn.sysctl_clen != 2) e(0); - - if (query_node(mib, 2, TEST_DYNAMIC, &scn) != 0) e(0); - if (scn.sysctl_csize != 1) e(0); - if (scn.sysctl_clen != 1) e(0); - - if (destroy_node(mib, i - 1, mib[i - 1]) != 0) e(0); - mib[i - 1]--; - - for (i--; i > 2; i--) - if (destroy_node(mib, i, mib[i]) != 0) e(0); - - if (query_node(mib, 2, TEST_DYNAMIC, &scn) != 0) e(0); - if (scn.sysctl_csize != 0) e(0); - if (scn.sysctl_clen != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * Finally, ensure that unprivileged processes cannot create nodes, - * even in the most friendly place possible. - */ - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags |= CTLFLAG_ANYWRITE; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - (void)test_nonroot(sub87c); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * Now that we are done, compare the parent's statistics with those - * obtained earlier once more. There must be no differences. - */ - if (query_node(mib, 1, MINIX_TEST, &scn) != 0) e(0); - if (scn.sysctl_clen != pscn.sysctl_clen) e(0); - if (scn.sysctl_csize != pscn.sysctl_csize) e(0); - - /* Do some more path-related error code tests unrelated to the rest. */ - memcpy(&scn, &tmpscn, sizeof(scn)); - mib[1] = INT_MAX; - if (create_node(mib, 2, &scn, TEST_DYNAMIC, "d", -1, NULL) != -1) e(0); - if (errno != ENOENT) e(0); - - mib[1] = MINIX_TEST; - mib[2] = TEST_INT; - if (create_node(mib, 3, &scn, TEST_DYNAMIC, "d", -1, NULL) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = TEST_BOOL; - if (create_node(mib, 3, &scn, TEST_DYNAMIC, "d", -1, NULL) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = CTL_CREATE; - if (create_node(mib, 3, &scn, TEST_DYNAMIC, "d", -1, NULL) != -1) e(0); - if (errno != EINVAL) e(0); - - /* Finally, try to create a node in a read-only directory node. */ - mib[2] = TEST_SECRET; - if (create_node(mib, 3, &scn, -1, "d", -1, NULL) != -1) e(0); - if (errno != EPERM) e(0); -} - -/* - * Test unprivileged node destruction. - */ -static void -sub87d(void) -{ - struct sysctlnode scn; - int mib[3]; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_DESTROY; - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = TEST_ANYWRITE; - - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); - - mib[0] = CTL_DESTROY; - scn.sysctl_num = CTL_MINIX; - if (sysctl(mib, 1, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); -} - -/* - * Test sysctl(2) node destruction. - */ -static void -test87d(void) -{ - struct sysctlnode scn, oldscn, newscn, tmpscn; - size_t oldlen; - char buf[16]; - int i, r, mib[4], id[15]; - - subtest = 3; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - (void)destroy_node(mib, 2, TEST_DYNAMIC); - - /* Start with the path-related error code tests this time. */ - mib[1] = INT_MAX; - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOENT) e(0); - - mib[1] = MINIX_TEST; - mib[2] = TEST_INT; - if (destroy_node(mib, 3, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = TEST_BOOL; - if (destroy_node(mib, 3, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = CTL_DESTROY; - if (destroy_node(mib, 3, TEST_DYNAMIC) != -1) e(0); - if (errno != EINVAL) e(0); - - /* Actual API tests. */ - mib[1] = MINIX_TEST; - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_IMMEDIATE | - CTLFLAG_READONLY | CTLTYPE_INT; - scn.sysctl_size = sizeof(int); - scn.sysctl_num = TEST_DYNAMIC; - scn.sysctl_idata = 31415926; - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - memcpy(&tmpscn, &scn, sizeof(scn)); - - mib[2] = CTL_DESTROY; - if (sysctl(mib, 3, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - if (sysctl(mib, 3, NULL, NULL, bad_ptr, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERS_0; - scn.sysctl_num = TEST_DYNAMIC; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = INT_MAX; /* anything not valid */ - oldlen = sizeof(scn); - if (sysctl(mib, 3, NULL, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOENT) e(0); - if (oldlen != 0) e(0); - - scn.sysctl_num = TEST_PERM; - oldlen = sizeof(scn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); - if (oldlen != 0) e(0); - - scn.sysctl_num = TEST_SECRET; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOTEMPTY) e(0); - - scn.sysctl_num = -1; - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOENT) e(0); - - scn.sysctl_num = TEST_DYNAMIC; - strlcpy(scn.sysctl_name, "dynami", sizeof(scn.sysctl_name)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - strlcpy(scn.sysctl_name, "dynamic2", sizeof(scn.sysctl_name)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - memset(scn.sysctl_name, 'd', sizeof(scn.sysctl_name)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - if (oldscn.sysctl_ver == 0) e(0); - oldscn.sysctl_ver = 0; - if (memcmp(&oldscn, &tmpscn, sizeof(oldscn))) e(0); - - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOENT) e(0); - - /* - * We already tested destruction of one static node, by destroying - * TEST_DYNAMIC on the first run. We now do a second deletion of a - * static node, TEST_DESTROY2, to test proper adjustment of parent - * stats. We do a third static node deletion (on TEST_DESTROY1) later, - * to see that static nodes with dynamic descriptions can be freed. - */ - if (query_node(mib, 1, MINIX_TEST, &oldscn) != 0) e(0); - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = TEST_DESTROY2; - r = sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)); - if (r != 0 && r != -1) e(0); - if (r == -1 && errno != ENOENT) e(0); - - if (query_node(mib, 1, MINIX_TEST, &newscn) != 0) e(0); - - if (newscn.sysctl_csize != oldscn.sysctl_csize) e(0); - if (newscn.sysctl_clen != oldscn.sysctl_clen - !r) e(0); - - /* Try to destroy a (static) node in a read-only directory node. */ - mib[2] = TEST_SECRET; - if (destroy_node(mib, 3, SECRET_VALUE) != -1) e(0); - if (errno != EPERM) e(0); - - /* - * Errors during data copy-out of the destroyed node should not undo - * its destruction. - */ - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(i)) e(0); - if (i != 31415926) e(0); - - mib[2] = CTL_DESTROY; - oldlen = sizeof(scn); - if (sysctl(mib, 3, bad_ptr, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - mib[2] = TEST_DYNAMIC; - i = 0; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOENT) e(0); - if (oldlen != 0) e(0); - if (i != 0) e(0); - - mib[2] = CTL_CREATE; - memcpy(&scn, &tmpscn, sizeof(scn)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = CTL_DESTROY; - oldlen = sizeof(scn) - 1; - if (sysctl(mib, 3, &scn, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOMEM) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = sizeof(i); - if (sysctl(mib, 3, &i, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOENT) e(0); - - /* - * Now create and destroy a whole bunch of nodes in a subtree, mostly - * test linked list manipulation, but also to ensure that a nonempty - * tree node cannot be destroyed. - */ - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_NODE; - if (create_node(mib, 2, &scn, TEST_DYNAMIC, "dynamic", -1, NULL) == -1) - e(0); - - for (i = 0; i < 15; i++) { - snprintf(buf, sizeof(buf), "node%d", i); - if ((id[i] = create_node(mib, 3, &scn, -1, buf, -1, - NULL)) == -1) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(i); - if (errno != ENOTEMPTY) e(i); - } - - for (i = 0; i < 15; i += 2) - if (destroy_node(mib, 3, id[i]) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTEMPTY) e(0); - - for (i = 0; i < 15; i += 2) { - snprintf(buf, sizeof(buf), "node%d", i); - if ((id[i] = create_node(mib, 3, &scn, -1, buf, -1, - NULL)) == -1) e(i); - } - - for (i = 0; i < 3; i++) - if (destroy_node(mib, 3, id[i]) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTEMPTY) e(0); - - for (i = 12; i < 15; i++) - if (destroy_node(mib, 3, id[i]) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTEMPTY) e(0); - - for (i = 6; i < 9; i++) - if (destroy_node(mib, 3, id[i]) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTEMPTY) e(0); - - for (i = 3; i < 6; i++) - if (destroy_node(mib, 3, id[i]) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != -1) e(0); - if (errno != ENOTEMPTY) e(0); - - for (i = 9; i < 12; i++) - if (destroy_node(mib, 3, id[i]) != 0) e(i); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Finally, ensure that unprivileged users cannot destroy nodes. */ - (void)test_nonroot(sub87d); -} - -/* - * Get or a set the description for a particular node. Compare the results - * with the given description. Return 0 on success, or -1 on failure with - * errno set. - */ -static int -describe_node(const int * path, unsigned int pathlen, int id, - const char * desc, int set) -{ - char buf[256], *p; - struct sysctlnode scn; - struct sysctldesc *scd; - size_t oldlen; - int mib[CTL_MAXNAME]; - - if (pathlen >= CTL_MAXNAME) e(0); - memcpy(mib, path, sizeof(mib[0]) * pathlen); - mib[pathlen] = CTL_DESCRIBE; - - memset(&scn, 0, sizeof(scn)); - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = id; - if (set) - scn.sysctl_desc = desc; - if (sysctl(mib, pathlen + 1, buf, &oldlen, &scn, sizeof(scn)) != 0) - return -1; - - scd = (struct sysctldesc *)buf; - if (scd->descr_num != id) e(0); - if (scd->descr_ver == 0) e(0); - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scd->descr_str) + 1) e(0); - if (strcmp(scd->descr_str, desc)) e(0); - if (oldlen != (size_t)((char *)NEXT_DESCR(scd) - buf)) e(0); - for (p = scd->descr_str + scd->descr_len; p != &buf[oldlen]; p++) - if (*p != '\0') e(0); - return 0; -} - -/* - * Test getting descriptions from an unprivileged process. - */ -static void -sub87e(void) -{ - static char buf[2048]; - char seen[32], *p; - struct sysctldesc *scd, *endscd; - size_t oldlen; - int mib[4]; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_DESCRIBE; - - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - - scd = (struct sysctldesc *)buf; - endscd = (struct sysctldesc *)&buf[oldlen]; - memset(seen, 0, sizeof(seen)); - - while (scd < endscd) { - if (scd->descr_num >= __arraycount(seen)) e(0); - if (seen[scd->descr_num]++) e(0); - - if (scd->descr_ver == 0) e(0); - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scd->descr_str) + 1) e(0); - - p = scd->descr_str + scd->descr_len; - while (p != (char *)NEXT_DESCR(scd)) - if (*p++ != '\0') e(0); - - scd = NEXT_DESCR(scd); - } - if (scd != endscd) e(0); - - if (!seen[TEST_INT]) e(0); - if (!seen[TEST_BOOL]) e(0); - if (!seen[TEST_QUAD]) e(0); - if (!seen[TEST_STRING]) e(0); - if (!seen[TEST_STRUCT]) e(0); - if (seen[TEST_PRIVATE]) e(0); - if (!seen[TEST_ANYWRITE]) e(0); - if (seen[TEST_SECRET]) e(0); - if (!seen[TEST_PERM]) e(0); - - if (describe_node(mib, 2, TEST_INT, "Value test field", 0) != 0) e(0); - if (describe_node(mib, 2, TEST_PRIVATE, "", 0) != -1) e(0); - if (errno != EPERM) e(0); - if (describe_node(mib, 2, TEST_SECRET, "", 0) != -1) e(0); - if (errno != EPERM) e(0); - if (describe_node(mib, 2, TEST_PERM, "", 0) != 0) e(0); - - mib[2] = TEST_SECRET; - mib[3] = CTL_DESCRIBE; - if (sysctl(mib, 3, NULL, NULL, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); - - if (describe_node(mib, 3, SECRET_VALUE, "", 0) != -1) e(0); - if (errno != EPERM) e(0); -} - -/* - * Test sysctl(2) node descriptions, part 1: getting descriptions. - */ -static void -test87e(void) -{ - static char buf[2048]; - char seen[32], *p; - struct sysctldesc *scd, *endscd; - struct sysctlnode scn; - size_t oldlen, len, sublen; - int mib[4]; - - subtest = 4; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_DESCRIBE; - memset(&scn, 0, sizeof(scn)); - - /* Start with tests for getting a description listing. */ - if (sysctl(mib, 3, NULL, NULL, NULL, 0) != 0) e(0); - - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - len = oldlen; - - memset(buf, 0, sizeof(buf)); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != len) e(0); - - scd = (struct sysctldesc *)buf; - endscd = (struct sysctldesc *)&buf[len]; - memset(seen, 0, sizeof(seen)); - - sublen = (size_t)((char *)NEXT_DESCR(scd) - buf); - - while (scd < endscd) { - if (scd->descr_num >= __arraycount(seen)) e(0); - if (seen[scd->descr_num]++) e(0); - - if (scd->descr_ver == 0) e(0); - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scd->descr_str) + 1) e(0); - - /* - * This is not supposed to be complete. We test different - * string lengths, private fields, and empty descriptions. - */ - switch (scd->descr_num) { - case TEST_INT: - if (strcmp(scd->descr_str, "Value test field")) e(0); - break; - case TEST_BOOL: - if (strcmp(scd->descr_str, "Boolean test field")) e(0); - break; - case TEST_QUAD: - if (strcmp(scd->descr_str, "Quad test field")) e(0); - break; - case TEST_STRING: - if (strcmp(scd->descr_str, "String test field")) e(0); - break; - case TEST_PRIVATE: - if (strcmp(scd->descr_str, "Private test field")) e(0); - break; - case TEST_SECRET: - if (strcmp(scd->descr_str, "Private subtree")) e(0); - break; - case TEST_PERM: - if (strcmp(scd->descr_str, "")) e(0); - break; - } - - /* - * If there are padding bytes, they must be zero, whether it is - * because we set them or the MIB service copied out zeroes. - */ - p = scd->descr_str + scd->descr_len; - while (p != (char *)NEXT_DESCR(scd)) - if (*p++ != '\0') e(0); - - scd = NEXT_DESCR(scd); - } - if (scd != endscd) e(0); - - if (!seen[TEST_INT]) e(0); - if (!seen[TEST_BOOL]) e(0); - if (!seen[TEST_QUAD]) e(0); - if (!seen[TEST_STRING]) e(0); - if (!seen[TEST_STRUCT]) e(0); - if (!seen[TEST_PRIVATE]) e(0); - if (!seen[TEST_ANYWRITE]) e(0); - if (!seen[TEST_SECRET]) e(0); - if (!seen[TEST_PERM]) e(0); - - memset(buf, 0, sizeof(buf)); - oldlen = sublen; - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != -1) e(0); - if (errno != ENOMEM) e(0); - - scd = (struct sysctldesc *)buf; - if (scd->descr_num != TEST_INT) e(0); - if (scd->descr_ver == 0) e(0); - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scd->descr_str) + 1) e(0); - if (strcmp(scd->descr_str, "Value test field")) e(0); - - /* Next up, tests for getting a particular node's description. */ - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, bad_ptr, &oldlen, NULL, 0) != -1) e(0); - if (errno != EFAULT) e(0); - - if (sysctl(mib, 3, NULL, NULL, bad_ptr, sizeof(scn) - 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, bad_ptr, sizeof(scn) + 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (sysctl(mib, 3, NULL, NULL, bad_ptr, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERS_0; - scn.sysctl_num = INT_MAX; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_flags = SYSCTL_VERSION; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOENT) e(0); - - scn.sysctl_num = TEST_BOOL; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - oldlen = sizeof(buf); - scn.sysctl_num = TEST_INT; - if (sysctl(mib, 3, bad_ptr, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - oldlen = sublen - 1; - scn.sysctl_num = TEST_INT; - if (sysctl(mib, 3, buf, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != sublen) e(0); - - if (describe_node(mib, 2, TEST_INT, "Value test field", 0) != 0) e(0); - if (describe_node(mib, 2, TEST_QUAD, "Quad test field", 0) != 0) e(0); - if (describe_node(mib, 2, TEST_PRIVATE, "Private test field", - 0) != 0) e(0); - if (describe_node(mib, 2, TEST_SECRET, "Private subtree", - 0) != 0) e(0); - if (describe_node(mib, 2, TEST_PERM, "", 0) != 0) e(0); - - /* - * Make sure that unprivileged users cannot access privileged nodes' - * descriptions. It doesn't sound too bad to me if they could, but - * these are apparently the rules.. - */ - (void)test_nonroot(sub87e); - - /* Do some more path-related error code tests unrelated to the rest. */ - mib[1] = INT_MAX; - if (describe_node(mib, 2, TEST_DYNAMIC, "", 0) != -1) e(0); - if (errno != ENOENT) e(0); - - mib[1] = MINIX_TEST; - mib[2] = TEST_INT; - if (describe_node(mib, 3, TEST_DYNAMIC, "", 0) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = TEST_BOOL; - if (describe_node(mib, 3, TEST_DYNAMIC, "", 0) != -1) e(0); - if (errno != ENOTDIR) e(0); - - mib[2] = CTL_DESCRIBE; - if (describe_node(mib, 3, TEST_DYNAMIC, "", 0) != -1) e(0); - if (errno != EINVAL) e(0); -} - -/* - * Test setting descriptions from an unprivileged process. - */ -static void -sub87f(void) -{ - struct sysctlnode scn; - int mib[3]; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_DESCRIBE; - - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = TEST_DYNAMIC; - scn.sysctl_desc = "Description."; - - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); -} - -/* - * Test sysctl(2) node descriptions, part 2: setting descriptions. - */ -static void -test87f(void) -{ - static char buf[2048]; - char seen, *p; - struct sysctlnode scn, tmpscn, scnset[3]; - struct sysctldesc *scd, *endscd, *scdset[2]; - size_t oldlen, len; - int i, r, mib[4], id[2]; - - subtest = 5; - - /* - * All tests that experiment with dynamic nodes must start with trying - * to destroy the TEST_DYNAMIC node first, as tests may be run - * individually, and this node exists as a static node after booting. - */ - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - (void)destroy_node(mib, 2, TEST_DYNAMIC); - - /* - * First try setting and retrieving the description of a dynamic node - * in a directory full of static nodes. - */ - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_IMMEDIATE | - CTLFLAG_READONLY | CTLTYPE_INT; - scn.sysctl_size = sizeof(int); - scn.sysctl_num = TEST_DYNAMIC; - scn.sysctl_idata = 27182818; - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - memcpy(&tmpscn, &scn, sizeof(tmpscn)); - - /* We should get an empty description for the node in a listing. */ - mib[2] = CTL_DESCRIBE; - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - - scd = (struct sysctldesc *)buf; - endscd = (struct sysctldesc *)&buf[oldlen]; - seen = 0; - - while (scd < endscd) { - if (scd->descr_num == TEST_DYNAMIC) { - if (seen++) e(0); - - if (scd->descr_len != 1) e(0); - if (scd->descr_str[0] != '\0') e(0); - } - - if (scd->descr_ver == 0) e(0); - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scd->descr_str) + 1) e(0); - - p = scd->descr_str + scd->descr_len; - while (p != (char *)NEXT_DESCR(scd)) - if (*p++ != '\0') e(0); - - scd = NEXT_DESCR(scd); - } - if (scd != endscd) e(0); - - if (!seen) e(0); - - /* We should get an empty description quering the node directly. */ - if (describe_node(mib, 2, TEST_DYNAMIC, "", 0) != 0) e(0); - - /* Attempt to set a description with a bad description pointer. */ - if (describe_node(mib, 2, TEST_DYNAMIC, bad_ptr, 1) != -1) e(0); - if (errno != EFAULT) e(0); - - /* Attempt to set a description that is longer than allowed. */ - memset(buf, 'A', sizeof(buf) - 1); - buf[sizeof(buf) - 1] = '\0'; - if (describe_node(mib, 2, TEST_DYNAMIC, buf, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - /* Now actually set a description. */ - if (describe_node(mib, 2, TEST_DYNAMIC, "Dynamic node", 1) != 0) e(0); - len = strlen("Dynamic node") + 1; - - /* We should get the new description for the node in a listing. */ - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, NULL, 0) != 0) e(0); - - scd = (struct sysctldesc *)buf; - endscd = (struct sysctldesc *)&buf[oldlen]; - seen = 0; - - while (scd < endscd) { - if (scd->descr_num == TEST_DYNAMIC) { - if (seen++) e(0); - - if (scd->descr_len != len) e(0); - if (strcmp(scd->descr_str, "Dynamic node")) e(0); - } - - if (scd->descr_ver == 0) e(0); - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scd->descr_str) + 1) e(0); - - p = scd->descr_str + scd->descr_len; - while (p != (char *)NEXT_DESCR(scd)) - if (*p++ != '\0') e(0); - - scd = NEXT_DESCR(scd); - } - if (scd != endscd) e(0); - - if (!seen) e(0); - - /* We should get the new description quering the node directly. */ - if (describe_node(mib, 2, TEST_DYNAMIC, "Dynamic node", 0) != 0) e(0); - - mib[2] = CTL_DESCRIBE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERS_0; - scn.sysctl_num = TEST_INT; - scn.sysctl_desc = "Test description"; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* It is not possible to replace an existing static description. */ - scn.sysctl_flags = SYSCTL_VERSION; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); - - /* Nonexistent nodes cannot be given a description. */ - scn.sysctl_num = INT_MAX; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOENT) e(0); - - /* It is not possible to replace an existing dynamic description. */ - scn.sysctl_num = TEST_DYNAMIC; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); - - /* It is not possible to set a description on a permanent node. */ - scn.sysctl_num = TEST_PERM; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EPERM) e(0); - - /* Verify that TEST_DYNAMIC now has CTLFLAG_OWNDESC set. */ - if (query_node(mib, 2, TEST_DYNAMIC, &scn) != 0) e(0); - if (!(scn.sysctl_flags & CTLFLAG_OWNDESC)) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * Set a description on a static node, ensure that CTLFLAG_OWNDESC is - * set, and then destroy the static node. This should still free the - * memory allocated for the description. We cannot test whether the - * memory is really freed, but at least we can trigger this case at - * all, and leave the rest up to memory checkers or whatever. Since we - * destroy the static node, we can not do this more than once, and thus - * we skip this test if the static node does not exist. - */ - r = describe_node(mib, 2, TEST_DESTROY1, "Destroy me", 1); - - if (r == -1 && errno != ENOENT) e(0); - else if (r == 0) { - if (query_node(mib, 2, TEST_DESTROY1, &scn) != 0) e(0); - if (!(scn.sysctl_flags & CTLFLAG_OWNDESC)) e(0); - - if (describe_node(mib, 2, TEST_DESTROY1, "Destroy me", 0) != 0) - e(0); - - if (destroy_node(mib, 2, TEST_DESTROY1) != 0) e(0); - } - - /* - * Test queries and description listings in subtrees. - */ - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_NODE; - scn.sysctl_num = TEST_DYNAMIC; - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - scn.sysctl_desc = "This will not be set."; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - /* Setting sysctl_desc should have no effect during creation. */ - if (describe_node(mib, 2, TEST_DYNAMIC, "", 0) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - id[0] = create_node(mib, 3, &tmpscn, CTL_CREATE, "NodeA", -1, NULL); - if (id[0] < 0) e(0); - id[1] = create_node(mib, 3, &tmpscn, CTL_CREATE, "NodeB", -1, NULL); - if (id[1] < 0) e(0); - if (id[0] == id[1]) e(0); - - mib[3] = CTL_QUERY; - oldlen = sizeof(scnset); - if (sysctl(mib, 4, scnset, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(scnset[0]) * 2) e(0); - i = (scnset[0].sysctl_num != id[0]); - if (scnset[i].sysctl_num != id[0]) e(0); - if (scnset[1 - i].sysctl_num != id[1]) e(0); - if (scnset[i].sysctl_flags & CTLFLAG_OWNDESC) e(0); - if (scnset[1 - i].sysctl_flags & CTLFLAG_OWNDESC) e(0); - - mib[3] = CTL_DESCRIBE; - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 4, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - - scdset[0] = (struct sysctldesc *)buf; - scdset[1] = NEXT_DESCR(scdset[0]); - if ((char *)NEXT_DESCR(scdset[1]) != &buf[oldlen]) e(0); - i = (scdset[0]->descr_num != id[0]); - if (scdset[i]->descr_num != id[0]) e(0); - if (scdset[i]->descr_ver == 0) e(0); - if (scdset[i]->descr_len != 1) e(0); - if (scdset[i]->descr_str[0] != '\0') e(0); - if (scdset[1 - i]->descr_num != id[1]) e(0); - if (scdset[1 - i]->descr_ver == 0) e(0); - if (scdset[1 - i]->descr_len != 1) e(0); - if (scdset[1 - i]->descr_str[0] != '\0') e(0); - - if (describe_node(mib, 3, id[0], "Description A", 1) != 0) e(0); - - mib[3] = CTL_QUERY; - oldlen = sizeof(scnset); - if (sysctl(mib, 4, scnset, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(scnset[0]) * 2) e(0); - i = (scnset[0].sysctl_num != id[0]); - if (scnset[i].sysctl_num != id[0]) e(0); - if (scnset[1 - i].sysctl_num != id[1]) e(0); - if (!(scnset[i].sysctl_flags & CTLFLAG_OWNDESC)) e(0); - if (scnset[1 - i].sysctl_flags & CTLFLAG_OWNDESC) e(0); - - mib[3] = CTL_DESCRIBE; - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 4, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - - scdset[0] = (struct sysctldesc *)buf; - scdset[1] = NEXT_DESCR(scdset[0]); - if ((char *)NEXT_DESCR(scdset[1]) != &buf[oldlen]) e(0); - i = (scdset[0]->descr_num != id[0]); - if (scdset[i]->descr_num != id[0]) e(0); - if (scdset[i]->descr_ver == 0) e(0); - if (strcmp(scdset[i]->descr_str, "Description A")) e(0); - if (scdset[i]->descr_len != strlen(scdset[i]->descr_str) + 1) e(0); - if (scdset[1 - i]->descr_num != id[1]) e(0); - if (scdset[1 - i]->descr_ver == 0) e(0); - if (scdset[1 - i]->descr_len != 1) e(0); - if (scdset[1 - i]->descr_str[0] != '\0') e(0); - - if (describe_node(mib, 3, id[1], "Description B", 1) != 0) e(0); - - mib[3] = CTL_QUERY; - oldlen = sizeof(scnset); - if (sysctl(mib, 4, scnset, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(scnset[0]) * 2) e(0); - i = (scnset[0].sysctl_num != id[0]); - if (scnset[i].sysctl_num != id[0]) e(0); - if (scnset[1 - i].sysctl_num != id[1]) e(0); - if (!(scnset[i].sysctl_flags & CTLFLAG_OWNDESC)) e(0); - if (!(scnset[1 - i].sysctl_flags & CTLFLAG_OWNDESC)) e(0); - - mib[3] = CTL_DESCRIBE; - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 4, buf, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - - scdset[0] = (struct sysctldesc *)buf; - scdset[1] = NEXT_DESCR(scdset[0]); - if ((char *)NEXT_DESCR(scdset[1]) != &buf[oldlen]) e(0); - i = (scdset[0]->descr_num != id[0]); - if (scdset[i]->descr_num != id[0]) e(0); - if (scdset[i]->descr_ver == 0) e(0); - if (strcmp(scdset[i]->descr_str, "Description A")) e(0); - if (scdset[i]->descr_len != strlen(scdset[i]->descr_str) + 1) e(0); - if (scdset[1 - i]->descr_num != id[1]) e(0); - if (scdset[1 - i]->descr_ver == 0) e(0); - if (strcmp(scdset[1 - i]->descr_str, "Description B")) e(0); - if (scdset[1 - i]->descr_len != strlen(scdset[1 - i]->descr_str) + 1) - e(0); - - if (destroy_node(mib, 3, id[0]) != 0) e(0); - if (destroy_node(mib, 3, id[1]) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * Test that the resulting description is copied out after setting it, - * and that copy failures do not undo the description getting set. - */ - if (create_node(mib, 2, &tmpscn, TEST_DYNAMIC, "dynamic", -1, - NULL) == -1) e(0); - - mib[2] = CTL_DESCRIBE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = TEST_DYNAMIC; - scn.sysctl_desc = "Testing.."; - memset(buf, 0, sizeof(buf)); - oldlen = sizeof(buf); - if (sysctl(mib, 3, buf, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen == 0) e(0); - len = oldlen; - - scd = (struct sysctldesc *)buf; - if (scd->descr_str[scd->descr_len - 1] != '\0') e(0); - if (scd->descr_len != strlen(scn.sysctl_desc) + 1) e(0); - if (strcmp(scd->descr_str, scn.sysctl_desc)) e(0); - if (oldlen != (size_t)((char *)NEXT_DESCR(scd) - buf)) e(0); - p = scd->descr_str + scd->descr_len; - while (p != (char *)NEXT_DESCR(scd)) - if (*p++ != '\0') e(0); - - if (describe_node(mib, 2, TEST_DYNAMIC, "Testing..", 0) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - if (create_node(mib, 2, &tmpscn, TEST_DYNAMIC, "dynamic", -1, - NULL) == -1) e(0); - - memset(buf, 0, sizeof(buf)); - oldlen = len - 1; - if (sysctl(mib, 3, buf, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != ENOMEM) e(0); - if (oldlen != len) e(0); - - if (describe_node(mib, 2, TEST_DYNAMIC, "Testing..", 0) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - if (create_node(mib, 2, &tmpscn, TEST_DYNAMIC, "dynamic", -1, - NULL) == -1) e(0); - - memset(buf, 0, sizeof(buf)); - oldlen = len; - if (sysctl(mib, 3, bad_ptr, &oldlen, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - if (describe_node(mib, 2, TEST_DYNAMIC, "Testing..", 0) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Finally, ensure that unprivileged users cannot set descriptions. */ - memcpy(&scn, &tmpscn, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_IMMEDIATE | - CTLFLAG_READWRITE | CTLFLAG_ANYWRITE | CTLTYPE_INT; - if (create_node(mib, 2, &scn, TEST_DYNAMIC, "dynamic", -1, - NULL) == -1) e(0); - - (void)test_nonroot(sub87f); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); -} - -/* - * Set or test buffer contents. When setting, the buffer is filled with a - * sequence of bytes that is a) free of null characters and b) likely to cause - * detection of wrongly copied subsequences. When testing, for any size up to - * the size used to set the buffer contents, 0 is returned if the buffer - * contents match expectations, or -1 if they do not. - */ -static int -test_buf(char * buf, unsigned char c, size_t size, int set) -{ - unsigned char *ptr; - int step; - - ptr = (unsigned char *)buf; - - for (step = 1; size > 0; size--) { - if (set) - *ptr++ = c; - else if (*ptr++ != c) - return -1; - - c += step; - if (c == 0) { - if (++step == 256) - step = 1; - c += step; - } - } - - return 0; -} - -/* - * Test large data sizes from an unprivileged process. - */ -static void -sub87g(void) -{ - char *ptr; - size_t size, oldlen; - int id, mib[3]; - - size = getpagesize() * 3; - - if ((ptr = mmap(NULL, size, PROT_READ, MAP_ANON | MAP_PRIVATE, -1, - 0)) == MAP_FAILED) e(0); - memset(ptr, 0x2f, size); - - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = TEST_DYNAMIC; - oldlen = size - 2; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size - 2) e(0); - if (test_buf(ptr, 'D', size - 2, 0) != 0) e(0); - - /* - * Given the large data size, we currently expect this attempt to - * write to the structure to be blocked by the MIB service. - */ - if (sysctl(mib, 3, NULL, NULL, ptr, oldlen) != -1) e(0); - if (errno != EPERM) e(0); - - /* Get the ID of the second dynamic node. */ - mib[2] = TEST_ANYWRITE; - oldlen = sizeof(id); - if (sysctl(mib, 3, &id, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(id)) e(0); - if (id < 0) e(0); - - /* - * Test data size limits for strings as well, although here we can also - * ensure that we hit the right check by testing with a shorter string. - */ - mib[2] = id; - oldlen = size; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - if (test_buf(ptr, 'f', size - 1, 0) != 0) e(0); - if (ptr[size - 1] != '\0') e(0); - - test_buf(ptr, 'h', size - 1, 1); - if (sysctl(mib, 3, NULL, NULL, ptr, size) != -1) e(0); - if (errno != EPERM) e(0); - - if (sysctl(mib, 3, NULL, NULL, ptr, getpagesize() - 1) != 0) e(0); - - if (munmap(ptr, size) != 0) e(0); -} - -/* - * Test large data sizes and mid-data page faults. - */ -static void -test87g(void) -{ - struct sysctlnode scn, newscn; - char *ptr; - size_t pgsz, size, oldlen; - int id, mib[3]; - - subtest = 6; - - /* - * No need to go overboard with sizes here; it will just cause the MIB - * service's memory usage to grow - permanently. Three pages followed - * by an unmapped page is plenty for this test. - */ - pgsz = getpagesize(); - size = pgsz * 3; - - if ((ptr = mmap(NULL, size + pgsz, PROT_READ, MAP_ANON | MAP_PRIVATE, - -1, 0)) == MAP_FAILED) e(0); - if (munmap(ptr + size, pgsz) != 0) e(0); - - (void)destroy_node(mib, 2, TEST_DYNAMIC); - - /* Test string creation initializers with an accurate length. */ - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_OWNDATA | - CTLFLAG_READWRITE | CTLTYPE_STRING; - scn.sysctl_num = TEST_DYNAMIC; - scn.sysctl_data = ptr; - scn.sysctl_size = size; - strlcpy(scn.sysctl_name, "dynamic", sizeof(scn.sysctl_name)); - test_buf(ptr, 'a', size, 1); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); /* no null terminator */ - - scn.sysctl_size++; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - scn.sysctl_size--; - ptr[size - 1] = '\0'; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - - memset(ptr, 0, size); - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - if (ptr[size - 1] != '\0') e(0); - if (test_buf(ptr, 'a', size - 1, 0) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test string creation initializers with no length. */ - mib[2] = CTL_CREATE; - scn.sysctl_size = 0; - test_buf(ptr, 'b', size, 1); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - test_buf(ptr, 'b', size - 1, 1); - ptr[size - 1] = '\0'; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (query_node(mib, 2, TEST_DYNAMIC, &newscn) != 0) e(0); - if (newscn.sysctl_size != size) e(0); - - mib[2] = TEST_DYNAMIC; - if (sysctl(mib, 3, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - - memset(ptr, 0x7e, size); - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - if (ptr[size - 1] != '\0') e(0); - if (test_buf(ptr, 'b', size - 1, 0) != 0) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* - * Test string creation initializers with a length exceeding the string - * length. If the string is properly null terminated, this should not - * result in a fault. - */ - mib[2] = CTL_CREATE; - scn.sysctl_size = size; - scn.sysctl_data = &ptr[size - pgsz - 5]; - test_buf(&ptr[size - pgsz - 5], 'c', pgsz + 5, 1); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - ptr[size - 1] = '\0'; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - if (query_node(mib, 2, TEST_DYNAMIC, &newscn) != 0) e(0); - if (newscn.sysctl_size != size) e(0); - - mib[2] = TEST_DYNAMIC; - oldlen = size - pgsz - 6; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != pgsz + 5) e(0); - /* We rely on only the actual string getting copied out here. */ - if (memcmp(ptr, &ptr[size - pgsz - 5], pgsz + 5)) e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - - /* Test structure creation initializers. */ - mib[2] = CTL_CREATE; - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_OWNDATA | - CTLFLAG_ANYWRITE | CTLFLAG_READWRITE | CTLTYPE_STRUCT; - scn.sysctl_size = size - 2; - scn.sysctl_data = &ptr[3]; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EFAULT) e(0); - - scn.sysctl_data = &ptr[2]; - test_buf(&ptr[2], 'd', size - 2, 1); - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - mib[2] = TEST_DYNAMIC; - memset(ptr, 0x3b, size); - oldlen = size - 2; - if (sysctl(mib, 3, &ptr[3], &oldlen, NULL, 0) != -1) e(0); - if (errno != EFAULT) e(0); - oldlen = size - 2; - if (sysctl(mib, 3, &ptr[2], &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size - 2) e(0); - if (test_buf(&ptr[2], 'd', size - 2, 0) != 0) e(0); - - /* - * Test setting new values. We already have a structure node, so let's - * start there. - */ - test_buf(&ptr[2], 'D', size - 2, 1); - if (sysctl(mib, 3, NULL, NULL, &ptr[3], size - 2) != -1) e(0); - if (errno != EFAULT) e(0); - - /* Did the mid-data fault cause a partial update? It better not. */ - memset(ptr, 0x4c, size); - oldlen = size - 2; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size - 2) e(0); - if (test_buf(ptr, 'd', size - 2, 0) != 0) e(0); - - test_buf(&ptr[2], 'D', size - 2, 1); - if (sysctl(mib, 3, NULL, NULL, &ptr[2], size - 2) != 0) e(0); - - memset(ptr, 0x5d, size); - oldlen = size - 2; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size - 2) e(0); - if (test_buf(ptr, 'D', size - 2, 0) != 0) e(0); - - /* - * We are going to reuse TEST_DYNAMIC for the non-root test later, so - * create a new node for string tests. - */ - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_OWNDATA | - CTLFLAG_ANYWRITE | CTLFLAG_READWRITE | CTLTYPE_STRING; - scn.sysctl_num = CTL_CREATE; - scn.sysctl_size = size; - scn.sysctl_data = ptr; - test_buf(ptr, 'e', size - 1, 1); - ptr[size - 1] = '\0'; - strlcpy(scn.sysctl_name, "dynamic2", sizeof(scn.sysctl_name)); - oldlen = sizeof(newscn); - if (sysctl(mib, 3, &newscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(newscn)) e(0); - id = newscn.sysctl_num; - if (id < 0) e(0); - - /* - * Test setting a short but faulty string, ensuring that no partial - * update on the field contents takes place. - */ - mib[2] = id; - memcpy(&ptr[size - 3], "XYZ", 3); - if (sysctl(mib, 3, NULL, NULL, &ptr[size - 3], 4) != -1) e(0); - if (errno != EFAULT) e(0); - - oldlen = size; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - if (test_buf(ptr, 'e', size - 1, 0) != 0) e(0); - if (ptr[size - 1] != '\0') e(0); - - memcpy(&ptr[size - 3], "XYZ", 3); - if (sysctl(mib, 3, NULL, NULL, &ptr[size - 3], 3) != 0) e(0); - - oldlen = size; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != 4) e(0); - if (strcmp(ptr, "XYZ")) e(0); - - test_buf(&ptr[1], 'f', size - 1, 1); - if (sysctl(mib, 3, NULL, NULL, &ptr[1], size - 1) != 0) e(0); - - test_buf(&ptr[1], 'G', size - 1, 1); - if (sysctl(mib, 3, NULL, NULL, &ptr[1], size) != -1) e(0); - if (errno != EFAULT) e(0); - - oldlen = size; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != size) e(0); - if (test_buf(ptr, 'f', size - 1, 0) != 0) e(0); - if (ptr[size - 1] != '\0') e(0); - - /* - * Test descriptions as well. First, the MIB service does not allow - * for overly long descriptions, although the limit is not exposed. - * Three memory pages worth of text is way too long though. - */ - memset(ptr, 'A', size); - if (describe_node(mib, 2, id, ptr, 1) != -1) e(0); - if (errno != EINVAL) e(0); /* not EFAULT, should never get that far */ - - ptr[size - 1] = '\0'; - if (describe_node(mib, 2, id, ptr, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (describe_node(mib, 2, id, "", 0) != 0) e(0); - - /* - * Second, the description routine must deal with faults occurring - * while it is trying to find the string end. - */ - ptr[size - 2] = 'B'; - ptr[size - 1] = 'C'; - if (describe_node(mib, 2, id, &ptr[size - 3], 1) != -1) e(0); - if (errno != EFAULT) e(0); - - if (describe_node(mib, 2, id, "", 0) != 0) e(0); - - ptr[size - 1] = '\0'; - if (describe_node(mib, 2, id, &ptr[size - 3], 1) != 0) e(0); - - if (describe_node(mib, 2, id, "AB", 0) != 0) e(0); - - /* Pass the second dynamic node ID to the unprivileged child. */ - mib[2] = TEST_ANYWRITE; - if (sysctl(mib, 3, NULL, NULL, &id, sizeof(id)) != 0) e(0); - - (void)test_nonroot(sub87g); - - mib[2] = id; - oldlen = size; - if (sysctl(mib, 3, ptr, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != pgsz) e(0); - if (test_buf(ptr, 'h', pgsz - 1, 1) != 0) e(0); - if (ptr[pgsz - 1] != '\0') e(0); - - if (destroy_node(mib, 2, TEST_DYNAMIC) != 0) e(0); - if (destroy_node(mib, 2, id) != 0) e(0); - - munmap(ptr, size); -} - -/* - * Verify whether the given node on the given path has the given node version. - * Return 0 if the version matches, or -1 if it does not or a failure occurred. - */ -static int -check_version(const int * path, unsigned int pathlen, int id, uint32_t ver) -{ - struct sysctlnode scn; - struct sysctldesc scd; - size_t oldlen; - int r, mib[CTL_MAXNAME]; - - assert(pathlen < CTL_MAXNAME); - memcpy(mib, path, sizeof(mib[0]) * pathlen); - mib[pathlen] = CTL_DESCRIBE; - - /* - * For some reason, when retrieving a particular description (as - * opposed to setting one), the node version number is not checked. - * In order to test this, we deliberately pass in a node version number - * that, if checked, would eventually cause failures. - */ - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = id; - scn.sysctl_ver = 1; - oldlen = sizeof(scd); - r = sysctl(mib, pathlen + 1, &scd, &oldlen, &scn, sizeof(scn)); - if (r == -1 && errno != ENOMEM) e(0); - - return (scd.descr_ver == ver) ? 0 : -1; -} - -/* - * Test sysctl(2) node versioning. - */ -static void -test87h(void) -{ - struct sysctlnode scn, oldscn; - size_t oldlen; - uint32_t ver[4]; - int mib[4], id[4]; - - /* - * The other tests have already tested sufficiently that a zero version - * is always accepted in calls. Here, we test that node versions - * actually change when creating and destroying nodes, and that the - * right version test is implemented for all of the four node meta- - * operations (query, create, destroy, describe). Why did we not do - * this earlier, you ask? Well, versioning was implemented later on. - */ - subtest = 7; - - /* - * Test versioning with node creation. - */ - mib[0] = CTL_MINIX; - mib[1] = MINIX_TEST; - mib[2] = CTL_CREATE; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_READWRITE | CTLTYPE_NODE; - scn.sysctl_num = CTL_CREATE; - strlcpy(scn.sysctl_name, "NodeA", sizeof(scn.sysctl_name)); - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - id[0] = oldscn.sysctl_num; - ver[0] = oldscn.sysctl_ver; - if (ver[0] == 0) e(0); - - if (check_version(mib, 0, CTL_MINIX, ver[0]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[0]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[0]) != 0) e(0); - - strlcpy(scn.sysctl_name, "NodeB", sizeof(scn.sysctl_name)); - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - id[1] = oldscn.sysctl_num; - ver[1] = oldscn.sysctl_ver; - if (ver[1] == 0) e(0); - if (ver[1] != NEXT_VER(ver[0])) e(0); - - if (check_version(mib, 0, CTL_MINIX, ver[1]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[1]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[0]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[1]) != 0) e(0); - - /* A version that is too high should be rejected. */ - mib[2] = id[0]; - mib[3] = CTL_CREATE; - scn.sysctl_flags = SYSCTL_VERSION | CTLFLAG_IMMEDIATE | - CTLFLAG_READWRITE | CTLTYPE_INT; - scn.sysctl_size = sizeof(int); - scn.sysctl_ver = NEXT_VER(ver[1]); - strlcpy(scn.sysctl_name, "ValueA", sizeof(scn.sysctl_name)); - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* The version of the parent node should be accepted. */ - scn.sysctl_ver = ver[0]; /* different from the root node version */ - oldlen = sizeof(oldscn); - if (sysctl(mib, 4, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - id[2] = oldscn.sysctl_num; - ver[2] = oldscn.sysctl_ver; - if (ver[2] == 0) e(0); - if (ver[2] != NEXT_VER(ver[1])) e(0); - - if (check_version(mib, 0, CTL_MINIX, ver[2]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[2]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[2]) != 0) e(0); - if (check_version(mib, 3, id[2], ver[2]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[1]) != 0) e(0); - - /* A version that is too low (old) should be rejected. */ - mib[2] = id[1]; - - scn.sysctl_ver = ver[0]; - strlcpy(scn.sysctl_name, "ValueB", sizeof(scn.sysctl_name)); - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* The version of the root node should be accepted. */ - scn.sysctl_ver = ver[2]; /* different from the parent node version */ - oldlen = sizeof(oldscn); - if (sysctl(mib, 4, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - id[3] = oldscn.sysctl_num; - ver[3] = oldscn.sysctl_ver; - if (ver[3] == 0) e(0); - if (ver[3] != NEXT_VER(ver[2])) e(0); - - if (check_version(mib, 0, CTL_MINIX, ver[3]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[3]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[2]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[3]) != 0) e(0); - if (check_version(mib, 3, id[3], ver[3]) != 0) e(0); - mib[2] = id[0]; - if (check_version(mib, 3, id[2], ver[2]) != 0) e(0); - - /* - * Test versioning with node queries. - */ - mib[3] = CTL_QUERY; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_ver = ver[0]; /* previous parent version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_ver = ver[2]; /* parent version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - scn.sysctl_ver = ver[2]; /* root version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - scn.sysctl_ver = NEXT_VER(ver[3]); /* nonexistent version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * Test versioning with node description. - */ - mib[2] = CTL_DESCRIBE; - scn.sysctl_num = id[0]; - scn.sysctl_ver = ver[3]; /* root and parent, but not target version */ - scn.sysctl_desc = "Parent A"; - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_ver = ver[1]; /* another bad version */ - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_ver = ver[2]; /* target version */ - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - /* Neither querying nor description should have changed versions. */ - if (check_version(mib, 0, CTL_MINIX, ver[3]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[3]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[2]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[3]) != 0) e(0); - mib[2] = id[1]; - if (check_version(mib, 3, id[3], ver[3]) != 0) e(0); - mib[2] = id[0]; - if (check_version(mib, 3, id[2], ver[2]) != 0) e(0); - - /* - * Test versioning with node destruction. - */ - mib[3] = CTL_DESTROY; - memset(&scn, 0, sizeof(scn)); - scn.sysctl_flags = SYSCTL_VERSION; - scn.sysctl_num = id[2]; - scn.sysctl_ver = ver[3]; /* root but not target version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_ver = ver[2]; /* target (and parent) version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - /* Fortunately, versions are predictable. */ - ver[0] = NEXT_VER(ver[3]); - - if (check_version(mib, 0, CTL_MINIX, ver[0]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[0]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[0]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[3]) != 0) e(0); - - mib[2] = id[1]; - scn.sysctl_num = id[3]; - scn.sysctl_ver = ver[0]; /* root but not target version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_ver = ver[3]; /* target (and parent) version */ - if (sysctl(mib, 4, NULL, NULL, &scn, sizeof(scn)) != 0) e(0); - - ver[1] = NEXT_VER(ver[0]); - - if (check_version(mib, 0, CTL_MINIX, ver[1]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[1]) != 0) e(0); - if (check_version(mib, 2, id[0], ver[0]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[1]) != 0) e(0); - - mib[2] = CTL_DESTROY; - scn.sysctl_num = id[0]; - scn.sysctl_ver = ver[1]; /* root and parent, but not target version */ - if (sysctl(mib, 3, NULL, NULL, &scn, sizeof(scn)) != -1) e(0); - if (errno != EINVAL) e(0); - - scn.sysctl_ver = ver[0]; /* target version */ - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - if (oldscn.sysctl_num != id[0]) e(0); - if (oldscn.sysctl_ver != ver[0]) e(0); - - ver[2] = NEXT_VER(ver[1]); - - if (check_version(mib, 0, CTL_MINIX, ver[2]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[2]) != 0) e(0); - if (check_version(mib, 2, id[1], ver[1]) != 0) e(0); - - /* For the last destruction, just see if we get the old version. */ - scn.sysctl_num = id[1]; - scn.sysctl_ver = 0; - oldlen = sizeof(oldscn); - if (sysctl(mib, 3, &oldscn, &oldlen, &scn, sizeof(scn)) != 0) e(0); - if (oldlen != sizeof(oldscn)) e(0); - if (oldscn.sysctl_num != id[1]) e(0); - if (oldscn.sysctl_ver != ver[1]) e(0); - - ver[3] = NEXT_VER(ver[2]); - - if (check_version(mib, 0, CTL_MINIX, ver[3]) != 0) e(0); - if (check_version(mib, 1, MINIX_TEST, ver[3]) != 0) e(0); -} - -/* - * Perform pre-test initialization. - */ -static void -test87_init(void) -{ - size_t oldlen; - int mib[3]; - - subtest = 99; - - if ((bad_ptr = mmap(NULL, getpagesize(), PROT_READ, - MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) e(0); - if (munmap(bad_ptr, getpagesize()) != 0) e(0); - - mib[0] = CTL_MINIX; - mib[1] = MINIX_MIB; - mib[2] = MIB_NODES; - oldlen = sizeof(nodes); - if (sysctl(mib, 3, &nodes, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(nodes)) e(0); - - mib[2] = MIB_OBJECTS; - oldlen = sizeof(objects); - if (sysctl(mib, 3, &objects, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(objects)) e(0); -} - -/* - * Perform post-test checks. - */ -static void -test87_check(void) -{ - unsigned int newnodes, newobjects; - size_t oldlen; - int mib[3]; - - subtest = 99; - - mib[0] = CTL_MINIX; - mib[1] = MINIX_MIB; - mib[2] = MIB_NODES; - oldlen = sizeof(newnodes); - if (sysctl(mib, 3, &newnodes, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(newnodes)) e(0); - - /* - * Upon the first run, the total number of nodes must actually go down, - * as we destroy number of static nodes. Upon subsequent runs, the - * number of nodes should remain stable. Thus, we can safely test that - * the number of nodes has not gone up as a result of the test. - */ - if (newnodes > nodes) e(0); - - mib[2] = MIB_OBJECTS; - oldlen = sizeof(newobjects); - if (sysctl(mib, 3, &newobjects, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(newobjects)) e(0); - - /* - * The number of dynamically allocated objects should remain the same - * across the test. - */ - if (newobjects != objects) e(0); -} - -/* - * Test program for sysctl(2). - */ -int -main(int argc, char ** argv) -{ - int i, m; - - start(87); - - if (argc == 2) - m = atoi(argv[1]); - else - m = 0xFF; - - test87_init(); - - for (i = 0; i < ITERATIONS; i++) { - if (m & 0x001) test87a(); - if (m & 0x002) test87b(); - if (m & 0x004) test87c(); - if (m & 0x008) test87d(); - if (m & 0x010) test87e(); - if (m & 0x020) test87f(); - if (m & 0x040) test87g(); - if (m & 0x080) test87h(); - } - - test87_check(); - - quit(); -} diff --git a/minix/tests/test88.c b/minix/tests/test88.c deleted file mode 100644 index 865682769..000000000 --- a/minix/tests/test88.c +++ /dev/null @@ -1,3011 +0,0 @@ -/* Tests for System V IPC semaphores - by D.C. van Moolenbroek */ -/* This test must be run as root, as it includes permission checking tests. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -#define ITERATIONS 3 - -#define WAIT_USECS 100000 /* time for processes to get ready */ - -#define KEY_A 0x73570001 -#define KEY_B (KEY_A + 1) -#define KEY_C (KEY_A + 2) - -#define ROOT_USER "root" /* name of root */ -#define ROOT_GROUP "wheel" /* name of root's group */ -#define NONROOT_USER "bin" /* name of any unprivileged user */ -#define NONROOT_GROUP "bin" /* name of any unprivileged group */ - -enum { - DROP_NONE, - DROP_USER, - DROP_ALL, -}; - -enum { - SUGID_NONE, - SUGID_ROOT_USER, - SUGID_NONROOT_USER, - SUGID_ROOT_GROUP, - SUGID_NONROOT_GROUP, -}; - -struct link { - pid_t pid; - int sndfd; - int rcvfd; -}; - -/* - * Test semaphore properties. This is a macro, so that it prints useful line - * information if an error occurs. - */ -#define TEST_SEM(id, num, val, pid, ncnt, zcnt) do { \ - if (semctl(id, num, GETVAL) != val) e(0); \ - if (pid != -1 && semctl(id, num, GETPID) != pid) e(1); \ - if (ncnt != -1 && semctl(id, num, GETNCNT) != ncnt) e(2); \ - if (zcnt != -1 && semctl(id, num, GETZCNT) != zcnt) e(3); \ -} while (0); - -static int nr_signals = 0; - -static size_t page_size; -static char *page_ptr; -static void *bad_ptr; - -/* - * Spawn a child process, with a pair of pipes to talk to it bidirectionally. - * Drop user and group privileges in the child process if requested. - */ -static void -spawn(struct link * link, void (* proc)(struct link *), int drop) -{ - struct passwd *pw; - struct group *gr; - int up[2], dn[2]; - - fflush(stdout); - fflush(stderr); - - if (pipe(up) != 0) e(0); - if (pipe(dn) != 0) e(0); - - link->pid = fork(); - - switch (link->pid) { - case 0: - close(up[1]); - close(dn[0]); - - link->pid = getppid(); - link->rcvfd = up[0]; - link->sndfd = dn[1]; - - errct = 0; - - switch (drop) { - case DROP_ALL: - if (setgroups(0, NULL) != 0) e(0); - - if ((gr = getgrnam(NONROOT_GROUP)) == NULL) e(0); - - if (setgid(gr->gr_gid) != 0) e(0); - if (setegid(gr->gr_gid) != 0) e(0); - - /* FALLTHROUGH */ - case DROP_USER: - if ((pw = getpwnam(NONROOT_USER)) == NULL) e(0); - - if (setuid(pw->pw_uid) != 0) e(0); - } - - proc(link); - - /* Close our pipe FDs on exit, so that we can make zombies. */ - exit(errct); - case -1: - e(0); - break; - } - - close(up[0]); - close(dn[1]); - - link->sndfd = up[1]; - link->rcvfd = dn[0]; -} - -/* - * Wait for a child process to terminate, and clean up. - */ -static void -collect(struct link * link) -{ - int status; - - close(link->sndfd); - close(link->rcvfd); - - if (waitpid(link->pid, &status, 0) != link->pid) e(0); - - if (!WIFEXITED(status)) e(0); - else errct += WEXITSTATUS(status); -} - -/* - * Forcibly terminate a child process, and clean up. - */ -static void -terminate(struct link * link) -{ - int status; - - if (kill(link->pid, SIGKILL) != 0) e(0); - - close(link->sndfd); - close(link->rcvfd); - - if (waitpid(link->pid, &status, 0) <= 0) e(0); - - if (WIFSIGNALED(status)) { - if (WTERMSIG(status) != SIGKILL) e(0); - } else { - if (!WIFEXITED(status)) e(0); - else errct += WEXITSTATUS(status); - } -} - -/* - * Send an integer value to the child or parent. - */ -static void -snd(struct link * link, int val) -{ - - if (write(link->sndfd, (void *)&val, sizeof(val)) != sizeof(val)) e(0); -} - -/* - * Receive an integer value from the child or parent, or -1 on EOF. - */ -static int -rcv(struct link * link) -{ - int r, val; - - if ((r = read(link->rcvfd, (void *)&val, sizeof(val))) == 0) - return -1; - - if (r != sizeof(val)) e(0); - - return val; -} - -/* - * Child procedure that creates semaphore sets. - */ -static void -test_perm_child(struct link * parent) -{ - struct passwd *pw; - struct group *gr; - struct semid_ds semds; - uid_t uid; - gid_t gid; - int mask, rmask, sugid, id[3]; - - /* - * Repeatedly create a number of semaphores with the masks provided by - * the parent process. - */ - while ((mask = rcv(parent)) != -1) { - rmask = rcv(parent); - sugid = rcv(parent); - - /* - * Create the semaphores. For KEY_A, if we are going to set - * the mode through IPC_SET anyway, start with a zero mask to - * check that the replaced mode is used (thus testing IPC_SET). - */ - if ((id[0] = semget(KEY_A, 3, - IPC_CREAT | IPC_EXCL | - ((sugid == SUGID_NONE) ? mask : 0))) == -1) e(0); - if ((id[1] = semget(KEY_B, 3, - IPC_CREAT | IPC_EXCL | mask | rmask)) == -1) e(0); - if ((id[2] = semget(KEY_C, 3, - IPC_CREAT | IPC_EXCL | rmask)) == -1) e(0); - - uid = geteuid(); - gid = getegid(); - if (sugid != SUGID_NONE) { - switch (sugid) { - case SUGID_ROOT_USER: - if ((pw = getpwnam(ROOT_USER)) == NULL) e(0); - uid = pw->pw_uid; - break; - case SUGID_NONROOT_USER: - if ((pw = getpwnam(NONROOT_USER)) == NULL) - e(0); - uid = pw->pw_uid; - break; - case SUGID_ROOT_GROUP: - if ((gr = getgrnam(ROOT_GROUP)) == NULL) e(0); - gid = gr->gr_gid; - break; - case SUGID_NONROOT_GROUP: - if ((gr = getgrnam(NONROOT_GROUP)) == NULL) - e(0); - gid = gr->gr_gid; - break; - } - - semds.sem_perm.uid = uid; - semds.sem_perm.gid = gid; - semds.sem_perm.mode = mask; - if (semctl(id[0], 0, IPC_SET, &semds) != 0) e(0); - semds.sem_perm.mode = mask | rmask; - if (semctl(id[1], 0, IPC_SET, &semds) != 0) e(0); - semds.sem_perm.mode = rmask; - if (semctl(id[2], 0, IPC_SET, &semds) != 0) e(0); - } - - /* Do a quick test to confirm the right privileges. */ - if (mask & IPC_R) { - if (semctl(id[0], 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_perm.mode != (SEM_ALLOC | mask)) e(0); - if (semds.sem_perm.uid != uid) e(0); - if (semds.sem_perm.gid != gid) e(0); - if (semds.sem_perm.cuid != geteuid()) e(0); - if (semds.sem_perm.cgid != getegid()) e(0); - } - - snd(parent, id[0]); - snd(parent, id[1]); - snd(parent, id[2]); - - /* The other child process runs here. */ - - if (rcv(parent) != 0) e(0); - - /* - * For owner tests, the other child may already have removed - * the semaphore sets, so ignore return values here. - */ - (void)semctl(id[0], 0, IPC_RMID); - (void)semctl(id[1], 0, IPC_RMID); - (void)semctl(id[2], 0, IPC_RMID); - } -} - -/* - * Perform a permission test. The given procedure will be called for various - * access masks, which it can use to determine whether operations on three - * created semaphore sets should succeed or fail. The first two semaphore sets - * are created with appropriate privileges, the third one is not. If the - * 'owner_test' variable is set, the test will change slightly so as to allow - * testing of operations that require a matching uid/cuid. - */ -static void -test_perm(void (* proc)(struct link *), int owner_test) -{ - struct link child1, child2; - int n, shift, bit, mask, rmask, drop1, drop2, sugid, id[3]; - - for (n = 0; n < 7; n++) { - /* - * Child 1 creates the semaphores, and child 2 opens them. - * For shift 6 (0700), child 1 drops its privileges to match - * child 2's (n=0). For shift 3 (0070), child 2 drops its user - * privileges (n=3). For shift 0 (0007), child 2 drops its - * group in addition to its user privileges (n=6). Also try - * with differing uid/cuid (n=1,2) and gid/cgid (n=4,5), where - * the current ownership (n=1,4) or the creator's ownership - * (n=2,5) is tested. - */ - switch (n) { - case 0: - shift = 6; - drop1 = DROP_ALL; - drop2 = DROP_ALL; - sugid = SUGID_NONE; - break; - case 1: - shift = 6; - drop1 = DROP_NONE; - drop2 = DROP_ALL; - sugid = SUGID_NONROOT_USER; - break; - case 2: - shift = 6; - drop1 = DROP_USER; - drop2 = DROP_ALL; - sugid = SUGID_ROOT_USER; - break; - case 3: - shift = 3; - drop1 = DROP_NONE; - drop2 = DROP_USER; - sugid = SUGID_NONE; - break; - case 4: - shift = 3; - drop1 = DROP_NONE; - drop2 = DROP_ALL; - sugid = SUGID_NONROOT_GROUP; - break; - case 5: - /* The root group has no special privileges. */ - shift = 3; - drop1 = DROP_NONE; - drop2 = DROP_USER; - sugid = SUGID_NONROOT_GROUP; - break; - case 6: - shift = 0; - drop1 = DROP_NONE; - drop2 = DROP_ALL; - sugid = SUGID_NONE; - break; - } - - spawn(&child1, test_perm_child, drop1); - spawn(&child2, proc, drop2); - - for (bit = 0; bit <= 7; bit++) { - mask = bit << shift; - rmask = 0777 & ~(7 << shift); - - snd(&child1, mask); - snd(&child1, rmask); - snd(&child1, sugid); - id[0] = rcv(&child1); - id[1] = rcv(&child1); - id[2] = rcv(&child1); - - snd(&child2, (owner_test) ? shift : bit); - snd(&child2, id[0]); - snd(&child2, id[1]); - snd(&child2, id[2]); - if (rcv(&child2) != 0) e(0); - - snd(&child1, 0); - } - - /* We use a bitmask of -1 to terminate the children. */ - snd(&child1, -1); - snd(&child2, -1); - - collect(&child1); - collect(&child2); - } -} - -/* - * Test semget(2) permission checks. Please note that the checks are advisory: - * nothing keeps a process from opening a semaphore set with fewer privileges - * than required by the operations the process subsequently issues on the set. - */ -static void -test88a_perm(struct link * parent) -{ - int r, tbit, bit, mask, id[3]; - - while ((tbit = rcv(parent)) != -1) { - id[0] = rcv(parent); - id[1] = rcv(parent); - id[2] = rcv(parent); - - /* - * We skip setting lower bits, as it is not clear what effect - * that should have. We assume that zero bits should result in - * failure. - */ - for (bit = 0; bit <= 7; bit++) { - mask = bit << 6; - - /* - * Opening semaphore set A must succeed iff the given - * bits are all set in the relevant three-bit section - * of the creation mask. - */ - r = semget(KEY_A, 0, mask); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if ((bit != 0 && (bit & tbit) == bit) != (r != -1)) - e(0); - if (r != -1 && r != id[0]) e(0); - - /* - * Same for semaphore set B, which was created with all - * irrelevant mode bits inverted. - */ - r = semget(KEY_B, 0, mask); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if ((bit != 0 && (bit & tbit) == bit) != (r != -1)) - e(0); - if (r != -1 && r != id[1]) e(0); - - /* - * Semaphore set C was created with only irrelevant - * mode bits set, so opening it must always fail. - */ - if (semget(KEY_C, 0, mask) != -1) e(0); - if (errno != EACCES) e(0); - } - - snd(parent, 0); - } -} - -/* - * Test the basic semget(2) functionality. - */ -static void -test88a(void) -{ - struct seminfo seminfo; - struct semid_ds semds; - time_t now; - unsigned int i, j; - int id[3], *idp; - - subtest = 0; - - /* - * The key IPC_PRIVATE must always yield a new semaphore set identifier - * regardless of whether IPC_CREAT and IPC_EXCL are supplied. - */ - if ((id[0] = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600)) < 0) e(0); - - if ((id[1] = semget(IPC_PRIVATE, 1, IPC_CREAT | IPC_EXCL | 0600)) < 0) - e(0); - - if ((id[2] = semget(IPC_PRIVATE, 1, 0600)) < 0) e(0); - - if (id[0] == id[1]) e(0); - if (id[1] == id[2]) e(0); - if (id[0] == id[2]) e(0); - - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - if (semctl(id[1], 0, IPC_RMID) != 0) e(0); - if (semctl(id[2], 0, IPC_RMID) != 0) e(0); - - /* Remove any leftovers from previous test runs. */ - if ((id[0] = semget(KEY_A, 0, 0600)) >= 0 && - semctl(id[0], 0, IPC_RMID) == -1) e(0); - if ((id[0] = semget(KEY_B, 0, 0600)) >= 0 && - semctl(id[0], 0, IPC_RMID) == -1) e(0); - - /* - * For non-IPC_PRIVATE keys, open(2)-like semantics apply with respect - * to IPC_CREAT and IPC_EXCL flags. The behavior of supplying IPC_EXCL - * without IPC_CREAT is undefined, so we do not test for that here. - */ - if (semget(KEY_A, 1, 0600) != -1) e(0); - if (errno != ENOENT); - - if ((id[0] = semget(KEY_A, 1, IPC_CREAT | IPC_EXCL | 0600)) < 0) e(0); - - if (semget(KEY_B, 1, 0600) != -1) e(0); - if (errno != ENOENT); - - if ((id[1] = semget(KEY_B, 1, IPC_CREAT | 0600)) < 0) e(0); - - if (id[0] == id[1]) e(0); - - if ((id[2] = semget(KEY_A, 1, 0600)) < 0) e(0); - if (id[2] != id[0]) e(0); - - if ((id[2] = semget(KEY_B, 1, IPC_CREAT | 0600)) < 0) e(0); - if (id[2] != id[2]) e(0); - - if (semget(KEY_A, 1, IPC_CREAT | IPC_EXCL | 0600) != -1) e(0); - if (errno != EEXIST) e(0); - - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - if (semctl(id[1], 0, IPC_RMID) != 0) e(0); - - /* - * Check that we get the right error when we run out of semaphore sets. - * It is possible that other processes in the system are using sets - * right now, so see if we can anywhere from three (the number we had - * already) to SEMMNI semaphore sets, and check for ENOSPC after that. - */ - if (semctl(0, 0, IPC_INFO, &seminfo) == -1) e(0); - if (seminfo.semmni < 3 || seminfo.semmni > USHRT_MAX) e(0); - - if ((idp = malloc(sizeof(int) * (seminfo.semmni + 1))) == NULL) e(0); - - for (i = 0; i < seminfo.semmni + 1; i++) { - if ((idp[i] = semget(KEY_A + i, 1, IPC_CREAT | 0600)) < 0) - break; - - /* Ensure that there are no ID collisions. O(n**2). */ - for (j = 0; j < i; j++) - if (idp[i] == idp[j]) e(0); - } - - if (errno != ENOSPC) e(0); - if (i < 3) e(0); - if (i == seminfo.semmni + 1) e(0); - - while (i-- > 0) - if (semctl(idp[i], 0, IPC_RMID) != 0) e(0); - - free(idp); - - /* - * The given number of semaphores must be within bounds. - */ - if (semget(KEY_A, -1, IPC_CREAT | 0600) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semget(KEY_A, 0, IPC_CREAT | 0600) != -1) e(0); - if (errno != EINVAL) e(0); - - if (seminfo.semmsl < 3 || seminfo.semmsl > USHRT_MAX) e(0); - if (semget(KEY_A, seminfo.semmsl + 1, IPC_CREAT | 0600) != -1) e(0); - if (errno != EINVAL) e(0); - - if ((id[0] = semget(KEY_A, seminfo.semmsl, IPC_CREAT | 0600)) < 0) - e(0); - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - - if ((id[0] = semget(KEY_A, 2, IPC_CREAT | 0600)) < 0) e(0); - - if ((id[1] = semget(KEY_A, 0, 0600)) < 0) e(0); - if (id[0] != id[1]) e(0); - - if ((id[1] = semget(KEY_A, 1, 0600)) < 0) e(0); - if (id[0] != id[1]) e(0); - - if ((id[1] = semget(KEY_A, 2, 0600)) < 0) e(0); - if (id[0] != id[1]) e(0); - - if ((id[1] = semget(KEY_A, 3, 0600)) != -1) e(0); - if (errno != EINVAL) e(0); - - if ((id[1] = semget(KEY_A, seminfo.semmsl + 1, 0600)) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - - /* - * Verify that the initial values for the semaphore set are as - * expected. - */ - time(&now); - if (seminfo.semmns < 3 + seminfo.semmsl) e(0); - if ((id[0] = semget(IPC_PRIVATE, 3, IPC_CREAT | IPC_EXCL | 0642)) < 0) - e(0); - if ((id[1] = semget(KEY_A, seminfo.semmsl, IPC_CREAT | 0613)) < 0) - e(0); - - if (semctl(id[0], 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_perm.uid != geteuid()) e(0); - if (semds.sem_perm.gid != getegid()) e(0); - if (semds.sem_perm.cuid != geteuid()) e(0); - if (semds.sem_perm.cgid != getegid()) e(0); - if (semds.sem_perm.mode != (SEM_ALLOC | 0642)) e(0); - if (semds.sem_perm._key != IPC_PRIVATE) e(0); - if (semds.sem_nsems != 3) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime < now || semds.sem_ctime >= now + 10) e(0); - - for (i = 0; i < semds.sem_nsems; i++) - TEST_SEM(id[0], i, 0, 0, 0, 0); - - if (semctl(id[1], 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_perm.uid != geteuid()) e(0); - if (semds.sem_perm.gid != getegid()) e(0); - if (semds.sem_perm.cuid != geteuid()) e(0); - if (semds.sem_perm.cgid != getegid()) e(0); - if (semds.sem_perm.mode != (SEM_ALLOC | 0613)) e(0); - if (semds.sem_perm._key != KEY_A) e(0); - if (semds.sem_nsems != seminfo.semmsl) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime < now || semds.sem_ctime >= now + 10) e(0); - - for (i = 0; i < semds.sem_nsems; i++) - TEST_SEM(id[1], i, 0, 0, 0, 0); - - if (semctl(id[1], 0, IPC_RMID) != 0) e(0); - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - - /* - * Finally, perform a number of permission-related checks. Since the - * main test program is running with superuser privileges, most of the - * permission tests use an unprivileged child process. - */ - /* The superuser can always open and destroy a semaphore set. */ - if ((id[0] = semget(KEY_A, 1, IPC_CREAT | IPC_EXCL | 0000)) < 0) e(0); - - if ((id[1] = semget(KEY_A, 0, 0600)) < 0) e(0); - if (id[0] != id[1]) e(0); - - if ((id[1] = semget(KEY_A, 0, 0000)) < 0) e(0); - if (id[0] != id[1]) e(0); - - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - - /* - * When an unprivileged process tries to open a semaphore set, the - * given upper three permission bits from the mode (0700) are tested - * against the appropriate permission bits from the semaphore set. - */ - test_perm(test88a_perm, 0 /*owner_test*/); -} - -/* - * Test semop(2) permission checks. - */ -static void -test88b_perm(struct link * parent) -{ - struct sembuf sops[2]; - size_t nsops; - int i, r, tbit, bit, id[3]; - - while ((tbit = rcv(parent)) != -1) { - id[0] = rcv(parent); - id[1] = rcv(parent); - id[2] = rcv(parent); - - /* - * This loop is designed such that failure of any bit-based - * subset will not result in subsequent operations blocking. - */ - for (i = 0; i < 8; i++) { - memset(sops, 0, sizeof(sops)); - - switch (i) { - case 0: - nsops = 1; - bit = 4; - break; - case 1: - sops[0].sem_op = 1; - nsops = 1; - bit = 2; - break; - case 2: - sops[0].sem_op = -1; - nsops = 1; - bit = 2; - break; - case 3: - sops[1].sem_op = 1; - nsops = 2; - bit = 6; - break; - case 4: - sops[0].sem_num = 1; - sops[1].sem_op = -1; - nsops = 2; - bit = 6; - break; - case 5: - sops[1].sem_num = 1; - nsops = 2; - bit = 4; - break; - case 6: - /* - * Two operations on the same semaphore. As - * such, this verifies that operations are - * processed in array order. - */ - sops[0].sem_op = 1; - sops[1].sem_op = -1; - nsops = 2; - bit = 2; - break; - case 7: - /* - * Test the order of checks. Since IPC_STAT - * requires read permission, it is reasonable - * that the check against sem_nsems be done - * only after the permission check as well. - * For this test we rewrite EFBIG to OK below. - */ - sops[0].sem_num = USHRT_MAX; - nsops = 2; - bit = 4; - break; - } - - r = semop(id[0], sops, nsops); - if (i == 7 && r == -1 && errno == EFBIG) r = 0; - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - r = semop(id[1], sops, nsops); - if (i == 7 && r == -1 && errno == EFBIG) r = 0; - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - if (semop(id[2], sops, nsops) != -1) e(0); - if (errno != EACCES) e(0); - } - - snd(parent, 0); - } -} - -/* - * Signal handler. - */ -static void -got_signal(int sig) -{ - - if (sig != SIGHUP) e(0); - if (nr_signals != 0) e(0); - nr_signals++; -} - -/* - * Child process for semop(2) tests, mainly testing blocking operations. - */ -static void -test88b_child(struct link * parent) -{ - struct sembuf sops[5]; - struct sigaction act; - int id; - - id = rcv(parent); - - memset(sops, 0, sizeof(sops)); - if (semop(id, sops, 1) != 0) e(0); - - if (rcv(parent) != 1) e(0); - - sops[0].sem_op = -3; - if (semop(id, sops, 1) != 0) e(0); - - if (rcv(parent) != 2) e(0); - - sops[0].sem_num = 2; - sops[0].sem_op = 2; - sops[1].sem_num = 1; - sops[1].sem_op = -1; - sops[2].sem_num = 0; - sops[2].sem_op = 1; - if (semop(id, sops, 3) != 0) e(0); - - if (rcv(parent) != 3) e(0); - - sops[0].sem_num = 1; - sops[0].sem_op = 0; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - sops[2].sem_num = 0; - sops[2].sem_op = 0; - sops[3].sem_num = 2; - sops[3].sem_op = 0; - sops[4].sem_num = 2; - sops[4].sem_op = 1; - if (semop(id, sops, 5) != 0) e(0); - - if (rcv(parent) != 4) e(0); - - sops[0].sem_num = 1; - sops[0].sem_op = -2; - sops[1].sem_num = 2; - sops[1].sem_op = 0; - if (semop(id, sops, 2) != 0) e(0); - - if (rcv(parent) != 5) e(0); - - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 1; - sops[1].sem_op = -1; - sops[1].sem_flg = IPC_NOWAIT; - if (semop(id, sops, 2) != 0) e(0); - - if (rcv(parent) != 6) e(0); - - sops[0].sem_num = 1; - sops[0].sem_op = 0; - sops[1].sem_num = 0; - sops[1].sem_op = 0; - sops[1].sem_flg = IPC_NOWAIT; - if (semop(id, sops, 2) != -1) e(0); - if (errno != EAGAIN) e(0); - - if (rcv(parent) != 7) e(0); - - sops[0].sem_num = 0; - sops[0].sem_op = 0; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - sops[1].sem_flg = 0; - if (semop(id, sops, 2) != 0) e(0); - - if (rcv(parent) != 8) e(0); - - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 1; - sops[1].sem_op = 2; - if (semop(id, sops, 2) != -1) e(0); - if (errno != ERANGE) e(0); - - memset(&act, 0, sizeof(act)); - act.sa_handler = got_signal; - sigfillset(&act.sa_mask); - if (sigaction(SIGHUP, &act, NULL) != 0) e(0); - - if (rcv(parent) != 9) e(0); - - memset(sops, 0, sizeof(sops)); - sops[0].sem_num = 0; - sops[0].sem_op = 0; - sops[1].sem_num = 0; - sops[1].sem_op = 1; - sops[2].sem_num = 1; - sops[2].sem_op = 0; - if (semop(id, sops, 3) != -1) - if (errno != EINTR) e(0); - if (nr_signals != 1) e(0); - - TEST_SEM(id, 0, 0, parent->pid, 0, 0); - TEST_SEM(id, 1, 1, parent->pid, 0, 0); - - if (rcv(parent) != 10) e(0); - - memset(sops, 0, sizeof(sops)); - sops[0].sem_op = -3; - if (semop(id, sops, 1) != -1) e(0); - if (errno != EIDRM) e(0); - - id = rcv(parent); - - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - if (semop(id, sops, 2) != -1) e(0); - if (errno != ERANGE) e(0); - - if (rcv(parent) != 11) e(0); - - sops[0].sem_num = 1; - sops[0].sem_op = 0; - sops[1].sem_num = 0; - sops[1].sem_op = -1; - if (semop(id, sops, 2) != 0) e(0); - - id = rcv(parent); - - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 1; - sops[1].sem_op = 0; - if (semop(id, sops, 2) != 0) e(0); - - snd(parent, errct); - if (rcv(parent) != 12) e(0); - - /* The child will be killed during this call. It should not return. */ - sops[0].sem_num = 1; - sops[0].sem_op = -1; - sops[1].sem_num = 0; - sops[1].sem_op = 3; - (void)semop(id, sops, 2); - - e(0); -} - -/* - * Test the basic semop(2) functionality. - */ -static void -test88b(void) -{ - struct seminfo seminfo; - struct semid_ds semds; - struct sembuf *sops, *sops2; - size_t size; - struct link child; - time_t now; - unsigned short val[2]; - int id; - - subtest = 1; - - /* Allocate a buffer for operations. */ - if (semctl(0, 0, IPC_INFO, &seminfo) == -1) e(0); - - if (seminfo.semopm < 3 || seminfo.semopm > USHRT_MAX) e(0); - - size = sizeof(sops[0]) * (seminfo.semopm + 1); - if ((sops = malloc(size)) == NULL) e(0); - memset(sops, 0, size); - - /* Do a few first tests with a set containing one semaphore. */ - if ((id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600)) == -1) e(0); - - /* If no operations are given, the call should succeed. */ - if (semop(id, NULL, 0) != 0) e(0); - - /* - * If any operations are given, the pointer must be valid. Moreover, - * partially valid buffers must never be processed partially. - */ - if (semop(id, NULL, 1) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semop(id, bad_ptr, 1) != -1) e(0); - if (errno != EFAULT) e(0); - - memset(page_ptr, 0, page_size); - sops2 = ((struct sembuf *)bad_ptr) - 1; - sops2->sem_op = 1; - if (semop(id, sops2, 2) != -1) e(0); - if (errno != EFAULT) e(0); - - TEST_SEM(id, 0, 0, 0, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - - /* - * A new semaphore set is initialized to an all-zeroes state, and a - * zeroed operation tests for a zeroed semaphore. This should pass. - */ - time(&now); - if (semop(id, sops, 1) != 0) e(0); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime < now || semds.sem_otime >= now + 10) e(0); - - /* Test the limit on the number of operations. */ - if (semop(id, sops, seminfo.semopm) != 0) e(0); - - if (semop(id, sops, seminfo.semopm + 1) != -1) e(0); - if (errno != E2BIG) e(0); - - if (semop(id, sops, SIZE_MAX) != -1) e(0); - if (errno != E2BIG) e(0); - - /* Test the range check on the semaphore numbers. */ - sops[1].sem_num = 1; - if (semop(id, sops, 2) != -1) e(0); - if (errno != EFBIG) e(0); - - sops[1].sem_num = USHRT_MAX; - if (semop(id, sops, 2) != -1) e(0); - if (errno != EFBIG) e(0); - - /* - * Test nonblocking operations on a single semaphore, starting with - * value limit and overflow cases. - */ - if (seminfo.semvmx < 3 || seminfo.semvmx > SHRT_MAX) e(0); - - sops[0].sem_flg = IPC_NOWAIT; - - /* This block does not trigger on MINIX3. */ - if (seminfo.semvmx < SHRT_MAX) { - sops[0].sem_op = seminfo.semvmx + 1; - if (semop(id, sops, 1) != -1) e(0); - if (errno != ERANGE) e(0); - if (semctl(id, 0, GETVAL) != 0) e(0); - } - - sops[0].sem_op = seminfo.semvmx; - if (semop(id, sops, 1) != 0) e(0); - if (semctl(id, 0, GETVAL) != seminfo.semvmx) e(0); - - /* As of writing, the proper checks for this is missing on NetBSD. */ - sops[0].sem_op = 1; - if (semop(id, sops, 1) != -1) e(0); - if (errno != ERANGE) e(0); - if (semctl(id, 0, GETVAL) != seminfo.semvmx) e(0); - - sops[0].sem_op = seminfo.semvmx; - if (semop(id, sops, 1) != -1) e(0); - if (errno != ERANGE) e(0); - if (semctl(id, 0, GETVAL) != seminfo.semvmx) e(0); - - sops[0].sem_op = SHRT_MAX; - if (semop(id, sops, 1) != -1) e(0); - if (errno != ERANGE) e(0); - if (semctl(id, 0, GETVAL) != seminfo.semvmx) e(0); - - /* This block does trigger on MINIX3. */ - if (seminfo.semvmx < -(int)SHRT_MIN) { - sops[0].sem_op = -seminfo.semvmx - 1; - if (semop(id, sops, 1) != -1) e(0); - if (errno != EAGAIN) e(0); - if (semctl(id, 0, GETVAL) != seminfo.semvmx) e(0); - } - - sops[0].sem_op = -seminfo.semvmx; - if (semop(id, sops, 1) != 0) e(0); - if (semctl(id, 0, GETVAL) != 0) e(0); - - /* - * Test basic nonblocking operations on a single semaphore. - */ - sops[0].sem_op = 0; - if (semop(id, sops, 1) != 0) e(0); - - sops[0].sem_op = 2; - if (semop(id, sops, 1) != 0) e(0); - if (semctl(id, 0, GETVAL) != 2) e(0); - - sops[0].sem_op = 0; - if (semop(id, sops, 1) != -1) e(0); - if (errno != EAGAIN) e(0); - - sops[0].sem_op = -3; - if (semop(id, sops, 1) != -1) e(0); - if (errno != EAGAIN) e(0); - - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - if (semctl(id, 0, GETVAL) != 3) e(0); - - sops[0].sem_op = -1; - if (semop(id, sops, 1) != 0) e(0); - if (semctl(id, 0, GETVAL) != 2) e(0); - - sops[0].sem_op = 0; - if (semop(id, sops, 1) != -1) e(0); - if (errno != EAGAIN) e(0); - - sops[0].sem_op = -2; - if (semop(id, sops, 1) != 0) e(0); - if (semctl(id, 0, GETVAL) != 0) e(0); - - sops[0].sem_op = 0; - if (semop(id, sops, 1) != 0) e(0); - - /* Make sure that not too much data is being read in. */ - sops2->sem_op = 0; - sops2--; - if (semop(id, sops2, 2) != 0) e(0); - - /* Even if no operations are given, the identifier must be valid. */ - if (semctl(id, 0, IPC_RMID) != 0) e(0); - - if (semop(id, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semop(-1, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semop(INT_MIN, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - memset(&semds, 0, sizeof(semds)); - id = IXSEQ_TO_IPCID(seminfo.semmni, semds.sem_perm); - if (semop(id, NULL, 0) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * Test permission checks. As part of this, test basic nonblocking - * multi-operation calls, including operation processing in array order - * and the order of (permission vs other) checks. - */ - test_perm(test88b_perm, 0 /*owner_test*/); - - /* - * Test blocking operations, starting with a single blocking operation. - */ - if ((id = semget(IPC_PRIVATE, 3, 0600)) == -1) e(0); - - memset(sops, 0, sizeof(sops[0])); - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - - TEST_SEM(id, 0, 1, getpid(), 0, 0); - - spawn(&child, test88b_child, DROP_NONE); - - snd(&child, id); - - /* - * In various places, we have to sleep in order to allow the child to - * get itself blocked in a semop(2) call. - */ - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, getpid(), 0, 1); - - sops[0].sem_op = -1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - - TEST_SEM(id, 0, 1, getpid(), 0, 0); - - snd(&child, 1); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, getpid(), 1, 0); - - /* This should cause a (fruitless) retry of the blocking operation. */ - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 2, getpid(), 1, 0); - - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - - /* - * Test blocking operations, verifying the correct operation of - * multiple (partially) blocking operations and atomicity. - */ - memset(sops, 0, sizeof(sops[0]) * 2); - if (semop(id, sops, 1) != 0) e(0); - - /* One blocking operation. */ - snd(&child, 2); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - TEST_SEM(id, 1, 0, 0, 1, 0); - TEST_SEM(id, 2, 0, 0, 0, 0); - - sops[0].sem_num = 1; - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, child.pid, 0, 0); - TEST_SEM(id, 1, 0, child.pid, 0, 0); - TEST_SEM(id, 2, 2, child.pid, 0, 0); - - /* Two blocking operations in one call, resolved at once. */ - snd(&child, 3); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, child.pid, 0, 1); - TEST_SEM(id, 1, 0, child.pid, 0, 0); - TEST_SEM(id, 2, 2, child.pid, 0, 0); - - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 2; - sops[1].sem_op = -2; - if (semop(id, sops, 2) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, 1, child.pid, 0, 0); - TEST_SEM(id, 2, 1, child.pid, 0, 0); - - /* Two blocking operations in one call, resolved one by one. */ - snd(&child, 4); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, 1, child.pid, 1, 0); - TEST_SEM(id, 2, 1, child.pid, 0, 0); - - sops[0].sem_num = 1; - sops[0].sem_op = 1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, 2, getpid(), 0, 0); - TEST_SEM(id, 2, 1, child.pid, 0, 1); - - sops[0].sem_num = 2; - sops[0].sem_op = -1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, 0, child.pid, 0, 0); - TEST_SEM(id, 2, 0, child.pid, 0, 0); - - /* One blocking op followed by a nonblocking one, cleared at once. */ - sops[0].sem_num = 0; - sops[0].sem_op = 0; - sops[1].sem_num = 1; - sops[1].sem_op = 0; - if (semop(id, sops, 2) != 0) e(0); - - snd(&child, 5); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, getpid(), 1, 0); - TEST_SEM(id, 1, 0, getpid(), 0, 0); - - sops[0].sem_num = 0; - sops[0].sem_op = 1; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - if (semop(id, sops, 2) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, 0, child.pid, 0, 0); - - /* One blocking op followed by a nonblocking one, only one cleared. */ - sops[0].sem_num = 0; - sops[0].sem_op = 1; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - if (semop(id, sops, 2) != 0) e(0); - - snd(&child, 6); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, getpid(), 0, 0); - TEST_SEM(id, 1, 1, getpid(), 0, 1); - - sops[0].sem_num = 1; - sops[0].sem_op = -1; - if (semop(id, sops, 1) != 0) e(0); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, getpid(), 0, 0); - TEST_SEM(id, 1, 0, getpid(), 0, 0); - - /* - * Ensure that all semaphore numbers are checked immediately, which - * given the earlier test results also implies that permissions are - * checked immediately (so we don't have to recheck that too). We do - * not check whether permissions are rechecked after a blocking - * operation, because the specification does not describe the intended - * behavior on this point. - */ - sops[0].sem_num = 0; - sops[0].sem_op = 0; - sops[1].sem_num = 4; - sops[1].sem_op = 0; - if (semop(id, sops, 2) != -1) e(0); - if (errno != EFBIG) e(0); - - /* - * Ensure that semaphore value overflow is detected properly, at the - * moment that the operation is actually processed. - */ - sops[0].sem_num = 1; - sops[0].sem_op = seminfo.semvmx; - if (semop(id, sops, 1) != 0) e(0); - - snd(&child, 7); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 1, getpid(), 0, 1); - TEST_SEM(id, 1, seminfo.semvmx, getpid(), 0, 0); - - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 1; - sops[1].sem_op = -1; - if (semop(id, sops, 2) != 0) e(0); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, seminfo.semvmx, child.pid, 0, 0); - - sops[0].sem_num = 1; - sops[0].sem_op = -2; - if (semop(id, sops, 1) != 0) e(0); - - snd(&child, 8); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, child.pid, 1, 0); - TEST_SEM(id, 1, seminfo.semvmx - 2, getpid(), 0, 0); - - sops[0].sem_num = 0; - sops[0].sem_op = 1; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - if (semop(id, sops, 2) != 0) e(0); - - TEST_SEM(id, 0, 1, getpid(), 0, 0); - TEST_SEM(id, 1, seminfo.semvmx - 1, getpid(), 0, 0); - - sops[0].sem_num = 0; - sops[0].sem_op = seminfo.semvmx - 1; - sops[1].sem_num = 0; - sops[1].sem_op = seminfo.semvmx - 1; - sops[2].sem_num = 0; - sops[2].sem_op = 2; - /* - * With the current SEMVMX, the sum of the values is now USHRT_MAX-1, - * which if processed could result in a zero semaphore value. That - * should not happen. Looking at you, NetBSD. - */ - if (semop(id, sops, 3) != -1) e(0); - if (errno != ERANGE) e(0); - - TEST_SEM(id, 0, 1, getpid(), 0, 0); - - /* - * Check that a blocking semop(2) call fails with EINTR if a signal is - * caught by the process after the call has blocked. - */ - if (semctl(id, 1, SETVAL, 0) != 0) e(0); - sops[0].sem_num = 0; - sops[0].sem_op = -1; - sops[1].sem_num = 1; - sops[1].sem_op = 1; - if (semop(id, sops, 2) != 0) e(0); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - TEST_SEM(id, 1, 1, getpid(), 0, 0); - - snd(&child, 9); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - TEST_SEM(id, 1, 1, getpid(), 0, 1); - - kill(child.pid, SIGHUP); - /* - * Kills are not guaranteed to be delivered immediately to processes - * other than the caller of kill(2), so let the child perform checks. - */ - - /* - * Check that a blocking semop(2) call fails with EIDRM if the - * semaphore set is removed after the call has blocked. - */ - snd(&child, 10); - - usleep(WAIT_USECS); - - if (semctl(id, 0, IPC_RMID) != 0) e(0); - - /* - * Check if sem_otime is updated correctly. Instead of sleeping for - * whole seconds so as to be able to detect differences, use SETVAL, - * which does not update sem_otime at all. This doubles as a first - * test to see if SETVAL correctly wakes up a blocked semop(2) call. - */ - if ((id = semget(IPC_PRIVATE, 2, 0600)) == -1) e(0); - - snd(&child, id); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, 0, 1, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - - if (semctl(id, 1, SETVAL, seminfo.semvmx) != 0) e(0); - - TEST_SEM(id, 0, 0, 0, 1, 0); - TEST_SEM(id, 1, seminfo.semvmx, 0, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - - if (semctl(id, 0, SETVAL, 1) != 0) e(0); - TEST_SEM(id, 0, 1, 0, 0, 0); - TEST_SEM(id, 1, seminfo.semvmx, 0, 0, 0); - - if (semctl(id, 0, SETVAL, 0) != 0) e(0); - - snd(&child, 11); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, 0, 0, 0); - TEST_SEM(id, 1, seminfo.semvmx, 0, 0, 1); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - - if (semctl(id, 1, SETVAL, 0) != 0) e(0); - - TEST_SEM(id, 0, 0, 0, 1, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - - time(&now); - if (semctl(id, 0, SETVAL, 2) != 0) e(0); - - TEST_SEM(id, 0, 1, child.pid, 0, 0); - TEST_SEM(id, 1, 0, child.pid, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime < now || semds.sem_otime >= now + 10) e(0); - - if (semctl(id, 0, IPC_RMID) != 0) e(0); - - /* - * Perform a similar test for SETALL, ensuring that it causes an - * ongoing semop(2) to behave correctly. - */ - if ((id = semget(IPC_PRIVATE, 2, 0600)) == -1) e(0); - - snd(&child, id); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, 0, 1, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - - val[0] = 1; - val[1] = 1; - if (semctl(id, 0, SETALL, val) != 0) e(0); - - TEST_SEM(id, 0, 1, 0, 0, 0); - TEST_SEM(id, 1, 1, 0, 0, 1); - - val[0] = 0; - val[1] = 1; - if (semctl(id, 0, SETALL, val) != 0) e(0); - - TEST_SEM(id, 0, 0, 0, 1, 0); - TEST_SEM(id, 1, 1, 0, 0, 0); - - val[0] = 1; - val[1] = 1; - if (semctl(id, 0, SETALL, val) != 0) e(0); - - TEST_SEM(id, 0, 1, 0, 0, 0); - TEST_SEM(id, 1, 1, 0, 0, 1); - - val[0] = 0; - val[1] = 0; - if (semctl(id, 0, SETALL, val) != 0) e(0); - - TEST_SEM(id, 0, 0, 0, 1, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - - time(&now); - val[0] = 1; - val[1] = 0; - if (semctl(id, 0, SETALL, val) != 0) e(0); - - TEST_SEM(id, 0, 0, child.pid, 0, 0); - TEST_SEM(id, 1, 0, child.pid, 0, 0); - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime < now || semds.sem_otime >= now + 10) e(0); - - /* - * Finally, ensure that if the child is killed, its blocked semop(2) - * call is properly cancelled. - */ - sops[0].sem_num = 0; - sops[0].sem_op = 0; - sops[1].sem_num = 1; - sops[1].sem_op = 0; - if (semop(id, sops, 2) != 0) e(0); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - TEST_SEM(id, 1, 0, getpid(), 0, 0); - - /* We'll be terminating the child, so let it report its errors now. */ - if (rcv(&child) != 0) e(0); - - snd(&child, 12); - - usleep(WAIT_USECS); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - TEST_SEM(id, 1, 0, getpid(), 1, 0); - - terminate(&child); - - TEST_SEM(id, 0, 0, getpid(), 0, 0); - TEST_SEM(id, 1, 0, getpid(), 0, 0); - - if (semctl(id, 0, IPC_RMID) != 0) e(0); - - free(sops); -} - -/* - * Test semctl(2) permission checks, part 1: regular commands. - */ -static void -test88c_perm1(struct link * parent) -{ - static const int cmds[] = { GETVAL, GETPID, GETNCNT, GETZCNT }; - struct semid_ds semds; - struct seminfo seminfo; - unsigned short val[3]; - int i, r, tbit, bit, id[3], cmd; - void *ptr; - - while ((tbit = rcv(parent)) != -1) { - id[0] = rcv(parent); - id[1] = rcv(parent); - id[2] = rcv(parent); - - /* First the read-only, no-argument cases. */ - bit = 4; - for (i = 0; i < __arraycount(cmds); i++) { - r = semctl(id[0], 0, cmds[i]); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - r = semctl(id[1], 0, cmds[i]); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - if (semctl(id[2], 0, cmds[i]) != -1) e(0); - if (errno != EACCES) e(0); - } - - /* - * Then SETVAL, which requires write permission and is the only - * one that takes an integer argument. - */ - bit = 2; - r = semctl(id[0], 0, SETVAL, 0); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - r = semctl(id[1], 0, SETVAL, 0); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - if (semctl(id[2], 0, SETVAL, 0) != -1) e(0); - if (errno != EACCES) e(0); - - /* - * Finally the commands that require read or write permission - * and take a pointer as argument. - */ - memset(val, 0, sizeof(val)); - - for (i = 0; i < 3; i++) { - switch (i) { - case 0: - cmd = GETALL; - ptr = val; - bit = 4; - break; - case 1: - cmd = SETALL; - ptr = val; - bit = 2; - break; - case 2: - cmd = IPC_STAT; - ptr = &semds; - bit = 4; - break; - default: - abort(); - } - - r = semctl(id[0], 0, cmd, ptr); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - r = semctl(id[1], 0, cmd, ptr); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - if (semctl(id[2], 0, cmd, ptr) != -1) e(0); - if (errno != EACCES) e(0); - } - - /* - * I was hoping to avoid this, but otherwise we have to make - * the other child iterate through all semaphore sets to find - * the right index for each of the identifiers. As noted in - * the IPC server itself as well, duplicating these macros is - * not a big deal since the split is firmly hardcoded through - * the exposure of IXSEQ_TO_IPCID to userland. - */ -#ifndef IPCID_TO_IX -#define IPCID_TO_IX(id) ((id) & 0xffff) -#endif - - bit = 4; - - r = semctl(IPCID_TO_IX(id[0]), 0, SEM_STAT, &semds); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - r = semctl(IPCID_TO_IX(id[1]), 0, SEM_STAT, &semds); - if (r < 0 && (r != -1 || errno != EACCES)) e(0); - if (((bit & tbit) == bit) != (r != -1)) e(0); - - if (semctl(IPCID_TO_IX(id[2]), 0, SEM_STAT, &semds) != -1) - e(0); - if (errno != EACCES) e(0); - - /* - * IPC_INFO and SEM_INFO should always succeed. They do not - * even take a semaphore set identifier. - */ - if (semctl(0, 0, IPC_INFO, &seminfo) == -1) e(0); - if (semctl(0, 0, SEM_INFO, &seminfo) == -1) e(0); - - snd(parent, 0); - } -} - -/* - * Test semctl(2) permission checks, part 2: the IPC_SET command. - */ -static void -test88c_perm2(struct link * parent) -{ - struct semid_ds semds; - int r, shift, id[3]; - - while ((shift = rcv(parent)) != -1) { - id[0] = rcv(parent); - id[1] = rcv(parent); - id[2] = rcv(parent); - - /* - * Test IPC_SET. Ideally, we would set the permissions to what - * they currently are, but we do not actually know what they - * are, and IPC_STAT requires read permission which we may not - * have! However, no matter what we do, we cannot prevent the - * other child from being able to remove the semaphore sets - * afterwards. So, we just set the permissions to all-zeroes; - * even though those values are meaningful (mode 0000, uid 0, - * gid 0) they could be anything: the API will accept anything. - * This does mean we need to test IPC_RMID permissions from - * another procedure, because we may now be locking ourselves - * out. The System V IPC interface is pretty strange that way. - */ - memset(&semds, 0, sizeof(semds)); - - r = semctl(id[0], 0, IPC_SET, &semds); - if (r < 0 && (r != -1 || errno != EPERM)) e(0); - if ((shift == 6) != (r != -1)) e(0); - - r = semctl(id[1], 0, IPC_SET, &semds); - if (r < 0 && (r != -1 || errno != EPERM)) e(0); - if ((shift == 6) != (r != -1)) e(0); - - /* For once, this too should succeed. */ - r = semctl(id[2], 0, IPC_SET, &semds); - if (r < 0 && (r != -1 || errno != EPERM)) e(0); - if ((shift == 6) != (r != -1)) e(0); - - snd(parent, 0); - } -} - -/* - * Test semctl(2) permission checks, part 3: the IPC_RMID command. - */ -static void -test88c_perm3(struct link * parent) -{ - int r, shift, id[3]; - - while ((shift = rcv(parent)) != -1) { - id[0] = rcv(parent); - id[1] = rcv(parent); - id[2] = rcv(parent); - - r = semctl(id[0], 0, IPC_RMID); - if (r < 0 && (r != -1 || errno != EPERM)) e(0); - if ((shift == 6) != (r != -1)) e(0); - - r = semctl(id[1], 0, IPC_RMID); - if (r < 0 && (r != -1 || errno != EPERM)) e(0); - if ((shift == 6) != (r != -1)) e(0); - - /* Okay, twice then. */ - r = semctl(id[2], 0, IPC_RMID); - if (r < 0 && (r != -1 || errno != EPERM)) e(0); - if ((shift == 6) != (r != -1)) e(0); - - snd(parent, 0); - } -} - -/* - * Test the basic semctl(2) functionality. - */ -static void -test88c(void) -{ - static const int cmds[] = { GETVAL, GETPID, GETNCNT, GETZCNT }; - struct seminfo seminfo; - struct semid_ds semds, osemds; - unsigned short val[4], seen[2]; - char statbuf[sizeof(struct semid_ds) + 1]; - unsigned int i, j; - time_t now; - int r, id, id2, badid1, badid2, cmd; - - subtest = 2; - - if (semctl(0, 0, IPC_INFO, &seminfo) == -1) e(0); - - /* - * Start with permission checks on the commands. IPC_SET and IPC_RMID - * are special: they check for ownership (uid/cuid) and return EPERM - * rather than EACCES on permission failure. - */ - test_perm(test88c_perm1, 0 /*owner_test*/); - test_perm(test88c_perm2, 1 /*owner_test*/); - test_perm(test88c_perm3, 1 /*owner_test*/); - - /* Create identifiers known to be invalid. */ - if ((badid1 = semget(IPC_PRIVATE, 1, 0600)) < 0) e(0); - - if (semctl(badid1, 0, IPC_RMID) != 0) e(0); - - memset(&semds, 0, sizeof(semds)); - badid2 = IXSEQ_TO_IPCID(seminfo.semmni, semds.sem_perm); - - if ((id = semget(IPC_PRIVATE, 3, IPC_CREAT | 0600)) < 0) e(0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime == 0) e(0); - - /* In this case we can't avoid sleeping for longer periods.. */ - while (time(&now) == semds.sem_ctime) - usleep(250000); - - /* - * Test the simple GET commands. The actual functionality of these - * commands have already been tested thoroughly as part of the - * semop(2) part of the test set, so we do not repeat that here. - */ - for (i = 0; i < __arraycount(cmds); i++) { - for (j = 0; j < 3; j++) - if (semctl(id, j, cmds[i]) != 0) e(0); - - if (semctl(badid1, 0, cmds[i]) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, cmds[i]) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, cmds[i]) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, cmds[i]) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, -1, cmds[i]) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 3, cmds[i]) != -1) e(0); - if (errno != EINVAL) e(0); - - /* These commands should not update ctime or otime. */ - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime >= now) e(0); - } - - /* - * Test the GETALL command. - */ - /* - * Contrary to what the Open Group specification suggests, actual - * implementations agree that the semnum parameter is to be ignored for - * calls not involving a specific semaphore in the set. - */ - for (j = 0; j < 5; j++) { - for (i = 0; i < __arraycount(val); i++) - val[i] = USHRT_MAX; - - if (semctl(id, (int)j - 1, GETALL, val) != 0) e(0); - for (i = 0; i < 3; i++) - if (val[i] != 0) e(0); - if (val[i] != USHRT_MAX) e(0); - } - - for (i = 0; i < __arraycount(val); i++) - val[i] = USHRT_MAX; - - if (semctl(badid1, 0, GETALL, val) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, GETALL, val) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, GETALL, val) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, GETALL, val) != -1) e(0); - if (errno != EINVAL) e(0); - - for (i = 0; i < __arraycount(val); i++) - if (val[i] != USHRT_MAX) e(0); - - if (semctl(id, 0, GETALL, NULL) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, GETALL, bad_ptr) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, GETALL, ((unsigned short *)bad_ptr) - 2) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, GETALL, ((unsigned short *)bad_ptr) - 3) != 0) e(0); - - /* Still no change in either otime or ctime. */ - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime >= now) e(0); - - /* - * Test the IPC_STAT command. This is the last command we are testing - * here that does not affect sem_ctime, so in order to avoid extra - * sleep times, we test this command first now. - */ - /* - * The basic IPC_STAT functionality has already been tested heavily as - * part of the semget(2) and permission tests, so we do not repeat that - * here. - */ - memset(statbuf, 0x5a, sizeof(statbuf)); - - if (semctl(badid1, 0, IPC_STAT, statbuf) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, IPC_STAT, statbuf) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, IPC_STAT, statbuf) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, IPC_STAT, statbuf) != -1) e(0); - if (errno != EINVAL) e(0); - - for (i = 0; i < sizeof(statbuf); i++) - if (statbuf[i] != 0x5a) e(0); - - if (semctl(id, 0, IPC_STAT, statbuf) != 0) e(0); - - if (statbuf[sizeof(statbuf) - 1] != 0x5a) e(0); - - if (semctl(id, 0, IPC_STAT, NULL) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, IPC_STAT, bad_ptr) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, IPC_STAT, ((struct semid_ds *)bad_ptr) - 1) != 0) - e(0); - - if (semctl(id, -1, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime >= now) e(0); - - /* - * Test SEM_STAT. - */ - if ((id2 = semget(KEY_A, seminfo.semmsl, IPC_CREAT | 0642)) < 0) e(0); - - memset(statbuf, 0x5a, sizeof(statbuf)); - - if (semctl(-1, 0, SEM_STAT, statbuf) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(seminfo.semmni, 0, SEM_STAT, statbuf) != -1) e(0); - if (errno != EINVAL) e(0); - - for (i = 0; i < sizeof(statbuf); i++) - if (statbuf[i] != 0x5a) e(0); - - memset(seen, 0, sizeof(seen)); - - for (i = 0; i < seminfo.semmni; i++) { - errno = 0; - if ((r = semctl(i, i / 2 - 1, SEM_STAT, statbuf)) == -1) { - if (errno != EINVAL) e(0); - continue; - } - if (r < 0) e(0); - memcpy(&semds, statbuf, sizeof(semds)); - if (!(semds.sem_perm.mode & SEM_ALLOC)) e(0); - if (semds.sem_ctime == 0) e(0); - if (IXSEQ_TO_IPCID(i, semds.sem_perm) != r) e(0); - if (r == id) { - seen[0]++; - if (semds.sem_perm.mode != (SEM_ALLOC | 0600)) e(0); - if (semds.sem_perm.uid != geteuid()) e(0); - if (semds.sem_perm.gid != getegid()) e(0); - if (semds.sem_perm.cuid != semds.sem_perm.uid) e(0); - if (semds.sem_perm.cgid != semds.sem_perm.gid) e(0); - if (semds.sem_perm._key != IPC_PRIVATE) e(0); - if (semds.sem_nsems != 3) e(0); - if (semds.sem_otime != 0) e(0); - - /* This is here because we need a valid index. */ - if (semctl(i, 0, SEM_STAT, NULL) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(i, 0, SEM_STAT, bad_ptr) != -1) e(0); - if (errno != EFAULT) e(0); - } else if (r == id2) { - seen[1]++; - if (semds.sem_perm.mode != (SEM_ALLOC | 0642)) e(0); - if (semds.sem_perm.uid != geteuid()) e(0); - if (semds.sem_perm.gid != getegid()) e(0); - if (semds.sem_perm.cuid != semds.sem_perm.uid) e(0); - if (semds.sem_perm.cgid != semds.sem_perm.gid) e(0); - if (semds.sem_perm._key != KEY_A) e(0); - if (semds.sem_nsems != seminfo.semmsl) e(0); - } - } - - if (seen[0] != 1) e(0); - if (seen[1] != 1) e(0); - - if (statbuf[sizeof(statbuf) - 1] != 0x5a) e(0); - - if (semctl(id, 5, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime >= now) e(0); - - /* - * Test SETVAL. We start with all the failure cases, so as to be able - * to check that sem_ctime is not changed in those cases. - */ - if (semctl(badid1, 0, SETVAL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, SETVAL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, SETVAL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, SETVAL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, -1, SETVAL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 3, SETVAL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, SETVAL, -1) != -1) e(0); - if (errno != ERANGE) e(0); - - if (semctl(id, 0, SETVAL, seminfo.semvmx + 1) != -1) e(0); - if (errno != ERANGE) e(0); - - TEST_SEM(id, 0, 0, 0, 0, 0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime >= now) e(0); - - /* Alright, there we go.. */ - if (semctl(id, 1, SETVAL, 0) != 0) e(0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime < now || semds.sem_ctime >= now + 10) e(0); - - TEST_SEM(id, 1, 0, 0, 0, 0); - - if (semctl(id, 2, SETVAL, seminfo.semvmx) != 0) e(0); - - TEST_SEM(id, 2, seminfo.semvmx, 0, 0, 0); - - if (semctl(id, 0, SETVAL, 1) != 0) e(0); - - TEST_SEM(id, 0, 1, 0, 0, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - TEST_SEM(id, 2, seminfo.semvmx, 0, 0, 0); - - if (semctl(id, 0, GETALL, val) != 0) e(0); - if (val[0] != 1) e(0); - if (val[1] != 0) e(0); - if (val[2] != seminfo.semvmx) e(0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - - while (time(&now) == semds.sem_ctime) - usleep(250000); - - /* - * Test SETALL. Same idea: failure cases first. - */ - if (semctl(badid1, 0, SETALL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, SETALL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, SETALL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, SETALL, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - val[0] = seminfo.semvmx + 1; - val[1] = 0; - val[2] = 0; - if (semctl(id, 0, SETALL, val) != -1) e(0); - if (errno != ERANGE) e(0); - - val[0] = 0; - val[1] = 1; - val[2] = seminfo.semvmx + 1; - if (semctl(id, 0, SETALL, val) != -1) e(0); - if (errno != ERANGE) e(0); - - if (semctl(id, 0, SETALL, NULL) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, SETALL, bad_ptr) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, SETALL, ((unsigned short *)bad_ptr) - 2) != -1) e(0); - if (errno != EFAULT) e(0); - - TEST_SEM(id, 0, 1, 0, 0, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - TEST_SEM(id, 2, seminfo.semvmx, 0, 0, 0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime >= now) e(0); - - val[0] = seminfo.semvmx; - val[1] = 0; - val[2] = 0; - if (semctl(id, 0, SETALL, val) != 0) e(0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_otime != 0) e(0); - if (semds.sem_ctime < now || semds.sem_ctime >= now + 10) e(0); - - TEST_SEM(id, 0, seminfo.semvmx, 0, 0, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - TEST_SEM(id, 2, 0, 0, 0, 0); - - val[0] = 0; - val[1] = 1; - val[2] = seminfo.semvmx; - if (semctl(id, INT_MAX, SETALL, val) != 0) e(0); - - TEST_SEM(id, 0, 0, 0, 0, 0); - TEST_SEM(id, 1, 1, 0, 0, 0); - TEST_SEM(id, 2, seminfo.semvmx, 0, 0, 0); - - memset(page_ptr, 0, page_size); - if (semctl(id, 0, SETALL, ((unsigned short *)bad_ptr) - 3) != 0) e(0); - - TEST_SEM(id, 0, 0, 0, 0, 0); - TEST_SEM(id, 1, 0, 0, 0, 0); - TEST_SEM(id, 2, 0, 0, 0, 0); - - while (time(&now) == semds.sem_ctime) - usleep(250000); - - /* - * Test IPC_SET. Its core functionality has already been tested - * thoroughly as part of the permission tests. - */ - if (semctl(badid1, 0, IPC_SET, &semds) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, IPC_SET, &semds) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, IPC_SET, &semds) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, IPC_SET, &semds) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, IPC_SET, NULL) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, IPC_SET, bad_ptr) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(id, 0, IPC_STAT, &osemds) != 0) e(0); - if (osemds.sem_otime != 0) e(0); - if (osemds.sem_ctime >= now) e(0); - - /* - * Only mode, uid, gid may be set. While the given mode is sanitized - * in our implementation (see below; the open group specification - * leaves this undefined), the uid and gid are not (we do not test this - * exhaustively). The other given fields must be ignored. The ctime - * field will be updated. - */ - memset(&semds, 0x5b, sizeof(semds)); - semds.sem_perm.mode = 0712; - semds.sem_perm.uid = UID_MAX; - semds.sem_perm.gid = GID_MAX - 1; - if (semctl(id, 0, IPC_SET, &semds) != 0) e(0); - - if (semctl(id, 0, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_perm.mode != (SEM_ALLOC | 0712)) e(0); - if (semds.sem_perm.uid != UID_MAX) e(0); - if (semds.sem_perm.gid != GID_MAX - 1) e(0); - if (semds.sem_perm.cuid != osemds.sem_perm.cuid) e(0); - if (semds.sem_perm.cgid != osemds.sem_perm.cgid) e(0); - if (semds.sem_perm._seq != osemds.sem_perm._seq) e(0); - if (semds.sem_perm._key != osemds.sem_perm._key) e(0); - if (semds.sem_nsems != osemds.sem_nsems) e(0); - if (semds.sem_otime != osemds.sem_otime) e(0); - if (semds.sem_ctime < now || semds.sem_ctime >= now + 10) e(0); - - /* It should be possible to set any mode, but mask 0777 is applied. */ - semds.sem_perm.uid = osemds.sem_perm.uid; - semds.sem_perm.gid = osemds.sem_perm.gid; - for (i = 0; i < 0777; i++) { - semds.sem_perm.mode = i; - if (semctl(id, i / 2 - 1, IPC_SET, &semds) != 0) e(0); - - if (semctl(id, i / 2 - 2, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_perm.mode != (SEM_ALLOC | i)) e(0); - - semds.sem_perm.mode = ~0777 | i; - if (semctl(id, i / 2 - 3, IPC_SET, &semds) != 0) e(0); - - if (semctl(id, i / 2 - 4, IPC_STAT, &semds) != 0) e(0); - if (semds.sem_perm.mode != (SEM_ALLOC | i)) e(0); - } - if (semds.sem_perm.uid != osemds.sem_perm.uid) e(0); - if (semds.sem_perm.gid != osemds.sem_perm.gid) e(0); - - if (semctl(id, 0, IPC_SET, ((struct semid_ds *)bad_ptr) - 1) != 0) - e(0); - - /* - * Test IPC_RMID. Its basic functionality has already been tested - * multiple times over, so there is not much left to do here. - */ - if (semctl(badid1, 0, IPC_RMID) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(badid2, 0, IPC_RMID) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(-1, 0, IPC_RMID) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(INT_MIN, 0, IPC_RMID) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, IPC_RMID) != 0) e(0); - - if (semctl(id, 0, IPC_RMID) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, IPC_STAT, &semds) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id2, 1, IPC_RMID) != 0) e(0); - - if (semctl(id2, 1, IPC_RMID) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * Test IPC_INFO and SEM_INFO. Right now, for all practical purposes, - * these identifiers behave pretty much the same. - */ - if ((id = semget(IPC_PRIVATE, 3, 0600)) == -1) e(0); - if ((id2 = semget(IPC_PRIVATE, 1, 0600)) == -1) e(0); - - for (i = 0; i <= 1; i++) { - cmd = (i == 0) ? IPC_INFO : SEM_INFO; - - memset(&seminfo, 0xff, sizeof(seminfo)); - - if ((r = semctl(0, 0, cmd, &seminfo)) == -1) e(0); - - /* - * These commands return the index of the highest in-use slot - * in the semaphore set table. Bad idea of course, because - * that means the value 0 has two potential meanings. Since we - * cannot guarantee that no other running application is using - * semaphores, we settle for "at least" tests based on the two - * semaphore sets we just created. - */ - if (r < 1 || r >= seminfo.semmni) e(0); - - /* - * Many of these checks are rather basic because of missing - * SEM_UNDO support. The only difference between IPC_INFO and - * SEM_INFO is the meaning of the semusz and semaem fields. - */ - if (seminfo.semmap < 0) e(0); - if (seminfo.semmni < 3 || seminfo.semmni > USHRT_MAX) e(0); - if (seminfo.semmns < 3 || seminfo.semmns > USHRT_MAX) e(0); - if (seminfo.semmnu < 0) e(0); - if (seminfo.semmsl < 3 || seminfo.semmsl > USHRT_MAX) e(0); - if (seminfo.semopm < 3 || seminfo.semopm > USHRT_MAX) e(0); - if (seminfo.semume < 0) e(0); - if (cmd == SEM_INFO) { - if (seminfo.semusz < 2) e(0); - } else - if (seminfo.semusz < 0) e(0); - if (seminfo.semvmx < 3 || seminfo.semvmx > SHRT_MAX) e(0); - if (cmd == SEM_INFO) { - if (seminfo.semaem < 4) e(0); - } else - if (seminfo.semaem < 0) e(0); - - if (semctl(INT_MAX, -1, cmd, &seminfo) == -1) e(0); - if (semctl(-1, INT_MAX, cmd, &seminfo) == -1) e(0); - - if (semctl(0, 0, cmd, NULL) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(0, 0, cmd, bad_ptr) != -1) e(0); - if (errno != EFAULT) e(0); - - if (semctl(0, 0, cmd, ((struct seminfo *)bad_ptr) - 1) == -1) - e(0); - } - - if (semctl(id2, 0, IPC_RMID) != 0) e(0); - - /* - * Finally, test invalid commands. Well, hopefully invalid commands, - * anyway. - */ - if (semctl(id, 0, INT_MIN) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, INT_MAX) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, IPC_RMID) != 0) e(0); -} - -/* - * Test SEM_UNDO support. Right now this functionality is missing altogether. - * For now, we test that any attempt to use SEM_UNDO fails. - */ -static void -test88d(void) -{ - struct sembuf sop; - int id; - - subtest = 3; - - if ((id = semget(IPC_PRIVATE, 1, 0600)) == -1) e(0); - - /* - * Use an all-ones (but positive) flag field. This will include - * SEM_UNDO, but also tell the IPC server to report no warning. - */ - if (!(SHRT_MAX & SEM_UNDO)) e(0); - sop.sem_num = 0; - sop.sem_op = 1; - sop.sem_flg = SHRT_MAX; - if (semop(id, &sop, 1) != -1) e(0); - if (errno != EINVAL) e(0); - - if (semctl(id, 0, IPC_RMID) != 0) e(0); -} - -enum { - RESUME_SEMOP, /* use semop() to resume blocked parties */ - RESUME_SETVAL, /* use semctl(SETVAL) to resume blocked parties */ - RESUME_SETALL, /* use semctl(SETALL) to resume blocked parties */ - NR_RESUMES -}; - -enum { - MATCH_FIRST, /* first match completes, blocks second match */ - MATCH_SECOND, /* first match does not complete, second match does */ - MATCH_KILL, /* second match completes after first is aborted */ - MATCH_BOTH, /* first and second match both complete */ - MATCH_CASCADE, /* completed match in turn causes another match */ - MATCH_ALL, /* a combination of the last two */ - NR_MATCHES -}; - -/* - * Auxiliary child procedure. The auxiliary children will deadlock until the - * semaphore set is removed. - */ -static void -test88e_childaux(struct link * parent) -{ - struct sembuf sops[3]; - struct seminfo seminfo; - int child, id, num; - - child = rcv(parent); - id = rcv(parent); - num = rcv(parent); - - memset(sops, 0, sizeof(sops)); - - /* These operations are guaranteed to never return successfully. */ - switch (child) { - case 1: - sops[0].sem_num = num; - sops[0].sem_op = 1; - sops[1].sem_num = num; - sops[1].sem_op = 0; - sops[2].sem_num = 0; - sops[2].sem_op = 1; - break; - case 2: - if (semctl(0, 0, IPC_INFO, &seminfo) == -1) e(0); - sops[0].sem_num = num; - sops[0].sem_op = -seminfo.semvmx; - sops[1].sem_num = num; - sops[1].sem_op = -seminfo.semvmx; - sops[2].sem_num = 0; - sops[2].sem_op = 1; - break; - default: - e(0); - } - - snd(parent, 0); - - if (semop(id, sops, 3) != -1) e(0); - if (errno != EIDRM) e(0); -} - -/* - * First child procedure. - */ -static void -test88e_child1(struct link * parent) -{ - struct sembuf sops[3]; - size_t nsops; - int match, id, expect; - - match = rcv(parent); - id = rcv(parent); - - /* Start off with some defaults, then refine by match type. */ - memset(sops, 0, sizeof(sops)); - sops[0].sem_num = 2; - sops[0].sem_op = -1; - nsops = 2; - expect = 0; - switch (match) { - case MATCH_FIRST: - sops[1].sem_num = 3; - sops[1].sem_op = 1; - break; - case MATCH_SECOND: - sops[1].sem_num = 3; - sops[1].sem_op = -1; - sops[2].sem_num = 0; - sops[2].sem_op = 1; - nsops = 3; - expect = -1; - break; - case MATCH_KILL: - sops[1].sem_num = 0; - sops[1].sem_op = 1; - expect = INT_MIN; - break; - case MATCH_BOTH: - case MATCH_CASCADE: - case MATCH_ALL: - sops[1].sem_num = 3; - sops[1].sem_op = 1; - break; - default: - e(0); - } - - snd(parent, 0); - - if (semop(id, sops, nsops) != expect) e(0); - if (expect == -1 && errno != EIDRM) e(0); -} - -/* - * Second child procedure. - */ -static void -test88e_child2(struct link * parent) -{ - struct sembuf sops[2]; - size_t nsops; - int match, id, expect; - - match = rcv(parent); - id = rcv(parent); - - /* Start off with some defaults, then refine by match type. */ - memset(sops, 0, sizeof(sops)); - sops[0].sem_num = 2; - sops[0].sem_op = -1; - nsops = 2; - expect = 0; - switch (match) { - case MATCH_FIRST: - sops[1].sem_num = 0; - sops[1].sem_op = 1; - expect = -1; - break; - case MATCH_SECOND: - case MATCH_KILL: - nsops = 1; - break; - case MATCH_BOTH: - case MATCH_ALL: - sops[1].sem_num = 3; - sops[1].sem_op = 1; - break; - case MATCH_CASCADE: - sops[0].sem_num = 3; - nsops = 1; - break; - default: - e(0); - } - - snd(parent, 0); - - if (semop(id, sops, nsops) != expect) e(0); - if (expect == -1 && errno != EIDRM) e(0); -} - -/* - * Third child procedure. - */ -static void -test88e_child3(struct link * parent) -{ - struct sembuf sops[1]; - size_t nsops; - int match, id; - - match = rcv(parent); - id = rcv(parent); - - /* Things are a bit simpler here. */ - memset(sops, 0, sizeof(sops)); - nsops = 1; - switch (match) { - case MATCH_ALL: - sops[0].sem_num = 3; - sops[0].sem_op = -2; - break; - default: - e(0); - } - - snd(parent, 0); - - if (semop(id, sops, nsops) != 0) e(0); -} - -/* - * Perform one test for operations affecting multiple processes. - */ -static void -sub88e(unsigned int match, unsigned int resume, unsigned int aux) -{ - struct link aux1, aux2, child1, child2, child3; - struct sembuf sop; - unsigned short val[4]; - int id, inc, aux_zcnt, aux_ncnt; - - /* - * For this test we use one single semaphore set, with four semaphores. - * The first semaphore is increased in the case that an operation that - * should never complete does complete, and thus should stay zero. - * Depending on 'aux', the second or third semaphore is used by the - * auxiliary children (if any, also depending on 'aux') to deadlock on. - * The third and higher semaphores are used in the main operations. - */ - if ((id = semget(IPC_PRIVATE, __arraycount(val), 0666)) == -1) e(0); - - aux_zcnt = aux_ncnt = 0; - - /* Start the first auxiliary child if desired, before all others. */ - if (aux & 1) { - spawn(&aux1, test88e_childaux, DROP_ALL); - - snd(&aux1, 1); - snd(&aux1, id); - snd(&aux1, (aux & 4) ? 2 : 1); - - if (rcv(&aux1) != 0) e(0); - - if (aux & 4) - aux_zcnt++; - } - - /* Start and configure all children for this specific match test. */ - spawn(&child1, test88e_child1, DROP_ALL); - - snd(&child1, match); - snd(&child1, id); - - if (rcv(&child1) != 0) e(0); - - /* - * For fairness tests, we must ensure that the first child blocks on - * the semaphore before the second child does. - */ - switch (match) { - case MATCH_FIRST: - case MATCH_SECOND: - case MATCH_KILL: - usleep(WAIT_USECS); - break; - } - - spawn(&child2, test88e_child2, DROP_NONE); - - snd(&child2, match); - snd(&child2, id); - - if (rcv(&child2) != 0) e(0); - - if (match == MATCH_ALL) { - spawn(&child3, test88e_child3, DROP_USER); - - snd(&child3, match); - snd(&child3, id); - - if (rcv(&child3) != 0) e(0); - } - - /* Start the second auxiliary child if desired, after all others. */ - if (aux & 2) { - spawn(&aux2, test88e_childaux, DROP_NONE); - - snd(&aux2, 2); - snd(&aux2, id); - snd(&aux2, (aux & 4) ? 2 : 1); - - if (rcv(&aux2) != 0) e(0); - - if (aux & 4) - aux_ncnt++; - } - - usleep(WAIT_USECS); - - /* - * Test semaphore values and determine the value with which to increase - * the third semaphore. For MATCH_KILL, also kill the first child. - */ - inc = 1; - switch (match) { - case MATCH_FIRST: - case MATCH_SECOND: - TEST_SEM(id, 2, 0, 0, 2 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 0, 0); - break; - case MATCH_KILL: - TEST_SEM(id, 2, 0, 0, 2 + aux_ncnt, aux_zcnt); - - terminate(&child1); - - /* As stated before, non-self kills need not be instant. */ - usleep(WAIT_USECS); - - TEST_SEM(id, 2, 0, 0, 1 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 0, 0); - break; - case MATCH_BOTH: - TEST_SEM(id, 2, 0, 0, 2 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 0, 0); - inc = 2; - break; - case MATCH_CASCADE: - TEST_SEM(id, 2, 0, 0, 1 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 1, 0); - break; - case MATCH_ALL: - TEST_SEM(id, 2, 0, 0, 2 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 1, 0); - inc = 2; - break; - default: - e(0); - } - - TEST_SEM(id, 0, 0, 0, 0, 0); - TEST_SEM(id, 1, 0, 0, -1, -1); - - /* Resume the appropriate set of children. */ - switch (resume) { - case RESUME_SEMOP: - memset(&sop, 0, sizeof(sop)); - sop.sem_num = 2; - sop.sem_op = inc; - if (semop(id, &sop, 1) != 0) e(0); - break; - case RESUME_SETVAL: - if (semctl(id, 2, SETVAL, inc) != 0) e(0); - break; - case RESUME_SETALL: - memset(val, 0, sizeof(val)); - val[2] = inc; - if (semctl(id, 0, SETALL, val) != 0) e(0); - break; - default: - e(0); - } - - /* - * See if the right children were indeed resumed, and retest the - * semaphore values. - */ - switch (match) { - case MATCH_FIRST: - TEST_SEM(id, 2, 0, child1.pid, 1 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 1, child1.pid, 0, 0); - collect(&child1); - break; - case MATCH_SECOND: - TEST_SEM(id, 2, 0, child2.pid, 1 + aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 0, 0); - collect(&child2); - break; - case MATCH_KILL: - TEST_SEM(id, 2, 0, child2.pid, aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, 0, 0, 0); - collect(&child2); - break; - case MATCH_BOTH: - /* - * The children are not ordered in this case, so we do not know - * which one gets access to the semaphores last. - */ - TEST_SEM(id, 2, 0, -1, aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 2, -1, 0, 0); - collect(&child1); - collect(&child2); - break; - case MATCH_CASCADE: - TEST_SEM(id, 2, 0, child1.pid, aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, child2.pid, 0, 0); - collect(&child1); - collect(&child2); - break; - case MATCH_ALL: - TEST_SEM(id, 2, 0, -1, aux_ncnt, aux_zcnt); - TEST_SEM(id, 3, 0, child3.pid, 0, 0); - collect(&child1); - collect(&child2); - collect(&child3); - break; - default: - e(0); - } - - TEST_SEM(id, 0, 0, 0, 0, 0); - TEST_SEM(id, 1, 0, 0, -1, -1); - - /* Remove the semaphore set. This should unblock remaining callers. */ - if (semctl(id, 0, IPC_RMID) != 0) e(0); - - /* Wait for the children that were not resumed, but should be now. */ - switch (match) { - case MATCH_FIRST: - collect(&child2); - break; - case MATCH_SECOND: - collect(&child1); - break; - case MATCH_KILL: - case MATCH_BOTH: - case MATCH_CASCADE: - case MATCH_ALL: - break; - default: - e(0); - } - - /* Wait for the auxiliary children as well. */ - if (aux & 1) - collect(&aux1); - if (aux & 2) - collect(&aux2); -} - -/* - * Test operations affecting multiple processes, ensuring the following points: - * 1) an operation resumes all possible waiters; 2) a resumed operation in turn - * correctly resumes other now-unblocked operations; 3) a basic level of FIFO - * fairness is provided between blocked parties; 4) all the previous points are - * unaffected by additional waiters that are not being resumed; 5) identifier - * removal properly resumes all affected waiters. - */ -static void -test88e(void) -{ - unsigned int resume, match, aux; - - subtest = 4; - - for (match = 0; match < NR_MATCHES; match++) - for (resume = 0; resume < NR_RESUMES; resume++) - for (aux = 1; aux <= 8; aux++) /* 0 and 4 are equal */ - sub88e(match, resume, aux); -} - -/* - * Verify that non-root processes can use sysctl(2) to see semaphore sets - * created by root. - */ -static void -test88f_child(struct link * parent) -{ - static const int mib[] = { CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_INFO, - KERN_SYSVIPC_SEM_INFO }; - struct sem_sysctl_info *semsi; - size_t len; - int id[2], id2, seen[2]; - int32_t i; - - id[0] = rcv(parent); - id[1] = rcv(parent); - - if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) != 0) e(0); - - if ((semsi = malloc(len)) == NULL) e(0); - - if (sysctl(mib, __arraycount(mib), semsi, &len, NULL, 0) != 0) e(0); - - seen[0] = seen[1] = 0; - for (i = 0; i < semsi->seminfo.semmni; i++) { - if (!(semsi->semids[i].sem_perm.mode & SEM_ALLOC)) - continue; - - id2 = IXSEQ_TO_IPCID(i, semsi->semids[i].sem_perm); - if (id2 == id[0]) - seen[0]++; - else if (id2 == id[1]) - seen[1]++; - } - - free(semsi); - - if (seen[0] != 1) e(0); - if (seen[1] != 1) e(0); -} - -/* - * Test sysctl(2) based information retrieval. This test aims to ensure that - * in particular ipcs(1) and ipcrm(1) will be able to do their jobs. - */ -static void -test88f(void) -{ - static const int mib[] = { CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_INFO, - KERN_SYSVIPC_SEM_INFO }; - struct seminfo seminfo, seminfo2; - struct sem_sysctl_info *semsi; - struct semid_ds_sysctl *semds; - struct link child; - size_t len, size; - int id[2], id2; - int32_t i, slot[2]; - - /* - * Verify that we can retrieve only the general semaphore information, - * without any actual semaphore set entries. This is actually a dirty - * sysctl-level hack, as sysctl requests should not behave differently - * based on the requested length. However, ipcs(1) relies on this. - */ - len = sizeof(seminfo); - if (sysctl(mib, __arraycount(mib), &seminfo, &len, NULL, 0) != 0) e(0); - if (len != sizeof(seminfo)) e(0); - - if (semctl(0, 0, IPC_INFO, &seminfo2) == -1) e(0); - - if (memcmp(&seminfo, &seminfo2, sizeof(seminfo)) != 0) e(0); - - /* Verify that the correct size estimation is returned. */ - if (seminfo.semmni <= 0) e(0); - if (seminfo.semmni > SHRT_MAX) e(0); - - size = sizeof(*semsi) + - sizeof(semsi->semids[0]) * (seminfo.semmni - 1); - - len = 0; - if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) != 0) e(0); - if (len != size) e(0); - - /* Create two semaphore sets that should show up in the listing. */ - if ((id[0] = semget(KEY_A, 5, IPC_CREAT | 0612)) < 0) e(0); - - if ((id[1] = semget(IPC_PRIVATE, 3, 0650)) < 0) e(0); - - /* - * Retrieve the entire semaphore array, and verify that the general - * semaphore information is still correct. - */ - if ((semsi = malloc(size)) == NULL) e(0); - - len = size; - if (sysctl(mib, __arraycount(mib), semsi, &len, NULL, 0) != 0) e(0); - if (len != size) e(0); - - if (sizeof(semsi->seminfo) != sizeof(seminfo)) e(0); - if (memcmp(&semsi->seminfo, &seminfo, sizeof(semsi->seminfo)) != 0) - e(0); - - /* Verify that our semaphore sets are each in the array once. */ - slot[0] = slot[1] = -1; - for (i = 0; i < seminfo.semmni; i++) { - if (!(semsi->semids[i].sem_perm.mode & SEM_ALLOC)) - continue; - - id2 = IXSEQ_TO_IPCID(i, semsi->semids[i].sem_perm); - if (id2 == id[0]) { - if (slot[0] != -1) e(0); - slot[0] = i; - } else if (id2 == id[1]) { - if (slot[1] != -1) e(0); - slot[1] = i; - } - } - - if (slot[0] < 0) e(0); - if (slot[1] < 0) e(0); - - /* Check that the semaphore sets have the expected properties. */ - semds = &semsi->semids[slot[0]]; - if (semds->sem_perm.uid != geteuid()) e(0); - if (semds->sem_perm.gid != getegid()) e(0); - if (semds->sem_perm.cuid != geteuid()) e(0); - if (semds->sem_perm.cgid != getegid()) e(0); - if (semds->sem_perm.mode != (SEM_ALLOC | 0612)) e(0); - if (semds->sem_perm._key != KEY_A) e(0); - if (semds->sem_nsems != 5) e(0); - if (semds->sem_otime != 0) e(0); - if (semds->sem_ctime == 0) e(0); - - semds = &semsi->semids[slot[1]]; - if (semds->sem_perm.uid != geteuid()) e(0); - if (semds->sem_perm.gid != getegid()) e(0); - if (semds->sem_perm.cuid != geteuid()) e(0); - if (semds->sem_perm.cgid != getegid()) e(0); - if (semds->sem_perm.mode != (SEM_ALLOC | 0650)) e(0); - if (semds->sem_perm._key != IPC_PRIVATE) e(0); - if (semds->sem_nsems != 3) e(0); - if (semds->sem_otime != 0) e(0); - if (semds->sem_ctime == 0) e(0); - - /* Make sure that non-root users can see them as well. */ - spawn(&child, test88f_child, DROP_ALL); - - snd(&child, id[0]); - snd(&child, id[1]); - - collect(&child); - - /* Clean up, and verify that the sets are no longer in the listing. */ - if (semctl(id[0], 0, IPC_RMID) != 0) e(0); - if (semctl(id[1], 0, IPC_RMID) != 0) e(0); - - len = size; - if (sysctl(mib, __arraycount(mib), semsi, &len, NULL, 0) != 0) e(0); - if (len != size) e(0); - - for (i = 0; i < seminfo.semmni; i++) { - if (!(semsi->semids[i].sem_perm.mode & SEM_ALLOC)) - continue; - - id2 = IXSEQ_TO_IPCID(i, semsi->semids[i].sem_perm); - if (id2 == id[0]) e(0); - if (id2 == id[1]) e(0); - } - - free(semsi); -} - -/* - * Initialize the test. - */ -static void -test88_init(void) -{ - static const int mib[] = { CTL_KERN, KERN_SYSVIPC, KERN_SYSVIPC_SEM }; - struct group *gr; - size_t len; - int i; - - /* Start with full root privileges. */ - setuid(geteuid()); - - if ((gr = getgrnam(ROOT_GROUP)) == NULL) e(0); - - setgid(gr->gr_gid); - setegid(gr->gr_gid); - - /* - * Verify that the IPC service is running at all. If not, there is - * obviously no point in running this test. - */ - len = sizeof(i); - if (sysctl(mib, __arraycount(mib), &i, &len, NULL, 0) != 0) e(0); - if (len != sizeof(i)) e(0); - - if (i == 0) { - printf("skipped\n"); - cleanup(); - exit(0); - } - - /* Allocate a memory page followed by an unmapped page. */ - page_size = getpagesize(); - page_ptr = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0); - if (page_ptr == MAP_FAILED) e(0); - bad_ptr = page_ptr + page_size; - if (munmap(bad_ptr, page_size) != 0) e(0); -} - -/* - * Test program for SysV IPC semaphores. - */ -int -main(int argc, char ** argv) -{ - int i, m; - - start(88); - - test88_init(); - - if (argc == 2) - m = atoi(argv[1]); - else - m = 0xFF; - - for (i = 0; i < ITERATIONS; i++) { - if (m & 0x01) test88a(); - if (m & 0x02) test88b(); - if (m & 0x04) test88c(); - if (m & 0x08) test88d(); - if (m & 0x10) test88e(); - if (m & 0x20) test88f(); - } - - quit(); -} diff --git a/minix/tests/test90.c b/minix/tests/test90.c deleted file mode 100644 index 8fc25fdb7..000000000 --- a/minix/tests/test90.c +++ /dev/null @@ -1,4185 +0,0 @@ -/* Advanced tests for UNIX Domain Sockets - by D.C. van Moolenbroek */ -/* - * This is a somewhat random collection of in-depth tests, complementing the - * more general functionality tests in test56. The overall test set is still - * by no means expected to be "complete." The subtests are in random order. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "socklib.h" - -#define ITERATIONS 1 - -#define SOCK_PATH_A "sock_a" -#define SOCK_PATH_B "sock_b" -#define SOCK_PATH_C "sock_c" -#define SOCK_PATH_D "sock_d" - -#define SOCK_PATH_A_X ".//sock_a" -#define SOCK_PATH_A_Y "./././sock_a" - -#define PRINT_STATS 0 - -/* - * Check that the given returned socket address matches the given path. A NULL - * path may be passed in to indicate the result should be for an unbound - * socket. - */ -static void -check_addr(struct sockaddr_un * sun, socklen_t len, const char * path) -{ - - if (len < offsetof(struct sockaddr_un, sun_path)) e(0); - - if (sun->sun_family != AF_UNIX) e(0); - if (sun->sun_len != len - ((path != NULL) ? 1 : 0)) e(0); - - len -= offsetof(struct sockaddr_un, sun_path); - - if (path != NULL) { - if (len != strlen(path) + 1) e(0); - if (sun->sun_path[len - 1] != '\0') e(0); - if (strcmp(sun->sun_path, path)) e(0); - } else - if (len != 0) e(0); -} - -/* - * Get a socket of the given type, bound to the given path. Return the file - * descriptor, as well as the bound addres in 'sun'. - */ -static int -get_bound_socket(int type, const char * path, struct sockaddr_un * sun) -{ - int fd; - - if ((fd = socket(AF_UNIX, type, 0)) < 0) e(0); - - (void)unlink(path); - - memset(sun, 0, sizeof(*sun)); - sun->sun_family = AF_UNIX; - strlcpy(sun->sun_path, path, sizeof(sun->sun_path)); - - if (bind(fd, (struct sockaddr *)sun, sizeof(*sun)) != 0) e(0); - - return fd; -} - -/* - * Get a pair of connected sockets. - */ -static void -get_socket_pair(int type, int fd[2]) -{ - struct sockaddr_un sunA, sunB; - - if ((type & ~SOCK_FLAGS_MASK) == SOCK_DGRAM) { - fd[0] = get_bound_socket(type, SOCK_PATH_A, &sunA); - fd[1] = get_bound_socket(type, SOCK_PATH_B, &sunB); - - if (connect(fd[0], (struct sockaddr *)&sunB, - sizeof(sunB)) != 0) e(0); - if (connect(fd[1], (struct sockaddr *)&sunA, - sizeof(sunA)) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); - } else - if (socketpair(AF_UNIX, type, 0, fd) != 0) e(0); -} - -/* - * Return the receive buffer size of the given socket. - */ -static int -get_rcvbuf_len(int fd) -{ - socklen_t len; - int val; - - len = sizeof(val); - if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len) != 0) e(0); - - if (len != sizeof(val)) e(0); - if (val <= 0) e(0); - - return val; -} - -static const enum state unix_connect_states[] = { - S_NEW, S_N_SHUT_R, S_N_SHUT_W, S_N_SHUT_RW, - S_BOUND, S_LISTENING, S_L_SHUT_R, S_L_SHUT_W, - S_L_SHUT_RW, S_CONNECTING, S_CONNECTED, S_ACCEPTED, - S_SHUT_R, S_SHUT_W, S_SHUT_RW, S_RSHUT_R, - S_RSHUT_W, S_RSHUT_RW, S_SHUT2_R, S_SHUT2_W, - S_SHUT2_RW, S_PRE_EOF, S_AT_EOF, S_POST_EOF, - S_PRE_SHUT_R, S_EOF_SHUT_R, S_POST_SHUT_R, S_PRE_SHUT_W, - S_EOF_SHUT_W, S_POST_SHUT_W, S_PRE_SHUT_RW, S_EOF_SHUT_RW, - S_POST_SHUT_RW, S_AT_RESET, S_POST_RESET, S_POST_FAILED - /* - * It is impossible to generate the S_PRE_RESET state: we can - * only generate a reset on a connected socket for which the - * other end is pending acceptance, by closing the listening - * socket. That means we cannot send data to the connected end - * (from the listening socket) before triggering the reset. - * - * It is impossible to generate the S_FAILED state: even a non- - * blocking connect will always fail immediately when it cannot - * connect to the target. - */ -}; - -static const int unix_connect_results[][__arraycount(unix_connect_states)] = { - [C_ACCEPT] = { - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EAGAIN, -ECONNABORTED, -ECONNABORTED, - -ECONNABORTED, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - }, - [C_BIND] = { - 0, 0, 0, 0, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, 0, 0, -EINVAL, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_CONNECT] = { - 0, 0, 0, 0, - 0, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EALREADY, -EISCONN, -EISCONN, - -EISCONN, -EISCONN, -EISCONN, -EISCONN, - -EISCONN, -EISCONN, -EISCONN, -EISCONN, - -EISCONN, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_GETPEERNAME] = { - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, -ENOTCONN, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - }, - [C_GETSOCKNAME] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_GETSOCKOPT_ERR] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, -ECONNRESET, 0, 0, - }, - [C_GETSOCKOPT_KA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_GETSOCKOPT_RB] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_IOCTL_NREAD] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_LISTEN] = { - -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, - 0, 0, 0, 0, - 0, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, - -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, - -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, - -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, -EDESTADDRREQ, - }, - [C_RECV] = { - -ENOTCONN, 0, -ENOTCONN, 0, - -ENOTCONN, -ENOTCONN, 0, -ENOTCONN, - 0, -EAGAIN, -EAGAIN, -EAGAIN, - 0, -EAGAIN, 0, -EAGAIN, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1, - 0, 0, 0, 0, - 0, -ECONNRESET, 0, -ENOTCONN, - }, - [C_RECVFROM] = { - -ENOTCONN, 0, -ENOTCONN, 0, - -ENOTCONN, -ENOTCONN, 0, -ENOTCONN, - 0, -EAGAIN, -EAGAIN, -EAGAIN, - 0, -EAGAIN, 0, -EAGAIN, - 0, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1, - 0, 0, 0, 0, - 0, -ECONNRESET, 0, -ENOTCONN, - }, - [C_SEND] = { - -ENOTCONN, -ENOTCONN, -EPIPE, -EPIPE, - -ENOTCONN, -ENOTCONN, -ENOTCONN, -EPIPE, - -EPIPE, -EAGAIN, 1, 1, - 1, -EPIPE, -EPIPE, -EPIPE, - 1, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -ECONNRESET, -EPIPE, -ENOTCONN, - }, - [C_SENDTO] = { - -ENOTCONN, -ENOTCONN, -EPIPE, -EPIPE, - -ENOTCONN, -ENOTCONN, -ENOTCONN, -EPIPE, - -EPIPE, -EAGAIN, 1, 1, - 1, -EPIPE, -EPIPE, -EPIPE, - 1, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -EPIPE, -EPIPE, -EPIPE, - -EPIPE, -ECONNRESET, -EPIPE, -ENOTCONN, - }, - [C_SELECT_R] = { - 1, 1, 1, 1, - 1, 0, 1, 1, - 1, 0, 0, 0, - 1, 0, 1, 0, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - }, - [C_SELECT_W] = { - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 0, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - }, - [C_SELECT_X] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SETSOCKOPT_BC] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SETSOCKOPT_KA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SETSOCKOPT_L] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SETSOCKOPT_RA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SHUTDOWN_R] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SHUTDOWN_RW] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, - [C_SHUTDOWN_W] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }, -}; - -/* - * Set up a connection-oriented socket file descriptor in the requested state - * and pass it to socklib_sweep_call() along with local and remote addresses - * and their length. - */ -static int -unix_connect_sweep(int domain, int type, int protocol __unused, - enum state state, enum call call) -{ - struct sockaddr_un sunA, sunB, sunC; - char buf[1]; - socklen_t len; - fd_set fds; - int r, fd, fd2, fd3, tmpfd, val, fl; - - (void)unlink(SOCK_PATH_A); - (void)unlink(SOCK_PATH_B); - (void)unlink(SOCK_PATH_C); - - memset(&sunA, 0, sizeof(sunA)); - sunA.sun_family = AF_UNIX; - strlcpy(sunA.sun_path, SOCK_PATH_A, sizeof(sunA.sun_path)); - - fd = fd3 = -1; - - fd2 = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_B, &sunB); - - if (listen(fd2, 1) == -1) e(0); - - switch (state) { - case S_NEW: - case S_N_SHUT_R: - case S_N_SHUT_W: - case S_N_SHUT_RW: - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - switch (state) { - case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - break; - - case S_BOUND: - case S_LISTENING: - case S_L_SHUT_R: - case S_L_SHUT_W: - case S_L_SHUT_RW: - fd = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_A, - &sunA); - - if (state == S_BOUND) - break; - - if (listen(fd, 1) == -1) e(0); - - switch (state) { - case S_L_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_L_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_L_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - break; - - case S_CONNECTING: - /* - * The following block is nonportable. On NetBSD, the - * LOCAL_CONNWAIT socket option is present but seems somewhat.. - * under-tested. On Linux, it is not possible to put a UNIX - * domain socket in a connecting state. - */ - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) - e(0); - - if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != -1) - e(0); - if (errno != EINPROGRESS) e(0); - - break; - - case S_CONNECTED: - case S_ACCEPTED: - case S_SHUT_R: - case S_SHUT_W: - case S_SHUT_RW: - case S_RSHUT_R: - case S_RSHUT_W: - case S_RSHUT_RW: - case S_SHUT2_R: - case S_SHUT2_W: - case S_SHUT2_RW: - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) - e(0); - - len = sizeof(sunC); - if ((fd3 = accept(fd2, (struct sockaddr *)&sunC, &len)) < 0) - e(0); - - if ((fl = fcntl(fd3, F_GETFL)) == -1) e(0); - if (fcntl(fd3, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - - /* Just to make sure, wait for the socket to be connected. */ - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, NULL, &fds, NULL, NULL) != 1) e(0); - - switch (state) { - case S_SHUT_R: - case S_SHUT2_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_SHUT_W: - case S_SHUT2_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_SHUT_RW: - case S_SHUT2_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - switch (state) { - case S_RSHUT_R: - case S_SHUT2_R: if (shutdown(fd3, SHUT_RD)) e(0); break; - case S_RSHUT_W: - case S_SHUT2_W: if (shutdown(fd3, SHUT_WR)) e(0); break; - case S_RSHUT_RW: - case S_SHUT2_RW: if (shutdown(fd3, SHUT_RDWR)) e(0); break; - default: break; - } - - if (state == S_ACCEPTED) { - tmpfd = fd; - fd = fd3; - fd3 = tmpfd; - } - - break; - - case S_PRE_EOF: - case S_AT_EOF: - case S_POST_EOF: - case S_PRE_SHUT_R: - case S_EOF_SHUT_R: - case S_POST_SHUT_R: - case S_PRE_SHUT_W: - case S_EOF_SHUT_W: - case S_POST_SHUT_W: - case S_PRE_SHUT_RW: - case S_EOF_SHUT_RW: - case S_POST_SHUT_RW: - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) - e(0); - - len = sizeof(sunC); - if ((fd3 = accept(fd2, (struct sockaddr *)&sunC, &len)) < 0) - e(0); - - if ((fl = fcntl(fd3, F_GETFL)) == -1) e(0); - if (fcntl(fd3, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - - if (send(fd3, "", 1, 0) != 1) e(0); - - if (close(fd3) != 0) e(0); - fd3 = -1; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - - switch (state) { - case S_AT_EOF: - case S_EOF_SHUT_R: - case S_EOF_SHUT_W: - case S_EOF_SHUT_RW: - if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); - break; - case S_POST_EOF: - case S_POST_SHUT_R: - case S_POST_SHUT_W: - case S_POST_SHUT_RW: - if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); - if (recv(fd, buf, sizeof(buf), 0) != 0) e(0); - break; - default: - break; - } - - switch (state) { - case S_PRE_SHUT_R: - case S_EOF_SHUT_R: - case S_POST_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_PRE_SHUT_W: - case S_EOF_SHUT_W: - case S_POST_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_PRE_SHUT_RW: - case S_EOF_SHUT_RW: - case S_POST_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - break; - - case S_AT_RESET: - case S_POST_RESET: - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) - e(0); - - /* - * Closing the listening socket before the connection has been - * accepted should generate ECONNRESET on the connected socket. - * Well, should.. we choose to do that. So does Linux. NetBSD - * just returns EOF for that case. There are really no strong - * arguments for either behavior. - */ - if (close(fd2) != 0) e(0); - - if (state == S_POST_RESET) - (void)recv(fd, buf, sizeof(buf), 0); - - /* Recreate the listening socket just for consistency. */ - fd2 = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_B, - &sunB); - - if (listen(fd2, 1) == -1) e(0); - - break; - - case S_POST_FAILED: - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - memset(&sunC, 0, sizeof(sunC)); - sunC.sun_family = AF_UNIX; - strlcpy(sunC.sun_path, SOCK_PATH_C, sizeof(sunC.sun_path)); - - r = connect(fd, (struct sockaddr *)&sunC, sizeof(sunC)); - if (r != -1 || errno != ENOENT) - e(0); - - break; - - default: - e(0); - } - - r = socklib_sweep_call(call, fd, (struct sockaddr *)&sunA, - (struct sockaddr *)&sunB, sizeof(struct sockaddr_un)); - - if (fd >= 0 && close(fd) != 0) e(0); - if (fd2 >= 0 && close(fd2) != 0) e(0); - if (fd3 >= 0 && close(fd3) != 0) e(0); - - (void)unlink(SOCK_PATH_A); - (void)unlink(SOCK_PATH_B); - - return r; -} - -static const enum state unix_dgram_states[] = { - S_NEW, S_N_SHUT_R, S_N_SHUT_W, S_N_SHUT_RW, - S_BOUND, S_CONNECTED, S_SHUT_R, S_SHUT_W, - S_SHUT_RW, S_RSHUT_R, S_RSHUT_W, S_RSHUT_RW, - S_SHUT2_R, S_SHUT2_W, S_SHUT2_RW, S_PRE_RESET, - S_AT_RESET, S_POST_RESET -}; - -static const int unix_dgram_results[][__arraycount(unix_dgram_states)] = { - [C_ACCEPT] = { - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, - }, - [C_BIND] = { - 0, 0, 0, 0, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, -EINVAL, - }, - [C_CONNECT] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, -ECONNREFUSED, - -ECONNREFUSED, -ECONNREFUSED, - }, - [C_GETPEERNAME] = { - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, -ENOTCONN, - -ENOTCONN, -ENOTCONN, - }, - [C_GETSOCKNAME] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_GETSOCKOPT_ERR] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, -ECONNRESET, - -ECONNRESET, 0, - }, - [C_GETSOCKOPT_KA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_GETSOCKOPT_RB] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_IOCTL_NREAD] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 1, - 0, 0, - }, - [C_LISTEN] = { - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, - }, - [C_RECV] = { - -EAGAIN, 0, -EAGAIN, 0, - -EAGAIN, -EAGAIN, 0, -EAGAIN, - 0, -EAGAIN, -EAGAIN, -EAGAIN, - 0, -EAGAIN, 0, 1, - -ECONNRESET, -EAGAIN, - }, - [C_RECVFROM] = { - -EAGAIN, 0, -EAGAIN, 0, - -EAGAIN, -EAGAIN, 0, -EAGAIN, - 0, -EAGAIN, -EAGAIN, -EAGAIN, - 0, -EAGAIN, 0, 1, - -ECONNRESET, -EAGAIN, - }, - [C_SEND] = { - -EDESTADDRREQ, -EDESTADDRREQ, -EPIPE, -EPIPE, - -EDESTADDRREQ, 1, 1, -EPIPE, - -EPIPE, -ENOBUFS, 1, -ENOBUFS, - -ENOBUFS, -EPIPE, -EPIPE, -ECONNRESET, - -ECONNRESET, -EDESTADDRREQ, - }, - [C_SENDTO] = { - 1, 1, -EPIPE, -EPIPE, - 1, 1, 1, -EPIPE, - -EPIPE, -ENOBUFS, 1, -ENOBUFS, - -ENOBUFS, -EPIPE, -EPIPE, -ECONNRESET, - -ECONNRESET, -ECONNREFUSED, - }, - [C_SELECT_R] = { - 0, 1, 0, 1, - 0, 0, 1, 0, - 1, 0, 0, 0, - 1, 0, 1, 1, - 1, 0, - }, - [C_SELECT_W] = { - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, 1, - }, - [C_SELECT_X] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SETSOCKOPT_BC] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SETSOCKOPT_KA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SETSOCKOPT_L] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SETSOCKOPT_RA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SHUTDOWN_R] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SHUTDOWN_RW] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, - [C_SHUTDOWN_W] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, - }, -}; - -/* - * Set up a datagram socket file descriptor in the requested state and pass it - * to socklib_sweep_call() along with local and remote addresses and their - * length. - */ -static int -unix_dgram_sweep(int domain __unused, int type, int protocol __unused, - enum state state, enum call call) -{ - struct sockaddr_un sunA, sunB; - char buf[1]; - int r, fd, fd2; - - (void)unlink(SOCK_PATH_A); - (void)unlink(SOCK_PATH_B); - - /* Create a bound remote socket. */ - fd2 = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_B, &sunB); - - switch (state) { - case S_NEW: - case S_N_SHUT_R: - case S_N_SHUT_W: - case S_N_SHUT_RW: - memset(&sunA, 0, sizeof(sunA)); - sunA.sun_family = AF_UNIX; - strlcpy(sunA.sun_path, SOCK_PATH_A, sizeof(sunA.sun_path)); - - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) - e(0); - - switch (state) { - case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - break; - - case S_BOUND: - case S_CONNECTED: - case S_SHUT_R: - case S_SHUT_W: - case S_SHUT_RW: - case S_RSHUT_R: - case S_RSHUT_W: - case S_RSHUT_RW: - case S_SHUT2_R: - case S_SHUT2_W: - case S_SHUT2_RW: - case S_PRE_RESET: - case S_AT_RESET: - case S_POST_RESET: - fd = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_A, - &sunA); - - if (state == S_BOUND) - break; - - if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) - e(0); - - switch (state) { - case S_SHUT_R: - case S_SHUT2_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_SHUT_W: - case S_SHUT2_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_SHUT_RW: - case S_SHUT2_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - switch (state) { - case S_RSHUT_R: - case S_SHUT2_R: if (shutdown(fd2, SHUT_RD)) e(0); break; - case S_RSHUT_W: - case S_SHUT2_W: if (shutdown(fd2, SHUT_WR)) e(0); break; - case S_RSHUT_RW: - case S_SHUT2_RW: if (shutdown(fd2, SHUT_RDWR)) e(0); break; - case S_PRE_RESET: - case S_AT_RESET: - case S_POST_RESET: - if (sendto(fd2, "", 1, 0, (struct sockaddr *)&sunA, - sizeof(sunA)) != 1) e(0); - - if (close(fd2) != 0) e(0); - fd2 = -1; - - if (state != S_PRE_RESET) { - if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); - } - if (state == S_POST_RESET) { - (void)recv(fd, buf, sizeof(buf), 0); - } - default: - break; - } - - break; - - default: - fd = -1; - e(0); - } - - r = socklib_sweep_call(call, fd, (struct sockaddr *)&sunA, - (struct sockaddr *)&sunB, sizeof(struct sockaddr_un)); - - if (close(fd) != 0) e(0); - if (fd2 != -1 && close(fd2) != 0) e(0); - - (void)unlink(SOCK_PATH_A); - (void)unlink(SOCK_PATH_B); - - return r; -} - -/* - * Sweep test for socket calls versus socket states of all socket types. - */ -static void -test90a(void) -{ - - subtest = 1; - - socklib_sweep(AF_UNIX, SOCK_STREAM, 0, unix_connect_states, - __arraycount(unix_connect_states), - (const int *)unix_connect_results, unix_connect_sweep); - - socklib_sweep(AF_UNIX, SOCK_SEQPACKET, 0, unix_connect_states, - __arraycount(unix_connect_states), - (const int *)unix_connect_results, unix_connect_sweep); - - socklib_sweep(AF_UNIX, SOCK_DGRAM, 0, unix_dgram_states, - __arraycount(unix_dgram_states), (const int *)unix_dgram_results, - unix_dgram_sweep); - -} - -/* - * Test for large sends and receives with MSG_WAITALL. - */ -static void -test90b(void) -{ - int fd[2]; - - subtest = 2; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0); - - socklib_large_transfers(fd); -} - -/* - * A randomized producer-consumer test for datagram sockets. - */ -static void -sub90c(int type) -{ - char *buf; - time_t t; - socklen_t len, size; - ssize_t r; - pid_t pid; - unsigned int count; - int i, fd[2], rcvlen, status, exp, flags, num, stat[2] = { 0, 0 }; - - get_socket_pair(type, fd); - - size = rcvlen = get_rcvbuf_len(fd[0]); - - if ((buf = malloc(size)) == NULL) e(0); - - t = time(NULL); - - /* - * We vary small versus large (random) send and receive sizes, - * splitting the entire transfer in four phases along those lines. - * - * In theory, the use of an extra system call and the use of MSG_PEEK - * both contribute to the expectation that the consumer side will fall - * behind the producer. In this case, we cannot vary receive sizes to - * compensate. This not appear to be a major problem here, though. - */ -#define NR_PACKETS (256 * 1024) - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - if (close(fd[0]) != 0) e(0); - - srand48(t + 1); - - for (count = 0; count < NR_PACKETS; ) { - if (count < NR_PACKETS / 2) - len = lrand48() % 64; - else - len = lrand48() % size; - - num = lrand48() % 16; - flags = 0; - if (num & 1) flags |= MSG_PEEK; - if (num & 2) flags |= MSG_WAITALL; - if (num & 4) flags |= MSG_DONTWAIT; - if (num & 8) { - /* - * Obviously there are race conditions here but - * the returned number should be accurate if - * not zero. Hopefully it's not always zero. - */ - if (ioctl(fd[1], FIONREAD, &exp) != 0) e(0); - if (exp < 0 || exp > rcvlen) e(0); - } else - exp = 0; - - stat[0]++; - - /* - * A lame approach to preventing unbounded spinning on - * ENOBUFS on the producer side. - */ - if (type == SOCK_DGRAM) - (void)send(fd[1], "", 1, MSG_DONTWAIT); - - if ((r = recv(fd[1], buf, len, flags)) == -1) { - if (errno != EWOULDBLOCK) e(0); - if (exp > 0) e(0); - - stat[1]++; - - continue; - } - - if (exp != 0) { - if (r == len && exp < r) e(0); - else if (r < len && exp != r) e(0); - } - - if (r >= 2 && - r > ((size_t)(unsigned char)buf[0] << 8) + - (size_t)(unsigned char)buf[1]) e(0); - - for (i = 2; i < r; i++) - if (buf[i] != (char)i) e(0); - - if (!(flags & MSG_PEEK)) - count++; - } - -#if PRINT_STATS - /* - * The second and third numbers should ideally be a large but - * non-dominating fraction of the first one. - */ - printf("RECV: total %d again %d\n", stat[0], stat[1]); -#endif - - if (close(fd[1]) != 0) e(0); - exit(errct); - case -1: - e(0); - } - - if (close(fd[1]) != 0) e(0); - - srand48(t); - - for (count = 0; count < NR_PACKETS; ) { - if (count < NR_PACKETS / 4 || - (count >= NR_PACKETS / 2 && count < NR_PACKETS * 3 / 4)) - len = lrand48() % 64; - else - len = lrand48() % size; - - buf[0] = (len >> 8) & 0xff; - buf[1] = len & 0xff; - for (i = 2; i < len; i++) - buf[i] = i; - - flags = (lrand48() % 2) ? MSG_DONTWAIT : 0; - - r = send(fd[0], buf, len, flags); - - if (r != len) { - if (r != -1) e(0); - - if (errno != EMSGSIZE && errno != EWOULDBLOCK && - errno != ENOBUFS) e(0); - - if (errno == ENOBUFS || errno == EWOULDBLOCK) { - /* - * As stated above: lame. Ideally we would - * continue only when the receiver side drains - * the queue, but it may block once it has done - * so. Instead, by going through consumer - * "tokens" we will ultimately block here and - * let the receiver catch up. - */ - if (type == SOCK_DGRAM && errno == ENOBUFS) - (void)recv(fd[0], buf, 1, 0); - - stat[0]++; - stat[1]++; - } - continue; - } else - stat[0]++; - - if (count % (NR_PACKETS / 4) == 0) - sleep(1); - - count++; - } - -#if PRINT_STATS - /* - * The second number should ideally be a large but non-dominating - * fraction of the first one. - */ - printf("SEND: total %d again %d\n", stat[0], stat[1]); -#endif - - free(buf); - - if (close(fd[0]) != 0) e(0); - - if (waitpid(pid, &status, 0) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); -} - -/* - * A randomized producer-consumer test. As part of this, we also perform very - * basic bulk functionality tests of FIONREAD, MSG_PEEK, MSG_DONTWAIT, and - * MSG_WAITALL. - */ -static void -test90c(void) -{ - int fd[2]; - - subtest = 4; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0); - - socklib_producer_consumer(fd); - - sub90c(SOCK_SEQPACKET); - - sub90c(SOCK_DGRAM); -} - -/* - * Test that immediately accepted non-blocking connect requests to a listening - * socket with LOCAL_CONNWAIT turned on, return OK rather than EINPROGRESS. - * This requires a hack in libsockevent. - */ -static void -test90d(void) -{ - struct sockaddr_un sunA, sunB; - socklen_t len; - pid_t pid; - int fd, fd2, fd3, val, status; - - subtest = 4; - - /* - * First ensure that a non-blocking connect to a listening socket that - * does not have a accept call blocked on it, fails with EINPROGRESS. - */ - fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sunA); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (listen(fd, 1) != 0) e(0); - - if ((fd2 = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0); - if (errno != EINPROGRESS) e(0); - - len = sizeof(sunB); - if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0); - check_addr(&sunB, len, NULL); - - if (close(fd) != 0) e(0); - if (close(fd2) != 0) e(0); - if (close(fd3) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - - /* - * Second, ensure that a blocking connect eventually does return - * success if an accept call is made later on. - */ - fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sunA); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (listen(fd, 1) != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - sleep(1); - - len = sizeof(sunB); - if ((fd2 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) - e(0); - check_addr(&sunB, len, NULL); - - exit(errct); - case -1: - e(0); - } - - if (close(fd) != 0) e(0); - - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (close(fd) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - - if (waitpid(pid, &status, 0) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * Finally, test the most implementation-complex case: a non-blocking - * connect should succeed (i.e., yield return code 0) immediately if - * there is a accept call blocked on it. - */ - fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sunA); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (listen(fd, 1) != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - len = sizeof(sunB); - if ((fd2 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) - e(0); - check_addr(&sunB, len, SOCK_PATH_B); - - exit(errct); - case -1: - e(0); - } - - if (close(fd) != 0) e(0); - - sleep(1); - - fd = get_bound_socket(SOCK_STREAM | SOCK_NONBLOCK, SOCK_PATH_B, &sunB); - - if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (close(fd) != 0) e(0); - - if (waitpid(pid, &status, 0) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); -} - -/* - * Test self-connecting datagram sockets. - */ -static void -test90e(void) -{ - struct sockaddr_un sunA, sunB, sunC; - socklen_t len; - char buf[3]; - pid_t pid; - int fdA, fdB, val, status; - - subtest = 5; - - fdA = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sunA); - fdB = get_bound_socket(SOCK_DGRAM, SOCK_PATH_B, &sunB); - - /* Connect the socket to itself, and attempt to communicate. */ - if (connect(fdA, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (send(fdA, "abc", 3, 0) != 3) e(0); - - if (recv(fdA, buf, sizeof(buf), 0) != 3) e(0); - if (strncmp(buf, "abc", 3)) e(0); - - /* Reconnect the socket to another target. */ - if (connect(fdA, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fdA, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0); - if (val != 0) e(0); - - if (send(fdA, "def", 3, 0) != 3) e(0); - - memset(&sunC, 0, sizeof(sunC)); - len = sizeof(sunC); - if (recvfrom(fdB, buf, sizeof(buf), 0, (struct sockaddr *)&sunC, - &len) != 3) e(0); - check_addr(&sunC, len, SOCK_PATH_A); - if (strncmp(buf, "def", 3)) e(0); - - /* Reconnect the socket to itself again. */ - if (connect(fdA, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fdA, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0); - if (val != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - if (recv(fdA, buf, sizeof(buf), 0) != 3) e(0); - if (strncmp(buf, "ghi", 3)) e(0); - - exit(errct); - case -1: - e(0); - } - - sleep(1); - - if (send(fdA, "ghi", 3, 0) != 3) e(0); - - if (waitpid(pid, &status, 0) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - if (close(fdA) != 0) e(0); - if (close(fdB) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); -} - -/* - * Test multiple blocked calls getting resumed (or not) upon connect(2) success - * or failure. This test uses LOCAL_CONNWAIT. TODO: rewrite this to use - * interprocess communication rather than the current carefully arranged and - * rather brittle timing approach. - */ -static void -sub90f(unsigned int test) -{ - struct sockaddr_un sun; - pid_t pid[4], apid; - char buf[1]; - unsigned int i; - socklen_t len; - int r, fd, fd2, fl, val, status; - - fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sun); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (listen(fd, 1) != 0) e(0); - - apid = fork(); - switch (apid) { - case 0: - errct = 0; - - sleep(3); - - if (test < 2) { - len = sizeof(sun); - if ((fd2 = accept(fd, (struct sockaddr *)&sun, - &len)) < 0) e(0); - - sleep(2); - - if (close(fd2) != 0) e(0); - } - - if (close(fd) != 0) e(0); - - exit(errct); - case -1: - e(0); - } - - if (close(fd) != 0) e(0); - - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0); - - for (i = 0; i < __arraycount(pid); i++) { - pid[i] = fork(); - switch (pid[i]) { - case 0: - errct = 0; - - sleep((i == 0) ? 1 : 2); - - if ((i & 1) == 0) { - r = send(fd, "", 1, 0); - - switch (test) { - case 0: - case 1: - if (r != 1) e(0); - break; - case 3: - if (i == 0) { - if (r != -1) e(0); - if (errno != ECONNRESET) e(0); - break; - } - /* FALLTHROUGH */ - case 2: - if (r != -1) e(0); - if (errno != ENOTCONN) e(0); - } - } else { - r = recv(fd, buf, sizeof(buf), 0); - - if (test >= 2) { - if (r != -1) e(0); - if (errno != ENOTCONN) e(0); - } else - if (r != 0) e(0); - } - - exit(errct); - case -1: - e(0); - } - } - - if (test & 1) { - if ((fl = fcntl(fd, F_GETFL)) == -1) e(0); - if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - } - - r = connect(fd, (struct sockaddr *)&sun, sizeof(sun)); - - if (test & 1) { - if (r != -1) e(0); - if (errno != EINPROGRESS) e(0); - - if (fcntl(fd, F_SETFL, fl) != 0) e(0); - - sleep(4); - } else { - if (test >= 2) { - if (r != -1) e(0); - if (errno != ECONNRESET) e(0); - } else - if (r != 0) e(0); - - sleep(1); - } - - /* - * If the connect failed, collect the senders and receivers. - * Otherwise, collect just the senders. - */ - for (i = 0; i < __arraycount(pid); i++) { - r = waitpid(pid[i], &status, WNOHANG); - if (r == pid[i]) { - if (test < 2 && (i & 1)) e(0); - if (!WIFEXITED(status)) e(0); - if (WEXITSTATUS(status) != 0) e(0); - } else if (r == 0) { - if (test >= 2 || !(i & 1)) e(0); - } else - e(0); - } - - if (close(fd) != 0) e(0); - - /* Wait for, and collect the accepting child. */ - if (waitpid(apid, &status, 0) != apid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * If the connect succeeded, collect the receivers, which will - * terminate once the accepting child closes the accepted socket. - */ - if (test < 2) { - if (waitpid(pid[1], &status, 0) != pid[1]) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - if (waitpid(pid[3], &status, 0) != pid[3]) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - } - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Test multiple blocked calls getting resumed (or not) upon connect(2) success - * or failure. In particular, ensure that the error code ends up with the - * right call. - */ -static void -test90f(void) -{ - - subtest = 6; - - /* If a connect succeeds, sends continue but reads block until EOF. */ - sub90f(0); /* blocking connect */ - sub90f(1); /* non-blocking connect */ - - /* If a blocking connect fails, the connect call gets the error. */ - sub90f(2); - - /* If a non-blocking connect fails, the first blocked call gets it. */ - sub90f(3); -} - -/* - * Test whether various calls all return the same expected error code. - */ -static void -sub90g(struct sockaddr_un * sun, int err) -{ - int fd; - - if ((fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0) e(0); - - if (connect(fd, (struct sockaddr *)sun, sizeof(*sun)) != -1) e(0); - if (errno != err) e(0); - - if (close(fd) != 0) e(0); - - if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0); - - if (sendto(fd, "", 1, 0, (struct sockaddr *)sun, sizeof(*sun)) != -1) - e(0); - if (errno != err) e(0); - - if (connect(fd, (struct sockaddr *)sun, sizeof(*sun)) != -1) e(0); - if (errno != err) e(0); - - if (close(fd) != 0) e(0); -} - -/* - * Test for error codes thrown by connect(2) and sendto(2) with problematic - * destinations. In particular, we verify that the errors for sendto(2) are - * the same as for connect(2), just like on NetBSD and Linux, even though - * POSIX does not document all of these under sendto(2). - */ -static void -test90g(void) -{ - struct sockaddr_un sun; - int fd; - - subtest = 7; - - fd = get_bound_socket(SOCK_STREAM, SOCK_PATH_A, &sun); - - sub90g(&sun, EPROTOTYPE); - - if (listen(fd, 1) != 0) e(0); - - sub90g(&sun, EPROTOTYPE); - - if (close(fd) != 0) e(0); - - sub90g(&sun, ECONNREFUSED); - - if (unlink(SOCK_PATH_A) != 0) e(0); - - sub90g(&sun, ENOENT); -} - -/* - * Test addresses returned for unbound connection-type sockets by various - * calls. - */ -static void -sub90h(int type) -{ - struct sockaddr_un sun; - socklen_t len; - char buf[1]; - int fd, fd2, fd3; - - fd = get_bound_socket(type, SOCK_PATH_A, &sun); - - if (listen(fd, 5) != 0) e(0); - - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0); - - /* Test for accept(2), which returns an empty address. */ - memset(&sun, 0, sizeof(sun)); - len = sizeof(sun); - if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0); - check_addr(&sun, len, NULL); - - /* Test for recvfrom(2), which ignores the address pointer. */ - if (send(fd2, "", 1, 0) != 1) e(0); - - memset(&sun, 0, sizeof(sun)); - len = sizeof(sun); - if (recvfrom(fd3, buf, sizeof(buf), 0, (struct sockaddr *)&sun, - &len) != 1) e(0); - if (len != 0) e(0); - if (sun.sun_family != 0) e(0); - if (sun.sun_len != 0) e(0); - - /* Test for getsockname(2), which returns an empty address. */ - memset(&sun, 0, sizeof(sun)); - len = sizeof(sun); - if (getsockname(fd2, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, NULL); - - /* Test for getpeername(2), which returns an empty address. */ - memset(&sun, 0, sizeof(sun)); - len = sizeof(sun); - if (getpeername(fd3, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, NULL); - - if (close(fd) != 0) e(0); - if (close(fd2) != 0) e(0); - if (close(fd3) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Test addresses returned for unbound sockets by various calls. - */ -static void -test90h(void) -{ - struct sockaddr_un sun; - socklen_t len; - char buf[1]; - int fd, fd2; - - subtest = 8; - - /* Connection-type socket tests. */ - sub90h(SOCK_STREAM); - - sub90h(SOCK_SEQPACKET); - - /* Datagram socket tests. */ - fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sun); - - if ((fd2 = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0); - - if (sendto(fd2, "", 1, 0, (struct sockaddr *)&sun, sizeof(sun)) != 1) - e(0); - - /* - * Datagram test for recvfrom(2), which returns no address. This is - * the one result in this subtest that is not specified by POSIX and - * (not so coincidentally) is different between NetBSD and Linux. - * MINIX3 happens to follow Linux behavior for now, but this may be - * changed in the future. - */ - memset(&sun, 0, sizeof(sun)); - len = sizeof(sun); - if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sun, - &len) != 1) e(0); - if (len != 0) e(0); - if (sun.sun_family != 0) e(0); - if (sun.sun_len != 0) e(0); - - /* Datagram test for getsockname(2), which returns an empty address. */ - memset(&sun, 0, sizeof(sun)); - len = sizeof(sun); - if (getsockname(fd2, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, NULL); - - if (close(fd) != 0) e(0); - if (close(fd2) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -#define MAX_FDS 7 - -/* - * Send anywhere from zero to MAX_FDS file descriptors onto a socket, possibly - * along with regular data. Return the result of the sendmsg(2) call, with - * errno preserved. Written to be reusable outside this test set. - */ -static int -send_fds(int fd, const char * data, size_t len, int flags, - struct sockaddr * addr, socklen_t addr_len, int * fds, int nfds) -{ - union { - char buf[CMSG_SPACE(MAX_FDS * sizeof(int))]; - struct cmsghdr cmsg; - } control; - struct msghdr msg; - struct iovec iov; - - assert(nfds >= 0 && nfds <= MAX_FDS); - - iov.iov_base = __UNCONST(data); - iov.iov_len = len; - - memset(&control.cmsg, 0, sizeof(control.cmsg)); - control.cmsg.cmsg_len = CMSG_LEN(nfds * sizeof(int)); - control.cmsg.cmsg_level = SOL_SOCKET; - control.cmsg.cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(&control.cmsg), fds, nfds * sizeof(int)); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &control; - msg.msg_controllen = control.cmsg.cmsg_len; - msg.msg_name = addr; - msg.msg_namelen = addr_len; - - return sendmsg(fd, &msg, flags); -} - -/* - * Receive anywhere from zero to up to MAX_FDS file descriptors from a socket, - * possibly along with regular data. The 'nfds' parameter must point to the - * maximum number of file descriptors to be received. Return the result of the - * recvmsg(2) call, with errno preserved. On success, return the received - * flags in 'rflags', the received file descriptors stored in 'fds' and their - * number stored in 'nfds'. Written to be (somewhat) reusable. - */ -static int -recv_fds(int fd, char * buf, size_t size, int flags, int * rflags, int * fds, - int * nfds) -{ - union { - char buf[CMSG_SPACE(MAX_FDS * sizeof(int))]; - struct cmsghdr cmsg; - } control; - struct msghdr msg; - struct iovec iov; - size_t len; - int r, rnfds; - - assert(*nfds >= 0 && *nfds <= MAX_FDS); - - iov.iov_base = buf; - iov.iov_len = size; - - memset(&control.cmsg, 0, sizeof(control.cmsg)); - control.cmsg.cmsg_len = CMSG_LEN(*nfds * sizeof(int)); - control.cmsg.cmsg_level = SOL_SOCKET; - control.cmsg.cmsg_type = SCM_RIGHTS; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &control; - msg.msg_controllen = control.cmsg.cmsg_len; - - if ((r = recvmsg(fd, &msg, flags)) < 0) - return r; - - if (msg.msg_controllen > 0) { - assert(msg.msg_controllen <= sizeof(control)); - assert(msg.msg_controllen >= sizeof(control.cmsg)); - len = control.cmsg.cmsg_len - CMSG_LEN(0); - assert(len % sizeof(int) == 0); - rnfds = len / sizeof(int); - assert(rnfds <= *nfds); - - memcpy(fds, CMSG_DATA(&control.cmsg), rnfds * sizeof(int)); - } else - rnfds = 0; - - *rflags = msg.msg_flags; - *nfds = rnfds; - return r; -} - -/* - * Generate and send zero or more file descriptors onto a socket, possibly - * along with regular data. Return the result of the sendmsg(2) call, with - * errno preserved. Also return a set of peer FDs for each of the sent file - * descriptors, which should later be used in a call to close_test_fds(). - */ -static int -send_test_fds(int fd, const char * data, size_t len, int flags, int * peers, - int nfds) -{ - int i, r, saved_errno, fds[MAX_FDS], pfd[2]; - - if (nfds > MAX_FDS) e(0); - - for (i = 0; i < nfds; i++) { - if (pipe2(pfd, O_NONBLOCK) != 0) e(0); - - peers[i] = pfd[0]; - fds[i] = pfd[1]; - } - - r = send_fds(fd, data, len, flags, NULL, 0, fds, nfds); - saved_errno = errno; - - for (i = 0; i < nfds; i++) - if (close(fds[i]) != 0) e(0); - - errno = saved_errno; - return r; -} - -/* - * Given an array of peer file descriptors as returned from a call to - * send_test_fds(), test if the original file descriptors have correctly been - * closed, and close all peer file descriptors. The ultimate goal here is to - * detect any possible file descriptor leaks in the UDS service. - */ -static void -close_test_fds(int * peers, int nfds) -{ - char buf[1]; - unsigned int i; - int fd; - - for (i = 0; i < nfds; i++) { - fd = peers[i]; - - /* If the other side is still open, we would get EAGAIN. */ - if (read(fd, buf, sizeof(buf)) != 0) e(0); - - if (close(peers[i]) != 0) e(0); - } -} - -/* - * Receive and close zero or more file descriptors from a socket, possibly - * along with regular data. Return the result of the recvmsg(2) call, with - * errno preserved. - */ -static int -recv_test_fds(int fd, char * buf, size_t size, int flags, int * rflags, - int * nfds) -{ - int i, r, saved_errno, fds[MAX_FDS]; - - if (*nfds > MAX_FDS) e(0); - - if ((r = recv_fds(fd, buf, size, flags, rflags, fds, nfds)) < 0) - return r; - saved_errno = errno; - - for (i = 0; i < *nfds; i++) - if (close(fds[i]) != 0) e(0); - - errno = saved_errno; - return r; -} - -/* - * Test receive requests on various socket states and in various forms. - * Following this function requires a very close look at what is in the - * receive queue versus what is being received. - */ -static void -sub90i_recv(int fd, int type, int state, int test, int sub, int sentfds) -{ - struct msghdr msg; - struct iovec iov; - char data[4]; - unsigned int i; - int res, err, nfds, rflags; - - memset(data, 0, sizeof(data)); - - if (sub & 2) { - rflags = 0; - nfds = sentfds; - res = recv_test_fds(fd, data, (sub & 1) ? 0 : sizeof(data), 0, - &rflags, &nfds); - if (rflags & MSG_CTRUNC) e(0); - if (nfds != 0 && nfds != sentfds) e(0); - if ((type == SOCK_STREAM) && (rflags & MSG_TRUNC)) e(0); - } else { - iov.iov_base = data; - iov.iov_len = (sub & 1) ? 0 : sizeof(data); - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - res = recvmsg(fd, &msg, 0); - if (res >= 0) - rflags = msg.msg_flags; - else - rflags = 0; - nfds = 0; - } - err = errno; - - if (res < -1 || res > (int)sizeof(data)) e(0); - - if (type == SOCK_STREAM) { - if (sub & 1) { - /* - * Zero-size requests should receive no regular data - * and no control data, even if the tail segment is - * zero-sized and terminated. This policy is in place - * for simplicity reasons. - */ - if (res != 0) e(0); - if (nfds != 0) e(0); - if (rflags & MSG_CTRUNC) e(0); - - /* - * Since nothing happened yet, do another, now non- - * zero receive call immediately, and continue as if - * that was the first call. - */ - sub = (sub & ~1) | 2; - nfds = sentfds; - rflags = 0; - res = recv_test_fds(fd, data, sizeof(data), 0, &rflags, - &nfds); - if (rflags & (MSG_TRUNC | MSG_CTRUNC)) e(0); - if (nfds != 0 && nfds != sentfds) e(0); - err = errno; - if (res < -1 || res > (int)sizeof(data)) e(0); - } - - if (state == 0 && !(test & 1) && !(sub & 13)) { - /* - * There are no regular data bytes to be received, and - * the current segment may still be extended (i.e., - * there is no EOF condition), and we are trying to - * receive at least one data byte. This is the - * SO_RCVLOWAT test. - */ - if (res != -1) e(0); - if (err != EWOULDBLOCK) e(0); - if (test == 4) { - /* - * There are still pending file descriptors but - * we cannot get them, due to the SO_RCVLOWAT - * test. This is proper behavior but somewhat - * annoying, because we want to see if UDS - * forgot to close any file descriptors. So, - * we let it force-close them here. - */ - if (shutdown(fd, SHUT_RD) != 0) e(0); - sub |= 8; - } - } else { - i = 0; - if (state == 1) { - if (res < 1) e(0); - if (data[i] != 'A') e(0); - i++; - } - if ((state == 0 && (test & 1)) || - (state == 1 && (test == 1 || test == 3))) { - if (res < i + 1) e(0); - if (data[i] != 'B') e(0); - i++; - } - if ((sub & 4) && (state != 1 || test < 4)) { - if (res < i + 1) e(0); - if (data[i] != 'C') e(0); - i++; - } - if (i != res) e(0); - if (state == 0 && test >= 4) { - if (sub & 2) { - if (nfds != sentfds) e(0); - } else - if (!(rflags & MSG_CTRUNC)) e(0); - } - } - - if (state == 1 && test >= 4) { - /* - * We just read the first segment, but there is a - * second segment with ancillary data. Read it too. - */ - nfds = sentfds; - rflags = 0; - res = recv_test_fds(fd, data, sizeof(data), 0, &rflags, - &nfds); - if (rflags & (MSG_TRUNC | MSG_CTRUNC)) e(0); - if (nfds != sentfds) e(0); /* untouched on failure */ - if (res < -1 || res > (int)sizeof(data)) e(0); - if (test != 5 && !(sub & 12)) { - if (res != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - /* As above. */ - if (shutdown(fd, SHUT_RD) != 0) e(0); - sub |= 8; - } else { - if (res != (test == 5) + !!(sub & 4)) e(0); - if (test == 5 && data[0] != 'B') e(0); - if ((sub & 4) && data[res - 1] != 'C') e(0); - } - } - } else { - if (res != ((state == 1 || (test & 1)) && !(sub & 1))) e(0); - if (state == 0 && test >= 4) { - if (sub & 2) { - if (nfds != sentfds) e(0); - } else - if (!(rflags & MSG_CTRUNC)) e(0); - } - if (res > 0 && data[0] != ((state == 1) ? 'A' : 'B')) e(0); - - if (state == 1) { - nfds = sentfds; - rflags = 0; - res = recv_test_fds(fd, data, sizeof(data), 0, &rflags, - &nfds); - if (res != (test & 1)) e(0); - if (res > 0 && data[0] != 'B') e(0); - if (nfds != ((test >= 4) ? sentfds : 0)) e(0); - } - - if (sub & 4) { - nfds = sentfds; - rflags = 0; - res = recv_test_fds(fd, data, sizeof(data), 0, &rflags, - &nfds); - if (res != 1) e(0); - if (data[0] != 'C') e(0); - if (nfds != 0) e(0); - } - } - - /* - * At this point, there is nothing to receive. Depending on - * whether we closed the socket, we expect EOF or EWOULDBLOCK. - */ - res = recv(fd, data, sizeof(data), 0); - if (type == SOCK_DGRAM || !(sub & 8)) { - if (res != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - } else - if (res != 0) e(0); -} - -/* - * Test send requests on various socket states and in various forms. - */ -static void -sub90i_send(int type, int state, int test, int sub) -{ - char *buf; - int r, res, err, fd[2], peers[2], rcvlen; - - get_socket_pair(type | SOCK_NONBLOCK, fd); - - /* - * State 0: an empty buffer. - * State 1: a non-empty, non-full buffer. - * State 2: a full buffer. - */ - if (state == 2) { - if (type == SOCK_STREAM) { - rcvlen = get_rcvbuf_len(fd[0]); - - if ((buf = malloc(rcvlen)) == NULL) e(0); - - memset(buf, 'A', rcvlen); - - if (send(fd[0], buf, rcvlen, 0) != rcvlen) e(0); - - free(buf); - } else { - while ((r = send(fd[0], "A", 1, 0)) == 1); - if (r != -1) e(0); - if (errno != - ((type == SOCK_SEQPACKET) ? EAGAIN : ENOBUFS)) - e(0); - } - } else if (state == 1) - if (send(fd[0], "A", 1, 0) != 1) e(0); - - /* - * Test 0: no data, no control data. - * Test 1: data, no control data. - * Test 2: no data, empty control data. - * Test 3: data, empty control data. - * Test 4: no data, control data with a file descriptor. - * Test 5: data, control data with a file descriptor. - */ - switch (test) { - case 0: - case 1: - res = send(fd[0], "B", test % 2, 0); - err = errno; - break; - case 2: - case 3: - res = send_test_fds(fd[0], "B", test % 2, 0, NULL, 0); - err = errno; - break; - case 4: - case 5: - res = send_test_fds(fd[0], "B", test % 2, 0, peers, - __arraycount(peers)); - err = errno; - break; - default: - res = -1; - err = EINVAL; - e(0); - } - - if (res < -1 || res > 1) e(0); - - switch (state) { - case 0: - case 1: - if (res != (test % 2)) e(0); - - /* - * Subtest bit 0x1: try a zero-size receive first. - * Subtest bit 0x2: try receiving control data. - * Subtest bit 0x4: send an extra segment with no control data. - * Subtest bit 0x8: after completing receives, expect EOF. - */ - if (sub & 4) - if (send(fd[0], "C", 1, 0) != 1) e(0); - if (sub & 8) - if (shutdown(fd[0], SHUT_WR) != 0) e(0); - - /* - * Assuming (sub&4), which means there is an extra "C".. - * - * For stream sockets, we should now receive: - * - state 0: - * - test 0, 2: "C" - * - test 1, 3: "BC" - * - test 4: "C" (w/fds) - * - test 5: "BC" (w/fds) - * - state 1: - * - test 0, 2: "AC" - * - test 1, 3: "ABC" - * - test 4: "A", "C" (w/fds) - * - test 5: "A", "BC" (w/fds) - * - * For packet sockets, we should now receive: - * - state 1: - * - all tests: "A", followed by.. - * - state 0, 1: - * - test 0, 2: "" (no fds), "C" - * - test 1, 3: "B" (no fds), "C" - * - test 4: "" (w/fds), "C" - * - test 5: "B" (w/fds), "C" - */ - sub90i_recv(fd[1], type, state, test, sub, - __arraycount(peers)); - - break; - case 2: - /* - * Alright, the results are a bit tricky to interpret here, - * because UDS's current strict send admission control prevents - * the receive buffer from being fully utilized. We therefore - * only test the following aspects: - * - * - if we sent no regular or control data to a stream socket, - * the call should have succeeded (note that the presence of - * empty control data may cause the call to fail); - * - if we sent either regular or control data to a stream - * socket, the call should have failed with EWOULDBLOCK; - * - if the call failed, the error should have been EWOULDBLOCK - * for connection-type sockets and ENOBUFS for connectionless - * sockets. - * - * Everything else gets a pass; we can't even be sure that for - * packet-oriented sockets we completely filled up the buffer. - */ - if (res == -1) { - if (type == SOCK_STREAM && test == 0) e(0); - - if (type != SOCK_DGRAM && err != EWOULDBLOCK) e(0); - if (type == SOCK_DGRAM && err != ENOBUFS) e(0); - } else - if (type == SOCK_STREAM && test != 0) e(0); - break; - } - - /* - * Make sure there are no more in-flight file descriptors now, even - * before closing the socket. - */ - if (res >= 0 && test >= 4) - close_test_fds(peers, __arraycount(peers)); - - close(fd[0]); - close(fd[1]); -} - -/* - * Test send and receive requests with regular data, control data, both, or - * neither, and test segment boundaries. - */ -static void -test90i(void) -{ - int state, test, sub; - - subtest = 9; - - for (state = 0; state < 3; state++) { - for (test = 0; test < 6; test++) { - for (sub = 0; sub < ((state < 2) ? 16 : 1); sub++) { - sub90i_send(SOCK_STREAM, state, test, sub); - - sub90i_send(SOCK_SEQPACKET, state, test, sub); - - sub90i_send(SOCK_DGRAM, state, test, sub); - } - } - } -} - -/* - * Test segmentation of file descriptor transfer on a particular socket type. - */ -static void -sub90j(int type) -{ - char path[PATH_MAX], buf[2]; - int i, fd[2], out[7], in[7], rflags, nfds; - ssize_t len; - - get_socket_pair(type, fd); - - for (i = 0; i < __arraycount(out); i++) { - snprintf(path, sizeof(path), "file%d", i); - out[i] = open(path, O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0644); - if (out[i] < 0) e(0); - if (write(out[i], path, strlen(path)) != strlen(path)) e(0); - if (lseek(out[i], 0, SEEK_SET) != 0) e(0); - } - - if (send_fds(fd[1], "A", 1, 0, NULL, 0, &out[0], 1) != 1) e(0); - if (send_fds(fd[1], "B", 1, 0, NULL, 0, &out[1], 3) != 1) e(0); - if (send_fds(fd[1], "C", 1, 0, NULL, 0, &out[4], 2) != 1) e(0); - - nfds = 2; - if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[0], &nfds) != 1) - e(0); - if (buf[0] != 'A') e(0); - if (rflags != 0) e(0); - if (nfds != 1) e(0); - - nfds = 5; - if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[1], &nfds) != 1) - e(0); - if (buf[0] != 'B') e(0); - if (rflags != 0) e(0); - if (nfds != 3) e(0); - - if (send_fds(fd[1], "D", 1, 0, NULL, 0, &out[6], 1) != 1) e(0); - - nfds = 2; - if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[4], &nfds) != 1) - e(0); - if (buf[0] != 'C') e(0); - if (rflags != 0) e(0); - if (nfds != 2) e(0); - - nfds = 2; - if (recv_fds(fd[0], buf, sizeof(buf), 0, &rflags, &in[6], &nfds) != 1) - e(0); - if (buf[0] != 'D') e(0); - if (rflags != 0) e(0); - if (nfds != 1) e(0); - - for (i = 0; i < __arraycount(in); i++) { - len = read(in[i], path, sizeof(path)); - if (len < 5 || len > 7) e(0); - path[len] = '\0'; - if (strncmp(path, "file", 4) != 0) e(0); - if (atoi(&path[4]) != i) e(0); - if (unlink(path) != 0) e(0); - if (close(in[i]) != 0) e(0); - } - - for (i = 0; i < __arraycount(out); i++) - if (close(out[i]) != 0) e(0); - - /* - * While we're here, see if UDS properly closes any remaining in-flight - * file descriptors when the socket is closed. - */ - if (send_test_fds(fd[1], "E", 1, 0, out, 7) != 1) e(0); - - close(fd[0]); - close(fd[1]); - - close_test_fds(out, 7); -} - -/* - * Test segmentation of file descriptor transfer. That is, there are multiple - * in-flight file descriptors, they must each be associated with their - * respective segments. - */ -static void -test90j(void) -{ - - subtest = 10; - - sub90j(SOCK_STREAM); - - sub90j(SOCK_SEQPACKET); - - sub90j(SOCK_DGRAM); -} - -/* - * Test whether we can deadlock UDS by making it close the last reference to - * an in-flight file descriptor for a UDS socket. Currently we allow VFS/UDS - * to get away with throwing EDEADLK as a sledgehammer approach to preventing - * problems with in-flight UDS sockets. - */ -static void -test90k(void) -{ - int r, fd[2], fd2; - - subtest = 11; - - get_socket_pair(SOCK_STREAM, fd); - - if ((fd2 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0); - - if ((r = send_fds(fd[0], "X", 1, 0, NULL, 0, &fd2, 1)) != 1) { - if (r != -1) e(0); - if (errno != EDEADLK) e(0); /* whew */ - } - - if (close(fd2) != 0) e(0); - if (close(fd[0]) != 0) e(0); - if (close(fd[1]) != 0) e(0); /* boom */ -} - -/* - * Test whether we can make UDS run out of file descriptors by transferring a - * UDS socket over itself and then closing all other references while it is - * in-flight. Currently we allow VFS/UDS to get away with throwing EDEADLK as - * a sledgehammer approach to preventing problems with in-flight UDS sockets. - */ -static void -test90l(void) -{ - struct sockaddr_un sun; - int i, r, fd, fd2; - - subtest = 12; - - fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sun); - - for (i = 0; i < OPEN_MAX + 1; i++) { - if ((fd2 = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0); - - if ((r = send_fds(fd2, "X", 1, 0, (struct sockaddr *)&sun, - sizeof(sun), &fd2, 1)) != 1) { - if (r != -1) e(0); - if (errno != EDEADLK) e(0); /* whew */ - } - - if (close(fd2) != 0) e(0); /* have fun in limbo.. */ - } - - if (close(fd) != 0) e(0); - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Receive with credentials. - */ -static int -recv_creds(int fd, char * buf, size_t size, int flags, int * rflags, - struct sockcred * sc, socklen_t * sc_len) -{ - union { - char buf[CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX))]; - struct cmsghdr cmsg; - } control; - struct msghdr msg; - struct iovec iov; - size_t len; - int r; - - iov.iov_base = buf; - iov.iov_len = size; - - memset(&control.cmsg, 0, sizeof(control.cmsg)); - control.cmsg.cmsg_len = CMSG_LEN(SOCKCREDSIZE(NGROUPS_MAX)); - control.cmsg.cmsg_level = SOL_SOCKET; - control.cmsg.cmsg_type = SCM_RIGHTS; - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = &control; - msg.msg_controllen = control.cmsg.cmsg_len; - - if ((r = recvmsg(fd, &msg, flags)) < 0) - return r; - - if (msg.msg_controllen > 0) { - assert(msg.msg_controllen <= sizeof(control)); - assert(msg.msg_controllen >= sizeof(control.cmsg)); - assert(control.cmsg.cmsg_len <= msg.msg_controllen); - len = control.cmsg.cmsg_len - CMSG_LEN(0); - assert(len >= sizeof(struct sockcred)); - assert(len <= SOCKCREDSIZE(NGROUPS_MAX)); - if (*sc_len > len) - *sc_len = len; - memcpy(sc, CMSG_DATA(&control.cmsg), *sc_len); - } else - *sc_len = 0; - - *rflags = msg.msg_flags; - return r; -} - -/* - * Test basic credentials passing on connection-oriented sockets. - */ -static void -sub90m(int type) -{ - struct sockaddr_un sun; - struct sockcred sc; - struct msghdr msg; - struct iovec iov; - socklen_t len; - char buf[1]; - int fd, fd2, fd3, val, rflags; - - fd = get_bound_socket(type, SOCK_PATH_A, &sun); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0); - - if (listen(fd, 1) != 0) e(0); - - if ((fd2 = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0); - - len = sizeof(sun); - if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0); - - if (send(fd2, "A", 1, 0) != 1) e(0); - if (send(fd2, "B", 1, 0) != 1) e(0); - - len = sizeof(sc); - if (recv_creds(fd3, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'A') e(0); - if (rflags != 0) e(0); - if (len != sizeof(sc)) e(0); - if (sc.sc_uid != getuid()) e(0); - if (sc.sc_euid != geteuid()) e(0); - - len = sizeof(sc); - if (recv_creds(fd3, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'B') e(0); - if (rflags != 0) e(0); - if (len != 0) e(0); - - if (send(fd3, "C", 1, 0) != 1) e(0); - - val = 1; - if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0); - - if (send(fd3, "D", 1, 0) != 1) e(0); - - val = 1; - if (setsockopt(fd2, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0); - - if (send(fd3, "E", 1, 0) != 1) e(0); - - len = sizeof(sc); - if (recv_creds(fd2, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'C') e(0); - if (rflags != 0) e(0); - if (len != 0) e(0); - - len = sizeof(sc); - if (recv_creds(fd2, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'D') e(0); - if (rflags != 0) e(0); - if (len != sizeof(sc)) e(0); - if (sc.sc_uid != getuid()) e(0); - if (sc.sc_euid != geteuid()) e(0); - - memset(&msg, 0, sizeof(msg)); - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - if (recvmsg(fd2, &msg, 0) != 1) e(0); - if (buf[0] != 'E') e(0); - if (msg.msg_flags != MSG_CTRUNC) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd3) != 0) e(0); - - val = 0; - if (setsockopt(fd, 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0); - - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0); - - len = sizeof(sun); - if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0); - - if (send(fd2, "F", 1, 0) != 1) e(0); - - len = sizeof(sc); - if (recv_creds(fd3, buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'F') e(0); - if (rflags != 0) e(0); - if (len != 0) e(0); - - if (close(fd) != 0) e(0); - if (close(fd2) != 0) e(0); - if (close(fd3) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * A few tests for credentials passing that matter to some applications: - * the credentials passing setting is inherited by accepted connections from - * their listening socket, and, credentials are passed only once on a - * connection-oriented socket. - */ -static void -test90m(void) -{ - struct sockcred sc; - socklen_t len; - char buf[1]; - int fd[2], val, rflags; - - subtest = 13; - - sub90m(SOCK_STREAM); - - sub90m(SOCK_SEQPACKET); - - get_socket_pair(SOCK_DGRAM, fd); - - val = 1; - if (setsockopt(fd[0], 0, LOCAL_CREDS, &val, sizeof(val)) != 0) e(0); - - if (send(fd[1], "A", 1, 0) != 1) e(0); - if (send(fd[0], "B", 1, 0) != 1) e(0); - if (send(fd[1], "C", 1, 0) != 1) e(0); - - len = sizeof(sc); - if (recv_creds(fd[0], buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'A') e(0); - if (rflags != 0) e(0); - if (len != sizeof(sc)) e(0); - if (sc.sc_uid != getuid()) e(0); - if (sc.sc_euid != geteuid()) e(0); - - len = sizeof(sc); - if (recv_creds(fd[1], buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'B') e(0); - if (rflags != 0) e(0); - if (len != 0) e(0); - - len = sizeof(sc); - if (recv_creds(fd[0], buf, sizeof(buf), 0, &rflags, &sc, &len) != 1) - e(0); - if (buf[0] != 'C') e(0); - if (rflags != 0) e(0); - if (len != sizeof(sc)) e(0); - if (sc.sc_uid != getuid()) e(0); - if (sc.sc_euid != geteuid()) e(0); - - if (close(fd[0]) != 0) e(0); - if (close(fd[1]) != 0) e(0); -} - -/* - * Test whether MSG_CMSG_CLOEXEC is honored when copying in file descriptors. - * We do not bother to test with execve(2w); obtaining the FD flags suffices. - */ -static void -test90n(void) -{ - char buf[1]; - int i, fd[2], sfd, rfd, fl, rflags, nfds; - - subtest = 14; - - get_socket_pair(SOCK_STREAM, fd); - - if ((sfd = open("/dev/null", O_RDONLY)) < 0) e(0); - - if (send_fds(fd[0], "A", 1, 0, NULL, 0, &sfd, 1) != 1) e(0); - if (send_fds(fd[0], "B", 1, 0, NULL, 0, &sfd, 1) != 1) e(0); - - if ((fl = fcntl(sfd, F_GETFD, 0)) < 0) e(0); - if (fcntl(sfd, F_SETFD, fl | FD_CLOEXEC) != 0) e(0); - - if (send_fds(fd[0], "C", 1, 0, NULL, 0, &sfd, 1) != 1) e(0); - if (send_fds(fd[0], "D", 1, 0, NULL, 0, &sfd, 1) != 1) e(0); - - for (i = 0; i < 4; i++) { - fl = (i & 1) ? MSG_CMSG_CLOEXEC : 0; - nfds = 1; - if (recv_fds(fd[1], buf, sizeof(buf), fl, &rflags, &rfd, - &nfds) != 1) e(0); - if (buf[0] != 'A' + i) e(0); - if (rflags != 0) e(0); - if (nfds != 1) e(0); - - if ((fl = fcntl(rfd, F_GETFD, 0)) < 0) e(0); - if (!!(fl & FD_CLOEXEC) != (i & 1)) e(0); - - if (close(rfd) != 0) e(0); - } - - if (close(sfd) != 0) e(0); - if (close(fd[0]) != 0) e(0); - if (close(fd[1]) != 0) e(0); -} - -/* - * Test failures sending and receiving sets of file descriptors. - */ -static void -sub90o(int type) -{ - static int ofd[OPEN_MAX]; - char buf[1]; - int i, fd[2], sfd[2], rfd[2], rflags, nfds; - - get_socket_pair(type, fd); - - if ((sfd[0] = open("/dev/null", O_RDONLY)) < 0) e(0); - sfd[1] = -1; - - if (send_fds(fd[0], "A", 1, 0, NULL, 0, &sfd[1], 1) != -1) e(0); - if (errno != EBADF) e(0); - if (send_fds(fd[0], "B", 1, 0, NULL, 0, &sfd[0], 2) != -1) e(0); - if (errno != EBADF) e(0); - if ((sfd[1] = dup(sfd[0])) < 0) e(0); - if (send_fds(fd[0], "C", 1, 0, NULL, 0, &sfd[0], 2) != 1) e(0); - - for (i = 0; i < __arraycount(ofd); i++) { - if ((ofd[i] = dup(sfd[0])) < 0) { - /* Either will do. */ - if (errno != EMFILE && errno != ENFILE) e(0); - break; - } - } - - nfds = 2; - if (recv_fds(fd[1], buf, sizeof(buf), 0, &rflags, rfd, &nfds) != -1) - e(0); - if (errno != ENFILE && errno != EMFILE) e(0); - - if (close(sfd[1]) != 0) e(0); - - nfds = 2; - if (recv_fds(fd[1], buf, sizeof(buf), 0, &rflags, rfd, &nfds) != -1) - e(0); - if (errno != ENFILE && errno != EMFILE) e(0); - - if (close(sfd[0]) != 0) e(0); - - nfds = 2; - if (recv_fds(fd[1], buf, sizeof(buf), 0, &rflags, rfd, &nfds) != 1) - e(0); - if (buf[0] != 'C') e(0); - if (rflags != 0) e(0); - if (nfds != 2) e(0); - - if (close(rfd[1]) != 0) e(0); - if (close(rfd[0]) != 0) e(0); - while (i-- > 0) - if (close(ofd[i]) != 0) e(0); - if (close(fd[1]) != 0) e(0); - if (close(fd[0]) != 0) e(0); -} - -/* - * Test failures sending and receiving sets of file descriptors. - */ -static void -test90o(void) -{ - const int types[] = { SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM }; - int i; - - subtest = 15; - - for (i = 0; i < OPEN_MAX + 1; i++) - sub90o(types[i % __arraycount(types)]); -} - -/* - * Test socket reuse for a particular socket type. - */ -static void -sub90p(int type) -{ - struct sockaddr_un sunA, sunB, sunC; - socklen_t len; - char buf[1]; - uid_t euid; - gid_t egid; - int fd, fd2, fd3, val; - - if ((fd = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - /* Unconnected. */ - - if (getpeereid(fd, &euid, &egid) != -1) e(0); - if (errno != ENOTCONN) e(0); - - fd2 = get_bound_socket(type, SOCK_PATH_A, &sunA); - - val = 1; - if (setsockopt(fd2, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (listen(fd2, 5) != 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0); - if (errno != EINPROGRESS) e(0); - /* Connecting. */ - - if (getpeereid(fd, &euid, &egid) != -1) e(0); - if (errno != ENOTCONN) e(0); - - len = sizeof(sunB); - if ((fd3 = accept(fd2, (struct sockaddr *)&sunB, &len)) < 0) e(0); - /* Connected. */ - - len = sizeof(sunC); - if (getpeername(fd, (struct sockaddr *)&sunC, &len) != 0) e(0); - check_addr(&sunC, len, SOCK_PATH_A); - - if (getpeereid(fd, &euid, &egid) != 0) e(0); - if (euid == -1 || egid == -1) e(0); - - if (getpeereid(fd3, &euid, &egid) != 0) e(0); - if (euid == -1 || egid == -1) e(0); - - if (send(fd3, "A", 1, 0) != 1) e(0); - if (send(fd3, "B", 1, 0) != 1) e(0); - if (send(fd3, "C", 1, 0) != 1) e(0); - - if (close(fd3) != 0) e(0); - /* Disconnected. */ - - if (getpeereid(fd, &euid, &egid) != -1) e(0); - if (errno != ENOTCONN) e(0); - - if (close(fd2) != 0) e(0); - fd2 = get_bound_socket(type, SOCK_PATH_B, &sunA); - - if (listen(fd2, 5) != 0) e(0); - - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0); - if (errno != EINPROGRESS) e(0); - /* Connecting. */ - - if (getpeereid(fd, &euid, &egid) != -1) e(0); - if (errno != ENOTCONN) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'A') e(0); - - len = sizeof(sunB); - if ((fd3 = accept(fd2, (struct sockaddr *)&sunB, &len)) < 0) e(0); - /* Connected. */ - - if (send(fd3, "D", 1, 0) != 1) e(0); - if (send(fd3, "E", 1, 0) != 1) e(0); - - len = sizeof(sunC); - if (getpeername(fd, (struct sockaddr *)&sunC, &len) != 0) e(0); - check_addr(&sunC, len, SOCK_PATH_B); - - if (getpeereid(fd, &euid, &egid) != 0) e(0); - if (euid == -1 || egid == -1) e(0); - - if (close(fd2) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); - - if (close(fd3) != 0) e(0); - /* Disconnected. */ - - if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != -1) e(0); - if (errno != ENOENT) e(0); - /* Unconnected. */ - - if (getpeereid(fd, &euid, &egid) != -1) e(0); - if (errno != ENOTCONN) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'B') e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - - if (bind(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'C') e(0); - - val = 0; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) e(0); - - if (listen(fd, 1) != 0) e(0); - /* Listening. */ - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'D') e(0); - - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - len = sizeof(sunC); - if ((fd3 = accept(fd, (struct sockaddr *)&sunC, &len)) < 0) e(0); - - if (send(fd2, "F", 1, 0) != 1) e(0); - - if (recv(fd3, buf, 1, 0) != 1) e(0); - if (buf[0] != 'F') e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'E') e(0); - - if (recv(fd, buf, 1, 0) != -1) e(0); - if (errno != ENOTCONN) e(0); - - /* It should be possible to obtain peer credentials now. */ - if (getpeereid(fd2, &euid, &egid) != 0) e(0); - if (euid == -1 || egid == -1) e(0); - - if (getpeereid(fd3, &euid, &egid) != 0) e(0); - if (euid == -1 || egid == -1) e(0); - - if (close(fd3) != 0) e(0); - if (close(fd2) != 0) e(0); - - if (close(fd) != 0) e(0); - /* Closed. */ - - if (unlink(SOCK_PATH_B) != 0) e(0); - - if ((fd = socket(AF_UNIX, type, 0)) < 0) e(0); - /* Unconnected. */ - - fd2 = get_bound_socket(type, SOCK_PATH_A, &sunA); - - if (listen(fd2, 5) != 0) e(0); - - if (connect(fd, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - /* Connected. */ - - len = sizeof(sunB); - if ((fd3 = accept(fd2, (struct sockaddr *)&sunB, &len)) < 0) e(0); - - if (close(fd2) != 0) e(0); - - memset(&sunB, 0, sizeof(sunB)); - sunB.sun_family = AF_UNIX; - strlcpy(sunB.sun_path, SOCK_PATH_B, sizeof(sunB.sun_path)); - - if (bind(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0); - - if (close(fd3) != 0) e(0); - /* Disconnected. */ - - if (listen(fd, 1) != 0) e(0); - /* Listening. */ - - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0); - - len = sizeof(sunC); - if ((fd3 = accept(fd, (struct sockaddr *)&sunC, &len)) < 0) e(0); - - /* It should NOT be possible to obtain peer credentials now. */ - if (getpeereid(fd2, &euid, &egid) != -1) e(0); - if (errno != EINVAL) e(0); - - if (getpeereid(fd3, &euid, &egid) != 0) e(0); - if (euid == -1 || egid == -1) e(0); - - if (close(fd3) != 0) e(0); - if (close(fd2) != 0) e(0); - - if (close(fd) != 0) e(0); - /* Closed. */ - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); -} - -/* - * Test socket reuse, receiving left-overs in the receive buffer, and the - * (in)ability to obtain peer credentials. - */ -static void -test90p(void) -{ - - subtest = 16; - - sub90p(SOCK_STREAM); - - sub90p(SOCK_SEQPACKET); -} - -/* - * Test state changes and errors related to connected datagram sockets. - */ -static void -test90q(void) -{ - struct sockaddr_un sunA, sunB, sunC, sunD; - socklen_t len; - char buf[1]; - int fd, fd2, fd3, val; - - subtest = 17; - - /* - * Sending a datagram to a datagram socket connected elsewhere should - * fail explicitly (specifically, EPERM). - */ - fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sunA); - fd2 = get_bound_socket(SOCK_DGRAM, SOCK_PATH_B, &sunB); - fd3 = get_bound_socket(SOCK_DGRAM, SOCK_PATH_C, &sunC); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (sendto(fd3, "A", 1, 0, (struct sockaddr *)&sunB, - sizeof(sunB)) != -1) e(0); - if (errno != EPERM) e(0); - - /* Similarly, connecting to such a socket should fail. */ - if (connect(fd3, (struct sockaddr *)&sunB, sizeof(sunB)) != -1) e(0); - if (errno != EPERM) e(0); - - if (send(fd2, "B", 1, 0) != 1) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'B') e(0); - - /* Reconnection of a socket's target should result in ECONNRESET. */ - if (connect(fd, (struct sockaddr *)&sunC, sizeof(sunC)) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fd2, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0); - if (val != ECONNRESET) e(0); - - if (send(fd2, "C", 1, 0) != -1) e(0); - if (errno != EDESTADDRREQ) e(0); - - if (send(fd, "D", 1, 0) != 1) e(0); - - len = sizeof(sunD); - if (recvfrom(fd3, buf, 1, 0, (struct sockaddr *)&sunD, &len) != 1) - e(0); - if (buf[0] != 'D') e(0); - check_addr(&sunD, len, SOCK_PATH_A); - - if (connect(fd2, (struct sockaddr *)&sunC, sizeof(sunC)) != 0) e(0); - - if (connect(fd3, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - memset(&sunD, 0, sizeof(sunD)); - sunD.sun_family = AF_UNIX; - strlcpy(sunD.sun_path, SOCK_PATH_D, sizeof(sunD.sun_path)); - - /* A failed reconnection attempt should not break the previous one. */ - if (connect(fd3, (struct sockaddr *)&sunD, sizeof(sunD)) != -1) e(0); - if (errno != ENOENT) e(0); - - /* The destination address should be ignored here. */ - if (sendto(fd3, "E", 1, 0, (struct sockaddr *)&sunB, - sizeof(sunB)) != 1) e(0); - - if (recv(fd2, buf, 1, 0) != -1) e(0); - if (errno != ECONNRESET) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'E') e(0); - - if (close(fd3) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0); - if (val != ECONNRESET) e(0); - - if (close(fd2) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0); - if (val != 0) e(0); - - if (close(fd) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); - if (unlink(SOCK_PATH_C) != 0) e(0); - - /* - * Finally, test unconnecting sockets. - */ - fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A, &sunA); - fd2 = get_bound_socket(SOCK_DGRAM, SOCK_PATH_B, &sunB); - - if (connect(fd, (struct sockaddr *)&sunB, sizeof(sunB)) != 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (sendto(fd2, "F", 1, 0, NULL, 0) != 1) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'F') e(0); - - memset(&sunC, 0, sizeof(sunC)); - sunC.sun_family = AF_UNSPEC; - if (connect(fd2, (struct sockaddr *)&sunC, sizeof(sunC)) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) != 0) e(0); - if (val != 0) e(0); - - if (send(fd, "G", 1, 0) != 1) e(0); - - if (sendto(fd2, "H", 1, 0, NULL, 0) != -1) e(0); - if (errno != EDESTADDRREQ) e(0); - - if (sendto(fd2, "I", 1, 0, (struct sockaddr *)&sunA, sizeof(sunA)) != 1) - e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (sendto(fd2, "J", 1, 0, NULL, 0) != 1) e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'I') e(0); - - if (recv(fd, buf, 1, 0) != 1) e(0); - if (buf[0] != 'J') e(0); - - if (recv(fd2, buf, 1, 0) != 1) e(0); - if (buf[0] != 'G') e(0); - - if (close(fd) != 0) e(0); - if (close(fd2) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); -} - -/* - * Test socket file name reuse. - */ -static void -test90r(void) -{ - struct sockaddr_un sun; - socklen_t len; - int fd, fd2, fd3, fd4; - - subtest = 18; - - fd = get_bound_socket(SOCK_STREAM | SOCK_NONBLOCK, SOCK_PATH_A, &sun); - - if (rename(SOCK_PATH_A, SOCK_PATH_B) != 0) e(0); - - if ((fd3 = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) e(0); - - if (connect(fd3, (struct sockaddr *)&sun, sizeof(sun)) != -1) e(0); - if (errno != ENOENT) e(0); - - if (listen(fd, 1) != 0) e(0); - - len = sizeof(sun); - if (getsockname(fd, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, SOCK_PATH_A); - - fd2 = get_bound_socket(SOCK_STREAM | SOCK_NONBLOCK, SOCK_PATH_A, &sun); - - if (listen(fd2, 1) != 0) e(0); - - len = sizeof(sun); - if (getsockname(fd2, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, SOCK_PATH_A); - - len = sizeof(sun); - if (getsockname(fd, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, SOCK_PATH_A); - - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - strlcpy(sun.sun_path, SOCK_PATH_B, sizeof(sun.sun_path)); - if (connect(fd3, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0); - - len = sizeof(sun); - if ((fd4 = accept(fd2, (struct sockaddr *)&sun, &len)) >= 0) e(0); - if (errno != EWOULDBLOCK) e(0); - if ((fd4 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0); - - len = sizeof(sun); - if (getpeername(fd3, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, SOCK_PATH_A); - - if (close(fd) != 0) e(0); - if (close(fd2) != 0) e(0); - if (close(fd3) != 0) e(0); - if (close(fd4) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); - if (unlink(SOCK_PATH_B) != 0) e(0); -} - -/* - * Test that non-canonized path names are accepted and returned. - * Also test datagram send errors on disconnect. - */ -static void -test90s(void) -{ - struct sockaddr_un sun; - socklen_t len; - int fd, fd2; - - subtest = 19; - - fd = get_bound_socket(SOCK_DGRAM, SOCK_PATH_A_X, &sun); - - if ((fd2 = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) e(0); - - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - strlcpy(sun.sun_path, SOCK_PATH_A_Y, sizeof(sun.sun_path)); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0); - - len = sizeof(sun); - if (getpeername(fd2, (struct sockaddr *)&sun, &len) != 0) e(0); - check_addr(&sun, len, SOCK_PATH_A_X); - - if (send(fd2, "A", 1, 0) != 1) e(0); - - if (close(fd) != 0) e(0); - - if (send(fd2, "B", 1, 0) != -1) e(0); - if (errno != ECONNRESET) e(0); - - if (send(fd2, "B", 1, 0) != -1) e(0); - if (errno != EDESTADDRREQ) e(0); - - if (close(fd2) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Test basic sysctl(2) socket enumeration for a specific socket type. - */ -static void -sub90t(int type, const char * path) -{ - struct kinfo_pcb *ki; - size_t i, len, oldlen; - int fd, mib[8]; - - if ((fd = socket(AF_UNIX, type, 0)) < 0) e(0); - - memset(mib, 0, sizeof(mib)); - - len = __arraycount(mib); - if (sysctlnametomib(path, mib, &len) != 0) e(0); - if (len != 4) e(0); - - if (sysctl(mib, __arraycount(mib), NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - if (oldlen % sizeof(*ki)) e(0); - - if ((ki = (struct kinfo_pcb *)malloc(oldlen)) == NULL) e(0); - - if (sysctl(mib, __arraycount(mib), ki, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - if (oldlen % sizeof(*ki)) e(0); - - /* - * We cannot check a whole lot of things, because we have no way of - * knowing which is the socket we created. Check some basics and leave - * it at that. This subtest is mostly trying to guarantee that - * netstat(1) will not show nothing, anyway. - */ - for (i = 0; i < oldlen / sizeof(*ki); i++) { - if (ki[i].ki_pcbaddr == 0) e(0); - if (ki[i].ki_sockaddr == 0) e(0); - if (ki[i].ki_family != AF_UNIX) e(0); - if (ki[i].ki_type != type) e(0); - if (ki[i].ki_protocol != 0) e(0); - } - - free(ki); - - if (close(fd) != 0) e(0); -} - -/* - * Test basic sysctl(2) socket enumeration support. - */ -static void -test90t(void) -{ - - subtest = 20; - - /* - * We test that for each of the socket types, when we create a socket, - * we can find at least one socket of that type in the respective - * sysctl(2) out. - */ - sub90t(SOCK_STREAM, "net.local.stream.pcblist"); - - sub90t(SOCK_SEQPACKET, "net.local.seqpacket.pcblist"); - - sub90t(SOCK_DGRAM, "net.local.dgram.pcblist"); -} - -/* - * Cause a pending recv() call to return. Here 'fd' is the file descriptor - * identifying the other end of the socket pair. If breaking the recv() - * requires sending data, 'data' and 'len' identify the data that should be - * sent. Return 'fd' if it is still open, or -1 if it is closed. - */ -static int -break_uds_recv(int fd, const char * data, size_t len) -{ - int fd2; - - /* - * This UDS-specific routine makes the recv() in one of two ways - * depending on whether the recv() call already made partial progress: - * if it did, this send call creates a segment boundary which should - * cut short the current receive call. If it did not, the send call - * will simply satisfy the receive call with regular data. - */ - if ((fd2 = open("/dev/null", O_RDONLY)) < 0) e(0); - - if (send_fds(fd, data, len, 0, NULL, 0, &fd2, 1) != len) e(0); - - if (close(fd2) != 0) e(0); - - return fd; -} - -/* - * Test for receiving on stream sockets. In particular, test SO_RCVLOWAT, - * MSG_PEEK, MSG_DONTWAIT, and MSG_WAITALL. - */ -static void -test90u(void) -{ - - subtest = 21; - - socklib_stream_recv(socketpair, AF_UNIX, SOCK_STREAM, break_uds_recv); -} - -#define MAX_BYTES 2 /* set to 3 for slightly better(?) testing */ -#define USLEEP_TIME 250000 /* increase on wimpy platforms if needed */ - -/* - * Signal handler which just needs to exist, so that invoking it will interrupt - * an ongoing system call. - */ -static void -test90_got_signal(int sig __unused) -{ - - /* Nothing. */ -} - -/* - * Test for sending on stream sockets. The quick summary here is that send() - * should basically act as the mirror of recv(MSG_WAITALL), i.e., it should - * keep suspending until all data is sent (or the call is interrupted or no - * more can possibly be sent), and, SO_SNDLOWAT, mirroring SO_RCVLOWAT, acts as - * an admission test for the send: nothing is sent until there is room in the - * send buffer (i.e., the peer's receive buffer) for at least the low send - * watermark, or the whole send request length, whichever is smaller. In - * addition, select(2) should use the same threshold. - */ -static void -sub90v(int iroom, int istate, int slowat, int len, int bits, int act) -{ - const char *data = "ABC"; /* this limits MAX_BYTES to 3 */ - struct sigaction sa; - struct timeval tv; - fd_set fds; - char buf[2], *sndbuf; - pid_t pid; - int fd[2], rcvlen, min, flags, res, err; - int pfd[2], eroom, tstate, fl, status; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0); - - /* - * Set up the initial condition on the sockets. - */ - rcvlen = get_rcvbuf_len(fd[1]); - if (rcvlen <= iroom) e(0); - rcvlen -= iroom; - - if ((sndbuf = malloc(rcvlen)) == NULL) e(0); - - memset(sndbuf, 'X', rcvlen); - if (send(fd[0], sndbuf, rcvlen, 0) != rcvlen) e(0); - - free(sndbuf); - - switch (istate) { - case 0: break; - case 1: if (shutdown(fd[0], SHUT_WR) != 0) e(0); break; - case 2: if (shutdown(fd[1], SHUT_RD) != 0) e(0); break; - case 3: if (close(fd[1]) != 0) e(0); break; - } - - if (setsockopt(fd[0], SOL_SOCKET, SO_SNDLOWAT, &slowat, - sizeof(slowat)) != 0) e(0); - - /* SO_SNDLOWAT is always bounded by the actual send length. */ - min = MIN(len, slowat); - - flags = MSG_NOSIGNAL; - if (bits & 1) flags |= MSG_DONTWAIT; - - /* - * Do a quick select test to see if its result indeed matches whether - * the available space in the "send" buffer meets the threshold. - */ - FD_ZERO(&fds); - FD_SET(fd[0], &fds); - tv.tv_sec = 0; - tv.tv_usec = 0; - res = select(fd[0] + 1, NULL, &fds, NULL, &tv); - if (res < 0 || res > 1) e(0); - if (res != (iroom >= slowat || istate > 0)) e(0); - if (res == 1 && !FD_ISSET(fd[0], &fds)) e(0); - - /* - * Cut short a whole lot of cases, to avoid the overhead of forking, - * namely when we know the call should return immediately. This is the - * case when the socket state disallows further sending, or when all - * data could be sent, or when the call was non-blocking. The low - * send watermark only helps determine whether anything was sent here. - */ - if (istate > 0 || iroom >= len || (flags & MSG_DONTWAIT)) { - res = send(fd[0], data, len, flags); - - if (istate > 0) { - if (res != -1) e(0); - if (errno != EPIPE) e(0); - } else if (iroom >= len) { - if (res != len) e(0); - } else if (iroom >= min) { - if (res != iroom) e(0); - } else { - if (res != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - } - - /* Early cleanup and return to avoid even more code clutter. */ - if (istate != 3 && close(fd[1]) != 0) e(0); - if (close(fd[0]) != 0) e(0); - - return; - } - - /* - * Now starts the interesting stuff: the send call should now block, - * even though if we add MSG_DONTWAIT it may not return EWOULDBLOCK, - * because MSG_DONTWAIT prevents the send from blocking after partial - * completion. As such, we can only test our expectations by letting - * the call block, in a child process, and waiting. We do test as much - * of the above assumption as we can for safety right here, but this is - * not a substitute for actually blocking even in these cases! - */ - if (iroom < min) { - if (send(fd[0], data, len, flags | MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - } - - /* - * If (act < 9), we receive 0, 1, or 2 bytes from the receive queue - * before forcing the send call to terminate in one of three ways. - * - * If (act == 9), we use a signal to interrupt the send call. - */ - if (act < 9) { - eroom = act % 3; - tstate = act / 3; - } else - eroom = tstate = 0; - - if (pipe2(pfd, O_NONBLOCK) != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - if (close(fd[1]) != 0) e(0); - if (close(pfd[0]) != 0) e(0); - - if (act == 9) { - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = test90_got_signal; - if (sigaction(SIGUSR1, &sa, NULL) != 0) e(0); - } - - res = send(fd[0], data, len, flags); - err = errno; - - if (write(pfd[1], &res, sizeof(res)) != sizeof(res)) e(0); - if (write(pfd[1], &err, sizeof(err)) != sizeof(err)) e(0); - - exit(errct); - case -1: - e(0); - } - - if (close(pfd[1]) != 0) e(0); - - /* - * Allow the child to enter the blocking send(2), and check the pipe - * to see if it is really blocked. - */ - if (usleep(USLEEP_TIME) != 0) e(0); - - if (read(pfd[0], &res, sizeof(res)) != -1) e(0); - if (errno != EAGAIN) e(0); - - if (eroom > 0) { - if (recv(fd[1], buf, eroom, 0) != eroom) e(0); - - /* - * The threshold for the send is now met if the entire request - * has been satisfied. - */ - if (iroom + eroom >= len) { - if ((fl = fcntl(pfd[0], F_GETFL)) == -1) e(0); - if (fcntl(pfd[0], F_SETFL, fl & ~O_NONBLOCK) != 0) - e(0); - - if (read(pfd[0], &res, sizeof(res)) != sizeof(res)) - e(0); - if (read(pfd[0], &err, sizeof(err)) != sizeof(err)) - e(0); - - if (res != len) e(0); - - /* Bail out. */ - goto cleanup; - } - } - - if (act < 9) { - /* - * Now test various ways to terminate the send call. Ideally - * we would also like to have a case that raises a socket error - * here, but with UDS there is currently no way to do that. - */ - switch (tstate) { - case 0: if (shutdown(fd[0], SHUT_WR) != 0) e(0); break; - case 1: if (shutdown(fd[1], SHUT_RD) != 0) e(0); break; - case 2: if (close(fd[1]) != 0) e(0); fd[1] = -1; break; - } - } else - if (kill(pid, SIGUSR1) != 0) e(0); - - if ((fl = fcntl(pfd[0], F_GETFL)) == -1) e(0); - if (fcntl(pfd[0], F_SETFL, fl & ~O_NONBLOCK) != 0) e(0); - - if (read(pfd[0], &res, sizeof(res)) != sizeof(res)) e(0); - if (read(pfd[0], &err, sizeof(err)) != sizeof(err)) e(0); - - /* - * If the send met the threshold before being terminate or interrupted, - * we should at least have sent something. Otherwise, the send was - * never admitted and should return EPIPE (if the send was terminated) - * or EINTR (if the child was killed). - */ - if (iroom + eroom >= min) { - if (res != MIN(iroom + eroom, len)) e(0); - } else { - if (res != -1) e(0); - if (act < 9) { - if (err != EPIPE) e(0); - } else - if (err != EINTR) e(0); - } - -cleanup: - if (close(pfd[0]) != 0) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - if (fd[1] != -1 && close(fd[1]) != 0) e(0); - if (close(fd[0]) != 0) e(0); -} - -/* - * Test for sending on stream sockets. In particular, test SO_SNDLOWAT and - * MSG_DONTWAIT. - */ -static void -test90v(void) -{ - int iroom, istate, slowat, len, bits, act; - - subtest = 22; - - /* Insanity. */ - for (iroom = 0; iroom <= MAX_BYTES; iroom++) - for (istate = 0; istate <= 3; istate++) - for (slowat = 1; slowat <= MAX_BYTES; slowat++) - for (len = 1; len <= MAX_BYTES; len++) - for (bits = 0; bits < 2; bits++) - for (act = 0; act <= 9; act++) - sub90v(iroom, istate, - slowat, len, bits, - act); -} - -/* - * Test that SO_RCVLOWAT is limited to the size of the receive buffer. - */ -static void -sub90w_recv(int fill_delta, int rlowat_delta, int exp_delta) -{ - char *buf; - int fd[2], rcvlen, fill, rlowat, res; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0); - - rcvlen = get_rcvbuf_len(fd[0]); - - if ((buf = malloc(rcvlen + 1)) == NULL) e(0); - - fill = rcvlen + fill_delta; - rlowat = rcvlen + rlowat_delta; - - memset(buf, 0, fill); - - if (send(fd[1], buf, fill, 0) != fill) e(0); - - if (setsockopt(fd[0], SOL_SOCKET, SO_RCVLOWAT, &rlowat, - sizeof(rlowat)) != 0) e(0); - - res = recv(fd[0], buf, rcvlen + 1, MSG_DONTWAIT); - if (exp_delta < 0) { - if (res != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - } else - if (res != rcvlen - exp_delta) e(0); - - free(buf); - - if (close(fd[0]) != 0) e(0); - if (close(fd[1]) != 0) e(0); -} - -/* - * Test that SO_SNDLOWAT is limited to the size of the "send" buffer. - */ -static void -sub90w_send(int fill, int slowat_delta, int exp_delta) -{ - char *buf; - socklen_t len; - int fd[2], sndlen, slowat, res; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) e(0); - - len = sizeof(sndlen); - if (getsockopt(fd[0], SOL_SOCKET, SO_SNDBUF, &sndlen, &len) != 0) e(0); - if (len != sizeof(sndlen)) e(0); - - if ((buf = malloc(sndlen + 1)) == NULL) e(0); - - slowat = sndlen + slowat_delta; - - if (fill > 0) { - memset(buf, 0, fill); - - if (send(fd[0], buf, fill, 0) != fill) e(0); - } - - if (setsockopt(fd[0], SOL_SOCKET, SO_SNDLOWAT, &slowat, - sizeof(slowat)) != 0) e(0); - - res = send(fd[0], buf, sndlen + 1, MSG_DONTWAIT); - if (exp_delta < 0) { - if (res != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - } else - if (res != sndlen - exp_delta) e(0); - - free(buf); - - if (close(fd[0]) != 0) e(0); - if (close(fd[1]) != 0) e(0); -} - -/* - * Test that on stream sockets, SO_RCVLOWAT and SO_SNDLOWAT are limited to - * their respective buffer sizes. - */ -static void -test90w(void) -{ - - subtest = 23; - - /* - * With the receive buffer filled except for one byte, all data should - * be retrieved unless the threshold is not met. - */ - sub90w_recv(-1, -1, 1); - sub90w_recv(-1, 0, -1); - sub90w_recv(-1, 1, -1); - - /* - * With the receive buffer filled completely, all data should be - * retrieved in all cases. - */ - sub90w_recv(0, -1, 0); - sub90w_recv(0, 0, 0); - sub90w_recv(0, 1, 0); - - /* - * With a "send" buffer that contains one byte, all data should be sent - * unless the threshold is not met. - */ - sub90w_send(1, -1, 1); - sub90w_send(1, 0, -1); - sub90w_send(1, 1, -1); - - /* - * With the "send" buffer filled completely, all data should be sent - * in all cases. - */ - sub90w_send(0, -1, 0); - sub90w_send(0, 0, 0); - sub90w_send(0, 1, 0); -} - -/* - * Test shutdown on listening sockets. - */ -static void -sub90x(int type, int how, int connwait) -{ - struct sockaddr_un sun; - socklen_t len; - char buf[1]; - int fd, fd2, fd3, val, fl; - - subtest = 24; - - fd = get_bound_socket(type, SOCK_PATH_A, &sun); - - if (listen(fd, 5) != 0) e(0); - - if (!connwait) { - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) - e(0); - } else { - val = 1; - if (setsockopt(fd, 0, LOCAL_CONNWAIT, &val, sizeof(val)) != 0) - e(0); - - if ((fd2 = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != -1) - e(0); - if (errno != EINPROGRESS) e(0); - } - - if (shutdown(fd, how) != 0) e(0); - - len = sizeof(sun); - if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0); - - if (write(fd2, "A", 1) != 1) e(0); - if (read(fd3, buf, 1) != 1) e(0); - if (buf[0] != 'A') e(0); - - if (write(fd3, "B", 1) != 1) e(0); - if (read(fd2, buf, 1) != 1) e(0); - if (buf[0] != 'B') e(0); - - len = sizeof(sun); - if (accept(fd, (struct sockaddr *)&sun, &len) != -1) e(0); - if (errno != ECONNABORTED) e(0); - - /* - * Strangely, both NetBSD and Linux (yes, my two reference platforms) - * return EWOULDBLOCK from non-blocking accept(2) calls even though - * they always return ECONNABORTED when blocking. For consistency and - * select(2), we always return ECONNABORTED. - */ - if ((fl = fcntl(fd, F_GETFL)) == -1) e(0); - if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - - len = sizeof(sun); - if (accept(fd, (struct sockaddr *)&sun, &len) != -1) e(0); - if (errno != ECONNABORTED) e(0); - - if (fcntl(fd, F_SETFL, fl) != 0) e(0); - - if (close(fd3) != 0) e(0); - if (close(fd2) != 0) e(0); - - if ((fd2 = socket(AF_UNIX, type | SOCK_NONBLOCK, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != -1) e(0); - if (errno != ECONNREFUSED) e(0); - - len = sizeof(sun); - if (accept(fd, (struct sockaddr *)&sun, &len) != -1) e(0); - if (errno != ECONNABORTED) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Test shutdown on listening sockets. Pending connections should still be - * acceptable (and not inherit the shutdown flags), but new connections must be - * refused, and the accept call must no longer ever block. - */ -static void -test90x(void) -{ - const int types[] = { SOCK_STREAM, SOCK_SEQPACKET }; - const int hows[] = { SHUT_RD, SHUT_WR, SHUT_RDWR }; - unsigned int i, j, k; - - for (i = 0; i < __arraycount(types); i++) - for (j = 0; j < __arraycount(hows); j++) - for (k = 0; k <= 1; k++) - sub90x(types[i], hows[j], k); -} - -/* - * Test accepting connections without LOCAL_CONNWAIT for the given socket type. - */ -static void -sub90y(int type) -{ - struct sockaddr_un sunA, sunB, sunC; - socklen_t len; - struct timeval tv; - fd_set fds; - char buf[7]; - uid_t uid; - gid_t gid; - int fd, fd2, fd3, fd4, val; - - fd = get_bound_socket(type | SOCK_NONBLOCK, SOCK_PATH_A, &sunA); - - len = sizeof(val); - if (getsockopt(fd, 0, LOCAL_CONNWAIT, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 0) e(0); - - if (listen(fd, 5) != 0) e(0); - - /* - * Any socket options should be inherited from the listening socket at - * connect time, and not be re-inherited at accept time. It does not - * really matter what socket option we set here, as long as it is - * supposed to be inherited. - */ - val = 123; - if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) != 0) - e(0); - - fd2 = get_bound_socket(type, SOCK_PATH_B, &sunB); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - val = 456; - if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) != 0) - e(0); - - /* - * Obtaining the peer name should work. As always, the name should be - * inherited from the listening socket. - */ - len = sizeof(sunC); - if (getpeername(fd2, (struct sockaddr *)&sunC, &len) != 0) e(0); - check_addr(&sunC, len, SOCK_PATH_A); - - /* - * Obtaining peer credentials should work. This is why NetBSD obtains - * the peer credentials at bind time, not at accept time. - */ - if (getpeereid(fd2, &uid, &gid) != 0) e(0); - if (uid != geteuid()) e(0); - if (gid != getegid()) e(0); - - /* - * Sending to the socket should work, and it should be possible to - * receive the data from the other side once accepted. - */ - if (send(fd2, "Hello, ", 7, 0) != 7) e(0); - if (send(fd2, "world!", 6, 0) != 6) e(0); - - /* Shutdown settings should be visible after accepting, too. */ - if (shutdown(fd2, SHUT_RDWR) != 0) e(0); - - len = sizeof(sunB); - if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0); - check_addr(&sunB, len, SOCK_PATH_B); - - len = sizeof(val); - if (getsockopt(fd3, SOL_SOCKET, SO_SNDLOWAT, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 123) e(0); - - if (recv(fd3, buf, 7, 0) != 7) e(0); - if (memcmp(buf, "Hello, ", 7) != 0) e(0); - if (recv(fd3, buf, 7, 0) != 6) e(0); - if (memcmp(buf, "world!", 6) != 0) e(0); - - if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); - - if (send(fd3, "X", 1, MSG_NOSIGNAL) != -1) e(0); - if (errno != EPIPE) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd3) != 0) e(0); - - if (unlink(SOCK_PATH_B) != 0) e(0); - - /* - * If the socket pending acceptance is closed, the listening socket - * should pretend as though the connection was never there. - */ - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - FD_ZERO(&fds); - FD_SET(fd, &fds); - tv.tv_sec = 0; - tv.tv_usec = 0; - if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (close(fd2) != 0) e(0); - - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fds)) e(0); - - len = sizeof(sunB); - if (accept(fd, (struct sockaddr *)&sunB, &len) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - /* - * Try the same thing, but now with the connection sandwiched between - * two different pending connections, which should be left intact. - */ - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (send(fd2, "A", 1, 0) != 1) e(0); - - if ((fd3 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd3, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (send(fd3, "B", 1, 0) != 1) e(0); - - if ((fd4 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd4, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (send(fd4, "C", 1, 0) != 1) e(0); - - if (close(fd3) != 0) e(0); - - len = sizeof(sunB); - if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0); - - if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0); - if (buf[0] != 'A') e(0); - - if (close(fd3) != 0) e(0); - if (close(fd2) != 0) e(0); - - FD_ZERO(&fds); - FD_SET(fd, &fds); - tv.tv_sec = 0; - tv.tv_usec = 0; - if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - len = sizeof(sunB); - if ((fd3 = accept(fd, (struct sockaddr *)&sunB, &len)) < 0) e(0); - - if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0); - if (buf[0] != 'C') e(0); - - if (close(fd3) != 0) e(0); - if (close(fd4) != 0) e(0); - - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fds)) e(0); - - len = sizeof(sunB); - if (accept(fd, (struct sockaddr *)&sunB, &len) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - /* - * If the listening socket is closed, the socket pending acceptance - * should be reset. We actually rely on this behavior in the sweep - * test, but we test this with more than one socket this time. - */ - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if ((fd3 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd3, (struct sockaddr *)&sunA, sizeof(sunA)) != 0) e(0); - - if (close(fd) != 0) e(0); - - if (recv(fd2, buf, sizeof(buf), 0) != -1) e(0); - if (errno != ECONNRESET) e(0); - - if (recv(fd2, buf, sizeof(buf), 0) != 0) e(0); - - if (recv(fd3, buf, sizeof(buf), 0) != -1) e(0); - if (errno != ECONNRESET) e(0); - - if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); - - if (close(fd3) != 0) e(0); - - if (close(fd2) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Test accepting connections without LOCAL_CONNWAIT. Since both the old UDS - * service and the initial version of the new UDS service supported only the - * LOCAL_CONNWAIT behavior, the alternative (which is now the default, as it is - * on other platforms) has been a bit under-tested so far. - */ -static void -test90y(void) -{ - - subtest = 25; - - sub90y(SOCK_STREAM); - - sub90y(SOCK_SEQPACKET); -} - -/* - * Test that SO_LINGER has no effect on sockets of the given type. - */ -static void -sub90z(int type) -{ - struct sockaddr_un sun; - socklen_t len; - struct linger l; - char buf[1]; - int fd, fd2, fd3; - - fd = get_bound_socket(type, SOCK_PATH_A, &sun); - - if (listen(fd, 1) != 0) e(0); - - if ((fd2 = socket(AF_UNIX, type, 0)) < 0) e(0); - - if (connect(fd2, (struct sockaddr *)&sun, sizeof(sun)) != 0) e(0); - - len = sizeof(sun); - if ((fd3 = accept(fd, (struct sockaddr *)&sun, &len)) < 0) e(0); - - if (close(fd) != 0) e(0); - - if (send(fd2, "A", 1, 0) != 1) e(0); - - l.l_onoff = 1; - l.l_linger = 0; - if (setsockopt(fd2, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) != 0) e(0); - - if (close(fd2) != 0) e(0); - - if (recv(fd3, buf, sizeof(buf), 0) != 1) e(0); - if (buf[0] != 'A') e(0); - - /* We should not get ECONNRESET now. */ - if (recv(fd3, buf, sizeof(buf), 0) != 0) e(0); - - if (close(fd3) != 0) e(0); - - if (unlink(SOCK_PATH_A) != 0) e(0); -} - -/* - * Test that SO_LINGER has no effect on UNIX domain sockets. In particular, a - * timeout of zero does not cause the connection to be reset forcefully. - */ -static void -test90z(void) -{ - - subtest = 26; - - sub90z(SOCK_STREAM); - - sub90z(SOCK_SEQPACKET); -} - -/* - * Test program for UDS. - */ -int -main(int argc, char ** argv) -{ - int i, m; - - start(90); - - if (argc == 2) - m = atoi(argv[1]); - else - m = 0xFFFFFFF; - - for (i = 0; i < ITERATIONS; i++) { - if (m & 0x0000001) test90a(); - if (m & 0x0000002) test90b(); - if (m & 0x0000004) test90c(); - if (m & 0x0000008) test90d(); - if (m & 0x0000010) test90e(); - if (m & 0x0000020) test90f(); - if (m & 0x0000040) test90g(); - if (m & 0x0000080) test90h(); - if (m & 0x0000100) test90i(); - if (m & 0x0000200) test90j(); - if (m & 0x0000400) test90k(); - if (m & 0x0000800) test90l(); - if (m & 0x0001000) test90m(); - if (m & 0x0002000) test90n(); - if (m & 0x0004000) test90o(); - if (m & 0x0008000) test90p(); - if (m & 0x0010000) test90q(); - if (m & 0x0020000) test90r(); - if (m & 0x0040000) test90s(); - if (m & 0x0080000) test90t(); - if (m & 0x0100000) test90u(); - if (m & 0x0200000) test90v(); - if (m & 0x0400000) test90w(); - if (m & 0x0800000) test90x(); - if (m & 0x1000000) test90y(); - if (m & 0x2000000) test90z(); - } - - quit(); - /* NOTREACHED */ -} diff --git a/minix/tests/test92.c b/minix/tests/test92.c deleted file mode 100644 index df5c71d61..000000000 --- a/minix/tests/test92.c +++ /dev/null @@ -1,1735 +0,0 @@ -/* Tests for RAW sockets (LWIP) - by D.C. van Moolenbroek */ -/* This test needs to be run as root: creating raw sockets is root-only. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "socklib.h" - -#define ITERATIONS 2 - -#define TEST_PROTO 253 /* from RFC 3692 */ -#define TEST_ICMPV6_TYPE_A 200 /* from RFC 4443 */ -#define TEST_ICMPV6_TYPE_B 201 /* from RFC 4443 */ - -static const enum state raw_states[] = { - S_NEW, S_N_SHUT_R, S_N_SHUT_W, S_N_SHUT_RW, - S_BOUND, S_CONNECTED, S_SHUT_R, S_SHUT_W, - S_SHUT_RW, -}; - -static const int raw_results[][__arraycount(raw_states)] = { - [C_ACCEPT] = { - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, - }, - [C_BIND] = { - 0, 0, 0, 0, - 0, -EINVAL, -EINVAL, -EINVAL, - -EINVAL, - }, - [C_CONNECT] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_GETPEERNAME] = { - -ENOTCONN, -ENOTCONN, -ENOTCONN, -ENOTCONN, - -ENOTCONN, 0, 0, 0, - 0, - }, - [C_GETSOCKNAME] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_GETSOCKOPT_ERR] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_GETSOCKOPT_KA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_GETSOCKOPT_RB] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_IOCTL_NREAD] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_LISTEN] = { - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, -EOPNOTSUPP, - -EOPNOTSUPP, - }, - [C_RECV] = { - -EAGAIN, 0, -EAGAIN, 0, - -EAGAIN, -EAGAIN, 0, -EAGAIN, - 0, - }, - [C_RECVFROM] = { - -EAGAIN, 0, -EAGAIN, 0, - -EAGAIN, -EAGAIN, 0, -EAGAIN, - 0, - }, - [C_SEND] = { - -EDESTADDRREQ, -EDESTADDRREQ, -EPIPE, -EPIPE, - -EDESTADDRREQ, 1, 1, -EPIPE, - -EPIPE, - }, - [C_SENDTO] = { - 1, 1, -EPIPE, -EPIPE, - 1, 1, 1, -EPIPE, - -EPIPE, - }, - [C_SELECT_R] = { - 0, 1, 0, 1, - 0, 0, 1, 0, - 1, - }, - [C_SELECT_W] = { - 1, 1, 1, 1, - 1, 1, 1, 1, - 1, - }, - [C_SELECT_X] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SETSOCKOPT_BC] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SETSOCKOPT_KA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SETSOCKOPT_L] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SETSOCKOPT_RA] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SHUTDOWN_R] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SHUTDOWN_RW] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, - [C_SHUTDOWN_W] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, - }, -}; - -/* - * Set up a RAW socket file descriptor in the requested state and pass it to - * socklib_sweep_call() along with local and remote addresses and their length. - */ -static int -raw_sweep(int domain, int type, int protocol, enum state state, - enum call call) -{ - struct sockaddr_in sinA, sinB; - struct sockaddr_in6 sin6A, sin6B; - struct sockaddr *addrA, *addrB; - socklen_t addr_len; - int r, fd, fd2; - - if (domain == AF_INET) { - memset(&sinA, 0, sizeof(sinA)); - sinA.sin_family = domain; - sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memcpy(&sinB, &sinA, sizeof(sinB)); - - addrA = (struct sockaddr *)&sinA; - addrB = (struct sockaddr *)&sinB; - addr_len = sizeof(sinA); - } else { - assert(domain == AF_INET6); - - memset(&sin6A, 0, sizeof(sin6A)); - sin6A.sin6_family = domain; - memcpy(&sin6A.sin6_addr, &in6addr_loopback, - sizeof(sin6A.sin6_addr)); - - memcpy(&sin6B, &sin6A, sizeof(sin6B)); - - addrA = (struct sockaddr *)&sin6A; - addrB = (struct sockaddr *)&sin6B; - addr_len = sizeof(sin6A); - } - - /* Create a bound remote socket. */ - if ((fd2 = socket(domain, type | SOCK_NONBLOCK, protocol)) < 0) e(0); - - if (bind(fd2, addrB, addr_len) != 0) e(0); - - switch (state) { - case S_NEW: - case S_N_SHUT_R: - case S_N_SHUT_W: - case S_N_SHUT_RW: - if ((fd = socket(domain, type | SOCK_NONBLOCK, - protocol)) < 0) e(0); - - switch (state) { - case S_N_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_N_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_N_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - break; - - case S_BOUND: - case S_CONNECTED: - case S_SHUT_R: - case S_SHUT_W: - case S_SHUT_RW: - if ((fd = socket(domain, type | SOCK_NONBLOCK, - protocol)) < 0) e(0); - - if (bind(fd, addrA, addr_len) != 0) e(0); - - if (state == S_BOUND) - break; - - if (connect(fd, addrB, addr_len) != 0) e(0); - - switch (state) { - case S_SHUT_R: if (shutdown(fd, SHUT_RD)) e(0); break; - case S_SHUT_W: if (shutdown(fd, SHUT_WR)) e(0); break; - case S_SHUT_RW: if (shutdown(fd, SHUT_RDWR)) e(0); break; - default: break; - } - - break; - - default: - fd = -1; - e(0); - } - - r = socklib_sweep_call(call, fd, addrA, addrB, addr_len); - - if (close(fd) != 0) e(0); - if (fd2 != -1 && close(fd2) != 0) e(0); - - return r; -} - -/* - * Sweep test for socket calls versus socket states of RAW sockets. - */ -static void -test92a(void) -{ - - subtest = 1; - - socklib_sweep(AF_INET, SOCK_RAW, TEST_PROTO, raw_states, - __arraycount(raw_states), (const int *)raw_results, raw_sweep); - - socklib_sweep(AF_INET6, SOCK_RAW, TEST_PROTO, raw_states, - __arraycount(raw_states), (const int *)raw_results, raw_sweep); -} - -/* - * Basic I/O test for raw sockets. - */ -static void -test92b(void) -{ - struct sockaddr_in sinA, sinB, sinC; - struct sockaddr_in6 sin6A, sin6B, sin6C; - socklen_t len; - unsigned int i; - uint8_t buf[256], packet[5]; - int fd, fd2; - - subtest = 2; - - /* First test IPv4. */ - if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - memset(&sinA, 0, sizeof(sinA)); - sinA.sin_family = AF_INET; - sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - for (i = 0; i < __arraycount(packet); i++) - packet[i] = (uint8_t)(-i); - - if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sinA, - sizeof(sinA)) != sizeof(packet)) e(0); - - memset(buf, 0, sizeof(buf)); - len = sizeof(sinB); - if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sinB, - &len) != sizeof(struct ip) + sizeof(packet)) e(0); - - if (memcmp(&buf[sizeof(struct ip)], packet, sizeof(packet)) != 0) e(0); - - if (len != sizeof(sinB)) e(0); - if (sinB.sin_len != sizeof(sinB)) e(0); - if (sinB.sin_family != AF_INET) e(0); - if (sinB.sin_port != htons(0)) e(0); - if (sinB.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) e(0); - - /* - * Test two additional things: - * - * 1) a non-zero port number is ignored when sending; - * 2) multiple raw sockets may receive the same packet. - */ - sinA.sin_port = htons(22); - - if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sinA, - sizeof(sinA)) != sizeof(packet)) e(0); - - memset(buf, 0, sizeof(buf)); - len = sizeof(sinC); - if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sinC, - &len) != sizeof(struct ip) + sizeof(packet)) e(0); - - if (memcmp(&buf[sizeof(struct ip)], packet, sizeof(packet)) != 0) e(0); - - if (len != sizeof(sinC)) e(0); - if (memcmp(&sinB, &sinC, sizeof(sinB)) != 0) e(0); - - memset(buf, 0, sizeof(buf)); - len = sizeof(sinC); - if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sinC, - &len) != sizeof(struct ip) + sizeof(packet)) e(0); - - if (memcmp(&buf[sizeof(struct ip)], packet, sizeof(packet)) != 0) e(0); - - if (len != sizeof(sinC)) e(0); - if (memcmp(&sinB, &sinC, sizeof(sinB)) != 0) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - /* Then test IPv6. */ - if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - memset(&sin6A, 0, sizeof(sin6A)); - sin6A.sin6_family = AF_INET6; - memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr)); - - if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sin6A, - sizeof(sin6A)) != sizeof(packet)) e(0); - - memset(buf, 0, sizeof(buf)); - len = sizeof(sin6B); - if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6B, - &len) != sizeof(packet)) e(0); - - if (memcmp(buf, packet, sizeof(packet)) != 0) e(0); - - if (len != sizeof(sin6B)) e(0); - if (sin6B.sin6_len != sizeof(sin6B)) e(0); - if (sin6B.sin6_family != AF_INET6) e(0); - if (sin6B.sin6_port != htons(0)) e(0); - if (memcmp(&sin6B.sin6_addr, &in6addr_loopback, - sizeof(sin6B.sin6_addr)) != 0) e(0); - - /* As above. */ - sin6A.sin6_port = htons(22); - - if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (sendto(fd, packet, sizeof(packet), 0, (struct sockaddr *)&sin6A, - sizeof(sin6A)) != sizeof(packet)) e(0); - - memset(buf, 0, sizeof(buf)); - len = sizeof(sin6C); - if (recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6C, - &len) != sizeof(packet)) e(0); - - if (memcmp(buf, packet, sizeof(packet)) != 0) e(0); - - if (len != sizeof(sin6C)) e(0); - if (memcmp(&sin6B, &sin6C, sizeof(sin6B)) != 0) e(0); - - memset(buf, 0, sizeof(buf)); - len = sizeof(sin6C); - if (recvfrom(fd2, buf, sizeof(buf), 0, (struct sockaddr *)&sin6C, - &len) != sizeof(packet)) e(0); - - if (memcmp(buf, packet, sizeof(packet)) != 0) e(0); - - if (len != sizeof(sin6C)) e(0); - if (memcmp(&sin6B, &sin6C, sizeof(sin6B)) != 0) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); -} - -/* - * Test the IPV6_CHECKSUM socket option. - */ -static void -test92c(void) -{ - struct sockaddr_in6 sin6; - struct icmp6_hdr icmp6_hdr; - uint8_t buf[6], buf2[6], *buf3; - socklen_t len; - unsigned int i; - int fd, fd2, val; - - subtest = 3; - - if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (shutdown(fd, SHUT_RD) != 0) e(0); - - /* For non-ICMPv6 sockets, checksumming is disabled by default. */ - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != -1) e(0); - - /* Test bad offsets. */ - val = -2; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != -1) e(0); - if (errno != EINVAL) e(0); - - val = 1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* Now test real checksum computation. */ - val = 0; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (shutdown(fd2, SHUT_WR) != 0) e(0); - - memset(buf, 0, sizeof(buf)); - buf[2] = 0xfe; - buf[3] = 0x95; - buf[4] = 0x4d; - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); - - if (sendto(fd, buf, 5, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 5) - e(0); - - if (recv(fd2, buf2, sizeof(buf2), 0) != 5) e(0); - - if (buf2[0] != 0xb3 || buf2[1] != 0x65) e(0); - if (memcmp(&buf2[2], &buf[2], 3) != 0) e(0); - - /* Turn on checksum verification on the receiving socket. */ - val = 0; - if (setsockopt(fd2, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - /* - * The current value of the checksum field should not be incorporated - * in the checksum, as that would result in an invalid checksum. - */ - buf[0] = 0xab; - buf[1] = 0xcd; - - if (sendto(fd, buf, 5, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 5) - e(0); - - if (recv(fd2, buf2, sizeof(buf2), 0) != 5) e(0); - - if (buf2[0] != 0xb3 || buf2[1] != 0x65) e(0); - if (memcmp(&buf2[2], &buf[2], 3) != 0) e(0); - - /* - * Turn off checksum computation on the sending side, so that the - * packet ends up being dropped on the receiving side. - */ - val = -1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - if (sendto(fd, buf, 5, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 5) - e(0); - - /* Send some packets that are too small to contain the checksum. */ - if (sendto(fd, buf, 0, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) - e(0); - if (sendto(fd, buf, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 1) - e(0); - - /* - * If this recv call is "too soon" (it should not be) and the packets - * arrive later anyway, then we will get a failure below. - */ - if (recv(fd2, buf2, sizeof(buf2), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - buf[0] = 0; - buf[1] = 0x67; - if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != 4) e(0); - - if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0); - if (memcmp(buf, buf2, 4) != 0) e(0); - - /* - * We repeat some of the tests with a non-zero checksum offset, just to - * be sure. - */ - val = 2; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 2) e(0); - - buf[0] = 0x56; - buf[1] = 0x78; - - for (i = 0; i <= 3; i++) { - if (sendto(fd, buf, i, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != -1) e(0); - if (errno != EINVAL) e(0); - } - - val = 2; - if (setsockopt(fd2, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != 4) e(0); - - if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0); - if (memcmp(buf, buf2, 2) != 0) e(0); - if (buf2[2] != 0xa8 || buf2[3] != 0x84) e(0); - - val = -1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - buf[2] = 0xa8; - buf[3] = 0x85; /* deliberately bad checksum */ - - /* All these should be dropped on the receiver side. */ - for (i = 0; i <= 4; i++) { - if (sendto(fd, buf, i, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != i) e(0); - } - - buf[3] = 0x84; /* good checksum */ - if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 4) - e(0); - - if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0); - if (memcmp(buf, buf2, 4) != 0) e(0); - - if (recv(fd2, buf2, sizeof(buf2), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - val = -1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - if (setsockopt(fd2, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - buf[3] = 0x85; - if (sendto(fd, buf, 4, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != 4) - e(0); - - if (recv(fd2, buf2, sizeof(buf2), 0) != 4) e(0); - if (memcmp(buf, buf2, 4) != 0) e(0); - - /* - * The following is a lwIP-specific test: lwIP does not support storing - * generated checksums beyond the first pbuf. We do not know the size - * of the first pbuf until we actually send a packet, so the setsockopt - * call will not fail, but sending the packet will. Depending on the - * buffer allocation strategy, the following test may or may not - * trigger this case; simply ensure that we do not crash the service. - */ - if ((buf3 = malloc(4096)) == NULL) e(0); - - val = 4094; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != 0) e(0); - - /* This call may or may not fail, but if it fails, it yields EINVAL. */ - if (sendto(fd, buf3, 4096, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) == -1 && errno != EINVAL) e(0); - - free(buf3); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - /* For ICMPv6 packets, checksumming is always enabled. */ - if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 2) e(0); - - val = -1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != -1) e(0); - if (errno != EINVAL) e(0); - - memset(&icmp6_hdr, 0, sizeof(icmp6_hdr)); - icmp6_hdr.icmp6_type = TEST_ICMPV6_TYPE_A; - icmp6_hdr.icmp6_code = 123; - icmp6_hdr.icmp6_cksum = htons(0); - - len = offsetof(struct icmp6_hdr, icmp6_dataun); - if (sendto(fd, &icmp6_hdr, len, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != len) e(0); - - if (recv(fd, &icmp6_hdr, sizeof(icmp6_hdr), 0) != len) e(0); - - if (icmp6_hdr.icmp6_type != TEST_ICMPV6_TYPE_A) e(0); - if (icmp6_hdr.icmp6_code != 123) e(0); - if (ntohs(icmp6_hdr.icmp6_cksum) != 0x3744) e(0); - - if (close(fd) != 0) e(0); - - /* For IPv4 and non-RAW IPv6 sockets, the option does not work. */ - for (i = 0; i <= 2; i++) { - switch (i) { - case 0: fd = socket(AF_INET6, SOCK_DGRAM, 0); break; - case 1: fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6); break; - case 2: fd = socket(AF_INET, SOCK_RAW, TEST_PROTO); break; - } - if (fd < 0) e(0); - - val = -1; - if (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - sizeof(val)) != -1) e(0); - if (errno != ENOPROTOOPT) e(0); - - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, - &len) != -1) e(0); - if (errno != ENOPROTOOPT) e(0); - - if (close(fd) != 0) e(0); - } -} - -/* - * Test the ICMP6_FILTER socket option. - */ -static void -test92d(void) -{ - struct sockaddr_in6 sin6; - struct sockaddr_in sin; - struct icmp6_filter filter; - struct icmp6_hdr packet; - socklen_t len; - struct timeval tv; - unsigned int i; - int fd, fd2; - - subtest = 4; - - /* - * We use two different sockets to eliminate the possibility that the - * filter is also applied when sending packets--it should not be. - */ - if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0); - - if (shutdown(fd, SHUT_WR) != 0) e(0); - - len = sizeof(filter); - if (getsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, &len) != 0) - e(0); - - /* We do not aim to test the ICMP6_FILTER macros here. */ - for (i = 0; i <= UINT8_MAX; i++) - if (!ICMP6_FILTER_WILLPASS(i, &filter)) e(0); - - if ((fd2 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0); - - ICMP6_FILTER_SETBLOCKALL(&filter); - if (setsockopt(fd2, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)) != 0) e(0); - - len = sizeof(filter); - if (getsockopt(fd2, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, &len) != 0) - e(0); - - for (i = 0; i <= UINT8_MAX; i++) - if (ICMP6_FILTER_WILLPASS(i, &filter)) e(0); - - ICMP6_FILTER_SETPASSALL(&filter); - ICMP6_FILTER_SETBLOCK(TEST_ICMPV6_TYPE_A, &filter); - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)) != 0) e(0); - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); - - memset(&packet, 0, sizeof(packet)); - packet.icmp6_type = TEST_ICMPV6_TYPE_A; - packet.icmp6_code = 12; - - if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(packet)) e(0); - - packet.icmp6_type = TEST_ICMPV6_TYPE_B; - packet.icmp6_code = 34; - - if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(packet)) e(0); - - memset(&packet, 0, sizeof(packet)); - - if (recv(fd, &packet, sizeof(packet), 0) != sizeof(packet)) e(0); - if (packet.icmp6_type != TEST_ICMPV6_TYPE_B) e(0); - if (packet.icmp6_code != 34) e(0); - - if (recv(fd, &packet, sizeof(packet), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - ICMP6_FILTER_SETBLOCKALL(&filter); - ICMP6_FILTER_SETPASS(TEST_ICMPV6_TYPE_A, &filter); - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)) != 0) e(0); - - memset(&packet, 0, sizeof(packet)); - packet.icmp6_type = TEST_ICMPV6_TYPE_B; - packet.icmp6_code = 56; - - if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(packet)) e(0); - - packet.icmp6_type = TEST_ICMPV6_TYPE_A; - packet.icmp6_code = 78; - - if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(packet)) e(0); - - /* - * RFC 3542 states that setting a zero-length filter resets the filter. - * This seems like one of those things that a standardization RFC - * should not mandate: it is redundant at the API level (one can set a - * PASSALL filter, which is the required default), it relies on an edge - * case (setsockopt taking a zero-length argument), and as a "shortcut" - * it does not even cover a case that is likely to occur (no actual - * program would reset its filter on a regular basis). Presumably it - * is a way to deallocate filter memory on some platforms, but was that - * worth the RFC inclusion? Anyhow, we support it; NetBSD does not. - */ - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, NULL, 0) != 0) e(0); - - packet.icmp6_type = TEST_ICMPV6_TYPE_B; - packet.icmp6_code = 90; - - if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(packet)) e(0); - - memset(&packet, 0, sizeof(packet)); - - if (recv(fd, &packet, sizeof(packet), 0) != sizeof(packet)) e(0); - if (packet.icmp6_type != TEST_ICMPV6_TYPE_A) e(0); - if (packet.icmp6_code != 78) e(0); - - if (recv(fd, &packet, sizeof(packet), 0) != sizeof(packet)) e(0); - if (packet.icmp6_type != TEST_ICMPV6_TYPE_B) e(0); - if (packet.icmp6_code != 90) e(0); - - if (recv(fd, &packet, sizeof(packet), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (recv(fd2, &packet, sizeof(packet), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - len = sizeof(filter); - if (getsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, &len) != 0) - e(0); - - for (i = 0; i <= UINT8_MAX; i++) - if (!ICMP6_FILTER_WILLPASS(i, &filter)) e(0); - - if (close(fd2) != 0) e(0); - - /* - * Let's get weird and send an ICMPv6 packet from an IPv4 socket. - * Currently, such packets are always dropped based on the rule that - * IPv6 sockets with checksumming enabled drop all IPv4 packets. As it - * happens, that is also all that is keeping this packet from arriving. - */ - if ((fd2 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0); - - ICMP6_FILTER_SETBLOCKALL(&filter); - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)) != 0) e(0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - memset(&packet, 0, sizeof(packet)); - packet.icmp6_type = TEST_ICMPV6_TYPE_A; - packet.icmp6_code = 123; - packet.icmp6_cksum = htons(0); /* TODO: use valid checksum */ - - if (sendto(fd2, &packet, sizeof(packet), 0, (struct sockaddr *)&sin, - sizeof(sin)) != sizeof(packet)) e(0); - - /* - * If the packet were to arrive at all, it should arrive instantly, so - * this is just an excuse to use SO_RCVTIMEO. - */ - tv.tv_sec = 0; - tv.tv_usec = 100000; - if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) - e(0); - - if (recv(fd, &packet, sizeof(packet), 0) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (close(fd2) != 0) e(0); - - if (close(fd) != 0) e(0); - - /* Make sure ICMP6_FILTER works on IPv6-ICMPv6 sockets only. */ - for (i = 0; i <= 2; i++) { - switch (i) { - case 0: fd = socket(AF_INET6, SOCK_DGRAM, 0); break; - case 1: fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO); break; - case 2: fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6); break; - } - if (fd < 0) e(0); - - if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)) != -1) e(0); - if (errno != ENOPROTOOPT) e(0); - - len = sizeof(filter); - if (getsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - &len) != -1) e(0); - if (errno != ENOPROTOOPT) e(0); - - if (close(fd) != 0) e(0); - } -} - -/* - * Test that IPPROTO_ICMPV6 has no special value on IPv4 raw sockets. In - * particular, test that no checksum is generated or verified. By now we have - * already tested that none of the IPv6 socket options work on such sockets. - */ -static void -test92e(void) -{ - char buf[sizeof(struct ip) + sizeof(struct icmp6_hdr)]; - struct sockaddr_in sin; - struct icmp6_hdr packet; - int fd; - - subtest = 5; - - if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0); - - memset(&packet, 0, sizeof(packet)); - packet.icmp6_type = TEST_ICMPV6_TYPE_A; - packet.icmp6_code = 123; - packet.icmp6_cksum = htons(0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (sendto(fd, &packet, sizeof(packet), 0, (struct sockaddr *)&sin, - sizeof(sin)) != sizeof(packet)) e(0); - - if (recv(fd, buf, sizeof(buf), 0) != sizeof(buf)) e(0); - - memcpy(&packet, &buf[sizeof(struct ip)], sizeof(packet)); - if (packet.icmp6_type != TEST_ICMPV6_TYPE_A) e(0); - if (packet.icmp6_code != 123) e(0); - if (packet.icmp6_cksum != htons(0)) e(0); - - if (close(fd) != 0) e(0); -} - -struct testpkt { - struct ip ip; - struct udphdr udp; - uint8_t data[6]; -} __packed; - -/* - * Test the IP_HDRINCL socket option. - */ -static void -test92f(void) -{ - struct sockaddr_in sin; - struct testpkt pkt, pkt2; - socklen_t len; - char buf[7]; - unsigned int i; - int fd, fd2, val; - - subtest = 6; - - /* See if we can successfully feign a UDP packet. */ - memset(&pkt, 0, sizeof(pkt)); - pkt.ip.ip_v = IPVERSION; - pkt.ip.ip_hl = sizeof(pkt.ip) >> 2; - pkt.ip.ip_tos = 123; - pkt.ip.ip_len = sizeof(pkt); /* swapped by OS */ - pkt.ip.ip_id = htons(456); - pkt.ip.ip_off = IP_DF; /* swapped by OS */ - pkt.ip.ip_ttl = 78; - pkt.ip.ip_p = IPPROTO_UDP; - pkt.ip.ip_sum = htons(0); /* filled by OS */ - pkt.ip.ip_src.s_addr = htonl(INADDR_LOOPBACK); - pkt.ip.ip_dst.s_addr = htonl(INADDR_LOOPBACK); - pkt.udp.uh_sport = htons(TEST_PORT_B); - pkt.udp.uh_dport = htons(TEST_PORT_A); - pkt.udp.uh_sum = htons(0); /* lazy.. */ - pkt.udp.uh_ulen = htons(sizeof(pkt.udp) + sizeof(pkt.data)); - memcpy(pkt.data, "Hello!", sizeof(pkt.data)); - - if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) e(0); - - if (shutdown(fd, SHUT_RD) != 0) e(0); - - /* IP_HDRINCL is never enabled by default. */ - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 0) e(0); - - val = 1; - if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) != 0) - e(0); - - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 1) e(0); - - if ((fd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(TEST_PORT_A); - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(fd2, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); - - sin.sin_port = htons(0); - - if (sendto(fd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&sin, - sizeof(sin)) != sizeof(pkt)) e(0); - - if (recv(fd2, &buf, sizeof(buf), 0) != sizeof(pkt.data)) e(0); - if (memcmp(buf, pkt.data, sizeof(pkt.data)) != 0) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, &len) != 0) e(0); - if (len != sizeof(val)) e(0); - if (val != 0) e(0); - - if (shutdown(fd, SHUT_RD) != 0) e(0); - - /* See if we can receive a packet for our own protocol. */ - pkt.ip.ip_p = TEST_PROTO; - - if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - val = 1; - if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) != 0) - e(0); - - if (sendto(fd, &pkt, sizeof(pkt), 0, (struct sockaddr *)&sin, - sizeof(sin)) != sizeof(pkt)) e(0); - - if (recv(fd2, &pkt2, sizeof(pkt2), 0) != sizeof(pkt2)) e(0); - - if (pkt2.ip.ip_v != pkt.ip.ip_v) e(0); - if (pkt2.ip.ip_hl != pkt.ip.ip_hl) e(0); - if (pkt2.ip.ip_tos != pkt.ip.ip_tos) e(0); - if (pkt2.ip.ip_len != pkt.ip.ip_len) e(0); - if (pkt2.ip.ip_id != pkt.ip.ip_id) e(0); - if (pkt2.ip.ip_off != pkt.ip.ip_off) e(0); - if (pkt2.ip.ip_ttl != pkt.ip.ip_ttl) e(0); - if (pkt2.ip.ip_p != pkt.ip.ip_p) e(0); - if (pkt2.ip.ip_sum == htons(0)) e(0); - if (pkt2.ip.ip_src.s_addr != pkt.ip.ip_src.s_addr) e(0); - if (pkt2.ip.ip_dst.s_addr != pkt.ip.ip_dst.s_addr) e(0); - - /* - * Test sending packets with weird sizes to ensure that we do not crash - * the service. These packets would never arrive anyway. - */ - if (sendto(fd, &pkt, 0, 0, (struct sockaddr *)&sin, - sizeof(sin)) != -1) e(0); - if (errno != EINVAL) e(0); - if (sendto(fd, &pkt, sizeof(pkt.ip) - 1, 0, (struct sockaddr *)&sin, - sizeof(sin)) != -1) e(0); - if (errno != EINVAL) e(0); - if (sendto(fd, &pkt, sizeof(pkt.ip), 0, (struct sockaddr *)&sin, - sizeof(sin)) != sizeof(pkt.ip)) e(0); - - if (recv(fd2, &pkt2, sizeof(pkt2), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - /* Ensure that the socket option does not work on other types. */ - for (i = 0; i <= 1; i++) { - switch (i) { - case 0: fd = socket(AF_INET, SOCK_DGRAM, 0); break; - case 1: fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO); break; - } - if (fd < 0) e(0); - - len = sizeof(val); - if (getsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, - &len) != -1) e(0); - if (errno != ENOPROTOOPT) e(0); - - if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, - sizeof(val)) != -1) e(0); - if (errno != ENOPROTOOPT) e(0); - - if (close(fd) != 0) e(0); - } -} - -/* - * Test the IPPROTO_RAW socket protocol. This test mostly shows that the - * IPPROTO_RAW protocol is nothing special: for both IPv4 and IPv6, it sends - * and receives packets with that protocol number. We already tested earlier - * that IP_HDRINCL is disabled by default on IPPROTO_RAW sockets, too. - */ -static void -test92g(void) -{ - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - char buf[sizeof(struct ip) + 1]; - int fd; - - subtest = 7; - - if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) e(0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (sendto(fd, "A", 1, 0, (struct sockaddr *)&sin, - sizeof(sin)) != 1) e(0); - - if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != sizeof(buf)) e(0); - if (buf[sizeof(struct ip)] != 'A') e(0); - - if (close(fd) != 0) e(0); - - if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)) < 0) e(0); - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); - - if (sendto(fd, "B", 1, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != 1) e(0); - - if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != 1) e(0); - if (buf[0] != 'B') e(0); - - if (close(fd) != 0) e(0); -} - -/* - * Test that connected raw sockets perform correct source-based filtering. - */ -static void -test92h(void) -{ - struct sockaddr_in sinA, sinB; - struct sockaddr_in6 sin6A, sin6B; - struct sockaddr sa; - socklen_t len; - char buf[sizeof(struct ip) + 1]; - int fd, fd2; - - subtest = 8; - - if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - len = sizeof(sinB); - if (getpeername(fd, (struct sockaddr *)&sinB, &len) != -1) e(0); - if (errno != ENOTCONN) e(0); - - memset(&sinA, 0, sizeof(sinA)); - sinA.sin_family = AF_INET; - sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - /* - * First test that packets with the right source are accepted. - * Unfortunately, source and destination are the same in this case, so - * this test is far from perfect. - */ - if (connect(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); - - if (getpeername(fd, (struct sockaddr *)&sinB, &len) != 0) e(0); - - if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (sendto(fd2, "A", 1, 0, (struct sockaddr *)&sinA, - sizeof(sinA)) != 1) e(0); - - buf[0] = '\0'; - if (recv(fd2, buf, sizeof(buf), 0) != sizeof(struct ip) + 1) e(0); - if (buf[sizeof(struct ip)] != 'A') e(0); - - buf[0] = '\0'; - if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != - sizeof(struct ip) + 1) e(0); - if (buf[sizeof(struct ip)] != 'A') e(0); - - memset(&sa, 0, sizeof(sa)); - sa.sa_family = AF_UNSPEC; - - sinA.sin_addr.s_addr = htonl(INADDR_NONE); - - /* While here, test unconnecting the socket. */ - if (connect(fd, &sa, sizeof(sa)) != 0) e(0); - - if (getpeername(fd, (struct sockaddr *)&sinB, &len) != -1) e(0); - if (errno != ENOTCONN) e(0); - - /* Then test that packets with the wrong source are ignored. */ - if (connect(fd, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); - - if (sendto(fd2, "B", 1, 0, (struct sockaddr *)&sinB, - sizeof(sinB)) != 1) e(0); - - buf[0] = '\0'; - if (recv(fd2, buf, sizeof(buf), 0) != sizeof(struct ip) + 1) e(0); - if (buf[sizeof(struct ip)] != 'B') e(0); - - if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - /* Repeat for IPv6, but now the other way around. */ - if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - len = sizeof(sin6B); - if (getpeername(fd, (struct sockaddr *)&sin6B, &len) != -1) e(0); - if (errno != ENOTCONN) e(0); - - memset(&sin6A, 0, sizeof(sin6A)); - sin6A.sin6_family = AF_INET6; - memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr)); - - memcpy(&sin6B, &sin6A, sizeof(sin6B)); - if (inet_pton(AF_INET6, "::2", &sin6B.sin6_addr) != 1) e(0); - - if (connect(fd, (struct sockaddr *)&sin6B, sizeof(sin6B)) != 0) e(0); - - if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (sendto(fd2, "C", 1, 0, (struct sockaddr *)&sin6A, - sizeof(sin6A)) != 1) e(0); - - buf[0] = '\0'; - if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0); - if (buf[0] != 'C') e(0); - - if (recv(fd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (connect(fd, &sa, sizeof(sa)) != 0) e(0); - - if (connect(fd, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); - - if (sendto(fd2, "D", 1, 0, (struct sockaddr *)&sin6A, - sizeof(sin6A)) != 1) e(0); - - buf[0] = '\0'; - if (recv(fd2, buf, sizeof(buf), 0) != 1) e(0); - if (buf[0] != 'D') e(0); - - buf[0] = '\0'; - if (recv(fd, buf, sizeof(buf), 0) != 1) e(0); - if (buf[0] != 'D') e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); -} - -/* - * Test sending large and small RAW packets. This test is an altered copy of - * test91e, but has been changed to IPv6 to cover a greater spectrum together. - */ -static void -test92i(void) -{ - struct sockaddr_in6 sin6; - struct msghdr msg; - struct iovec iov; - char *buf; - unsigned int i, j; - int r, fd, fd2, val; - - subtest = 9; - - if ((buf = malloc(65536)) == NULL) e(0); - - if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); - - if (bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); - - val = 65536; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) != 0) - e(0); - - if ((fd2 = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - /* - * A maximum send buffer size of a full packet size's worth may always - * be set, although this is not necessarily the actual maximum. - */ - val = 65535; - if (setsockopt(fd2, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) != 0) - e(0); - - /* Find the largest possible packet size that can actually be sent. */ - for (i = 0; i < val; i += sizeof(int)) { - j = i ^ 0xdeadbeef; - memcpy(&buf[i], &j, sizeof(j)); - } - - for (val = 65536; val > 0; val--) { - if ((r = sendto(fd2, buf, val, 0, (struct sockaddr *)&sin6, - sizeof(sin6))) == val) - break; - if (r != -1) e(0); - if (errno != EMSGSIZE) e(0); - } - - if (val != 65535 - sizeof(struct ip6_hdr)) e(0); - - memset(buf, 0, val); - buf[val] = 'X'; - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = buf; - iov.iov_len = val + 1; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - if (recvmsg(fd, &msg, 0) != val) e(0); - if (msg.msg_flags != 0) e(0); - - for (i = 0; i < val; i += sizeof(int)) { - j = i ^ 0xdeadbeef; - if (memcmp(&buf[i], &j, MIN(sizeof(j), val - i))) e(0); - } - if (buf[val] != 'X') e(0); - - if (sendto(fd2, buf, val, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != - val) e(0); - - /* - * Make sure that there are no off-by-one errors in the receive code, - * and that MSG_TRUNC is set (only) when not the whole packet was - * received. - */ - memset(&iov, 0, sizeof(iov)); - iov.iov_base = buf; - iov.iov_len = val; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - if (recvmsg(fd, &msg, 0) != val) e(0); - if (msg.msg_flags != 0) e(0); - - if (sendto(fd2, buf, val, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != - val) e(0); - - buf[val - 1] = 'Y'; - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = buf; - iov.iov_len = val - 1; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - if (recvmsg(fd, &msg, 0) != val - 1) e(0); - if (msg.msg_flags != MSG_TRUNC) e(0); - - for (i = 0; i < val - 1; i += sizeof(int)) { - j = i ^ 0xdeadbeef; - if (memcmp(&buf[i], &j, MIN(sizeof(j), val - 1 - i))) e(0); - } - if (buf[val - 1] != 'Y') e(0); - - if (sendto(fd2, buf, val, 0, (struct sockaddr *)&sin6, sizeof(sin6)) != - val) e(0); - - buf[0] = 'Z'; - - memset(&iov, 0, sizeof(iov)); - iov.iov_base = buf; - iov.iov_len = 0; - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - if (recvmsg(fd, &msg, 0) != 0) e(0); - if (msg.msg_flags != MSG_TRUNC) e(0); - if (buf[0] != 'Z') e(0); - - /* Make sure that zero-sized packets can be sent and received. */ - if (sendto(fd2, buf, 0, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != 0) e(0); - - /* - * Note how we currently assume that packets sent over localhost will - * arrive immediately, so that we can use MSG_DONTWAIT to avoid that - * the test freezes. - */ - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - if (recvmsg(fd, &msg, MSG_DONTWAIT) != 0) e(0); - if (msg.msg_flags != 0) e(0); - if (buf[0] != 'Z') e(0); - - if (recv(fd, buf, val, MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - /* - * When sending lots of small packets, ensure that fewer packets arrive - * than we sent. This sounds weird, but we cannot actually check the - * internal TCP/IP buffer granularity and yet we want to make sure that - * the receive queue is measured in terms of buffers rather than packet - * sizes. In addition, we check that older packets are favored, - * instead discarding new ones when the receive buffer is full. - */ - for (i = 0; i < 65536 / sizeof(j); i++) { - j = i; - if (sendto(fd2, &j, sizeof(j), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(j)) e(0); - } - - for (i = 0; i < 1025; i++) { - r = recv(fd, &j, sizeof(j), MSG_DONTWAIT); - if (r == -1) { - if (errno != EWOULDBLOCK) e(0); - break; - } - if (r != sizeof(j)) e(0); - if (i != j) e(0); - } - if (i == 1025) e(0); - - if (close(fd2) != 0) e(0); - if (close(fd) != 0) e(0); - - free(buf); -} - -/* - * Test sending and receiving with bad pointers. - */ -static void -test92j(void) -{ - struct sockaddr_in sin; - char *ptr; - int i, fd; - - subtest = 10; - - if ((ptr = mmap(NULL, PAGE_SIZE * 2, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_PRIVATE, -1, 0)) == MAP_FAILED) e(0); - - if (munmap(&ptr[PAGE_SIZE], PAGE_SIZE) != 0) e(0); - - if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); - - memset(ptr, 'A', PAGE_SIZE); - - if (sendto(fd, &ptr[PAGE_SIZE / 2], PAGE_SIZE, 0, - (struct sockaddr *)&sin, sizeof(sin)) != -1) e(0); - if (errno != EFAULT) e(0); - - memset(ptr, 'B', PAGE_SIZE); - - if (sendto(fd, ptr, PAGE_SIZE - sizeof(struct ip), 0, - (struct sockaddr *)&sin, sizeof(sin)) != - PAGE_SIZE - sizeof(struct ip)) e(0); - - memset(ptr, 0, PAGE_SIZE); - - if (recvfrom(fd, &ptr[PAGE_SIZE / 2], PAGE_SIZE, 0, NULL, 0) != -1) - e(0); - if (errno != EFAULT) e(0); - - if (recvfrom(fd, ptr, PAGE_SIZE * 2, 0, NULL, 0) != PAGE_SIZE) e(0); - for (i = sizeof(struct ip); i < PAGE_SIZE; i++) - if (ptr[i] != 'B') e(0); - - if (close(fd) != 0) e(0); - - if (munmap(ptr, PAGE_SIZE) != 0) e(0); -} - -/* - * Test basic sysctl(2) socket enumeration support. - */ -static void -test92k(void) -{ - struct kinfo_pcb ki; - struct sockaddr_in lsin, rsin; - struct sockaddr_in6 lsin6, rsin6; - int fd, fd2, val; - - subtest = 11; - - if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0, - &ki) != 0) e(0); - - if ((fd = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - memset(&lsin, 0, sizeof(lsin)); - lsin.sin_len = sizeof(lsin); - lsin.sin_family = AF_INET; - - memset(&rsin, 0, sizeof(rsin)); - rsin.sin_len = sizeof(rsin); - rsin.sin_family = AF_INET; - - if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq != 0) e(0); - - if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0, - &ki) != 0) e(0); - - lsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(fd, (struct sockaddr *)&lsin, sizeof(lsin)) != 0) e(0); - - if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq != 0) e(0); - if (ki.ki_pflags & INP_HDRINCL) e(0); - - rsin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (connect(fd, (struct sockaddr *)&rsin, sizeof(rsin)) != 0) e(0); - - if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq != 0) e(0); - - if ((fd2 = socket(AF_INET, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - if (sendto(fd2, "ABC", 3, 0, (struct sockaddr *)&lsin, - sizeof(lsin)) != 3) e(0); - - if (close(fd2) != 0) e(0); - - val = 1; - if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) != 0) - e(0); - - if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin, sizeof(lsin)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin, sizeof(rsin)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq < 3) e(0); /* size is rounded up */ - if (!(ki.ki_pflags & INP_HDRINCL)) e(0); - - if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0, - &ki) != 0) e(0); - - if (close(fd) != 0) e(0); - - /* Test IPv6 sockets as well. */ - if ((fd = socket(AF_INET6, SOCK_RAW, TEST_PROTO)) < 0) e(0); - - memset(&lsin6, 0, sizeof(lsin6)); - lsin6.sin6_len = sizeof(lsin6); - lsin6.sin6_family = AF_INET6; - - memset(&rsin6, 0, sizeof(rsin6)); - rsin6.sin6_len = sizeof(rsin6); - rsin6.sin6_family = AF_INET6; - - if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq != 0) e(0); - - memcpy(&lsin6.sin6_addr, &in6addr_loopback, sizeof(lsin6.sin6_addr)); - if (bind(fd, (struct sockaddr *)&lsin6, sizeof(lsin6)) != 0) e(0); - - if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq != 0) e(0); - if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); - - memcpy(&rsin6.sin6_addr, &in6addr_loopback, sizeof(rsin6.sin6_addr)); - if (connect(fd, (struct sockaddr *)&rsin6, sizeof(rsin6)) != 0) - e(0); - - if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0, - &ki) != 1) e(0); - if (ki.ki_type != SOCK_RAW) e(0); - if (ki.ki_tstate != 0) e(0); - if (memcmp(&ki.ki_src, &lsin6, sizeof(lsin6)) != 0) e(0); - if (memcmp(&ki.ki_dst, &rsin6, sizeof(rsin6)) != 0) e(0); - if (ki.ki_sndq != 0) e(0); - if (ki.ki_rcvq != 0) e(0); - if (!(ki.ki_pflags & IN6P_IPV6_V6ONLY)) e(0); - - if (socklib_find_pcb("net.inet.raw.pcblist", TEST_PROTO, 0, 0, - &ki) != 0) e(0); - - if (close(fd) != 0) e(0); - - if (socklib_find_pcb("net.inet6.raw6.pcblist", TEST_PROTO, 0, 0, - &ki) != 0) e(0); -} - -/* - * Test local and remote IPv6 address handling. In particular, test scope IDs - * and IPv4-mapped IPv6 addresses. - */ -static void -test92l(void) -{ - - subtest = 12; - - socklib_test_addrs(SOCK_RAW, TEST_PROTO); -} - -/* - * Test setting and retrieving basic multicast transmission options. - */ -static void -test92m(void) -{ - - subtest = 13; - - socklib_multicast_tx_options(SOCK_RAW); -} - -/* - * Test multicast support. - */ -static void -test92n(void) -{ - - subtest = 14; - - socklib_test_multicast(SOCK_RAW, TEST_PROTO); -} - -/* - * Test small and large ICMP echo ("ping") packets. This test aims to confirm - * expected behavior resulting from the LWIP service's memory pool policies: - * lwIP should reply to ICMP echo requests that fit in a single 512-byte buffer - * (including space for ethernet headers, even on loopback interfaces), but not - * to requests exceeding a single buffer. - */ -static void -test92o(void) -{ - struct sockaddr_in6 sin6; - struct icmp6_hdr packet; - char buf[512]; - int fd; - - subtest = 15; - - /* IPv6 only for now, for simplicity reasons. */ - if ((fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) e(0); - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); - - memset(&packet, 0, sizeof(packet)); - packet.icmp6_type = ICMP6_ECHO_REQUEST; - packet.icmp6_code = 0; - packet.icmp6_id = getpid(); - packet.icmp6_seq = 1; - - memset(buf, 'A', sizeof(buf)); - memcpy(buf, &packet, sizeof(packet)); - - if (sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(buf)) e(0); - - packet.icmp6_seq = 2; - - memset(buf, 'B', sizeof(buf)); - memcpy(buf, &packet, sizeof(packet)); - - if (sendto(fd, buf, sizeof(buf) - 100, 0, (struct sockaddr *)&sin6, - sizeof(sin6)) != sizeof(buf) - 100) e(0); - - do { - memset(buf, '\0', sizeof(buf)); - - if (recv(fd, buf, sizeof(buf), 0) <= 0) e(0); - - memcpy(&packet, buf, sizeof(packet)); - } while (packet.icmp6_type == ICMP6_ECHO_REQUEST); - - if (packet.icmp6_type != ICMP6_ECHO_REPLY) e(0); - if (packet.icmp6_code != 0) e(0); - if (packet.icmp6_id != getpid()) e(0); - if (packet.icmp6_seq != 2) e(0); - if (buf[sizeof(buf) - 101] != 'B') e(0); - - if (close(fd) != 0) e(0); -} - -/* - * Test program for LWIP RAW sockets. - */ -int -main(int argc, char ** argv) -{ - int i, m; - - start(92); - - if (argc == 2) - m = atoi(argv[1]); - else - m = 0xFFFF; - - for (i = 0; i < ITERATIONS; i++) { - if (m & 0x0001) test92a(); - if (m & 0x0002) test92b(); - if (m & 0x0004) test92c(); - if (m & 0x0008) test92d(); - if (m & 0x0010) test92e(); - if (m & 0x0020) test92f(); - if (m & 0x0040) test92g(); - if (m & 0x0080) test92h(); - if (m & 0x0100) test92i(); - if (m & 0x0200) test92j(); - if (m & 0x0400) test92k(); - if (m & 0x0400) test92k(); - if (m & 0x0800) test92l(); - if (m & 0x1000) test92m(); - if (m & 0x2000) test92n(); - if (m & 0x4000) test92o(); - } - - quit(); - /* NOTREACHED */ -} diff --git a/minix/tests/test94.c b/minix/tests/test94.c deleted file mode 100644 index 0587b7692..000000000 --- a/minix/tests/test94.c +++ /dev/null @@ -1,2650 +0,0 @@ -/* Tests for BPF devices (LWIP) - by D.C. van Moolenbroek */ -/* This test needs to be run as root: opening BPF devices is root-only. */ -/* - * We do not attempt to test the BPF filter code here. Such a test is better - * done through standardized tests and with direct use of the filter code. - * The current BPF filter implementation has been run through the FreeBSD - * BPF filter regression tests (from their tools/regression/bpf/bpf_filter), of - * which only the last test (0084 - "Check very long BPF program") failed due - * to our lower and strictly enforced BPF_MAXINSNS value. Future modifications - * of the BPF filter code should be tested against at least that test set. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" - -#define ITERATIONS 2 - -#define LOOPBACK_IFNAME "lo0" - -#define TEST_PORT_A 12345 -#define TEST_PORT_B 12346 - -#define SLEEP_TIME 250000 /* (us) - increases may require code changes */ - -#define NONROOT_USER "bin" /* name of any unprivileged user */ - -#ifdef NO_INET6 -const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; -#endif /* NO_INET6 */ - -static unsigned int got_signal; - -/* - * Signal handler. - */ -static void -test94_signal(int sig) -{ - - if (sig != SIGUSR1) e(0); - - got_signal++; -} - -/* - * Send UDP packets on the given socket 'fd' so as to fill up a BPF store - * buffer of size 'size' exactly. The provided buffer 'buf' may be used for - * packet generation and is at least of 'size' bytes. Return the number of - * packets sent. - */ -static uint32_t -test94_fill_exact(int fd, uint8_t * buf, size_t size, uint32_t seq) -{ - size_t hdrlen, len; - - hdrlen = BPF_WORDALIGN(sizeof(struct bpf_hdr)) + sizeof(struct ip) + - sizeof(struct udphdr) + sizeof(seq); - - for (len = 16; len <= hdrlen; len <<= 1); - if (len > size) e(0); - - hdrlen = BPF_WORDALIGN(hdrlen - sizeof(seq)); - - for (; size > 0; seq++) { - memset(buf, 'Y', len - hdrlen); - if (len - hdrlen > sizeof(seq)) - buf[sizeof(seq)] = 'X'; - buf[len - hdrlen - 1] = 'Z'; - memcpy(buf, &seq, sizeof(seq)); - - if (write(fd, buf, len - hdrlen) != len - hdrlen) e(0); - - size -= len; - } - - return seq; -} - -/* - * Send UDP packets on the given socket 'fd' so as to fill up at least a BPF - * store buffer of size 'size', with at least one more packet being sent. The - * provided buffer 'buf' may be used for packet generation and is at least of - * 'size' bytes. - */ -static void -test94_fill_random(int fd, uint8_t * buf, size_t size) -{ - size_t hdrlen, len; - ssize_t left; - uint32_t seq; - - hdrlen = BPF_WORDALIGN(BPF_WORDALIGN(sizeof(struct bpf_hdr)) + - sizeof(struct ip) + sizeof(struct udphdr)); - - /* Even if we fill the buffer exactly, we send one more packet. */ - for (left = (ssize_t)size, seq = 1; left >= 0; seq++) { - len = hdrlen + sizeof(seq) + lrand48() % (size / 10); - - memset(buf, 'Y', len - hdrlen); - if (len - hdrlen > sizeof(seq)) - buf[sizeof(seq)] = 'X'; - buf[len - hdrlen - 1] = 'Z'; - memcpy(buf, &seq, sizeof(seq)); - - if (write(fd, buf, len - hdrlen) != len - hdrlen) e(0); - - left -= BPF_WORDALIGN(len); - } -} - -/* - * Send a UDP packet with a specific size of 'size' bytes and sequence number - * 'seq' on socket 'fd', using 'buf' as scratch buffer. - */ -static void -test94_add_specific(int fd, uint8_t * buf, size_t size, uint32_t seq) -{ - - size += sizeof(seq); - - memset(buf, 'Y', size); - if (size > sizeof(seq)) - buf[sizeof(seq)] = 'X'; - buf[size - 1] = 'Z'; - memcpy(buf, &seq, sizeof(seq)); - - if (write(fd, buf, size) != size) e(0); -} - -/* - * Send a randomly sized, relatively small UDP packet on the given socket 'fd', - * using sequence number 'seq'. The buffer 'buf' may be used as scratch buffer - * which is at most 'size' bytes--the same size as the total BPF buffer. - */ -static void -test94_add_random(int fd, uint8_t * buf, size_t size, uint32_t seq) -{ - - test94_add_specific(fd, buf, lrand48() % (size / 10), seq); -} - -/* - * Check whether the packet in 'buf' of 'caplen' captured bytes out of - * 'datalen' data bytes is one we sent. If so, return an offset to the packet - * data. If not, return a negative value. - */ -static ssize_t -test94_check_pkt(uint8_t * buf, ssize_t caplen, ssize_t datalen) -{ - struct ip ip; - struct udphdr uh; - - if (caplen < sizeof(ip)) - return -1; - - memcpy(&ip, buf, sizeof(ip)); - - if (ip.ip_v != IPVERSION) - return -1; - if (ip.ip_hl != sizeof(ip) >> 2) - return -1; - if (ip.ip_p != IPPROTO_UDP) - return -1; - - if (caplen - sizeof(ip) < sizeof(uh)) - return -1; - - memcpy(&uh, buf + sizeof(ip), sizeof(uh)); - - if (uh.uh_sport != htons(TEST_PORT_A)) - return -1; - if (uh.uh_dport != htons(TEST_PORT_B)) - return -1; - - if (datalen - sizeof(ip) != ntohs(uh.uh_ulen)) e(0); - - return sizeof(ip) + sizeof(uh); -} - -/* - * Check whether the capture in 'buf' of 'len' bytes looks like a valid set of - * captured packets. The valid packets start from sequence number 'seq'; the - * next expected sequence number is returned. If 'filtered' is set, there - * should be no other packets in the capture; otherwise, other packets are - * ignored. - */ -static uint32_t -test94_check(uint8_t * buf, ssize_t len, uint32_t seq, int filtered, - uint32_t * caplen, uint32_t * datalen) -{ - struct bpf_hdr bh; - ssize_t off; - uint32_t nseq; - - while (len > 0) { - /* - * We rely on the assumption that the last packet in the buffer - * is padded to alignment as well; if not, this check fails. - */ - if (len < BPF_WORDALIGN(sizeof(bh))) e(0); - - memcpy(&bh, buf, sizeof(bh)); - - /* - * The timestamp fields should be filled in. The tests that - * use this function do not set a capture length below the - * packet length. The header must be exactly as large as we - * expect: no small-size tricks (as NetBSD uses) and no - * unexpected extra padding. - */ - if (bh.bh_tstamp.tv_sec == 0 && bh.bh_tstamp.tv_usec == 0) - e(0); - if (caplen != NULL) { - if (bh.bh_caplen != *caplen) e(0); - if (bh.bh_datalen != *datalen) e(0); - - caplen++; - datalen++; - } else - if (bh.bh_datalen != bh.bh_caplen) e(0); - if (bh.bh_hdrlen != BPF_WORDALIGN(sizeof(bh))) e(0); - - if (bh.bh_hdrlen + BPF_WORDALIGN(bh.bh_caplen) > len) e(0); - - buf += bh.bh_hdrlen; - len -= bh.bh_hdrlen; - - if ((off = test94_check_pkt(buf, bh.bh_caplen, - bh.bh_datalen)) < 0) { - if (filtered) e(0); - - buf += BPF_WORDALIGN(bh.bh_caplen); - len -= BPF_WORDALIGN(bh.bh_caplen); - - continue; - } - - if (bh.bh_caplen < off + sizeof(seq)) e(0); - - memcpy(&nseq, &buf[off], sizeof(nseq)); - - if (nseq != seq++) e(0); - - off += sizeof(seq); - if (off < bh.bh_caplen) { - /* If there is just one byte, it is 'Z'. */ - if (off < bh.bh_caplen && off < bh.bh_datalen - 1) { - if (buf[off] != 'X') e(0); - - for (off++; off < bh.bh_caplen && - off < bh.bh_datalen - 1; off++) - if (buf[off] != 'Y') e(0); - } - if (off < bh.bh_caplen && off == bh.bh_datalen - 1 && - buf[off] != 'Z') e(0); - } - - buf += BPF_WORDALIGN(bh.bh_caplen); - len -= BPF_WORDALIGN(bh.bh_caplen); - } - - return seq; -} - -/* - * Filter program to ensure that the given (datalink-headerless) packet is an - * IPv4 UDP packet from port 12345 to port 12346. Important: the 'k' value of - * the last instruction must be the accepted packet size, and is modified by - * some of the tests further down! - */ -static struct bpf_insn test94_filter[] = { - { BPF_LD+BPF_B+BPF_ABS, 0, 0, 0 }, /* is this an IPv4 header? */ - { BPF_ALU+BPF_RSH+BPF_K, 0, 0, 4 }, - { BPF_JMP+BPF_JEQ+BPF_K, 0, 7, 4 }, - { BPF_LD+BPF_B+BPF_ABS, 0, 0, 9 }, /* is this a UDP packet? */ - { BPF_JMP+BPF_JEQ+BPF_K, 0, 5, IPPROTO_UDP }, - { BPF_LDX+BPF_B+BPF_MSH, 0, 0, 0 }, - { BPF_LD+BPF_H+BPF_IND, 0, 0, 0 }, /* source port 12345? */ - { BPF_JMP+BPF_JEQ+BPF_K, 0, 2, TEST_PORT_A }, - { BPF_LD+BPF_H+BPF_IND, 0, 0, 2 }, /* destination port 12346? */ - { BPF_JMP+BPF_JEQ+BPF_K, 1, 0, TEST_PORT_B }, - { BPF_RET+BPF_K, 0, 0, 0 }, /* reject the packet */ - { BPF_RET+BPF_K, 0, 0, (uint32_t)-1 }, /* accept the (whole) packet */ -}; - -/* - * Set up a BPF device, a pair of sockets of which traffic will be captured on - * the BPF device, a buffer for capturing packets, and optionally a filter. - * If the given size is non-zero, use that as buffer size. Return the BPF - * device's actual buffer size, which is also the size of 'buf'. - */ -static size_t -test94_setup(int * fd, int * fd2, int * fd3, uint8_t ** buf, unsigned int size, - int set_filter) -{ - struct sockaddr_in sinA, sinB; - struct ifreq ifr; - struct bpf_program bf; - unsigned int dlt; - - if ((*fd = open(_PATH_BPF, O_RDWR)) < 0) e(0); - - if (size != 0 && ioctl(*fd, BIOCSBLEN, &size) != 0) e(0); - - if (ioctl(*fd, BIOCGBLEN, &size) != 0) e(0); - if (size < 1024 || size > BPF_MAXBUFSIZE) e(0); - - if ((*buf = malloc(size)) == NULL) e(0); - - if (set_filter) { - /* - * Install a filter to improve predictability for the tests. - */ - memset(&bf, 0, sizeof(bf)); - bf.bf_len = __arraycount(test94_filter); - bf.bf_insns = test94_filter; - if (ioctl(*fd, BIOCSETF, &bf) != 0) e(0); - } - - /* Bind to the loopback device. */ - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, LOOPBACK_IFNAME, sizeof(ifr.ifr_name)); - if (ioctl(*fd, BIOCSETIF, &ifr) != 0) e(0); - - /* - * If the loopback device's data link type is not DLT_RAW, our filter - * and size calculations will not work. - */ - if (ioctl(*fd, BIOCGDLT, &dlt) != 0) e(0); - if (dlt != DLT_RAW) e(0); - - /* We use UDP traffic for our test packets. */ - if ((*fd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); - - memset(&sinA, 0, sizeof(sinA)); - sinA.sin_family = AF_INET; - sinA.sin_port = htons(TEST_PORT_A); - sinA.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (bind(*fd2, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); - - memcpy(&sinB, &sinA, sizeof(sinB)); - sinB.sin_port = htons(TEST_PORT_B); - if (connect(*fd2, (struct sockaddr *)&sinB, sizeof(sinB)) != 0) e(0); - - if ((*fd3 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); - - if (bind(*fd3, (struct sockaddr *)&sinB, sizeof(sinB)) != 0) e(0); - - if (connect(*fd3, (struct sockaddr *)&sinA, sizeof(sinA)) != 0) e(0); - - return size; -} - -/* - * Clean up resources allocated by test94_setup(). - */ -static void -test94_cleanup(int fd, int fd2, int fd3, uint8_t * buf) -{ - - if (close(fd3) != 0) e(0); - - if (close(fd2) != 0) e(0); - - free(buf); - - if (close(fd) != 0) e(0); -} - -/* - * Test reading packets from a BPF device, using regular mode. - */ -static void -test94a(void) -{ - struct bpf_program bf; - struct timeval tv; - fd_set fds; - uint8_t *buf; - pid_t pid; - size_t size; - ssize_t len; - uint32_t seq; - int fd, fd2, fd3, status, bytes, fl; - - subtest = 1; - - size = test94_setup(&fd, &fd2, &fd3, &buf, 0 /*size*/, - 0 /*set_filter*/); - - /* - * Test that a filled-up store buffer will be returned to a pending - * read call. Perform this first test without a filter, to ensure that - * the default behavior is to accept all packets. The side effect is - * that we may receive other loopback traffic as part of our capture. - */ - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_fill_random(fd2, buf, size); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - len = read(fd, buf, size); - - if (len < size * 3/4) e(0); - if (len > size) e(0); - test94_check(buf, len, 1 /*seq*/, 0 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* Only the exact buffer size may be used in read calls. */ - if (read(fd, buf, size - 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (read(fd, buf, size + 1) != -1) e(0); - if (errno != EINVAL) e(0); - if (read(fd, buf, sizeof(struct bpf_hdr)) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * Install a filter to improve predictability for the remaining tests. - */ - memset(&bf, 0, sizeof(bf)); - bf.bf_len = __arraycount(test94_filter); - bf.bf_insns = test94_filter; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - /* - * Next we want to test that an already filled-up buffer will be - * returned to a read call immediately. We take the opportunity to - * test that filling the buffer will also wake up a blocked select - * call. In addition, we test ioctl(FIONREAD). - */ - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - if (bytes != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_fill_random(fd2, buf, size); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - len = read(fd, buf, size); - - if (len < size * 3/4) e(0); - if (len > size) e(0); - seq = test94_check(buf, len, 1 /*seq*/, 1 /*filtered*/, - NULL /*caplen*/, NULL /*datalen*/); - - if (len != bytes) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* There is one more packet in the store buffer at this point. */ - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - if (FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - if (bytes != 0) e(0); - - /* - * Next, we test whether read timeouts work, first checking that a - * timed-out read call returns any packets currently in the buffer. - * We use sleep and a signal as a crude way to test that the call was - * actually blocked until the timeout occurred. - */ - got_signal = 0; - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - signal(SIGUSR1, test94_signal); - - usleep(SLEEP_TIME); - - test94_add_random(fd2, buf, size, seq + 1); - - usleep(SLEEP_TIME); - - if (got_signal != 0) e(0); - pause(); - if (got_signal != 1) e(0); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - tv.tv_sec = 0; - tv.tv_usec = SLEEP_TIME * 3; - if (ioctl(fd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* two packets < 3/4 of the size */ - if (test94_check(buf, len, seq, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 2) e(0); - - if (kill(pid, SIGUSR1) != 0) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * Next, see if a timed-out read will all buffers empty yields EAGAIN. - */ - tv.tv_sec = 0; - tv.tv_usec = SLEEP_TIME; - if (ioctl(fd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EAGAIN) e(0); - - /* - * Verify that resetting the timeout to zero makes the call block - * forever (for short test values of "forever" anyway), because - * otherwise this may create a false illusion of correctness in the - * next test, for non-blocking calls. As a side effect, this tests - * read call signal interruption, and ensures no partial results are - * returned in that case. - */ - tv.tv_sec = 0; - tv.tv_usec = 0; - if (ioctl(fd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - signal(SIGUSR1, test94_signal); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EINTR) e(0); - - if (got_signal != 1) e(0); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - usleep(SLEEP_TIME * 2); - - if (kill(pid, SIGUSR1) != 0) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * Repeat the same test with a non-full, non-empty buffer, to ensure - * that interrupted reads do not return partial results. - */ - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - signal(SIGUSR1, test94_signal); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EINTR) e(0); - - if (got_signal != 1) e(0); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - usleep(SLEEP_TIME); - - test94_add_random(fd2, buf, size, 2); - - usleep(SLEEP_TIME); - - if (kill(pid, SIGUSR1) != 0) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * Test non-blocking reads with empty, full, and non-empty buffers. - * Against common sense, the last case should return whatever is in - * the buffer rather than EAGAIN, like immediate-mode reads would. - */ - if ((fl = fcntl(fd, F_GETFL)) == -1) e(0); - if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - seq = test94_check(buf, len, 2 /*seq*/, 1 /*filtered*/, - NULL /*caplen*/, NULL /*datalen*/); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EAGAIN) e(0); - - test94_fill_random(fd2, buf, size); - - len = read(fd, buf, size); - if (len < size * 3/4) e(0); - if (len > size) e(0); - seq = test94_check(buf, len, 1 /*seq*/, 1 /*filtered*/, - NULL /*caplen*/, NULL /*datalen*/); - - len = read(fd, buf, size); - - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - if (test94_check(buf, len, seq, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 1) e(0); - - if (fcntl(fd, F_SETFL, fl) != 0) e(0); - - /* - * Test two remaining aspects of select(2): single-packet arrivals do - * not cause a wake-up, and the read timer has no effect. The latter - * is a deliberate implementation choice where we diverge from NetBSD, - * because it requires keeping state in a way that violates the - * principle of system call independence. - */ - tv.tv_sec = 0; - tv.tv_usec = SLEEP_TIME * 2; - if (ioctl(fd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_add_random(fd2, buf, size, 1); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - tv.tv_sec = 1; - tv.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - test94_cleanup(fd, fd2, fd3, buf); -} - -/* - * Test reading packets from a BPF device, using immediate mode. - */ -static void -test94b(void) -{ - struct timeval tv; - fd_set fds; - uint8_t *buf; - unsigned int val; - size_t size; - ssize_t len; - uint32_t seq; - pid_t pid; - int fd, fd2, fd3, bytes, status, fl; - - subtest = 2; - - size = test94_setup(&fd, &fd2, &fd3, &buf, 0 /*size*/, - 1 /*set_filter*/); - - val = 1; - if (ioctl(fd, BIOCIMMEDIATE, &val) != 0) e(0); - - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - if (bytes != 0) e(0); - - /* - * Ensure that if the hold buffer is full, an immediate-mode read - * returns the content of the hold buffer, even if the store buffer is - * not empty. - */ - test94_fill_random(fd2, buf, size); - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - - len = read(fd, buf, size); - if (len < size * 3/4) e(0); - if (len > size) e(0); - seq = test94_check(buf, len, 1 /*seq*/, 1 /*filtered*/, - NULL /*caplen*/, NULL /*datalen*/); - - if (len != bytes) e(0); - - /* - * There is one packet left in the buffer. In immediate mode, this - * packet should be returned immediately. - */ - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - if (test94_check(buf, len, seq, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 1) e(0); - - if (len != bytes) e(0); - - /* The buffer is now empty again. */ - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 0) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - if (bytes != 0) e(0); - - /* - * Immediate-mode reads may return multiple packets from the store - * buffer. - */ - test94_add_random(fd2, buf, size, seq + 1); - test94_add_random(fd2, buf, size, seq + 2); - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, &tv) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* two packets < 3/4 of the size */ - if (test94_check(buf, len, seq + 1, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 3) e(0); - - if (len != bytes) e(0); - - /* - * Now test waking up suspended calls, read(2) first. - */ - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_add_random(fd2, buf, size, seq + 3); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - if (test94_check(buf, len, seq + 3, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 4) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * Then select(2). - */ - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_add_random(fd2, buf, size, seq + 4); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - if (test94_check(buf, len, seq + 4, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 5) e(0); - - if (len != bytes) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * Non-blocking reads should behave just as with regular mode. - */ - if ((fl = fcntl(fd, F_GETFL)) == -1) e(0); - if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EAGAIN) e(0); - - test94_fill_random(fd2, buf, size); - - len = read(fd, buf, size); - if (len < size * 3/4) e(0); - if (len > size) e(0); - seq = test94_check(buf, len, 1 /*seq*/, 1 /*filtered*/, - NULL /*caplen*/, NULL /*datalen*/); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - if (test94_check(buf, len, seq, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq + 1) e(0); - - if (fcntl(fd, F_SETFL, fl) != 0) e(0); - - /* - * Timeouts should work with immediate mode. - */ - tv.tv_sec = 0; - tv.tv_usec = SLEEP_TIME; - if (ioctl(fd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EAGAIN) e(0); - - test94_cleanup(fd, fd2, fd3, buf); -} - -/* - * Test reading packets from a BPF device, with an exactly filled buffer. The - * idea is that normally the store buffer is considered "full" if the next - * packet does not fit in it, but if no more bytes are left in it, it can be - * rotated immediately. This is a practically useless edge case, but we - * support it, so we might as well test it. Also, some of the code for this - * case is shared with other rare cases that we cannot test here (interfaces - * disappearing, to be specific), and exactly filling up the buffers does test - * some other bounds checks so all that might make this worth it anyway. While - * we are exercising full control over our buffers, also check statistics. - */ -static void -test94c(void) -{ - struct bpf_stat bs; - fd_set fds; - uint8_t *buf; - size_t size; - pid_t pid; - uint32_t count, seq; - int fd, fd2, fd3, bytes, status, fl; - - subtest = 3; - - size = test94_setup(&fd, &fd2, &fd3, &buf, 0 /*size*/, - 1 /*set_filter*/); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != 0) e(0); - if (bs.bs_drop != 0) e(0); - - /* - * Test read, select, and ioctl(FIONREAD) on an exactly filled buffer. - */ - count = test94_fill_exact(fd2, buf, size, 0); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != count) e(0); - if (bs.bs_recv < bs.bs_capt) e(0); /* may be more */ - if (bs.bs_drop != 0) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - if (bytes != size) e(0); - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if (read(fd, buf, size) != size) e(0); - test94_check(buf, size, 0 /*seq*/, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/); - - /* - * If the store buffer is full, the buffers should be swapped after - * emptying the hold buffer. - */ - seq = test94_fill_exact(fd2, buf, size, 1); - test94_fill_exact(fd2, buf, size, seq); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != count * 3) e(0); - if (bs.bs_recv < bs.bs_capt) e(0); /* may be more */ - if (bs.bs_drop != 0) e(0); - - test94_add_random(fd2, buf, size, 0); /* this one will get dropped */ - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != count * 3 + 1) e(0); - if (bs.bs_recv < bs.bs_capt) e(0); /* may be more */ - if (bs.bs_drop != 1) e(0); - - test94_add_random(fd2, buf, size, 0); /* this one will get dropped */ - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != count * 3 + 2) e(0); - if (bs.bs_recv < bs.bs_capt) e(0); /* may be more */ - if (bs.bs_drop != 2) e(0); - - if (ioctl(fd, FIONREAD, &bytes) != 0) e(0); - if (bytes != size) e(0); - - if (read(fd, buf, size) != size) e(0); - if (test94_check(buf, size, 1 /*seq*/, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != seq) e(0); - - if (read(fd, buf, size) != size) e(0); - if (test94_check(buf, size, seq, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != count * 2 + 1) e(0); - - /* - * See if an exactly filled buffer resumes reads... - */ - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_fill_exact(fd2, buf, size, 1); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - if (read(fd, buf, size) != size) e(0); - test94_check(buf, size, 1 /*seq*/, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - /* - * ...and selects. - */ - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - usleep(SLEEP_TIME); - - test94_fill_exact(fd2, buf, size, seq); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, NULL, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - if ((fl = fcntl(fd, F_GETFL)) == -1) e(0); - if (fcntl(fd, F_SETFL, fl | O_NONBLOCK) != 0) e(0); - - if (read(fd, buf, size) != size) e(0); - test94_check(buf, size, seq, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EAGAIN) e(0); - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != count * 5 + 2) e(0); - if (bs.bs_recv < bs.bs_capt) e(0); /* may be more */ - if (bs.bs_drop != 2) e(0); - - test94_cleanup(fd, fd2, fd3, buf); -} - -/* - * Test receipt of large packets on BPF devices. Large packets should be - * truncated to the size of the buffer, but unless the filter specifies a - * smaller capture size, no more than that. - */ -static void -test94d(void) -{ - struct bpf_hdr bh; - uint8_t *buf, *buf2; - size_t size; - ssize_t len; - int fd, fd2, fd3, datalen; - - subtest = 4; - - /* - * Specify a size smaller than the largest packet we can send on the - * loopback device. The size we specify here is currently the default - * size already anyway, but that might change in the future. - */ - size = test94_setup(&fd, &fd2, &fd3, &buf, 32768 /*size*/, - 1 /*set_filter*/); - if (size != 32768) e(0); - - datalen = 65000; - if (setsockopt(fd2, SOL_SOCKET, SO_SNDBUF, &datalen, - sizeof(datalen)) != 0) e(0); - - if ((buf2 = malloc(datalen)) == NULL) e(0); - - memset(buf2, 'Y', datalen); - buf2[0] = 'X'; - buf2[size - sizeof(struct udphdr) - sizeof(struct ip) - - BPF_WORDALIGN(sizeof(bh)) - 1] = 'Z'; - - if (write(fd2, buf2, datalen) != datalen) e(0); - - if (read(fd, buf, size) != size) e(0); - - memcpy(&bh, buf, sizeof(bh)); - - if (bh.bh_hdrlen != BPF_WORDALIGN(sizeof(bh))) e(0); - if (bh.bh_caplen != size - BPF_WORDALIGN(sizeof(bh))) e(0); - if (bh.bh_datalen != - sizeof(struct ip) + sizeof(struct udphdr) + datalen) e(0); - - if (buf[BPF_WORDALIGN(sizeof(bh)) + sizeof(struct ip) + - sizeof(struct udphdr)] != 'X') e(0); - if (buf[size - 2] != 'Y') e(0); - if (buf[size - 1] != 'Z') e(0); - - /* - * Add a smaller packet in between, to ensure that 1) the large packet - * is not split across buffers, and 2) the packet is truncated to the - * size of the buffer, not the available part of the buffer. Note how - * forced rotation and our exact-fill policy preclude us from having to - * use immediate mode for any of this. - */ - test94_add_random(fd2, buf, size, 1 /*seq*/); - - if (write(fd2, buf2, datalen) != datalen) e(0); - - len = read(fd, buf, size); - if (len <= 0) e(0); - if (len >= size * 3/4) e(0); /* one packet < 3/4 of the size */ - if (test94_check(buf, len, 1 /*seq*/, 1 /*filtered*/, NULL /*caplen*/, - NULL /*datalen*/) != 2) e(0); - - if (read(fd, buf, size) != size) e(0); - - memcpy(&bh, buf, sizeof(bh)); - - if (bh.bh_hdrlen != BPF_WORDALIGN(sizeof(bh))) e(0); - if (bh.bh_caplen != size - BPF_WORDALIGN(sizeof(bh))) e(0); - if (bh.bh_datalen != - sizeof(struct ip) + sizeof(struct udphdr) + datalen) e(0); - - if (buf[BPF_WORDALIGN(sizeof(bh)) + sizeof(struct ip) + - sizeof(struct udphdr)] != 'X') e(0); - if (buf[size - 2] != 'Y') e(0); - if (buf[size - 1] != 'Z') e(0); - - free(buf2); - - test94_cleanup(fd, fd2, fd3, buf); -} - -/* - * Test whether our filter is active through two-way communication and a - * subsequent check on the BPF statistics. We do not actually look through the - * captured packets, because who knows what else is active on the loopback - * device (e.g., X11) and the extra code specifically to extract our packets in - * the other direction is simply not worth it. - */ -static void -test94_comm(int fd, int fd2, int fd3, int filtered) -{ - struct bpf_stat bs; - char c; - - if (write(fd2, "A", 1) != 1) e(0); - - if (read(fd3, &c, 1) != 1) e(0); - if (c != 'A') e(0); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv == 0) e(0); - if (bs.bs_capt == 0) e(0); - - if (ioctl(fd, BIOCFLUSH) != 0) e(0); - - if (write(fd3, "B", 1) != 1) e(0); - - if (read(fd2, &c, 1) != 1) e(0); - if (c != 'B') e(0); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv == 0) e(0); - - if (filtered) { - if (bs.bs_capt != 0) e(0); - if (bs.bs_drop != 0) e(0); - } else - if (bs.bs_capt == 0) e(0); - - if (ioctl(fd, BIOCFLUSH) != 0) e(0); -} - -/* - * Test filter installation and mechanics. - */ -static void -test94e(void) -{ - struct bpf_program bf; - struct bpf_stat bs; - struct bpf_hdr bh; - uint8_t *buf; - size_t size, len, plen, alen, off; - uint32_t seq, caplen[4], datalen[4]; - int i, fd, fd2, fd3, val; - - subtest = 5; - - /* - * We have already tested installing a filter both before and after - * attaching to an interface by now, so we do not repeat that here. - */ - size = test94_setup(&fd, &fd2, &fd3, &buf, 0 /*size*/, - 0 /*set_filter*/); - - val = 1; - if (ioctl(fd, BIOCIMMEDIATE, &val) != 0) e(0); - - /* - * A filter that is too large is rejected. Unfortunately, due to - * necessary IOCTL rewriting, this tests libc, not the service. - */ - memset(&bf, 0, sizeof(bf)); - bf.bf_len = BPF_MAXINSNS + 1; - bf.bf_insns = NULL; - if (ioctl(fd, BIOCSETF, &bf) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * An invalid filter is rejected. In this test case, the truncated - * filter has a jump target beyond the end of the filter program. - */ - memset(&bf, 0, sizeof(bf)); - bf.bf_len = __arraycount(test94_filter) - 1; - bf.bf_insns = test94_filter; - if (ioctl(fd, BIOCSETF, &bf) != -1) e(0); - if (errno != EINVAL) e(0); - - test94_comm(fd, fd2, fd3, 0 /*filtered*/); - - bf.bf_len++; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_comm(fd, fd2, fd3, 1 /*filtered*/); - - /* - * Installing a zero-length filter clears the current filter, if any. - */ - memset(&bf, 0, sizeof(bf)); - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_comm(fd, fd2, fd3, 0 /*filtered*/); - - /* Test this twice to trip over unconditional filter deallocation. */ - memset(&bf, 0, sizeof(bf)); - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_comm(fd, fd2, fd3, 0 /*filtered*/); - - /* - * Test both aligned and unaligned capture sizes. For each, test - * sizes larger than, equal to, and smaller than the capture size. - * In both cases, aggregate the packets into a single buffer and only - * then go through them, to see whether alignment was done correctly. - * We cannot do everything in one go as BIOCSETF implies a BIOCFLUSH. - */ - plen = sizeof(struct ip) + sizeof(struct udphdr) + sizeof(seq); - if (BPF_WORDALIGN(plen) != plen) e(0); - alen = BPF_WORDALIGN(plen + 1); - if (alen - 2 <= plen + 1) e(0); - - /* First the aligned cases. */ - test94_filter[__arraycount(test94_filter) - 1].k = alen; - - memset(&bf, 0, sizeof(bf)); - bf.bf_len = __arraycount(test94_filter); - bf.bf_insns = test94_filter; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_comm(fd, fd2, fd3, 1 /*filtered*/); - - test94_add_specific(fd2, buf, alen + 1 - plen, 1); - caplen[0] = alen; - datalen[0] = alen + 1; - - test94_add_specific(fd2, buf, alen - plen, 2); - caplen[1] = alen; - datalen[1] = alen; - - test94_add_specific(fd2, buf, alen + 3 - plen, 3); - caplen[2] = alen; - datalen[2] = alen + 3; - - test94_add_specific(fd2, buf, alen - 1 - plen, 4); - caplen[3] = alen - 1; - datalen[3] = alen - 1; - - memset(buf, 0, size); - - len = read(fd, buf, size); - - if (test94_check(buf, len, 1 /*seq*/, 1 /*filtered*/, caplen, - datalen) != 5) e(0); - - /* Then the unaligned cases. */ - test94_filter[__arraycount(test94_filter) - 1].k = alen + 1; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_add_specific(fd2, buf, alen + 2 - plen, 5); - caplen[0] = alen + 1; - datalen[0] = alen + 2; - - test94_add_specific(fd2, buf, alen + 1 - plen, 6); - caplen[1] = alen + 1; - datalen[1] = alen + 1; - - test94_add_specific(fd2, buf, alen + 9 - plen, 7); - caplen[2] = alen + 1; - datalen[2] = alen + 9; - - test94_add_specific(fd2, buf, alen - plen, 8); - caplen[3] = alen; - datalen[3] = alen; - - memset(buf, 0, size); - - len = read(fd, buf, size); - - if (test94_check(buf, len, 5 /*seq*/, 1 /*filtered*/, caplen, - datalen) != 9) e(0); - - /* - * Check that capturing only one byte from packets is possible. Not - * that that would be particularly useful. - */ - test94_filter[__arraycount(test94_filter) - 1].k = 1; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_add_random(fd2, buf, size, 9); - test94_add_random(fd2, buf, size, 10); - test94_add_random(fd2, buf, size, 11); - - memset(buf, 0, size); - - len = read(fd, buf, size); - if (len <= 0) e(0); - - off = 0; - for (i = 0; i < 3; i++) { - if (len - off < sizeof(bh)) e(0); - memcpy(&bh, &buf[off], sizeof(bh)); - - if (bh.bh_tstamp.tv_sec == 0 && bh.bh_tstamp.tv_usec == 0) - e(0); - if (bh.bh_caplen != 1) e(0); - if (bh.bh_datalen < plen) e(0); - if (bh.bh_hdrlen != BPF_WORDALIGN(sizeof(bh))) e(0); - - off += bh.bh_hdrlen; - - if (buf[off] != 0x45) e(0); - - off += BPF_WORDALIGN(bh.bh_caplen); - } - if (off != len) e(0); - - /* - * Finally, a zero capture size should result in rejected packets only. - */ - test94_filter[__arraycount(test94_filter) - 1].k = 0; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - test94_add_random(fd2, buf, size, 12); - test94_add_random(fd2, buf, size, 13); - test94_add_random(fd2, buf, size, 14); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv < 3) e(0); - if (bs.bs_capt != 0) e(0); - if (bs.bs_drop != 0) e(0); - - /* Restore the capture limit of the filter to its original state. */ - test94_filter[__arraycount(test94_filter) - 1].k = (uint32_t)-1; - - test94_cleanup(fd, fd2, fd3, buf); -} - -/* - * Compute an IP checksum. - */ -static uint16_t -test94_cksum(uint8_t * buf, size_t len) -{ - uint32_t sum, word; - - /* This is a really dumb implementation but *shrug*. */ - for (sum = 0; len > 0; sum += word) { - if (len > 1) { - word = buf[0] << 8 | buf[1]; - buf += 2; - len -= 2; - } else { - word = buf[0] << 8; - len--; - } - } - - while (sum > UINT16_MAX) - sum = (sum & UINT16_MAX) + (sum >> 16); - - return ~(uint16_t)sum; -} - -/* - * Set up UDP headers for a packet. The packet uses IPv4 unless 'v6' is set, - * in which case IPv6 is used. The given buffer must be large enough to - * contain the headers and the (to be appended) data. The function returns the - * offset into the buffer to the data portion of the packet. - */ -static size_t -test94_make_pkt(uint8_t * buf, size_t len, int v6) -{ - struct ip ip; - struct ip6_hdr ip6; - struct udphdr uh; - size_t off; - - if (!v6) { - memset(&ip, 0, sizeof(ip)); - ip.ip_v = IPVERSION; - ip.ip_hl = sizeof(ip) >> 2; - ip.ip_len = htons(sizeof(ip) + sizeof(uh) + len); - ip.ip_ttl = 255; - ip.ip_p = IPPROTO_UDP; - ip.ip_sum = 0; - ip.ip_src.s_addr = htonl(INADDR_LOOPBACK); - ip.ip_dst.s_addr = htonl(INADDR_LOOPBACK); - - memcpy(buf, &ip, sizeof(ip)); - ip.ip_sum = htons(test94_cksum(buf, sizeof(ip))); - memcpy(buf, &ip, sizeof(ip)); - if (test94_cksum(buf, sizeof(ip)) != 0) e(0); - - off = sizeof(ip); - } else { - memset(&ip6, 0, sizeof(ip6)); - ip6.ip6_vfc = IPV6_VERSION; - ip6.ip6_plen = htons(sizeof(uh) + len); - ip6.ip6_nxt = IPPROTO_UDP; - ip6.ip6_hlim = 255; - memcpy(&ip6.ip6_src, &in6addr_loopback, sizeof(ip6.ip6_src)); - memcpy(&ip6.ip6_dst, &in6addr_loopback, sizeof(ip6.ip6_dst)); - - memcpy(buf, &ip6, sizeof(ip6)); - - off = sizeof(ip6); - } - - memset(&uh, 0, sizeof(uh)); - uh.uh_sport = htons(TEST_PORT_A); - uh.uh_dport = htons(TEST_PORT_B); - uh.uh_ulen = htons(sizeof(uh) + len); - uh.uh_sum = 0; /* lazy but we also don't have the data yet */ - - memcpy(buf + off, &uh, sizeof(uh)); - - return off + sizeof(uh); -} - -/* - * Test sending packets by writing to a BPF device. - */ -static void -test94f(void) -{ - struct bpf_stat bs; - struct ifreq ifr; - fd_set fds; - uint8_t *buf; - size_t off; - unsigned int i, uval, mtu; - int fd, fd2, fd3; - - subtest = 6; - - (void)test94_setup(&fd, &fd2, &fd3, &buf, 0 /*size*/, - 1 /*set_filter*/); - - /* - * Select queries should always indicate that the device is writable. - */ - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, NULL, &fds, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - /* - * Test packet size limits. For loopback devices, the maximum data - * link layer level maximum transmission unit should be 65535-4 = - * 65531 bytes. Obtain the actual value anyway; it might have changed. - */ - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, LOOPBACK_IFNAME, sizeof(ifr.ifr_name)); - - if (ioctl(fd2, SIOCGIFMTU, &ifr) != 0) e(0); - mtu = ifr.ifr_mtu; - - if ((buf = realloc(buf, UINT16_MAX + 1)) == NULL) e(0); - - memset(buf, 0, UINT16_MAX + 1); - - for (i = UINT16_MAX + 1; i > mtu; i--) { - if (write(fd, buf, i) != -1) e(0); - if (errno != EMSGSIZE) e(0); - } - - /* This packet will be discarded as completely crap. That's fine. */ - if (write(fd, buf, mtu) != mtu) e(0); - - /* - * Zero-sized writes are accepted but do not do anything. - */ - if (write(fd, buf, 0) != 0) e(0); - - /* - * Send an actual packet, and see if it arrives. - */ - off = test94_make_pkt(buf, 6, 0 /*v6*/); - memcpy(buf + off, "Hello!", 6); - - if (write(fd, buf, off + 6) != off + 6) e(0); - - memset(buf, 0, mtu); - if (read(fd3, buf, mtu) != 6) e(0); - if (memcmp(buf, "Hello!", 6) != 0) e(0); - - /* - * Enable feedback mode to test that the packet now arrives twice. - * Send a somewhat larger packet to test that data copy-in handles - * offsets correctly. - */ - uval = 1; - if (ioctl(fd, BIOCSFEEDBACK, &uval) != 0) e(0); - - off = test94_make_pkt(buf, 12345, 0 /*v6*/); - for (i = 0; i < 12345; i++) - buf[off + i] = 1 + (i % 251); /* the largest prime < 255 */ - - if (write(fd, buf, off + 12345) != off + 12345) e(0); - - /* We need a default UDP SO_RCVBUF >= 12345 * 2 for this. */ - memset(buf, 0, UINT16_MAX); - if (recv(fd3, buf, UINT16_MAX, 0) != 12345) e(0); - for (i = 0; i < 12345; i++) - if (buf[i] != 1 + (i % 251)) e(0); - - memset(buf, 0, UINT16_MAX); - if (recv(fd3, buf, UINT16_MAX, MSG_DONTWAIT) != 12345) e(0); - for (i = 0; i < 12345; i++) - if (buf[i] != 1 + (i % 251)) e(0); - - if (recv(fd3, buf, UINT16_MAX, MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - /* - * The two valid packets we sent will have been captured by our BPF - * device as well, because SEESENT is enabled by default and also - * applies to packets written to a BPF device. The reason for that is - * that it allows tcpdump(8) to see what DHCP clients are sending, for - * example. The packets we sent are accepted by the installed filter. - */ - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_capt != 2) e(0); - - /* Now that we've written data, test select once more. */ - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, NULL, &fds, NULL, NULL) != 1) e(0); - if (!FD_ISSET(fd, &fds)) e(0); - - test94_cleanup(fd, fd2, fd3, buf); -} - -/* - * Test read, write, and select operations on unconfigured devices. - */ -static void -test94g(void) -{ - fd_set rfds, wfds; - uint8_t *buf; - unsigned int size; - int fd; - - subtest = 7; - - if ((fd = open(_PATH_BPF, O_RDWR)) < 0) e(0); - - if (ioctl(fd, BIOCGBLEN, &size) != 0) e(0); - if (size < 1024 || size > BPF_MAXBUFSIZE) e(0); - - if ((buf = malloc(size)) == NULL) e(0); - - if (read(fd, buf, size) != -1) e(0); - if (errno != EINVAL) e(0); - - if (write(fd, buf, size) != -1) e(0); - if (errno != EINVAL) e(0); - - FD_ZERO(&rfds); - FD_SET(fd, &rfds); - FD_ZERO(&wfds); - FD_SET(fd, &wfds); - - if (select(fd + 1, &rfds, &wfds, NULL, NULL) != 2) e(0); - - if (!FD_ISSET(fd, &rfds)) e(0); - if (!FD_ISSET(fd, &wfds)) e(0); - - free(buf); - - if (close(fd) != 0) e(0); -} - -/* - * Test various IOCTL calls. Several of these tests are rather superficial, - * because we would need a real interface, rather than the loopback device, to - * test their functionality properly. Also note that we skip various checks - * performed as part of the earlier subtests. - */ -static void -test94h(void) -{ - struct bpf_stat bs; - struct bpf_version bv; - struct bpf_dltlist bfl; - struct ifreq ifr; - struct timeval tv; - uint8_t *buf; - size_t size; - unsigned int uval, list[2]; - int cfd, ufd, fd2, fd3, val; - - subtest = 8; - - /* - * Many IOCTLs work only on configured or only on unconfigured BPF - * devices, so for convenience we create a file descriptor for each. - */ - size = test94_setup(&cfd, &fd2, &fd3, &buf, 0 /*size*/, - 1 /*set_filter*/); - - if ((ufd = open(_PATH_BPF, O_RDWR)) < 0) e(0); - - /* - * The BIOCSBLEN value is silently corrected to fall within a valid - * range, and BIOCGBLEN can be used to obtain the corrected value. We - * do not know the valid range, so we use fairly extreme test values. - */ - uval = 1; - if (ioctl(ufd, BIOCSBLEN, &uval) != 0) e(0); - - if (ioctl(ufd, BIOCGBLEN, &uval) != 0) e(0); - if (uval < sizeof(struct bpf_hdr) || uval > BPF_MAXBUFSIZE) e(0); - - uval = (unsigned int)-1; - if (ioctl(ufd, BIOCSBLEN, &uval) != 0) e(0); - - if (ioctl(ufd, BIOCGBLEN, &uval) != 0) e(0); - if (uval < sizeof(struct bpf_hdr) || uval > BPF_MAXBUFSIZE) e(0); - - uval = 0; - if (ioctl(ufd, BIOCSBLEN, &uval) != 0) e(0); - - if (ioctl(ufd, BIOCGBLEN, &uval) != 0) e(0); - if (uval < sizeof(struct bpf_hdr) || uval > BPF_MAXBUFSIZE) e(0); - - uval = 1024; /* ..a value that should be acceptable but small */ - if (ioctl(ufd, BIOCSBLEN, &uval) != 0) e(0); - if (ioctl(ufd, BIOCGBLEN, &uval) != 0) e(0); - if (uval != 1024) e(0); - - /* - * For configured devices, it is not possible to adjust the buffer size - * but it is possible to obtain its size. - */ - if (ioctl(cfd, BIOCSBLEN, &uval) != -1) e(0); - if (errno != EINVAL) e(0); - - if (ioctl(cfd, BIOCGBLEN, &uval) != 0) e(0); - if (uval != size) e(0); - - /* - * BIOCFLUSH resets both buffer contents and statistics. - */ - uval = 1; - if (ioctl(cfd, BIOCIMMEDIATE, &uval) != 0) e(0); - - test94_fill_exact(fd2, buf, size, 1 /*seq*/); - test94_fill_exact(fd2, buf, size, 1 /*seq*/); - test94_fill_exact(fd2, buf, size, 1 /*seq*/); - - if (ioctl(cfd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv == 0) e(0); - if (bs.bs_drop == 0) e(0); - if (bs.bs_capt == 0) e(0); - - /* Do make sure that statistics are not cleared on retrieval.. */ - if (ioctl(cfd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv == 0) e(0); - if (bs.bs_drop == 0) e(0); - if (bs.bs_capt == 0) e(0); - - if (ioctl(cfd, FIONREAD, &val) != 0) e(0); - if (val == 0) e(0); - - if (ioctl(cfd, BIOCFLUSH) != 0) e(0); - - /* There is a race condition for bs_recv here, so we cannot test it. */ - if (ioctl(cfd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_drop != 0) e(0); - if (bs.bs_capt != 0) e(0); - - if (ioctl(cfd, FIONREAD, &val) != 0) e(0); - if (val != 0) e(0); - - /* - * Although practically useless, BIOCFLUSH works on unconfigured - * devices. So does BIOCGSTATS. - */ - if (ioctl(ufd, BIOCFLUSH) != 0) e(0); - - if (ioctl(ufd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv != 0) e(0); - if (bs.bs_drop != 0) e(0); - if (bs.bs_capt != 0) e(0); - - /* - * BIOCPROMISC works on configured devices only. On loopback devices - * it has no observable effect though. - */ - if (ioctl(ufd, BIOCPROMISC) != -1) e(0); - if (errno != EINVAL) e(0); - - if (ioctl(cfd, BIOCPROMISC) != 0) e(0); - - /* - * BIOCGDLT does not work on unconfigured devices. - */ - if (ioctl(ufd, BIOCGDLT, &uval) != -1) e(0); - if (errno != EINVAL) e(0); - - /* - * BIOCGETIF works only on configured devices, where it returns the - * associated device name. - */ - if (ioctl(ufd, BIOCGETIF, &ifr) != -1) e(0); - if (errno != EINVAL) e(0); - - memset(&ifr, 'X', sizeof(ifr)); - if (ioctl(cfd, BIOCGETIF, &ifr) != 0) e(0); - if (strcmp(ifr.ifr_name, LOOPBACK_IFNAME) != 0) e(0); - - /* - * BIOCSETIF works only on unconfigured devices, and accepts only valid - * valid interface names. The name is forced to be null terminated. - */ - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, LOOPBACK_IFNAME, sizeof(ifr.ifr_name)); - if (ioctl(cfd, BIOCSETIF, &ifr) != -1) e(0); - if (errno != EINVAL) e(0); - - memset(&ifr, 0, sizeof(ifr)); - memset(ifr.ifr_name, 'x', sizeof(ifr.ifr_name)); - if (ioctl(ufd, BIOCSETIF, &ifr) != -1) e(0); - if (errno != ENXIO) e(0); - - /* Anyone that has ten loopback devices is simply insane. */ - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, LOOPBACK_IFNAME, sizeof(ifr.ifr_name)); - ifr.ifr_name[strlen(ifr.ifr_name) - 1] += 9; - if (ioctl(ufd, BIOCSETIF, &ifr) != -1) e(0); - if (errno != ENXIO) e(0); - - /* - * It is possible to turn BIOCIMMEDIATE on and off. We already enabled - * it a bit higher up. Note that our implementation does not support - * toggling the setting while a read call is no progress, and toggling - * the setting will have no effect while a select call is in progress; - * similar restrictions apply to effectively all relevant settings. - * Either way we do not test that here either. - */ - test94_add_random(fd2, buf, size, 1 /*seq*/); - - if (ioctl(cfd, FIONREAD, &val) != 0) e(0); - if (val == 0) e(0); - - uval = 0; - if (ioctl(cfd, BIOCIMMEDIATE, &uval) != 0) e(0); - - if (ioctl(cfd, FIONREAD, &val) != 0) e(0); - if (val != 0) e(0); - - uval = 1; - if (ioctl(cfd, BIOCIMMEDIATE, &uval) != 0) e(0); - - if (ioctl(cfd, FIONREAD, &val) != 0) e(0); - if (val == 0) e(0); - - if (ioctl(cfd, BIOCFLUSH) != 0) e(0); - - /* - * BIOCIMMEDIATE also works on unconfigured devices. - */ - uval = 1; - if (ioctl(ufd, BIOCIMMEDIATE, &uval) != 0) e(0); - - uval = 0; - if (ioctl(ufd, BIOCIMMEDIATE, &uval) != 0) e(0); - - /* - * BIOCVERSION should return the current BPF interface version. - */ - if (ioctl(ufd, BIOCVERSION, &bv) != 0) e(0); - if (bv.bv_major != BPF_MAJOR_VERSION) e(0); - if (bv.bv_minor != BPF_MINOR_VERSION) e(0); - - /* - * BIOCSHDRCMPLT makes sense only for devices with data link headers, - * which rules out loopback devices. Check the default and test - * toggling it, and stop there. - */ - /* The default value is off. */ - uval = 1; - if (ioctl(ufd, BIOCGHDRCMPLT, &uval) != 0) e(0); - if (uval != 0) e(0); - - uval = 2; - if (ioctl(ufd, BIOCSHDRCMPLT, &uval) != 0) e(0); - - if (ioctl(ufd, BIOCGHDRCMPLT, &uval) != 0) e(0); - if (uval != 1) e(0); - - uval = 0; - if (ioctl(ufd, BIOCSHDRCMPLT, &uval) != 0) e(0); - - uval = 1; - if (ioctl(ufd, BIOCGHDRCMPLT, &uval) != 0) e(0); - if (uval != 0) e(0); - - /* - * BIOCSDLT works on configured devices. For loopback devices, it can - * only set the data link type to its current value, which on MINIX3 - * for loopback devices is DLT_RAW (i.e., no headers at all). - */ - uval = DLT_RAW; - if (ioctl(ufd, BIOCSDLT, &uval) != -1) e(0); - if (errno != EINVAL) e(0); - - uval = DLT_RAW; - if (ioctl(cfd, BIOCSDLT, &uval) != 0) e(0); - - uval = DLT_NULL; - if (ioctl(cfd, BIOCSDLT, &uval) != -1) e(0); - if (errno != EINVAL) e(0); - - if (ioctl(cfd, BIOCGDLT, &uval) != 0) e(0); - if (uval != DLT_RAW) e(0); - - /* - * BIOCGDLTLIST works on configured devices only, and may be used to - * both query the size of the list and obtain the list. On MINIX3, - * loopback devices will only ever return DLT_RAW. Unfortunately, - * much of the handling for this IOCTL is in libc for us, which is also - * why we do not test bad pointers and stuff like that. - */ - memset(&bfl, 0, sizeof(bfl)); - if (ioctl(ufd, BIOCGDLTLIST, &bfl) != -1) e(0); - if (errno != EINVAL) e(0); - - memset(&bfl, 0, sizeof(bfl)); - if (ioctl(cfd, BIOCGDLTLIST, &bfl) != 0) e(0); - if (bfl.bfl_len != 1) e(0); - if (bfl.bfl_list != NULL) e(0); - - memset(&bfl, 0, sizeof(bfl)); - bfl.bfl_len = 2; /* should be ignored */ - if (ioctl(cfd, BIOCGDLTLIST, &bfl) != 0) e(0); - if (bfl.bfl_len != 1) e(0); - if (bfl.bfl_list != NULL) e(0); - - memset(&bfl, 0, sizeof(bfl)); - memset(list, 0, sizeof(list)); - bfl.bfl_list = list; - if (ioctl(cfd, BIOCGDLTLIST, &bfl) != -1) e(0); - if (errno != ENOMEM) e(0); - if (list[0] != 0) e(0); - - memset(&bfl, 0, sizeof(bfl)); - bfl.bfl_len = 1; - bfl.bfl_list = list; - if (ioctl(cfd, BIOCGDLTLIST, &bfl) != 0) e(0); - if (bfl.bfl_len != 1) e(0); - if (bfl.bfl_list != list) e(0); - if (list[0] != DLT_RAW) e(0); - if (list[1] != 0) e(0); - - memset(&bfl, 0, sizeof(bfl)); - memset(list, 0, sizeof(list)); - bfl.bfl_len = 2; - bfl.bfl_list = list; - if (ioctl(cfd, BIOCGDLTLIST, &bfl) != 0) e(0); - if (bfl.bfl_len != 1) e(0); - if (bfl.bfl_list != list) e(0); - if (list[0] != DLT_RAW) e(0); - if (list[1] != 0) e(0); - - /* - * For loopback devices, BIOCSSEESENT is a bit weird: packets are - * captured on output to get a complete view of loopback traffic, and - * not also on input because that would then duplicate the traffic. As - * a result, turning off BIOCSSEESENT for a loopback device means that - * no packets will be captured at all anymore. First test the default - * and toggling on the unconfigured device, then reproduce the above on - * the configured device. - */ - /* The default value is on. */ - uval = 0; - if (ioctl(ufd, BIOCGSEESENT, &uval) != 0) e(0); - if (uval != 1) e(0); - - uval = 0; - if (ioctl(ufd, BIOCSSEESENT, &uval) != 0) e(0); - - uval = 1; - if (ioctl(ufd, BIOCGSEESENT, &uval) != 0) e(0); - if (uval != 0) e(0); - - uval = 2; - if (ioctl(ufd, BIOCSSEESENT, &uval) != 0) e(0); - - if (ioctl(ufd, BIOCGSEESENT, &uval) != 0) e(0); - if (uval != 1) e(0); - - if (ioctl(cfd, BIOCGSEESENT, &uval) != 0) e(0); - if (uval != 1) e(0); - - uval = 0; - if (ioctl(cfd, BIOCSSEESENT, &uval) != 0) e(0); - - if (ioctl(cfd, BIOCFLUSH) != 0) e(0); - - test94_add_random(fd2, buf, size, 1 /*seq*/); - - if (ioctl(cfd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv != 0) e(0); - - uval = 1; - if (ioctl(cfd, BIOCSSEESENT, &uval) != 0) e(0); - - if (ioctl(cfd, BIOCFLUSH) != 0) e(0); - - test94_add_random(fd2, buf, size, 1 /*seq*/); - - if (ioctl(cfd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv == 0) e(0); - - /* - * The BIOCSRTIMEOUT values are rounded up to clock granularity. - * Invalid timeout values are rejected. - */ - /* The default value is zero. */ - tv.tv_sec = 99; - if (ioctl(ufd, BIOCGRTIMEOUT, &tv) != 0) e(0); - if (tv.tv_sec != 0) e(0); - if (tv.tv_usec != 0) e(0); - - tv.tv_usec = 1000000; - if (ioctl(ufd, BIOCSRTIMEOUT, &tv) != -1) e(0); - if (errno != EINVAL) e(0); - - tv.tv_usec = -1; - if (ioctl(ufd, BIOCSRTIMEOUT, &tv) != -1) e(0); - if (errno != EINVAL) e(0); - - tv.tv_sec = -1; - tv.tv_usec = 0; - if (ioctl(ufd, BIOCSRTIMEOUT, &tv) != -1) e(0); - if (errno != EINVAL) e(0); - - tv.tv_sec = INT_MAX; - if (ioctl(ufd, BIOCSRTIMEOUT, &tv) != -1) e(0); - if (errno != EDOM) e(0); - - if (ioctl(ufd, BIOCGRTIMEOUT, &tv) != 0) e(0); - if (tv.tv_sec != 0) e(0); - if (tv.tv_usec != 0) e(0); - - tv.tv_sec = 123; - tv.tv_usec = 1; - if (ioctl(ufd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - if (ioctl(ufd, BIOCGRTIMEOUT, &tv) != 0) e(0); - if (tv.tv_sec != 123) e(0); - if (tv.tv_usec == 0) e(0); /* rounding should be up */ - - tv.tv_sec = 0; - tv.tv_usec = 0; - if (ioctl(ufd, BIOCSRTIMEOUT, &tv) != 0) e(0); - - if (ioctl(ufd, BIOCGRTIMEOUT, &tv) != 0) e(0); - if (tv.tv_sec != 0) e(0); - if (tv.tv_usec != 0) e(0); - - /* - * BIOCSFEEDBACK is another weird setting for which we only test - * default and toggling here. - */ - /* The default value is off. */ - uval = 1; - if (ioctl(ufd, BIOCGFEEDBACK, &uval) != 0) e(0); - if (uval != 0) e(0); - - uval = 2; - if (ioctl(ufd, BIOCSFEEDBACK, &uval) != 0) e(0); - - if (ioctl(ufd, BIOCGFEEDBACK, &uval) != 0) e(0); - if (uval != 1) e(0); - - uval = 0; - if (ioctl(ufd, BIOCSFEEDBACK, &uval) != 0) e(0); - - uval = 1; - if (ioctl(ufd, BIOCGFEEDBACK, &uval) != 0) e(0); - if (uval != 0) e(0); - - /* Clean up. */ - if (close(ufd) != 0) e(0); - - test94_cleanup(cfd, fd2, fd3, buf); -} - -/* IPv6 version of our filter. */ -static struct bpf_insn test94_filter6[] = { - { BPF_LD+BPF_B+BPF_ABS, 0, 0, 0 }, /* is this an IPv6 header? */ - { BPF_ALU+BPF_RSH+BPF_K, 0, 0, 4 }, - { BPF_JMP+BPF_JEQ+BPF_K, 0, 6, 6 }, - { BPF_LD+BPF_B+BPF_ABS, 0, 0, 6 }, /* is this a UDP packet? */ - { BPF_JMP+BPF_JEQ+BPF_K, 0, 4, IPPROTO_UDP }, - { BPF_LD+BPF_H+BPF_ABS, 0, 0, 40 }, /* source port 12345? */ - { BPF_JMP+BPF_JEQ+BPF_K, 0, 2, TEST_PORT_A }, - { BPF_LD+BPF_H+BPF_ABS, 0, 0, 42 }, /* destination port 12346? */ - { BPF_JMP+BPF_JEQ+BPF_K, 1, 0, TEST_PORT_B }, - { BPF_RET+BPF_K, 0, 0, 0 }, /* reject the packet */ - { BPF_RET+BPF_K, 0, 0, (uint32_t)-1 }, /* accept the (whole) packet */ -}; - -/* - * Test receipt of IPv6 packets, because it was getting a bit messy to - * integrate that into the previous subtests. We just want to make sure that - * IPv6 packets are properly filtered and captured at all. The rest of the - * code is entirely version agnostic anyway. - */ -static void -test94i(void) -{ - struct sockaddr_in6 sin6A, sin6B; - struct bpf_program bf; - struct bpf_stat bs; - struct bpf_hdr bh; - struct ifreq ifr; - struct ip6_hdr ip6; - struct udphdr uh; - uint8_t *buf, c; - socklen_t socklen; - ssize_t len; - size_t off; - unsigned int uval, size, dlt; - int fd, fd2, fd3; - - subtest = 9; - - if ((fd = open(_PATH_BPF, O_RDWR)) < 0) e(0); - - if (ioctl(fd, BIOCGBLEN, &size) != 0) e(0); - if (size < 1024 || size > BPF_MAXBUFSIZE) e(0); - - if ((buf = malloc(size)) == NULL) e(0); - - /* Install the filter. */ - memset(&bf, 0, sizeof(bf)); - bf.bf_len = __arraycount(test94_filter6); - bf.bf_insns = test94_filter6; - if (ioctl(fd, BIOCSETF, &bf) != 0) e(0); - - uval = 1; - if (ioctl(fd, BIOCIMMEDIATE, &uval) != 0) e(0); - - /* Bind to the loopback device. */ - memset(&ifr, 0, sizeof(ifr)); - strlcpy(ifr.ifr_name, LOOPBACK_IFNAME, sizeof(ifr.ifr_name)); - if (ioctl(fd, BIOCSETIF, &ifr) != 0) e(0); - - /* - * If the loopback device's data link type is not DLT_RAW, our filter - * and size calculations will not work. - */ - if (ioctl(fd, BIOCGDLT, &dlt) != 0) e(0); - if (dlt != DLT_RAW) e(0); - - /* We use UDP traffic for our test packets. */ - if ((fd2 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); - - memset(&sin6A, 0, sizeof(sin6A)); - sin6A.sin6_family = AF_INET6; - sin6A.sin6_port = htons(TEST_PORT_A); - memcpy(&sin6A.sin6_addr, &in6addr_loopback, sizeof(sin6A.sin6_addr)); - if (bind(fd2, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); - - memcpy(&sin6B, &sin6A, sizeof(sin6B)); - sin6B.sin6_port = htons(TEST_PORT_B); - if (connect(fd2, (struct sockaddr *)&sin6B, sizeof(sin6B)) != 0) e(0); - - if ((fd3 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); - - if (bind(fd3, (struct sockaddr *)&sin6B, sizeof(sin6B)) != 0) e(0); - - if (connect(fd3, (struct sockaddr *)&sin6A, sizeof(sin6A)) != 0) e(0); - - if (write(fd2, "A", 1) != 1) e(0); - - if (read(fd3, &c, 1) != 1) e(0); - if (c != 'A') e(0); - - if (write(fd3, "B", 1) != 1) e(0); - - if (read(fd2, &c, 1) != 1) e(0); - if (c != 'B') e(0); - - if (ioctl(fd, BIOCGSTATS, &bs) != 0) e(0); - if (bs.bs_recv < 2) e(0); - if (bs.bs_capt != 1) e(0); - if (bs.bs_drop != 0) e(0); - - memset(buf, 0, size); - - len = read(fd, buf, size); - - if (len != BPF_WORDALIGN(sizeof(bh)) + - BPF_WORDALIGN(sizeof(ip6) + sizeof(uh) + 1)) e(0); - - memcpy(&bh, buf, sizeof(bh)); - - if (bh.bh_tstamp.tv_sec == 0 && bh.bh_tstamp.tv_usec == 0) e(0); - if (bh.bh_caplen != sizeof(ip6) + sizeof(uh) + 1) e(0); - if (bh.bh_datalen != bh.bh_caplen) e(0); - if (bh.bh_hdrlen != BPF_WORDALIGN(sizeof(bh))) e(0); - - if (buf[bh.bh_hdrlen + sizeof(ip6) + sizeof(uh)] != 'A') e(0); - - /* - * Finally, do a quick test to see if we can send IPv6 packets by - * writing to the BPF device. We rely on such packets being generated - * properly in a later test. - */ - off = test94_make_pkt(buf, 6, 1 /*v6*/); - memcpy(buf + off, "Hello!", 6); - - if (write(fd, buf, off + 6) != off + 6) e(0); - - socklen = sizeof(sin6A); - if (recvfrom(fd3, buf, size, 0, (struct sockaddr *)&sin6A, - &socklen) != 6) e(0); - - if (memcmp(buf, "Hello!", 6) != 0) e(0); - if (socklen != sizeof(sin6A)) e(0); - if (sin6A.sin6_family != AF_INET6) e(0); - if (sin6A.sin6_port != htons(TEST_PORT_A)) e(0); - if (memcmp(&sin6A.sin6_addr, &in6addr_loopback, - sizeof(sin6A.sin6_addr)) != 0) e(0); - - free(buf); - - if (close(fd3) != 0) e(0); - - if (close(fd2) != 0) e(0); - - if (close(fd) != 0) e(0); -} - -/* - * Test the BPF sysctl(7) interface at a basic level. - */ -static void -test94j(void) -{ - struct bpf_stat bs1, bs2; - struct bpf_d_ext *bde; - uint8_t *buf; - unsigned int slot, count, uval; - size_t len, oldlen, size, bdesize; - int fd, fd2, fd3, val, mib[5], smib[3], found; - - subtest = 10; - - /* - * Obtain the maximum buffer size. The value must be sane. - */ - memset(mib, 0, sizeof(mib)); - len = __arraycount(mib); - if (sysctlnametomib("net.bpf.maxbufsize", mib, &len) != 0) e(0); - if (len != 3) e(0); - - oldlen = sizeof(val); - if (sysctl(mib, len, &val, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(val)) e(0); - - if (val < 1024 || val > INT_MAX / 2) e(0); - - /* - * Attempt to set the maximum buffer size. This is not (yet) supported - * so for now we want to make sure that it really does not work. - */ - if (sysctl(mib, len, NULL, NULL, &val, sizeof(val)) != -1) e(0); - if (errno != EPERM) e(0); - - /* - * Obtain global statistics. We check the actual statistics later on. - */ - memset(smib, 0, sizeof(smib)); - len = __arraycount(smib); - if (sysctlnametomib("net.bpf.stats", smib, &len) != 0) e(0); - if (len != 3) e(0); - - oldlen = sizeof(bs1); - if (sysctl(smib, len, &bs1, &oldlen, NULL, 0) != 0) e(0); - if (oldlen != sizeof(bs1)) e(0); - - /* - * Set up a BPF descriptor, and retrieve the list of BPF peers. We - * should be able to find our BPF peer. - */ - memset(mib, 0, sizeof(mib)); - len = __arraycount(mib); - if (sysctlnametomib("net.bpf.peers", mib, &len) != 0) e(0); - if (len != 3) e(0); - mib[len++] = sizeof(*bde); /* size of each element */ - mib[len++] = INT_MAX; /* limit on elements to return */ - - size = test94_setup(&fd, &fd2, &fd3, &buf, 0 /*size*/, - 1 /*set_filter*/); - - /* Generate some traffic to bump the statistics. */ - count = test94_fill_exact(fd2, buf, size, 0); - test94_fill_exact(fd2, buf, size, 0); - test94_fill_exact(fd2, buf, size, 0); - - if (write(fd3, "X", 1) != 1) e(0); - - if (sysctl(mib, len, NULL, &oldlen, NULL, 0) != 0) e(0); - if (oldlen == 0) e(0); - - /* Add some slack space ourselves to prevent problems with churn. */ - bdesize = oldlen + sizeof(*bde) * 8; - if ((bde = malloc(bdesize)) == NULL) e(0); - - oldlen = bdesize; - if (sysctl(mib, len, bde, &oldlen, NULL, 0) != 0) e(0); - if (oldlen % sizeof(*bde)) e(0); - - found = 0; - for (slot = 0; slot < oldlen / sizeof(*bde); slot++) { - if (bde[slot].bde_pid != getpid()) - continue; - - if (bde[slot].bde_bufsize != size) e(0); - if (bde[slot].bde_promisc != 0) e(0); - if (bde[slot].bde_state != BPF_IDLE) e(0); - if (bde[slot].bde_immediate != 0) e(0); - if (bde[slot].bde_hdrcmplt != 0) e(0); - if (bde[slot].bde_seesent != 1) e(0); - if (bde[slot].bde_rcount < count * 3 + 1) e(0); - if (bde[slot].bde_dcount != count) e(0); - if (bde[slot].bde_ccount != count * 3) e(0); - if (strcmp(bde[slot].bde_ifname, LOOPBACK_IFNAME) != 0) e(0); - - found++; - } - if (found != 1) e(0); - - /* - * If global statistics are an accumulation of individual devices' - * statistics (they currently are not) then such a scheme should take - * into account device flushes. - */ - if (ioctl(fd, BIOCFLUSH) != 0) e(0); - - test94_cleanup(fd, fd2, fd3, buf); - - /* - * Now see if the global statistics have indeed changed correctly. - */ - oldlen = sizeof(bs2); - if (sysctl(smib, __arraycount(smib), &bs2, &oldlen, NULL, 0) != 0) - e(0); - if (oldlen != sizeof(bs2)) e(0); - - if (bs2.bs_recv < bs1.bs_recv + count * 3 + 1) e(0); - if (bs2.bs_drop != bs1.bs_drop + count) e(0); - if (bs2.bs_capt != bs1.bs_capt + count * 3) e(0); - - /* - * Check an unconfigured BPF device as well. - */ - if ((fd = open(_PATH_BPF, O_RDWR)) < 0) e(0); - - /* - * Toggle some flags. It is too much effort to test them all - * individually (which, in the light of copy-paste mistakes, would be - * the right thing to do) but at least we'll know something gets set. - */ - uval = 1; - if (ioctl(fd, BIOCIMMEDIATE, &uval) != 0) e(0); - if (ioctl(fd, BIOCSHDRCMPLT, &uval) != 0) e(0); - - uval = 0; - if (ioctl(fd, BIOCSSEESENT, &uval) != 0) e(0); - - oldlen = bdesize; - if (sysctl(mib, len, bde, &oldlen, NULL, 0) != 0) e(0); - if (oldlen % sizeof(*bde)) e(0); - - found = 0; - for (slot = 0; slot < oldlen / sizeof(*bde); slot++) { - if (bde[slot].bde_pid != getpid()) - continue; - - if (bde[slot].bde_bufsize != size) e(0); - if (bde[slot].bde_promisc != 0) e(0); - if (bde[slot].bde_state != BPF_IDLE) e(0); - if (bde[slot].bde_immediate != 1) e(0); - if (bde[slot].bde_hdrcmplt != 1) e(0); - if (bde[slot].bde_seesent != 0) e(0); - if (bde[slot].bde_rcount != 0) e(0); - if (bde[slot].bde_dcount != 0) e(0); - if (bde[slot].bde_ccount != 0) e(0); - if (bde[slot].bde_ifname[0] != '\0') e(0); - - found++; - } - if (found != 1) e(0); - - close(fd); - - /* - * At this point there should be no BPF device left for our PID. - */ - oldlen = bdesize; - if (sysctl(mib, len, bde, &oldlen, NULL, 0) != 0) e(0); - if (oldlen % sizeof(*bde)) e(0); - - for (slot = 0; slot < oldlen / sizeof(*bde); slot++) - if (bde[slot].bde_pid == getpid()) e(0); - found++; - - free(bde); -} - -/* - * Test privileged operations as an unprivileged caller. - */ -static void -test94k(void) -{ - struct passwd *pw; - pid_t pid; - size_t len, oldlen; - int mib[5], status; - - subtest = 11; - - pid = fork(); - switch (pid) { - case 0: - errct = 0; - - if ((pw = getpwnam(NONROOT_USER)) == NULL) e(0); - - if (setuid(pw->pw_uid) != 0) e(0); - - /* - * Opening /dev/bpf must fail. Note that this is a system - * configuration issue rather than a LWIP service issue. - */ - if (open(_PATH_BPF, O_RDWR) != -1) e(0); - if (errno != EACCES) e(0); - - /* - * Retrieving the net.bpf.peers list must fail, too. - */ - memset(mib, 0, sizeof(mib)); - len = __arraycount(mib); - if (sysctlnametomib("net.bpf.peers", mib, &len) != 0) e(0); - if (len != 3) e(0); - mib[len++] = sizeof(struct bpf_d_ext); - mib[len++] = INT_MAX; - - if (sysctl(mib, len, NULL, &oldlen, NULL, 0) != -1) e(0); - if (errno != EPERM) e(0); - - exit(errct); - case -1: - e(0); - - break; - default: - break; - } - - if (wait(&status) != pid) e(0); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) e(0); -} - -/* - * Test that traffic directed to loopback addresses be dropped on non-loopback - * interfaces. In particular, inbound traffic to 127.0.0.1 and ::1 should not - * be accepted on any interface that does not own those addresses. This test - * is here because BPF feedback mode is (currently) the only way in which we - * can generate inbound traffic the ethernet level, and even then only as a - * side effect of sending outbound traffic. That is: this test sends the same - * test packets to the local network! As such it must be performed only when - * USENETWORK=yes and therefore at the user's risk. - */ -static void -test94l(void) -{ - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - struct sockaddr_dl sdl; - struct ifreq ifr; - struct ifaddrs *ifa, *ifp; - struct if_data *ifdata; - uint8_t buf[sizeof(struct ether_header) + MAX(sizeof(struct ip), - sizeof(struct ip6_hdr)) + sizeof(struct udphdr) + 6]; - struct ether_header ether; - const uint8_t ether_src[ETHER_ADDR_LEN] = - { 0x02, 0x00, 0x01, 0x12, 0x34, 0x56 }; - unsigned int val; - size_t off; - int bfd, sfd; - - subtest = 12; - - if (!get_setting_use_network()) - return; - - memset(&ifr, 0, sizeof(ifr)); - memset(ðer, 0, sizeof(ether)); - - /* - * Start by finding a suitable ethernet interface that is up and of - * which the link is not down. Without one, we cannot perform this - * test. Save the interface name and the ethernet address. - */ - if (getifaddrs(&ifa) != 0) e(0); - - for (ifp = ifa; ifp != NULL; ifp = ifp->ifa_next) { - if (!(ifp->ifa_flags & IFF_UP) || ifp->ifa_addr == NULL || - ifp->ifa_addr->sa_family != AF_LINK) - continue; - - ifdata = (struct if_data *)ifp->ifa_data; - if (ifdata != NULL && ifdata->ifi_type == IFT_ETHER && - ifdata->ifi_link_state != LINK_STATE_DOWN) { - strlcpy(ifr.ifr_name, ifp->ifa_name, - sizeof(ifr.ifr_name)); - - memcpy(&sdl, (struct sockaddr_dl *)ifp->ifa_addr, - offsetof(struct sockaddr_dl, sdl_data)); - if (sdl.sdl_alen != sizeof(ether.ether_dhost)) e(0); - memcpy(ether.ether_dhost, - ((struct sockaddr_dl *)ifp->ifa_addr)->sdl_data + - sdl.sdl_nlen, sdl.sdl_alen); - break; - } - } - - freeifaddrs(ifa); - - if (ifp == NULL) - return; - - /* Open a BPF device and bind it to the ethernet interface we found. */ - if ((bfd = open(_PATH_BPF, O_RDWR)) < 0) e(0); - - if (ioctl(bfd, BIOCSETIF, &ifr) != 0) e(0); - - if (ioctl(bfd, BIOCGDLT, &val) != 0) e(0); - if (val != DLT_EN10MB) e(0); - - val = 1; - if (ioctl(bfd, BIOCSFEEDBACK, &val) != 0) e(0); - - /* We use UDP traffic for our test packets, IPv4 first. */ - if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) e(0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(TEST_PORT_B); - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) != 0) e(0); - - /* - * Construct and send a packet. We already filled in the ethernet - * destination address. Put in a source address that is locally - * administered but valid (and as such no reason for packet rejection). - */ - memcpy(ether.ether_shost, ether_src, sizeof(ether.ether_shost)); - ether.ether_type = htons(ETHERTYPE_IP); - - memcpy(buf, ðer, sizeof(ether)); - off = sizeof(ether); - off += test94_make_pkt(buf + off, 6, 0 /*v6*/); - if (off + 6 > sizeof(buf)) e(0); - memcpy(buf + off, "Hello!", 6); - - if (write(bfd, buf, off + 6) != off + 6) e(0); - - /* The packet MUST NOT arrive. */ - if (recv(sfd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (close(sfd) != 0) e(0); - - /* Try the same thing, but now with an IPv6 packet. */ - if ((sfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) e(0); - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(TEST_PORT_B); - memcpy(&sin6.sin6_addr, &in6addr_loopback, sizeof(sin6.sin6_addr)); - if (bind(sfd, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) e(0); - - ether.ether_type = htons(ETHERTYPE_IPV6); - - memcpy(buf, ðer, sizeof(ether)); - off = sizeof(ether); - off += test94_make_pkt(buf + off, 6, 1 /*v6*/); - if (off + 6 > sizeof(buf)) e(0); - memcpy(buf + off, "Hello!", 6); - - if (write(bfd, buf, off + 6) != off + 6) e(0); - - if (recv(sfd, buf, sizeof(buf), MSG_DONTWAIT) != -1) e(0); - if (errno != EWOULDBLOCK) e(0); - - if (close(sfd) != 0) e(0); - if (close(bfd) != 0) e(0); -} - -/* - * Test program for LWIP BPF. - */ -int -main(int argc, char ** argv) -{ - int i, m; - - start(94); - - srand48(time(NULL)); - - if (argc == 2) - m = atoi(argv[1]); - else - m = 0xFFF; - - for (i = 0; i < ITERATIONS; i++) { - if (m & 0x001) test94a(); - if (m & 0x002) test94b(); - if (m & 0x004) test94c(); - if (m & 0x008) test94d(); - if (m & 0x010) test94e(); - if (m & 0x020) test94f(); - if (m & 0x040) test94g(); - if (m & 0x080) test94h(); - if (m & 0x100) test94i(); - if (m & 0x200) test94j(); - if (m & 0x400) test94k(); - if (m & 0x800) test94l(); - } - - quit(); - /* NOTREACHED */ -} diff --git a/minix/usr.bin/trace/ioctl.c b/minix/usr.bin/trace/ioctl.c deleted file mode 100644 index 70f2479e4..000000000 --- a/minix/usr.bin/trace/ioctl.c +++ /dev/null @@ -1,232 +0,0 @@ - -#include "inc.h" - -#include - -static char ioctlbuf[IOCPARM_MASK]; - -static const struct { - const char *(*name)(unsigned long); - int (*arg)(struct trace_proc *, unsigned long, void *, int); - int is_svrctl; -} ioctl_table[] = { - { block_ioctl_name, block_ioctl_arg, FALSE }, - { char_ioctl_name, char_ioctl_arg, FALSE }, - { net_ioctl_name, net_ioctl_arg, FALSE }, - { svrctl_name, svrctl_arg, TRUE }, -}; - -/* - * Print an IOCTL request code, and save certain values in the corresponding - * process structure in order to be able to print the IOCTL argument. - */ -void -put_ioctl_req(struct trace_proc * proc, const char * name, unsigned long req, - int is_svrctl) -{ - const char *text; - size_t size; - unsigned int i, group, cmd; - int r, w, big; - - proc->ioctl_index = -1; - - if (valuesonly > 1) { - put_value(proc, name, "0x%lx", req); - - return; - } - - /* - * Lookups are bruteforce across the IOCTL submodules; they're all - * checked. We could use the group letter but that would create more - * issues than it solves. Our hope is that at least the compiler is - * smart about looking up particular codes in each switch statement, - * although in the worst case, it's a full O(n) lookup. - */ - for (i = 0; i < COUNT(ioctl_table); i++) { - /* IOCTLs and SVRCTLs are considered different name spaces. */ - if (ioctl_table[i].is_svrctl != is_svrctl) - continue; - - if ((text = ioctl_table[i].name(req)) != NULL) { - proc->ioctl_index = i; - - if (valuesonly) - break; - - put_field(proc, name, text); - - return; - } - } - - r = _MINIX_IOCTL_IOR(req); - w = _MINIX_IOCTL_IOW(req); - big = _MINIX_IOCTL_BIG(req); - size = (size_t)(big ? _MINIX_IOCTL_SIZE_BIG(req) : IOCPARM_LEN(req)); - group = big ? 0 : IOCGROUP(req); - cmd = req & 0xff; /* shockingly there is no macro for this.. */ - - /* - * Not sure why an entire bit is wasted on IOC_VOID (legacy reasons?), - * but since the redundancy is there, we might as well check whether - * this is a valid IOCTL request. Also, we expect the group to be a - * printable character. If either check fails, print just a number. - */ - if (((req & IOC_VOID) && (r || w || big || size > 0)) || - (!(req & IOC_VOID) && ((!r && !w) || size == 0)) || - (!big && (group < 32 || group > 127))) { - put_value(proc, name, "0x%lx", req); - - return; - } - - if (big) { - /* For big IOCTLs, "R" becomes before "W" (old MINIX style). */ - put_value(proc, name, "_IO%s%s_BIG(%u,%zu)", - r ? "R" : "", w ? "W" : "", cmd, size); - } else if (IOCGROUP(req) >= 32 && IOCGROUP(req) < 127) { - /* For normal IOCTLs, "W" comes before "R" (NetBSD style). */ - put_value(proc, name, "_IO%s%s('%c',%u,%zu)", - w ? "W" : "", r ? "R" : "", group, cmd, size); - } -} - -/* - * Print the supplied (out) part of an IOCTL argument, as applicable. For - * efficiency reasons, this function assumes that put_ioctl_req() has been - * called for the corresponding IOCTL already, so that the necessary fields in - * the given proc structure are set as expected. - */ -int -put_ioctl_arg_out(struct trace_proc * proc, const char * name, - unsigned long req, vir_bytes addr, int is_svrctl) -{ - size_t size; - int dir, all; - - dir = (_MINIX_IOCTL_IOW(req) ? IF_OUT : 0) | - (_MINIX_IOCTL_IOR(req) ? IF_IN : 0); - - if (dir == 0) { - proc->ioctl_index = -1; /* no argument to print at all */ - - return CT_DONE; - } - - /* No support for printing big-IOCTL contents just yet. */ - if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) || - proc->ioctl_index == -1) { - put_ptr(proc, name, addr); - - return CT_DONE; - } - - assert(proc->ioctl_index >= 0); - assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table)); - assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl); - - proc->ioctl_flags = - ioctl_table[proc->ioctl_index].arg(proc, req, NULL, dir); - - if (proc->ioctl_flags == 0) { /* no argument printing for this IOCTL */ - put_ptr(proc, name, addr); - - proc->ioctl_index = -1; /* forget about the IOCTL handler */ - - return CT_DONE; - } - - /* - * If this triggers, the IOCTL handler returns a direction that is not - * part of the actual IOCTL, and the handler should be fixed. - */ - if (proc->ioctl_flags & ~dir) { - output_flush(); /* show the IOCTL name for debugging */ - - assert(0); - } - - if (!(proc->ioctl_flags & IF_OUT)) - return CT_NOTDONE; - - size = IOCPARM_LEN(req); - - if (size > sizeof(ioctlbuf) || - mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) { - put_ptr(proc, name, addr); - - /* There's no harm in trying the _in side later anyhow.. */ - return CT_DONE; - } - - put_open(proc, name, 0, "{", ", "); - - all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_OUT); - - if (!all) - put_field(proc, NULL, ".."); - - put_close(proc, "}"); - - return CT_DONE; -} - -/* - * Print the returned (in) part of an IOCTL argument, as applicable. This - * function assumes that it is preceded by a call to put_ioctl_arg_out for this - * process. - */ -void -put_ioctl_arg_in(struct trace_proc * proc, const char * name, int failed, - unsigned long req, vir_bytes addr, int is_svrctl) -{ - size_t size; - int all; - - if (valuesonly > 1 || _MINIX_IOCTL_BIG(req) || - proc->ioctl_index == -1) { - put_result(proc); - - return; - } - - assert(proc->ioctl_index >= 0); - assert((unsigned int)proc->ioctl_index < COUNT(ioctl_table)); - assert(ioctl_table[proc->ioctl_index].is_svrctl == is_svrctl); - assert(proc->ioctl_flags != 0); - - if (proc->ioctl_flags & IF_OUT) - put_result(proc); - if (!(proc->ioctl_flags & IF_IN)) - return; - - size = IOCPARM_LEN(req); - - if (failed || size > sizeof(ioctlbuf) || - mem_get_data(proc->pid, addr, ioctlbuf, size) < 0) { - if (!(proc->ioctl_flags & IF_OUT)) { - put_ptr(proc, name, addr); - put_equals(proc); - put_result(proc); - } else if (!failed) - put_field(proc, NULL, "{..}"); - - return; - } - - put_open(proc, name, 0, "{", ", "); - - all = ioctl_table[proc->ioctl_index].arg(proc, req, ioctlbuf, IF_IN); - - if (!all) - put_field(proc, NULL, ".."); - - put_close(proc, "}"); - - if (!(proc->ioctl_flags & IF_OUT)) { - put_equals(proc); - put_result(proc); - } -} diff --git a/minix/usr.bin/trace/service/mib.c b/minix/usr.bin/trace/service/mib.c deleted file mode 100644 index 9734a0e03..000000000 --- a/minix/usr.bin/trace/service/mib.c +++ /dev/null @@ -1,1090 +0,0 @@ - -#include "inc.h" - -#include -#include -#include -#include - -struct sysctl_tab { - int id; - size_t size; - const struct sysctl_tab *tab; - int (*proc)(struct trace_proc *, const char *, int, const void *, - vir_bytes, size_t); -}; -#define NODE(i,t) { .id = i, .size = __arraycount(t), .tab = t } -#define PROC(i,s,p) { .id = i, .size = s, .proc = p } - -/* - * Print CTL_KERN KERN_CLOCKRATE. - */ -static int -put_kern_clockrate(struct trace_proc * proc, const char * name, - int type __unused, const void * ptr, vir_bytes addr __unused, - size_t size __unused) -{ - const struct clockinfo *ci; - - ci = (const struct clockinfo *)ptr; - - put_value(proc, "hz", "%d", ci->hz); - put_value(proc, "tick", "%d", ci->tick); - if (verbose > 0) { - put_value(proc, "tickadj", "%d", ci->tickadj); - put_value(proc, "stathz", "%d", ci->stathz); - put_value(proc, "profhz", "%d", ci->profhz); - return TRUE; - } else - return FALSE; -} - -/* - * Print CTL_KERN KERN_PROC2. - */ -static int -put_kern_proc2(struct trace_proc * proc, const char * name, int type, - const void * ptr, vir_bytes addr, size_t size) -{ - const int *mib; - const char *text; - unsigned int i; - - if (type == ST_NAME) { - mib = (const int *)ptr; - - for (i = 0; i < size; i++) { - text = NULL; - - if (i == 0) { - switch (mib[i]) { - case KERN_PROC_ALL: text = ""; break; - case KERN_PROC_PID: text = ""; break; - case KERN_PROC_PGRP: text = ""; break; - case KERN_PROC_SESSION: - text = ""; break; - case KERN_PROC_TTY: text = ""; break; - case KERN_PROC_UID: text = ""; break; - case KERN_PROC_RUID: text = ""; break; - case KERN_PROC_GID: text = ""; break; - case KERN_PROC_RGID: text = ""; break; - } - } else if (i == 1 && mib[0] == KERN_PROC_TTY) { - switch ((dev_t)mib[i]) { - case KERN_PROC_TTY_NODEV: - text = ""; break; - case KERN_PROC_TTY_REVOKE: - text = ""; break; - } - } - - if (!valuesonly && text != NULL) - put_field(proc, NULL, text); - else - put_value(proc, NULL, "%d", mib[i]); - } - - /* - * Save the requested structure length, so that we can later - * determine how many elements were returned (see below). - */ - proc->sctl_arg = (size == 4) ? mib[2] : 0; - - return 0; - } - - if (proc->sctl_arg > 0) { - /* TODO: optionally dump struct kinfo_drivers array */ - put_open(proc, name, 0, "[", ", "); - if (size > 0) - put_tail(proc, size / proc->sctl_arg, 0); - put_close(proc, "]"); - } else - put_ptr(proc, name, addr); - - return TRUE; -} - -/* - * Print CTL_KERN KERN_PROC_ARGS. - */ -static int -put_kern_proc_args(struct trace_proc * proc, const char * name, int type, - const void * ptr, vir_bytes addr, size_t size) -{ - const int *mib; - const char *text; - unsigned int i; - int v; - - if (type == ST_NAME) { - mib = (const int *)ptr; - - for (i = 0; i < size; i++) { - text = NULL; - - if (i == 1) { - switch (mib[i]) { - case KERN_PROC_ARGV: text = ""; break; - case KERN_PROC_ENV: text = ""; break; - case KERN_PROC_NARGV: text = ""; break; - case KERN_PROC_NENV: text = ""; break; - } - } - - if (!valuesonly && text != NULL) - put_field(proc, NULL, text); - else - put_value(proc, NULL, "%d", mib[i]); - } - - /* Save the subrequest, so that we can later print data. */ - proc->sctl_arg = (size == 2) ? mib[1] : -999; - - return 0; - } - - if ((proc->sctl_arg == KERN_PROC_NARGV || - proc->sctl_arg == KERN_PROC_NENV) && size == sizeof(v) && - mem_get_data(proc->pid, addr, &v, sizeof(v)) >= 0) { - put_open(proc, name, PF_NONAME, "{", ", "); - - put_value(proc, NULL, "%d", v); - - put_close(proc, "}"); - } else - put_ptr(proc, name, addr); - - return TRUE; -} - -/* - * Print CTL_KERN KERN_CP_TIME. - */ -static int -put_kern_cp_time(struct trace_proc * proc, const char * name __unused, - int type, const void * ptr, vir_bytes addr __unused, size_t size) -{ - const uint64_t *p; - unsigned int i; - const int *mib; - - if (type == ST_NAME) { - mib = (const int *)ptr; - for (i = 0; i < size; i++) - put_value(proc, NULL, "%d", mib[i]); - - return 0; - } - - p = (const uint64_t *)ptr; - - /* TODO: support for multi-CPU results */ - for (i = 0; i < CPUSTATES; i++) - put_value(proc, NULL, "%"PRIu64, p[i]); - - return TRUE; -} - -/* - * Print CTL_KERN KERN_CONSDEV. - */ -static int -put_kern_consdev(struct trace_proc * proc, const char * name, - int type __unused, const void * ptr, vir_bytes addr __unused, - size_t size __unused) -{ - - put_dev(proc, NULL, *(const dev_t *)ptr); - - return TRUE; -} - -/* - * Print CTL_KERN KERN_DRIVERS. - */ -static int -put_kern_drivers(struct trace_proc * proc, const char * name, - int type __unused, const void * ptr __unused, vir_bytes addr __unused, - size_t size) -{ - - /* TODO: optionally dump struct kinfo_drivers array */ - put_open(proc, name, 0, "[", ", "); - if (size > 0) - put_tail(proc, size / sizeof(struct kinfo_drivers), 0); - put_close(proc, "]"); - - return TRUE; -} - -/* - * Print CTL_KERN KERN_BOOTTIME. - */ -static int -put_kern_boottime(struct trace_proc * proc, const char * name, - int type __unused, const void * ptr __unused, vir_bytes addr, - size_t size) -{ - - if (size == sizeof(struct timeval)) - put_struct_timeval(proc, name, 0, addr); - else - put_ptr(proc, name, addr); - - return TRUE; -} - -/* - * Print CTL_KERN KERN_SYSVIPC KERN_SYSVIPC_INFO. - */ -static int -put_kern_sysvipc_info(struct trace_proc * proc, const char * name, - int type, const void * ptr, vir_bytes addr, size_t size) -{ - const int *mib; - const char *text; - unsigned int i; - - /* - * TODO: print the obtained structure(s). For now we are just - * concerned with the name components. - */ - if (type != ST_NAME) { - put_ptr(proc, name, addr); - - return TRUE; - } - - mib = (const int *)ptr; - - for (i = 0; i < size; i++) { - text = NULL; - - if (i == 0) { - switch (mib[i]) { - case KERN_SYSVIPC_SEM_INFO: text = ""; break; - case KERN_SYSVIPC_SHM_INFO: text = ""; break; - case KERN_SYSVIPC_MSG_INFO: text = ""; break; - } - } - - if (!valuesonly && text != NULL) - put_field(proc, NULL, text); - else - put_value(proc, NULL, "%d", mib[i]); - } - - return 0; -} - -/* The CTL_KERN KERN_SYSVIPC table. */ -static const struct sysctl_tab kern_sysvipc_tab[] = { - PROC(KERN_SYSVIPC_INFO, 0, put_kern_sysvipc_info), -}; - -/* The CTL_KERN table. */ -static const struct sysctl_tab kern_tab[] = { - PROC(KERN_CLOCKRATE, sizeof(struct clockinfo), put_kern_clockrate), - PROC(KERN_PROC2, 0, put_kern_proc2), - PROC(KERN_PROC_ARGS, 0, put_kern_proc_args), - PROC(KERN_CP_TIME, sizeof(uint64_t) * CPUSTATES, put_kern_cp_time), - PROC(KERN_CONSDEV, sizeof(dev_t), put_kern_consdev), - PROC(KERN_DRIVERS, 0, put_kern_drivers), - NODE(KERN_SYSVIPC, kern_sysvipc_tab), - PROC(KERN_BOOTTIME, 0, put_kern_boottime), -}; - -/* - * Print CTL_VM VM_LOADAVG. - */ -static int -put_vm_loadavg(struct trace_proc * proc, const char * name __unused, - int type __unused, const void * ptr, vir_bytes addr __unused, - size_t size __unused) -{ - const struct loadavg *loadavg; - unsigned int i; - - loadavg = (const struct loadavg *)ptr; - - put_open(proc, "ldavg", 0, "{", ", "); - - for (i = 0; i < __arraycount(loadavg->ldavg); i++) - put_value(proc, NULL, "%"PRIu32, loadavg->ldavg[i]); - - put_close(proc, "}"); - - if (verbose > 0) { - put_value(proc, "fscale", "%ld", loadavg->fscale); - - return TRUE; - } else - return FALSE; -} - -/* The CTL_VM table. */ -static const struct sysctl_tab vm_tab[] = { - PROC(VM_LOADAVG, sizeof(struct loadavg), put_vm_loadavg), -}; - -/* - * Print CTL_NET PF_ROUTE 0. - */ -static int -put_net_route_rtable(struct trace_proc * proc, const char * name, - int type, const void * ptr, vir_bytes addr, size_t size) -{ - const int *mib; - const char *text; - unsigned int i; - - /* - * TODO: print the obtained structure(s). For now we are just - * concerned with the name components. - */ - if (type != ST_NAME) { - put_ptr(proc, name, addr); - - return TRUE; - } - - mib = (const int *)ptr; - - for (i = 0; i < size; i++) { - text = NULL; - - switch (i) { - case 0: - switch (mib[i]) { - case AF_UNSPEC: text = ""; break; - case AF_LINK: text = ""; break; - case AF_INET: text = ""; break; - case AF_INET6: text = ""; break; - /* TODO: add more address families here */ - } - break; - case 1: - switch (mib[i]) { - case NET_RT_DUMP: text = ""; break; - case NET_RT_FLAGS: text = ""; break; - case NET_RT_IFLIST: text = ""; break; - } - break; - case 2: - if (mib[1] == NET_RT_IFLIST && mib[i] == 0) - text = ""; - } - - if (!valuesonly && text != NULL) - put_field(proc, NULL, text); - else - put_value(proc, NULL, "%d", mib[i]); - } - - return 0; -} - -/* The CTL_NET PF_ROUTE table. */ -static const struct sysctl_tab net_route_tab[] = { - PROC(0, 0, put_net_route_rtable), -}; - -/* The CTL_NET table. */ -static const struct sysctl_tab net_tab[] = { - NODE(PF_ROUTE, net_route_tab), -}; - -/* The top-level table, which is indexed by identifier. */ -static const struct sysctl_tab root_tab[] = { - [CTL_KERN] = NODE(0, kern_tab), - [CTL_VM] = NODE(0, vm_tab), - [CTL_NET] = NODE(0, net_tab), -}; - -/* - * This buffer should be large enough to avoid having to perform dynamic - * allocation in all but highly exceptional cases. The CTL_KERN subtree is - * currently the largest, so we base the buffer size on its length. - * TODO: merge this buffer with ioctlbuf. - */ -static char sysctlbuf[sizeof(struct sysctlnode) * KERN_MAXID]; - -static const struct flags sysctl_flags[] = { - FLAG_MASK(SYSCTL_VERS_MASK, SYSCTL_VERS_0), - FLAG_MASK(SYSCTL_VERS_MASK, SYSCTL_VERSION), -#define SYSCTL_VER_ENTRIES 2 /* the first N entries are for SYSCTL_VERS_MASK */ - FLAG(CTLFLAG_UNSIGNED), - FLAG(CTLFLAG_OWNDESC), - FLAG(CTLFLAG_MMAP), - FLAG(CTLFLAG_ALIAS), - FLAG(CTLFLAG_ANYNUMBER), - FLAG(CTLFLAG_ROOT), - FLAG(CTLFLAG_HEX), - FLAG(CTLFLAG_IMMEDIATE), - FLAG(CTLFLAG_OWNDATA), - FLAG(CTLFLAG_HIDDEN), - FLAG(CTLFLAG_PERMANENT), - FLAG(CTLFLAG_PRIVATE), - FLAG(CTLFLAG_ANYWRITE), - FLAG_MASK(CTLFLAG_READWRITE, CTLFLAG_READONLY), - FLAG_MASK(CTLFLAG_READWRITE, CTLFLAG_READWRITE), - FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_NODE), - FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_INT), - FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_STRING), - FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_QUAD), - FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_STRUCT), - FLAG_MASK(SYSCTL_TYPEMASK, CTLTYPE_BOOL), -}; - -/* - * Print the immediate value of a sysctl node. - */ -static void -put_sysctl_imm(struct trace_proc * proc, struct sysctlnode * scn, int use_name) -{ - const char *name; - - name = NULL; - - switch (SYSCTL_TYPE(scn->sysctl_flags)) { - case CTLTYPE_INT: - if (use_name) - name = "sysctl_idata"; - if (scn->sysctl_flags & CTLFLAG_HEX) - put_value(proc, name, "0x%x", scn->sysctl_idata); - else if (scn->sysctl_flags & CTLFLAG_UNSIGNED) - put_value(proc, name, "%u", scn->sysctl_idata); - else - put_value(proc, name, "%d", scn->sysctl_idata); - break; - case CTLTYPE_BOOL: - if (use_name) - name = "sysctl_bdata"; - put_field(proc, name, (scn->sysctl_bdata) ? "true" : "false"); - break; - case CTLTYPE_QUAD: - if (use_name) - name = "sysctl_qdata"; - if (scn->sysctl_flags & CTLFLAG_HEX) - put_value(proc, name, "0x%"PRIx64, scn->sysctl_qdata); - else - put_value(proc, name, "%"PRIu64, scn->sysctl_qdata); - break; - } -} - -/* - * Printer for CTL_QUERY data. - */ -static int -put_sysctl_query(struct trace_proc * proc, const char * name, int type, - const void * data __unused, vir_bytes addr, size_t size) -{ - struct sysctlnode scn; - - if (type == ST_NEWP) { - if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn))) - return TRUE; - - /* Print just the protocol version, that's all there is. */ - if (verbose > 1) - put_flags(proc, "sysctl_flags", sysctl_flags, - SYSCTL_VER_ENTRIES, "0x%x", scn.sysctl_flags); - - put_close_struct(proc, FALSE /*all*/); - } else { - /* TODO: optionally dump struct sysctlnode array */ - put_open(proc, name, 0, "[", ", "); - if (size > 0) - put_tail(proc, size / sizeof(scn), 0); - put_close(proc, "]"); - } - - return TRUE; -} - -/* - * Printer for CTL_CREATE data. - */ -static int -put_sysctl_create(struct trace_proc * proc, const char * name, int type, - const void * data __unused, vir_bytes addr, size_t size) -{ - struct sysctlnode scn; - - if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn))) - return TRUE; - - if (type == ST_NEWP) - put_flags(proc, "sysctl_flags", sysctl_flags, - COUNT(sysctl_flags), "0x%x", scn.sysctl_flags); - - if (scn.sysctl_num == CTL_CREATE && type == ST_NEWP && !valuesonly) - put_field(proc, "sysctl_num", "CTL_CREATE"); - else - put_value(proc, "sysctl_num", "%d", scn.sysctl_num); - - if (type == ST_NEWP) { - put_buf(proc, "sysctl_name", PF_LOCADDR | PF_STRING, - (vir_bytes)scn.sysctl_name, sizeof(scn.sysctl_name)); - } - if (scn.sysctl_ver != 0 && verbose > 0) - put_value(proc, "sysctl_ver", "%u", scn.sysctl_ver); - - if (type == ST_NEWP) { - if (scn.sysctl_flags & CTLFLAG_IMMEDIATE) - put_sysctl_imm(proc, &scn, TRUE /*use_name*/); - - switch (SYSCTL_TYPE(scn.sysctl_flags)) { - case CTLTYPE_NODE: - break; - case CTLTYPE_STRING: - if (scn.sysctl_data != NULL) - put_buf(proc, "sysctl_data", PF_STRING, - (vir_bytes)scn.sysctl_data, - (scn.sysctl_size > 0) ? scn.sysctl_size : - SSIZE_MAX /* hopefully it stops early */); - if (scn.sysctl_data != NULL || verbose == 0) - break; - /* FALLTHROUGH */ - default: - if (!(scn.sysctl_flags & CTLFLAG_IMMEDIATE) && - verbose > 0) - put_ptr(proc, "sysctl_data", - (vir_bytes)scn.sysctl_data); - break; - } - - if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_STRUCT || - verbose > 0) - put_value(proc, "sysctl_size", "%zu", scn.sysctl_size); - } - - put_close_struct(proc, FALSE /*all*/); - - return TRUE; -} - -/* - * Printer for CTL_DESTROY data. - */ -static int -put_sysctl_destroy(struct trace_proc * proc, const char * name, int type, - const void * data __unused, vir_bytes addr, size_t size) -{ - struct sysctlnode scn; - - if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn))) - return TRUE; - - if (type == ST_NEWP) { - put_value(proc, "sysctl_num", "%d", scn.sysctl_num); - if (scn.sysctl_name[0] != '\0') - put_buf(proc, "sysctl_name", PF_LOCADDR | PF_STRING, - (vir_bytes)scn.sysctl_name, - sizeof(scn.sysctl_name)); - if (scn.sysctl_ver != 0 && verbose > 0) - put_value(proc, "sysctl_ver", "%u", scn.sysctl_ver); - } - - put_close_struct(proc, FALSE /*all*/); - - return TRUE; -} - -/* - * Printer for CTL_CREATE data. - */ -static int -put_sysctl_describe(struct trace_proc * proc, const char * name, int type, - const void * data __unused, vir_bytes addr, size_t size) -{ - struct sysctlnode scn; - - if (type == ST_NEWP) { - if (!put_open_struct(proc, name, 0, addr, &scn, sizeof(scn))) - return TRUE; - - /* Print just the protocol version, that's all there is. */ - if (verbose > 1) - put_flags(proc, "sysctl_flags", sysctl_flags, - SYSCTL_VER_ENTRIES, "0x%x", scn.sysctl_flags); - - put_value(proc, "sysctl_num", "%d", scn.sysctl_num); - - if (scn.sysctl_desc != NULL) - put_buf(proc, "sysctl_desc", PF_STRING, - (vir_bytes)scn.sysctl_desc, 1024 /*no constant!*/); - else if (verbose > 0) - put_ptr(proc, "sysctl_desc", - (vir_bytes)scn.sysctl_desc); - - put_close_struct(proc, FALSE /*all*/); - } else { - /* TODO: optionally dump struct sysctldesc array */ - put_field(proc, name, (size == 0) ? "[]" : "[..]"); - } - - return TRUE; -} - -/* - * Printer for generic data, using the node flags stored in proc->sysctl_flags. - */ -static int -put_sysctl_generic(struct trace_proc * proc, const char * name, int type, - const void * data __unused, vir_bytes addr, size_t size) -{ - struct sysctlnode scn; - void *ptr; - size_t len; - - switch (SYSCTL_TYPE(proc->sctl_flags)) { - case CTLTYPE_STRING: - put_buf(proc, name, PF_STRING, addr, size); - return TRUE; - case CTLTYPE_INT: - ptr = &scn.sysctl_idata; - len = sizeof(scn.sysctl_idata); - break; - case CTLTYPE_BOOL: - ptr = &scn.sysctl_bdata; - len = sizeof(scn.sysctl_bdata); - break; - case CTLTYPE_QUAD: - ptr = &scn.sysctl_qdata; - len = sizeof(scn.sysctl_qdata); - break; - case CTLTYPE_STRUCT: - default: - ptr = NULL; - len = 0; - break; - } - - if (ptr == NULL || len != size || - mem_get_data(proc->pid, addr, ptr, len) < 0) { - put_ptr(proc, name, addr); - return TRUE; - } - - put_open(proc, name, PF_NONAME, "{", ", "); - - scn.sysctl_flags = proc->sctl_flags; - - put_sysctl_imm(proc, &scn, FALSE); - - put_close(proc, "}"); - - return TRUE; -} - -/* - * Obtain information about a particular node 'id' in the node directory - * identified by the MIB path 'name' (length 'namelen'). Return TRUE if the - * node was found, in which case it is copied into 'scnp'. Return FALSE if the - * node was not found or another error occurred. - */ -static int -get_sysctl_node(const int * name, unsigned int namelen, int id, - struct sysctlnode * scnp) -{ - struct sysctlnode *scn, *escn, *fscn; - char *buf; - size_t len, elen; - int r, mib[CTL_MAXNAME]; - - assert(namelen < CTL_MAXNAME); - assert(id >= 0); - - /* Query the parent, first using our static buffer for the results. */ - memcpy(mib, name, sizeof(mib[0]) * namelen); - mib[namelen] = CTL_QUERY; - len = sizeof(sysctlbuf); - r = sysctl(mib, namelen + 1, sysctlbuf, &len, NULL, 0); - if (r == -1 && (errno != ENOMEM || len == 0)) - return FALSE; - - /* Even with partial results, check if we already found the node. */ - elen = MIN(len, sizeof(sysctlbuf)); - scn = (struct sysctlnode *)sysctlbuf; - escn = (struct sysctlnode *)&sysctlbuf[elen]; - fscn = NULL; /* pointer to the node once found, NULL until then */ - for (; scn < escn && fscn == NULL; scn++) - if (scn->sysctl_num == id) - fscn = scn; - - /* If our buffer was too small, use a temporary buffer. */ - if (fscn == NULL && r == -1) { - if ((buf = malloc(len)) == NULL) - return FALSE; - if (sysctl(mib, namelen, buf, &len, NULL, 0) == 0) { - scn = (struct sysctlnode *)sysctlbuf; - escn = (struct sysctlnode *)&sysctlbuf[len]; - for (; scn < escn && fscn != NULL; scn++) - if (scn->sysctl_num == id) - fscn = scn; - } - free(buf); - } - - if (fscn != NULL) { - memcpy(scnp, fscn, sizeof(*scnp)); - return TRUE; - } else - return FALSE; -} - -/* - * Print the name string of one level of a sysctl(2) name, while also gathering - * information about the target node. Return 1 if name interpretation should - * continue as before, meaning this function will also be called for the next - * name component (if any). Return 0 if the rest of the name should be printed - * as numbers, without interpretation. Return -1 if printing the name is now - * complete. - */ -static int -put_sysctl_namestr(struct trace_proc * proc, const int * name, - unsigned int namelen, unsigned int n, int all, - const struct sysctl_tab ** sctp) -{ - const struct sysctl_tab *sct; - struct sysctlnode scn; - const char *namestr; - int i, r, id, is_last; - - assert(n < namelen); - - id = name[n]; - is_last = (n == namelen - 1 && all); - namestr = NULL; - - /* Negative identifiers are meta-identifiers. */ - if (id < 0) { - switch (id) { - case CTL_EOL: namestr = ""; break; - case CTL_QUERY: namestr = ""; break; - case CTL_CREATE: namestr = ""; break; - case CTL_CREATESYM: namestr = ""; break; - case CTL_DESTROY: namestr = ""; break; - case CTL_MMAP: namestr = ""; break; - case CTL_DESCRIBE: namestr = ""; break; - } - - /* For some of them, we can print their parameters. */ - if (is_last) { - switch (id) { - case CTL_QUERY: - proc->sctl_proc = put_sysctl_query; - break; - case CTL_CREATE: - proc->sctl_proc = put_sysctl_create; - break; - case CTL_DESTROY: - proc->sctl_proc = put_sysctl_destroy; - break; - case CTL_DESCRIBE: - proc->sctl_proc = put_sysctl_describe; - break; - } - } - - /* - * Meta-identifiers are allowed only at the very end of a name, - * so if anything follows a meta-identifier, there is no good - * way to interpret it. We just print numbers. - */ - r = 0; - } else if (get_sysctl_node(name, n, id, &scn)) { - /* - * For regular identifiers, first see if we have a callback - * function that does the interpretation. The use of the - * callback function depends on whether the current node is of - * type CTLTYPE_NODE: if it is, the callback function is - * responsible for printing the rest of the name (and we return - * -1 here after we are done, #1); if it isn't, then we just - * use the callback function to interpret the node value (#2). - * If we do not have a callback function, but the current node - * is of type CTLTYPE_NODE *and* has a non-NULL callback - * function registered in the MIB service, the remote callback - * function would interpret the rest of the name, so we simply - * print the rest of the name as numbers (returning 0 once we - * are done, #3). Without a MIB-service callback function, - * such nodes are just taken as path components and thus we - * return 1 to continue resolution (#4). Finally, if we do not - * have a callback function, and the current node is a data - * node (i.e., *not* of type CTLTYPE_NODE), we try to interpret - * it generically if it is the last component (#5), or we give - * up and just print numbers otherwise (#6). - */ - - /* Okay, so start by looking up the node in our own tables. */ - sct = NULL; - if (n == 0) { - /* The top level is ID-indexed for performance. */ - if ((unsigned int)id < __arraycount(root_tab)) - *sctp = &root_tab[id]; - else - *sctp = NULL; - } else if (*sctp != NULL) { - /* Other levels are searched, because of sparseness. */ - sct = (*sctp)->tab; /* NULL if missing or leaf */ - for (i = (int)(*sctp)->size; sct != NULL && i > 0; - i--, sct++) - if (sct->id == id) - break; - if (i == 0) - sct = NULL; - *sctp = sct; - } - - /* Now determine what to do. */ - if (SYSCTL_TYPE(scn.sysctl_flags) == CTLTYPE_NODE) { - if (sct != NULL && sct->proc != NULL) { - proc->sctl_size = sct->size; - proc->sctl_proc = sct->proc; - r = -1; /* #1 */ - } else if (scn.sysctl_func != NULL) - r = 0; /* #3 */ - else - r = 1; /* #4 */ - } else { - if (!is_last) - r = 0; /* #6 */ - else if (sct != NULL && sct->proc != NULL) { - /* A nonzero size must match the node size. */ - if (sct->size == 0 || - sct->size == scn.sysctl_size) { - proc->sctl_size = sct->size; - proc->sctl_proc = sct->proc; - } - r = 0; /* #2 */ - } else { - proc->sctl_flags = scn.sysctl_flags; - proc->sctl_proc = put_sysctl_generic; - r = 0; /* #5 */ - } - } - - namestr = scn.sysctl_name; - } else { - /* - * The node was not found. This basically means that we will - * not be able to get any information about deeper nodes - * either. We do not even try: just print numbers. - */ - r = 0; - } - - if (!valuesonly && namestr != NULL) - put_field(proc, NULL, namestr); - else - put_value(proc, NULL, "%d", id); - - /* - * Did we determine that the rest of the name should be printed by the - * callback function? Then we might as well make that happen. The - * abuse of the parameter types is not great, oh well. - */ - if (r == -1) - (void)proc->sctl_proc(proc, NULL, ST_NAME, &name[n + 1], 0, - namelen - n - 1); - - return r; -} - -/* - * Print the sysctl(2) name parameter, and gather information needed to print - * the oldp and newp parameters later. - */ -static void -put_sysctl_name(struct trace_proc * proc, const char * name, int flags, - vir_bytes addr, unsigned int namelen) -{ - const struct sysctl_tab *sct = NULL; - int r, all, namebuf[CTL_MAXNAME]; - unsigned int n; - - if (namelen > CTL_MAXNAME) { - namelen = CTL_MAXNAME; - all = 0; - } else - all = 1; - - if ((flags & PF_FAILED) || valuesonly > 1 || namelen > CTL_MAXNAME || - (namelen > 0 && !(flags & PF_LOCADDR) && - mem_get_data(proc->pid, addr, namebuf, - namelen * sizeof(namebuf[0])) < 0)) { - if (flags & PF_LOCADDR) - put_field(proc, name, "&.."); - else - put_ptr(proc, name, addr); - return; - } else if (namelen > 0 && (flags & PF_LOCADDR)) - memcpy(namebuf, (void *)addr, sizeof(namebuf[0]) * namelen); - - /* - * Print the path name of the node as possible, and find information - * about the target node as we go along. See put_sysctl_namestr() for - * the meaning of 'r'. - */ - put_open(proc, name, PF_NONAME, "[", "."); - for (n = 0, r = 1; n < namelen; n++) { - if (r == 1) { - if ((r = put_sysctl_namestr(proc, namebuf, namelen, n, - all, &sct)) < 0) - break; - } else - put_value(proc, NULL, "%d", namebuf[n]); - } - if (!all) - put_field(proc, NULL, ".."); - put_close(proc, "]"); -} - -/* - * Print the sysctl(2) oldp or newp parameter. PF_ALT means that the given - * parameter is newp rather than oldp, in which case PF_FAILED will not be set. - */ -static void -put_sysctl_data(struct trace_proc * proc, const char * name, int flags, - vir_bytes addr, size_t len) -{ - char *ptr; - int type, all; - - if ((flags & PF_FAILED) || addr == 0 || valuesonly > 1 || - proc->sctl_proc == NULL || proc->sctl_size > sizeof(sysctlbuf) || - (proc->sctl_size > 0 && (proc->sctl_size != len || - mem_get_data(proc->pid, addr, sysctlbuf, proc->sctl_size) < 0))) { - put_ptr(proc, name, addr); - return; - } - - type = (flags & PF_ALT) ? ST_NEWP : ST_OLDP; - ptr = (proc->sctl_size > 0) ? sysctlbuf : NULL; - - /* - * The rough idea here: we have a "simple" mode and a "flexible" mode, - * depending on whether a size was specified in our table. For the - * simple mode, we only call the callback function when we have been - * able to copy in the data. A surrounding {} block will be printed - * automatically, the callback function only has to print the data - * fields. The simple mode is basically for structures. In contrast, - * the flexible mode leaves both the copying and the printing entirely - * to the callback function, which thus may print the pointer on copy - * failure (in which case the surrounding {}s would get in the way). - */ - if (ptr != NULL) - put_open(proc, name, 0, "{", ", "); - - all = proc->sctl_proc(proc, name, type, ptr, addr, len); - - if (ptr != NULL) { - if (all == FALSE) - put_field(proc, NULL, ".."); - put_close(proc, "}"); - } -} - -static int -mib_sysctl_out(struct trace_proc * proc, const message * m_out) -{ - unsigned int namelen; - - /* Reset the sysctl-related state. */ - proc->sctl_flags = 0; - proc->sctl_size = 0; - proc->sctl_proc = NULL; - proc->sctl_arg = 0; - - namelen = m_out->m_lc_mib_sysctl.namelen; - - /* As part of processing the name, we initialize the state. */ - if (namelen <= CTL_SHORTNAME) - put_sysctl_name(proc, "name", PF_LOCADDR, - (vir_bytes)&m_out->m_lc_mib_sysctl.name, namelen); - else - put_sysctl_name(proc, "name", 0, m_out->m_lc_mib_sysctl.namep, - namelen); - - put_value(proc, "namelen", "%u", namelen); - - if (m_out->m_lc_mib_sysctl.oldp == 0 || valuesonly > 1) { - put_sysctl_data(proc, "oldp", 0, - m_out->m_lc_mib_sysctl.oldp, - m_out->m_lc_mib_sysctl.oldlen); - /* If oldp is NULL, oldlen may contain garbage; don't print. */ - if (m_out->m_lc_mib_sysctl.oldp != 0) - put_value(proc, "oldlen", "%zu", /* {%zu} is more */ - m_out->m_lc_mib_sysctl.oldlen); /* correct.. */ - else - put_value(proc, "oldlen", "%d", 0); - put_sysctl_data(proc, "newp", PF_ALT, - m_out->m_lc_mib_sysctl.newp, - m_out->m_lc_mib_sysctl.newlen); - put_value(proc, "newlen", "%zu", - m_out->m_lc_mib_sysctl.newlen); - return CT_DONE; - } else - return CT_NOTDONE; -} - -static void -mib_sysctl_in(struct trace_proc * proc, const message * m_out, - const message * m_in, int failed) -{ - int err; - - if (m_out->m_lc_mib_sysctl.oldp != 0 && valuesonly <= 1) { - put_sysctl_data(proc, "oldp", failed, - m_out->m_lc_mib_sysctl.oldp, - m_in->m_mib_lc_sysctl.oldlen /* the returned length */); - put_value(proc, "oldlen", "%zu", /* {%zu} is more correct.. */ - m_out->m_lc_mib_sysctl.oldlen); - put_sysctl_data(proc, "newp", PF_ALT, - m_out->m_lc_mib_sysctl.newp, - m_out->m_lc_mib_sysctl.newlen); - put_value(proc, "newlen", "%zu", - m_out->m_lc_mib_sysctl.newlen); - put_equals(proc); - } - - put_result(proc); - - /* - * We want to print the returned old length in the following cases: - * 1. the call succeeded, the old pointer was NULL, and no new data was - * supplied; - * 2. the call succeeded, the old pointer was not NULL, and the - * returned old length is different from the supplied old length. - * 3. the call failed with ENOMEM or EEXIST, and the old pointer was - * not NULL (an undocumented NetBSD feature, used by sysctl(8)). - */ - if (/*#1*/ (!failed && m_out->m_lc_mib_sysctl.oldp == 0 && - (m_out->m_lc_mib_sysctl.newp == 0 || - m_out->m_lc_mib_sysctl.newlen == 0)) || - /*#2*/ (!failed && m_out->m_lc_mib_sysctl.oldp != 0 && - m_out->m_lc_mib_sysctl.oldlen != m_in->m_mib_lc_sysctl.oldlen) || - /*#3*/ (failed && call_errno(proc, &err) && - (err == ENOMEM || err == EEXIST) && - m_out->m_lc_mib_sysctl.oldp != 0)) { - put_open(proc, NULL, 0, "(", ", "); - put_value(proc, "oldlen", "%zu", m_in->m_mib_lc_sysctl.oldlen); - put_close(proc, ")"); - } -} - -#define MIB_CALL(c) [((MIB_ ## c) - MIB_BASE)] - -static const struct call_handler mib_map[] = { - MIB_CALL(SYSCTL) = HANDLER("sysctl", mib_sysctl_out, mib_sysctl_in), -}; - -const struct calls mib_calls = { - .endpt = MIB_PROC_NR, - .base = MIB_BASE, - .map = mib_map, - .count = COUNT(mib_map) -}; diff --git a/minix/usr.sbin/mkfs.mfs/mkfs.c b/minix/usr.sbin/mkfs.mfs/mkfs.c deleted file mode 100644 index a3e619bed..000000000 --- a/minix/usr.sbin/mkfs.mfs/mkfs.c +++ /dev/null @@ -1,1544 +0,0 @@ -/* mkfs - make the MINIX filesystem Authors: Tanenbaum et al. */ - -/* Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans */ - -#if HAVE_NBTOOL_CONFIG_H -#include "nbtool_config.h" -#endif - -#include -#include -#include - -#if defined(__minix) -#include -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Definition of the file system layout: */ -#include "const.h" -#include "type.h" -#include "mfsdir.h" -#include "super.h" - -#define INODE_MAP START_BLOCK -/* inode zone indexes pointing to single and double indirect zones */ -#define S_INDIRECT_IDX (NR_DZONES) -#define D_INDIRECT_IDX (NR_DZONES+1) - - -#define MAX_TOKENS 10 -#define LINE_LEN 300 - -/* some Minix specific types that do not conflict with Posix */ -#ifndef block_t -typedef uint32_t block_t; /* block number */ -#endif -#ifndef zone_t -typedef uint32_t zone_t; /* zone number */ -#endif -#ifndef bit_t -typedef uint32_t bit_t; /* bit number in a bit map */ -#endif -#ifndef bitchunk_t -typedef uint32_t bitchunk_t; /* collection of bits in a bitmap */ -#endif - -struct fs_size { - ino_t inocount; /* amount of inodes */ - zone_t zonecount; /* amount of zones */ - block_t blockcount; /* amount of blocks */ -}; - -extern char *optarg; -extern int optind; - -block_t nrblocks; -int zone_per_block, zone_shift = 0; -zone_t next_zone, zoff, nr_indirzones; -int inodes_per_block, indir_per_block, indir_per_zone; -unsigned int zone_size; -ino_t nrinodes, inode_offset, next_inode; -int lct = 0, fd, print = 0; -int simple = 0, dflag = 0, verbose = 0; -int donttest; /* skip test if it fits on medium */ -char *progname; -uint64_t fs_offset_bytes, fs_offset_blocks, written_fs_size = 0; - -time_t current_time; -char *zero; -unsigned char *umap_array; /* bit map tells if block read yet */ -size_t umap_array_elements; -block_t zone_map; /* where is zone map? (depends on # inodes) */ -#ifndef MFS_STATIC_BLOCK_SIZE -size_t block_size; -#else -#define block_size MFS_STATIC_BLOCK_SIZE -#endif - -FILE *proto; - -int main(int argc, char **argv); -void detect_fs_size(struct fs_size * fssize); -void sizeup_dir(struct fs_size * fssize); -block_t sizeup(char *device); -static int bitmapsize(bit_t nr_bits, size_t blk_size); -void super(zone_t zones, ino_t inodes); -void rootdir(ino_t inode); -void enter_symlink(ino_t inode, char *link); -int dir_try_enter(zone_t z, ino_t child, char const *name); -void eat_dir(ino_t parent); -void eat_file(ino_t inode, int f); -void enter_dir(ino_t parent, char const *name, ino_t child); -void add_zone(ino_t n, zone_t z, size_t bytes, time_t cur_time); -void incr_link(ino_t n); -void incr_size(ino_t n, size_t count); -static ino_t alloc_inode(int mode, int usrid, int grpid); -static zone_t alloc_zone(void); -void insert_bit(block_t block, bit_t bit); -int mode_con(char *p); -void get_line(char line[LINE_LEN], char *parse[MAX_TOKENS]); -void check_mtab(const char *devname); -time_t file_time(int f); -__dead void pexit(char const *s, ...) __printflike(1,2); -void *alloc_block(void); -void print_fs(void); -int read_and_set(block_t n); -void special(char *string, int insertmode); -__dead void usage(void); -void get_block(block_t n, void *buf); -void get_super_block(void *buf); -void put_block(block_t n, void *buf); -static uint64_t mkfs_seek(uint64_t pos, int whence); -static ssize_t mkfs_write(void * buf, size_t count); - -/*================================================================ - * mkfs - make filesystem - *===============================================================*/ -int -main(int argc, char *argv[]) -{ - int nread, mode, usrid, grpid, ch, extra_space_percent, Tflag = 0; - block_t blocks, maxblocks, bblocks; - ino_t inodes, root_inum; - char *token[MAX_TOKENS], line[LINE_LEN], *sfx; - struct fs_size fssize; - int insertmode = 0; - - progname = argv[0]; - - /* Process switches. */ - blocks = 0; - inodes = 0; - bblocks = 0; -#ifndef MFS_STATIC_BLOCK_SIZE - block_size = 0; -#endif - zone_shift = 0; - extra_space_percent = 0; - while ((ch = getopt(argc, argv, "B:b:di:ltvx:z:I:T:")) != EOF) - switch (ch) { -#ifndef MFS_STATIC_BLOCK_SIZE - case 'B': - block_size = strtoul(optarg, &sfx, 0); - switch(*sfx) { - case 'b': case 'B': /* bytes; NetBSD-compatible */ - case '\0': break; - case 'K': - case 'k': block_size*=1024; break; - case 's': block_size*=SECTOR_SIZE; break; - default: usage(); - } - break; -#else - case 'B': - if (block_size != strtoul(optarg, (char **) NULL, 0)) - errx(4, "block size must be exactly %d bytes", - MFS_STATIC_BLOCK_SIZE); - break; - (void)sfx; /* shut up warnings about unused variable...*/ -#endif - case 'I': - fs_offset_bytes = strtoul(optarg, (char **) NULL, 0); - insertmode = 1; - break; - case 'b': - blocks = bblocks = strtoul(optarg, (char **) NULL, 0); - break; - case 'T': - Tflag = 1; - current_time = strtoul(optarg, (char **) NULL, 0); - break; - case 'd': - dflag = 1; - break; - case 'i': - inodes = strtoul(optarg, (char **) NULL, 0); - break; - case 'l': print = 1; break; - case 't': donttest = 1; break; - case 'v': ++verbose; break; - case 'x': extra_space_percent = atoi(optarg); break; - case 'z': zone_shift = atoi(optarg); break; - default: usage(); - } - - if (argc == optind) usage(); - - /* Get the current time, set it to the mod time of the binary of - * mkfs itself when the -d flag is used. The 'current' time is put into - * the i_mtimes of all the files. This -d feature is useful when - * producing a set of file systems, and one wants all the times to be - * identical. First you set the time of the mkfs binary to what you - * want, then go. - */ - if(Tflag) { - if(dflag) - errx(1, "-T and -d both specify a time and so are mutually exclusive"); - } else if(dflag) { - struct stat statbuf; - if (stat(progname, &statbuf)) { - err(1, "stat of itself"); - } - current_time = statbuf.st_mtime; - } else { - current_time = time((time_t *) 0); /* time mkfs is being run */ - } - - /* Percentage of extra size must be nonnegative. - * It can legitimately be bigger than 100 but has to make some sort of sense. - */ - if(extra_space_percent < 0 || extra_space_percent > 2000) usage(); - -#ifdef DEFAULT_BLOCK_SIZE - if(!block_size) block_size = DEFAULT_BLOCK_SIZE; -#endif - if (block_size % SECTOR_SIZE) - errx(4, "block size must be multiple of sector (%d bytes)", SECTOR_SIZE); -#ifdef MIN_BLOCK_SIZE - if (block_size < MIN_BLOCK_SIZE) - errx(4, "block size must be at least %d bytes", MIN_BLOCK_SIZE); -#endif -#ifdef MAX_BLOCK_SIZE - if (block_size > MAX_BLOCK_SIZE) - errx(4, "block size must be at most %d bytes", MAX_BLOCK_SIZE); -#endif - if(block_size%INODE_SIZE) - errx(4, "block size must be a multiple of inode size (%d bytes)", INODE_SIZE); - - if(zone_shift < 0 || zone_shift > 14) - errx(4, "zone_shift must be a small non-negative integer"); - zone_per_block = 1 << zone_shift; /* nr of blocks per zone */ - - inodes_per_block = INODES_PER_BLOCK(block_size); - indir_per_block = INDIRECTS(block_size); - indir_per_zone = INDIRECTS(block_size) << zone_shift; - /* number of file zones we can address directly and with a single indirect*/ - nr_indirzones = NR_DZONES + indir_per_zone; - zone_size = block_size << zone_shift; - /* Checks for an overflow: only with very big block size */ - if (zone_size <= 0) - errx(4, "Zones are too big for this program; smaller -B or -z, please!"); - - /* now that the block size is known, do buffer allocations where - * possible. - */ - zero = alloc_block(); - - fs_offset_blocks = roundup(fs_offset_bytes, block_size) / block_size; - - /* Determine the size of the device if not specified as -b or proto. */ - maxblocks = sizeup(argv[optind]); - if (bblocks != 0 && bblocks + fs_offset_blocks > maxblocks && !insertmode) { - errx(4, "Given size -b %d exceeds device capacity(%d)\n", bblocks, maxblocks); - } - - if (argc - optind == 1 && bblocks == 0) { - blocks = maxblocks; - /* blocks == 0 is checked later, but leads to a funny way of - * reporting a 0-sized device (displays usage). - */ - if(blocks < 1) { - errx(1, "zero size device."); - } - } - - /* The remaining args must be 'special proto', or just 'special' if the - * no. of blocks has already been specified. - */ - if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage(); - - if (maxblocks && blocks > maxblocks && !insertmode) { - errx(1, "%s: number of blocks too large for device.", argv[optind]); - } - - /* Check special. */ - check_mtab(argv[optind]); - - /* Check and start processing proto. */ - optarg = argv[++optind]; - if (optind < argc && (proto = fopen(optarg, "r")) != NULL) { - /* Prototype file is readable. */ - lct = 1; - get_line(line, token); /* skip boot block info */ - - /* Read the line with the block and inode counts. */ - get_line(line, token); - if (bblocks == 0){ - blocks = strtol(token[0], (char **) NULL, 10); - } else { - if(bblocks < strtol(token[0], (char **) NULL, 10)) { - errx(1, "%s: number of blocks given as parameter(%d)" - " is too small for given proto file(%ld).", - argv[optind], bblocks, - strtol(token[0], (char **) NULL, 10)); - }; - } - inodes = strtol(token[1], (char **) NULL, 10); - - /* Process mode line for root directory. */ - get_line(line, token); - mode = mode_con(token[0]); - usrid = atoi(token[1]); - grpid = atoi(token[2]); - - if(blocks <= 0 && inodes <= 0){ - detect_fs_size(&fssize); - blocks = fssize.blockcount; - inodes = fssize.inocount; - blocks += blocks*extra_space_percent/100; - inodes += inodes*extra_space_percent/100; -/* XXX is it OK to write on stdout? Use warn() instead? Also consider using verbose */ - fprintf(stderr, "dynamically sized filesystem: %u blocks, %u inodes\n", - (unsigned int) blocks, (unsigned int) inodes); - } - } else { - lct = 0; - if (optind < argc) { - /* Maybe the prototype file is just a size. Check. */ - blocks = strtoul(optarg, (char **) NULL, 0); - if (blocks == 0) errx(2, "Can't open prototype file"); - } - - /* Make simple file system of the given size, using defaults. */ - mode = 040777; - usrid = 0; - grpid = 0; - simple = 1; - } - - if (inodes == 0) { - long long kb = ((unsigned long long)blocks*block_size) / 1024; - - inodes = kb / 2; - if (kb >= 100000) inodes = kb / 4; - if (kb >= 1000000) inodes = kb / 6; - if (kb >= 10000000) inodes = kb / 8; - if (kb >= 100000000) inodes = kb / 10; - if (kb >= 1000000000) inodes = kb / 12; -/* XXX check overflow: with very large number of blocks, this results in insanely large number of inodes */ -/* XXX check underflow (if/when ino_t is signed), else the message below will look strange */ - - /* round up to fill inode block */ - inodes += inodes_per_block - 1; - inodes = inodes / inodes_per_block * inodes_per_block; - } - - if (blocks < 5) errx(1, "Block count too small"); - if (inodes < 1) errx(1, "Inode count too small"); - - nrblocks = blocks; - nrinodes = inodes; - - umap_array_elements = 1 + blocks/8; - if(!(umap_array = malloc(umap_array_elements))) - err(1, "can't allocate block bitmap (%u bytes).", - (unsigned)umap_array_elements); - - /* Open special. */ - special(argv[--optind], insertmode); - - if (!donttest) { - uint16_t *testb; - ssize_t w; - - testb = alloc_block(); - - /* Try writing the last block of partition or diskette. */ - mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET); - testb[0] = 0x3245; - testb[1] = 0x11FF; - testb[block_size/2-1] = 0x1F2F; - w=mkfs_write(testb, block_size); - sync(); /* flush write, so if error next read fails */ - mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET); - testb[0] = 0; - testb[1] = 0; - testb[block_size/2-1] = 0; - nread = read(fd, testb, block_size); - if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF || - testb[block_size/2-1] != 0x1F2F) { - warn("nread = %d\n", nread); - warnx("testb = 0x%x 0x%x 0x%x\n", - testb[0], testb[1], testb[block_size-1]); - errx(1, "File system is too big for minor device (read)"); - } - mkfs_seek((uint64_t)(blocks - 1) * block_size, SEEK_SET); - testb[0] = 0; - testb[1] = 0; - testb[block_size/2-1] = 0; - mkfs_write(testb, block_size); - mkfs_seek(0L, SEEK_SET); - free(testb); - } - - /* Make the file-system */ - - put_block(BOOT_BLOCK, zero); /* Write a null boot block. */ - put_block(BOOT_BLOCK+1, zero); /* Write another null block. */ - - super(nrblocks >> zone_shift, inodes); - - root_inum = alloc_inode(mode, usrid, grpid); - rootdir(root_inum); - if (simple == 0) eat_dir(root_inum); - - if (print) print_fs(); - else if (verbose > 1) { - if (zone_shift) - fprintf(stderr, "%d inodes used. %u zones (%u blocks) used.\n", - (int)next_inode-1, next_zone, next_zone*zone_per_block); - else - fprintf(stderr, "%d inodes used. %u zones used.\n", - (int)next_inode-1, next_zone); - } - - if(insertmode) printf("%"PRIu64"\n", written_fs_size); - - return(0); - - /* NOTREACHED */ -} /* end main */ - -/*================================================================ - * detect_fs_size - determine image size dynamically - *===============================================================*/ -void -detect_fs_size(struct fs_size * fssize) -{ - int prev_lct = lct; - off_t point = ftell(proto); - block_t initb; - zone_t initz; - - fssize->inocount = 1; /* root directory node */ - fssize->zonecount = 0; - fssize->blockcount = 0; - - sizeup_dir(fssize); - - initb = bitmapsize(1 + fssize->inocount, block_size); - initb += bitmapsize(fssize->zonecount, block_size); - initb += START_BLOCK; - initb += (fssize->inocount + inodes_per_block - 1) / inodes_per_block; - initz = (initb + zone_per_block - 1) >> zone_shift; - - fssize->blockcount = initb+ fssize->zonecount; - lct = prev_lct; - fseek(proto, point, SEEK_SET); -} - -void -sizeup_dir(struct fs_size * fssize) -{ - char *token[MAX_TOKENS], *p; - char line[LINE_LEN]; - FILE *f; - off_t size; - int dir_entries = 2; - zone_t dir_zones = 0, fzones, indirects; - - while (1) { - get_line(line, token); - p = token[0]; - if (*p == '$') { - dir_zones = (dir_entries / (NR_DIR_ENTRIES(block_size) * zone_per_block)); - if(dir_entries % (NR_DIR_ENTRIES(block_size) * zone_per_block)) - dir_zones++; - if(dir_zones > NR_DZONES) - dir_zones++; /* Max single indir */ - fssize->zonecount += dir_zones; - return; - } - - p = token[1]; - fssize->inocount++; - dir_entries++; - - if (*p == 'd') { - sizeup_dir(fssize); - } else if (*p == 'b' || *p == 'c') { - - } else if (*p == 's') { - fssize->zonecount++; /* Symlink contents is always stored a block */ - } else { - if ((f = fopen(token[4], "rb")) == NULL) { -/* on minix natively, allow EACCES and skip the entry. - * while crossbuilding, always fail on error. - */ -#ifdef __minix - if(errno == EACCES) - warn("dynamic sizing: can't open %s", token[4]); - else -#endif - err(1, "dynamic sizing: can't open %s", token[4]); - } else if (fseek(f, 0, SEEK_END) < 0) { - pexit("dynamic size detection failed: seek to end of %s", - token[4]); - } else if ( (size = ftell(f)) == (off_t)(-1)) { - pexit("dynamic size detection failed: can't tell size of %s", - token[4]); - } else { - fclose(f); - fzones = roundup(size, zone_size) / zone_size; - indirects = 0; - /* XXX overflow? fzones is u32, size is potentially 64-bit */ - if (fzones > NR_DZONES) - indirects++; /* single indirect needed */ - if (fzones > nr_indirzones) { - /* Each further group of 'indir_per_zone' - * needs one supplementary indirect zone: - */ - indirects += roundup(fzones - nr_indirzones, - indir_per_zone) / indir_per_zone; - indirects++; /* + double indirect needed!*/ - } - fssize->zonecount += fzones + indirects; - } - } - } -} - -/*================================================================ - * sizeup - determine device size - *===============================================================*/ -block_t -sizeup(char * device) -{ - block_t d; -#if defined(__minix) - uint64_t bytes, resize; - uint32_t rem; -#else - off_t size; -#endif - - - if ((fd = open(device, O_RDONLY)) == -1) { - if (errno != ENOENT) - perror("sizeup open"); - return 0; - } - -#if defined(__minix) - if(minix_sizeup(device, &bytes) < 0) { - perror("sizeup"); - return 0; - } - - d = (uint32_t)(bytes / block_size); - rem = (uint32_t)(bytes % block_size); - - resize = ((uint64_t)d * block_size) + rem; - if(resize != bytes) { - /* Assume block_t is unsigned */ - d = (block_t)(-1ul); - fprintf(stderr, "%s: truncating FS at %lu blocks\n", - progname, (unsigned long)d); - } -#else - size = mkfs_seek(0, SEEK_END); - /* Assume block_t is unsigned */ - if (size / block_size > (block_t)(-1ul)) { - d = (block_t)(-1ul); - fprintf(stderr, "%s: truncating FS at %lu blocks\n", - progname, (unsigned long)d); - } else - d = size / block_size; -#endif - - return d; -} - -/* - * copied from fslib - */ -static int -bitmapsize(bit_t nr_bits, size_t blk_size) -{ - block_t nr_blocks; - - nr_blocks = nr_bits / FS_BITS_PER_BLOCK(blk_size); - if (nr_blocks * FS_BITS_PER_BLOCK(blk_size) < nr_bits) - ++nr_blocks; - return(nr_blocks); -} - -/*================================================================ - * super - construct a superblock - *===============================================================*/ - -void -super(zone_t zones, ino_t inodes) -{ - block_t inodeblks, initblks, i; - unsigned long nb; - long long ind_per_zone, zo; - void *buf; - struct super_block *sup; - - sup = buf = alloc_block(); - -#ifdef MFSFLAG_CLEAN - /* The assumption is that mkfs will create a clean FS. */ - sup->s_flags = MFSFLAG_CLEAN; -#endif - - sup->s_ninodes = inodes; - /* Check for overflow; cannot happen on V3 file systems */ - if(inodes != sup->s_ninodes) - errx(1, "Too much inodes for that version of Minix FS."); - sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */ - sup->s_zones = zones; - /* Check for overflow; can only happen on V1 file systems */ - if(zones != sup->s_zones) - errx(1, "Too much zones (blocks) for that version of Minix FS."); - -#ifndef MFS_STATIC_BLOCK_SIZE -#define BIGGERBLOCKS "Please try a larger block size for an FS of this size." -#else -#define BIGGERBLOCKS "Please use MinixFS V3 for an FS of this size." -#endif - sup->s_imap_blocks = nb = bitmapsize(1 + inodes, block_size); - /* Checks for an overflow: nb is uint32_t while s_imap_blocks is of type - * int16_t */ - if(sup->s_imap_blocks != nb) { - errx(1, "too many inode bitmap blocks.\n" BIGGERBLOCKS); - } - sup->s_zmap_blocks = nb = bitmapsize(zones, block_size); - /* Idem here check for overflow */ - if(nb != sup->s_zmap_blocks) { - errx(1, "too many block bitmap blocks.\n" BIGGERBLOCKS); - } - inode_offset = START_BLOCK + sup->s_imap_blocks + sup->s_zmap_blocks; - inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block; - initblks = inode_offset + inodeblks; - sup->s_firstdatazone_old = nb = - (initblks + (1 << zone_shift) - 1) >> zone_shift; - if(nb >= zones) errx(1, "bit maps too large"); - if(nb != sup->s_firstdatazone_old) { - /* The field is too small to store the value. Fortunately, the value - * can be computed from other fields. We set the on-disk field to zero - * to indicate that it must not be used. Eventually, we can always set - * the on-disk field to zero, and stop using it. - */ - sup->s_firstdatazone_old = 0; - } - sup->s_firstdatazone = nb; - zoff = sup->s_firstdatazone - 1; - sup->s_log_zone_size = zone_shift; - sup->s_magic = SUPER_MAGIC; -#ifdef MFS_SUPER_BLOCK_SIZE - sup->s_block_size = block_size; - /* Check for overflow */ - if(block_size != sup->MFS_SUPER_BLOCK_SIZE) - errx(1, "block_size too large."); - sup->s_disk_version = 0; -#endif - - ind_per_zone = (long long) indir_per_zone; - zo = NR_DZONES + ind_per_zone + ind_per_zone*ind_per_zone; -#ifndef MAX_MAX_SIZE -#define MAX_MAX_SIZE (INT32_MAX) -#endif - if(MAX_MAX_SIZE/block_size < zo) { - sup->s_max_size = MAX_MAX_SIZE; - } - else { - sup->s_max_size = zo * block_size; - } - - if (verbose>1) { - fprintf(stderr, "Super block values:\n" - "\tnumber of inodes\t%12d\n" - "\tnumber of zones \t%12d\n" - "\tinode bit map blocks\t%12d\n" - "\tzone bit map blocks\t%12d\n" - "\tfirst data zone \t%12d\n" - "\tblocks per zone shift\t%12d\n" - "\tmaximum file size\t%12d\n" - "\tmagic number\t\t%#12X\n", - sup->s_ninodes, sup->s_zones, - sup->s_imap_blocks, sup->s_zmap_blocks, sup->s_firstdatazone, - sup->s_log_zone_size, sup->s_max_size, sup->s_magic); -#ifdef MFS_SUPER_BLOCK_SIZE - fprintf(stderr, "\tblock size\t\t%12d\n", sup->s_block_size); -#endif - } - - mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET); - mkfs_write(buf, SUPER_BLOCK_BYTES); - - /* Clear maps and inodes. */ - for (i = START_BLOCK; i < initblks; i++) put_block((block_t) i, zero); - - next_zone = sup->s_firstdatazone; - next_inode = 1; - - zone_map = INODE_MAP + sup->s_imap_blocks; - - insert_bit(zone_map, 0); /* bit zero must always be allocated */ - insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but - * must be allocated */ - - free(buf); -} - - -/*================================================================ - * rootdir - install the root directory - *===============================================================*/ -void -rootdir(ino_t inode) -{ - zone_t z; - - z = alloc_zone(); - add_zone(inode, z, 2 * sizeof(struct direct), current_time); - enter_dir(inode, ".", inode); - enter_dir(inode, "..", inode); - incr_link(inode); - incr_link(inode); -} - -void -enter_symlink(ino_t inode, char *lnk) -{ - zone_t z; - size_t len; - char *buf; - - buf = alloc_block(); - z = alloc_zone(); - len = strlen(lnk); - if (len >= block_size) - pexit("symlink too long, max length is %u", (unsigned)block_size - 1); - strcpy(buf, lnk); - put_block((z << zone_shift), buf); - - add_zone(inode, z, len, current_time); - - free(buf); -} - - -/*================================================================ - * eat_dir - recursively install directory - *===============================================================*/ -void -eat_dir(ino_t parent) -{ - /* Read prototype lines and set up directory. Recurse if need be. */ - char *token[MAX_TOKENS], *p; - char line[LINE_LEN]; - int mode, usrid, grpid, maj, min, f; - ino_t n; - zone_t z; - size_t size; - - while (1) { - get_line(line, token); - p = token[0]; - if (*p == '$') return; - p = token[1]; - mode = mode_con(p); - usrid = atoi(token[2]); - grpid = atoi(token[3]); - n = alloc_inode(mode, usrid, grpid); - - /* Enter name in directory and update directory's size. */ - enter_dir(parent, token[0], n); - incr_size(parent, sizeof(struct direct)); - - /* Check to see if file is directory or special. */ - incr_link(n); - if (*p == 'd') { - /* This is a directory. */ - z = alloc_zone(); /* zone for new directory */ - add_zone(n, z, 2 * sizeof(struct direct), current_time); - enter_dir(n, ".", n); - enter_dir(n, "..", parent); - incr_link(parent); - incr_link(n); - eat_dir(n); - } else if (*p == 'b' || *p == 'c') { - /* Special file. */ - maj = atoi(token[4]); - min = atoi(token[5]); - size = 0; - if (token[6]) size = atoi(token[6]); - size = block_size * size; - add_zone(n, (zone_t) (makedev(maj,min)), size, current_time); - } else if (*p == 's') { - enter_symlink(n, token[4]); - } else { - /* Regular file. Go read it. */ - if ((f = open(token[4], O_RDONLY)) < 0) { -/* on minix natively, allow EACCES and skip the entry. - * while crossbuilding, always fail on error. - */ -#ifdef __minix - if(errno == EACCES) - warn("Can't open %s", token[4]); - else -#endif - err(1, "Can't open %s", token[4]); - } else { - eat_file(n, f); - } - } - } - -} - -/*================================================================ - * eat_file - copy file to MINIX - *===============================================================*/ -/* Zonesize >= blocksize */ -void -eat_file(ino_t inode, int f) -{ - int ct = 0, i, j; - zone_t z = 0; - char *buf; - time_t timeval; - - buf = alloc_block(); - - do { - for (i = 0, j = 0; i < zone_per_block; i++, j += ct) { - memset(buf, 0, block_size); - if ((ct = read(f, buf, block_size)) > 0) { - if (i == 0) z = alloc_zone(); - put_block((z << zone_shift) + i, buf); - } - } - timeval = (dflag ? current_time : file_time(f)); - if (ct) add_zone(inode, z, (size_t) j, timeval); - } while (ct == block_size); - close(f); - free(buf); -} - -int -dir_try_enter(zone_t z, ino_t child, char const *name) -{ - struct direct *dir_entry = alloc_block(); - int r = 0; - block_t b; - int i, l; - - b = z << zone_shift; - for (l = 0; l < zone_per_block; l++, b++) { - get_block(b, dir_entry); - - for (i = 0; i < NR_DIR_ENTRIES(block_size); i++) - if (!dir_entry[i].d_ino) - break; - - if(i < NR_DIR_ENTRIES(block_size)) { - r = 1; - dir_entry[i].d_ino = child; - assert(sizeof(dir_entry[i].d_name) == MFS_DIRSIZ); - if (verbose && strlen(name) > MFS_DIRSIZ) - fprintf(stderr, "File name %s is too long, truncated\n", name); - strncpy(dir_entry[i].d_name, name, MFS_DIRSIZ); - put_block(b, dir_entry); - break; - } - } - - free(dir_entry); - - return r; -} - -/*================================================================ - * directory & inode management assist group - *===============================================================*/ -void -enter_dir(ino_t parent, char const *name, ino_t child) -{ - /* Enter child in parent directory */ - /* Works for dir > 1 block and zone > block */ - unsigned int k; - block_t b, indir; - zone_t z; - int off; - struct inode *ino; - struct inode *inoblock = alloc_block(); - zone_t *indirblock = alloc_block(); - - assert(!(block_size % sizeof(struct direct))); - - /* Obtain the inode structure */ - b = ((parent - 1) / inodes_per_block) + inode_offset; - off = (parent - 1) % inodes_per_block; - get_block(b, inoblock); - ino = inoblock + off; - - for (k = 0; k < NR_DZONES; k++) { - z = ino->i_zone[k]; - if (z == 0) { - z = alloc_zone(); - ino->i_zone[k] = z; - } - - if(dir_try_enter(z, child, __UNCONST(name))) { - put_block(b, inoblock); - free(inoblock); - free(indirblock); - return; - } - } - - /* no space in directory using just direct blocks; try indirect */ - if (ino->i_zone[S_INDIRECT_IDX] == 0) - ino->i_zone[S_INDIRECT_IDX] = alloc_zone(); - - indir = ino->i_zone[S_INDIRECT_IDX] << zone_shift; - --indir; /* Compensate for ++indir below */ - for(k = 0; k < (indir_per_zone); k++) { - if (k % indir_per_block == 0) - get_block(++indir, indirblock); - z = indirblock[k % indir_per_block]; - if(!z) { - z = indirblock[k % indir_per_block] = alloc_zone(); - put_block(indir, indirblock); - } - if(dir_try_enter(z, child, __UNCONST(name))) { - put_block(b, inoblock); - free(inoblock); - free(indirblock); - return; - } - } - - pexit("Directory-inode %u beyond single indirect blocks. Could not enter %s", - (unsigned)parent, name); -} - - -void -add_zone(ino_t n, zone_t z, size_t bytes, time_t mtime) -{ - /* Add zone z to inode n. The file has grown by 'bytes' bytes. */ - - int off, i, j; - block_t b; - zone_t indir, dindir; - struct inode *p, *inode; - zone_t *blk, *dblk; - - assert(inodes_per_block*sizeof(*inode) == block_size); - if(!(inode = alloc_block())) - err(1, "Couldn't allocate block of inodes"); - - b = ((n - 1) / inodes_per_block) + inode_offset; - off = (n - 1) % inodes_per_block; - get_block(b, inode); - p = &inode[off]; - p->i_size += bytes; - p->i_mtime = mtime; -#ifndef MFS_INODE_ONLY_MTIME /* V1 file systems did not have them... */ - p->i_atime = p->i_ctime = current_time; -#endif - for (i = 0; i < NR_DZONES; i++) - if (p->i_zone[i] == 0) { - p->i_zone[i] = z; - put_block(b, inode); - free(inode); - return; - } - - assert(indir_per_block*sizeof(*blk) == block_size); - if(!(blk = alloc_block())) - err(1, "Couldn't allocate indirect block"); - - /* File has grown beyond a small file. */ - if (p->i_zone[S_INDIRECT_IDX] == 0) - p->i_zone[S_INDIRECT_IDX] = alloc_zone(); - indir = p->i_zone[S_INDIRECT_IDX] << zone_shift; - put_block(b, inode); - --indir; /* Compensate for ++indir below */ - for (i = 0; i < (indir_per_zone); i++) { - if (i % indir_per_block == 0) - get_block(++indir, blk); - if (blk[i % indir_per_block] == 0) { - blk[i] = z; - put_block(indir, blk); - free(blk); - free(inode); - return; - } - } - - /* File has grown beyond single indirect; we need a double indirect */ - assert(indir_per_block*sizeof(*dblk) == block_size); - if(!(dblk = alloc_block())) - err(1, "Couldn't allocate double indirect block"); - - if (p->i_zone[D_INDIRECT_IDX] == 0) - p->i_zone[D_INDIRECT_IDX] = alloc_zone(); - dindir = p->i_zone[D_INDIRECT_IDX] << zone_shift; - put_block(b, inode); - --dindir; /* Compensate for ++indir below */ - for (j = 0; j < (indir_per_zone); j++) { - if (j % indir_per_block == 0) - get_block(++dindir, dblk); - if (dblk[j % indir_per_block] == 0) - dblk[j % indir_per_block] = alloc_zone(); - indir = dblk[j % indir_per_block] << zone_shift; - --indir; /* Compensate for ++indir below */ - for (i = 0; i < (indir_per_zone); i++) { - if (i % indir_per_block == 0) - get_block(++indir, blk); - if (blk[i % indir_per_block] == 0) { - blk[i] = z; - put_block(dindir, dblk); - put_block(indir, blk); - free(dblk); - free(blk); - free(inode); - return; - } - } - } - - pexit("File has grown beyond double indirect"); -} - - -/* Increment the link count to inode n */ -void -incr_link(ino_t n) -{ - int off; - static int enter = 0; - static struct inode *inodes = NULL; - block_t b; - - if (enter++) pexit("internal error: recursive call to incr_link()"); - - b = ((n - 1) / inodes_per_block) + inode_offset; - off = (n - 1) % inodes_per_block; - { - assert(sizeof(*inodes) * inodes_per_block == block_size); - if(!inodes && !(inodes = alloc_block())) - err(1, "couldn't allocate a block of inodes"); - - get_block(b, inodes); - inodes[off].i_nlinks++; - /* Check overflow (particularly on V1)... */ - if (inodes[off].i_nlinks <= 0) - pexit("Too many links to a directory"); - put_block(b, inodes); - } - enter = 0; -} - - -/* Increment the file-size in inode n */ -void -incr_size(ino_t n, size_t count) -{ - block_t b; - int off; - - b = ((n - 1) / inodes_per_block) + inode_offset; - off = (n - 1) % inodes_per_block; - { - struct inode *inodes; - - assert(inodes_per_block * sizeof(*inodes) == block_size); - if(!(inodes = alloc_block())) - err(1, "couldn't allocate a block of inodes"); - - get_block(b, inodes); - /* Check overflow; avoid compiler spurious warnings */ - if (inodes[off].i_size+(int)count < inodes[off].i_size || - inodes[off].i_size > MAX_MAX_SIZE-(int)count) - pexit("File has become too big to be handled by MFS"); - inodes[off].i_size += count; - put_block(b, inodes); - free(inodes); - } -} - - -/*================================================================ - * allocation assist group - *===============================================================*/ -static ino_t -alloc_inode(int mode, int usrid, int grpid) -{ - ino_t num; - int off; - block_t b; - struct inode *inodes; - - num = next_inode++; - if (num > nrinodes) { - pexit("File system does not have enough inodes (only %llu)", nrinodes); - } - b = ((num - 1) / inodes_per_block) + inode_offset; - off = (num - 1) % inodes_per_block; - - assert(inodes_per_block * sizeof(*inodes) == block_size); - if(!(inodes = alloc_block())) - err(1, "couldn't allocate a block of inodes"); - - get_block(b, inodes); - if (inodes[off].i_mode) { - pexit("allocation new inode %llu with non-zero mode - this cannot happen", - num); - } - inodes[off].i_mode = mode; - inodes[off].i_uid = usrid; - inodes[off].i_gid = grpid; - if (verbose && (inodes[off].i_uid != usrid || inodes[off].i_gid != grpid)) - fprintf(stderr, "Uid/gid %d.%d do not fit within inode, truncated\n", usrid, grpid); - put_block(b, inodes); - - free(inodes); - - /* Set the bit in the bit map. */ - insert_bit((block_t) INODE_MAP, num); - return(num); -} - - -/* Allocate a new zone */ -static zone_t -alloc_zone(void) -{ - /* Works for zone > block */ - block_t b; - int i; - zone_t z; - - z = next_zone++; - b = z << zone_shift; - if (b > nrblocks - zone_per_block) - pexit("File system not big enough for all the files"); - for (i = 0; i < zone_per_block; i++) - put_block(b + i, zero); /* give an empty zone */ - - insert_bit(zone_map, z - zoff); - return z; -} - - -/* Insert one bit into the bitmap */ -void -insert_bit(block_t map, bit_t bit) -{ - int boff, w, s; - unsigned int bits_per_block; - block_t map_block; - bitchunk_t *buf; - - buf = alloc_block(); - - bits_per_block = FS_BITS_PER_BLOCK(block_size); - map_block = map + bit / bits_per_block; - if (map_block >= inode_offset) - pexit("insertbit invades inodes area - this cannot happen"); - boff = bit % bits_per_block; - - assert(boff >=0); - assert(boff < FS_BITS_PER_BLOCK(block_size)); - get_block(map_block, buf); - w = boff / FS_BITCHUNK_BITS; - s = boff % FS_BITCHUNK_BITS; - buf[w] |= (1 << s); - put_block(map_block, buf); - - free(buf); -} - - -/*================================================================ - * proto-file processing assist group - *===============================================================*/ -int mode_con(char *p) -{ - /* Convert string to mode */ - int o1, o2, o3, mode; - char c1, c2, c3; - - c1 = *p++; - c2 = *p++; - c3 = *p++; - o1 = *p++ - '0'; - o2 = *p++ - '0'; - o3 = *p++ - '0'; - mode = (o1 << 6) | (o2 << 3) | o3; - if (c1 == 'd') mode |= S_IFDIR; - if (c1 == 'b') mode |= S_IFBLK; - if (c1 == 'c') mode |= S_IFCHR; - if (c1 == 's') mode |= S_IFLNK; - if (c1 == 'l') mode |= S_IFLNK; /* just to be somewhat ls-compatible*/ -/* XXX note: some other mkfs programs consider L to create hardlinks */ - if (c1 == '-') mode |= S_IFREG; - if (c2 == 'u') mode |= S_ISUID; - if (c3 == 'g') mode |= S_ISGID; -/* XXX There are no way to encode S_ISVTX */ - return(mode); -} - -void -get_line(char line[LINE_LEN], char *parse[MAX_TOKENS]) -{ - /* Read a line and break it up in tokens */ - int k; - char c, *p; - int d; - - for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0; - memset(line, 0, LINE_LEN); - k = 0; - p = line; - while (1) { - if (++k > LINE_LEN) pexit("Line too long"); - d = fgetc(proto); - if (d == EOF) pexit("Unexpected end-of-file"); - *p = d; - if (*p == ' ' || *p == '\t') *p = 0; - if (*p == '\n') { - lct++; - *p++ = 0; - *p = '\n'; - break; - } - p++; - } - - k = 0; - p = line; - while (1) { - c = *p++; - if (c == '\n') return; - if (c == 0) continue; - parse[k++] = p - 1; - do { - c = *p++; - } while (c != 0 && c != '\n'); - } -} - - -/*================================================================ - * other stuff - *===============================================================*/ - -/* - * Check to see if the special file named 'device' is mounted. - */ -void -check_mtab(const char *device) /* /dev/hd1 or whatever */ -{ -#if defined(__minix) - int n, r; - struct stat sb; - char dev[PATH_MAX], mount_point[PATH_MAX], - type[MNTNAMELEN], flags[MNTFLAGLEN]; - - r= stat(device, &sb); - if (r == -1) - { - if (errno == ENOENT) - return; /* Does not exist, and therefore not mounted. */ - err(1, "stat %s failed", device); - } - if (!S_ISBLK(sb.st_mode)) - { - /* Not a block device and therefore not mounted. */ - return; - } - - if (load_mtab(__UNCONST("mkfs")) < 0) return; - while (1) { - n = get_mtab_entry(dev, mount_point, type, flags); - if (n < 0) return; - if (strcmp(device, dev) == 0) { - /* Can't mkfs on top of a mounted file system. */ - errx(1, "%s is mounted on %s", device, mount_point); - } - } -#else - (void) device; /* shut up warnings about unused variable... */ -#endif -} - - -time_t -file_time(int f) -{ - struct stat statbuf; - - if (!fstat(f, &statbuf)) - return current_time; - if (statbuf.st_mtime<0 || statbuf.st_mtime>(uint32_t)(-1)) - return current_time; - return(statbuf.st_mtime); -} - - -__dead void -pexit(char const * s, ...) -{ - va_list va; - - va_start(va, s); - vwarn(s, va); - va_end(va); - if (lct != 0) - warnx("Line %d being processed when error detected.\n", lct); - exit(2); -} - - -void * -alloc_block(void) -{ - void *buf; - - if(!(buf = malloc(block_size))) { - err(1, "couldn't allocate filesystem buffer"); - } - memset(buf, 0, block_size); - - return buf; -} - -void -print_fs(void) -{ - int i, j; - ino_t k; - struct inode *inode2; - unsigned short *usbuf; - block_t b; - struct direct *dir; - - assert(inodes_per_block * sizeof(*inode2) == block_size); - if(!(inode2 = alloc_block())) - err(1, "couldn't allocate a block of inodes"); - - assert(NR_DIR_ENTRIES(block_size)*sizeof(*dir) == block_size); - if(!(dir = alloc_block())) - err(1, "couldn't allocate a block of directory entries"); - - usbuf = alloc_block(); - get_super_block(usbuf); - printf("\nSuperblock: "); - for (i = 0; i < 8; i++) printf("%06ho ", usbuf[i]); - printf("\n "); - for (i = 0; i < 8; i++) printf("%#04hX ", usbuf[i]); - printf("\n "); - for (i = 8; i < 15; i++) printf("%06ho ", usbuf[i]); - printf("\n "); - for (i = 8; i < 15; i++) printf("%#04hX ", usbuf[i]); - get_block((block_t) INODE_MAP, usbuf); - printf("...\nInode map: "); - for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]); - get_block((block_t) zone_map, usbuf); - printf("...\nZone map: "); - for (i = 0; i < 9; i++) printf("%06ho ", usbuf[i]); - printf("...\n"); - - free(usbuf); - usbuf = NULL; - - k = 0; - for (b = inode_offset; k < nrinodes; b++) { - get_block(b, inode2); - for (i = 0; i < inodes_per_block; i++) { - k = inodes_per_block * (int) (b - inode_offset) + i + 1; - /* Lint but OK */ - if (k > nrinodes) break; - { - if (inode2[i].i_mode != 0) { - printf("Inode %3u: mode=", (unsigned)k); - printf("%06o", (unsigned)inode2[i].i_mode); - printf(" uid=%2d gid=%2d size=", - (int)inode2[i].i_uid, (int)inode2[i].i_gid); - printf("%6ld", (long)inode2[i].i_size); - printf(" zone[0]=%u\n", (unsigned)inode2[i].i_zone[0]); - } - if ((inode2[i].i_mode & S_IFMT) == S_IFDIR) { - /* This is a directory */ - get_block(inode2[i].i_zone[0] << zone_shift, dir); - for (j = 0; j < NR_DIR_ENTRIES(block_size); j++) - if (dir[j].d_ino) - printf("\tInode %2u: %s\n", - (unsigned)dir[j].d_ino, - dir[j].d_name); - } - } - } - } - - if (zone_shift) - printf("%d inodes used. %u zones (%u blocks) used.\n", - (int)next_inode-1, next_zone, next_zone*zone_per_block); - else - printf("%d inodes used. %u zones used.\n", (int)next_inode-1, next_zone); - free(dir); - free(inode2); -} - - -/* - * The first time a block is read, it returns all 0s, unless there has - * been a write. This routine checks to see if a block has been accessed. - */ -int -read_and_set(block_t n) -{ - int w, s, mask, r; - - w = n / 8; - - assert(n < nrblocks); - if(w >= umap_array_elements) { - errx(1, "umap array too small - this can't happen"); - } - s = n % 8; - mask = 1 << s; - r = (umap_array[w] & mask ? 1 : 0); - umap_array[w] |= mask; - return(r); -} - -__dead void -usage(void) -{ - fprintf(stderr, "Usage: %s [-dltv] [-b blocks] [-i inodes]\n" - "\t[-z zone_shift] [-I offset] [-x extra] [-B blocksize] special [proto]\n", - progname); - exit(4); -} - -void -special(char * string, int insertmode) -{ - int openmode = O_RDWR; - if(!insertmode) openmode |= O_TRUNC; - fd = open(string, O_RDWR | O_CREAT, 0644); - if (fd < 0) err(1, "Can't open special file %s", string); - mkfs_seek(0, SEEK_SET); -} - - - -/* Read a block. */ -void -get_block(block_t n, void *buf) -{ - ssize_t k; - - /* First access returns a zero block */ - if (read_and_set(n) == 0) { - memcpy(buf, zero, block_size); - return; - } - mkfs_seek((uint64_t)(n) * block_size, SEEK_SET); - k = read(fd, buf, block_size); - if (k != block_size) - pexit("get_block couldn't read block #%u", (unsigned)n); -} - -/* Read the super block. */ -void -get_super_block(void *buf) -{ - ssize_t k; - - mkfs_seek((off_t) SUPER_BLOCK_BYTES, SEEK_SET); - k = read(fd, buf, SUPER_BLOCK_BYTES); - if (k != SUPER_BLOCK_BYTES) - err(1, "get_super_block couldn't read super block"); -} - -/* Write a block. */ -void -put_block(block_t n, void *buf) -{ - - (void) read_and_set(n); - - mkfs_seek((uint64_t)(n) * block_size, SEEK_SET); - mkfs_write(buf, block_size); -} - -static ssize_t -mkfs_write(void * buf, size_t count) -{ - uint64_t fssize; - ssize_t w; - - /* Perform & check write */ - w = write(fd, buf, count); - if(w < 0) - err(1, "mkfs_write: write failed"); - if(w != count) - errx(1, "mkfs_write: short write: %zd != %zu", w, count); - - /* Check if this has made the FS any bigger; count bytes after offset */ - fssize = mkfs_seek(0, SEEK_CUR); - - assert(fssize >= fs_offset_bytes); - fssize -= fs_offset_bytes; - fssize = roundup(fssize, block_size); - if(fssize > written_fs_size) - written_fs_size = fssize; - - return w; -} - -/* Seek to position in FS we're creating. */ -static uint64_t -mkfs_seek(uint64_t pos, int whence) -{ - if(whence == SEEK_SET) pos += fs_offset_bytes; - off_t newpos; - if((newpos=lseek(fd, pos, whence)) == (off_t) -1) - err(1, "mkfs_seek: lseek failed"); - return newpos; -}