diff -Nru bit-babbler-0.4/configure bit-babbler-0.5/configure --- bit-babbler-0.4/configure 2015-12-17 11:50:28.000000000 +0000 +++ bit-babbler-0.5/configure 2016-01-18 03:59:30.000000000 +0000 @@ -1,7 +1,7 @@ #! /bin/sh -# From configure.ac generated by Makeup 0.27. +# From configure.ac generated by Makeup 0.28. # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bit-babbler 0.4. +# Generated by GNU Autoconf 2.69 for bit-babbler 0.5. # # Report bugs to . # @@ -583,8 +583,8 @@ # Identity of this package. PACKAGE_NAME='bit-babbler' PACKAGE_TARNAME='bit-babbler' -PACKAGE_VERSION='0.4' -PACKAGE_STRING='bit-babbler 0.4' +PACKAGE_VERSION='0.5' +PACKAGE_STRING='bit-babbler 0.5' PACKAGE_BUGREPORT='ron@debian.org' PACKAGE_URL='' @@ -1350,7 +1350,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bit-babbler 0.4 to adapt to many kinds of systems. +\`configure' configures bit-babbler 0.5 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1415,7 +1415,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bit-babbler 0.4:";; + short | recursive ) echo "Configuration of bit-babbler 0.5:";; esac cat <<\_ACEOF @@ -1574,7 +1574,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bit-babbler configure 0.4 +bit-babbler configure 0.5 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2108,7 +2108,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bit-babbler $as_me 0.4, which was +It was created by bit-babbler $as_me 0.5, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -5021,7 +5021,7 @@ CXXFLAGS=${CXXFLAGS:-$cc_flags$cxx_flags} # add 's' here and omit ranlib from the build step -ARFLAGS=ruvs +ARFLAGS=rDvs if test "$ac_cv_enable_bison_deprecated_warnings" = no; then : @@ -9242,7 +9242,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bit-babbler $as_me 0.4, which was +This file was extended by bit-babbler $as_me 0.5, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -9308,7 +9308,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bit-babbler config.status 0.4 +bit-babbler config.status 0.5 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -9435,9 +9435,9 @@ - makeup_version="0.27" + makeup_version="0.28" package_name="bit-babbler" - package_version="0.4" + package_version="0.5" __package_config_dir="" __package_config_public="setup.h" diff -Nru bit-babbler-0.4/configure.ac bit-babbler-0.5/configure.ac --- bit-babbler-0.4/configure.ac 2015-12-17 11:50:27.000000000 +0000 +++ bit-babbler-0.5/configure.ac 2016-01-18 03:59:29.000000000 +0000 @@ -1,4 +1,4 @@ -# Makeup 0.27 generated configure.ac. +# Makeup 0.28 generated configure.ac. # Do not edit this file directly, your changes will be lost. # Copyright (C) 2003 - 2008, Ron # @@ -12,10 +12,10 @@ # and make it clear that your modifications are licenced strictly # according to the GPL 2 or a compatible licence. -AC_INIT([bit-babbler], [0.4], [ron@debian.org]) +AC_INIT([bit-babbler], [0.5], [ron@debian.org]) AC_COPYRIGHT([Copyright (C) 2003 - 2008, Ron ]) AC_PREREQ(2.61) -AC_REVISION(generated by Makeup 0.27) +AC_REVISION(generated by Makeup 0.28) AC_CONFIG_SRCDIR(Makeup/Makeup.conf) @@ -296,7 +296,7 @@ CXXFLAGS=${CXXFLAGS:-$cc_flags$cxx_flags} # add 's' here and omit ranlib from the build step -ARFLAGS=ruvs +ARFLAGS=rDvs dnl bison3 complains loudly about a bunch of constructs that must still be used @@ -679,9 +679,9 @@ ACM_CONFIG_MAKEFILE([Makeup/gmake-fragments], [ - makeup_version="0.27" + makeup_version="0.28" package_name="bit-babbler" - package_version="0.4" + package_version="0.5" __package_config_dir="" __package_config_public="setup.h" ]) diff -Nru bit-babbler-0.4/debian/bit-babbler.udev bit-babbler-0.5/debian/bit-babbler.udev --- bit-babbler-0.4/debian/bit-babbler.udev 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/debian/bit-babbler.udev 2016-01-15 15:35:53.000000000 +0000 @@ -1 +1,46 @@ -ACTION=="add|change", SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTRS{idProduct}=="7840", GROUP="bit-babbler", MODE="0660" +SUBSYSTEM!="usb", GOTO="bb_end" + +ACTION!="add|change", GOTO="bb_end_add" +ATTR{idVendor}!="0403", GOTO="bb_end" +ATTR{idProduct}!="7840", GOTO="bb_end" + +# Allow users in group bit-babbler to access the device directly. +# Create a symlink to a well known name that can be used in the cgroup_device_acl +# configuration in /etc/libvirt/qemu.conf, and for other similar purposes too. +# And run the bbvirt script to see if this device was configured for hotplugging +# into a virtual machine. +GROUP="bit-babbler", MODE="0660", SYMLINK="bitbabbler/$attr{serial}", \ + RUN+="/usr/bin/bbvirt attach $attr{serial} --busnum $attr{busnum} --devnum $attr{devnum}" + +# If ACLs are supported, grant users in the bit-babbler group access to the device +# with them too. This is mainly so that if a VM is halted, the device will revert +# to normal access from the host system again. The libvirt 'managed' mode will not +# restore the original ownership when it releases the device, it will just make it +# be root:root, stomping the GROUP we set above. +TEST=="/usr/bin/setfacl", RUN+="/usr/bin/setfacl -m g:bit-babbler:rw $devnode" + +# Enable USB autosuspend. The BitBabbler devices support suspending correctly, +# though not every controller they might be plugged into will always play nicely. +# It should be safe to enable it here, even if an upstream hub or controller +# needs it disabled. The XHCI controllers seem to be the most troublesome, but +# mostly with older kernels. +TEST=="power/control", ATTR{power/control}="auto" +TEST=="power/autosuspend_delay_ms", ATTR{power/autosuspend_delay_ms}="2000" + +LABEL="bb_end_add" + + +ACTION!="remove", GOTO="bb_end" + +# Explicitly detach unplugged devices from the VM if they were passed through to it. +# If we don't do this, the stale configuration will remain, and could +# match some other completely different device that is plugged in later ... +# This is why we can't make persistent changes to the domain definition for VMs that +# aren't running when the device is plugged in, because if the host goes down without +# this rule being run, we'd never clean those up. +# +# We can't test against the attributes here, if this would match they are already gone. +ENV{ID_VENDOR_ID}=="0403", ENV{ID_MODEL_ID}=="7840", \ + RUN+="/usr/bin/bbvirt detach $env{ID_SERIAL_SHORT} --busnum $env{BUSNUM} --devnum $env{DEVNUM}" + +LABEL="bb_end" diff -Nru bit-babbler-0.4/debian/changelog bit-babbler-0.5/debian/changelog --- bit-babbler-0.4/debian/changelog 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/debian/changelog 2016-01-15 15:35:53.000000000 +0000 @@ -1,3 +1,53 @@ +bit-babbler (0.5) unstable; urgency=medium + + * Add more options to optimise for minimal power consumption. The defaults + before now were mostly focussed on keeping a good supply of fresh entropy + being regularly mixed into the kernel pool, and on minimising the risk of + starvation delays when demand is high. But there's an equally important + group of users who not only want good entropy, but also want to minimise + idle power usage as much as possible. So we now have some extra tunables + to better support that too. + + The rate at which new entropy is mixed into the kernel pool even when it + has not fallen below its low water mark is now directly configurable, as + is the rate at which we throttle down requesting more entropy from the + hardware when real demand for it falls. Tuning these can minimise how + often we are responsible for waking the CPU on an otherwise idle system. + + It is also now possible to configure the devices to be released when we + expect to be idle beyond a given period of time, which will allow them to + be powered down and suspended, and only woken again when we do need more + entropy from them. There are new udev rules which automatically enable + the USB autosuspend feature of the Linux kernel for them when they are + plugged in, which means this will work without needing to manually set + all that up (unless you want to further tweak the parameters there too). + + * Don't create the control socket by default when only a limited number of + output --bytes are requested. It can still be enabled explicitly if you + do want it available while they are being read, but that's normally of + fairly limited use, and it's otherwise just annoying to have to remember + to explicitly disable it when extracting a block of entropy in this way, + and confusing to users if it complains they don't have permission to + (re)create it in the default location. + + * Defer device initialisation until the pool threads have been started. + Most users won't really notice any difference from that, but when you + have 100 devices in a machine together then even small delays quickly + add up to become a thumb twiddling pause if they are serialised rather + than being run in parallel. + + * Better support for pass-through to libvirt managed virtual machines when + there is more than one BitBabbler device in the host. + + This is still more painful than it really ought to be, but we now have a + big enough hammer pounding on enough of the rough edges in libvirt support + for things to work like USB devices should be expected to work. They can + be hotplugged dynamically without admin intervention to the guest machines + you want them assigned to, and assigned to guest machines without fragile + hacks based on which USB port they are plugged into. + + -- Ron Lee Wed, 23 Dec 2015 00:38:47 +1030 + bit-babbler (0.4) unstable; urgency=medium * Switch to using libusb-1.0 now. It turns out that libusb-0.1 doesn't diff -Nru bit-babbler-0.4/debian/control bit-babbler-0.5/debian/control --- bit-babbler-0.4/debian/control 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/debian/control 2016-01-15 15:35:53.000000000 +0000 @@ -9,7 +9,7 @@ Package: bit-babbler Architecture: any Depends: ${shlibs:Depends} -Suggests: munin-node, libjson-xs-perl +Suggests: munin-node, libjson-xs-perl, libvirt-bin Description: BitBabbler hardware TRNG and kernel entropy source support This package provides supporting software for the BitBabbler true random number generator hardware. It includes: @@ -25,6 +25,9 @@ . You will need to install libjson-xs-perl if you wish to use the munin-node script for continuous graphing and monitoring of device performance. + . + You will need to install libvirt-bin if you want hotplug support for adding + devices to libvirt managed virtual machines. Package: bit-babbler-dbg Section: debug diff -Nru bit-babbler-0.4/debian/copyright bit-babbler-0.5/debian/copyright --- bit-babbler-0.4/debian/copyright 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/debian/copyright 2016-01-15 15:35:53.000000000 +0000 @@ -1,7 +1,7 @@ The bit-babbler package is: - Copyright (C) 2003 - 2015, Ron Lee + Copyright (C) 2003 - 2016, Ron Lee It is distributed according to the terms of the GNU GPL v2. diff -Nru bit-babbler-0.4/debian/rules bit-babbler-0.5/debian/rules --- bit-babbler-0.4/debian/rules 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/debian/rules 2016-01-15 15:35:53.000000000 +0000 @@ -20,10 +20,10 @@ HARD_CPPFLAGS = -D_FORTIFY_SOURCE=2 HARD_LDFLAGS = -Wl,-z,now -ifneq (,$(filter-out $(DEB_HOST_ARCH), alpha hppa arm)) +ifneq (,$(filter-out alpha hppa arm, $(DEB_HOST_ARCH))) HARD_CFLAGS += -fstack-protector --param ssp-buffer-size=4 endif -ifneq (,$(filter-out $(DEB_HOST_ARCH), ia64 hppa avr32)) +ifneq (,$(filter-out ia64 hppa avr32, $(DEB_HOST_ARCH))) HARD_LDFLAGS += -Wl,-z,relro endif @@ -86,7 +86,7 @@ dh_install debian/bit-babbler-sysctl.conf etc/sysctl.d dh_installinit --restart-after-upgrade --name seedd dh_installudev - dh_installexamples doc/examples/* + dh_installexamples doc/examples/* libvirt/qemu-hook touch $@ diff -Nru bit-babbler-0.4/doc/man/bbvirt.1 bit-babbler-0.5/doc/man/bbvirt.1 --- bit-babbler-0.4/doc/man/bbvirt.1 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/doc/man/bbvirt.1 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,365 @@ +.\" Hey, EMACS: -*- nroff -*- +.\" First parameter, NAME, should be all caps +.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection +.\" other parameters are allowed: see man(7), man(1) +.TH BBVIRT 1 "January 12, 2016" +.\" Please adjust this date whenever revising the manpage. +.\" +.\" Some roff macros, for reference: +.\" .nh disable hyphenation +.\" .hy enable hyphenation +.\" .ad l left justify +.\" .ad b justify to both left and right margins +.\" .nf disable filling +.\" .fi enable filling +.\" .br insert line break +.\" .sp insert n+1 empty lines +.\" for manpage-specific macros, see man(7) +.SH NAME +bbvirt \- hotplug BitBabbler devices into libvirt managed domains + +.SH SYNOPSIS +.B bbvirt +.I action +.RI [ options ] + +.BR "bbvirt attach" | detach +.I device +.RI [ options ] + +.BR "bbvirt attach\-all" | detach\-all +.RI [ domain ] +.RI [ options ] + + +.SH DESCRIPTION +The \fBbbvirt\fP program is an attempt to take some of the pain out of what is +currently required to distribute multiple USB devices between the host and +guest virtual machines. While there are several ways in which this may be +configured and managed, at present none of them actually provide a complete and +coherent solution on their own, all of them fall short of the mark in some +significant and annoying way. The aim here is to piece together enough of +those hacks to actually get all of the functionality that we want now, until +the libvirt native support for this improves enough to not need it anymore. + +At present this deals with libvirt managed QEMU/KVM virtual machines. + +.SS What do we want? +The ideal behaviour here is pretty simple. Given some arbitrary number of +BitBabbler devices, we should be able to assign them to either the host machine, +or to a guest VM running on it, and once we do that they should behave in the +normal manner expected of any USB device. + +.IP - 2 +If they are plugged in when the guest machine is started, they should be seen +by that machine as they would be by the host. + +.IP - 2 +If they are plugged in after the machine is started, they should be hotplugged +into that machine as they would be on the host. + +.IP - 2 +If they are unplugged while the machine is running, they should be cleanly +removed from it, as they would be on the host. + + +.SS Why can't we have it? +Right now, libvirt gives us two ways that we can assign USB devices from the +host to a guest domain. + +.IP - 2 +We can assign them by their USB vendor and product ID. But that only works +when there is just a single device of that type in the host. Which is pretty +useless in most of the cases that we care about here, where the host and each +of the guests are likely to have one or more BitBabbler devices of their own +assigned to them. + +.IP - 2 +We can assign them by their logical address on the USB bus. But that isn't a +constant that we can statically configure for the domain. Every time a device +is plugged in, or replugged, or reset, or the host machine is rebooted, that +address is likely to change since it is dynamically allocated when the device +is enumerated on the bus. + +.PP +There is a third way, but it relies on bypassing the normal libvirt +configuration to make direct use of the QEMU ability to assign a device by its +physical address on the bus. Which is better, but still not a magic bullet +since it relies on plugging exactly the same devices into exactly the same +ports every time (and on having those ports enumerated in the same way by the +host on every reboot, which isn't guaranteed either). It also forces us to +jump through other hoops, since we then need additional complication to manage +the access permissions of the device manually outside of libvirt, but still +in coordination with it. + +The even bigger failing, which all of those methods have in common, is they all +depend on the device already being plugged in before the guest is started. If +it is inserted after the guest is started, or removed and replugged while the +guest is running, or if the host bus or a hub bounces causing a reconnect, then +the device will not be (re)attached to the guest. The only way to fix that if +it happens is to manually reattach the device with an arcane incantation in XML +(which relies on you knowing the new address of the device), or to completely +power down and restart the guest. Not the pinnacle of user-friendly operation +that we are looking for here. + + +.SS What can we do about it? +There was a patch submitted to libvirt some years back which would have allowed +a device to be specified by both its USB product ID and its serial number, but +that got some push-back, and so far has still not been applied upstream. That +would have gone a long way toward making this both easy and clean, leaving us +only with the hotplug aspect to deal with. We'll leave grumpy snark about that +as an exercise for the reader\ ... + +Another alternative is we can delegate finding the device's logical address to +a hotplug manager like \fBudev\fP(7). This is attractive in the sense that we +can know when the address of a device changes and what it changes to, but +\fBudev\fP itself isn't very friendly to the idea of local admin customisation +(while it is possible to do, it seems to be getting increasingly strongly +discouraged) and using it still requires some external glue to translate its +events into something that libvirt can act on to configure the guest machine. + +The \fBbbvirt\fP program provides that glue, and a user friendly method of +assigning which devices should belong to which guest domains, and a front end +that can be invoked manually or by other admin controlled tasks to quickly and +easily add or remove BitBabbler devices from any of the running guest machines. + +But the limitation this approach has, is that it can't easily know when a guest +machine is started which should have devices that are already plugged in added +to it. In theory we could add them to its persistent domain definition, but +that has its own problems because we can only add devices by their ephemeral +logical address, and we can't guarantee that we will get called to remove them +from the domain again when that address becomes invalid (like if the host is +suddenly powered off or it is otherwise not cleanly shut down), so we could end +up with many stale entries accumulating in the persistent domain configuration, +which could later match some completely different device to what we had wanted +attached to it. Which means until that somehow gets fixed, it's only safe to +add them to a live guest domain, so that they will always be removed again when +it is halted, no matter how it ended up getting halted. + +Clearly we've still got some way to go to get to our ideal here. + + +.SS What if we hit it with *two* hammers? +There appears to be only two ways that we can get notified of a guest machine +being started at present. One involves running yet another daemon process, +which would do little more than just sit around waiting for someone to start a +guest so it could tell us about that. But then we'd have yet another thing to +configure, yet another process running, and yet more problems with figuring out +how to ensure we don't lose a race when the host is booted, between getting the +initial set of device events, that process being ready and active, and any +guests that will be autostarted at boot actually starting. + +The other way is to use a libvirt hook. Which in turn has the problem of not +actually allowing us to run any libvirt functions from it, which we need to do +in order to attach the device to the host. And which we can't guarantee that +we can just install by default, because there can be only one such hook on the +system, which the local admin may already be using ... + +There is a third way, but that would involve requiring the local admin to start +all guest machines through a wrapper of our own, instead of via whatever +mechanism they already know and use. Which doesn't scale to support other USB +devices in the same situation, among the many ways that would be a horrible +solution to inflict on people. + +But there is a loophole we can exploit. We can use the libvirt qemu hook to +trigger a change event for \fBudev\fP, which can in turn invoke \fBbbvirt\fP +in much the same way that would happen if the device was really hotplugged, +which gives us the extra layer of indirection we need to be able to safely do +that from the hook. Rube Goldberg would be proud, and some of the pieces may +require hand-assembly, but with all of this in place, we can have something +resembling normal USB functionality in the guest machines. + +It's not pretty, but it will work with what we have to work with. + +.SS Ok, just tell me where to hit it. +To string this together, you'll need to ensure all of the following: + +.IP - 2 +The \fBudev\fP(7) rules from the bit-babbler package are installed. If you +installed this from the Debian packages that should already be done. If you +didn't, you will need to install the rules that are found in +\fIdebian/bit-babbler.udev\fP from the source package to a suitable place on +your system (probably \fI/etc/udev/rules.d\fP). + +.IP - 2 +The \fBbbvirt\fP(1) script is installed in a place where the \fBudev\fP rules +will find it. If you didn't install this from the Debian packages, and it +isn't in \fI/usr/bin\fP, then you'll need to tweak the \fBudev\fP rules to suit. + +.IP - 2 +The devices you wish to use in guest machines, and the machines you wish to +use them in, are specified in the \fBbbvirt\fP configuration file. The default +location for that is \fI/etc/bit-babbler/vm.conf\fP. If you wish to use a +different file you will need to pass its location with the \fB\-\-config\fP +option in the \fBudev\fP rules, and update the hook script use that file too. +The details of what you can put in that file are described in the +\fBCONFIGURATION OPTIONS\fP section below. + +.IP - 2 +The libvirt hook file is installed. If all the above is done, then devices +will be added to the running guest machines if they get plugged in while the +guest is running. This last step ensures devices which are already plugged in +will be added to newly started guests too (which includes guests that are +started automatically when the host machine boots). + +Until there is some safe way we can install this without conflicting with or +overwriting an existing hook, everyone will need to do this step manually. If +you have installed the Debian packages, then the example hook script that +we've provided for this can +be found in \fI/usr/share/doc/bit-babbler/examples/qemu-hook\fP. If you didn't +it can be found in \fIlibvirt/qemu-hook\fP of the source package. + +You will need to install that file as \fI/etc/libvirt/hooks/qemu\fP, or merge +its content with the existing \fIqemu\fP file there if you already have that +hook set. If that file did not previously exist, you will need to restart +\fBlibvirtd\fP(8) to get it to begin using it. + +.PP +That should cover all of the needed automation, but you can also attach and +detach devices manually at any time too. The details of doing that will be +described in the following section. Otherwise, with all the above done, there +is no other reason to need to invoke \fBbbvirt\fP directly. + + +.SH OPTIONS +There are two primary modes of operation for \fBbbvirt\fP which are selected +by the initial action option. If the action to perform is \fBattach\fP or +\fBdetach\fP then only a single device will be acted upon, and which device +that should be must be specified explicitly, even if there is only one device +present on the host at the time. When invoking \fBbbvirt\fP manually, the +\fIdevice\fP may be specified by its serial number, its logical address on the +bus (in the form \fIbusnum\fP:\fIdevnum\fP, given as decimal integers), or its +physical address on the bus (in the form +\fIbusnum\fP-\fIport\fP[\fI.port\fP\ ...]). + +If the action to perform is \fBattach\-all\fP or \fBdetach\-all\fP, then the +device(s) to act upon are selected by \fIdomain\fP association instead. If a +\fIdomain\fP is explicitly specified, then all devices which are assigned to +that guest domain in the configuration file will be acted upon in the same way +as if \fBbbvirt\fP was invoked for each of them individually with the +\fBattach\fP or \fBdetach\fP action. If no \fIdomain\fP is provided, then all +of the configured guest domains will be acted upon in this way. + +The following additional options are available: + +.TP +.B \-C, \-\-config +Specify an alternative configuration file to import the device assignments +from. If the path to the file is not provided explicitly, then it will be +looked for in the \fI/etc/bit\-babbler\fP directory (with a \fI.conf\fP +suffix). + +.TP +.BI "\-c, \-\-connect=" URI +Specify the \fBvirsh\fP(1) connection \fIURI\fP to use. This will override +a \fBDOMAIN_URI\fP set for the domain in the configuration file. If that is +not set using either of these methods then the \fBvirsh\fP default for the +user running \fBbbvirt\fP will be used. + +.TP +.BI "\-D, \-\-domain=" name +Specify the libvirt domain to act upon. This may be used to override the +device allocation from the configuration file when \fBbbvirt\fP is invoked +manually, or to act on a device or domain that is not currently specified in +the configuration file. + +.TP +.BI "\-b, \-\-busnum=" num +Specify the USB bus number that the device is attached to. This option is +mostly used to avoid \fBbbvirt\fP needing to look this up when it is already +known (such as when it is called from a \fBudev\fP rule). There isn't usually +much reason to pass this if invoking \fBbbvirt\fP manually, since you can just +specify the device by its logical or physical address instead. + +.TP +.BI "\-d, \-\-devnum=" num +Specify the USB device number that the device is currently assigned. Together +with the bus number, this forms the logical address of the device. This option +is mostly used to avoid \fBbbvirt\fP needing to look this up when it is already +known (such as when it is called from a \fBudev\fP rule). There isn't usually +much reason to pass this if invoking \fBbbvirt\fP manually, since you can just +specify the device by its logical address instead. + +.TP +.B \-n, \-\-dry\-run +Don't attach or detach any devices, just show what would be attempted if this +was a live run. This option implies a minimal level of \fB\-\-verbose\fP, but +the verbosity may be increased further by also passing that option explicitly. + +.TP +.B \-v, \-\-verbose +Make more noise about what is really going on. It may be passed multiple times +to increase the verbosity further. + +.TP +.B \-?, \-\-help +Show a brief summary of the available options. + + +.SH CONFIGURATION OPTIONS +The \fBbbvirt\fP configuration file contains variable assignments using the +\fBbash\fP(1) shell syntax. It is sourced as a shell snippet, so you could in +principle construct the configuration for each domain dynamically, but most +typically a simple static assignment of devices to domains will suffice. If +you do elect to run code in it, you should be very defensive about namespacing +any other variables you use, or any other side effects you might cause to +happen. Any number of guest domains may be configured in it. + +For each guest domain, two variables control the behaviour of \fBbbvirt\fP: + +.TP +.BI DOMAIN_URI_ domain = URI +This variable is optional, and sets the \fBvirsh\fP(1) connection \fIURI\fP to +use when attaching or detaching devices from the given \fIdomain\fP. If the +\fB\-\-connect\fP option is explicitly passed to \fBbbvirt\fP it will override +what is set here. If the connection \fIURI\fP is not set using either of these +methods then the \fBvirsh\fP default for the user running \fBbbvirt\fP will be +used (which would normally be root if run from \fBudev\fP). + +.TP +.BI DOMAIN_RNG_ domain =( " device serial numbers \fR... " ) +This variable is required if automatic passthrough of devices to a domain is +desired. It is a bash array, populated with a space separated list of all the +device serial numbers that you want assigned to \fIdomain\fP. It is not an +error for devices to be listed here which are not currently plugged in. It is +important to ensure that devices are only assigned to one \fIdomain\fP though, +and that devices assigned to guest domains will not be used by a \fBseedd\fP(1) +instance running on the host (which means the \fBseedd\fP configuration needs +to be passed an explicit list of the devices that it may use too). + +The device serial number must always be used here. You cannot specify a +device by its logical or physical address on the bus (like you can in most +other places where we take a device ID). + + +.SH FILES +.TP +.I /etc/bit-babbler/vm.conf +The default configuration file for assigning BitBabbler devices to libvirt +managed virtual machine domains. + +.TP +.I /lib/udev/rules.d/60-bit-babbler.rules +The default \fBudev\fP(7) rules granting direct device access to users in the +group \fBbit\-babbler\fP, enabling USB autosuspend when the device is idle, and +invoking \fBbbvirt\fP to handle device hotplug for virtual machines. +These can be overridden by creating \fI/etc/udev/rules.d/60\-bit\-babbler.rules\fP +and populating it with your own rules. + +.TP +.I /etc/libvirt/hooks/qemu +The libvirt hook script needed to enable cold-plugging of already present +devices into newly (re)started virtual machines. + + +.SH SEE ALSO +.BR seedd (1), +.BR virsh (1). + + +.SH AUTHOR +.B bbvirt +was written by Ron . You can send bug reports, feature requests, +praise and complaints to support@bitbabbler.org. + diff -Nru bit-babbler-0.4/doc/man/seedd.1 bit-babbler-0.5/doc/man/seedd.1 --- bit-babbler-0.4/doc/man/seedd.1 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/doc/man/seedd.1 2016-01-15 15:35:53.000000000 +0000 @@ -29,12 +29,46 @@ or directly seeding the kernel entropy pool with it on demand. +.SH USAGE +The number of configurable options for \fBseedd\fP has now outgrown what most +people will care about or want to use, which would normally be less than ideal +for something like this, but it does have a rather diverse range of user needs, +and it is important that we support those well. + +Unless you fall into the special use category, then the following examples are +probably about all (or still more than) you might ever need: + + Show all available BitBabbler devices, in detail: + + seedd -sv (or \-\-scan \-\-verbose) + + Output 1 million bytes to a file, drawn from all available devices: + + seedd -b 1000000 > random\-bytes.out + + Stream entropy continuously to \fIstdout\fP (with no control socket): + + seedd -o -c none | your\-thing\-reading\-stdin + + Run as a daemon, feeding entropy to the OS kernel pool: + + seedd -k -d + +To read from only specific devices, add the \fB\-\-device\-id\fP option too. + + .SH OPTIONS The following options are available: .TP .B \-s, \-\-scan -Scan the system for available BitBabbler devices. +Scan the system for available BitBabbler devices, reporting them in a human +readable format. + +.TP +.B " \-\-shell-mr" +Scan the system for available BitBabbler devices, reporting them in a machine +readable format that is suitable for importing into shell scripts. .TP .BI "\-i, \-\-device\-id=" id @@ -80,7 +114,9 @@ or \fB\-\-udp-out\fP options are used. A suffix of 'k', 'M', or 'G' will multiply \fIn\fP by the respective power of two. If this option is not used, then entropy will be output until the process is explicitly terminated (or -receives SIGPIPE). Passing this option implies \fB\-\-stdout\fP. +receives SIGPIPE). Passing this option implies \fB\-\-stdout\fP, and also +\fB\-\-control\-socket=none\fP unless the control socket option is explicitly +passed to enable it. .TP .B \-k, \-\-kernel @@ -230,6 +266,35 @@ all good sizes on computers are always a power of two if so. .TP +.BI " \-\-kernel\-refill=" sec +Set the maximum time in seconds before fresh entropy will be added to the OS +kernel pool, even when it has not been drained below its usual refill threshold. +This option has no effect unless the \fB\-\-kernel\fP option is being used. + +When feeding the OS pool, \fBseedd\fP will be woken to immediately add more +entropy to it any time that it falls below the configured minimum watermark +(which on Linux is set by \fI/proc/sys/kernel/random/write_wakeup_threshold\fP +and can be configured persistently in +\fI/etc/sysctl.d/bit\-babbler\-sysctl.conf\fP). + +In addition to that, it will also wake up periodically to mix fresh entropy +into the OS pool even if it is not being consumed (testing that the output of +the device is still passing all the QA testing in the process). This option +configures how long it will wait since the last time fresh entropy was added +before doing that. If set to 0, then we will never add more entropy unless +explicitly woken by the OS pool falling below its watermark. The default is +60 seconds, and there probably aren't many reasons to reduce that, but you may +want to increase or disable it on low power systems which you don't want to be +waking up just to do this. + +The main downside to increasing it is that on relatively quiet systems it may +take (significantly) longer for the long term QA tests (in particular the 16 +bit tests) to accumulate enough results for analysis, and you lose some of the +confidence that comes with a higher rate of continual sampling from the device. +This option lets you choose the right balance for your own use. If unsure, +leaving it at its default setting is probably the right answer. + +.TP .B \-v, \-\-verbose Make more noise about what is going on internally. If used (once) with the \fB\-\-scan\fP option this will show more information about each device, but @@ -324,16 +389,143 @@ controlling an individual source, enabling it when set to 1. .TP +.BI " \-\-idle\-sleep=" initial : max +This option permits tuning how the devices back off from generating entropy at +the maximum rate, when it is not being consumed from the output pool. When the +output pool is not full, entropy will be read from the devices as quickly as +possible to try to refill it. Once it is full, they will begin to be throttled +according to the following algorithm: + +The \fIinitial\fP value is the number of milliseconds to sleep when the output +pool first becomes full again. If this value is 0, then the device will +immediately remain idle until the output pool is no longer full. Otherwise, +reading from the device will pause for either this number of milliseconds, or +until the pool is no longer full, whichever comes first. If that timeout +expires and the pool is still full, another block of entropy will be generated +and mixed into the pool, then the timeout will be doubled. This process will +continue until the timeout reaches the \fImax\fP value (which is also in +milliseconds), at which point it will not increase any further. The device +will always be woken immediately any time the output pool is not full, and the +timeout cycle will begin again from the \fIinitial\fP value each time that +occurs. + +As a special case, if the \fImax\fP value is set to 0, with an \fIinitial\fP +value that is not zero, the exponential back off will occur as above until +the timeout reaches or exceeds 512 ms, at which point further activity will +again be suspended indefinitely until the output pool is no longer full. This +allows for a mode of operation where the device will still go into a hard +suspend when no entropy is being consumed from the output pool, but only after +mixing several blocks of entropy from each device that is configured this way +into it. + +The default configuration used if this is not set explicitly is +\fIinitial\fP=100 and \fImax\fP=60000. Usually the only reason to change this +is if you are trying to minimise the power usage on a low power system which +you don't want continually waking up to generate entropy that nothing is using. +For that use, if you are feeding the OS kernel pool, you will probably also want +to set the \fB\-\-kernel\-refill\fP option to a suitable value, since it will +cause the devices to wake up independently of what is set here (by reading from +the output pool, making it be no longer full). Dialling the verbosity up to +level 6 (with \fB\-vvvvvv\fP) while tweaking this will let you watch how the +reads from the devices are actually throttled. + +When setting this, either of \fIinitial\fP or \fImax\fP may be omitted (in +which case they will retain their default value), but the ':' must always be +included. It probably doesn't make a lot of sense to set this differently for +each device (especially not for devices which are grouped together), but that +is permitted if you really have some reason to want to do that. + +.TP +.BI " \-\-suspend\-after=" ms +Set the minimum expected device idle time for which we should allow the device +to be suspended. On Linux, USB devices that are idle can automatically be +suspended into a low power state, but in order to qualify as being 'idle' for +that purpose, we need to release our claim on the device. Full details of the +OS part of that can be found here: + +.nh +https://www.kernel.org/doc/Documentation/usb/power\-management.txt +.hy + +The default is 0, which means \fBseedd\fP will never release a device it has +claimed. The benefit of this is that no other process can claim it while it +is released (accidentally or otherwise), which would prevent us from being +able to use it again when we do require entropy from it. It also ensures +there is minimal latency when we are woken up to read entropy from it again. + +Setting this to a value greater than zero means that when the output pool is +full, and we are expecting to sleep for at least that amount of time before +reading from the device again, then the claim on the device will be released, +and the OS will be able to suspend it until we need it again. If the pool is +drained and requires more entropy before that time, then we will still reclaim +the device immediately and begin reading from it again, but there will be a +small amount of additional latency while it wakes up and is reinitialised for +use. This option should usually be set in conjunction with +\fB\-\-idle\-sleep\fP and \fB\-\-kernel\-refill\fP which control how often the +device will be woken again to refresh the entropy pools when it might otherwise +have remained idle. If they never allow it to sleep for longer than this time, +then this option will have no effect. + +It probably doesn't make much sense to set this below about 10000 (10 seconds) +otherwise the overhead of releasing, reclaiming, and reinitialising the device +might actually use more power than suspending it saves. And it definitely +doesn't make much sense to set it to a value less than what is configured for +the \fIautosuspend_delay_ms\fP option in the kernel, since while we will release +the device any time that we \fIexpect\fP to sleep for this long (regardless of +whether we actually do or not), the kernel will not actually suspend it until +the \fIautosuspend_delay_ms\fP time has elapsed \fIafter\fP we have released it. +So if it doesn't get to actually suspend it, we would just be chewing extra CPU +cycles, and adding extra latency to obtaining entropy when it is needed, for no +net gain. + +.TP +.B " \-\-low\-power" +This is a convenience option, which is equivalent to setting: + + \-\-kernel\-refill=3600 \-\-idle-sleep=100:0 \-\-suspend\-after=10000 + +And which in turn means: + +We will wake up to mix more entropy into the kernel pool at least once an hour +(though it is likely that most systems will already drain it below its threshold +and so wake us to refill it before that time expires anyway). + +We will mix at least 6 blocks of fresh entropy into the \fBseedd\fP output pool +each time we are woken, before suspending indefinitely again (until either we +are woken by the kernel needing entropy or by the timeout above expiring, or +until something else consumes entropy from the output pool - such as from the +UDP socket if that is enabled). This is based on doubling the \fIinitial\fP +\fB\-\-idle-sleep\fP timeout each time the output pool remains full, until we +exceed the minimum amount of time that really will perform a sleep (512ms), +and then sleeping until explicitly woken again after that. + +We will release the device, giving the OS the opportunity to suspend it, each +time it does become fully idle (since an indefinite sleep is considered to be +longer than any fixed amount of time). + +Any or all of those options may still be customised by passing them explicitly +\fIafter\fP this option on the command line (in the same way that passing them +twice would also override the first instance). + +This isn't necessarily the configuration offering the lowest possible power +consumption, but it's intended to strike a reasonable balance for systems +where keeping idle power consumption low is more a important concern than +continually mixing in additional fresh entropy or minimising the latency if +demand for entropy suddenly surges (which is what the normal defaults are +more oriented toward). At the very least it should be a reasonable starting +point to begin experimenting from on low power systems. + +.TP .B " \-\-no\-qa" Disable gating entropy output on the result of quality and health checking. You pretty much never want to use this unless you are generating streams to \fIstdout\fP for no other reason than to analyse their quality with some other tool, such as \fBdieharder\fP(1) or the NIST test suite or similar. For that -type of use we definitely don't want to be filtering out blocks that already -failed our own internal continuous testing, or the value of such testing will -be almost as tainted as the people who say "after whitening our RNG with SHA-1 -it now passes all the statistical tests perfectly!", and there's enough of -those in the world already, we didn't build this to follow in their footsteps. +type of use we definitely don't want to be filtering out blocks which have +already failed our own internal quality analysis, otherwise the value of such +testing will be almost as tainted as that of the people who say "after whitening +our RNG with SHA-1 it now passes all of the statistical tests perfectly!", and +there's already more than enough fossils in that tarpit. It is not possible to disable this for data which is passed directly to the kernel entropy pool, there is absolutely no reason to ever want to do that, @@ -380,6 +572,11 @@ .SH FILES .TP +.I /etc/default/seedd +The optional configuration overrides for the init script, used when automatically +starting as a daemon at system boot time. + +.TP .I /var/run/bit\-babbler/seedd.socket The default control socket path if not explicitly specified. @@ -390,17 +587,21 @@ for an immediate top up. .TP -.I /etc/munin/plugin\-conf.d/bit\-babbler -The \fBmunin\-node\fP configuration for continuous monitoring. +.I /lib/udev/rules.d/60-bit-babbler.rules +The default \fBudev\fP(7) rules granting direct device access to users in the +group \fBbit\-babbler\fP, enabling USB autosuspend when the device is idle, and +invoking \fBbbvirt\fP to handle device hotplug for virtual machines. +These can be overridden by creating \fI/etc/udev/rules.d/60\-bit\-babbler.rules\fP +and populating it with your own rules. .TP -.I /etc/default/seedd -The optional configuration overrides for the init script, used when automatically -starting as a daemon at system boot time. +.I /etc/munin/plugin\-conf.d/bit\-babbler +The \fBmunin\-node\fP configuration for continuous monitoring. .SH SEE ALSO -.BR bbctl (1). +.BR bbctl (1), +.BR bbvirt (1). .SH AUTHOR diff -Nru bit-babbler-0.4/doc/README.mingw bit-babbler-0.5/doc/README.mingw --- bit-babbler-0.4/doc/README.mingw 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/doc/README.mingw 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ -The bit-babbler source can be built for Window systems using the +The bit-babbler source can be built for Windows systems using the mingw-w64 toolchain, which provides implementations for some of the POSIX features that we still use at present, even when building for that platform. @@ -62,7 +62,6 @@ $ cd build-dir $ ../configure --disable-shared \ --host x86_64-w64-mingw32 \ - --without-udev \ LIBUSB_DIR=/home/you/libusb-1.0.20/build-dir/install-dir/usr/local $ make @@ -88,8 +87,8 @@ it is running. -You will also need the mscvr110 runtime library installed. It is no longer -included by default in Window 10 (but it should be already installed in the +You will also need the msvcr110 runtime library installed. It is no longer +included by default in Windows 10 (but it should be already installed in the earlier versions). It can be obtained from here: http://www.microsoft.com/en-us/download/details.aspx?id=30679 diff -Nru bit-babbler-0.4/doc/virtual_machines bit-babbler-0.5/doc/virtual_machines --- bit-babbler-0.4/doc/virtual_machines 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/doc/virtual_machines 2016-01-15 15:35:53.000000000 +0000 @@ -20,6 +20,109 @@ to other virtualisation systems too. +The best and most complete solution that we have at present requires erecting +some scaffolding around libvirt to provide the support for USB hotplug which +it is currently missing and help us plaster over some of the other cracks that +needed functionality would otherwise fall into and become lost. + +There are more details on all that in the bbvirt(1) manual page, what follows +here is the quick-start guide for assembling it. + + + Install the udev rules. + ----------------------- + +If you installed the bit-babbler Debian package, this is already done. If you +didn't, you'll want to copy the file debian/bit-babbler.udev to somewhere like +/etc/udev/rules.d/60-bit-babbler.rules. + +Ensure that the two rules which RUN bbvirt in it point to the actual location +of that script on your system. By default it is expected in /usr/bin, but if +you built the bit-babbler source with ./configure && make and didn't change +the prefix it may be in /usr/local/bin instead. + + + Configure the device assignments that you want. + ----------------------------------------------- + +The /etc/bit-babbler/vm.conf file is where you define the defaults you want to +use for which device will be assigned to which virtual machine. If you are +also running seedd(1) on the host machine you will need to explicitly define +which devices will be used on the host too, which is set in /etc/default/seedd +if you're using the Debian package init script (or something derived from it) +to start that daemon. + + + Install the libvirt hook. + ------------------------- + +The example file for that can be found in libvirt/qemu-hook of the bit-babbler +source package, or /usr/share/doc/bit-babbler/examples/qemu-hook if the Debian +binary package is installed. It either needs to be copied to be the libvirt +'qemu' hook (typically /etc/libvirt/hooks/qemu) or merged with whatever is +already there if you do already have a qemu hook installed, since there can be +only one such hook with current libvirt (which is why we can't safely install +this automatically at present). + +If there was no qemu hook installed previously, you will need to restart the +libvirtd process once this is done (but that won't disturb any of your already +running guests). + + + Share and enjoy! + ---------------- + +If that all went as planned, the BitBabbler devices you assigned to the guest +domains should now be hotplugged into them whenever the devices are inserted +or the guest machine is started. + +If the devices were already plugged in, and the guests were already running, +you can synthesise a udev event to attach them to the guests immediately with: + + # udevadm trigger -c change -s usb -a "idVendor=0403" -a "idProduct=7840" + + + Guest domain configuration. + --------------------------- + +With the above all in place, there is no need to make any change to the guest +domain definitions for the devices you want attached to them. They will be +dynamically added to them using the logical address reported by udev. + +There is one change still worth making to them if you haven't already though. +To get the full throughput from the BitBabbler devices they will need to be +attached to a USB2.0 capable port in the guest. However the default libvirt +configuration generally only creates a USB1.1 host controller, which will be +a significant bottleneck for getting bits out of them. + +The easiest way to ensure that is to simply change the USB to be +an XHCI device, since it will automatically support both USB3.0 devices and +all lower speed devices without the need to individually configure a full set +of companion controllers for lower speed devices. It's also supposed to be +a more efficient driver. It's a bit more bleeding edge than the others, and +may have some issues to shake out (the Debian kFreeBSD kernel did not support +XHCI when we were testing the BitBabbler support for it, and bugs are still +being fixed in the Linux kernel for it) but mostly it's been working quite +well for us. + +To do that, you'll want to replace the USB section with something +like this: + + +
+ + +Where 'slot' is any free PCI slot not already in use by any other controller +in the guest. + + +=============================================================================== + +You can fairly safely ignore everything below here now, as the above is both +all you should need and our best recommendation so far. What follows remains +mostly as a historical note of things we tried that fell short of being an +entirely satisfactory solution in one or (usually) more ways. + The easy case ------------- @@ -84,9 +187,10 @@ # seedd -sv Have 3 devices: - Device 0: Serial 'GK9VZF', ... bus 3, device 6, port 3-2 - Device 1: Serial 'GDYXTH', ... bus 5, device 2, port 5-4 - Device 2: Serial 'GTH4R8', ... bus 5, device 7, port 5-2.3 + Bus:Dev VID:PID + 003:006 0403:7840 Serial 'GK9VZF', ... port 3-2 + 005:002 0403:7840 Serial 'GDYXTH', ... port 5-4 + 005:007 0403:7840 Serial 'GTH4R8', ... port 5-2.3 So the above
refers here to the device with serial number GK9VZF. diff -Nru bit-babbler-0.4/include/bit-babbler/aligned_recast.h bit-babbler-0.5/include/bit-babbler/aligned_recast.h --- bit-babbler-0.4/include/bit-babbler/aligned_recast.h 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/aligned_recast.h 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,89 @@ +// This file is distributed as part of the bit-babbler package. +// Copyright 2015 - 2016, Ron + +#ifndef _BB_ALIGNED_RECAST_H +#define _BB_ALIGNED_RECAST_H + +#include +#include + + +namespace BitB +{ + + template< typename T > struct related_type { typedef void* void_type; }; + template< typename T > struct related_type< const T* > { typedef const void* void_type; }; + + template< typename T > struct alignment_of { enum { value = __alignof__(T) }; }; + template< typename T > struct alignment_of< T* > { enum { value = __alignof__(T) }; }; + + + + // Return true if pointer p is aligned to some multiple of S. + template< size_t S > + bool IsAligned( const void *p ) + { //{{{ + + if( S & (S - 1) ) + { + // We shouldn't normally ever be here, the natural alignment of types + // on most platforms is always a power of 2, and checking that should + // be faster than the modulus here. But since this should get compiled + // out as dead code if we don't need it, there's no harm in also having + // a fully generic implementation just in case we ever really do. + if( __builtin_expect(reinterpret_cast(p) % S, 0) ) + return false; + + } else { + + if( __builtin_expect(reinterpret_cast(p) & (S - 1), 0) ) + return false; + } + + return true; + + } //}}} + + // Return true if pointer p is aligned to some multiple of the alignment of type T. + template< typename T > + bool IsAligned( const void *p ) + { + return IsAligned< alignment_of::value >( p ); + } + + + + // Safe cast back to a type with increased alignment. + // + // This will cast pointer p, to type T, after asserting that it is already + // suitably aligned to some multiple of S. + // + // The main use for this is portably squelching -Wcast-align warnings where + // it is certain that the actual alignment of the pointer being punned will + // always be sufficient, with a runtime check to assert that really is true. + template< typename T, size_t S, typename P > + T aligned_recast( P p ) + { //{{{ + + if( ! IsAligned( p ) ) + throw std::invalid_argument( + stringprintf( "aligned_recast: %s %p has alignment < %zu in cast to %s", + EM_TYPEOF(P), p, S, EM_TYPEOF(T) ) ); + + return reinterpret_cast( static_cast< typename related_type

