02
Nov 17

fork()ing and fstat()ing in JRuby using FFI on linux

Sometimes, $DAYJOB can get kindof technical. For reasons I won’t go into here because NDA, the following axioms are true for this puzzle:

  • we have to work in JRuby
  • we are in a plugin within a larger framework providing a service
  • we have to restart the entire service
  • we don’t have a programmatic way to do so
  • we don’t want to rely on external artifacts and cron

Now, this isn’t the initial framing set of axioms you understand; this is what we’re facing into after a few weeks of trying everything else first.

So; obvious solution, system('/etc/init.d/ourService restart').
Except that JRuby doesn’t do system(). Or fork(), exec(), daemon(), or indeed any kind of process duplication I could find. Oh-kay, so we can write to a file, have a cronjob watch for the file and restart the service and delete the file if it finds it. Except that for Reasons (again, NDA), that’s not possible because we can’t rely on having access to cron on all platforms.

Okay. Can we cheat?

Well, yes… allegedly. We can use the Foreign Function Interface to bind to libc and access the functions behind JRuby’s back.

require 'ffi'

module Exec
   extend FFI::Library

   attach_function :my_exec, :execl, [:string, :string, :varargs], :int
   attach_function :fork, [], :int
end

vim1 = '/usr/bin/vim'
vim2 = 'vim'
if Exec.fork == 0
   Exec.my_exec vim1, vim2, :pointer, nil
end

Process.waitall

Of course, I’m intending to kill the thing that fires this off, so a little more care is needed. For a start, it’s not vim I’m playing with. So…

module LibC
   extend FFI::Library

   ffi_lib FFI::Library::LIBC

   # Timespec struct datatype
      class Timespec < FFI::Struct
      layout :tv_sec, :time_t,
      :tv_nsec, :long
   end

   # stat struct datatype
   # (see /usr/include/sys/stat.h and /usr/include/bits/stat.h)
   class Stat < FFI::Struct
      layout :st_dev, :dev_t,
             :st_ino, :ino_t,
             :st_nlink, :nlink_t,
             :st_mode, :mode_t,
             :st_uid, :uid_t,
             :st_gid, :gid_t,
             :__pad0, :int,
             :st_rdev, :dev_t,
             :st_size, :off_t,
             :st_blksize, :long,
             :st_blocks, :long,
             :st_atimespec, LibC::Timespec,
             :st_mtimespec, LibC::Timespec,
             :st_ctimespec, LibC::Timespec,
             :__unused0, :long,
             :__unused1, :long,
             :__unused2, :long,
             :__unused3, :long,
             :__unused4, :long
   end

   # Filetype mask
   S_IFMT = 0o170000

   # File types.
   S_IFIFO = 0o010000
   S_IFCHR = 0o020000
   S_IFDIR = 0o040000
   S_IFBLK = 0o060000
   S_IFREG = 0o100000
   S_IFLNK = 0o120000
   S_IFSOCK = 0o140000

   attach_function :getpid, [], :pid_t
   attach_function :setsid, [], :pid_t
   attach_function :fork, [], :int
   attach_function :execl, [:string, :string, :string, :varargs], :int
   attach_function :chdir, [:string], :int
   attach_function :close, [:int], :int
   attach_function :fstat, :__fxstat, [:int, :int, :pointer], :int
end

So that’s bound a bunch of libc functions for use in JRuby. But why __fxstat() instead of fstat()? Interesting detail; the stat() function family aren’t in libc, at least not on most modern linux platforms. They’re in a small static library (libc_unshared.a in centOS). There’s usually a linker directive that makes that transparent but here we’re acting behind the scenes so we don’t get that niceity so we directly access the underlying xstat() functions instead.

I need to close some network ports (or the restart goes badly because the child process inherits the ports’ file descriptors and someone didn’t set them to close on exec()). A small helper function is useful here:

# Helper function to check if a file descriptor is a socket or not
def socket?(fd)
   # data structure to hold the stat_t data
   stat = LibC::Stat.new

   # JRuby's IO object types can't seem get a grip on fd's inherited from
   # another process correctly in a forked child process so we have
   # to FFI out to libc.
   rc = LibC.fstat(0, fd, stat.pointer)
   if rc == -1
      errno = FFI::LastError.error
      false
   else
      # Now we do some bit twiddling. In Octal, no less.
      filetype = stat[:st_mode] & LibC::S_IFMT

      if filetype == LibC::S_IFSOCK
         true
      else
         false
      end
   end
rescue => e
   false
end

