<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: salotz</title>
    <description>The latest articles on DEV Community by salotz (@salotz).</description>
    <link>https://dev.to/salotz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F343610%2F9e04687f-c73b-475d-9806-188f2ee93985.png</url>
      <title>DEV Community: salotz</title>
      <link>https://dev.to/salotz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/salotz"/>
    <language>en</language>
    <item>
      <title>Cron alternative with runit and snooze</title>
      <dc:creator>salotz</dc:creator>
      <pubDate>Fri, 23 Oct 2020 22:25:36 +0000</pubDate>
      <link>https://dev.to/salotz/cron-alternative-with-runit-and-snooze-56n0</link>
      <guid>https://dev.to/salotz/cron-alternative-with-runit-and-snooze-56n0</guid>
      <description>&lt;p&gt;I have tried many times to use &lt;code&gt;cron&lt;/code&gt; effectively, but ultimately this&lt;br&gt;
ends in failure each time. Lets look at why I find cron onerous and a&lt;br&gt;
solution I came up with[1].&lt;/p&gt;

&lt;p&gt;Skip to this section if you just want to see the solution.&lt;/p&gt;
&lt;h1&gt;
  
  
  Non-Issues
&lt;/h1&gt;

&lt;p&gt;Conceptually specifying when to run things with a special syntax is not&lt;br&gt;
a bad idea and not why I find cron hard to use.&lt;/p&gt;

&lt;p&gt;Just as a reminder this is what a cron job I found in my system for the&lt;br&gt;
&lt;code&gt;dma&lt;/code&gt; mail agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/5 *    * * *   root    [ -x /usr/sbin/dma ] &amp;amp;&amp;amp; /usr/sbin/dma -q1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The meaning and examples for the timing specs are well known and not&lt;br&gt;
really an issue here. There are lots of alternatives that try to improve&lt;br&gt;
on this, this is not one of them (although it is different in this&lt;br&gt;
regard).&lt;/p&gt;
&lt;h1&gt;
  
  
  Sys-Admin Orientation
&lt;/h1&gt;

&lt;p&gt;My first issue with &lt;code&gt;cron&lt;/code&gt; is that it is inseparable (or at least&lt;br&gt;
practically so) from the base *nix system. That means you can't run it&lt;br&gt;
as a user as it is meant to be the scheduled thing runner for a&lt;br&gt;
multi-user system. So while you can have the system run things on behalf&lt;br&gt;
of you as a user you can't really control it.&lt;/p&gt;

&lt;p&gt;In this day and age, multi-user systems are becoming pretty rare outside&lt;br&gt;
HPCC systems in academia and gov't labs. For most people they want to&lt;br&gt;
run simple recurring tasks on their desktop, laptop, VPS server, or&lt;br&gt;
maybe even a homelab server. In all these cases user's are usually&lt;br&gt;
simply mechanisms for implementing access control and not for distinct&lt;br&gt;
human entities.&lt;/p&gt;

&lt;p&gt;This kind of sysadmin oriented baggage is pretty pervasive in *nix&lt;br&gt;
systems and cron is not alone here.&lt;/p&gt;

&lt;p&gt;Because cron is so deeply embedded into the lizard brain of *nix it is&lt;br&gt;
necessary to have a properly running and configured cron for the&lt;br&gt;
stability of your system.&lt;/p&gt;

&lt;p&gt;As a reasonably competent user of linux desktops, the thought of&lt;br&gt;
inadvertently messing up &lt;code&gt;/etc/crontab&lt;/code&gt; and its ilk is enough to keep me&lt;br&gt;
away from it.&lt;/p&gt;

&lt;p&gt;For instance where exactly am I supposed to put a cron job? On Ubuntu&lt;br&gt;
20.04 (KDE Neon actually) I see this in my &lt;code&gt;/etc&lt;/code&gt; dir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; ls /etc/ | grep cron
anacrontab
cron.d
cron.daily
cron.hourly
cron.monthly
crontab
cron.weekly
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally this is highly variable distro to distro.&lt;/p&gt;