::void_type >( p ) ); + + } //}}} + + // Cast pointer p, to type T, after asserting that it is already suitably + // aligned to some multiple of the alignment of type T. + template< typename T, typename P > + T aligned_recast( P p ) + { + return aligned_recast< T, alignment_of::value >( p ); + } + +} + +#endif // _BB_ALIGNED_RECAST_H + +// vi:sts=4:sw=4:et:foldmethod=marker diff -Nru bit-babbler-0.4/include/bit-babbler/exceptions.h bit-babbler-0.5/include/bit-babbler/exceptions.h --- bit-babbler-0.4/include/bit-babbler/exceptions.h 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/exceptions.h 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2003 - 2015, Ron +// Copyright 2003 - 2016, Ron #ifndef _BB_EXCEPTIONS_H #define _BB_EXCEPTIONS_H @@ -79,6 +79,11 @@ ~Exception() throw() {} + void SetMessage( const std::string &msg ) throw() + { + m_msg = msg; + } + void SetMessage( const char *format, va_list args ) throw() { char *msg = NULL; diff -Nru bit-babbler-0.4/include/bit-babbler/ftdi-device.h bit-babbler-0.5/include/bit-babbler/ftdi-device.h --- bit-babbler-0.4/include/bit-babbler/ftdi-device.h 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/ftdi-device.h 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2010 - 2015, Ron +// Copyright 2010 - 2016, Ron #ifndef _BB_FTDI_DEVICE_H #define _BB_FTDI_DEVICE_H @@ -195,19 +195,20 @@ unsigned m_latency; unsigned m_maxpacket; + uint16_t /* InterfaceIndex */ m_index; uint8_t m_configuration; uint8_t m_interface; - uint16_t /* InterfaceIndex */ m_index; + uint8_t m_altsetting; uint8_t m_epin; // Endpoint device->host address uint8_t m_epout; // Endpoint host->device address + uint8_t m_linestatus; + size_t m_chunksize; size_t m_chunkhead; size_t m_chunklen; uint8_t *m_chunkbuf; - uint8_t m_linestatus; - protected: @@ -573,7 +574,7 @@ #ifdef CHECK_LINE_STATUS - if( xfer == 2 ) + if( __builtin_expect(xfer == 2, 0) ) { if( __builtin_expect( m_chunkbuf[0] != (FTDI_DSR | FTDI_CTS | FTDI_MS_LOW) @@ -740,6 +741,79 @@ } //}}} + // This is the maximum amount of data we allow for a single transfer. + //{{{ + // Mostly it is chosen to limit the amount of time we might block on + // waiting for a single transfer to complete. + // + // Returns the actual size that was set, which may have been clamped to + // the maximum transfer size for the device, or rounded to the next + // largest multiple of the maximum packet size. + // + // NOTE: + // Changing the chunk size will recreate the internal chunk buffer and + // discard any data that was still in it at the time. There is no + // internal locking of that here, so it is the caller's responsibility + // to ensure nothing is concurrently trying to access it (ie. in a call + // to ftdi_read) and that discarding any data in it will be safe and/or + // acceptable. + //}}} + size_t SetChunkSize( size_t bytes ) + { //{{{ + + if( bytes > m_dev->GetMaxTransferSize() ) + bytes = m_dev->GetMaxTransferSize(); + + // Round up the desired chunksize to the next multiple of m_maxpacket. + size_t chunksize = bytes + m_maxpacket - 1 - (bytes - 1) % m_maxpacket; + + if( chunksize != m_chunksize ) + { + if( m_chunkbuf ) + { + delete [] m_chunkbuf; + m_chunkbuf = NULL; // Just in case new throws ... + } + + m_chunkbuf = new uint8_t[chunksize]; + m_chunksize = chunksize; + m_chunkhead = 0; + m_chunklen = 0; + } + + return m_chunksize; + + } //}}} + + // Set the timeout for completing short packets when there is no more data to send + //{{{ + // It is usually better to use an explicit flush, like MPSSE_SEND_IMMEDIATE + // or the other on-chip triggers, than to try to tune throughput with this. + // So normally this should be set to a large enough value to permit complete + // transfer of the largest expected packet size, without truncating them due + // to a timer expiry. There is some inherent latency in the chip which can + // make that be a slightly longer time than the theoretical transfer time of + // the data. + // + // NOTE: + // Calling this does not in itself change the current latency setting (and + // changing it on the fly between transfers is known to get the chip into a + // confused state in some circumstances, so you should rethink if you really + // want to do that anyway). The desired latency must be calculated and set + // before calling InitMPSSE(), since that is the only place at present where + // the value set here will be used. + //}}} + void SetLatency( unsigned ms ) + { //{{{ + + if( ms < 1 || ms > 255 ) + ThrowError( _("FTDI::SetLatency( %u ): invalid value, must be > 0 and < 255"), ms ); + + m_latency = ms; + + } //}}} + + // Put the chip into MPSSE mode bool InitMPSSE() { //{{{ @@ -774,8 +848,7 @@ } //}}} - - void Close() + void ResetBitmode() { //{{{ if( ! m_dh ) @@ -788,46 +861,50 @@ ftdi_set_bitmode( BITMODE_RESET ); ftdi_reset(); } - BB_CATCH_ALL( 2, _("FTDI: shutdown failed") ) - - - ScopedCancelState cancelstate; - - int ret = libusb_release_interface( *m_dh, 0 ); - if( ret < 0 ) - LogUSBError<2>( ret, _("FTDI: failed to release interface") ); + BB_CATCH_ALL( 2, _("FTDI: ResetBitmode failed") ) } //}}} public: - FTDI( const USBContext::Device::Handle &dev ) + FTDI( const USBContext::Device::Handle &dev, bool claim_now = true ) : m_dev( dev ) - , m_dh( m_dev->OpenDevice() ) , m_timeout( 5000 ) // milliseconds , m_latency( 1 ) + , m_index( FTDI_INTERFACE_A ) , m_configuration( 1 ) // bConfigurationValue , m_interface( 0 ) // bInterfaceNumber - , m_index( FTDI_INTERFACE_A ) + , m_altsetting( 0 ) // bAlternateSetting + , m_linestatus( 0 ) , m_chunksize( 0 ) , m_chunkhead( 0 ) , m_chunklen( 0 ) , m_chunkbuf( NULL ) - , m_linestatus( 0 ) { //{{{ - // Sanity check some things before we access them. - const USBContext::Device::Config &c = m_dev->GetConfiguration( m_configuration ); + LogMsg<2>( "+ FTDI" ); - if( c.interface.size() <= m_interface ) - ThrowError( _("FTDI: expecting interface %u from configuration %u, " - " but %zu interfaces were reported by the device."), - m_interface, m_configuration, c.interface.size() ); - - m_maxpacket = c.interface[m_interface].alt[0].endpoint[0].wMaxPacketSize; - m_epin = c.interface[m_interface].alt[0].endpoint[0].bEndpointAddress; - m_epout = c.interface[m_interface].alt[0].endpoint[1].bEndpointAddress; + // Sanity check some things before we access them. + try { + const USBContext::Device:: + AltSetting &alt = m_dev->GetConfiguration( m_configuration ) + .GetInterface( m_interface ) + .GetAltSetting( m_altsetting ); + + if( alt.endpoint.size() != 2 ) + throw Error( _("Configuration %u, Interface %u, AltSetting %u " + "has %zu endpoints, expecting 2"), m_configuration, + m_interface, m_altsetting, alt.endpoint.size() ); + + m_maxpacket = alt.endpoint[0].wMaxPacketSize; + m_epin = alt.endpoint[0].bEndpointAddress; + m_epout = alt.endpoint[1].bEndpointAddress; + } + catch( const std::exception &e ) + { + ThrowError( _("FTDI: %s"), e.what() ); + } // We could probably default this one safely to 64 if we couldn't read it, // but that's also probably a sign of something bigger gone wrong too ... @@ -846,19 +923,8 @@ if( USBContext::Device::Endpoint::Direction( m_epout ) != LIBUSB_ENDPOINT_OUT ) ThrowError( _("FTDI: device endpoint[1] direction is not 'OUT'") ); - { - ScopedCancelState cancelstate; - - int ret = libusb_set_configuration( *m_dh, m_configuration ); - - if( ret < 0 ) - ThrowUSBError( ret, _("FTDI: failed to set configuration 1") ); - - ret = libusb_claim_interface( *m_dh, m_interface ); - - if( ret < 0 ) - ThrowUSBError( ret, _("FTDI: failed to claim device") ); - } + if( claim_now ) + Claim(); SetChunkSize( 65536 ); @@ -869,7 +935,7 @@ LogMsg<2>( "- FTDI" ); - Close(); + Release(); if( m_chunkbuf ) delete [] m_chunkbuf; @@ -877,59 +943,108 @@ } //}}} - // Return true if this is device d. - bool IsDevice( const USBContext::Device::Handle &d ) + // This may be called whether we've claimed the device interface or not. + //{{{ + // If the device configuration cannot be restored, then the device + // may be disconnected and reconnected, in which case this will then + // throw a USBError with LIBUSB_ERROR_NOT_FOUND set, the claim on the + // device interface (if it was held) will be released, and this FTDI + // instance will henceforth be invalid (since the device it refers to + // will no longer exist). + // + // If hotplug is enabled, the device should be re-enumerated again + // normally (if it can be reconnected). Otherwise you may need to + // rescan the bus to find it again. + //}}} + void SoftReset() { //{{{ - // A null device never matches anything. Just like SQL! - if( ! m_dev || ! d ) - return false; + if( ! m_dh ) + { + m_dev->OpenDevice()->SoftReset(); + return; + } - return *m_dev == *d; + try { + m_dh->SoftReset(); + } + catch( ... ) + { + m_dh = NULL; + throw; + } } //}}} - // This is the maximum amount of data we allow for a single transfer. + // Return true if we currently have a claim on the device interface. + bool IsClaimed() const + { + return m_dh != NULL; + } + + // Returns true if the device was (newly) claimed by calling this, //{{{ - // Mostly it is chosen to limit the amount of time we might block on - // waiting for a single transfer to complete. + // or false if it was already claimed by us. Will throw if getting + // a claim on the device fails. // - // Returns the actual size that was set, which may have been clamped to - // the maximum transfer size for the device, or rounded to the next - // largest multiple of the maximum packet size. + // There is no reference count on claims. No matter how many times + // you call this, the first call to Release() will drop the claim. //}}} - size_t SetChunkSize( size_t bytes ) + virtual bool Claim() { //{{{ - if( bytes > m_dev->GetMaxTransferSize() ) - bytes = m_dev->GetMaxTransferSize(); + if( m_dh != NULL ) + return false; - // Round up the desired chunksize to the next multiple of m_maxpacket. - size_t chunksize = bytes + m_maxpacket - 1 - (bytes - 1) % m_maxpacket; + try { + m_dh = m_dev->OpenDevice(); + m_dh->SetConfiguration( m_configuration ); + m_dh->ClaimInterface( m_interface ); - if( chunksize != m_chunksize ) - { - if( m_chunkbuf ) - delete [] m_chunkbuf; + if( m_altsetting ) + m_dh->SetAltInterface( m_interface, m_altsetting ); - m_chunkbuf = new uint8_t[chunksize]; - m_chunksize = chunksize; - m_chunkhead = 0; - m_chunklen = 0; + return true; + } + catch( ... ) + { + m_dh = NULL; + throw; } - - return m_chunksize; } //}}} - void SetLatency( unsigned ms ) + // Release the current claim on this device. + //{{{ + // It is the responsibility of the caller to ensure that no other + // functions which might access the device are called while a claim + // on it is not held. + //}}} + virtual void Release() + { + m_dh = NULL; + } + + + // If the endpoint_address isn't specified explicitly, try to clear + // a stall from all endpoints of the currently claimed interface(s). + void ClearHalt( unsigned endpoint_address = 0x100 ) + { + if( m_dh != NULL ) + m_dh->ClearHalt( endpoint_address ); + } + + + // Return true if this is device d. + bool IsDevice( const USBContext::Device::Handle &d ) { //{{{ - if( ms < 1 || ms > 255 ) - ThrowError( _("FTDI::SetLatency( %u ): invalid value, must be > 0 and < 255"), ms ); + // A null device never matches anything. Just like SQL! + if( ! m_dev || ! d ) + return false; - m_latency = ms; + return *m_dev == *d; } //}}} @@ -1053,12 +1168,10 @@ va_start( arglist, format ); msg.append( ": " ) - .append( vstringprintf( format, arglist ) ) - .append( ": " ) - .append( libusb_strerror(libusb_error(err)) ); + .append( vstringprintf( format, arglist ) ); va_end( arglist ); - throw Error( msg ); + throw USBError( err, "%s", msg.c_str() ); } //}}} diff -Nru bit-babbler-0.4/include/bit-babbler/qa.h bit-babbler-0.5/include/bit-babbler/qa.h --- bit-babbler-0.4/include/bit-babbler/qa.h 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/qa.h 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2014 - 2015, Ron +// Copyright 2014 - 2016, Ron // // With much kudos to John "Random" Walker for the public domain ENT suite // of tests, which the implementation below doesn't actually take any code @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -921,18 +922,18 @@ // sufficient alignment to cast to (at least) uint16_t, so suppress // the compile time warning and throw at runtime if that's not true. // - // We only need to care about power of 2 alignments here. - if( __builtin_expect(intptr_t(buf) & (__alignof__(T) - 1), 0) ) - throw Error( "Ent%zu::Analyse: buffer %p has alignment less than %zu (needed for %s)", - NBITS, buf, __alignof__(T), EM_TYPEOF(T) ); - - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wcast-align" - const T *b = reinterpret_cast(buf); - #pragma GCC diagnostic pop + // If this ever isn't true, we can use IsAligned to check if we need + // to copy it to an aligned bounce buffer first. + try { + const T *b = aligned_recast< const T* >( buf ); - analyse_monte( buf, len ); - analyse( b, sample_len ); + analyse_monte( buf, len ); + analyse( b, sample_len ); + } + catch( const std::exception &e ) + { + throw Error( "Ent%zu::Analyse: %s", NBITS, e.what() ); + } } //}}} diff -Nru bit-babbler-0.4/include/bit-babbler/refptr.h bit-babbler-0.5/include/bit-babbler/refptr.h --- bit-babbler-0.4/include/bit-babbler/refptr.h 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/refptr.h 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2003 - 2015, Ron +// Copyright 2003 - 2016, Ron #ifndef _BB_REFPTR_H #define _BB_REFPTR_H @@ -206,8 +206,11 @@ ++m_count; if( __builtin_expect(m_count == 0, 0) ) + { + mutex_unlock(); throw Error( "Ref with zero ref count. RefCountedBy<%s> overflow", EM_TYPEOF(T) ); + } mutex_unlock(); } @@ -216,8 +219,12 @@ mutex_lock(); if( __builtin_expect(m_count == 0, 0) ) + { + mutex_unlock(); throw Error( "Unref with zero ref count in RefCountedBy<%s>", EM_TYPEOF(T) ); + } + if( --m_count == 0 ) { mutex_unlock(); diff -Nru bit-babbler-0.4/include/bit-babbler/secret-source.h bit-babbler-0.5/include/bit-babbler/secret-source.h --- bit-babbler-0.4/include/bit-babbler/secret-source.h 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/secret-source.h 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2010 - 2015, Ron +// Copyright 2010 - 2016, Ron #ifndef _BB_SECRET_SOURCE_H #define _BB_SECRET_SOURCE_H @@ -7,8 +7,6 @@ #include #include -#include - #if EM_PLATFORM_LINUX #include #include @@ -68,6 +66,9 @@ unsigned m_disable_pol; unsigned m_bitrate; unsigned m_fold; + unsigned m_sleep_init; // in milliseconds + unsigned m_sleep_max; + unsigned m_suspend_after; bool m_no_qa; @@ -144,8 +145,12 @@ unsigned latency; unsigned fold; unsigned group; + unsigned sleep_init; // in milliseconds + unsigned sleep_max; + unsigned suspend_after; bool no_qa; + Options() : enable_mask( 0x0f ) , disable_polarity( 0x00 ) @@ -153,9 +158,51 @@ , latency( unsigned(-1) ) , fold( unsigned(-1) ) , group( 0 ) + , sleep_init( 100 ) + , sleep_max( 60000 ) + , suspend_after( 0 ) , no_qa( false ) {} + + void SetIdleSleep( const std::string &arg ) + { //{{{ + + size_t n = arg.find(':'); + + if( n == std::string::npos ) + throw Error( _("BitBabbler::Options: invalid idle-sleep argument '%s'"), + arg.c_str() ); + + if( n != 0 ) + { + try { + sleep_init = StrToScaledUL( arg.substr(0, n) ); + } + catch( const std::exception &e ) + { + throw Error( _("BitBabbler::Options: invalid idle-sleep init '%s': %s"), + arg.c_str(), e.what() ); + } + } + + if( n + 1 < arg.size() ) + { + try { + sleep_max = StrToScaledUL( arg.substr(n + 1) ); + } + catch( const std::exception &e ) + { + throw Error( _("BitBabbler::Options: invalid idle-sleep max '%s': %s"), + arg.c_str(), e.what() ); + } + } + + if( sleep_max && sleep_init > sleep_max ) + throw Error( _("BitBabbler::Options: invalid idle-sleep, init %u > max %u"), + sleep_init, sleep_max ); + } //}}} + }; //}}} @@ -210,12 +257,17 @@ public: - BitBabbler( const USBContext::Device::Handle &dev, const Options &options = Options() ) - : FTDI( dev ) + BitBabbler( const USBContext::Device::Handle &dev, + const Options &options = Options(), + bool claim_now = true ) + : FTDI( dev, false ) , m_enable_mask( ~options.enable_mask << 4 & 0xf0 ) , m_disable_pol( options.disable_polarity << 4 & 0xf0 ) , m_bitrate( choose_bitrate(options) ) , m_fold( choose_folding(options) ) + , m_sleep_init( options.sleep_init ) + , m_sleep_max( options.sleep_max ) + , m_suspend_after( options.suspend_after ) , m_no_qa( options.no_qa ) { //{{{ @@ -283,16 +335,38 @@ LogMsg<3>( "Chunk size %u, %u ms/per chunk (latency %u ms, max packet %u)", chunksize, chunksize * 8000 / m_bitrate, latency, maxpacket ); - init_device(); + if( claim_now ) + Claim(); } //}}} ~BitBabbler() { LogMsg<2>( "- BitBabbler" ); + Release(); } + virtual bool Claim() + { //{{{ + + if( ! FTDI::Claim() ) + return false; + + init_device(); + return true; + + } //}}} + + virtual void Release() + { //{{{ + + ResetBitmode(); + FTDI::Release(); + + } //}}} + + unsigned GetBitrate() const { return m_bitrate; @@ -303,6 +377,21 @@ return m_fold; } + unsigned GetIdleSleepInit() const + { + return m_sleep_init; + } + + unsigned GetIdleSleepMax() const + { + return m_sleep_max; + } + + unsigned GetSuspendAfter() const + { + return m_suspend_after; + } + bool NoQA() const { return m_no_qa; @@ -312,7 +401,7 @@ size_t read( uint8_t *buf, size_t len ) { //{{{ - if( len < 1 || len > 65536 ) + if( __builtin_expect( len < 1 || len > 65536, 0 ) ) throw Error( _("BitBabbler::read( %zu ): invalid length"), len ); const uint8_t cmd[] = @@ -347,7 +436,8 @@ WriteCommand( cmd, sizeof(cmd) ); goto ok; } - BB_CATCH_ALL( 0, stringprintf("BitBabbler::read( %zu ) exception", len).c_str() ) + catch( const abi::__forced_unwind& ) { throw; } + BB_CATCH_STD( 0, stringprintf("BitBabbler::read( %zu ) exception", len).c_str() ) while( ++reset_attempts < FTDI_INIT_RETRIES ) { // We shouldn't ever get here in normal operation, but if for some @@ -362,6 +452,7 @@ // any of these operations, but any real or permanent error should // normally result in bailing out before that limit is reached. LogMsg<1>( "BitBabbler::read( %zu ): attempting to reset device", len ); + FTDI::Claim(); init_device(); WriteCommand( cmd, sizeof(cmd) ); @@ -374,13 +465,13 @@ do { size_t ret = ftdi_read( buf + count, len - count ); - if( ret > 0 ) + if( __builtin_expect( ret > 0, 1 ) ) { LogMsg<6>( "BitBabbler::read( %zu ): read %zu (n = %zu)", len, ret, n ); count += ret; - if( count == len ) + if( __builtin_expect( count == len, 1 ) ) { // This is just to create buffer bloat errors, // mostly for testing the purge recovery code. @@ -428,6 +519,30 @@ { //{{{ public: + struct Options + { //{{{ + + size_t pool_size; + std::string kernel_device; + unsigned kernel_refill_time; // in seconds + + + Options() + : pool_size( 65536 ) + , kernel_device( "/dev/random" ) + , kernel_refill_time( 60 ) + {} + + + std::string Str() const + { + return stringprintf( "Size %zu, Kernel dev '%s', refill time %us", + pool_size, kernel_device.c_str(), kernel_refill_time ); + } + + }; //}}} + + class Group : public RefCounted { //{{{ public: @@ -698,13 +813,12 @@ typedef std::list< pthread_t > ThreadList; + const Options m_opt; + uint8_t *m_buf; - size_t m_size; size_t m_fill; size_t m_next; - std::string m_kerneldev; - Group::Map m_groups; Source::List m_sources; ThreadList m_threads; @@ -714,11 +828,16 @@ pthread_cond_t m_sinkcond; + // You must hold m_mutex to call this + bool PoolIsFull_() + { + return m_fill == m_opt.pool_size; + } bool PoolIsFull() { ScopedMutex lock( &m_mutex ); - return m_fill == m_size; + return PoolIsFull_(); } void AddEntropy( uint8_t *buf, size_t len ) @@ -730,12 +849,12 @@ ScopedMutex lock( &m_mutex ); size_t n = 0; - if( m_fill < m_size ) + if( m_fill < m_opt.pool_size ) { - size_t b = std::min( m_size - m_fill, len ); + size_t b = std::min( m_opt.pool_size - m_fill, len ); Log<5>( "Pool::AddEntropy: add %zu / %zu octets at %zu / %zu\n", - b, len, m_fill, m_size ); + b, len, m_fill, m_opt.pool_size ); memcpy( m_buf + m_fill, buf, b ); n = b; @@ -746,17 +865,17 @@ while( n < len ) { - size_t b = std::min( m_size - m_next, len - n ); + size_t b = std::min( m_opt.pool_size - m_next, len - n ); Log<5>( "Pool::AddEntropy: mix %zu / %zu octets at %zu / %zu\n", - b, len, m_next, m_size ); + b, len, m_next, m_opt.pool_size ); for( size_t i = 0; i < b; ++i ) m_buf[m_next + i] ^= buf[n + i]; n += b; m_next += b; - if( m_next >= m_size ) + if( m_next >= m_opt.pool_size ) m_next = 0; } @@ -785,14 +904,21 @@ void do_source_thread( const Source::Handle &s ) { //{{{ - // All times in milliseconds - static const unsigned INITIAL_SLEEP = 100; - static const unsigned MIN_SLEEP = 500; // really 800 - static const unsigned MAX_SLEEP = 60000; // really 102400 + // All times here are in milliseconds. + // - MIN_SLEEP is the minimum timeout before we will actually sleep. + // - MAX_SLEEP is the longest duration we will actually sleep for, + // with 0 meaning sleep indefinitely once MIN_SLEEP is exceeded. + // - INITIAL_SLEEP is the duration we start doubling from once the + // Pool is full, with 0 meaning sleep indefinitely immediately. + static const unsigned MIN_SLEEP = 512; + static const unsigned MAX_SLEEP = s->babbler->GetIdleSleepMax(); + static const unsigned INITIAL_SLEEP = s->babbler->GetIdleSleepInit(); + static const unsigned SUSPEND_AFTER = s->babbler->GetSuspendAfter(); SetThreadName( s->babbler->GetSerial().substr(0,15) ); - s->babbler->LogMsg<3>( "Pool: begin source_thread" ); + s->babbler->LogMsg<3>( "Pool: begin source_thread (idle sleep %u:%u, suspend %u)", + INITIAL_SLEEP, MAX_SLEEP, SUSPEND_AFTER ); // At rates of 5Mbps or greater, wait for the first Ent8 test results // before declaring the source is generating an acceptable quality of @@ -806,63 +932,134 @@ bool no_qa = s->babbler->NoQA(); unsigned sleep_for = 0; - for(;;) - { - if( sleep_for > MIN_SLEEP ) + + for(;;) try { + + s->babbler->Claim(); + + for(;;) { - timespec wait_until; + if( __builtin_expect( sleep_for == unsigned(-1), 0 ) ) + { + // Sleep until we're explicitly woken by the pool being read from. + ScopedMutex lock( &m_mutex ); - GetFutureTimespec( wait_until, sleep_for ); + if( __builtin_expect( PoolIsFull_(), 1 ) ) + { + s->babbler->LogMsg<6>( "Pool: source_thread waiting for wakeup" ); - ScopedMutex lock( &m_mutex ); + if( SUSPEND_AFTER ) + s->babbler->Release(); - int ret = pthread_cond_timedwait( &m_sourcecond, &m_mutex, &wait_until ); + int ret = pthread_cond_wait( &m_sourcecond, &m_mutex ); - if( ret && ret != ETIMEDOUT ) - throw SystemError( ret, "pthread_cond_timedwait failed: %s", - strerror(ret) ); - } + if( ret ) + throw SystemError( ret, "pthread_cond_wait failed: %s", + strerror(ret) ); + if( SUSPEND_AFTER ) + { + lock.Unlock(); + s->babbler->Claim(); + } + } + } + else if( __builtin_expect( sleep_for >= MIN_SLEEP, 0 ) ) + { + // Sleep until explicitly woken or the timeout expires. + timespec wait_until; + GetFutureTimespec( wait_until, sleep_for ); - for( size_t p = 0, n = 0; p <= s->size - read_size; p += n ) - { - pthread_testcancel(); - ScopedCancelState cancelstate; + ScopedMutex lock( &m_mutex ); - n = s->babbler->read( s->buf + p, read_size ); - } + if( __builtin_expect( PoolIsFull_(), 1 ) ) + { + s->babbler->LogMsg<6>( "Pool: source_thread sleeping for %ums", + sleep_for ); + if( SUSPEND_AFTER && sleep_for >= SUSPEND_AFTER ) + s->babbler->Release(); + + int ret = pthread_cond_timedwait( &m_sourcecond, &m_mutex, &wait_until ); + + if( ret && ret != ETIMEDOUT ) + throw SystemError( ret, "pthread_cond_timedwait failed: %s", + strerror(ret) ); + if( SUSPEND_AFTER && sleep_for >= SUSPEND_AFTER ) + { + lock.Unlock(); + s->babbler->Claim(); + } + } + } - size_t n = FoldBytes( s->buf, s->size, fold ); + for( size_t p = 0, n = 0; p <= s->size - read_size; p += n ) + n = s->babbler->read( s->buf + p, read_size ); - if( PoolIsFull() ) - { - // If the pool is already (still) full, start to throttle back - // on how often we keep mixing more new entropy into it. It's - // not that it hurts to do that, but there's probably not much - // point burning CPU cycles to do so as fast as possible while - // nobody is actually consuming what we alredy have. - if( sleep_for == 0 ) - sleep_for = INITIAL_SLEEP; + size_t n = FoldBytes( s->buf, s->size, fold ); - else if( sleep_for < MAX_SLEEP ) - sleep_for *= 2; + + if( __builtin_expect( PoolIsFull(), 0 ) ) + { + // If the pool is already (still) full, start to throttle back + // on how often we keep mixing more new entropy into it. It's + // not that it hurts to do that, but there's probably not much + // point burning CPU cycles to do so as fast as possible while + // nobody is actually consuming what we alredy have. + if( sleep_for == 0 ) + { + sleep_for = INITIAL_SLEEP ? INITIAL_SLEEP : unsigned(-1); + } + else if( sleep_for < MIN_SLEEP || sleep_for < MAX_SLEEP ) + { + sleep_for *= 2; + + if( MAX_SLEEP && sleep_for > MAX_SLEEP ) + sleep_for = MAX_SLEEP; + } + else if( MAX_SLEEP == 0 ) + { + sleep_for = unsigned(-1); + } + } + else + sleep_for = 0; + + // Don't idle if the QA check failed. We want to find out as quickly + // as possible if that was just a transient spike outside the limits + // (which being random is always possible, however rare it may be), + // and bring the device back on line if that's what it appears to be. + // If it stays bad, then the sysadmin is going to need to take some + // action of their own in response to the alert, and this likewise + // will ensure they have as much data as possible, as quickly as + // possible to base that decision on. + if( __builtin_expect( qa.Check( s->buf, n ) || no_qa, 1 ) ) + s->group->AddEntropy( s->groupmask, s->buf, n ); + else + sleep_for = 0; } - else - sleep_for = 0; + } + catch( const USBError &e ) + { + switch( e.GetErrorCode() ) + { + case LIBUSB_ERROR_PIPE: + s->babbler->LogMsg<1>( "Pool source_thread caught (device %sclaimed): %s", + s->babbler->IsClaimed() ? "": "un", e.what() ); + s->babbler->Release(); + break; + + case LIBUSB_ERROR_TIMEOUT: + case LIBUSB_ERROR_OTHER: + s->babbler->LogMsg<1>( "Pool source_thread caught: %s", e.what() ); + + s->babbler->SoftReset(); + s->babbler->FTDI::Release(); + break; - // Don't idle if the QA check failed. We want to find out as quickly - // as possible if that was just a transient spike outside the limits - // (which being random is always possible, however rare it may be), - // and bring the device back on line if that's what it appears to be. - // If it stays bad, then the sysadmin is going to need to take some - // action of their own in response to the alert, and this likewise - // will ensure they have as much data as possible, as quickly as - // possible to base that decision on. - if( qa.Check( s->buf, n ) || no_qa ) - s->group->AddEntropy( s->groupmask, s->buf, n ); - else - sleep_for = 0; + default: + throw; + } } } //}}} @@ -949,7 +1146,8 @@ SetThreadName( "kernel pool" ); try { - pool->FeedKernelEntropy( pool->m_kerneldev ); + Log<3>( "Pool: begin feedkernel_thread\n" ); + pool->FeedKernelEntropy(); } catch( const abi::__forced_unwind& ) { @@ -969,15 +1167,15 @@ typedef RefPtr< Pool > Handle; - Pool( size_t size = 65536 ) - : m_size( size ) + Pool( const Options &options = Options() ) + : m_opt( options ) , m_fill( 0 ) , m_next( 0 ) { //{{{ - Log<2>( "+ Pool( %zu )\n", m_size ); + Log<2>( "+ Pool( %s )\n", m_opt.Str().c_str() ); - m_buf = new uint8_t[size]; + m_buf = new uint8_t[m_opt.pool_size]; pthread_mutex_init( &m_mutex, NULL ); pthread_cond_init( &m_sourcecond, NULL ); @@ -1033,7 +1231,7 @@ delete [] m_buf; - Log<2>( "- Pool( %zu )\n", m_size ); + Log<2>( "- Pool( %s )\n", m_opt.Str().c_str() ); } //}}} @@ -1066,7 +1264,7 @@ if( gi == m_groups.end() ) { - g = new Group( this, group_id, m_size ); + g = new Group( this, group_id, m_opt.pool_size ); m_groups[group_id] = g; } else { @@ -1131,7 +1329,7 @@ ScopedMutex lock( &m_mutex ); - while( m_fill < m_size && m_fill < len ) + while( m_fill < m_opt.pool_size && m_fill < len ) pthread_cond_wait( &m_sinkcond, &m_mutex ); size_t n = std::min( m_fill, len ); @@ -1141,7 +1339,7 @@ pthread_cond_broadcast( &m_sourcecond ); - Log<5>( "Pool::read( %zu ) returning %zu\n", len, n ); + Log<5>( "Pool::read( %zu ) returning %zu (%zu remain)\n", len, n, m_fill ); return n; } //}}} @@ -1199,7 +1397,7 @@ BB_NORETURN - void FeedKernelEntropy( const std::string &dev = "/dev/random" ) + void FeedKernelEntropy( const std::string &dev = std::string() ) { //{{{ #if EM_PLATFORM_LINUX @@ -1222,15 +1420,16 @@ // we tell it we are providing should be reasonable even if we are // a hair below that in the worst case. - int fd = open( dev.c_str(), O_RDWR ); + int fd = open( dev.empty() ? m_opt.kernel_device.c_str() : dev.c_str(), O_RDWR ); if( fd < 0 ) throw SystemError( _("Pool::FeedKernelEntropy: failed to open %s"), - dev.c_str() ); + dev.empty() ? m_opt.kernel_device.c_str() : dev.c_str() ); const unsigned N = QA::FIPS::BUFFER_SIZE; // 20kbits for FIPS test. const unsigned folds = 2; - const int timeout = 60000; // 60sec + const int timeout = m_opt.kernel_refill_time + ? m_opt.kernel_refill_time * 1000 : -1; union { uint8_t b[N + sizeof(struct rand_pool_info)]; @@ -1249,12 +1448,7 @@ for(;;) { - struct pollfd p = { fd: fd, events: POLLOUT, revents: 0 }; - int r = poll( &p, 1, timeout ); - size_t n; - - if( r < 0 ) - throw SystemError( _("Pool::FeedKernelEntropy: poll failed") ); + size_t n; do { n = read( buf, N ); @@ -1284,6 +1478,12 @@ if( ioctl( fd, RNDADDENTROPY, &rpi ) ) throw SystemError( _("Pool::FeedKernelEntropy: ioctl failed") ); + + struct pollfd p = { fd: fd, events: POLLOUT, revents: 0 }; + int r = poll( &p, 1, timeout ); + + if( r < 0 ) + throw SystemError( _("Pool::FeedKernelEntropy: poll failed") ); } #else @@ -1293,19 +1493,17 @@ } //}}} - void FeedKernelEntropyAsync( const std::string &dev = "/dev/random" ) + void FeedKernelEntropyAsync() { //{{{ ScopedMutex lock( &m_mutex ); pthread_t p; - m_kerneldev = dev; - int ret = pthread_create( &p, NULL, feedkernel_thread, this ); if( ret ) - throw SystemError( ret, _("Pool::FeedKernelEntropyAsync( %s ): " - "failed to create thread"), dev.c_str() ); + throw SystemError( ret, _("Pool::FeedKernelEntropyAsync: " + "failed to create thread") ); m_threads.push_back( p ); @@ -1351,7 +1549,7 @@ if( m_device_options.empty() ) { m_pool->AddSource( m_default_options.group, - new BitBabbler( d, m_default_options ) ); + new BitBabbler( d, m_default_options, false ) ); return; } @@ -1361,7 +1559,7 @@ { if( i->id.Matches( d ) ) { - m_pool->AddSource( i->group, new BitBabbler( d, *i ) ); + m_pool->AddSource( i->group, new BitBabbler( d, *i, false ) ); return; } } diff -Nru bit-babbler-0.4/include/bit-babbler/usbcontext.h bit-babbler-0.5/include/bit-babbler/usbcontext.h --- bit-babbler-0.4/include/bit-babbler/usbcontext.h 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/include/bit-babbler/usbcontext.h 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2010 - 2015, Ron +// Copyright 2010 - 2016, Ron #ifndef _BB_USBCONTEXT_H #define _BB_USBCONTEXT_H @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -52,6 +53,34 @@ namespace BitB { + // Exception class for errors from libusb + class USBError : public Error + { //{{{ + private: + + libusb_error m_usberr; + + + public: + + BB_PRINTF_FORMAT(3,4) + USBError( int code, const char *format, ... ) throw() + : m_usberr( libusb_error(code) ) + { + va_list arglist; + + va_start( arglist, format ); + SetMessage( vstringprintf( format, arglist ) + + ": " + libusb_strerror(m_usberr) ); + va_end( arglist ); + } + + + libusb_error GetErrorCode() const { return m_usberr; } + + }; //}}} + + // Manage a libusb context and the devices associated with it class USBContext { //{{{ @@ -522,9 +551,13 @@ // USB AlternateSettings are numbered from 0, so alt[0] should be AlternateSetting 0. AltSetting::Vector alt; + uint8_t bInterfaceNumber; Interface( const libusb_interface &iface ) + : bInterfaceNumber( iface.num_altsetting > 0 + ? iface.altsetting[0].bInterfaceNumber + : uint8_t(-1) ) { //{{{ for( int i = 0; i < iface.num_altsetting; ++i ) @@ -552,6 +585,17 @@ } //}}} + const AltSetting &GetAltSetting( uint8_t bAlternateSetting ) const + { //{{{ + + if( __builtin_expect( alt.size() <= bAlternateSetting, 0 ) ) + throw Error( _("Interface %u has no alt setting %u"), + bInterfaceNumber, bAlternateSetting ); + + return alt[bAlternateSetting]; + + } //}}} + std::string Str() const { //{{{ @@ -591,8 +635,8 @@ int ret = libusb_get_config_descriptor( dev, i, &c ); if( ret ) - ThrowError( ret, _("USBContext::Device::Config: " - "failed to get configuration %zu descriptor"), i ); + throw USBError( ret, _("USBContext::Device::Config: " + "failed to get configuration %zu descriptor"), i ); if( c->bConfigurationValue != i + 1 ) { @@ -662,6 +706,17 @@ } //}}} + const Interface &GetInterface( uint8_t bInterfaceNumber ) const + { //{{{ + + if( __builtin_expect( interface.size() <= bInterfaceNumber, 0 ) ) + throw Error( _("Configuration %u has no interface %u"), + bConfigurationValue, bInterfaceNumber ); + + return interface[bInterfaceNumber]; + + } //}}} + std::string Str() const { //{{{ @@ -681,10 +736,19 @@ // Scoped container for open device handles class Open : public RefCounted { //{{{ + + // Let Device get at the private constructor + friend class Device; + private: + typedef std::set< uint8_t > ClaimSet; + typedef std::tr1::unordered_map< uint8_t, uint8_t > AltMap; + Device::Handle m_device; - libusb_device_handle *m_handle; + libusb_device_handle *m_handle; + ClaimSet m_claims; + AltMap m_altmap; void do_open( libusb_device *dev ) @@ -694,8 +758,39 @@ int ret = libusb_open( dev, &m_handle ); if( ret < 0 ) - ThrowError( ret, _("Device::Open failed") ); + throw USBError( ret, _("Device::Open failed") ); + + } //}}} + + void release_interface( uint8_t n ) + { //{{{ + + int ret = libusb_release_interface( m_handle, n ); + if( ret < 0 ) + LogUSBError<2>( ret, _("Device::Open( %s ): failed to release interface %u"), + m_device->IDStr().c_str(), n ); + } //}}} + + void release_claims() + { //{{{ + + for( ClaimSet::iterator i = m_claims.begin(), e = m_claims.end(); i != e; ++i ) + release_interface( *i ); + + m_claims.clear(); + m_altmap.clear(); + + } //}}} + + + // This one is only for use in the Device constructor, where + // we can't safely take a Handle to a partly constructed Device. + Open( libusb_device *dev, const Device *d ) + { //{{{ + do_open( dev ); + Log<3>( "+ Device::Open( %p %03u:%03u )\n", m_handle, + d->m_busnum, d->m_devnum ); } //}}} @@ -710,23 +805,209 @@ Log<3>( "+ Device::Open( %s )\n", m_device->IDStr().c_str() ); } - Open( libusb_device *dev, const Device *d ) - { - do_open( dev ); - Log<3>( "+ Device::Open( %p %03u:%03u )\n", m_handle, - d->m_busnum, d->m_devnum ); - } - ~Open() - { - if( ! m_device ) + { //{{{ + + if( __builtin_expect( ! m_device, 0 ) ) Log<3>( "- Device::Open( %p )\n", m_handle ); else Log<3>( "- Device::Open( %s )\n", m_device->IDStr().c_str() ); ScopedCancelState cancelstate; + + release_claims(); libusb_close(m_handle); - } + + } //}}} + + + // We cannot claim interfaces if the device is bound to another driver. + void ForceDetach( uint8_t bInterfaceNumber ) + { //{{{ + + ScopedCancelState cancelstate; + + int ret = libusb_detach_kernel_driver( m_handle, bInterfaceNumber ); + if( ret ) + throw USBError( ret, _("Device( %s ): failed to detach interface %u"), + m_device->IDStr().c_str(), bInterfaceNumber ); + + Log<1>( "Detached interface %u of %s\n", bInterfaceNumber, + m_device->IDStr().c_str() ); + } //}}} + + + // This may be called whether we've claimed any interfaces or not. + //{{{ + // If the device configuration cannot be restored, then the device + // may be disconnected and reconnected, in which case this will then + // throw a USBError with LIBUSB_ERROR_NOT_FOUND set, and this handle + // will no longer be valid. If hotplug is enabled, the device should + // be re-enumerated in that case. If not, you may need to rescan the + // bus to find it again. + //}}} + void SoftReset() + { //{{{ + + ScopedCancelState cancelstate; + + int ret = libusb_reset_device( m_handle ); + if( ret ) + throw USBError( ret, _("Device( %s ): SoftReset failed"), + m_device->IDStr().c_str() ); + + Log<1>( "Reset %s\n", m_device->IDStr().c_str() ); + + } //}}} + + + // This cannot be called on a device that is already claimed. + void SetConfiguration( uint8_t bConfigurationValue ) + { //{{{ + + ScopedCancelState cancelstate; + + int ret = libusb_set_configuration( m_handle, bConfigurationValue ); + if( ret < 0 ) + throw USBError( ret, _("Device( %s ): failed to set configuration %u"), + m_device->IDStr().c_str(), bConfigurationValue ); + } //}}} + + // Return the currently active bConfigurationValue. + uint8_t GetConfiguration() + { //{{{ + + ScopedCancelState cancelstate; + int config; + + int ret = libusb_get_configuration( m_handle, &config ); + if( ret ) + throw USBError( ret, _("Device( %s ): failed to get current configuration"), + m_device->IDStr().c_str() ); + + // Valid USB bConfigurationValue starts at 1. + if( __builtin_expect( config < 1 || config > 255, 0 ) ) + throw Error( _("Device( %s ): invalid current config (1 < %d < 256)"), + m_device->IDStr().c_str(), config ); + return config; + + } //}}} + + + void ClaimInterface( uint8_t bInterfaceNumber ) + { //{{{ + + ScopedCancelState cancelstate; + + int ret = libusb_claim_interface( m_handle, bInterfaceNumber ); + if( ret < 0 ) + throw USBError( ret, _("Device( %s ): failed to claim interface %u"), + m_device->IDStr().c_str(), bInterfaceNumber ); + + m_claims.insert( bInterfaceNumber ); + + } //}}} + + void ClaimAllInterfaces() + { //{{{ + + ScopedCancelState cancelstate; + uint8_t bConfigurationValue = GetConfiguration(); + const Device::Config &c = m_device->GetConfiguration( bConfigurationValue ); + + for( Interface::Vector::const_iterator i = c.interface.begin(), + e = c.interface.end(); i != e; ++i ) + { + try { + ClaimInterface( i->bInterfaceNumber ); + } + catch( ... ) + { + release_claims(); + throw; + } + } + + } //}}} + + void ReleaseInterface( uint8_t bInterfaceNumber ) + { //{{{ + + ScopedCancelState cancelstate; + + release_interface( bInterfaceNumber ); + m_claims.erase( bInterfaceNumber ); + m_altmap.erase( bInterfaceNumber ); + + } //}}} + + void ReleaseAllInterfaces() + { //{{{ + + ScopedCancelState cancelstate; + + release_claims(); + + } //}}} + + + // We must hold the claim to the device interface to call this. + void SetAltInterface( uint8_t bInterfaceNumber, uint8_t bAlternateSetting ) + { //{{{ + + ScopedCancelState cancelstate; + + int ret = libusb_set_interface_alt_setting( m_handle, bInterfaceNumber, + bAlternateSetting ); + if( ret < 0 ) + throw USBError( ret, _("Device( %s ): failed to set interface %u, alt %u"), + m_device->IDStr().c_str(), bInterfaceNumber, + bAlternateSetting ); + m_altmap[bInterfaceNumber] = bAlternateSetting; + + } //}}} + + + // If the endpoint_address isn't specified explicitly, try to clear + // a stall from all endpoints of the currently claimed interface(s). + // + // We must hold the claim to the device interface to call this. + void ClearHalt( unsigned endpoint_address = 0x100 ) + { //{{{ + + ScopedCancelState cancelstate; + + if( endpoint_address == 0x100 ) + { + Endpoint::AddressSet a; + uint8_t bConfigurationValue = GetConfiguration(); + const Device::Config &c = m_device->GetConfiguration( bConfigurationValue ); + + for( ClaimSet::iterator i = m_claims.begin(), + e = m_claims.end(); i != e; ++ i ) + { + uint8_t alt = 0; + + if( m_altmap.find( *i ) != m_altmap.end() ) + alt = m_altmap[ *i ]; + + c.GetInterface( *i ).GetAltSetting( alt ).GetEndpointAddresses( a ); + } + + for( Endpoint::AddressSet::iterator i = a.begin(), e = a.end(); i != e; ++i ) + ClearHalt( *i ); + + return; + } + + int ret = libusb_clear_halt( m_handle, endpoint_address ); + if( ret ) + throw USBError( ret, _("Device( %s ): ClearHalt failed for endpoint %02x"), + m_device->IDStr().c_str(), endpoint_address ); + + Log<1>( "Device( %s ): cleared halt on endpoint %02x\n", + m_device->IDStr().c_str(), endpoint_address ); + } //}}} operator libusb_device_handle*() @@ -774,15 +1055,51 @@ return std::string(); ScopedCancelState cancelstate; + unsigned char s[128]; + unsigned retries = 0; - unsigned char s[128]; - int ret = libusb_get_string_descriptor_ascii( *dev, idx, s, sizeof(s) ); + try_again: + int ret = libusb_get_string_descriptor_ascii( *dev, idx, s, sizeof(s) ); if( ret < 0 ) { - LogError<1>( ret, _("USB Device( %03u:%03u ): " - "failed to get string descriptor %u"), - m_busnum, m_devnum, idx ); + if( ++retries <= 3 ) + { + switch( ret ) + { + case LIBUSB_ERROR_PIPE: + // A control endpoint can't really stall, but it may still + // return this if some other error occurs, so just try again. + LogUSBError<1>( ret, _("USB Device( %03u:%03u ): " + "failed to get string descriptor %u " + "on attempt %u, retrying"), + m_busnum, m_devnum, idx, retries ); + goto try_again; + + case LIBUSB_ERROR_TIMEOUT: + case LIBUSB_ERROR_OTHER: + LogUSBError<1>( ret, _("USB Device( %03u:%03u ): " + "failed to get string descriptor %u " + "on attempt %u, resetting device"), + m_busnum, m_devnum, idx, retries ); + + // We can't call dev->SoftReset here, because we're still in + // the Device constructor, and it wants to access m_device, + // so just do it the old fashioned way here. + ret = libusb_reset_device( *dev ); + if( ret ) + throw USBError( ret, _("USB Device( %03u:%03u ): reset failed"), + m_busnum, m_devnum ); + goto try_again; + + default: + break; + } + } + + LogUSBError<1>( ret, _("USB Device( %03u:%03u ): " + "failed to get string descriptor %u"), + m_busnum, m_devnum, idx ); return std::string(); } @@ -810,8 +1127,8 @@ m_devport += stringprintf(".%d", ports[i]); } else if( ret < 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED ) - ThrowError( ret, _("USB Device( %03u:%03u ): failed to get port numbers"), - m_busnum, m_devnum ); + throw USBError( ret, _("USB Device( %03u:%03u ): failed to get port numbers"), + m_busnum, m_devnum ); #endif @@ -928,8 +1245,8 @@ int ret = libusb_get_device_descriptor( m_dev, &desc ); if( ret < 0 ) - ThrowError( ret, _("Device( %03u:%03u ): failed to get descriptor"), - m_busnum, m_devnum ); + throw USBError( ret, _("Device( %03u:%03u ): failed to get descriptor"), + m_busnum, m_devnum ); m_vendorid = desc.idVendor; m_productid = desc.idProduct; @@ -978,75 +1295,6 @@ return new Open( this ); } - void ForceDetach( int interface = 0 ) - { //{{{ - - ScopedCancelState cancelstate; - Open::Handle h = OpenDevice(); - int ret = libusb_detach_kernel_driver( *h, interface ); - - if( ret ) - ThrowError( ret, _("Device::ForceDetach failed for %s"), - IDStr().c_str() ); - Log<1>( "Detached %s\n", IDStr().c_str() ); - - } //}}} - - void SoftReset() - { //{{{ - - ScopedCancelState cancelstate; - Open::Handle h = OpenDevice(); - int ret = libusb_reset_device( *h ); - - if( ret ) - ThrowError( ret, _("Device::SoftReset failed for %s"), IDStr().c_str() ); - - Log<1>( "Reset %s\n", IDStr().c_str() ); - - } //}}} - - // If the endpoint_address isn't specified explicitly, try to clear a - // stall from all endpoints of the current configuration of the device. - void ClearHalt( unsigned endpoint_address = 0x100 ) - { //{{{ - - ScopedCancelState cancelstate; - Open::Handle h = OpenDevice(); - - if( endpoint_address == 0x100 ) - { - int config; - int ret = libusb_get_configuration( *h, &config ); - - if( ret ) - ThrowError( ret, _("Device::ClearHalt " - "failed to get current configuration for %s"), - IDStr().c_str() ); - // Valid USB bConfigurationValue starts at 1. - if( config < 1 ) - throw Error( _("Device::ClearHalt current config %d < 1"), config ); - - - Endpoint::AddressSet a; - - GetConfiguration( config ).GetEndpointAddresses( a ); - - for( Endpoint::AddressSet::iterator i = a.begin(), e = a.end(); i != e; ++i ) - ClearHalt( *i ); - - return; - } - - int ret = libusb_clear_halt( *h, endpoint_address ); - if( ret ) - ThrowError( ret, _("Device::ClearHalt failed for endpoint %02x of %s"), - endpoint_address, IDStr().c_str() ); - - Log<1>( "Cleared halt on endpoint %02x of %s\n", endpoint_address, IDStr().c_str() ); - - } //}}} - // Device info accessors //{{{ @@ -1134,6 +1382,64 @@ m_mfg.c_str(), m_product.c_str(), DevicePortStr().c_str() ); } //}}} + + // Return the device information as a string of nul separated fields. + //{{{ + // This is primarily intended for importing device data into shell scripts + // in a way that is both safe and easy to parse. The string will contain + // 9 fields, each terminated by a trailing nul. Some fields may be empty. + // + // The fields contain (in the order they are output): + // + // \nD: - Start of record magic. Used to sanity check that we have + // the first field when multiple devices are output together, + // and as a promise of the format and content of the following + // data. If we ever need to break that promise, the magic will + // change too to signal whatever the new promise may be. + // + // Bus number - A 3 digit, 0-padded, decimal number. The USB bus that the + // device is on. + // + // Device number - A 3 digit, 0-padded, decimal number. The logical address + // of the device on the given USB bus. + // + // Vendor ID - A 4 digit hexadecimal number. The device vendor's USB ID. + // + // Product ID - A 4 digit hexadecimal number. The device's USB product ID. + // + // Serial number - An arbitrary string containing the device serial data. + // This may be empty. + // + // Manufacturer - An arbitrary string containing the manufacturer's name. + // This may be empty. + // + // Product - An arbitrary string containing the product's name. + // This may be empty. + // + // Device port - A period separated string of the physical port numbers that + // are the device's physical address on the given USB bus. + // This may be empty, since we aren't always able to obtain + // this information of every platform. + //}}} + std::string ShellMrStr() const + { //{{{ + + std::string s( "\nD:" ); + + s.append( 1, '\0' ); + s.append( stringprintf( "%03u", m_busnum ) ).append( 1, '\0' ); + s.append( stringprintf( "%03u", m_devnum ) ).append( 1, '\0' ); + s.append( stringprintf( "%04x", m_vendorid ) ).append( 1, '\0' ); + s.append( stringprintf( "%04x", m_productid ) ).append( 1, '\0' ); + s.append( m_serial ).append( 1, '\0' ); + s.append( m_mfg ).append( 1, '\0' ); + s.append( m_product ).append( 1, '\0' ); + s.append( m_devport ).append( 1, '\0' ); + + return s; + + } //}}} + //}}} }; //}}} @@ -1166,7 +1472,7 @@ int ret = libusb_get_device_list( m_usb, &devs ); if( ret < 0 ) - ThrowError( ret, _("USBContext: failed to enumerate devices") ); + throw USBError( ret, _("USBContext: failed to enumerate devices") ); for( libusb_device **dev = devs; *dev; ++dev ) { @@ -1310,7 +1616,7 @@ int ret = libusb_init( &m_usb ); if( ret ) - ThrowError( ret, _("USBContext: failed to create libusb context") ); + throw USBError( ret, _("USBContext: failed to create libusb context") ); pthread_mutex_init( &m_device_mutex, NULL ); @@ -1355,7 +1661,7 @@ int ret = libusb_get_device_list( m_usb, &devs ); if( ret < 0 ) - ThrowError( ret, _("USBContext: failed to enumerate devices") ); + throw USBError( ret, _("USBContext: failed to enumerate devices") ); if( ! append ) m_devices.clear(); @@ -1368,7 +1674,7 @@ ret = libusb_get_device_descriptor(d, &desc); if( ret < 0 ) { - LogError<1>( ret, _("USBContext::EnumerateDevices: failed to get descriptor") ); + LogUSBError<1>( ret, _("USBContext::EnumerateDevices: failed to get descriptor") ); continue; } @@ -1434,7 +1740,7 @@ } //}}} - // List all available devices + // List all available devices in a human readable form void ListDevices() const { //{{{ @@ -1470,27 +1776,27 @@ } //}}} - - template< int N > - BB_PRINTF_FORMAT(2,3) - static void LogError( int err, const char *format, ... ) + // List all available devices in a machine readable form + // that is suitable for importing into shell scripts. + void ListDevicesShellMR() const { //{{{ - va_list arglist; - std::string msg; + std::string s; - va_start( arglist, format ); - msg.append( vstringprintf( format, arglist ) ) - .append( ": " ) - .append( libusb_strerror(libusb_error(err)) ); - va_end( arglist ); + for( Device::List::const_iterator i = m_devices.begin(), + e = m_devices.end(); i != e; ++i ) + s.append( (*i)->ShellMrStr() ); - Log( "%s\n", msg.c_str() ); + if( ! s.empty() ) + if( write( STDOUT_FILENO, s.data(), s.size() ) ) + { /* Suppress the unused return warning here */ } } //}}} - BB_PRINTF_FORMAT(2,3,noreturn) - static void ThrowError( int err, const char *format, ... ) + + template< int N > + BB_PRINTF_FORMAT(2,3) + static void LogUSBError( int err, const char *format, ... ) { //{{{ va_list arglist; @@ -1502,7 +1808,7 @@ .append( libusb_strerror(libusb_error(err)) ); va_end( arglist ); - throw Error( msg ); + Log( "%s\n", msg.c_str() ); } //}}} diff -Nru bit-babbler-0.4/libvirt/bbvirt bit-babbler-0.5/libvirt/bbvirt --- bit-babbler-0.4/libvirt/bbvirt 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/libvirt/bbvirt 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,425 @@ +#!/bin/bash +# This file is distributed as part of the bit-babbler package. +# Copyright 2015 - 2016, Ron + +# Default configuration if not explicitly specified. +config_dir="/etc/bit-babbler" +config_file="$config_dir/vm.conf" +verbose=0 + +# Default to searching the PATH for tools we use. +seedd="seedd" +virsh="virsh" + + +die() +{ + echo "$0: $*" 1>&2 + exit 1 +} + +verb() +{ + n=$1 + shift + + (( verbose < n )) || echo "$*" +} + +usage() +{ + cat 1>&2 < [options] + bbvirt attach-all|detach-all [] [options] + + Helper script to hotplug BitBabbler devices into (and back out of) libvirt + managed virtual machines, either manually, or triggered by udev events. + + The first form above is used to attach or detach a single device from a VM, + and is suitable for use as a hotplug trigger, such as a udev rule. + + The second form is offered as a convenience for the task of attaching or + detaching all devices assigned to a particular VM domain (or all of the + configured domains if no explicit domain is passed). + + A may be specified by its serial number, its logical address on the + USB bus in the form BUSNUM:DEVNUM, or its physical address on the USB bus in + the form BUS-PORT[.PORT ...]. A is the libvirt VM domain name. + + The following options may also be used: + + -C, --config "file" Use the specified configuration file for the default + assignment of devices to VM domains. + + -c, --connect "URI" Override the default virsh URI (or the DOMAIN_URI that + is specified in the configuration file). + + -D, --domain "name" Act on the given domain, overriding any assignment of + the device in the configuration file. + + -b, --busnum "num" Explicitly specify the USB bus number that the device + is attached to, rather than searching for it. + + -d, --devnum "num" Explicitly specify the logical USB device number on + the USB bus, rather than searching for it. + + -n, --dry-run Don't attach or detach any devices, just show what + would happen if this was a live run. + + -v, --verbose Be more noisy about what is happening. + -?, --help Shows this usage summary. + +EOF + exit "$1" +} + +parse_args() +{ + while (( $# )); do + case $1 in + attach|detach) action=$1; shift; device=$1 ;; + + attach-all|detach-all) action=${1%-all} + do_all=1 + if [[ $2 != -* ]]; then + shift + domain=$1 + fi + ;; + + --busnum=*) printf -v busnum "%03d" "$(( 10#${1#*=} ))" ;; + --busnum|-b) shift; printf -v busnum "%03d" "$(( 10#$1 ))" ;; + + --devnum=*) printf -v devnum "%03d" "$(( 10#${1#*=} ))" ;; + --devnum|-d) shift; printf -v devnum "%03d" "$(( 10#$1 ))" ;; + + --connect=*) uri=${1#*=} ;; + --connect|-c) shift; uri=$1 ;; + + --domain=*) domain=${1#*=} ;; + --domain|-D) shift; domain=$1 ;; + + --config=*) config_file=${1#*=} ;; + --config|-C) shift; config_file=$1 ;; + + --dry-run|-n) dry_run=1 ;& # --dry-run implies --verbose + --verbose|-v) (( ++verbose )) ;; + -vv) (( verbose += 2 ));; + -vvv) (( verbose += 3 ));; + -vvvv) (( verbose += 4 ));; + + --help|-\?) show_help=1 ;; + + *) + die "ERROR: unrecognised option '$1', try --help" + ;; + esac + shift + done +} + +parse_args "$@" + +[ -z "$show_help" ] || usage 0 + + +# Import the configuration of which devices belong to which VM domains. +import_domain_config() +{ + [[ $config_file == */* ]] || + config_file="$config_dir/$(basename -- "$config_file" .conf).conf" + + if [ -f "$config_file" ] && [ -r "$config_file" ]; then + . "$config_file" + else + verb 1 "Unable to read config file '$config_file'" + exit 0 + fi +} + + +# Device array indices +DA_STRIDE=9 +DA_BUSNUM=1 +DA_DEVNUM=2 +DA_SERIAL=5 +DA_PORTNUM=8 +DA_MAGIC=$'\nD:' + +# Import details of available devices in the shell machine readable format. +get_available_devices() +{ + all_devices=() + + # Clear IFS, the leading \n is part of the magic and we don't want it stripped. + while IFS= read -r -d '' f; do + all_devices+=("$f") + done < <( "$seedd" --shell-mr ) + + if (( verbose > 3 )); then + printf "seedd reported:" + printf " '%s'" "${all_devices[@]}" + printf "\n" + fi +} + +# Find a device matching some combination of attributes. +# get_device_by index match [index match ...] +get_device_by() +{ + compare=( "$@" ) + selected_device=() + + for (( i = 0; i < ${#all_devices[@]}; i += DA_STRIDE )); do + + # Assert the array is framed with the expected stride and magic. + [ "${all_devices[$i]}" = "$DA_MAGIC" ] || + die "Invalid device array magic at element $i '${all_devices[$i]}'" + + # Try the next device if this one doesn't match all attributes + for (( j = 0; j < $#; j += 2 )); do + index=${compare[$j]} + match=${compare[(($j + 1))]} + [ "${all_devices[(($i + $index))]}" = "$match" ] || continue 2 + done + + # Slice all details of the first matching device. + selected_device=( "${all_devices[@]:$i:$DA_STRIDE}" ) + break; + done +} + +# Do $action for each available device assigned to $domain (with $propagate_opts) +act_on_all_devices_in_domain() +{ + devs="DOMAIN_RNG_${domain}[@]" + + for dev in "${!devs}"; do + verb 4 "VM $domain, $action device $dev" + + get_device_by "$DA_SERIAL" "$dev" + + if (( ${#selected_device[@]} == DA_STRIDE )); then + + exec_opts=( "$action" "$dev" -D "$domain" ) + exec_opts+=( -b "${selected_device[$DA_BUSNUM]}" ) + exec_opts+=( -d "${selected_device[$DA_DEVNUM]}" ) + exec_opts+=( "${propagate_opts[@]}" ) + + verb 2 "$0 ${exec_opts[*]}" + "$0" "${exec_opts[@]}" + fi + done +} + + +# For attach-all or detach-all, we synthesise a series of attach/detach calls +# with all the necessary options for each device in the requested domain(s). +if [ -n "$do_all" ]; then + + import_domain_config + get_available_devices + + propagate_opts=( ${config_file:+ -C "$config_file"} ) + propagate_opts+=( ${uri:+ -c "$uri"} ) + propagate_opts+=( ${dry_run:+ -n} ) + + # Propagate verbose flags up to -vvvv (the maximum level we actually use), + # accounting for the fact that dry_run bumps the verbosity level too. + for (( i = ${dry_run:-0}; i < verbose; ++i )); do + v+="v" + done + propagate_opts+=( ${v:+ "-${v:0:4}"} ) + + + if [ -n "$domain" ]; then + + # Act on all the devices configured for the given domain + act_on_all_devices_in_domain + + else + + # Act on all the devices configured for all domains + for dom in "${!DOMAIN_RNG_@}"; do + domain=${dom#DOMAIN_RNG_} + act_on_all_devices_in_domain + done + fi + + exit 0 +fi + + +# We need at least these two to do anything at all below here. +[ -n "$action" ] || die "No action specified." +[ -n "$device" ] || die "No device specified." + + +check_or_set_busnum() +{ + if [ -z "$busnum" ]; then + busnum=$1 + elif [ "$busnum" != "$1" ]; then + die "Device bus $1 != --busnum $busnum." + fi +} + +check_or_set_devnum() +{ + if [ -z "$devnum" ]; then + devnum=$1 + elif [ "$devnum" != "$1" ]; then + die "Device number $1 != --devnum $devnum." + fi +} + + +# Figure out which device we've been asked to act on. +if [[ $device =~ ^[[:digit:]]{1,3}:[[:digit:]]{1,3}$ ]]; then + + # We were passed a device logical address in the form BUSNUM:DEVNUM + printf -v bnum "%03d" "$(( 10#${device%:*} ))" + printf -v dnum "%03d" "$(( 10#${device#*:} ))" + + check_or_set_busnum "$bnum" + check_or_set_devnum "$dnum" + + get_available_devices + get_device_by "$DA_BUSNUM" "$busnum" "$DA_DEVNUM" "$devnum" + + (( ${#selected_device[@]} == DA_STRIDE )) || + die "Failed to find device '$device'" + + devserial=${selected_device[$DA_SERIAL]} + + verb 1 "Device at logical address $bnum:$dnum has serial '$devserial'." + +elif [[ $device =~ ^[[:digit:]]+-[[:digit:].]+$ ]]; then + + # We were passed a device physical address in the form BUS-PORT[.PORT ...] + printf -v bnum "%03d" "$(( 10#${device%-*} ))" + pnum=${device#*-} + + check_or_set_busnum "$bnum" + + get_available_devices + get_device_by "$DA_BUSNUM" "$busnum" "$DA_PORTNUM" "$pnum" + + (( ${#selected_device[@]} == DA_STRIDE )) || + die "Failed to find device '$device'" + + devserial=${selected_device[$DA_SERIAL]} + + check_or_set_devnum "${selected_device[$DA_DEVNUM]}" + + verb 1 "Device at physical address $((10#$bnum))-$pnum has serial '$devserial'." + +elif [[ $device =~ ^[A-Z0-9]{6,7}$ ]]; then + + # If it wasn't either of the above, assume this may be a serial number. + devserial=$device + +else + + die "Invalid device identifier '$device'" +fi + + +# Build an index mapping device serial numbers to domain names. +map_devices_to_domains() +{ + import_domain_config + + for dom in "${!DOMAIN_RNG_@}"; do + verb 4 "domain: $dom" + + devs="${dom}[@]" + for dev in "${!devs}"; do + verb 4 " dev: $dev" + domains[$dev]=${dom#DOMAIN_RNG_} + done + done + + if (( verbose > 2 )); then + for dev in "${!domains[@]}"; do + echo "device $dev is in domain ${domains[$dev]}" + done + fi +} + + +# If the VM domain wasn't explicitly specified, try to find it from the +# configured device allocations. It's not an error for it not to be, +# the udev rule will run this for all devices, even those that we aren't +# passing through to a VM. +if [ -z "$domain" ]; then + + declare -A domains + map_devices_to_domains + + domain=${domains[$devserial]} + + if [ -z "$domain" ]; then + verb 1 "Device '$devserial' is not assigned to any domain." + exit 0 + fi +fi + +# Check if we need to pass an explicit --connect URI to virsh. +if [ -z "$uri" ]; then + uri_config="DOMAIN_URI_$domain" + uri=${!uri_config} +fi + + +# Check that we were passed, or have determined, the logical address of the +# device, since that is the only way that we can pass it to virsh at present. +if [ -z "$busnum" ] || [ -z "$devnum" ]; then + + get_available_devices + get_device_by "$DA_SERIAL" "$devserial" + + check_or_set_busnum "${selected_device[$DA_BUSNUM]}" + check_or_set_devnum "${selected_device[$DA_DEVNUM]}" + + if [ -z "$busnum" ] || [ -z "$devnum" ]; then + die "Could not get bus or device number for '$device'" + fi +fi + + +# Create the foul format that virsh requires us to use for this. +device_xml() +{ + cat < + +