And now the actual restart function itself:

def restart
   pid = LibC.getpid
   rc = LibC.chdir('/')
   if rc == -1
      errno = FFI::LastError.error
      return errno
   end

   # close any open network sockets so the restart doesn't hang
   fds = Dir.entries("/proc/#{pid}/fd")
   fds.each do |fd|
      # skip . and .. which we pick up because of the /proc approach to
      # getting the list of file descriptors
      next if fd.to_i.zero?

      # skip any non-network socket file descriptors as they're not going to
      # cause us any issues and leaving them lets us log a little longer.
      next unless socket?(fd.to_i)

      # JRuby's IO objects can't get a handle on these fd's for some reason,
      # possibly because we're in a child process. So we use libc's close()
      rc = LibC.close(fd.to_i)
      next if rc.zero?
      errno = FFI::LastError.error
      return errno
   end

   # We're now ready to fork and restart the service
   rc = LibC.fork
   if rc == -1
      # If fork() failed we're probably in a world of hurt
      errno = FFI::LastError.error
      return errno
   elsif rc.zero?
      # We are now the daemon. We can't hang about (thanks to 
      # JRuby's un-thread-safe nature) so we immediately swap out our 
      # process image with that of the service restart script. 
      # This marks the end of execution of this thread and there is no return.
      LibC.execl '/etc/init.d/ourService', 'ourService', 'restart', :pointer, nil
   end
rescue => e
# Handle errors here (removed for clarity)
end

An interesting problem to solve, this one. And by “interesting” I mean “similar to learning how to pull teeth while only able to access the mouth via the nose”. But in case it’s of use to someone…


14
Jul 17

DerBlinkenLightzen!

8-pin microcontrollers are pretty neat things. I’d only used the larger PICs before, I didn’t see how these could be all that useful. And the 16C74s could do more… twenty years ago. These days, the 8-pin PICs have caught up with the older models in internal features, if not pincount. It’s really quite remarkable:

Same amount of program memory as the one from PICrat (actually, the modern version of the chip from PICrat, with the fancy flash memory), the CPU’s faster, it has more RAM, it has an internal EEPROM for persistent storage, same number of digital comms, one less CCP (but 32 fewer pins, so okay), half as many ADC channels (again, 32 fewer pins) but higher precision on those channels, same number of timers, a new comparator, a wider range of operating voltages and it takes less power (much less, it turns out).

Yes, it’s an 8-pin microcontroller; most of us forget that these shiny PCs are only a tiny, tiny, tiny fraction of the deployed computers in the world. These PICs probably outnumber them, and Moore’s law is in full swing on that side of the industry still.

So anyway, a fortnight or so back I’m watching a Big Clive video about making up a DMX cable tester with a PIC12F635…

…and I thought I’d like to play with a 12F so I bought a tube of them (they’re a lot less than a euro apiece) and a programmer. I splurged and got one of the higher-end 12Fs, the 12F1840, and then got to wrestling with resurrecting a 20-year-old toolchain.

GPASM, it turns out, has improved considerably, and there’s finally a CLI for the PICkit3 (I remember it coming out and the sheer daftness of the dropping of the PICkit2’s pk2cmd program that rendered the PICkit3 useless on launch to anyone running linux at the time). It only took a few hours of rereading manuals and poking things randomly before getting the flashing LED “hello world” program to work.

So, what’s next? Well, apart from the really neat WS2812 array in the vice there, which has a fun data protocol that’ll need a bit of programming work to hook up to (there is a C library for it, but I’ll have to rework that in assembly for my toolchain), and ignoring this idea I have for an interface between a sensor and an I2C link to a raspberry Pi that I have for another project, there’s also a little box dropped off by the postman to one side here. See, ebay’s one of those places where you can get fun toys for next to nothing if you look and are patient…

70 5×7 LED matrix displays for €17. 70! Mind you, I’ll need a few 74 series parts, some darlington arrays and a bit of wiring, but this could be fun…


10
Jul 17

PICRat

So twenty years ago this year I graduated, and just after that (and before I started chasing a PhD), I did some work on a project to create a platform for building a micromouse robot. Not a speed run winner or anything, but just a bog standard, easy to use, off-the-shelf black box platform.

