public inbox for gentoo-amd64@lists.gentoo.org
 help / color / mirror / Atom feed
From: Duncan <1i5t5.duncan@cox.net>
To: gentoo-amd64@lists.gentoo.org
Subject: [gentoo-amd64] Re: Systemd migration: opinion and questions
Date: Mon, 2 Mar 2015 05:13:26 +0000 (UTC)	[thread overview]
Message-ID: <pan$70c01$55eb964a$c41ab754$d017dd2a@cox.net> (raw)
In-Reply-To: CAGfcS_n1_8=Sx3F-3b_tS5cLXXxHs9Ru7s8nJWVL-mu=X=e8ZA@mail.gmail.com

Rich Freeman posted on Sun, 01 Mar 2015 14:13:53 -0500 as excerpted:

> On Sun, Mar 1, 2015 at 1:20 PM, Marc Joliet <marcec@gmx.de> wrote:
>>
>> Regardless: thoughts?
> 
> I'd probably just do this:
>> Am Sun, 1 Mar 2015 08:34:19 -0500 schrieb Rich Freeman
>> <rich0@gentoo.org>:
>>>
>>> The timer keeps running if you set the dependency on the service.  So,
>>> next time the timer runs, it will try again.  You might want to just
>>> set an hourly job and have it check for a successful run in the last
>>> day or whatever.
>>>
> You could of course trigger this from either the mount or hourly.
> Anytime you mount the drive or every hour systemd will run the service,
> and the service will see if it managed to do a backup/etc in the last
> day/week/whatever, and then run if appropriate.

This is actually how I setup several former cron-jobs as systemd timers, 
here, based on an hourly check somewhat similar to what most crons 
(including gentoo's for over 10 years now and mandrake's before that) are 
actually setup to do to get around the fact that cron won't on-its-own 
trigger after restart if the machine was down or cron not running when 
the configured time for a job ran.

Here's how I have it setup here.  Note that my initials are jed, and I 
use them regularly as a prefix/suffix to denote custom configs (here, 
systemd units) I've created myself, as opposed to those shipped in 
whatever package.


/etc/systemd/system/jed.hourly.timer:

[Unit]
Description=JED Hourly timer

[Timer]
OnBootSec=10min
OnUnitActiveSec=1h
Unit=jed.hourly.target

[Install]
WantedBy=timers.target


timers.target is a systemd default target with the purpose of starting 
all the various timers.  The install entry says to install a symlink 
pointing to jed.hourly.timer in the timers.target.wants subdir, which 
will of course cause timers.target to start it.

The 10 minute OnBootSec setting says wait 10 minutes after boot before 
activating this timer the first time.  The 1h OnUnitActiveSec setting 
says reactivate it every hour after its first activation.  Thus, we have 
an hourly timer, except that its first run is offset from boot by only 10 
minutes, not a full hour.

Meanwhile, what it actually does when it runs is activate 
jed.hourly.target -- that's the Unit= line.


/etc/systemd/system/jed.hourly.target:

[Unit]
Description=JED Hourly target
StopWhenUnneeded=yes


This one, like most targets, is pretty simple, its only purpose being a 
synchronization point, activating a bunch of services and/or other units, 
generally those with symlinks installed in the corresponding 
jed.hourly.target.wants subdir, at once.

Note the StopWhenUnneeded=yes setting.  The sole purpose of activating 
this thing is to trigger all its wants deps to run, and as soon as it has 
done that, it can stop again.  This lets the target "rest" between hourly 
triggerings.


Symlinks in /etc/systemd/jed.hourly.target.wants/:

jed.hourly.timestamp.service


That's it, just the one.  With just it there it would have in fact been 
just as easy for me to set jed.hourly.timer's 
Unit=jed.hourly.timestamp.service, and omitted jed.hourly.target 
entirely.  However, using the target is MUCH more flexible, as it allows 
me to add new services I want triggered hourly to the same target, as I 
decide I want/need them.

If you're familiar with the usual cron setup, in particular, the usual 
/etc/crond.hourly/ (or whatever it was, that's from memory as I replaced 
my cronjobs with timers and don't even have a cron installed, these 
days), that's EXACTLY the function /etc/systemd/jed.hourly.target.wants 
ends up filling, with the minor exception being that as I set it up, it 
triggers hourly, starting 10 minutes after boot, instead of hourly, at 
some arbitrary minute of the hour.  That actually suits my purposes 
better than an arbitrary time of the hour would, but if people really 
wanted more-cron-line specific minutes of the hour, that would be set 
using OnCalendar= instead of the boot+10min and hourly thereafter, that I 
used.


/etc/systemd/jed.hourly.timestamp.service:

