1 |
Rich Freeman posted on Sun, 01 Mar 2015 14:13:53 -0500 as excerpted: |
2 |
|
3 |
> On Sun, Mar 1, 2015 at 1:20 PM, Marc Joliet <marcec@×××.de> wrote: |
4 |
>> |
5 |
>> Regardless: thoughts? |
6 |
> |
7 |
> I'd probably just do this: |
8 |
>> Am Sun, 1 Mar 2015 08:34:19 -0500 schrieb Rich Freeman |
9 |
>> <rich0@g.o>: |
10 |
>>> |
11 |
>>> The timer keeps running if you set the dependency on the service. So, |
12 |
>>> next time the timer runs, it will try again. You might want to just |
13 |
>>> set an hourly job and have it check for a successful run in the last |
14 |
>>> day or whatever. |
15 |
>>> |
16 |
> You could of course trigger this from either the mount or hourly. |
17 |
> Anytime you mount the drive or every hour systemd will run the service, |
18 |
> and the service will see if it managed to do a backup/etc in the last |
19 |
> day/week/whatever, and then run if appropriate. |
20 |
|
21 |
This is actually how I setup several former cron-jobs as systemd timers, |
22 |
here, based on an hourly check somewhat similar to what most crons |
23 |
(including gentoo's for over 10 years now and mandrake's before that) are |
24 |
actually setup to do to get around the fact that cron won't on-its-own |
25 |
trigger after restart if the machine was down or cron not running when |
26 |
the configured time for a job ran. |
27 |
|
28 |
Here's how I have it setup here. Note that my initials are jed, and I |
29 |
use them regularly as a prefix/suffix to denote custom configs (here, |
30 |
systemd units) I've created myself, as opposed to those shipped in |
31 |
whatever package. |
32 |
|
33 |
|
34 |
/etc/systemd/system/jed.hourly.timer: |
35 |
|
36 |
[Unit] |
37 |
Description=JED Hourly timer |
38 |
|
39 |
[Timer] |
40 |
OnBootSec=10min |
41 |
OnUnitActiveSec=1h |
42 |
Unit=jed.hourly.target |
43 |
|
44 |
[Install] |
45 |
WantedBy=timers.target |
46 |
|
47 |
|
48 |
timers.target is a systemd default target with the purpose of starting |
49 |
all the various timers. The install entry says to install a symlink |
50 |
pointing to jed.hourly.timer in the timers.target.wants subdir, which |
51 |
will of course cause timers.target to start it. |
52 |
|
53 |
The 10 minute OnBootSec setting says wait 10 minutes after boot before |
54 |
activating this timer the first time. The 1h OnUnitActiveSec setting |
55 |
says reactivate it every hour after its first activation. Thus, we have |
56 |
an hourly timer, except that its first run is offset from boot by only 10 |
57 |
minutes, not a full hour. |
58 |
|
59 |
Meanwhile, what it actually does when it runs is activate |
60 |
jed.hourly.target -- that's the Unit= line. |
61 |
|
62 |
|
63 |
/etc/systemd/system/jed.hourly.target: |
64 |
|
65 |
[Unit] |
66 |
Description=JED Hourly target |
67 |
StopWhenUnneeded=yes |
68 |
|
69 |
|
70 |
This one, like most targets, is pretty simple, its only purpose being a |
71 |
synchronization point, activating a bunch of services and/or other units, |
72 |
generally those with symlinks installed in the corresponding |
73 |
jed.hourly.target.wants subdir, at once. |
74 |
|
75 |
Note the StopWhenUnneeded=yes setting. The sole purpose of activating |
76 |
this thing is to trigger all its wants deps to run, and as soon as it has |
77 |
done that, it can stop again. This lets the target "rest" between hourly |
78 |
triggerings. |
79 |
|
80 |
|
81 |
Symlinks in /etc/systemd/jed.hourly.target.wants/: |
82 |
|
83 |
jed.hourly.timestamp.service |
84 |
|
85 |
|
86 |
That's it, just the one. With just it there it would have in fact been |
87 |
just as easy for me to set jed.hourly.timer's |
88 |
Unit=jed.hourly.timestamp.service, and omitted jed.hourly.target |
89 |
entirely. However, using the target is MUCH more flexible, as it allows |
90 |
me to add new services I want triggered hourly to the same target, as I |
91 |
decide I want/need them. |
92 |
|
93 |
If you're familiar with the usual cron setup, in particular, the usual |
94 |
/etc/crond.hourly/ (or whatever it was, that's from memory as I replaced |
95 |
my cronjobs with timers and don't even have a cron installed, these |
96 |
days), that's EXACTLY the function /etc/systemd/jed.hourly.target.wants |
97 |
ends up filling, with the minor exception being that as I set it up, it |
98 |
triggers hourly, starting 10 minutes after boot, instead of hourly, at |
99 |
some arbitrary minute of the hour. That actually suits my purposes |
100 |
better than an arbitrary time of the hour would, but if people really |
101 |
wanted more-cron-line specific minutes of the hour, that would be set |
102 |
using OnCalendar= instead of the boot+10min and hourly thereafter, that I |
103 |
used. |
104 |
|
105 |
|
106 |
/etc/systemd/jed.hourly.timestamp.service: |
107 |
|
108 |
[Unit] |
109 |
Description=JED Hourly timestamp updater |
110 |
|
111 |
[Service] |
112 |
Type=oneshot |
113 |
ExecStart=/etc/systemd/local-scripts/jed.hourly.timestamp |
114 |
|
115 |
[Install] |
116 |
WantedBy=jed.hourly.target |
117 |
|
118 |
|
119 |
This service simply runs the script named in execstart, oneshot, meaning |
120 |
it runs until its done, then stops. Which is what we want, since we're |
121 |
triggering it once per hour via the timers above. |
122 |
|
123 |
The install section simply tells systemd where to put that symlink |
124 |
mentioned above, in the appropriate target.wants subdir, when the service |
125 |
is "installed". |
126 |
|
127 |
|
128 |
/etc/systemd/local-scripts/jed.hourly.timestamp: |
129 |
|
130 |
#!/bin/bash |
131 |
|
132 |
stampfile=/var/log/jed.hourly.day.stamp |
133 |
|
134 |
# if stamp is old or doesn't exist, update stampfile and run... |
135 |
doit(){ |
136 |
touch $stampfile |
137 |
systemctl start jed.daily.target |
138 |
exit |
139 |
} |
140 |
|
141 |
[[ -f stampfile ]] || doit |
142 |
|
143 |
yesterday=$(date -d yesterday +"%Y%m%d%H%M") |
144 |
|
145 |
stamp=$(date -r "$stampfile" +"%Y%m%d%H%M") |
146 |
|
147 |
[[ $stamp ]] || doit |
148 |
|
149 |
[[ $stamp -lt $yesterday ]] && doit |
150 |
|
151 |
exit 0 |
152 |
|
153 |
|
154 |
This is where the custom magic happens and the real work gets done. If |
155 |
you're familiar with the usual cronjob runscripts setup, this will look |
156 |
similar. Basically, systemd timers, etc, magic above only sets up an |
157 |
hourly systemd timer, not a daily, weekly, etc. This script actually |
158 |
sets up a daily trigger, based on a daily timestamp that's checked every |
159 |
hour to see if it has been 24 hours or longer since the last run, updated |
160 |
if so, and the daily trigger triggered. |
161 |
|
162 |
As I said, the idea is very similar to that used with a standard cronjob |
163 |
runscripts setup, since that's exactly where I got it from. =:^) Like |
164 |
the cronjob setup, if the system was down the hour the daily would have |
165 |
otherwise triggered, this script ensures that it's triggered the next |
166 |
time the hourly timer is run... which will normally be 10 minutes after |
167 |
boot due to the 24 hours elapsing when the system was down, and the boot |
168 |
+10min setting in that first unit, jed.hourly.timer, above. |
169 |
|
170 |
OK, so what does this script trigger? systemctl start jed.daily.target |
171 |
|
172 |
Again, I /could/ have setup a daily timer much like the hourly timer, and |
173 |
handled it that way. Instead, I chose to use the hourly time to activate |
174 |
a service to run this script, handling the daily mechanism that way. |
175 |
|
176 |
(FWIW I put the stampfile itself in /var/log primarily because that's a |
177 |
conveniently writable place to put it. My /, including much of /var, is |
178 |
mounted read-only by default, while /var/log obviously must be writable. |
179 |
And thought about from a particular viewpoint, that timestamp /is/ a log |
180 |
in some fashion, since it effectively logs the last time the daily jobs |
181 |
should have run.) |
182 |
|
183 |
OK, we're almost there now. =:^) But tying up loose ends... |
184 |
|
185 |
|
186 |
/etc/systemd/system/jed.daily.target: |
187 |
|
188 |
[Unit] |
189 |
Description=JED Daily Target |
190 |
StopWhenUnneeded=yes |
191 |
|
192 |
|
193 |
Pretty much identical to the hourly target as the functionality is the |
194 |
same, just with a different name, reflecting the different timing. |
195 |
|
196 |
|
197 |
Symlinks in /etc/systemd/system/jed.daily.target.wants: |
198 |
|
199 |
jed.daily.logrotate.service. |
200 |
|
201 |
|
202 |
For the moment that's it. I haven't needed weekly, monthly, etc, |
203 |
scheduling, so I've not created it. But having done the hourly to daily |
204 |
thing, I could easily copy and convert that to weekly or whatever, |
205 |
triggered by the daily, just as daily is triggered by the hourly. |
206 |
|
207 |
And to tie up the final loose end before I apply the above to the case of |
208 |
this thread, as well as to supply a sample logrotate systemd service for |
209 |
anyone else wanting to switch to systemd timer based triggering and get |
210 |
rid of cron's triggering (while noting that the systemd way to do it |
211 |
would be to use tmpfiles.d settings, but I still find logrotate useful |
212 |
for doing actual log rotation, thus keeping those settings separate from |
213 |
systemd's config)... |
214 |
|
215 |
|
216 |
/etc/systemd/system/jed.daily.logrotate.service: |
217 |
|
218 |
[Unit] |
219 |
Description=JED daily logrotate |
220 |
|
221 |
[Service] |
222 |
Type=oneshot |
223 |
Nice=10 |
224 |
ExecStart=/usr/sbin/logrotate --state /var/log/logrotate.state /etc/ |
225 |
logrotate.conf |
226 |
|
227 |
[Install] |
228 |
WantedBy=jed.daily.target |
229 |
|
230 |
|
231 |
(Note the wrap of the execstart line...) |
232 |
|
233 |
################### |
234 |
|
235 |
OK, so how does all that apply to the case of this thread? =:^) |
236 |
|
237 |
Simple enough. Where I threw in that jed.hourly.timestamp.service systemd |
238 |
unit file (that is, symlinked into the hourly target.wants subdir) and |
239 |
jed.hourly.timestamp script that it called, you'd throw in another |
240 |
service, either in addition to the timestamp one I used, or if you don't |
241 |
want to use my daily solution, in place of it. |
242 |
|
243 |
|
244 |
The key thing to understand here is that once you've setup a service to |
245 |
run a script you create yourself, you can have it do literally whatever |
246 |
you want it too, just as you would with any other script -- it's NO |
247 |
LONGER limited to the strict declarative style and options systemd |
248 |
provides -- you're ONLY using systemd to /start/ it. =:^) |
249 |
|
250 |
So... suppose you want it to check hourly to see if it can and should |
251 |
trigger something else, but only run once a day... the timer and |
252 |
timestamp-script-based framework I have above already does the hourly |
253 |
check part, as well as the only running once a day part. |
254 |
|
255 |
If you prefer that it check every 10 minutes, it's simple enough to |
256 |
change that hourly thing to 10 minutes. If you like it hourly but want |
257 |
it to ALWAYS be hourly, including not checking the first time until an |
258 |
hour after boot, that's simple enough as well. Similarly, if you want |
259 |
the final action to run only every two days, or twice a day, or |
260 |
whatever. It's simple enough to change the timestamp mechanism to make |
261 |
that happen. |
262 |
|
263 |
Meanwhile, with the basic framework already there, it should also be |
264 |
simple enough to plugin some conditional logic testing to see if the |
265 |
partition device-files are showing up or not. If not, simply quit |
266 |
without updating the timestamp and wait for the next hourly or whatever |
267 |
check to roll around. If so, and the timestamp is also expired, then run |
268 |
the scripted backup or whatever else you were wanting it to do. |
269 |
|
270 |
... And of course you can rename them from jed.* to your own name of |
271 |
choice, too. Jed.* is fine for my own usage, but it would look a bit |
272 |
ridiculous, and could well defeat its site-specific-id purpose if someone |
273 |
actually decided to ship it as part of some package, if that jed.* naming |
274 |
pattern remained as installed out of my own control. But be sure to |
275 |
change the names /in/ the files too, not just the names /of/ the files, |
276 |
or systemd will be complaining about the stupid admin trying to give it |
277 |
nonsensical unit files to run. =:^P |
278 |
|
279 |
Also, one last caution. I chose to retype most of the above files |
280 |
manually, getting back into what they did and how as I did so, instead of |
281 |
copy/pasting them and then sitting there studying them to remember how |
282 |
they worked. Not so boring that way, but there's possibly a few typos I |
283 |
missed. They should be reasonably obvious and easy to fix, tho, if I did |
284 |
miss some... |
285 |
|
286 |
-- |
287 |
Duncan - List replies preferred. No HTML msgs. |
288 |
"Every nonfree program has a lord, a master -- |
289 |
and if you use the program, he is your master." Richard Stallman |