Als je op een bepaald tijdstip eenmalig, of repeterend, iets wil uitvoeren, dan doe je dit met at(1) of cron(1). Maar wat nu als je wil dat er wat uitgevoerd wordt als een bepaald bestand is aangepast? Dan heb je niet zoveel aan at(1) en cron(1), maar inotify(7) kan het wel! Echter dat is een API, en als je niet zo bedreven bent in het programmeren in bijvoorbeeld C of C++, dan is dat een doodlopende weg.

Arjan ten Hoopen  |  Gepubliceerd in Linux Magazine 2021 – 3

Had je gedacht; inofity (7) heeft nog twee familieleden inotifywatch(1) en inotifywait(1). Deze twee familieleden komen uit het pakket inotify-tools. Dit pakket wordt meegeleverd met elke distributie. Installeer haar met je package manager.

Deze twee tools kun je op de commando regel gebruiken, en dus ook in een shell script. Probeer eens inotifywait -m /tmp en maak ondertussen wat bestanden in /tmp.

Dit pakket maakt het mogelijk om een soort van cron te maken op bestandsaanpassingen in plaats van datum en tijd. Echter inotifywait is een erg basaal tool. Om er een cron voor bestandsaanpassingen van te maken, zijn er nog wat toeters en bellen nodig.

Open Source

Maar dit is Linux Magazine, hét blad over open source. En zoals we al eerder hebben laten zien, als een stukje open source software er nog niet is … dan gaan we het maken. Het doel is om een service te maken die acties uitvoert nadat er een gebeurtenis is geweest op een bestaand bestand. Tevens gaan we ervoor zorgen dat iedereen met een beetje basale commando regel kennis de scripts kan door gronden.

We beginnen met een pakkende naam; we hebben gekozen voor icron. Alle software is te downloaden via de Linux Magazine website. Als je de man page van inotifywait leest, zal je zien dat, wanneer je het eenmaal aan de praat hebt, inotifywait een melding geeft als er een event op een bestand of map heeft plaatsgevonden. In dit artikel beperken we ons tot bestaande bestanden. Bedenk dat wat we maken open source is, zodat je het eventueel zelf kunt aanpassen/verbeteren naar je eigen eisen.

Kort door de bocht; als we inotifywait gaan vertellen welke bestanden “gewatched” moeten worden, dan zal deze op haar beurt melding geven van events op deze bestanden. We hebben straks dus zeker een configuratiebestand nodig. Hierin gaan we vermelden welke actie uitgevoerd moet worden als er een event heeft plaatsgevonden op het bestand. Wil je meerdere acties, plaats dan meerdere regels. We gaan ze straks, van boven naar beneden, sequentieel uitvoeren.

Afbeelding 2: Journalctl in actie

We moeten inotifywait natuurlijk ook gaan vertellen welke bestanden “gewatched” moeten worden. Maar eigenlijk staat dit al in ons configuratiebestand. Hier staat niet alleen het bestand, maar ook het event, en de uit te voeren actie. Als we nu, op runtime, het configuratiebestand filteren en kopiëren naar een tijdelijk bestand met daarin alleen de te “watchen” bestandsnamen, dan kan dan dit tijdelijke bestand als argument meegeven worden aan inotifywait. Inotifywait weet dan waar ze op letten moet. In de manpage van inotifywait wordt dit de fromfile genoemd.

We willen flexibiliteit, dus we plaatsen de naam van dit (fromfile) bestand ook in het configuratiebestand. Inotifywait zal informatie over events wegschrijven naar stdout. Echter als inotifywait geconfigureerd wordt als een service, moet de output in een bestand geschreven worden. Flexibiliteit is key, dus wordt dit ook een parameter die we op gaan nemen in het configuratiebestand. In de manpage van inotifywait wordt dit de outfile genoemd.

We hebben nu alle informatie om inotifywait aan het werk te zetten. Aangezien het een service is, kunnen we door de outfile te lezen zien wat er gebeurt is. In de outfile staan immers alle events die er plaats hebben gevonden. Als er op een bestand een event, of meerdere events, plaats heeft gevonden dan wordt het configuratiebestand geraadpleegd of hier ook een actie, of meerdere acties, aan gekoppeld zijn. De acties die uitgevoerd moeten worden, schrijven we tijdelijk weg in een bestand. Dit bestand moet echter uniek zijn, daarom plakken we het proces nummer van icron hieraan vast.

Als alle acties bekend zijn, worden ze één voor één uitgevoerd. Let echter wel op wat je als actie definieert. De actie wordt uitgevoerd via de eval built-in van de shell. Deze built-in heeft zo zijn eigenaardigheden. Het komt er op neer dat het aan te raden is geen expansies (*) of aanhalingstekens (enkel en dubbel) te gebruiken. Hou het op simpele commando’s met eventueel wat argumenten.

En dat is eigenlijk het hele trucje. De broncode is ruim gedocumenteerd. Iedereen met een beetje commandoregelkennis kan dit script doorgronden. Programmeertechnisch is het niet allemaal even efficiënt, met als gevolg wat performance penalty’s, maar voor huis-tuin-en-keukengebruik voldoet het meer dan voldoende. Het zal zeker je systeem niet “opeten”.
Het script zelf heet icron. Je mag het opslaan daar waar je wil. Het configuratiebestand heet icron.config en moet je bij het uitvoeren meegeven als argument. In dit configuratiebestand staat duidelijk gedocumenteerd wat de syntax is als ook enkele voorbeeld regels.

Nog maar één hindernis te nemen: het laten automatisch runnen op je computer. Dat gaan we doen met systemd(1). We willen namelijk dat icron opstart tijdens het booten. Dit vereist dus een

Afbeelding 3: systemctl status.

icron.service bestand. Ook dit bestand maakt onderdeel uit van de software. De plek van icorn.service is echter wel specifiek. Plaats haar in /etc/systemd/system (hiervoor moet je root zijn). Gebruik je favoriete editor om aanpassingen te maken aan icron.service. Achter User moet de naam komen te staan van de gebruiker die (in de achtergrond) deze service uitvoert (root is een slecht idee!). Pas ook de waarde van Group aan, onder deze groep zal het icron daemon proces uitgevoerd worden. De WorkingDirectory is niet zo heel belangrijk, je zou haar zelfs weg kunnen laten. Ik heb echter door schade en schande geleerd zo defensief mogelijk te programmeren. Dus ik geef daemons altijd /tmp als WorkingDirectory.

De laatste die aandacht behoeft is ExecStart. Plaats hierin het icron commando (volledig pad) dat uitgevoerd moet worden om icron op te starten. Het commando icron heeft één argument, namelijk het configuratiebestand. Neem hier ook het volledig pad voor.

Nadat je je aanpassingen hebt doorgevoerd sla je het bestand op en laat (als root) systemd zichzelf herladen. Dit doe je met sudo systemctl daemon-reload

Systemd is nu op hoogte van het nieuwe icron.service bestand. Om ervoor te zorgen dat het tijdens de volgende boot automatisch opstart, voer je uit sudo systemctl enable icron. Nu alleen nog starten van icron via sudo systemctl start icron. Alle meldingen zullen naar de systemd journal database geschreven worden. Wil je zien wat er allemaal onder de motorkap gebeurt, dan doe je dit via sudo journalctl -fa Wil je alleen weten wat icron onder de motorkap uitvoert, dan doe je dit met sudo journalctl -fa -u icron.

Veel plezier met de code.

 LINKS:

icron bron bestanden