{"id":181,"date":"2016-03-16T23:57:42","date_gmt":"2016-03-17T03:57:42","guid":{"rendered":"https:\/\/stump.io\/blog\/?p=181"},"modified":"2016-03-16T23:59:38","modified_gmt":"2016-03-17T03:59:38","slug":"irssi-and-afs","status":"publish","type":"post","link":"https:\/\/stump.io\/blog\/2016\/03\/16\/irssi-and-afs\/","title":{"rendered":"irssi and AFS"},"content":{"rendered":"<p>For the vast majority of my IRC needs, I used to use a Quassel core. But for a couple of more lighthearted channels I hang out in, I instead started using (and have since folded the rest of my IRC setup into) what on its face appears to be a traditional irssi-in-screen setup on a JHU ACM system so I could have scripts loaded to implement some fun little bits of functionality that others in those channels can invoke.<\/p>\n<p>The traditionality ends there, though.<\/p>\n<p>This is because the ACM uses AFS, so my homedir is in AFS. And there are some channels I am archiving, and to do that, irssi needs to be able to access my homedir for as long as it continues to run. So I need something to maintain an AFS token, which also requires maintaining a Kerberos ticket, but I don&#8217;t want this to give irssi access to everything my Kerberos identity is able to do.<\/p>\n<p>So here&#8217;s what I did:<\/p>\n<p>First, I made a new Kerberos principal <code>stump\/irc<\/code> and a keytab and pts entry for it and stored the keytab in my homedir. (As part of the JHU ACM sysadmin team I could just do this myself; we&#8217;d be happy to do the same thing for any user who asks.) Since my own realm is cross-realmed with the ACM, I could have made this principal here in <code>STUMP.IO<\/code>-land instead (and then gotten a ticket and run <code>aklog<\/code> to create the pts entry), but I chose not to. I saved the keytab as <code>~\/irc.keytab<\/code>.<\/p>\n<p>Then I granted the pts entry the minimum permissions it needed on my homedir to meaningfully run irssi with my configuration and archiving. For me, this is <code>l<\/code> on the root level of my homedir, <code>rlidw<\/code> recursively on <code>~\/irclogs<\/code>, <code>rl<\/code> recursively on <code>~\/perl5<\/code> (because I used the wonderful <a href=\"http:\/\/search.cpan.org\/~apeiron\/local-lib\/\">local::lib<\/a> to install some Perl packages to my homedir that are used by my scripts but aren&#8217;t installed system-wide), and <code>rlidw<\/code> recursively on <code>~\/.irssi<\/code>.<\/p>\n<p>Then I wrote a wrapper script to do the runtime setup for starting irssi, as follows:<\/p>\n<pre>#!\/bin\/sh\r\nset -e\r\nTMPDIR=`mktemp -d \/tmp\/stump_irc_XXXXXX`\r\ntrap 'rm -rf \"$TMPDIR\"' 0 1 2 15\r\ncp -a ~\/irc.keytab \"$TMPDIR\"\/keytab\r\nk5start -U -f \"$TMPDIR\"\/keytab -k \"$TMPDIR\"\/krb5cc -t -- irssi \"$@\"<\/pre>\n<p>(The reason for copying the keytab into the temp dir is that <code>k5start<\/code> sets up a PAG, and thereby loses access to the AFS tokens it was started with, before it reads the keytab. And if, like me, you&#8217;re in the [very good!] habit of <code>exec<\/code>-ing the real program when you write a wrapper script, note that you can&#8217;t do this here due to the <code>trap<\/code>.)<\/p>\n<p>Now I can just run that script in a screen and get an irssi that will continue working indefinitely.<\/p>\n<p>&nbsp;<\/p>\n<p>&#8230;or it would, but because AFS only writes back file contents on <code>close<\/code> or <code>fsync<\/code>, each channel&#8217;s archive since the last time it was (re-)joined cannot be viewed from other systems. And worse, if the machine loses power or otherwise shuts down ungracefully (which semi-regularly happens, due to the state of constant flux the ACM systems are in, though things are much better now than they were when I first set this up), those spans of archives fall on the floor, which is very sad.<\/p>\n<p>So I wrote a quick script to call <code>fsync<\/code> on every file descriptor irssi has open (because there was no obvious way to figure out which ones were the logfiles, I just took the brute force approach) every so often. Here it is:<\/p>\n<pre>use strict;\r\nuse warnings;\r\n\r\nuse Irssi;\r\nuse IO::Handle;\r\nuse POSIX;\r\n\r\nmy $VERSION = '0.1';\r\nmy %IRSSI = (\r\n  'authors' =&gt; 'John Stumpo',\r\n  'name' =&gt; 'fsync.pl',\r\n  'description' =&gt; 'Periodically fsync()s all file descriptors irssi has open',\r\n  'license' =&gt; 'Public domain (CC0)',\r\n);\r\n\r\nour $FSYNC_MSECS = 300000; # every 5 minutes\r\n\r\nsub fsync_everything {\r\n  my $io = IO::Handle-&gt;new();\r\n  for (my $fd = 0; $fd &lt; 1024; $fd++) {\r\n    # Sadly, modern Perls don't let you just call POSIX::fsync on an int,\r\n    # insisting that you do it through an IO::Handle, which has the highly\r\n    # undesirable (for us) behavior of closing the file descriptor when the\r\n    # object goes away without any documented-reliable way to override this.\r\n    # Therefore, dup the fd first, and fsync and close the duplicate. The dup\r\n    # also serves as a check that $fd is actually an open file descriptor.\r\n    my $duped_fd = POSIX::dup($fd);\r\n    next unless defined($duped_fd);\r\n    $io-&gt;fdopen($duped_fd, 'r');\r\n    $io-&gt;sync();\r\n    $io-&gt;close();\r\n  }\r\n}\r\n\r\nIrssi::timeout_add($FSYNC_MSECS, \\&amp;fsync_everything, undef);<\/pre>\n<p>As the comment says, it would have been nice to just call <code>POSIX::fsync<\/code> on each int from 0 to some reasonable value and ignore errors (as I could by using <code>fsync<\/code> from C or <code>os.fsync<\/code>\u00a0from Python), but Perl doesn&#8217;t let you do that anymore without going through a handle object that closes the file descriptor when it goes away and doesn&#8217;t give you a choice about that, hence the dance with <code>POSIX::dup<\/code>. (And closing a <code>dup<\/code>&#8216;d file descriptor that goes into AFS doesn&#8217;t flush cached writes\u00a0&#8211; only when the last file descriptor closes does that happen, aside from <code>fsync<\/code> being\u00a0called.)<\/p>\n<p>Happy IRCing on AFS!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For the vast majority of my IRC needs, I used to use a Quassel core. But for a couple of more lighthearted channels I hang out in, I instead started using (and have since folded the rest of my IRC setup into) what on its face appears to be a traditional irssi-in-screen setup on a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/posts\/181"}],"collection":[{"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/comments?post=181"}],"version-history":[{"count":10,"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/posts\/181\/revisions"}],"predecessor-version":[{"id":233,"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/posts\/181\/revisions\/233"}],"wp:attachment":[{"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/media?parent=181"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/categories?post=181"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/stump.io\/blog\/wp-json\/wp\/v2\/tags?post=181"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}