Squashed commit of the following:
commit a90721e6275ba6eab733d3bbad7949b1d959faca
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 20:24:25 2013 +0200
3.3.0 sync fixes
Change-Id: Ic91e326623f6dbad58754152c4a632e4406f1117
commit f76695798821432c57fcdccc60208f06e9c9b028
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Oct 2 13:37:49 2013 +0200
keymaps: improve keypad slash support
Now that the keymaps can distinguish between the regular slash key
and the slash key on the numeric keypad, we can avoid localization
of the latter.
Change-Id: I20ead7d26a9baa82f5a522562524fd75d44efb42
commit 1bba92dc81840d55959925c36a5e387588e10710
Author: Mikal Villa <mikal.villa@gmail.com>
Date: Wed Oct 2 12:30:03 2013 +0200
Norwegian keymap
Change-Id: I181234afc8f1a058e92af6c1fe88979463aaff45
commit 916d1652d6a5fc7502dabca5d07e4aa598ce3fee
Author: Thomas Cort <tcort@minix3.org>
Date: Fri Sep 6 21:40:42 2013 -0400
Importing usr.bin/uname
Change-Id: I4c316221e288edd839e26a2af4cb59f28bf722c1
commit 03c06a560ef8d83eae5253ad0c22de0caa56ea5c
Author: Thomas Cort <tcort@minix3.org>
Date: Fri Sep 6 21:40:31 2013 -0400
uname: normalize release and version
Most systems provide the full version number in the
'release' field and the kernel version in 'version'.
Minix used to split the full version number between
release and version which caused problems for pkgsrc
and other applications. This patch brings Minix's
uname in line with other systems such as NetBSD.
It also brings the getty banner in line with NetBSD.
Old Minix uname:
sysname->Minix
nodename->10.0.2.15
release->3
version->2.1
machine->i686
New Minix uname:
sysname->Minix
nodename->10.0.2.15
release->3.2.1
version->Minix 3.2.1 (GENERIC)
machine->i686
Change-Id: I966633dfdcf2f9485966bb0d0d042afc45bbeb7d
commit f86d1a0245060845e902d8f3aa415fde3b2d7a0f
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Sep 19 10:57:10 2013 +0200
Replacing timer_t by netbsd's timer_t
* Renamed struct timer to struct minix_timer
* Renamed timer_t to minix_timer_t
* Ensured all the code uses the minix_timer_t typedef
* Removed ifdef around _BSD_TIMER_T
* Removed include/timers.h and merged it into include/minix/timers.h
* Resolved prototype conflict by renaming kernel's (re)set_timer
to (re)set_kernel_timer.
Change-Id: I56f0f30dfed96e1a0575d92492294cf9a06468a5
commit e3b7451fb77cb652b3d7a62e74e5071d6fed3211
Author: Lionel Sambuc <lionel@minix3.org>
Date: Wed Oct 2 10:56:24 2013 +0200
ARM serial driver: Comment termios_baud_rate.
The B0-B115200 defines are flags, and not the actual speed they
represent.
This fixes an incoherency for B0 handling, and documents why it is
required to call the function again after changing the speed flag.
DFL_BAUD is set to one of the flag, so to translate it to an actual
speed, the function calls itself again, which will always be able to
finish without inducing another recursive call.
Change-Id: I04ebfaefee31a88d05f0b726352d1581a966147b
commit 48432465aea5996af40fcee60f304a25a859f1dd
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Oct 1 23:25:01 2013 +0200
TTY: skip /dev/log checks if console is serial
It is unclear why /dev/log has its own open/close rules, but those
rules conflict with serial console redirection. This does not solve
the root of the problem, but it puts back in place more or less the
same workaround that was already in place before the TTY overhaul.
Change-Id: Ib53abbc28a76c1f2b0befc8448aeed0173bc96a5
commit 6914f1b1fdf39dd5e5161eda406592ee3beb110f
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:45:42 2013 +0200
Corrections to keep VFS ABI-compatible
Change-Id: Id8a53a687577c1b6f83c11c2e5fb0b8ed1cd2a08
commit 5f2721f5c672330afa589562bb01e163e5eb4e00
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:35:11 2013 +0200
Remove support for obsolete 3.2.1 ABI
Change-Id: I76b4960bda41f55d9c42f8c99c5beae3424ca851
commit 68b7ece9e806f4d52086983d45bc436e2fd21d88
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 31 22:59:44 2013 +0200
Fix various file system warnings
Change-Id: Ied10498c3ae14f9f2fd06914f23239df330fa296
commit bf9fe26adba1e7cdb4126e920513475c493380e6
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:22:13 2013 +0200
VFS/FS: replace protocol version with flag field
The main motivation for this change is that only Loris supports
multithreading, and Loris supports dynamic thread allocation, so the
number of supported threads can be implemented as a bit flag (i.e.,
either 1 or "at least as many as VFS has"). The ABI break obviates the
need to support file system versioning at this time, and several
other aspects are better implemented as flags as well. Other changes:
- replace peek/bpeek test upon mount with FS flag as well;
- mark libsffs as 64-bit file size capable;
- remove old (3.2.1) getdents support.
Change-Id: I313eace9c50ed816656c31cd47d969033d952a03
commit c9dee913657b61d3dc582399e673d6b7d6893f9c
Author: Lionel Sambuc <lionel@minix3.org>
Date: Fri Apr 19 13:16:51 2013 +0200
usr.bin/stat Update
Change-Id: I029160c73baab1b3465bc5397a36c55886db225b
commit 6696977375f7a1d459ccd89ca732e1fe894eeeff
Author: Lionel Sambuc <lionel@minix3.org>
Date: Fri Apr 19 09:54:51 2013 +0200
almost aligned ioctl prototype
Change-Id: I7f3eaa99d2a9767f71e8387cea5c7f56dcb28f99
commit 70b4c94174621116bdad888ec4978e6601dccf83
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Apr 18 14:42:15 2013 +0200
32 to 64 bits fsblkcnt_t and fsfilcnt_t.
Change-Id: I432229143c85cd178262b802a76ac606801ac59a
commit 6cc1244a4543d1ea6882a555905b46abfe36c8b6
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Apr 18 11:08:16 2013 +0200
moving prototypes to lib.h
Change-Id: If53d3f5ee761b10e0f3d4346a0c5b39ba7901c65
commit bc5de5f0a1f379f60cdab6f620d1cf0b58391abf
Author: Lionel Sambuc <lionel@minix3.org>
Date: Thu Apr 18 11:07:44 2013 +0200
struct uucred
Change-Id: Ia97cb6c38bb566be30d568a252ae7b76142a21dd
commit 7cef2f014e2596746900f3300744c33c660fbfa3
Author: Lionel Sambuc <lionel@minix3.org>
Date: Tue Apr 16 11:48:54 2013 +0200
Alignement on netbsd types, part 1
The following types are modified (old -> new):
* _BSD_USECONDS_T_ int -> unsigned int
* __socklen_t __int32_t -> __uint32_t
* blksize_t uint32_t -> int32_t
* rlim_t uint32_t -> uint64_t
On ARM:
* _BSD_CLOCK_T_ int -> unsigned int
On Intel:
* _BSD_CLOCK_T_ int -> unsigned long
bin/cat is also updated in order to fix warnings.
_BSD_TIMER_T_ has still to be aligned.
Change-Id: I2b4fda024125a19901120546c4e22e443ba5e9d7
commit 7f17cc1299be7b37f264905843c20eed48b1c276
Author: Lionel Sambuc <lionel@minix3.org>
Date: Fri Aug 23 20:27:27 2013 +0200
Adapt the type used for adjtime_delta
clock_t is currently a signed type, but in NetBSD this is not the
case. As we plan on aligning our types we have to change this as this
prevents negative delta from being correctly used.
Change-Id: I9bccdee2b41626b0262471dc1900de505a1991a7
commit 6e7038f040b6e869d54d4ee37c4b12197463eb83
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 19:01:19 2013 +0200
Butchered version of "Bumping version.."
Change-Id: Ieb5f31fe037bcf45cc86a906f983a0ab3f636468
commit 905a73dd50e2d14714d1c3ef5a2e033c4476a731
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 18:57:04 2013 +0200
Trimmed version of "VFS: use 64-bit file..."
Change-Id: I1b53ef05e939f6afed97f507d443a1d60ed81a21
commit 0a6d8e38369e339c9873510442105ba987c5f10b
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Oct 5 18:37:39 2013 +0200
Trimmed version of "stat.h: remove some big_ types"
Change-Id: I6c17d6e5e9d2ab4c1bbf33e461cec4c37b870266
commit 8aaf8adf38b17fb3607e2efe17fd0551f5109352
Author: Thomas Veerman <thomas@minix3.org>
Date: Thu Mar 7 14:46:21 2013 +0000
Define protocol version of {mode,ino,uid,gid}_t
Change-Id: Ia2027749f2ce55a561d19eb895a5618505e9a2ac
commit 4f1ba5747a58d6ccbc120298bd063fb0b15b9d1c
Author: Thomas Veerman <thomas@minix3.org>
Date: Mon Mar 25 21:09:10 2013 +0000
VFS-FS protocol: add versioning
Change-Id: Ice6fbfd4b535b7435653fa08b27a3378d1cfdbf8
commit 7d64ee23ff70b169cdada1a8d8512d96c93c8075
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 28 14:46:21 2013 +0200
Input infrastructure, INPUT server, PCKBD driver
This commit separates the low-level keyboard driver from TTY, putting
it in a separate driver (PCKBD). The commit also separates management
of raw input devices from TTY, and puts it in a separate server
(INPUT). All keyboard and mouse input from hardware is sent by drivers
to the INPUT server, which either sends it to a process that has
opened a raw input device, or otherwise forwards it to TTY for
standard processing.
Design by Dirk Vogt. Prototype by Uli Kastlunger.
Additional changes made to the prototype:
- the event communication is now based on USB HID codes; all input
drivers have to use USB codes to describe events;
- all TTY keymaps have been converted to USB format, with the effect
that a single keymap covers all keys; there is no (static) escaped
keymap anymore;
- further keymap tweaks now allow remapping of literally all keys;
- input device renumbering and protocol rewrite;
- INPUT server rewrite, with added support for cancel and select;
- PCKBD reimplementation, including PC/AT-to-USB translation;
- support for manipulating keyboard LEDs has been added;
- keyboard and mouse multiplexer devices have been added to INPUT,
primarily so that an X server need only open two devices;
- a new "libinputdriver" library abstracts away protocol details from
input drivers, and should be used by all future input drivers;
- both INPUT and PCKBD can be restarted;
- TTY is now scheduled by KERNEL, so that it won't be punished for
running a lot; without this, simply running "yes" on the console
kills the system;
- the KIOCBELL IOCTL has been moved to /dev/console;
- support for the SCANCODES termios setting has been removed;
- obsolete keymap compression has been removed;
- the obsolete Olivetti M24 keymap has been removed.
Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578
commit 34b46a5c4f4819806da51b7899718eb22ae64da2
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Sep 27 11:56:29 2013 +0000
TTY: allow selecting on translated minors
Due to the existence of /dev/console and /dev/log, and the new
"console=" setting, it is now possible that a single non-PTY object
(e.g. serial) is accessible through two different minor numbers. This
poses a problem when sending late select replies (CDEV_SEL2_REPLY),
because the object's minor number can not be used to identify the
device. Since selecting on such objects through translated minor
numbers is actually required, we now save the minor number used to
initiate the select query in order to send a late reply.
The solution is suboptimal, as it is not possible to use two different
minors to select on the same object at once. In the future, there
should be at least one select record for each minor that can be used
with each object.
Change-Id: I4d39681d2ffd68b4047daf933d45b7bafe3c885e
commit f358ba2c481fb86fd02017fd6b36066ea8f61f79
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 21 17:35:15 2013 +0200
Take LOG out of the boot image
Change-Id: Id2629776b53aae46629b04a42c15cbbacac9b949
commit 6d9c0948daa9d081c4cebabd8bc05d9002fbe344
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 21 15:03:20 2013 +0200
Kernel: make SIGKMESS target process list dynamic
The set of processes to which a SIGKMESS signal is sent whenever new
diagnostics messages are added to the kernel's message buffer, is now
no longer hardcoded. Instead, processes can (un)register themselves
to receive such notifications, by means of sys_diagctl().
Change-Id: I9d6ac006a5d9bbfad2757587a068fc1ec3cc083e
commit 41a5194722435a573ba3f5a42ae745164ba5d4b2
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 21 00:58:46 2013 +0200
Rename SYSCTL kernel call to DIAGCTL
Change-Id: I1b17373f01808d887dcbeab493838946fbef4ef6
commit 0d370e8db6a9bd0dd180179133745bfdce69a7b9
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 20:13:59 2013 +0200
Prevent Jenkins from breaking on old df(1)
Change-Id: Ie8e1758bd9de4d5c95c597302f80d568058fbb68
commit f3f35bf3b108e43df7f8deeacadbfac61ba65c37
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 14:09:47 2013 +0200
Add testvnd.sh test script
As part of this, change the "run" script to allow certain scripts to
be run as root only.
Change-Id: I846e41037f9d4f6c7fc0b5ea8250303a7bd72f5d
commit 2f200b12e2cc00b9cfa5bfb7cc2e4627799f4080
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 14:06:46 2013 +0200
Import NetBSD vndconfig(8)
The tool has been changed heavily to match our VND driver model.
NetBSD is in the process of renaming it from vnconfig(8) to
vndconfig(8). To keep things in sync, we have to play along.
Change-Id: Ie86df184f03ab00573ea76b43c9caa0412e8321d
commit d5998fd28e51eb21e211c326bdd1b84322700382
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 14:02:17 2013 +0200
Add VND driver, providing loopback devices
Change-Id: I40fa695e28c67477a75383e6f1550e451afcab41
commit 318e281a2827cf10acb024822976d107267d5c94
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 13:55:15 2013 +0200
VFS: add dupfrom(2) call
This call copies a file descriptor from a remote process into the
calling process. The call is for the VND driver only, and in the
future, ACLs will prevent any other process from using this call.
Change-Id: Ib16fdd1f1a12cb38a70d7e441dad91bc86898f6d
commit cf669ab888662d4152d8b4353e4166775dca08d4
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 11:46:08 2013 +0200
tests: do not skip installed shell tests
When installed, the test scripts lose their ".sh" suffix, causing them
to be skipped by the "run" script. With this patch, the tests are no
longer specified with ".sh" suffix in the run script, and the suffix
is added automatically as necessary.
Change-Id: I0b72312e79992b9818559c6546a0e52cd95184c2
commit 36cfed7ef5aa345e25704f959d08afb6e3d0a5d3
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 13:41:56 2013 +0200
blocktest: prepare to be run as part of tests
- fail SEF initialization if any of the subtests failed, so that the
party invoking the "service up" can tell whether the test succeeded;
- add "nocontig" option, because VM isn't particularly good at
allocating contiguous memory;
- add "silent" option, because it floods the console otherwise;
- allow the device size to be smaller than the maximum transfer size;
- install files to installed test directory.
Change-Id: I45c818f817c11d90c5f94ae26a2fc49e36e6761e
commit bd282f39f7d646afe1025563044f3bcc278c2e3c
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 18 13:38:36 2013 +0200
Enable devname(3)
There is no support for a device name database yet, so this call is
expected to be fairly slow.
Change-Id: I73aa5f267e2b6921b7d3bbdcc4beac463931132c
commit c1e5ff56bdd2e760dc00ba2f89e9b4a615c89c78
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Sep 15 13:09:00 2013 +0200
libbdev: be less noisy about clean driver restarts
Change-Id: Ie02a459c9b544d361ab00bac431ef99de53b0c5f
commit 27dd4c8924afabd3118e3988607f99871d8500ac
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Sep 14 14:43:53 2013 +0200
Straighten ioctl.h
- include all ioctl subheaders, properly listing all letters;
- change FBD's ioctl calls to use 'B' instead of 'F', in
preparation of the VND driver.
Change-Id: Ia718979568cc057f47cf505a89238d5b3b6695d4
commit 8fa683c5fc30c354fee376ef15158f79a7ae2e3c
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Sep 15 18:55:42 2013 +0200
VM: readd support for forgetting cached FS blocks
Not all services involved in block I/O go through VM to access the
blocks they need. As a result, the blocks in VM may become stale,
possibly causing corruption when the stale copy is restored by a
service that does go through VM later on. This patch restores support
for forgetting cached blocks that belong to a particular device, and
makes the relevant file systems use this functionality 1) when
requested by VFS through REQ_FLUSH, and 2) upon unmount.
Change-Id: I0758c5ed8fe4b5ba81d432595d2113175776aff8
commit de1b9ea6e0387ad002cc709bfc440c0b7b37a514
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 14:50:18 2013 +0200
filter: use libblockdriver
Change-Id: Ifbca2482e996ddca58036d45f557165e636fb3fa
commit 1ac96993033b39dfee21eabd3eb9ff2d6b8df97c
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 20:25:01 2013 +0200
Rewrite character driver protocol
As a side effect, remove the clone style, as the normal device style
supports device cloning now.
Change-Id: Ie82d1ef0385514a04a8faa139129a617895780b5
commit c6ee04eaec08b3101c440a9b84a4d728285586c3
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 16:06:37 2013 +0200
Remove support for reopening character devices
Previously, VFS would reopen a character device after a driver crash
if the associated file descriptor was opened with the O_REOPEN flag.
This patch removes support for this feature. The code was complex,
full of uncovered corner cases, and hard to test. Moreover, it did not
actually hide the crash from user applications: they would get an
error code to indicate that something went wrong, and have to decide
based on the nature of the underlying device how to continue.
- remove support for O_REOPEN, and make playwave(1) reopen its device;
- remove support for the DEV_REOPEN protocol message;
- remove all code in VFS related to reopening character devices;
- no longer change VFS filp reference count and FD bitmap upon filp
invalidation; instead, make get_filp* fail all calls on invalidated
FDs except when obtained with the locktype VNODE_OPCL which is used
by close_fd only;
- remove the VFS fproc file descriptor bitmap entirely, returning to
the situation that a FD is in use if its slot points to a filp; use
FILP_CLOSED as single means of marking a filp as invalidated.
Change-Id: I34f6bc69a036b3a8fc667c1f80435ff3af56558f
commit ba7c49ff69b09bfe5301774c1d2f41a0f75c2829
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 12:19:08 2013 +0200
VFS: rework device code
- block the calling thread on character device close;
- fully separate block and character open/close routines;
- reuse generic open/close code for the cloning case;
- zero all messages to drivers before filling them;
- use appropriate types for major/minor device numbers.
Change-Id: Ia90e6fe5688f212f835c5ee1bfca831cb249cf51
commit 6244eaf06240f02833f21313f788e3087f3dccf0
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 9 00:04:12 2013 +0200
Make PFS backcalls regular VFS calls
- prefix them with VFS_ as they are going to VFS;
- give these calls normal call numbers;
- give them their own set of message field aliases;
- also make do_mapdriver a regular call.
Change-Id: I2140439f288b06d699a1f65438bd8306509b259e
commit 29a324bc6ced0a8c5f7efa604214aa1bc26d2ff5
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 12:48:10 2013 +0200
TTY: use libchardriver; clean up
- writing to a PTY master side blocks if there is not already a
blocked reader on the slave side, and select now reflects this;
- internally, TTY now uses a test based on "caller != NONE" rather
than "grant != GRANT_INVALID" to identify whether a call is
currently ongoing;
- "offset" fields have been removed as they equal the corresponding
"cum" fields;
- improved variable typing and function naming here and there;
- various other small fixes.
Change-Id: I6b51452888942e864b4e034e8c8490576184a23e
commit 77ac2ac06355dc2aac382761f9a9fb28c1129bd1
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 01:13:59 2013 +0200
VFS: select(2) fixes
- check each file descriptor's open access mode (filp_mode);
- treat an error returned by a character driver as a select error;
- check all filps in each set before finishing select;
- do not copy back file descriptor sets if an error occurred;
- remove the hardcoded list of supported character major devices,
since all drivers should now be capable of responding properly;
- add tests to test40 and fix its error count aggregation.
Change-Id: I57ef58d3afb82640fc50b59c859ee4b25f02db17
commit 52008671f66c28199b28f9203349777321f46297
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 01:07:28 2013 +0200
Retire EBADIOCTL in favor of ENOTTY
Change-Id: I6bd0e301d21ab7f2336e350e7e6e15d238c2c93d
commit 5495625b6fec35117c99b137bf141904c1355f4a
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 4 15:42:36 2013 +0000
libnetsock: use libchardriver
Change-Id: Ia5b780cad0b0c636db9bd866c7223da0d38ef6ea
commit d953501984b8f54ad8670df7337c5632a48e1820
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 00:50:36 2013 +0200
LWIP: move chardev message parsing into libnetsock
Change-Id: Ie23fd003c9fa35811548f388c8e9b55e8d9de8d7
commit 57043b1d6c87892d222afafe07d50b52a7ff1b35
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 9 14:53:28 2013 +0000
INET: use libchardriver
Change-Id: Icf8a1a5769ce0aede1cc28da99b9daf7d328182c
commit f5fb4390e6b217897f09cfe92aa34053bbf56c24
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 02:00:20 2013 +0200
PFS: use libchardriver; clean up
- simplify and repair UDS request handling state machine;
- simplify interface used between internal modules;
- implement missing support for nonblocking I/O;
- fix select implementation;
- clean up global variables.
Change-Id: Ia82c5c6f05cc3f0a498efc9a26de14b1cde6eace
commit 1d6a7a20e800dde581377d05a58673ef7486b61a
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 01:59:20 2013 +0200
libaudiodriver: use libchardriver
Change-Id: I299d58d110ad14b69076276ba46c4325875c34ca
commit c7ed4e35b4d7e59305815c0f701522948749874d
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 01:57:41 2013 +0200
printer: use libchardriver
Change-Id: Ifa3cabeada74c32df2613b8279c00f86e831c775
commit b48201c328cb31d49a968ae7a97c7f40270e4e47
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 3 01:49:38 2013 +0200
libchardriver: full API rewrite
The new API now covers the entire character driver protocol, while
hiding all the message details. It should therefore be used by all
new character drivers. All existing drivers that already made use of
libchardriver have been changed to use the new API.
As one of the most important API changes, support for scatter and
gather transfers has been removed, as several key drivers already
did not support this, and it could be supported at the safecopy
level instead (for a future readv/writev).
Additional changes include:
- respond to block device open requests to avoid hanging VFS threads;
- add support for sef_cancel.
Change-Id: I1bab6c1cb66916c71b87aeb1db54a9bdf171fe6b
commit c6682c308a5a97e6fd75dbf32ad3efd23a2b8a35
Author: David van Moolenbroek <david@minix3.org>
Date: Thu Aug 1 18:20:33 2013 +0200
blocktest: add support for no alignment
Some block drivers do not impose any alignment requirements, and this
patch allows such block drivers to pass the test set. As a side effect,
minimal support for min_write is added, but this part of blocktest is
in need of further improvement.
commit 3593e262bc06e6e49fe2cb4a7b42bfab460986c8
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Apr 18 00:04:28 2012 +0200
libutil: let opendisk(3) try /dev
If a device node is given without path, and opening the node fails
initially, prepend "/dev/" to the node name and try opening again.
This is more in line with NetBSD behavior.
commit de1dd17ebcc546dc6341e595cee6b5e1180ade64
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 23:57:32 2013 +0200
Block drivers: make IOCTL request unsigned long
The block driver protocol and libblockdriver's bdr_ioctl hook are
changed, as well as the users of this hook. Other parts of the system
are expected to change accordingly eventually, since the ioctl(2)
prototype has been aligned with NetBSD's.
Change-Id: Ide46245b22cfa89ed267a38088fb0ab7696eba92
commit cc8c282ae20f774d72fb5b1473e44aaf40a00f99
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Jul 27 00:49:49 2013 +0200
Block protocol: add user endpoint to IOCTL request
I/O control requests now come with the endpoint of the user process
that initiated the ioctl(2) call. It is stored in a new BDEV_USER
field, which is an alias for BDEV_FLAGS. The contents of this field
are to be used only in highly specific situations. It should be
preserved (not replaced!) by services that forward IOCTL requests,
and may be set to NONE for service-initiated IOCTL requests.
Change-Id: I68a01b9ce43eca00e61b985a9cf87f55ba683de4
commit 2838190944c3d2320d5aac28ae1418da5a90d0a5
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Jul 27 00:49:49 2013 +0200
Block protocol: use own [RW]_BIT definitions
The original R_BIT and W_BIT definitions have nothing to do with the
way these bits are used. Their distinct usage is more apparent when
they have different names.
Change-Id: Ia984457f900078b2e3502ceed565fead4e5bb965
commit f54a06c5fba8b96bbaba43942f51a73c1ee917b4
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Sep 10 23:35:15 2013 +0200
libblockdriver: expose BLOCKDRIVER_MAX_DEVICES
This constant determines the range of valid device_id_t values that
a block driver can return from the bdr_device hook: a value between
0 and (BLOCKDRIVER_MAX_DEVICES - 1) inclusive.
Change-Id: I80fac469e88ac13d4b869007e6f2c2f7569da433
commit 2032299e680852b80fe3969130d46550bf89d8be
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Sep 11 13:33:00 2013 +0200
libblockdriver: various updates
- internal structure rearrangement;
- respond to char device open requests to avoid hanging VFS threads;
- make drivers use designated initializers;
- use devminor_t for all minor device numbers;
- change bdr_other hook to take ipc_status and return nothing;
- fix default geometry computation;
- add support for sef_cancel.
Change-Id: Ia063a136a3ddb2b78de36180feda870605753d70
commit 35504ddaed58ee747ed3e11e29bb1d793d071445
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Sep 1 14:34:17 2013 +0200
Block drivers: reply ENOTTY to unknown IOCTLs
Change-Id: Ie2e82d2491d546f4dd73b009100646e249a147b5
commit 45dc155af3b0f9993fd02025866ed7c824b94ac6
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Jul 28 14:03:07 2013 +0200
Move SUB_PER_DRIVE definition into minix/drvlib.h
commit c15b4f7c771f5486a9766aa0fec866eb8c7489c2
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 2 17:34:44 2013 +0200
VFS: update filp_pos on chardev I/O (workaround)
Previously, reading from or writing to a character device would not
update the file position on the corresponding filp object. Performing
this update correctly is not trivial: during and after the I/O
operation, the filp object must not be locked. Ideally, read/write
requests on a filp that is already involved in a read/write operation,
should be queued. For now, we optimistically update the file position
at the start of the I/O; this works under the assumptions listed in
the corresponding comment.
Change-Id: I172a61781850423709924390ae3df1f2d1f94707
commit 986012fcf657eabd9a89b45e81277009ce0cf47c
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Sep 2 13:45:02 2013 +0200
I2C: change BUSC_I2C_xxx to use own protocol
Previously it would use bits of the character driver protocol, which
will change heavily. In the new situation, the BUSC_I2C_xxx requests
use a protocol more in line with the PCI protocol, with the reply code
in m_type.
Change-Id: I51597b3f191078c8178ce17372de123031f7a4c4
commit 38b010966e38a119b4ceb6ec7b5e0e1ae5df86c0
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 31 16:13:37 2013 +0200
tests: add test77 for opening/closing PTYs
Change-Id: I30e3418f75137aa08037fa6581ff3d4cce32a114
commit 40b4156afb5ca47b4cac51a503790a65528a3efa
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 31 16:08:25 2013 +0200
TTY: fix for PTY open/close logic
Opening and closing the master side of a pseudo terminal without
opening the slave side would result in the pseudo terminal becoming
permanently unavailable. In addition, reopening the slave side
would be possible but not allow for I/O. Finally, attempting to
open an in-use master would wipe its I/O state. These issues have
been resolved.
Change-Id: I9235e3d9aba321803f9280b86b6b5e3646ad5ef3
commit 3fad8d48237c70c5d26cb12af1214e96ae3b547e
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 18:48:56 2013 +0200
tests: remove select subdirectory
This test set has been obsoleted by test40.
Change-Id: I55439152824906778ad07d409dfb327ac10bea70
commit 6b913b2d113c9faa5b5adbcd01fd6c5c7490ce7c
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 18:43:23 2013 +0200
tests: add test76 for interrupting VFS operations
Change-Id: Ic436cac61de8c42e0c7ee2d442c647528654cde9
commit e269f19d9f1087db57b70b7794a8d36d51a67aae
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Aug 28 13:08:16 2013 +0200
VFS: fix interruption of blocking pipe operations
POSIX states that when interrupted, partially successful pipe
operations should return the partial result rather than EINTR. VFS
previously wouldn't look at the partial result, and not clear it
either, which would result in a panic upon the next pipe operation.
Change-Id: Ia1eb72b4b77394051444e63a1390d49bb315eb04
commit 2ba8242dd73b4854d064caa6f3059cddec5a64fc
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 00:57:16 2013 +0200
libmthread: do not dump stack for free threads
Change-Id: Ic438a252f5bddaf1513f554c71173e6fffb0c674
commit 7e2989e2bf14230965767d7ef1bf58192c3f6d9c
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 14:00:50 2013 +0200
VFS: worker thread model overhaul
The main purpose of this patch is to fix handling of unpause calls
from PM while another call is ongoing. The solution to this problem
sparked a full revision of the threading model, consisting of a large
number of related changes:
- all active worker threads are now always associated with a process,
and every process has at most one active thread working for it;
- the process lock is always held by a process's worker thread;
- a process can now have both normal work and postponed PM work
associated to it;
- timer expiry and non-postponed PM work is done from the main thread;
- filp garbage collection is done from a thread associated with VFS;
- reboot calls from PM are now done from a thread associated with PM;
- the DS events handler is protected from starting multiple threads;
- support for a system worker thread has been removed;
- the deadlock recovery thread has been replaced by a parameter to the
worker_start() function; the number of worker threads has
consequently been increased by one;
- saving and restoring of global but per-thread variables is now
centralized in worker_suspend() and worker_resume(); err_code is now
saved and restored in all cases;
- the concept of jobs has been removed, and job_m_in now points to a
message stored in the worker thread structure instead;
- the PM lock has been removed;
- the separate exec lock has been replaced by a lock on the VM
process, which was already being locked for exec calls anyway;
- PM_UNPAUSE is now processed as a postponed PM request, from a thread
associated with the target process;
- the FP_DROP_WORK flag has been removed, since it is no longer more
than just an optimization and only applied to processes operating on
a pipe when getting killed;
- assignment to "fp" now takes place only when obtaining new work in
the main thread or a worker thread, when resuming execution of a
thread, and in the special case of exiting processes during reboot;
- there are no longer special cases where the yield() call is used to
force a thread to run.
Change-Id: I7a97b9b95c2450454a9b5318dfa0e6150d4e6858
commit 67ef9a11c2735b3fba17efcde2893e79fe64e6cd
Author: David van Moolenbroek <david@minix3.org>
Date: Wed Aug 28 15:58:30 2013 +0200
Retire ptrace(T_DUMPCORE), dumpcore(1), gcore(1)
The T_DUMPCORE implementation was not only broken - it would currently
produce a coredump of the tracer process rather than the traced
process - but also deeply flawed, and fixing it would require serious
alteration of PM's internal state machine. It should be possible to
implement the same functionality in userland, and that is now the
suggested way forward. For now, also remove the (identical) utilities
using T_DUMPCORE: dumpcore(1) and gcore(1).
Change-Id: I1d51be19c739362b8a5833de949b76382a1edbcc
commit 7016b1cd1b7932a91d7a82ac7ff903cd136f4861
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 13:42:51 2013 +0200
VFS: process char driver replies from main thread
Previously, processing of some replies coming from character drivers
could block on locks, and therefore, such processing was done from
threads that were associated to the character driver process. The
hidden consequence of this was that if all threads were in use, VFS
could drop replies coming from the driver. This patch returns VFS to
a situation where the replies from character drivers are processed
instantly from the main thread, by removing the situations that may
cause VFS to block while handling those replies.
- change the locking model for select, so that it will never block
on any processing that happens after the select call has been set
up, in particular processing of character driver select replies;
- clearly mark all select routines that may never block;
- protect against race conditions in do_select as result of the
locking that still does happen there (as is required for pipes);
- also handle select timers from the main thread;
- move processing of character driver replies into device.c.
Change-Id: I4dc8e69f265cbd178de0fbf321d35f58f067cc57
commit 9aa8d1e3f53c08a9521258d27fa121219255ed3c
Author: David van Moolenbroek <david@minix3.org>
Date: Sun Aug 25 00:26:38 2013 +0200
VFS: properly cancel select queries on unpause
Change-Id: I16e71db3f5c1bcc7ba6045bc9f02b13d71dc31eb
commit 538bb4cecfbad4cc8b9f4204f9912e70a5f61ccf
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 13:33:56 2013 +0200
VFS: remove support for sync char driver protocol
Change-Id: I57cc870a053b813b3a3fc45da46606ea84fe4cb1
commit c1e783a7e130c75e9bfdfa73553162a1d795fc73
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 13:00:44 2013 +0200
VFS: remove FP_BLOCKED_ON_DOPEN
These days, DEV_OPEN calls to character drivers block the calling
thread until completion or failure, and thus never return SUSPEND to
the caller. The same already applied to BDEV_OPEN calls to block
drivers. It has thus become impossible for a process to enter a state
of being blocked on a device open call.
There is currently no support for restarting device open calls to
restarted character drivers. This support was present in the _DOPEN
logic, but was already no longer triggering. In the future, this case
should be handled by the thread performing the open request.
Change-Id: I6cc1e7b4c9ed116c6ce160b315e6e060124dce00
commit 42a9a183f74cc198cd7ae39b8270beeb58578c7d
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 12:18:27 2013 +0200
PFS: remember request information for IOCTLs
Not doing so caused PFS to commit protocol violations by relying on
stale information when sending replies. This stale information always
happened to be correct, which is why the problem went unnoticed.
Change-Id: Ia42ca670718d6e731193cd2c34a3ff455f8a94d3
commit e02b1fbdfb51099c708836e6a00268b10b9bb7c2
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 11:14:03 2013 +0200
Retire the synchronous character driver protocol
- change all sync char drivers into async drivers;
- retire support for the sync protocol in libchardev;
- remove async dev style, as this is now the default;
- remove dev_status from VFS;
- clean up now-unused protocol messages.
Change-Id: I6aacff712292f6b29f2ccd51bc1e7d7003723e87
commit 46d1a1098a2a7d5ca7c4110b7d24212f7d14dab4
Author: David van Moolenbroek <david@minix3.org>
Date: Fri Aug 30 10:48:34 2013 +0200
Sync char protocol: add nonblocking transfer flag
The async char protocol already has this, so this patch closes the
gap between the two protocols a bit. Support for this flag has been
added to all sync char drivers that support CANCEL at all.
The LOG driver was already using the asynchronous protocol, but it
did not support the nonblocking transfer flag. This has been fixed
as well.
Change-Id: Ia55432c9f102765b59ad3feb45a8bd47a782c93f
commit 4c2553666f86231eb0a0124b52f542fea66c23be
Author: David van Moolenbroek <david@minix3.org>
Date: Sat Aug 24 12:29:39 2013 +0200
VFS: set w_drv_sendrec only when needed
As with w_task, this ensures that the field remains cleared if it is
not used. Without this, worker_stop could mistakenly identify a thread
as talking to a device driver rather than a (crashed) file server.
Change-Id: I7d3ebed3efc3cd4f5c891f61c67a6463109b6376
commit 37e76dea4202e58cd918738b6b13f205c29c5623
Author: Thomas Veerman <thomas@minix3.org>
Date: Sat Aug 24 12:23:41 2013 +0200
VFS: set w_task only when needed
It was always set, but not always cleared, when talking to asynchronous
drivers. This could cause erratic behavior upon a driver crash.
Normally, a worker thread's w_task field is set when it's about to
communicate with a driver or FS. Then upon receiving a reply we can
do sanity checks (that the thread we want to wake up was actually
waiting for a reply). Also, when a driver/FS crashes, we can identify
which worker threads were talking to the crashed endpoint and handle
the error gracefully.
Asynchronous drivers are a bit special, though. In most cases, the
sender of the request is not interested in the reply (the sender was
suspended and only wants to know whether the request was successfully
caried out or not). However, the open request is special, as the reply
carries information needed by the sender. This is the only request
where a worker thread actually yields and waits for the result. This is
also the only case where we're interested in setting w_task for
asynchronous drivers.
Change-Id: Ia1ce2747937df376122b5e13b6a069de27fcc379
commit e990e9c7f067ac67fd455c4a4eec472702a01929
Author: David van Moolenbroek <david@minix3.org>
Date: Mon Aug 19 20:34:15 2013 +0200
Import NetBSD df(1)
Change-Id: Ia60f8b23b961e4132efece1e7e38f0d63597c13b
commit fce9d229ff353aa132bf9f72c5f0262a20be4e1b
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:33:43 2013 +0200
Enable getmntinfo(3)
Change-Id: Id9d11a67cdcad331a52030127f7c7ead3c847919
commit 598d91b5fd73ffefa6702798ed4c5c1e01722e0e
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 00:49:07 2013 +0200
test55: add tests for getvfsstat(2)
Change-Id: Iad4567068a82bb0e780891a478ba5a06b63f1d48
commit 52b521faa0aa9ba5236a890b3a42ac0bc0a0d1bd
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:28:23 2013 +0200
Implement support for [f]statvfs1(2)
The [f]statvfs(3) calls now use [f]statvfs1(2).
Change-Id: I56c92fe1c70670f476631c4a50e47351562712c6
commit ca5da8f6c0e5d9515ddc4adaf3e8515740859d3e
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:39:47 2013 +0200
Implement support for getvfsstat(2)
Change-Id: I4680c071b94fa855edfb6220b03cff6b20f3c1c9
commit a269463c47cfb4b9223087da16bc0608a37fc16c
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:37:18 2013 +0200
Redo mount(2)/umount(2) ABI
- pass in file system type through mount(2), and return this type in
statvfs structures as generated by [f]statvfs(2);
- align mount flags field with NetBSD's, splitting out service flags
which are not to be passed to VFS;
- remove limitation of mount ABI to 16-byte labels, so that labels
can be made larger in the future;
- introduce new m11 message union type for mount(2) as side effect.
Change-Id: Ia9ed4566d88af5239749c57274312be26bb61448
commit e94b0816548a65beb37c09837fd8c51aa922f736
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 01:35:35 2013 +0200
Align "struct statvfs" with NetBSD
This is a requirement for implementing calls such as getmntinfo(3).
VFS is now responsible for filling in some of the structure's fields.
Change-Id: I457b463c70ea2f326b9c50de954a91cfefba0065
commit da756158a15ac245ceff5e35517fa1621d55ad64
Author: David van Moolenbroek <david@minix3.org>
Date: Tue Aug 20 00:55:49 2013 +0200
VFS/FS: remove fstatfs(2) and REQ_FSTATFS
The fstatfs(3) call now uses fstatvfs(2).
Change-Id: Ic6bbb040e1a8f01aaffe1a6a809dd4c7a2eae2f6
Change-Id: I14141beaf9804e10d9cf3de706bdc9b9d9cc947b
2938 lines
62 KiB
C
2938 lines
62 KiB
C
/*
|
|
* 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()
|
|
*/
|
|
|
|
#define DEBUG 0
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/un.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
/* 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"
|
|
|
|
|
|
/* 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"
|
|
|
|
/* buffer for send/recv */
|
|
#define BUFSIZE (128)
|
|
|
|
#define ISO8601_FORMAT "%Y-%m-%dT%H:%M:%S"
|
|
|
|
/* socket types supported */
|
|
int types[3] = {SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM};
|
|
char sock_fullpath[PATH_MAX + 1];
|
|
|
|
void test_abort_client_server(int abort_type);
|
|
void test_abort_client(int abort_type);
|
|
void test_abort_server(pid_t pid, int abort_type);
|
|
|
|
/* timestamps for debug and error logs */
|
|
char *get_timestamp(void)
|
|
{
|
|
struct tm *tm;
|
|
time_t t;
|
|
size_t len;
|
|
char *s;
|
|
|
|
len = sizeof(char) * 32;
|
|
|
|
t = time(NULL);
|
|
if (t == -1) {
|
|
return NULL;
|
|
}
|
|
tm = gmtime(&t);
|
|
if (tm == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
s = (char *) malloc(len);
|
|
if (!s) {
|
|
perror("malloc");
|
|
return NULL;
|
|
}
|
|
memset(s, '\0', len);
|
|
|
|
strftime(s, len - 1, ISO8601_FORMAT, tm);
|
|
return s;
|
|
}
|
|
|
|
/* macro to display information about a failed test and increment the errct */
|
|
void test_fail_fl(char *msg, char *file, int line)
|
|
{
|
|
char *timestamp;
|
|
timestamp = get_timestamp();
|
|
if (errct == 0) fprintf(stderr, "\n");
|
|
fprintf(stderr, "[ERROR][%s] (%s Line %d) %s [pid=%d:errno=%d:%s]\n",
|
|
timestamp, file, line, msg, getpid(),
|
|
errno, strerror(errno));
|
|
fflush(stderr);
|
|
if (timestamp != NULL) {
|
|
free(timestamp);
|
|
timestamp = NULL;
|
|
}
|
|
e(7);
|
|
}
|
|
#define test_fail(msg) test_fail_fl(msg, __FILE__, __LINE__)
|
|
|
|
/* Convert name to the full path of the socket. Assumes name is in cwd. */
|
|
char *fullpath(char *name)
|
|
{
|
|
char cwd[PATH_MAX + 1];
|
|
|
|
if (realpath(".", cwd) == NULL)
|
|
test_fail("Couldn't retrieve current working dir");
|
|
|
|
snprintf(sock_fullpath, PATH_MAX, "%s/%s", cwd, name);
|
|
|
|
return(sock_fullpath);
|
|
}
|
|
|
|
#if DEBUG == 1
|
|
/* macros to display debugging information */
|
|
void debug_fl(char *msg, char *file, int line)
|
|
{
|
|
char *timestamp;
|
|
timestamp = get_timestamp();
|
|
fprintf(stdout,"[DEBUG][%s] (%s:%d) %s [pid=%d]\n",
|
|
timestamp, __FILE__, __LINE__, msg, getpid());
|
|
fflush(stdout);
|
|
if (timestamp != NULL) {
|
|
free(timestamp);
|
|
timestamp = NULL;
|
|
}
|
|
}
|
|
#define debug(msg) debug_fl(msg, __FILE__, __LINE__)
|
|
#else
|
|
#define debug(msg)
|
|
#endif
|
|
|
|
#define SOCKET(sd,domain,type,protocol) \
|
|
do { \
|
|
errno = 0; \
|
|
sd = socket(domain, type, protocol); \
|
|
if (sd == -1) { \
|
|
test_fail("sd = socket(domain, type, protocol) failed");\
|
|
} \
|
|
} while (0)
|
|
|
|
#define UNLINK(path) \
|
|
do { \
|
|
int rc; \
|
|
errno = 0; \
|
|
rc = unlink(path); \
|
|
if (rc == -1 && errno != ENOENT) { \
|
|
test_fail("unlink(path) failed"); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define SYMLINK(oldpath,newpath) \
|
|
do { \
|
|
int rc; \
|
|
errno = 0; \
|
|
rc = symlink(oldpath,newpath); \
|
|
if (rc == -1) { \
|
|
test_fail("symlink(oldpath,newpath) failed"); \
|
|
} \
|
|
} while(0)
|
|
|
|
#define CLOSE(sd) \
|
|
do { \
|
|
int rc; \
|
|
errno = 0; \
|
|
rc = close(sd); \
|
|
if (rc == -1) { \
|
|
test_fail("close(sd) failed"); \
|
|
} \
|
|
} while (0)
|
|
|
|
void test_socket(void)
|
|
{
|
|
struct stat statbuf, statbuf2;
|
|
int sd, sd2;
|
|
int rc;
|
|
int i;
|
|
|
|
debug("entering test_socket()");
|
|
|
|
debug("Test socket() with an unsupported address family");
|
|
|
|
errno = 0;
|
|
sd = socket(-1, SOCK_STREAM, 0);
|
|
if (!(sd == -1 && errno == EAFNOSUPPORT)) {
|
|
test_fail("socket");
|
|
if (sd != -1) {
|
|
CLOSE(sd);
|
|
}
|
|
}
|
|
|
|
debug("Test socket() with all available 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");
|
|
}
|
|
}
|
|
|
|
errno = 0;
|
|
sd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (!(sd == -1 && errno == EMFILE)) {
|
|
test_fail("socket() call with all fds open should fail");
|
|
if (sd != -1) {
|
|
CLOSE(sd);
|
|
}
|
|
}
|
|
|
|
for (i = 3; i < getdtablesize(); i++) {
|
|
CLOSE(i);
|
|
}
|
|
|
|
debug("Test socket() with an mismatched protocol");
|
|
|
|
errno = 0;
|
|
sd = socket(PF_UNIX, SOCK_STREAM, 4);
|
|
if (!(sd == -1 && errno == EPROTONOSUPPORT)) {
|
|
test_fail("socket() should fail with errno = EPROTONOSUPPORT");
|
|
if (sd != -1) {
|
|
CLOSE(sd);
|
|
}
|
|
}
|
|
|
|
debug("Test socket() success");
|
|
|
|
/*
|
|
* open 2 sockets at once and *then* close them.
|
|
* This will test that /dev/uds is cloning properly.
|
|
*/
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
rc = fstat(sd, &statbuf);
|
|
if (rc == -1) {
|
|
test_fail("fstat failed on sd");
|
|
}
|
|
|
|
rc = fstat(sd2, &statbuf2);
|
|
if (rc == -1) {
|
|
test_fail("fstat failed on sd2");
|
|
}
|
|
|
|
|
|
if (statbuf.st_dev == statbuf2.st_dev) {
|
|
test_fail("/dev/uds isn't being cloned");
|
|
}
|
|
|
|
CLOSE(sd2);
|
|
CLOSE(sd);
|
|
|
|
debug("leaving test_socket()");
|
|
}
|
|
|
|
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) != 4) {
|
|
test_fail("SUN_LEN(&sun) should be 4");
|
|
}
|
|
|
|
if (PF_UNIX != PF_LOCAL || PF_UNIX != PF_FILE || PF_UNIX != AF_UNIX) {
|
|
test_fail("PF_UNIX, PF_LOCAL, PF_FILE, and AF_UNIX");
|
|
}
|
|
}
|
|
|
|
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()");
|
|
}
|
|
|
|
void test_ucred(void)
|
|
{
|
|
struct uucred credentials;
|
|
socklen_t ucred_length;
|
|
uid_t euid = geteuid();
|
|
gid_t egid = getegid();
|
|
int sv[2];
|
|
int rc;
|
|
|
|
debug("Test credentials passing");
|
|
|
|
ucred_length = sizeof(struct uucred);
|
|
|
|
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], SOL_SOCKET, SO_PEERCRED, &credentials,
|
|
&ucred_length);
|
|
if (rc == -1) {
|
|
test_fail("getsockopt(SO_PEERCRED) failed");
|
|
} else if (credentials.cr_ngroups != 0 ||
|
|
credentials.cr_uid != geteuid() ||
|
|
credentials.cr_gid != getegid()) {
|
|
/* printf("%d=%d %d=%d %d=%d",credentials.cr_ngroups, 0,
|
|
credentials.cr_uid, geteuid(), credentials.cr_gid, 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.cr_uid != euid || credentials.cr_gid != egid) {
|
|
test_fail("getpeereid() didn't give the correct euid/egid");
|
|
}
|
|
|
|
CLOSE(sv[0]);
|
|
CLOSE(sv[1]);
|
|
}
|
|
|
|
void test_getsockname(void)
|
|
{
|
|
int sd;
|
|
int rc;
|
|
struct sockaddr_un addr, sock_addr;
|
|
socklen_t sock_addr_len;
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
|
|
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");
|
|
}
|
|
|
|
debug("Test getsockname() success");
|
|
|
|
memset(&sock_addr, '\0', sizeof(struct sockaddr_un));
|
|
sock_addr_len = sizeof(struct sockaddr_un);
|
|
|
|
rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len);
|
|
if (rc == -1) {
|
|
test_fail("getsockname() should have worked");
|
|
}
|
|
|
|
if (!(sock_addr.sun_family == AF_UNIX && strncmp(sock_addr.sun_path,
|
|
fullpath(TEST_SUN_PATH),
|
|
sizeof(sock_addr.sun_path) - 1) == 0)) {
|
|
test_fail("getsockname() did return the right address");
|
|
fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path,
|
|
sock_addr.sun_path);
|
|
}
|
|
|
|
CLOSE(sd);
|
|
}
|
|
|
|
void test_bind(void)
|
|
{
|
|
struct sockaddr_un addr;
|
|
struct sockaddr_un sock_addr;
|
|
socklen_t sock_addr_len;
|
|
int sd;
|
|
int sd2;
|
|
int rc;
|
|
|
|
debug("entering test_bind()");
|
|
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() success");
|
|
|
|
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");
|
|
}
|
|
|
|
debug("Test getsockname() success");
|
|
|
|
memset(&sock_addr, '\0', sizeof(struct sockaddr_un));
|
|
sock_addr_len = sizeof(struct sockaddr_un);
|
|
|
|
rc = getsockname(sd, (struct sockaddr *) &sock_addr, &sock_addr_len);
|
|
if (rc == -1) {
|
|
test_fail("getsockname() should have worked");
|
|
}
|
|
|
|
if (!(sock_addr.sun_family == AF_UNIX &&
|
|
strncmp(sock_addr.sun_path,
|
|
fullpath(TEST_SUN_PATH),
|
|
sizeof(sock_addr.sun_path) - 1) == 0)) {
|
|
|
|
test_fail("getsockname() didn't return the right addr");
|
|
fprintf(stderr, "exp: '%s' | got: '%s'\n", addr.sun_path,
|
|
sock_addr.sun_path);
|
|
}
|
|
|
|
debug("Test bind() with a address that has already been bind()'d");
|
|
|
|
SOCKET(sd2, PF_UNIX, SOCK_STREAM, 0);
|
|
errno = 0;
|
|
rc = bind(sd2, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (!((rc == -1) && (errno == EADDRINUSE))) {
|
|
test_fail("bind() should have failed with EADDRINUSE");
|
|
}
|
|
CLOSE(sd2);
|
|
CLOSE(sd);
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
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() with a NULL address");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
errno = 0;
|
|
rc = bind(sd, (struct sockaddr *) NULL, sizeof(struct sockaddr_un));
|
|
if (!((rc == -1) && (errno == EFAULT))) {
|
|
test_fail("bind() should have failed with EFAULT");
|
|
}
|
|
CLOSE(sd);
|
|
|
|
debug("Test bind() using a symlink loop");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SYM_A);
|
|
UNLINK(TEST_SYM_B);
|
|
|
|
SYMLINK(TEST_SYM_A, 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 == 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, strlen(addr.sun_path) + 1);
|
|
if (rc == -1) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
CLOSE(sd);
|
|
UNLINK("foo");
|
|
|
|
debug("leaving test_bind()");
|
|
}
|
|
|
|
void test_listen(void)
|
|
{
|
|
int rc;
|
|
|
|
debug("entering test_listen()");
|
|
|
|
debug("Test listen() with a bad file descriptor");
|
|
|
|
errno = 0;
|
|
rc = listen(-1, 0);
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("listen(-1, 0) should have failed");
|
|
}
|
|
|
|
debug("Test listen() with a non-socket file descriptor");
|
|
|
|
errno = 0;
|
|
rc = listen(0, 0);
|
|
/* Test on errno disabled here: there's currently no telling what this
|
|
* will return. POSIX says it should be ENOTSOCK, MINIX3 libc returns
|
|
* ENOSYS, and we used to test for ENOTTY here..
|
|
*/
|
|
if (!(rc == -1)) {
|
|
test_fail("listen(0, 0) should have failed");
|
|
}
|
|
|
|
debug("leaving test_listen()");
|
|
}
|
|
|
|
void test_shutdown(void)
|
|
{
|
|
int how[3] = { SHUT_RD, SHUT_WR, SHUT_RDWR };
|
|
int sd;
|
|
int rc;
|
|
int i;
|
|
|
|
debug("entering test_shutdown()");
|
|
|
|
/* test for each direction (read, write, read-write) */
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
debug("test shutdown() with an invalid descriptor");
|
|
|
|
errno = 0;
|
|
rc = shutdown(-1, how[i]);
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("shutdown(-1, how[i]) should have failed");
|
|
}
|
|
|
|
debug("test shutdown() with a non-socket descriptor");
|
|
|
|
errno = 0;
|
|
rc = shutdown(0, how[i]);
|
|
if (!(rc == -1 && errno == ENOSYS)) {
|
|
test_fail("shutdown() should have failed with ENOSYS");
|
|
}
|
|
|
|
debug("test shutdown() with a socket that is not connected");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
errno = 0;
|
|
rc = shutdown(sd, how[i]);
|
|
if (!(rc == -1 && errno == ENOTCONN)) {
|
|
test_fail("shutdown() should have failed");
|
|
}
|
|
CLOSE(sd);
|
|
}
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
errno = 0;
|
|
rc = shutdown(sd, -1);
|
|
if (!(rc == -1 && errno == ENOTCONN)) {
|
|
test_fail("shutdown(sd, -1) should have failed with ENOTCONN");
|
|
}
|
|
CLOSE(sd);
|
|
|
|
debug("leaving test_shutdown()");
|
|
}
|
|
|
|
void test_close(void)
|
|
{
|
|
struct sockaddr_un addr;
|
|
int sd, sd2;
|
|
int rc, i;
|
|
|
|
debug("entering test_close()");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
debug("Test close() success");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc != 0) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
|
|
CLOSE(sd);
|
|
|
|
debug("Close an already closed file descriptor");
|
|
|
|
errno = 0;
|
|
rc = close(sd);
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("close(sd) should have failed with EBADF");
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
debug("dup()'ing a file descriptor and closing both should work");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc != 0) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
|
|
errno = 0;
|
|
sd2 = dup(sd);
|
|
if (sd2 == -1) {
|
|
test_fail("dup(sd) should have worked");
|
|
} else {
|
|
CLOSE(sd2);
|
|
CLOSE(sd);
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
/* Create and close a socket a bunch of times.
|
|
* If the implementation doesn't properly free the
|
|
* socket during close(), eventually socket() will
|
|
* fail when the internal descriptor table is full.
|
|
*/
|
|
for (i = 0; i < 1024; i++) {
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
CLOSE(sd);
|
|
}
|
|
|
|
debug("leaving test_close()");
|
|
}
|
|
|
|
void test_sockopts(void)
|
|
{
|
|
int i;
|
|
int rc;
|
|
int sd;
|
|
int option_value;
|
|
socklen_t option_len;
|
|
|
|
debug("entering test_sockopts()");
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
SOCKET(sd, PF_UNIX, types[i], 0);
|
|
|
|
debug("Test setsockopt() works");
|
|
|
|
option_value = 0;
|
|
option_len = sizeof(option_value);
|
|
errno = 0;
|
|
rc = getsockopt(sd, SOL_SOCKET, SO_TYPE, &option_value,
|
|
&option_len);
|
|
if (rc != 0) {
|
|
test_fail("setsockopt() should have worked");
|
|
}
|
|
|
|
if (option_value != types[i]) {
|
|
test_fail("SO_TYPE didn't seem to work.");
|
|
}
|
|
|
|
CLOSE(sd);
|
|
}
|
|
|
|
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
debug("Test setsockopt() works");
|
|
|
|
option_value = 0;
|
|
option_len = sizeof(option_value);
|
|
errno = 0;
|
|
rc = getsockopt(sd, SOL_SOCKET, SO_SNDBUF, &option_value, &option_len);
|
|
if (rc != 0) {
|
|
test_fail("getsockopt() should have worked");
|
|
}
|
|
|
|
if (option_value != PIPE_BUF) {
|
|
test_fail("SO_SNDBUF didn't seem to work.");
|
|
}
|
|
|
|
CLOSE(sd);
|
|
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
debug("Test setsockopt() works");
|
|
|
|
option_value = 0;
|
|
option_len = sizeof(option_value);
|
|
errno = 0;
|
|
rc = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &option_value, &option_len);
|
|
if (rc != 0) {
|
|
test_fail("getsockopt() should have worked");
|
|
}
|
|
|
|
if (option_value != PIPE_BUF) {
|
|
test_fail("SO_RCVBUF didn't seem to work.");
|
|
}
|
|
|
|
CLOSE(sd);
|
|
|
|
|
|
debug("leaving test_sockopts()");
|
|
}
|
|
|
|
void test_read(void)
|
|
{
|
|
int rc;
|
|
int fd;
|
|
char buf[BUFSIZE];
|
|
|
|
debug("entering test_read()");
|
|
|
|
errno = 0;
|
|
rc = read(-1, buf, sizeof(buf));
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("read() should have failed with EBADF");
|
|
}
|
|
|
|
fd = open("/tmp", O_RDONLY);
|
|
if (fd == -1) {
|
|
test_fail("open(\"/tmp\", O_RDONLY) should have worked");
|
|
}
|
|
|
|
CLOSE(fd);
|
|
|
|
debug("leaving test_read()");
|
|
}
|
|
|
|
void test_write(void)
|
|
{
|
|
int rc;
|
|
char buf[BUFSIZE];
|
|
|
|
debug("entering test_write()");
|
|
|
|
errno = 0;
|
|
rc = write(-1, buf, sizeof(buf));
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("write() should have failed with EBADF");
|
|
}
|
|
|
|
debug("leaving test_write()");
|
|
}
|
|
|
|
void test_dup(void)
|
|
{
|
|
struct stat info1;
|
|
struct stat info2;
|
|
struct sockaddr_un addr;
|
|
int sd, sd2;
|
|
int rc;
|
|
int i;
|
|
|
|
debug("entering test_dup()");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
debug("Test dup()");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc != 0) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
|
|
errno = 0;
|
|
sd2 = dup(sd);
|
|
if (sd2 == -1) {
|
|
test_fail("dup(sd) should have worked");
|
|
}
|
|
|
|
rc = fstat(sd, &info1);
|
|
if (rc == -1) {
|
|
test_fail("fstat(fd, &info1) failed");
|
|
}
|
|
|
|
rc = fstat(sd2, &info2);
|
|
if (rc == -1) {
|
|
test_fail("fstat(sd, &info2) failed");
|
|
}
|
|
|
|
if (info1.st_ino != info2.st_ino) {
|
|
test_fail("dup() failed info1.st_ino != info2.st_ino");
|
|
}
|
|
|
|
CLOSE(sd);
|
|
CLOSE(sd2);
|
|
|
|
debug("Test dup() with a closed socket");
|
|
|
|
errno = 0;
|
|
rc = dup(sd);
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("dup(sd) on a closed socket shouldn't have worked");
|
|
}
|
|
|
|
debug("Test dup() with socket descriptor of -1");
|
|
|
|
errno = 0;
|
|
rc = dup(-1);
|
|
if (!(rc == -1 && errno == EBADF)) {
|
|
test_fail("dup(-1) shouldn't have worked");
|
|
}
|
|
|
|
debug("Test dup() when all of the file descriptors are taken");
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
for (i = 4; i < getdtablesize(); i++) {
|
|
rc = open("/dev/null", O_RDONLY);
|
|
if (rc == -1) {
|
|
test_fail("we couldn't open /dev/null for read");
|
|
}
|
|
}
|
|
|
|
errno = 0;
|
|
sd2 = dup(sd);
|
|
if (!(sd2 == -1 && errno == EMFILE)) {
|
|
test_fail("dup(sd) should have failed with errno = EMFILE");
|
|
}
|
|
|
|
for (i = 3; i < getdtablesize(); i++) {
|
|
CLOSE(i);
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
debug("leaving test_dup()");
|
|
}
|
|
|
|
void test_dup2(void)
|
|
{
|
|
struct stat info1;
|
|
struct stat info2;
|
|
struct sockaddr_un addr;
|
|
int sd;
|
|
int fd;
|
|
int rc;
|
|
|
|
debug("entering test_dup2()");
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc != 0) {
|
|
test_fail("bind() should have worked");
|
|
}
|
|
|
|
fd = open("/dev/null", O_RDONLY);
|
|
if (fd == -1) {
|
|
test_fail("open(\"/dev/null\", O_RDONLY) failed");
|
|
}
|
|
|
|
fd = dup2(sd, fd);
|
|
if (fd == -1) {
|
|
test_fail("dup2(sd, fd) failed.");
|
|
}
|
|
|
|
memset(&info1, '\0', sizeof(struct stat));
|
|
memset(&info2, '\0', sizeof(struct stat));
|
|
|
|
rc = fstat(fd, &info1);
|
|
if (rc == -1) {
|
|
test_fail("fstat(fd, &info1) failed");
|
|
}
|
|
|
|
rc = fstat(sd, &info2);
|
|
if (rc == -1) {
|
|
test_fail("fstat(sd, &info2) failed");
|
|
}
|
|
|
|
if (!(info1.st_ino == info2.st_ino &&
|
|
major(info1.st_dev) == major(info2.st_dev) &&
|
|
minor(info1.st_dev) == minor(info2.st_dev))) {
|
|
|
|
test_fail("dup2() failed");
|
|
}
|
|
|
|
CLOSE(fd);
|
|
CLOSE(sd);
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
debug("leaving test_dup2()");
|
|
|
|
}
|
|
|
|
/*
|
|
* A toupper() server. This toy server converts a string to upper case.
|
|
*/
|
|
void test_xfer_server(pid_t pid)
|
|
{
|
|
int i;
|
|
struct timeval tv;
|
|
fd_set readfds;
|
|
int status;
|
|
int rc;
|
|
int sd;
|
|
char buf[BUFSIZE];
|
|
socklen_t client_addr_size;
|
|
int client_sd;
|
|
struct sockaddr_un addr;
|
|
struct sockaddr_un client_addr;
|
|
|
|
status = 0;
|
|
rc = 0;
|
|
sd = 0;
|
|
client_sd = 0;
|
|
client_addr_size = sizeof(struct sockaddr_un);
|
|
|
|
memset(&buf, '\0', sizeof(buf));
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
memset(&client_addr, '\0', sizeof(struct sockaddr_un));
|
|
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
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");
|
|
}
|
|
|
|
/* we're ready for connections, time to tell the client to start
|
|
* the test
|
|
*/
|
|
kill(pid, SIGUSR1);
|
|
|
|
tv.tv_sec = 10;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(sd, &readfds);
|
|
|
|
/* use select() in case the client is really broken and never
|
|
* attempts to connect (we don't want to block on accept()
|
|
* forever).
|
|
*/
|
|
rc = select(sd + 1, &readfds, NULL, NULL, &tv);
|
|
if (rc == -1) {
|
|
test_fail("[server] select() should not have failed");
|
|
}
|
|
|
|
if (rc != 1) {
|
|
test_fail("[server] select() should have returned 1");
|
|
printf("[server] select returned %d\n", rc);
|
|
}
|
|
|
|
if (!(FD_ISSET(sd, &readfds))) {
|
|
test_fail("[server] client didn't connect within 10 seconds");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
|
|
client_sd = accept(sd, (struct sockaddr *) &client_addr,
|
|
&client_addr_size);
|
|
|
|
if (client_sd == -1) {
|
|
test_fail("accept() should have worked");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
} else {
|
|
debug("[server] client accept()'d");
|
|
}
|
|
|
|
debug("[server] Reading message");
|
|
rc = read(client_sd, buf, sizeof(buf));
|
|
if (rc == -1) {
|
|
test_fail("read() failed unexpectedly");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
debug("[server] we got the following message:");
|
|
debug(buf);
|
|
|
|
for (i = 0; i < rc && i < 127; i++) {
|
|
buf[i] = toupper(buf[i]);
|
|
}
|
|
|
|
debug("[server] Writing message...");
|
|
rc = write(client_sd, buf, sizeof(buf));
|
|
if (rc == -1) {
|
|
test_fail("write(client_sd, buf, sizeof(buf)) failed");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
|
|
if (rc < strlen(buf)) {
|
|
test_fail("[server] write didn't write all the bytes");
|
|
}
|
|
|
|
memset(&buf, '\0', sizeof(buf));
|
|
|
|
debug("[server] Recv message");
|
|
rc = recv(client_sd, buf, sizeof(buf), 0);
|
|
if (rc == -1) {
|
|
test_fail("recv() failed unexpectedly");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
debug("[server] we got the following message:");
|
|
debug(buf);
|
|
|
|
for (i = 0; i < rc && i < 127; i++) {
|
|
buf[i] = toupper(buf[i]);
|
|
}
|
|
|
|
debug("[server] Sending message...");
|
|
rc = send(client_sd, buf, sizeof(buf), 0);
|
|
if (rc == -1) {
|
|
test_fail("send(client_sd, buf, sizeof(buf), 0) failed");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
|
|
if (rc < strlen(buf)) {
|
|
test_fail("[server] write didn't write all the bytes");
|
|
}
|
|
|
|
memset(&buf, '\0', sizeof(buf));
|
|
|
|
debug("[server] Recvfrom message");
|
|
rc = recvfrom(client_sd, buf, sizeof(buf), 0, NULL, 0);
|
|
if (rc == -1) {
|
|
test_fail("recvfrom() failed unexpectedly");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
debug("[server] we got the following message:");
|
|
debug(buf);
|
|
|
|
for (i = 0; i < rc && i < 127; i++) {
|
|
buf[i] = toupper(buf[i]);
|
|
}
|
|
|
|
debug("[server] Sendto message...");
|
|
rc = sendto(client_sd, buf, sizeof(buf), 0, NULL, 0);
|
|
if (rc == -1) {
|
|
test_fail("sendto() failed");
|
|
kill(pid, SIGKILL);
|
|
return;
|
|
}
|
|
|
|
if (rc < strlen(buf)) {
|
|
test_fail("[server] write didn't write all the bytes");
|
|
}
|
|
|
|
shutdown(client_sd, SHUT_RDWR);
|
|
CLOSE(client_sd);
|
|
|
|
shutdown(sd, SHUT_RDWR);
|
|
CLOSE(sd);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
int server_ready = 0;
|
|
|
|
/* signal handler for the client */
|
|
void test_xfer_sighdlr(int sig)
|
|
{
|
|
debug("entering signal handler");
|
|
switch (sig) {
|
|
/* the server will send SIGUSR1 when it is time for us
|
|
* to start the tests
|
|
*/
|
|
case SIGUSR1:
|
|
server_ready = 1;
|
|
debug("got SIGUSR1, the server is ready for the client");
|
|
break;
|
|
default:
|
|
debug("didn't get SIGUSR1");
|
|
}
|
|
debug("leaving signal handler");
|
|
}
|
|
|
|
/*
|
|
* A toupper() client.
|
|
*/
|
|
void test_xfer_client(void)
|
|
{
|
|
struct uucred credentials;
|
|
socklen_t ucred_length;
|
|
struct timeval tv;
|
|
fd_set readfds;
|
|
struct sockaddr_un addr;
|
|
struct sockaddr_un peer_addr;
|
|
socklen_t peer_addr_len;
|
|
int sd;
|
|
int rc;
|
|
char buf[BUFSIZE];
|
|
|
|
debug("[client] entering test_xfer_client()");
|
|
errct = 0; /* reset error count */
|
|
ucred_length = sizeof(struct uucred);
|
|
memset(&buf, '\0', sizeof(buf));
|
|
|
|
while (server_ready == 0) {
|
|
debug("[client] waiting for the server to signal");
|
|
sleep(1);
|
|
}
|
|
|
|
peer_addr_len = sizeof(struct sockaddr_un);
|
|
|
|
|
|
debug("Creating symlink to TEST_SUN_PATH");
|
|
|
|
SYMLINK(TEST_SUN_PATH, TEST_SYM_A);
|
|
|
|
memset(&addr, '\0', sizeof(struct sockaddr_un));
|
|
strncpy(addr.sun_path, TEST_SYM_A, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
debug("[client] creating client socket");
|
|
SOCKET(sd, PF_UNIX, SOCK_STREAM, 0);
|
|
|
|
debug("[client] connecting to server through the symlink");
|
|
rc = connect(sd, (struct sockaddr *) &addr,
|
|
sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("[client] connect() should have worked");
|
|
} else {
|
|
debug("[client] connected");
|
|
}
|
|
|
|
debug("[client] testing getpeername()");
|
|
memset(&peer_addr, '\0', sizeof(struct sockaddr_un));
|
|
rc = getpeername(sd, (struct sockaddr *) &peer_addr, &peer_addr_len);
|
|
if (rc == -1) {
|
|
test_fail("[client] getpeername() should have worked");
|
|
}
|
|
|
|
/* we need to use the full path "/usr/src/test/DIR_56/test.sock"
|
|
* because that is what is returned by getpeername().
|
|
*/
|
|
|
|
if (!(peer_addr.sun_family == AF_UNIX &&
|
|
strncmp(peer_addr.sun_path,
|
|
fullpath(TEST_SUN_PATH),
|
|
sizeof(peer_addr.sun_path) - 1) == 0)) {
|
|
|
|
test_fail("getpeername() didn't return the right address");
|
|
}
|
|
|
|
strncpy(buf, "Hello, World!", sizeof(buf) - 1);
|
|
debug("[client] send to server");
|
|
rc = write(sd, buf, sizeof(buf));
|
|
if (rc == -1) {
|
|
test_fail("[client] write() failed unexpectedly");
|
|
}
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
debug("[client] read from server");
|
|
rc = read(sd, buf, sizeof(buf));
|
|
if (rc == -1) {
|
|
test_fail("[client] read() failed unexpectedly");
|
|
} else {
|
|
debug("[client] we got the following message:");
|
|
debug(buf);
|
|
}
|
|
|
|
if (strncmp(buf, "HELLO, WORLD!", sizeof(buf)) != 0) {
|
|
test_fail("[client] We didn't get the correct response");
|
|
}
|
|
|
|
memset(&buf, '\0', sizeof(buf));
|
|
strncpy(buf, "Bonjour!", sizeof(buf) - 1);
|
|
|
|
debug("[client] send to server");
|
|
rc = send(sd, buf, sizeof(buf), 0);
|
|
if (rc == -1) {
|
|
test_fail("[client] send() failed unexpectedly");
|
|
}
|
|
|
|
debug("Test passing the client credentials to the server");
|
|
|
|
memset(&credentials, '\0', ucred_length);
|
|
rc = getsockopt(sd, SOL_SOCKET, SO_PEERCRED, &credentials,
|
|
&ucred_length);
|
|
|
|
if (rc == -1) {
|
|
test_fail("[client] getsockopt() failed");
|
|
} else if (credentials.cr_uid != geteuid() ||
|
|
credentials.cr_gid != getegid()) {
|
|
printf("%d=%d=%d %d=%d=%d\n", credentials.cr_uid, getuid(),
|
|
geteuid(), credentials.cr_gid, getgid(), getegid());
|
|
test_fail("[client] Credential passing gave us a bad UID/GID");
|
|
}
|
|
|
|
debug("Testing select()");
|
|
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 500000;
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(sd, &readfds);
|
|
|
|
rc = select(sd + 1, &readfds, NULL, NULL, &tv);
|
|
if (rc == -1) {
|
|
test_fail("[client] select() should not have failed");
|
|
}
|
|
|
|
if (rc != 1) {
|
|
test_fail("[client] select() should have returned 1");
|
|
}
|
|
|
|
if (!(FD_ISSET(sd, &readfds))) {
|
|
test_fail("The server didn't respond within 2.5 seconds");
|
|
}
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
debug("[client] recv from server");
|
|
rc = recv(sd, buf, sizeof(buf), 0);
|
|
if (rc == -1) {
|
|
test_fail("[client] recv() failed unexpectedly");
|
|
} else {
|
|
debug("[client] we got the following message:");
|
|
debug(buf);
|
|
}
|
|
|
|
if (strncmp(buf, "BONJOUR!", sizeof(buf)) != 0) {
|
|
test_fail("[client] We didn't get the right response.");
|
|
}
|
|
|
|
memset(&buf, '\0', sizeof(buf));
|
|
strncpy(buf, "Hola!", sizeof(buf) - 1);
|
|
|
|
debug("[client] sendto to server");
|
|
rc = sendto(sd, buf, sizeof(buf), 0, NULL, 0);
|
|
if (rc == -1) {
|
|
test_fail("[client] sendto() failed");
|
|
}
|
|
|
|
debug("Testing select()");
|
|
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 500000;
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(sd, &readfds);
|
|
|
|
rc = select(sd + 1, &readfds, NULL, NULL, &tv);
|
|
if (rc == -1) {
|
|
test_fail("[client] select() should not have failed");
|
|
}
|
|
|
|
if (rc != 1) {
|
|
test_fail("[client] select() should have returned 1");
|
|
}
|
|
|
|
if (!(FD_ISSET(sd, &readfds))) {
|
|
test_fail("[client] The server didn't respond in 2.5 seconds");
|
|
}
|
|
|
|
memset(buf, '\0', sizeof(buf));
|
|
debug("[client] recvfrom from server");
|
|
rc = recvfrom(sd, buf, sizeof(buf), 0, NULL, 0);
|
|
if (rc == -1) {
|
|
test_fail("[cleint] recvfrom() failed unexpectedly");
|
|
} else {
|
|
debug("[client] we got the following message:");
|
|
debug(buf);
|
|
}
|
|
|
|
if (strncmp(buf, "HOLA!", sizeof(buf)) != 0) {
|
|
test_fail("[client] We didn't get the right response.");
|
|
}
|
|
|
|
debug("[client] closing socket");
|
|
CLOSE(sd);
|
|
|
|
debug("[client] leaving test_xfer_client()");
|
|
exit(errct);
|
|
}
|
|
|
|
void test_xfer(void)
|
|
{
|
|
pid_t pid;
|
|
|
|
UNLINK(TEST_SYM_A);
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
/* 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 == -1) {
|
|
test_fail("fork() failed");
|
|
return;
|
|
} else if (pid == 0) {
|
|
debug("child");
|
|
test_xfer_client();
|
|
test_fail("we should never get here");
|
|
exit(1);
|
|
} else {
|
|
debug("parent");
|
|
test_xfer_server(pid);
|
|
debug("parent done");
|
|
}
|
|
|
|
UNLINK(TEST_SYM_A);
|
|
UNLINK(TEST_SUN_PATH);
|
|
}
|
|
|
|
void test_simple_client(int type)
|
|
{
|
|
char buf[BUFSIZE];
|
|
int sd, rc;
|
|
struct sockaddr_un addr;
|
|
|
|
sd = socket(PF_UNIX, type, 0);
|
|
if (sd == -1) {
|
|
test_fail("socket");
|
|
exit(errct);
|
|
}
|
|
|
|
while (server_ready == 0) {
|
|
debug("[client] waiting for the server");
|
|
sleep(1);
|
|
}
|
|
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
bzero(buf, BUFSIZE);
|
|
snprintf(buf, BUFSIZE-1, "Hello, My Name is Client.");
|
|
|
|
if (type == SOCK_DGRAM) {
|
|
|
|
rc = sendto(sd, buf, strlen(buf) + 1, 0,
|
|
(struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("sendto");
|
|
exit(errct);
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = connect(sd, (struct sockaddr *) &addr,
|
|
sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("connect");
|
|
exit(errct);
|
|
}
|
|
|
|
rc = write(sd, buf, strlen(buf) + 1);
|
|
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
}
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
rc = read(sd, buf, BUFSIZE);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
}
|
|
|
|
if (strcmp("Hello, My Name is Server.", buf) != 0) {
|
|
test_fail("didn't read the correct string");
|
|
}
|
|
}
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
exit(errct);
|
|
}
|
|
|
|
void test_simple_server(int type, pid_t pid)
|
|
{
|
|
char buf[BUFSIZE];
|
|
int sd, rc, client_sd, status;
|
|
struct sockaddr_un addr;
|
|
socklen_t addr_len;
|
|
|
|
addr_len = sizeof(struct sockaddr_un);
|
|
|
|
sd = socket(PF_UNIX, type, 0);
|
|
if (sd == -1) {
|
|
test_fail("socket");
|
|
}
|
|
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("bind");
|
|
}
|
|
|
|
if (type == SOCK_DGRAM) {
|
|
|
|
/* ready for client */
|
|
kill(pid, SIGUSR1);
|
|
|
|
rc = recvfrom(sd, buf, BUFSIZE, 0,
|
|
(struct sockaddr *) &addr, &addr_len);
|
|
if (rc == -1) {
|
|
test_fail("recvfrom");
|
|
}
|
|
|
|
} else {
|
|
|
|
rc = listen(sd, 5);
|
|
if (rc == -1) {
|
|
test_fail("listen");
|
|
}
|
|
|
|
/* we're ready for connections, time to tell the client
|
|
* to start the test
|
|
*/
|
|
kill(pid, SIGUSR1);
|
|
|
|
client_sd = accept(sd, (struct sockaddr *) &addr, &addr_len);
|
|
if (client_sd == -1) {
|
|
test_fail("accept");
|
|
}
|
|
|
|
memset(buf, '\0', BUFSIZE);
|
|
rc = read(client_sd, buf, BUFSIZE);
|
|
if (rc == -1) {
|
|
test_fail("read");
|
|
}
|
|
|
|
if (strcmp("Hello, My Name is Client.", buf) != 0) {
|
|
test_fail("didn't read the correct string");
|
|
}
|
|
|
|
/* added for extra fun to make the client block on read() */
|
|
sleep(1);
|
|
|
|
bzero(buf, BUFSIZE);
|
|
snprintf(buf, BUFSIZE-1, "Hello, My Name is Server.");
|
|
|
|
rc = write(client_sd, buf, strlen(buf) + 1);
|
|
if (rc == -1) {
|
|
test_fail("write");
|
|
}
|
|
rc = close(client_sd);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
}
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void test_abort_client_server(int abort_type)
|
|
{
|
|
pid_t pid;
|
|
debug("test_simple_client_server()");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
/* 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 == -1) {
|
|
test_fail("fork() failed");
|
|
return;
|
|
} else if (pid == 0) {
|
|
debug("child");
|
|
test_abort_client(abort_type);
|
|
test_fail("we should never get here");
|
|
exit(1);
|
|
} else {
|
|
debug("parent");
|
|
test_abort_server(pid, abort_type);
|
|
debug("parent done");
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
}
|
|
|
|
void test_abort_client(int abort_type)
|
|
{
|
|
char buf[BUFSIZE];
|
|
int sd, rc;
|
|
struct sockaddr_un addr;
|
|
|
|
sd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (sd == -1) {
|
|
test_fail("socket");
|
|
exit(errct);
|
|
}
|
|
|
|
while (server_ready == 0) {
|
|
debug("[client] waiting for the server");
|
|
sleep(1);
|
|
}
|
|
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
bzero(buf, BUFSIZE);
|
|
snprintf(buf, BUFSIZE-1, "Hello, My Name is Client.");
|
|
|
|
rc = connect(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("connect");
|
|
exit(errct);
|
|
}
|
|
|
|
if (abort_type == 2) {
|
|
/* Give server a chance to close connection */
|
|
sleep(2);
|
|
rc = write(sd, buf, strlen(buf) + 1);
|
|
if (rc != -1) {
|
|
test_fail("write should have failed\n");
|
|
}
|
|
if (errno != ECONNRESET) {
|
|
test_fail("errno should've been ECONNRESET\n");
|
|
}
|
|
}
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
exit(errct);
|
|
}
|
|
|
|
void test_abort_server(pid_t pid, int abort_type)
|
|
{
|
|
char buf[BUFSIZE];
|
|
int sd, rc, client_sd, status;
|
|
struct sockaddr_un addr;
|
|
socklen_t addr_len;
|
|
|
|
addr_len = sizeof(struct sockaddr_un);
|
|
|
|
sd = socket(PF_UNIX, SOCK_STREAM, 0);
|
|
if (sd == -1) {
|
|
test_fail("socket");
|
|
}
|
|
|
|
strncpy(addr.sun_path, TEST_SUN_PATH, sizeof(addr.sun_path) - 1);
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
rc = bind(sd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un));
|
|
if (rc == -1) {
|
|
test_fail("bind");
|
|
}
|
|
|
|
rc = listen(sd, 5);
|
|
if (rc == -1) {
|
|
test_fail("listen");
|
|
}
|
|
|
|
/* we're ready for connections, time to tell the client
|
|
* to start the test
|
|
*/
|
|
kill(pid, SIGUSR1);
|
|
|
|
client_sd = accept(sd, (struct sockaddr *) &addr, &addr_len);
|
|
if (client_sd == -1) {
|
|
test_fail("accept");
|
|
}
|
|
|
|
if (abort_type == 1) {
|
|
memset(buf, '\0', BUFSIZE);
|
|
rc = read(client_sd, buf, BUFSIZE);
|
|
if (rc != -1) {
|
|
test_fail("read should've failed\n");
|
|
}
|
|
if (errno != ECONNRESET) {
|
|
test_fail("errno should've been ECONNRESET\n");
|
|
}
|
|
} /* else if (abort_type == 2) { */
|
|
rc = close(client_sd);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
/* } */
|
|
|
|
rc = close(sd);
|
|
if (rc == -1) {
|
|
test_fail("close");
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void test_simple_client_server(int type)
|
|
{
|
|
pid_t pid;
|
|
debug("test_simple_client_server()");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
|
|
/* 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 == -1) {
|
|
test_fail("fork() failed");
|
|
return;
|
|
} else if (pid == 0) {
|
|
debug("child");
|
|
test_simple_client(type);
|
|
test_fail("we should never get here");
|
|
exit(1);
|
|
} else {
|
|
debug("parent");
|
|
test_simple_server(type, pid);
|
|
debug("parent done");
|
|
}
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
void test_msg_dgram(void)
|
|
{
|
|
int rc;
|
|
int src;
|
|
int dst;
|
|
struct sockaddr_un addr;
|
|
struct iovec iov[3];
|
|
struct msghdr msg1;
|
|
struct msghdr msg2;
|
|
char buf1[BUFSIZE];
|
|
char buf2[BUFSIZE];
|
|
char buf3[BUFSIZE];
|
|
socklen_t addrlen = sizeof(struct sockaddr_un);
|
|
|
|
debug("test msg_dgram");
|
|
|
|
UNLINK(TEST_SUN_PATH);
|
|
UNLINK(TEST_SUN_PATHB);
|
|
|
|
src = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
if (src == -1) {
|
|
test_fail("socket");
|
|
}
|
|
|
|
dst = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
if (dst == -1) {
|
|
test_fail("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");
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
memset(&buf1, '\0', BUFSIZE);
|
|
memset(&buf2, '\0', BUFSIZE);
|
|
memset(&buf3, '\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);
|
|
|
|
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 = NULL;
|
|
msg2.msg_controllen = 0;
|
|
msg2.msg_flags = 0;
|
|
|
|
rc = recvmsg(dst, &msg2, 0);
|
|
if (rc == -1) {
|
|
test_fail("recvmsg");
|
|
}
|
|
|
|
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,
|
|
fullpath(TEST_SUN_PATHB))) {
|
|
test_fail("recvmsg");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void test_scm_credentials(void)
|
|
{
|
|
int rc;
|
|
int src;
|
|
int dst;
|
|
struct uucred 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 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");
|
|
}
|
|
|
|
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;
|
|
|
|
debug("sending msg1");
|
|
|
|
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,
|
|
fullpath(TEST_SUN_PATHB))) {
|
|
test_fail("recvmsg");
|
|
}
|
|
|
|
debug("looking for credentials");
|
|
|
|
memset(&cred, '\0', sizeof(struct uucred));
|
|
for (cmsg = CMSG_FIRSTHDR(&msg2); cmsg != NULL;
|
|
cmsg = CMSG_NXTHDR(&msg2, cmsg)) {
|
|
|
|
if (cmsg->cmsg_level == SOL_SOCKET &&
|
|
cmsg->cmsg_type == SCM_CREDENTIALS) {
|
|
|
|
memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct uucred));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cred.cr_ngroups != 0 || cred.cr_uid != geteuid() ||
|
|
cred.cr_gid != getegid()) {
|
|
|
|
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);
|
|
}
|
|
|
|
void test_connect(void)
|
|
{
|
|
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(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()");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void test_select()
|
|
{
|
|
int i, nfds = -1;
|
|
int socks[2];
|
|
fd_set readfds, writefds;
|
|
struct timeval tv;
|
|
int res = 0;
|
|
char buf[1];
|
|
|
|
for (i = 0; i < OPEN_MAX; i++) {
|
|
FD_CLR(i, &readfds);
|
|
FD_CLR(i, &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[1] + 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]);
|
|
|
|
}
|
|
|
|
void test_fchmod()
|
|
{
|
|
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]);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
|
|
debug("entering main()");
|
|
|
|
start(56);
|
|
|
|
test_socket();
|
|
test_bind();
|
|
test_listen();
|
|
test_getsockname();
|
|
test_header();
|
|
test_shutdown();
|
|
test_close();
|
|
test_permissions();
|
|
test_dup();
|
|
test_dup2();
|
|
test_socketpair();
|
|
test_shutdown();
|
|
test_read();
|
|
test_write();
|
|
test_sockopts();
|
|
test_ucred();
|
|
test_xfer();
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
test_simple_client_server(types[i]);
|
|
if (types[i] != SOCK_DGRAM) test_vectorio(types[i]);
|
|
if (types[i] != SOCK_DGRAM) test_msg(types[i]);
|
|
}
|
|
test_abort_client_server(1);
|
|
test_abort_client_server(2);
|
|
test_msg_dgram();
|
|
test_connect();
|
|
test_multiproc_read();
|
|
test_multiproc_write();
|
|
test_scm_credentials();
|
|
test_fd_passing();
|
|
test_select();
|
|
test_fchmod();
|
|
quit();
|
|
|
|
return -1; /* we should never get here */
|
|
}
|
|
|