This was 1997, remember, these things technically did exist but they were research project level expensive and finicky and whole companies were making good money building them; Arduino is still six years out at this point and it would be years before you saw the level of available support hardware it enjoys today. Lego Mindstorms is years away and Lego’s RCX doesn’t come out till the following year. The BASICStamp is the most popular solution to beginner hobbyists and it costs the guts of $100 at the time if I recall correctly. You had PC104 based systems available but that was about as close as you got to a Raspberry Pi and your average PC104 was about as expensive as your average desktop PC. So for beginners you pretty much had Microchip’s PIC or its newfangled Amtel clones as your highest bang-for-buck choice of small simple microcontroller. There were older choices still around of course, the 8051 and 68HC11 will probably still be there after the cockroaches die from radiation poisoning.

So at this stage if you wanted to build a micromouse and were a beginner with just about a screwdriver to your name in terms of tooling, you were going to have to roll the whole thing yourself from the ground up, sourcing parts from dumpster diving and with zip-ties, the battery wires and the circuit boards themselves being major structural elements, as with the one Ian and myself built, Brandon:

Brandon Micromouse

Brandon Micromouse

So the idea with PICRat was to be something like the dozens of kits you can now buy off the shelf as a beginner. Unfortunately as a commercial idea it was horribly badly conceived – timed just as a wave of fully developed, well funded alternatives were about to land on the market (and in fact create the market), developed without any facilities or tools or manpower beyond one graduate so new he hadn’t even finished the graduating ceremonies yet, and done without any real plan or oversight or direction other than “go build a robot that a PC can connect to by serial port so students can do a micromouse project in a college course without having to do all the mechatronics legwork”.

Yes, serial port. RS232C baby. USB 1.0 is a year old at this stage but nobody is using it, and USB 1.1 (the first actually used version) is a year away. WiFi? Good luck, 802.11a only comes out this year, 802.11b next year and it’s years before that costs less than a grand in hardware. We have radio modems at this point and yes, that is exactly what it sounds like and it was an actual improvement because at least with those we didn’t have to go to ComReg to get experimental radio licences to use them…

Anyway, the spec I was given sounds like it’s complete enough… if you know the square root of nothing about building a commercial product (let alone a robot). And I knew just barely under the square root of nothing about commercial products at the time so I dove right in 😀 File that under “learning opportunities seized with both hands and all available teeth”. These days I’ve learned enough to back away slowly from the crazy person suggesting that I suffer for six months before the project gets dropped and its failure blamed on the nearest lightning rod (ie. the one guy working on building in three months with no tools what commercial companies were taking twenty man-years in fully equipped labs to produce).

Not a fun time. However, I came across the code for the project this weekend while playing with a PIC12 chip (more on that later), and hey, historical value at least and I’m pretty sure I still own the IP (no, seriously, it’s a whole thing I’m not getting into here, but shambolic is a pretty solidly descriptive term of that time), so I put it up on github for anyone who’s interested in how you hook a PIC 16C74 up to a PC and act like a state machine controlled over a serial line. By the way, that’s 16C74 – that’s how old this is, you had to burn the code into an eprom in the chip because the F series (that are all you can get these days) with the fancy FLASH memory were an order of magnitude more expensive…

Of course, it wasn’t just code, it was an inch-thick (2.5cm for us metric types) manual to learn, from scratch, for a non-von-neumann architecture (which college had failed to mention as a thing that existed in computing) and in assembly because the C compilers for 8-bit microchip MCUs were absent then and thin on the ground even now if you’re not able to drop a few hundred euro on one, and of course there was some wire-wrapping hardware fabricobbling:

And yes, this was before digital cameras were retailing at a cent apiece for the sensor, how did you tell? That’s a scanned photo from a normal film camera (I think we were doing a lab equipment audit at the time or something). It’s not an Instagram filter, it was taken with black and white film because that was still cheaper to develop than colour…
Top one’s the first prototype. I think the bottom one was the second prototype (it’s been twenty years and I can’t find my notes…)

It did eventually get used, by another new graduate equally haplessly roped in to do another robotics project without tools or anything else such a thing needs. He built his own board using the design and parts from PICRat and that was the end of the hardware:

And thus ends the saga of about five months of my life way back when. It was a lesson I didn’t learn at the time, which nobody explained to me at the time, which I’m now sure my supervisor at that point either didn’t know himself or didn’t know how to teach it, a lesson I have since learned the hard way a few times over. I guess that’s part of going from being an engineering graduate that knows the technology to being an engineer who knows engineering 😀

For those new hands who don’t have anyone to pass it on, let me restate it here (but anyone in that field can tell you as well) — build as little as possible yourself, and focus everything on that little. As Red Whittaker once told two rather new researchers who were wholly out of their depth and sinking at the time, you can do anything; you just can’t do everything.