[Unit]
Description=JED Hourly timestamp updater

[Service]
Type=oneshot
ExecStart=/etc/systemd/local-scripts/jed.hourly.timestamp

[Install]
WantedBy=jed.hourly.target


This service simply runs the script named in execstart, oneshot, meaning 
it runs until its done, then stops.  Which is what we want, since we're 
triggering it once per hour via the timers above.

The install section simply tells systemd where to put that symlink 
mentioned above, in the appropriate target.wants subdir, when the service 
is "installed".


/etc/systemd/local-scripts/jed.hourly.timestamp:

#!/bin/bash

stampfile=/var/log/jed.hourly.day.stamp

# if stamp is old or doesn't exist, update stampfile and run...
doit(){
	touch $stampfile
	systemctl start jed.daily.target
	exit
}

[[ -f stampfile ]] || doit

yesterday=$(date -d yesterday +"%Y%m%d%H%M")

stamp=$(date -r "$stampfile" +"%Y%m%d%H%M")

[[ $stamp ]] || doit

[[ $stamp -lt $yesterday ]] && doit

exit 0


This is where the custom magic happens and the real work gets done.  If 
you're familiar with the usual cronjob runscripts setup, this will look 
similar.  Basically, systemd timers, etc, magic above only sets up an 
hourly systemd timer, not a daily, weekly, etc.  This script actually 
sets up a daily trigger, based on a daily timestamp that's checked every 
hour to see if it has been 24 hours or longer since the last run, updated 
if so, and the daily trigger triggered.

As I said, the idea is very similar to that used with a standard cronjob 
runscripts setup, since that's exactly where I got it from. =:^)  Like 
the cronjob setup, if the system was down the hour the daily would have 
otherwise triggered, this script ensures that it's triggered the next 
time the hourly timer is run... which will normally be 10 minutes after 
boot due to the 24 hours elapsing when the system was down, and the boot
+10min setting in that first unit, jed.hourly.timer, above.

OK, so what does this script trigger?  systemctl start jed.daily.target

Again, I /could/ have setup a daily timer much like the hourly timer, and 
handled it that way.  Instead, I chose to use the hourly time to activate 
a service to run this script, handling the daily mechanism that way.

(FWIW I put the stampfile itself in /var/log primarily because that's a 
conveniently writable place to put it.  My /, including much of /var, is 
mounted read-only by default, while /var/log obviously must be writable.  
And thought about from a particular viewpoint, that timestamp /is/ a log 
in some fashion, since it effectively logs the last time the daily jobs 
should have run.)

OK, we're almost there now. =:^)  But tying up loose ends...


/etc/systemd/system/jed.daily.target:

[Unit]
Description=JED Daily Target
StopWhenUnneeded=yes


Pretty much identical to the hourly target as the functionality is the 
same, just with a different name, reflecting the different timing.


Symlinks in /etc/systemd/system/jed.daily.target.wants:

jed.daily.logrotate.service.


For the moment that's it.  I haven't needed weekly, monthly, etc, 
scheduling, so I've not created it.  But having done the hourly to daily 
thing, I could easily copy and convert that to weekly or whatever, 
triggered by the daily, just as daily is triggered by the hourly.

And to tie up the final loose end before I apply the above to the case of 
this thread, as well as to supply a sample logrotate systemd service for 
anyone else wanting to switch to systemd timer based triggering and get 
rid of cron's triggering (while noting that the systemd way to do it 
would be to use tmpfiles.d settings, but I still find logrotate useful 
for doing actual log rotation, thus keeping those settings separate from 
systemd's config)...


/etc/systemd/system/jed.daily.logrotate.service:

[Unit]
Description=JED daily logrotate

[Service]
Type=oneshot
Nice=10
ExecStart=/usr/sbin/logrotate --state /var/log/logrotate.state /etc/
logrotate.conf

[Install]
WantedBy=jed.daily.target


(Note the wrap of the execstart line...)

###################

OK, so how does all that apply to the case of this thread? =:^)

Simple enough.  Where I threw in that jed.hourly.timestamp.service systemd 
unit file (that is, symlinked into the hourly target.wants subdir) and 
jed.hourly.timestamp script that it called, you'd throw in another 
service, either in addition to the timestamp one I used, or if you don't 
want to use my daily solution, in place of it.


The key thing to understand here is that once you've setup a service to 
run a script you create yourself, you can have it do literally whatever 
you want it too, just as you would with any other script -- it's NO 
LONGER limited to the strict declarative style and options systemd 
provides -- you're ONLY using systemd to /start/ it. =:^)

So... suppose you want it to check hourly to see if it can and should 
trigger something else, but only run once a day... the timer and 
timestamp-script-based framework I have above already does the hourly 
check part, as well as the only running once a day part.

