11
Nov 17

Guides and tests

Well, made a dog’s breakfast of the bandsaw boxes I was playing at. Couldn’t make the turn needed and the blade came out the side of the blank. Oh well. Now I have two paperweights (and one successful box, out of four blanks). I’ve ordered a one-eighth inch blade off Tuffsaws, that should get here next week and I can try again. I’ll prep new blanks tomorrow for that.

Bit of a waste, those 🙁

Meanwhile the older replacement y-lever for the #5½ arrived and I replaced the existing y-lever with it and it’s been a lot nicer to work with since. And no surprise. This is what the more modern y-lever looks like, two pressed steel parts riveted together:

Unfortunately, the rivet on mine isn’t as tight as it should be and even glue didn’t stop the inevitable end result (and if you’re thinking this would interfere with setting the plane you’d be right):

Meanwhile, back on the main project…

The bridle joints involved are a bit of an oddity; nobody really cuts them often enough to get good at them, at least not off the saw, so these are Richard McGuire’s basic jigs for helping to cut them (and the test sticks in the backgrounds, just a spare bit of poplar that I’m using up).

Four cuts, four jigs and the japanese saw reappears.

Okay, doesn’t look too awful…

Ah. Right. Hm. Odd. No gaps but also torqued right the way over. So check the guides again and yup, the shoulder of the cut was just not tidy enough so out with the chisel and a bit of tweaking and another test joint gets cut…

Ah. Bother. Okay, one guide still needs a wee bit of tweaking and we cut another test joint…

and…

Right. That’ll do I think.

Next step, a 12 degree guide and then on to cut the actual leg&apron joints.

 


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…