03
Nov 17

Flattening and boxes

So, started off flattening the apron. This went pretty well after I switched over to the #5 and sharpened it up a bit. The narrower blade means less pushing effort and that seemed to help a lot. And then it was time to thickness down from an inch to 3/4 of an inch.

This is not my favourite activity. And honestly, if they made an induction motor benchtop thicknesser I’d have bought one already, but unfortunately they’re all universal motor things — and lunchbox thicknessers, even if you fit them with helical blades and all the fancy doo-dads, are just too damn noisy to run in a housing estate. You’ll wind up triggering a torch-and-pitchfork party of your very own if you do that round here once too often. So until I have a larger shed and room for a larger, possibly older, floorstanding planer/thicknesser, I have to do this part by hand. At least Sid makes the task easier with his ridiculous level of camber…

Anyway, the board was a manageable size, so push hard and on we go. And switching over to the #5 for the last mm or so to arrive in a controlled manner and…

Flat and at thickness. I gauged out for the two aprons and set it aside and ripped down the middle a little later on with the bandsaw (using the new tuffsaws blade – makes a rather surprising difference, those things, much cleaner cut and less drift).

Then on to mucking about…

So the glue-up was messy and there are steps all over, but apparently that’s to be expected. Next step, sand the badgers off everything with my handy dandy disk sander. Which I don’t have one of. Bugger. Well…

Sod it, I started this thing on a bandsaw, might as well keep up the machine operator vibe…

Didn’t want to keep all of your fingers, did you?

Hm. Not terrible for a first try. Slap some shellac on it…

And some felt for the inside lining and call it done:

Well, not absolutely awful. Okay, so it is if you look close – there’s no room in there, the edges aren’t parallel, the drawer’s a bit gappy, and so on. But it was fun to try it and I have some ideas for a nicer one. Need more practice though, I keep getting lost in the sequence of cuts on these things. Well, in that vein…

This one might be interesting, I used the new tuffsaws fine-tooth blade. Much smoother cut, but I don’t think the bandsaw will ever be a precision tool. Hell of a lot friendlier to the nose when you plug it into the dust collection though…


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…


02
Nov 17

Progress and mucking about

So I figured I’d start by playing with the new toy and taking some test cuts.

The blade runs sort-of true. Well, I wasn’t expecting laser levels of perfection here, but the guides really are letting things down. The lower thrust bearing can’t be backed off readily to adjust it so it’s not poking the blade out of true, which is disappointing. And I really can’t run this thing without dust extraction or the whole lower case clogs up and the bearing itself locks up. Well, I knew machinery would counter its speed by increasing the amount of faffing about needed to support it. This is why you usually mount this stuff permanently where you have room to manoeuvre around it. But again, 8’x6′ shed, no room to think, let alone manoeuvre, so we need to make do.

It can’t cut very tight corners, a 2cm radius seems about the most it’s comfortable with. But that could still work. I was playing about making a bandsaw box. They’re not too terrible to do.

Gluing up for these boxes is a bit of a faff mind you.

Well, quite a lot of a faff depending on how badly you design the sodding things. Oh well.

And the three blades I ordered from tuffsaws arrived.

One for very rough work or even small resawing work (but really, you’re talking about resawing stuff that’s at most 70-80mm wide so I’m guessing that’s going to be underused). One slightly more sturdy blade than the one that came with the bandsaw to use for general-purpose stuff, and a very fine-toothed narrow blade to do curving work.

Tuffsaws do have a one-eighth inch blade as well, might try that if the quarter-inch one doesn’t do the job.

Then I carried on with the new project, laying out the rough rips for legs and aprons:

Finally getting to use my new panel gauge in anger. Works quite well for rough layout, but I need to sharpen that pin, it’s not the finest gauge line in the world.

Then I used the new toy to make the rough cuts.

Definitely not up to finished work levels of cleanness, but it cut through inch-thick oak like it was foam, so it saves a bit of work (though the faffing about setting up and cleaning down after using the bandsaw is just a pain in the fundament so handsaws definitely aren’t out of a job yet). I’ll flatten these tomorrow and thickness them, then rip out the individual legs and aprons (there’s two in each board).