+ + +EOL +} + + +opts=( ${uri:+ -c "$uri"} "$action-device" "$domain" ) + +# Tell them what we are going to do. +verb 1 "$virsh ${opts[*]} --live" +verb 2 "$(device_xml "$busnum" "$devnum")" + +# Do it (maybe). +[ -n "$dry_run" ] || + "$virsh" "${opts[@]}" <(device_xml "$busnum" "$devnum") --live + +# Either way, don't fail at doing it. This could be called by udev when a +# device is hotplugged, and we don't really want it to bitch at people just +# because the domain isn't actually running right now. It's not necessarily +# an error for running this to be a no-op. +# +# We could try to do some other checks to see if the VM is running first, but +# it's hard to avoid a race where it might start or stop between checking that +# and acting on it, so we just try it and either it will work or it won't. +exit 0 + +# vi:sts=4:sw=4:et:foldmethod=marker diff -Nru bit-babbler-0.4/libvirt/qemu-hook bit-babbler-0.5/libvirt/qemu-hook --- bit-babbler-0.4/libvirt/qemu-hook 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/libvirt/qemu-hook 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,38 @@ +#!/bin/bash +# This file is distributed as part of the bit-babbler package. +# Copyright 2015 - 2016, Ron +# +# Example libvirt QEMU hook for cold-plugging BitBabbler devices into +# newly started virtual machines. To use this, it must be installed +# as /etc/libvirt/hooks/qemu (or wherever the equivalent configuration +# is found on your system), and then libvirtd must be restarted if you +# did not previously have a 'qemu' hook installed there. It does not +# need to be restarted again if you modify an existing script there. +# +# This script assumes that you have the udev rules from the bit-babbler +# package installed and active, and that you have bbvirt(1) configured +# to assign devices to the guest domains you want them available in. +# +# It will use the device assignments from /etc/bit-babbler/vm.conf to +# trigger cold plug events for each device that should be made available +# in the guest VM that is being started (which will in turn signal the +# bbvirt helper script to actually attach them to that guest). + +. /etc/bit-babbler/vm.conf + +guest_name=$1 +operation=$2 + +if [ "$operation" = "started" ]; then + devices="DOMAIN_RNG_${guest_name}[@]" + opts=( -c change -s usb -a "idVendor=0403" -a "idProduct=7840" ) + + for d in "${!devices}"; do + /sbin/udevadm trigger "${opts[@]}" -a "serial=$d" + done +fi + +# Always return success here, we don't want to abort guest operations. +exit 0 + +# vi:sts=4:sw=4:et:foldmethod=marker diff -Nru bit-babbler-0.4/libvirt/vm.conf bit-babbler-0.5/libvirt/vm.conf --- bit-babbler-0.4/libvirt/vm.conf 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/libvirt/vm.conf 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,32 @@ +# System configuration file for bbvirt. +# This file is sourced as a bash shell script, see bbvirt(1) for more details. + +# BitBabbler devices are assigned to libvirt managed virtual machines here +# through the use of a pair of variables for each VM domain that devices are +# to be passed through to. Any number of devices and libvirt domains can be +# configured here. +# +# DOMAIN_URI_="" +# - Is the optional --connect URI passed to virsh(1) for domain . +# If not specified, then the system default for virsh (for the user that +# is running bbvirt) will be used. +# +# DOMAIN_RNG_=( ) +# - Is a bash array containing the list of device serial numbers which are +# to be passed through to domain . It is not an error for devices +# which are not currently plugged in to be listed here. If the udev rule +# to invoke bbvirt is active, then these devices will all be passed through +# to that VM when they are plugged in. You just need to ensure that each +# device listed is only allocated to one VM domain, and that they will not +# be used by a seedd(1) instance running on the host. + +# For example: + +#DOMAIN_URI_sid="qemu:///system" +#DOMAIN_RNG_sid=( KWIF4Q JAXJE6 ) + +#DOMAIN_URI_kbsd="qemu+ssh://your.domain/system" +#DOMAIN_RNG_kbsd=( G2SJ3Z ) + + +# vi:sts=4:sw=4:et:syntax=sh:filetype=sh diff -Nru bit-babbler-0.4/Makeup/ac-fragments/configure.stdtools bit-babbler-0.5/Makeup/ac-fragments/configure.stdtools --- bit-babbler-0.4/Makeup/ac-fragments/configure.stdtools 2015-12-06 02:25:27.000000000 +0000 +++ bit-babbler-0.5/Makeup/ac-fragments/configure.stdtools 2015-12-29 17:16:16.000000000 +0000 @@ -260,7 +260,7 @@ CXXFLAGS=${CXXFLAGS:-$cc_flags$cxx_flags} # add 's' here and omit ranlib from the build step -ARFLAGS=ruvs +ARFLAGS=rDvs dnl bison3 complains loudly about a bunch of constructs that must still be used diff -Nru bit-babbler-0.4/Makeup/config/Package.conf bit-babbler-0.5/Makeup/config/Package.conf --- bit-babbler-0.4/Makeup/config/Package.conf 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/Makeup/config/Package.conf 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ PACKAGE_NAME = bit-babbler -PACKAGE_VERSION = 0.4 +PACKAGE_VERSION = 0.5 PACKAGE_MAINTAINER = ron@debian.org PACKAGE_TESTS = configure.stdtools configure.i18n PACKAGE_CONFIG_HEADER = setup.h @@ -7,4 +7,4 @@ PACKAGE_DIST_COMPRESS = gz PACKAGE_BUILD_ROOTCMD = fakeroot PACKAGE_INSTALL_ROOTCMD = sudo -PACKAGE_TARGETS = seedd bbctl bbcheck man1 munin-script munin-conf +PACKAGE_TARGETS = seedd bbctl bbcheck bbvirt vm-conf munin-script munin-conf man1 diff -Nru bit-babbler-0.4/Makeup/config/target.bbvirt bit-babbler-0.5/Makeup/config/target.bbvirt --- bit-babbler-0.4/Makeup/config/target.bbvirt 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/Makeup/config/target.bbvirt 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,6 @@ +bbvirt_TYPE = DATA +bbvirt_DATA_SRCDIR = $(srcdir)/libvirt +bbvirt_DATA_INSTALLDIR = $(bindir) +bbvirt_DATA_FILES = bbvirt + +INSTALL_DATA = $(INSTALL_PROGRAM) diff -Nru bit-babbler-0.4/Makeup/config/target.man1 bit-babbler-0.5/Makeup/config/target.man1 --- bit-babbler-0.4/Makeup/config/target.man1 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/Makeup/config/target.man1 2016-01-15 15:35:53.000000000 +0000 @@ -1,4 +1,4 @@ man1_TYPE = DATA man1_DATA_SRCDIR = $(srcdir)/doc/man man1_DATA_INSTALLDIR = $(mandir)/man1 -man1_DATA_FILES = seedd.1 bbcheck.1 bbctl.1 +man1_DATA_FILES = seedd.1 bbcheck.1 bbctl.1 bbvirt.1 diff -Nru bit-babbler-0.4/Makeup/config/target.vm-conf bit-babbler-0.5/Makeup/config/target.vm-conf --- bit-babbler-0.4/Makeup/config/target.vm-conf 1970-01-01 00:00:00.000000000 +0000 +++ bit-babbler-0.5/Makeup/config/target.vm-conf 2016-01-15 15:35:53.000000000 +0000 @@ -0,0 +1,4 @@ +vm-conf_TYPE = DATA +vm-conf_DATA_SRCDIR = $(srcdir)/libvirt +vm-conf_DATA_INSTALLDIR = /etc/bit-babbler +vm-conf_DATA_FILES = vm.conf diff -Nru bit-babbler-0.4/Makeup/gmake-fragments/makefile.makeup bit-babbler-0.5/Makeup/gmake-fragments/makefile.makeup --- bit-babbler-0.4/Makeup/gmake-fragments/makefile.makeup 2015-12-06 02:25:27.000000000 +0000 +++ bit-babbler-0.5/Makeup/gmake-fragments/makefile.makeup 2015-12-29 17:16:16.000000000 +0000 @@ -169,7 +169,7 @@ echo '#' >> $(1); \ echo '# Copyright 2003 - 2015, Ron ' >> $(1); \ echo >> $(1); \ - echo 'MAKEUP_VERSION = 0.27' >> $(1); \ + echo 'MAKEUP_VERSION = 0.28' >> $(1); \ echo '#MAKEUP_VERBOSE = yes' >> $(1); \ echo >> $(1); \ echo 'MAKEUP_DIR = $$(top_srcdir)/Makeup' >> $(1); \ diff -Nru bit-babbler-0.4/Makeup/Makeup.conf bit-babbler-0.5/Makeup/Makeup.conf --- bit-babbler-0.4/Makeup/Makeup.conf 2015-12-17 11:50:27.000000000 +0000 +++ bit-babbler-0.5/Makeup/Makeup.conf 2016-01-18 03:59:29.000000000 +0000 @@ -2,7 +2,7 @@ # # Copyright 2003 - 2015, Ron -MAKEUP_VERSION = 0.27 +MAKEUP_VERSION = 0.28 #MAKEUP_VERBOSE = yes MAKEUP_DIR = $(top_srcdir)/Makeup diff -Nru bit-babbler-0.4/src/bbcheck.cpp bit-babbler-0.5/src/bbcheck.cpp --- bit-babbler-0.4/src/bbcheck.cpp 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/src/bbcheck.cpp 2016-01-15 15:35:53.000000000 +0000 @@ -666,6 +666,11 @@ usage(); return EXIT_SUCCESS; + case ':': + fprintf(stderr, "%s: missing argument for '%s', try --help\n", + argv[0], argv[optind - 1] ); + return EXIT_FAILURE; + case VERSION_OPT: printf("bbcheck " PACKAGE_VERSION "\n"); return EXIT_SUCCESS; diff -Nru bit-babbler-0.4/src/bbctl.cpp bit-babbler-0.5/src/bbctl.cpp --- bit-babbler-0.4/src/bbctl.cpp 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/src/bbctl.cpp 2016-01-15 15:35:53.000000000 +0000 @@ -181,6 +181,11 @@ usage(); return EXIT_SUCCESS; + case ':': + fprintf(stderr, "%s: missing argument for '%s', try --help\n", + argv[0], argv[optind - 1] ); + return EXIT_FAILURE; + case VERSION_OPT: printf("bbctl " PACKAGE_VERSION "\n"); return EXIT_SUCCESS; diff -Nru bit-babbler-0.4/src/seedd.cpp bit-babbler-0.5/src/seedd.cpp --- bit-babbler-0.4/src/seedd.cpp 2015-12-17 11:49:09.000000000 +0000 +++ bit-babbler-0.5/src/seedd.cpp 2016-01-15 15:35:53.000000000 +0000 @@ -1,5 +1,5 @@ // This file is distributed as part of the bit-babbler package. -// Copyright 2012 - 2015, Ron +// Copyright 2012 - 2016, Ron #ifndef _REENTRANT #error "seedd requires pthread support" @@ -46,6 +46,7 @@ printf("\n"); printf("Options:\n"); printf(" -s, --scan Scan for available devices\n"); + printf(" --shell-mr Output a machine readable list of devices\n"); printf(" -i, --device-id=id Read from only the selected device(s)\n"); printf(" -b, --bytes=n Send n bytes to stdout\n"); printf(" -d, --daemon Run as a background daemon\n"); @@ -57,6 +58,7 @@ printf(" -c, --control-socket=path Where to create the control socket\n"); printf(" --socket-group=grp Grant group access to the control socket\n"); printf(" --watch=path:ms:bs:n Monitor an external device\n"); + printf(" --kernel-refill=sec Max time in seconds before OS pool refresh\n"); printf(" -v, --verbose Enable verbose output\n"); printf(" -?, --help Show this help message\n"); printf(" --version Print the program version\n"); @@ -67,6 +69,9 @@ printf(" -f, --fold=n Set the amount of entropy folding\n"); printf(" -g, --group=n The pool group to add the device to\n"); printf(" --enable=mask Select a subset of the generators\n"); + printf(" --idle-sleep=init:max Tune the rate of pool refresh when idle\n"); + printf(" --suspend-after=ms Set the threshold for USB autosuspend\n"); + printf(" --low-power Convenience preset for idle and suspend\n"); printf(" --no-qa Don't drop blocks that fail QA checking\n"); printf("\n"); printf("Report bugs to support@bitbabbler.org\n"); @@ -107,11 +112,11 @@ unsigned opt_daemon = 0; unsigned opt_kernel = 0; unsigned opt_stdout = 0; - size_t opt_poolsize = 65536; - std::string opt_controlsock = DEFAULT_CONTROL_SOCK; + std::string opt_controlsock; std::string opt_socketgroup; std::string opt_socket_source; + Pool::Options pool_options; Pool::Group::Options::List group_options; BitBabbler::Options default_options; BitBabbler::Options::List device_options; @@ -119,23 +124,32 @@ enum { + SHELL_MR_OPT, LATENCY_OPT, ENABLE_OPT, NOQA_OPT, SOCKET_GROUP_OPT, WATCH_OPT, + KERNEL_REFILL_TIME_OPT, + IDLE_SLEEP_OPT, + SUSPEND_AFTER_OPT, + LOW_POWER_OPT, VERSION_OPT }; struct option long_options[] = { { "scan", no_argument, NULL, 's' }, + { "shell-mr", no_argument, NULL, SHELL_MR_OPT }, { "device-id", required_argument, NULL, 'i' }, { "bitrate", required_argument, NULL, 'r' }, { "latency", required_argument, NULL, LATENCY_OPT }, { "fold", required_argument, NULL, 'f' }, { "group", required_argument, NULL, 'g' }, { "enable", required_argument, NULL, ENABLE_OPT }, + { "idle-sleep", required_argument, NULL, IDLE_SLEEP_OPT }, + { "suspend-after", required_argument, NULL, SUSPEND_AFTER_OPT }, + { "low-power", no_argument, NULL, LOW_POWER_OPT }, { "no-qa", no_argument, NULL, NOQA_OPT }, { "bytes", required_argument, NULL, 'b' }, { "daemon", no_argument, NULL, 'd' }, @@ -147,6 +161,7 @@ { "control-socket", required_argument, NULL, 'c' }, { "socket-group", required_argument, NULL, SOCKET_GROUP_OPT }, { "watch", required_argument, NULL, WATCH_OPT }, + { "kernel-refill", required_argument, NULL, KERNEL_REFILL_TIME_OPT }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, '?' }, { "version", no_argument, NULL, VERSION_OPT }, @@ -169,6 +184,10 @@ opt_scan = 1; break; + case SHELL_MR_OPT: + opt_scan = 2; + break; + case 'i': { BitBabbler::Options bbo = default_options; @@ -254,6 +273,40 @@ break; } + case IDLE_SLEEP_OPT: + try { + if( device_options.empty() ) + default_options.SetIdleSleep( optarg ); + else + device_options.back().SetIdleSleep( optarg ); + } + catch( const std::exception &e ) + { + fprintf( stderr, "%s: error, %s\n", argv[0], e.what() ); + return EXIT_FAILURE; + } + break; + + case SUSPEND_AFTER_OPT: + if( device_options.empty() ) + default_options.suspend_after = StrToScaledUL( optarg ); + else + device_options.back().suspend_after = StrToScaledUL( optarg ); + + break; + + case LOW_POWER_OPT: + if( device_options.empty() ) + { + default_options.SetIdleSleep( "100:0" ); + default_options.suspend_after = 10000; + } else { + device_options.back().SetIdleSleep( "100:0" ); + device_options.back().suspend_after = 10000; + } + pool_options.kernel_refill_time = 3600; + break; + case NOQA_OPT: if( device_options.empty() ) default_options.no_qa = true; @@ -285,7 +338,11 @@ break; case 'P': - opt_poolsize = StrToScaledUL( optarg, 1024 ); + pool_options.pool_size = StrToScaledUL( optarg, 1024 ); + break; + + case KERNEL_REFILL_TIME_OPT: + pool_options.kernel_refill_time = StrToUL( optarg, 10 ); break; case 'G': @@ -310,6 +367,11 @@ usage(); return EXIT_SUCCESS; + case ':': + fprintf(stderr, "%s: missing argument for '%s', try --help\n", + argv[0], argv[optind - 1] ); + return EXIT_FAILURE; + case VERSION_OPT: printf("seedd " PACKAGE_VERSION "\n"); return EXIT_SUCCESS; @@ -342,8 +404,19 @@ if( opt_scan ) { - d.ListDevices(); - return EXIT_SUCCESS; + switch( opt_scan ) + { + case 1: + d.ListDevices(); + return EXIT_SUCCESS; + + case 2: + d.ListDevicesShellMR(); + return EXIT_SUCCESS; + } + + fprintf(stderr, "seedd: unknown device scan option %u\n", opt_scan ); + return EXIT_FAILURE; } else if( d.GetNumDevices() == 0 && ! d.HasHotplugSupport() ) { @@ -355,7 +428,7 @@ pthread_t main_thread = pthread_self(); - Pool::Handle pool = new Pool( opt_poolsize ); + Pool::Handle pool = new Pool( pool_options ); for( Pool::Group::Options::List::iterator i = group_options.begin(), e = group_options.end(); i != e; ++i ) @@ -380,6 +453,9 @@ if( opt_stdout || opt_bytes ) { + if( opt_bytes && opt_controlsock.empty() ) + opt_controlsock = "none"; + #if EM_PLATFORM_MSW setmode( STDOUT_FILENO, O_BINARY ); #endif @@ -393,6 +469,9 @@ watch_sinks.push_back( new SecretSink( *i ) ); + if( opt_controlsock.empty() ) + opt_controlsock = DEFAULT_CONTROL_SOCK; + ControlSock::Handle ctl = CreateControlSocket( opt_controlsock, opt_socketgroup );