&lt;p&gt;For instance here is part of the &lt;code&gt;man&lt;/code&gt; page for cron on my system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEBIAN SPECIFIC
       Debian introduces some changes to cron that were not originally available  upstream.   The  most
       significant changes introduced are:

       —      Support for /etc/cron.{hourly,daily,weekly,monthly} via /etc/crontab,

       —      Support for /etc/cron.d (drop-in dir for package crontabs),

       —      PAM support,

       —      SELinux support,

       —      auditlog support,

       —      DST and other time-related changes/fixes,

       —      SGID crontab(1) instead of SUID root,

       —      Debian-specific file locations and commands,

       —      Debian-specific configuration (/etc/default/cron),

       —      numerous other smaller features and fixes.

       Support  for  /etc/cron.hourly,  /etc/cron.daily, /etc/cron.weekly and /etc/cron.monthly is pro‐
       vided in Debian through the default setting of the /etc/crontab file (see the system-wide  exam‐
       ple  in crontab(5)).  The default system-wide crontab contains four tasks: run every hour, every
       day, every week and every month.  Each of these tasks will execute run-parts providing each  one
       of the directories as an argument.  These tasks are disabled if anacron is installed (except for
       the hourly task) to prevent conflicts between both daemons.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This goes on for several more paragraphs of neckbeard-speak that I don't&lt;br&gt;
pretend to understand. Am I running &lt;code&gt;anacron&lt;/code&gt;? Why? What is &lt;code&gt;run-parts&lt;/code&gt;?&lt;br&gt;
Its obvious a lot of this is for security purposes that are important in&lt;br&gt;
enterprise environments that a professional sysadmin is paid a hefty&lt;br&gt;
salary to comprehend. So there is probably a simple answer to this mess,&lt;br&gt;
except I don't have direct access to the people who made this mess to&lt;br&gt;
ask them.&lt;/p&gt;

&lt;p&gt;This is a scenario where reading the manual leaves me more confused than&lt;br&gt;
I started. I'll probably get chastised for overcomplicating things. In&lt;br&gt;
pre-emptive response I will leave this quote from James Clear's mailing&lt;br&gt;
list I got today:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To simplify before you understand the details is ignorance.&lt;/p&gt;

&lt;p&gt;To simplify after you understand the details is genius.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm in the former category.&lt;/p&gt;

&lt;p&gt;I would &lt;em&gt;like&lt;/em&gt; to schedule backups to happen everynight, but I would&lt;br&gt;
rather not upset the fragile balance distro maintainers have made to&lt;br&gt;
keep your desktop running "smoothly".&lt;/p&gt;

&lt;p&gt;Cron is simply not meant for a simple user like me.&lt;/p&gt;
&lt;h1&gt;
  
  
  Control &amp;amp; Supervision
&lt;/h1&gt;

&lt;p&gt;The second major problem I had with cron was the real lack of control&lt;br&gt;
you have over it. You write text into the file and cron goes off and&lt;br&gt;
does its thing, just like Ritchie and Thompson intended.&lt;/p&gt;

&lt;p&gt;Everyone with a modicum of understanding of why databases exist sees the&lt;br&gt;
obvious issues with inconsistency that this has. So no longer can you&lt;br&gt;
just edit &lt;code&gt;/etc/crontab&lt;/code&gt; but rather you are supposed to go through&lt;br&gt;
helper tools (like &lt;code&gt;crontab&lt;/code&gt;) which does all manner of locking and&lt;br&gt;
gatekeeping to desparately pretend its a database.&lt;/p&gt;

&lt;p&gt;Using tools that automatically open up editors is another pet-peeve of&lt;br&gt;
mine since you now are bringing in a lot of other assumptions about the&lt;br&gt;
configuration of your system. I hope you know how to safely close files&lt;br&gt;
in &lt;code&gt;nano&lt;/code&gt; and &lt;code&gt;vi&lt;/code&gt;! Further, I like to keep all my configurations in a&lt;br&gt;
central configuration directory that I can use normal version control&lt;br&gt;
etc. How am I supposed to "load" these into the cron jobs without&lt;br&gt;
manually copy-pasting, at least if your going by the "manual". Aren't&lt;br&gt;
these systems supposed to be scriptable? [2]&lt;/p&gt;

&lt;p&gt;As we'll get to in the actual solution I'm presenting here, the loading,&lt;br&gt;
unloading, restarting, pausing etc. of jobs is eerily similar to the&lt;br&gt;
feature sets used in PID 0 programs like &lt;code&gt;systemd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Indeed &lt;code&gt;systemd&lt;/code&gt; has a similar timer system, which has much better&lt;br&gt;
control, but again suffers from being sysadmin oriented. Also writing&lt;br&gt;
unit files blows. I do it, but I try not to.&lt;/p&gt;