If you prefer that it check every 10 minutes, it's simple enough to 
change that hourly thing to 10 minutes.  If you like it hourly but want 
it to ALWAYS be hourly, including not checking the first time until an 
hour after boot, that's simple enough as well.  Similarly, if you want 
the final action to run only every two days, or twice a day, or 
whatever.  It's simple enough to change the timestamp mechanism to make 
that happen.

Meanwhile, with the basic framework already there, it should also be 
simple enough to plugin some conditional logic testing to see if the 
partition device-files are showing up or not.  If not, simply quit 
without updating the timestamp and wait for the next hourly or whatever 
check to roll around.  If so, and the timestamp is also expired, then run 
the scripted backup or whatever else you were wanting it to do.

... And of course you can rename them from jed.* to your own name of 
choice, too.  Jed.* is fine for my own usage, but it would look a bit 
ridiculous, and could well defeat its site-specific-id purpose if someone 
actually decided to ship it as part of some package, if that jed.* naming 
pattern remained as installed out of my own control.  But be sure to 
change the names /in/ the files too, not just the names /of/ the files, 
or systemd will be complaining about the stupid admin trying to give it 
nonsensical unit files to run. =:^P

Also, one last caution.  I chose to retype most of the above files 
manually, getting back into what they did and how as I did so, instead of 
copy/pasting them and then sitting there studying them to remember how 
they worked.  Not so boring that way, but there's possibly a few typos I 
missed.  They should be reasonably obvious and easy to fix, tho, if I did 
miss some...

-- 
Duncan - List replies preferred.   No HTML msgs.
"Every nonfree program has a lord, a master --
and if you use the program, he is your master."  Richard Stallman



  reply	other threads:[~2015-03-02  5:13 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-24 20:15 [gentoo-amd64] Systemd migration: opinion and questions Marc Joliet
2015-02-24 20:41 ` Randy Barlow
2015-02-24 23:11   ` Marc Joliet
2015-02-25 22:42     ` Marc Joliet
2015-02-27 22:29       ` Marc Joliet
2015-02-24 21:44 ` Rich Freeman
2015-02-25  7:50   ` Marc Joliet
2015-02-25 12:01     ` Rich Freeman
2015-02-25 18:25       ` Marc Joliet
2015-03-01 12:48         ` Marc Joliet
2015-03-01 13:34           ` Rich Freeman
2015-03-01 18:20             ` Marc Joliet
2015-03-01 19:13               ` Rich Freeman
2015-03-02  5:13                 ` Duncan [this message]
2015-03-14 14:01                   ` [gentoo-amd64] " Marc Joliet
2015-03-14 12:57                 ` [gentoo-amd64] " Marc Joliet
2015-03-14 13:02               ` Marc Joliet
2015-02-25 10:13   ` [gentoo-amd64] " Duncan
2015-02-25 12:13     ` Rich Freeman
2015-02-26  0:35       ` Duncan
2015-02-25 18:56     ` Marc Joliet
2015-02-26  1:55       ` Duncan
2015-02-24 21:51 ` [gentoo-amd64] " Frank Peters
2015-02-25 14:31   ` Michael Mattes
2015-02-25 20:28   ` Marc Joliet
2015-02-25 10:15 ` [gentoo-amd64] " Duncan
2015-02-25 10:33 ` Duncan
2015-02-25 19:17   ` Marc Joliet
2015-02-25 19:31     ` Rich Freeman
2015-02-25 19:54       ` Marc Joliet
2015-02-25 22:30 ` [gentoo-amd64] " Marc Joliet
2015-05-20  8:01 ` Marc Joliet
2015-05-20 10:44   ` [gentoo-amd64] " Duncan
2015-05-20 11:22     ` Rich Freeman
2015-05-21  9:36       ` Duncan
2015-05-21 11:33         ` Marc Joliet
2015-05-23  8:49         ` Marc Joliet
2015-05-23  9:32           ` Marc Joliet
2015-05-23 10:41           ` Duncan
2015-05-23 11:11             ` Marc Joliet
2015-05-23 11:37               ` Rich Freeman
2015-05-23 12:02                 ` Duncan
2015-05-23 18:07               ` Marc Joliet
2015-05-23  8:17       ` Duncan
2015-05-23 12:14         ` Duncan
2015-05-21 11:29     ` Marc Joliet
  -- strict thread matches above, loose matches on Subject: below --
2015-02-25 11:04 Duncan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='pan$70c01$55eb964a$c41ab754$d017dd2a@cox.net' \
    --to=1i5t5.duncan@cox.net \
    --cc=gentoo-amd64@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox