Disassembler

Artificial intelligence is no match for natural stupidity.
01listopad2012

Cron - jako švýcarské hodinky


Jako švýcarské hodinky značky Roiex, vyrobené v Číně a prodané v tureckém bazaru, chtělo by se dodat po tom, co si člověk trochu projde kód cronu nacházejícího se v repozitářích Debianu a Ubuntu a potká se s některými zvláštnostmi.

Frankensteinova chlouba


Debianí cron i redhatí cronie vychází z původního vixie cronu z roku 1994, tedy z verze 3.0pl1. Red Hat měl alespoň tolik slušnosti svůj cron forknout a pojmenovat jinak, když už ho chtěli překopat. A že kvůli všelijakým SELinuxům, AppArmorům a jiným zvířátkům, která by cronu mohla překazit plány a o kterých se vývojářům v devadesátých letech ani nesnilo, překopán být musel. Debiani to vzali zkratkou a zasloužili by si za to laškovně popíchnout rozžhaveným pohrabáčem. Původní vixie cron má cca 4500 řádků.

root@vm:~/cron-3.0pl1.orig# wc -l *.c *.h | tail -1
  4459 total

Debian distribuuje cron ve verzi 3.0pl1-124. To zjednodušeně znamená, že do jeho kódu vecpali už 124 commitů se změnami. Pojďme se tedy podívat, kolik řádků má patch

root@vm:~# grep -c ^[+-] cron_3.0pl1-124.diff
7519

Ne, srandu si fakt nedělám. Patch má o tři tisíce řádků více než samotný základ. Úplně tedy nechápu, proč se to pořád jmenuje cron, když už je to nějakou dobu úplně jiný, vixie cronem volně inspirovaný program. Ostatně statistiku změn a i změny samotné si můžete prohlédnout na Debian patch trackeru.

Ticho po pěšině


S Debianím cronomutantem sranda ale nekončí. To si takhle vezmete pár cron skriptů z Red Hatu, strčíte je na deb-based distribuci a oni najednou bez zjevného důvodu přestanou fungovat. Spustíte-li skripty ručně, samozřejmě udělají, co se od nich očekává. Cron je však okázale ignoruje a v čas spuštění nepošle do syslogu ani řádku. Skripty pro něj prostě neexistují. Alespoň do té doby, než /etc/cron.d/mujscript.cron přejmenujete na /etc/cron.d/mujscript. Pak už cron soubor vidí a skript ochotně spustí. Příčinou je výše zmíněný debianí patch, který kvůli run-parts vynutí kontrolu názvů souboru a pokud nevyhoví regulárním výrazům pro jmenné prostory LANANA (^[a-z0-9]+$), LSB hierarchical and reserved namespaces (^_?([a-z0-9_.]+-)+[a-z0-9]+$) a ani vlastnímu debianímu cron script namespace (^[a-z0-9][a-z0-9-]*$), skript prostě potichu ignoruje.

Další problém nastane, když si úlohy do cronu přidáváte skriptem. Řekněme, že máte nějaký instalační skript, ve kterém sedí třeba řádek

echo -ne '00 20 * * * appsrv /app/scripts/end_of_day.py\n \
00 22 * * * appsrv /app/scripts/send_report.py' > /etc/cron.d/myapp

Vím, že vypadá ošklivě a byl by daleko líp čitelnější rozepsaný, ale když vám jej píše nějaký Rajesh z Bangalore, je tohle ještě zatraceně slušný výstup. Nicméně /app/scripts/send_report.py se i přesto, že je přítomen v /etc/cron.d/myapp nikdy nespustí. Příčinu lze odhalit pozorným přečtením manuálové stránky crontab(1). Dole se alibisticky krčí

DIAGNOSTICS
       A fairly informative usage message appears if you run it with a bad command line.

       cron requires that each entry in a crontab end in a  newline  character.  If  the
       last  entry  in  a crontab is missing the newline, cron will consider the crontab
       (at least partially) broken and refuse to install it.

Tedy, chybí-li newline na konci posledního řádku, řádek bude ignorován, a vám o tom samozřejmě opět nikdo neřekne. V době Windows-tolerant aplikací, které správně pochopí soubory nejen bez newline na konci, ale i s oblíbenými windowsovskými ^M by člověk usoudil, že v těch 7,5K řádcích patchů přece musí být taková kravina ošetřená. Nebo že cron aspoň něco žbleptne do syslogu, když potká řádek, který se mu nelíbí. Ale kluci z Debianu mají dlouhou tradici v rozbíjení funkčních věcí, takže to asi ani jinak nešlo. Díky nim jsem k vytvoření vlastní distribuce zase o krůček blíž.