&lt;p&gt;Back to cron though, the only solution to this I have found is to&lt;br&gt;
install a program called &lt;a href="https://cronitor.io/"&gt;cronitor&lt;/a&gt;, which is like&lt;br&gt;
a freemium tool that really isn't that scriptable either due to the&lt;br&gt;
(very nice) terminal UI. This is useful, but I don't really see this as&lt;br&gt;
something I can expect to have going into the future on all my systems.&lt;/p&gt;
&lt;h1&gt;
  
  
  Observability &amp;amp; Logging Utilities
&lt;/h1&gt;

&lt;p&gt;I was able to get through all the issues above through sheer force of&lt;br&gt;
will, but ultimately I was done in by the necessary interaction of cron&lt;br&gt;
to two even more obtuse systems in *nix: email and logging.&lt;/p&gt;

&lt;p&gt;Email is sort of baked into cron and from what I can tell is the main&lt;br&gt;
way of getting notifications that running things had issues.&lt;/p&gt;

&lt;p&gt;For instance if your trying to run a script and you got the path wrong&lt;br&gt;
you'll need a working email server for cron to send a message. And if&lt;br&gt;
you give a mouse a cookie…&lt;/p&gt;

&lt;p&gt;Then you'll need to make sure your mail boxes for the users on your&lt;br&gt;
system are working, you know where they are and you have a client for&lt;br&gt;
reading them.&lt;/p&gt;

&lt;p&gt;This seems like a huge dependency to have to just get a message about a&lt;br&gt;
job having an error in it?&lt;/p&gt;

&lt;p&gt;Shouldn't there just be a log file I can pop open in a text editor (like&lt;br&gt;
Ritchie and Thompson intended goddamit)?&lt;/p&gt;

&lt;p&gt;For me and I'm sure many others, I've never had a need to interact with&lt;br&gt;
intra-system email. Even on the HPCC systems I worked in for my PhD this&lt;br&gt;
is basically an unused feature.&lt;/p&gt;

&lt;p&gt;When you do get jobs running without error (and thus no reason to email&lt;br&gt;
you), you'll want to see their logs too.&lt;/p&gt;

&lt;p&gt;In cron, its completely up to you to create and manage the lifecycle of&lt;br&gt;
logs in your system. While in theory this is a good thing since it&lt;br&gt;
allows you to not be locked into something you hate, in practice for&lt;br&gt;
non-neckbeards it involves you having detailed knowledge of yet another&lt;br&gt;
complex subsystem.&lt;/p&gt;

&lt;p&gt;I simply don't know where to even start here. The cron docs say to use&lt;br&gt;
&lt;code&gt;rsyslogd&lt;/code&gt;, which I don't know how to use. Furthermore, my system is&lt;br&gt;
using systemd which has nice commands that show you the latest in the&lt;br&gt;
logs. Is this subsystem disjoint? It wouldn't suprise me that they would&lt;br&gt;
jump through different calcified hoops to keep 40 year old things&lt;br&gt;
running.&lt;/p&gt;

&lt;p&gt;Again there is probably a simple answer to all this, but its one that&lt;br&gt;
reading the man page can't get you. I don't log a lot of stuff, but&lt;br&gt;
mistakes happen and logs can fill up a &lt;code&gt;/&lt;/code&gt; partition scarily easy.&lt;/p&gt;
&lt;h1&gt;
  
  
  Runit tutorial
&lt;/h1&gt;

&lt;p&gt;I've contemplated and even tried a few alternatives to cron. This one&lt;br&gt;
was discovered while throwing stones at other birds. I was pleasantly&lt;br&gt;
surprised.&lt;/p&gt;

&lt;p&gt;Basically it boils down to using a process supervisor called&lt;br&gt;
&lt;a href="http://smarden.org/runit/"&gt;runit&lt;/a&gt; and a fancy big brother to the unix&lt;br&gt;
&lt;code&gt;sleep&lt;/code&gt; command called&lt;br&gt;
&lt;a href="https://github.com/leahneukirchen/snooze"&gt;snooze&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Runit was originally meant to be a replacement for older init systems&lt;br&gt;
like &lt;code&gt;sysvinit&lt;/code&gt; that is cross-platform as well as very simple and not&lt;br&gt;
requiring many libraries. This was to suite it as part of the core&lt;br&gt;
system to bootstrap everything else (think cron and &lt;code&gt;sshd&lt;/code&gt; etc.).&lt;/p&gt;

&lt;p&gt;While these are nice characteristics they aren't killer for us here.&lt;br&gt;
However, nowadays its pretty popular with the anti-systemd crowd. Its an&lt;br&gt;
option for init systems in Gentoo and others and is the default in Void&lt;br&gt;
linux. Besides its simplicity it is pretty easy to get up and configured&lt;br&gt;
and you just run a few shell scripts to get everything going.&lt;/p&gt;

&lt;p&gt;I have a few complaints&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; the documentation is kind of nonlinear and doesn't give you a
walkthrough of how to use the whole system [3].&lt;/li&gt;
&lt;li&gt; commands are disjointed and spread between a number of executables
and use of standard unix commands like &lt;code&gt;ln&lt;/code&gt; and &lt;code&gt;rm&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second point is actually a feature where each of the little&lt;br&gt;
components can be used standalone. However, this makes it a little more&lt;br&gt;
confusing to wrap your head around and I found myself constantly&lt;br&gt;
reviewing my notes to know which command to use.&lt;/p&gt;

&lt;p&gt;I solved this with a few shell functions, but I would like to see a&lt;br&gt;
wrapper CLI to make it a little centralized conceptually (and to add a&lt;br&gt;
few convenience features) for those that would want it.&lt;/p&gt;

&lt;p&gt;Reading these articles also helped in understanding it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mikeperham.com/2014/07/07/use-runit/"&gt;https://www.mikeperham.com/2014/07/07/use-runit/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rubyists.github.io/2011/05/02/runit-for-ruby-and-everything-else.html"&gt;https://rubyists.github.io/2011/05/02/runit-for-ruby-and-everything-else.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even after reading these I had to muck around and figure a bunch of&lt;br&gt;
little details out so I thought I would throw my own little tutorial on&lt;br&gt;
the pile to hopefully save some people's time and make runit a little&lt;br&gt;
more approachable.&lt;/p&gt;

&lt;p&gt;Luckily on Ubuntu 20.04 its really easy to get runit installed and&lt;br&gt;
running as a systemd service. Just install using apt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;runit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This even starts the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status runit
&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; runit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normally there are 3 &lt;em&gt;stages&lt;/em&gt; (i.e. states) runit has:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Single run tasks on startup&lt;/li&gt;
&lt;li&gt; Process supervision: starting, stopping, restarting services&lt;/li&gt;
&lt;li&gt; Shutting services down as the system goes down&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Because we aren't using runit as a PID 0 init system, we only care about&lt;br&gt;
stage 2 &amp;amp; 3. The &lt;code&gt;apt&lt;/code&gt; installation takes care of this for us&lt;br&gt;
thankfully.&lt;/p&gt;

&lt;p&gt;So you should see the following directories appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/etc/runit&lt;/code&gt;
stages and runlevel stuff, ignore this (for now).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/sv&lt;/code&gt;
The services directory, this is where you author things.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/service&lt;/code&gt;
This is where "enabled" services are put.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll call these different directories by the environment variable I&lt;br&gt;
refer to them as. I put this in my &lt;code&gt;~/.profile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## runit known env variables&lt;/span&gt;

&lt;span class="c"&gt;# the active service directory to query for state, this is what it is&lt;/span&gt;
&lt;span class="c"&gt;# default, but I like to set so its easier for me to disable services&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SVDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/service"&lt;/span&gt;

&lt;span class="c"&gt;# SNIPPET: the normal wait time&lt;/span&gt;
&lt;span class="c"&gt;# export SVWAIT=7&lt;/span&gt;

&lt;span class="c"&gt;## my vars, not recognized by any runit tools&lt;/span&gt;

&lt;span class="c"&gt;# this is the standard directory of where services are put for the&lt;/span&gt;
&lt;span class="c"&gt;# system. The SerVice LIBrary&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SVLIB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/etc/sv"&lt;/span&gt;

&lt;span class="c"&gt;# log dir&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SVLOG_SYS_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/local/log/runit"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To define a &lt;strong&gt;service&lt;/strong&gt; you make a directory in &lt;code&gt;SVLIB&lt;/code&gt; with some&lt;br&gt;
specially named shell scripts. Mine has these directories in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; ls $SVLIB
backup  hello  printer_live  recollindex  test_env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each one is a specific service. Lets first look at &lt;code&gt;hello&lt;/code&gt; to get a&lt;br&gt;
simple picture of what these are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; ls $SVLIB/hello
finish  log  run  supervise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most important one is &lt;code&gt;run&lt;/code&gt; which is a shell script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="c"&gt;# run the service&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; :
&lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello"&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just prints "Hello" to stdout and then waits 2 seconds.&lt;/p&gt;

&lt;p&gt;This service isn't being run yet. For that you need to put it into the&lt;br&gt;
&lt;code&gt;SVDIR&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SVLIB&lt;/span&gt;&lt;span class="s2"&gt;/hello"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SVDIR&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the status of the service with the &lt;code&gt;sv&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; sudo sv status $SVDIR/hello
run: /etc/service/hello: (pid 664634) 193s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the status of all services similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sv status &lt;span class="nv"&gt;$SVDIR&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; sudo sv status $SVDIR/hello
down: /etc/service/hello: 1s, normally up, want up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is something wrong with your run script.&lt;/p&gt;

&lt;p&gt;Looking at &lt;code&gt;sudo systemctl status runit&lt;/code&gt; and &lt;code&gt;sudo journalctl -u&lt;br&gt;
runit&lt;/code&gt; could usually help me figure the issue out (no email!!!).&lt;/p&gt;

&lt;p&gt;Once its working you should see the "Hello"s on the log for runit if you&lt;br&gt;
aren't logging this service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; sudo journalctl -u runit | tail
Oct 23 16:49:30 ostrich 2[664634]: Hello
Oct 23 16:49:32 ostrich 2[664634]: Hello
Oct 23 16:49:34 ostrich 2[664634]: Hello
Oct 23 16:49:36 ostrich 2[664634]: Hello
Oct 23 16:49:38 ostrich 2[664634]: Hello
Oct 23 16:49:40 ostrich 2[664634]: Hello
Oct 23 16:49:42 ostrich 2[664634]: Hello
Oct 23 16:49:44 ostrich 2[664634]: Hello
Oct 23 16:49:46 ostrich 2[664634]: Hello
Oct 23 16:49:48 ostrich 2[664634]: Hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next you'll have the &lt;code&gt;finish&lt;/code&gt; script which is just what should be run at&lt;br&gt;
the end of the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Shutting Down"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't have anything to do really so we just write a message. But you&lt;br&gt;
could do cleanup stuff here if you want.&lt;/p&gt;

&lt;p&gt;Last the logging spec. This is a subdirectory called &lt;code&gt;log&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; tree $SVLIB/hello/log
/etc/sv/hello/log
├── run
└── supervise [error opening dir]

1 directory, 1 file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where again the &lt;code&gt;run&lt;/code&gt; is a shell script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;exec &lt;/span&gt;svlogd &lt;span class="nt"&gt;-tt&lt;/span&gt; /var/local/log/runit/hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep things simple this is what you want. In general you could swap&lt;br&gt;
out different logging daemons other than &lt;code&gt;svlogd&lt;/code&gt; (which comes with&lt;br&gt;
runit), but I don't see a reason to and this Just Works™. Basically&lt;br&gt;
runit will create this as a separate service, but just knows how to pipe&lt;br&gt;
around outputs now.&lt;/p&gt;

&lt;p&gt;If you add these and then reload the services:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;sv reload &lt;span class="nv"&gt;$SVDIR&lt;/span&gt;/hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll stop seeing "Hello" in the runit system log, and start seeing it&lt;br&gt;
in the log file we configured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo less +F "$SVLOG_SYS_DIR/hello/current"
# or
sudo tail -f "$SVLOG_SYS_DIR/hello/current"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we go over configuring the logging daemon (that &lt;code&gt;sysvlogd&lt;/code&gt; thing&lt;br&gt;
we ran in &lt;code&gt;log/run&lt;/code&gt;) I should mention all those &lt;code&gt;supervise&lt;/code&gt; dirs that&lt;br&gt;
were laying around.&lt;/p&gt;

&lt;p&gt;These basically are the locks and other control data that runit uses to&lt;br&gt;
manage the services. Don't mess with them. They are owned by root&lt;br&gt;
anyways. One thing you can do if you think you messed things up is to&lt;br&gt;
disable the service and remove them all to start fresh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$SVDIR&lt;/span&gt;/hello
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$SVLIB&lt;/span&gt;/hello/supervise
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$SVLIB&lt;/span&gt;/hello/log/supervise
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now one last thing is to configure the log. This file doesn't go in the&lt;br&gt;
service directory (&lt;code&gt;SVLIB&lt;/code&gt;) but the directory where the logs are. So&lt;br&gt;
make this file &lt;code&gt;SVLOG_SYS_DIR/hello/config&lt;/code&gt; and it should have something&lt;br&gt;
like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# max size in bytes
s100000

# keep max of 10 files
n10

# minimum of 5 files
N5

# rotate every number of seconds
t86400

# prepend each log message with the characters
pHELLO::
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you rotate logs and control file sizes. Its a really not nice&lt;br&gt;
file format but I will forgive them considering they aren't using any&lt;br&gt;
libraries for TOML or YAML parsing or such things. Again something I&lt;br&gt;
would improve on for non PID 0 usage.&lt;/p&gt;

&lt;p&gt;With this all in place you'll see something like this in&lt;br&gt;
&lt;code&gt;SVLOG_SYS_DIR/hello&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--&amp;gt; sudo tree $SVLOG_SYS_DIR/hello
/var/local/log/runit/hello
├── @400000005f929f2c12d2041c.s
├── @400000005f92b3043a84c42c.s
├── @400000005f92c6dd22d71b04.s
├── @400000005f92dab60d0ce77c.s
├── @400000005f92ee8f37dec1dc.s
├── @400000005f93026921e78ae4.s
├── @400000005f931643127f5bf4.s
├── @400000005f932a1d27cdd3b4.s
├── @400000005f933df70f77ffc4.s
├── @400000005f933e0a239542b4.s
├── config
├── current
└── lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where those ID named files are the rotated logs.&lt;/p&gt;

&lt;p&gt;Now that we're done with the runit tutorial lets show you how to make a&lt;br&gt;
timer service that acts like a cron job.&lt;/p&gt;
&lt;h1&gt;
  
  
  Timer Services With Runit and Snooze
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;snooze&lt;/code&gt; was also available in the Ubuntu package index so we just&lt;br&gt;
install with apt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;snooze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise its very basic C so should be trivial to compile and install.&lt;/p&gt;

&lt;p&gt;First lets just right a timer script that runs a command every 40&lt;br&gt;
seconds seconds with &lt;code&gt;sleep&lt;/code&gt; and then we can just replace &lt;code&gt;sleep&lt;/code&gt; with&lt;br&gt;
&lt;code&gt;snooze&lt;/code&gt;. Our &lt;code&gt;run&lt;/code&gt; script is then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sleeping until time to run"&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;40

/home/user/.local/bin/run_thing

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Finished with task"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we are running this script as a service and the job of a service&lt;br&gt;
manager is to restart failing services, this script will just run over&lt;br&gt;
and over. There really wasn't a need for the while loop in our &lt;code&gt;hello&lt;/code&gt;&lt;br&gt;
example.&lt;/p&gt;

&lt;p&gt;Now to make this "cron-like" we replace &lt;code&gt;sleep&lt;/code&gt; with &lt;code&gt;snooze&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sleeping until time to run"&lt;/span&gt;
snooze

/home/user/.local/bin/run_thing

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Finished with task"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the default of &lt;code&gt;snooze&lt;/code&gt; is just to block until midnight every&lt;br&gt;
night (that is hour zero). So basically its just calculating how long it&lt;br&gt;
is until midnight and sleeping until then. Pretty simple right? Once&lt;br&gt;
snoozing is over the command will run the task will terminate, get&lt;br&gt;
restarted and then will snooze again until next time.&lt;/p&gt;

&lt;p&gt;You can see all the options for configuring when &lt;code&gt;snooze&lt;/code&gt; will sleep&lt;br&gt;
until in its docs and man page (this one is actually readable). But for&lt;br&gt;
instance you can set it sleep until 3 AM on Mondays and Thursdays:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;snooze &lt;span class="nt"&gt;-w1&lt;/span&gt;,4 &lt;span class="nt"&gt;-H3&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something else I like to do is have it run a number of jobs in serial as&lt;br&gt;
a cycle. Where you would have to have multiple cron jobs to achieve this&lt;br&gt;
you can do it in one script with this method.&lt;/p&gt;

&lt;p&gt;I have a two print jobs every week to keep the heads from drying out.&lt;br&gt;
Black and white on Monday and color on Thursday at noon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Snoozing until time to print"&lt;/span&gt;
snooze &lt;span class="nt"&gt;-w1&lt;/span&gt; &lt;span class="nt"&gt;-H12&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Printing black and white test page"&lt;/span&gt;

&lt;span class="c"&gt;# black and white&lt;/span&gt;
lp /home/salotz/testpage_bw.pdf


&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Snoozing until next print job"&lt;/span&gt;
snooze &lt;span class="nt"&gt;-w4&lt;/span&gt; &lt;span class="nt"&gt;-H12&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Printing RBG color test page"&lt;/span&gt;

&lt;span class="c"&gt;# color: RBG&lt;/span&gt;
lp /home/salotz/testpage_color_rbg.pdf

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"End of run script job cycle"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With cron you'd have something like [4]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 12 * * 1 salotz lp /home/salotz/testpage_bw.pdf
0 12 * * 4 salotz lp /home/salotz/testpage_color_rbg.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the syntax for snooze is actually pretty similar and perhaps a little&lt;br&gt;
cleaner and semantic.&lt;/p&gt;

&lt;p&gt;So just to review: By avoiding cron we've avoided some of the most&lt;br&gt;
difficult and finnicky sysadmin tasks in linux, namely email and&lt;br&gt;
logging, and traded them for runit. I think its a fair trade and as&lt;br&gt;
we've seen setting up runit is trivial in Ubuntu (and likely other&lt;br&gt;
distros).&lt;/p&gt;

&lt;p&gt;I can definitely see now why runit has fierce supporters. I'll&lt;br&gt;
definitely be using it for situations I would normally use systemd as&lt;br&gt;
well. While I'm not going to be running Void linux on my desktop any&lt;br&gt;
time soon its a good candidate for inside of containers.&lt;/p&gt;
&lt;h1&gt;
  
  
  Final Notes
&lt;/h1&gt;

&lt;p&gt;As I mentioned I am not a grey-neckbeard and so if I've made any gross&lt;br&gt;
oversights in my claims, please let me know in the comments. I would&lt;br&gt;
love to like cron more.&lt;/p&gt;
&lt;h1&gt;
  
  
  Comments
&lt;/h1&gt;

&lt;p&gt;Comments are implemented as a mailing list hosted on sourcehut:&lt;br&gt;
&lt;a href="https://lists.sr.ht/%7Esalotz/salotz.info_comments-section"&gt;https://lists.sr.ht/~salotz/salotz.info_comments-section&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comments are for the whole website so start your subject line with:&lt;br&gt;
&lt;code&gt;[runit_snooze]&lt;/code&gt; for this post.&lt;/p&gt;
&lt;h1&gt;
  
  
  Footnotes
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Well, the idea was hinted at in the &lt;code&gt;snooze&lt;/code&gt; README but I put it&lt;br&gt;
together.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Every shell script is a scar:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;master_tab&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$CONFIGS_LIB_DIR&lt;/span&gt;/cron/header.crontab&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$CONFIGS_LIB_DIR&lt;/span&gt;/cron/misc.crontab&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$CONFIGS_LIB_DIR&lt;/span&gt;/cron/my.crontab&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# then write to the crontab file after cleaning it out&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;crontab &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; crontab &lt;span class="nt"&gt;-l&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$master_tab&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; | crontab -

&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It only has How-To Guides and Reference according to this&lt;br&gt;&lt;br&gt;
classification: &lt;a href="https://documentation.divio.com/"&gt;&lt;/a&gt;&lt;a href="https://documentation.divio.com/"&gt;https://documentation.divio.com/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No I didn't do that myself, of course I used this&lt;br&gt;&lt;br&gt;
&lt;a href="https://crontab.guru/"&gt;&lt;/a&gt;&lt;a href="https://crontab.guru/"&gt;https://crontab.guru/&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
  </channel>
</rss>
