Cron, czyli automatyzujemy działania w Linuksie.

Każdy administrator po to piszę skrypty, by automatyzować swoje zadania. Jednak, czym byłoby automatyzowanie bez ich regularnego uruchamiania? W Linuksie z pomocą przychodzi nam cron, będący potężnym narzędziem, zwanym także czasem crontab (jednak nazwa ta pochodzi od tabeli programu cron, która jest plikiem, w którym…) zanim jednak napiszę dalej – kilka słów o samym programie cron.

Mam nadzieje, że czytelnik Philip, który w komentarzach do artykułu o usuwaniu plików zgłosił problemy z działaniem crona, znajdzie w poniższym wpisie ukojenie dla zszarganych nerwów administratorskich.

Cron i crontab – wprowadzenie

Cron jest unixowym (wybaczcie mi o językowi puryści) / Linuksowym (wybaczcie mi przewrażliwieni linuxiarze) demonem. Jednak nie robi on rzeczy strasznych, wręcz przeciwnie jest bardzo użytecznym narzędziem. Sama jego nazwa zaś nie pochodzi od greckiego Kronosa, tylko od Chronosa zwanego także Ojcem Czasu. Wszakże od zarządzania zadaniami w czasie jest właśnie cron i crontaby, bo jakże można je pominąć.

Tak więc mamy program cron, który w swojej mądrości przeszukuje co minutę system plików (a właściwie wybraną jego część – /var/spool/cron) w celu odnalezienia tabel (crontabów) użytkowników (tak, tak, użytkownik, też może mieć własny crontab – oczywiście o ile ma do niego dostęp 😉 ). Dodatkowo, sprawdza także czas modyfikacji plików crontab, i jeśli w ciągu minuty nastapiła zmiana wpisów w jakimś pliku crontab, tabela zostaje przeładowana. Dzięki takiemu rozwiązaniu nie musimy restartować demona po każdej modyfikacji zdań. Kolejnym z etapów jest załadowanie reguł z pliku /etc/crontab (jednak ten plik nico różni się od innych plików) Całości dopełniają odpowiednie katalogi: /etc/cron.d,  /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly, /etc/cron.monthly zdarzają się także (w zależności od używanej dystrybucji, lub preferencji administratora) /etc/cron.yearly. Ufam, że przeznaczenie tych katalogów jest jasne 😉 Jeśli nie to kwestia wyjaśnienia, zawartość tych katalogów wywoływana jest z crontaba należącego do użytkownika root:

# Run hourly jobs at 55 minutes after the hour:
55 * * * * /usr/bin/run-parts /etc/cron.hourly 1> /dev/null
#
# Run daily jobs at 4:30 every day:
30 4 * * * /usr/bin/run-parts /etc/cron.daily 1> /dev/null
#
# Run weekly jobs at 4:30 on the first day of the week:
30 4 * * 0 /usr/bin/run-parts /etc/cron.weekly 1> /dev/null
#
# Run monthly jobs at 6:30 on the first day of the month:
30 6 1 * * /usr/bin/run-parts /etc/cron.monthly 1> /dev/null

Do dyspozycji jest też plik /etc/cron.deny i /etc/cron.allow.

Użytkownicy i cron.

Ostatnie dwa pliki, jak można się domyśleć, służą do zezwolenia lub zabronienia korzystania z usług serwowanych przez cron. Teraz słowo rozwinięcia: brak tych plików sprawia, że dostęp do dobrodziejstw automatyzacji zadań ma tylko użytkownik root. Sterowanie zaś dostęp do crona odbywa się za pomocą w/w plików. Odbywa się w sposób następujący:

  • utworzony plik /etc/cron.deny zezwoli na korzystanie wszystkim użytkownikom, a zabroni tylko takim, którzy zostaną wymienieni w tym pliku (pusty plik pozwala na korzystanie z crona wszystkim użytkownikom);
  • utworzony plik /etc/cron.allow zabroni korzystania wszystkim użytkownikom, poza wylistowanymi w pliku (pusty plik zabrania korzystania z crona wszystkim użytkownikom)

Oczywiście z tych reguł nie korzysta użytkownik root. Sama budowa tych plików jest bardzo prosta – użytkownicy powinni być do nich wpisani linijka po linijce:

[root etc]# cat cron.deny
nobody
deamon
test

W takiej konfiguracji użytkownik próbujący dodawania zadań otrzyma komunikat:

[test etc]$ crontab -e
You (test) are not allowed to use this program (crontab)
See crontab(1) for more information

W przypadku, gdy użytkownik posiada już jakieś wpisy w swoim własnym crontabie, np:

[test ~]$ crontab -l
*/5 * * * * echo „wykonuje sie co 5 minut” >> /home/test/test

I zostanie dopisany do pliku /etc/cron.deny jego zadania nadal będą wykonywane, ale użytkownik straci możliwość ich edycji, czy nawet listowania. Dzieje się tak, gdyż jego tablica została „załadowana” do /var/spool/cron.

Tajemniczy crontab

Crontab, to nic innego jak zwykły plik tekstowy (bo jaki mógłby być plik do konfigurowania usług w Linuksie?) ze swoją specyficzną budową i tak pierwsza część każdej linijki (każde zadanie musi być wpisane w oddzielnej linii, chyba, że mamy kaprys wywoływania dwóch poleceń naraz.) określa nam „ramy czasowe” wykonywania polecania lub skryptu. Najbardziej obrazowo przedstawia to przykład zawarty w pliku /etc/crontab:
crontab
Jak widać pierwsza kolumna określa minutę wykonywania zadań, druga to godzina, trzecia – dzień, czwarta jest odpowiednikiem miesiąca (od 1 do 12 lub od jan – dec) ostatnia zaś odpowiada za dzień tygodnia (uwaga niespodzianka – od 0 do 7, gdzie 0 i 7 to Niedziela, lub od sun do sat)
Następnym parametrem może być użytkownik (jednak to sprawdza się tylko w pliku /etc/crontab). Ostatnia zaś komenda to skrypt lub polecenie do wykonania (zalecam stosowanie bezwzględnie bezwzględnych ścieżek do pliku/skryptu).
Wydawać by się mogło, że to już wszystko, jednak tak nie jest. Odpowiednie pola umożliwiają nam bowiem tworzenie bądź to zakresów, bądź ustalenia częstotliwości wykonywania i tak wpis:

  • */5 * * * * – oznacza wykonywanie polecenia co 5 minut każdego dnia
  • 0 12-14 * * * – oznacza wykonywanie polecenia o 12:00, 13:00 i 14:00 każdego dnia
  • 15 23 * * mon-fri – oznacza zadanie wykonywane codziennie od poniedziałku do piątku o godzinie 23:15
  • 0 4 * * mon-fri/2 – uruchomi polecenie w każdy poniedziałek, środę i piątek o godzinie 4:00

Specjalne znaczniki czasowe

Cron rozpoznaje specjalne znaczniki czasu, takie jak daily, monthly itp… poniższa tabelka przybliży ich znaczenie:

Znacznik Opis Odpowiednik wpisu
@reboot Uruchom podczas startu. brak
@yearly Uruchom raz w roku 0 0 1 1 *
@annually Uruchom raz w roku 0 0 1 1 *
@monthly Uruchom raz na miesiąc 0 0 1 * *
@weekly Uruchom raz na tydzień 0 0 * * 0
@daily Uruchom codziennie 0 0 * * *
@midnight Uruchom codziennie 0 0 * * *
@hourly Uruchom co godzinę 0 * * * *

Polecenia i opcje.
Jak już wspomniałem, cron może być modyfikowany za pomocą polecenia crontab. Jego składa zaś jest następująca:
crontab -l listuje zadania
crontab -e pozwala na edycję zadań
dodatkowo użytkownik root może określić użytkownika i tak:
crontab -u test -l – wylistuje zadania użytkownika test (oczywiście nawet w przypadku, gdy użytkownik test znajduje się w pliku /etc/cron.deny):

[root cron]# cat /etc/cron.deny
nobody
deamon
test
[root cron]# crontab -u test -l
*/1 * * * * echo „wykonuje sie co 5 minut” > /home/test/test

Mam nadzieję, że problem naszego czytelnika zostanie rozwiązany (przez niego samego rzecz jasna), a inni odnajdą się w automatyzacji zadań w Linuksie. Zachęcam do komentowania.

Piotr Berent

Piotr Berent od 2002 w pocie czoła pracujący w środowisku IT, obecnie freelancer - Inżynier Systemowy. Entuzjasta wirtualizacji, automatyzacji i rozwiązań opartych o narzędzia open-source.

Przeczytaj także...

10 komentarzy

  1. dev-null pisze:

    A ja proszę o informację jak uruchomić job’a co drugi tydzień 😉

    • Tego się chyba nie da zrobić w 100% dokładnie, tzn możesz na przykład wymusić działanie job’a np o 6.30 co drugi dzień przez: 30 6 1-31/2 * * …ale w miesiącu, który ma 31 dni, job wykona się 2 dni pod rząd tj 31 i 1. Czy to Cię zadowala? Może Piotr coś dopowie – on tu jest największym specem o Linuksa w redakcji 🙂

    • Piotr Berent pisze:

      Łukasz był blisko tylko nie doczytał… 😉

      Co drugi tydzień (np. w piątek o 10:15) – odpowiada zapisowi:
      15 10 * * 5/2

    • cron nie ma takiej możliwości. jedyny sposób to uruchamianie joba co tydzień, i skrypt ma sprawdzić czy podczas uruchomienia jest to ten tydzień o który nam chodzi – puszcza dalej zadanie, czy jest to zły tydzień i skrypt powinien od razu się zakończyć. najprościej na początku skryptu wywołać odpowiednio polecenie `date` aby na wyjściu podało numer tygodnia, i sprawdzić podzielność / niepodzielność przez 2. pozostaje problem na przełomie roku. ewentualnie date niech poda ilość dni od czasu „zero”, podzielić przez 7 bez reszty, i sprawdzić podzielność / niepodzielność właśnie tego przez 2. wtedy nawet na przełomie roku będzie ok.

  2. Wojciech Nadolecki pisze:

    Ostatnio na laborkach bawiliśmy się w cronie – dodawaliśmy do niego różne skrypty, które miały być wykonywane co określony czas. Ciekawa sprawa

    • No to dam wam teraz zagadkę praktyczną. Skrypt ma się odpalać co jakiś czas, ale może się wykonywać długo. Nie ma gwarancji że się skończy, ale nie mogą jednocześnie pracować dwie kopie. Jak to zrobicie? 😛

    • Oczywiście „długo” i „nie ma gwarancji że się skończy”, to znaczy dłużej niż termin kolejnego odpalenia skryptu, bo skrypt skończyć się kiedyś musi 🙂

    • widzę że nikt nie spotkał się z prawdziwymi problemami, jedynie sami teoretycy 😛 a więc tak:
      skrypt zawsze się musi skończyć, nawet jeśli końcem jego wykonywania jest restart systemu 🙂 ale cóż, widzę że nikt nie ma pomysłu. więc powiem. trzeba zastosować w prostszym przypadku mechanizm lockowania umówionego pliku, aby drugi proces nie mógł tego pliku zająć. ewentualnie jeśli jest ryzyko że proces może się zawiesić, trzeba wprowadzić mechanizm sprawdzania czy proces faktycznie wisi, a jeśli tak ubijać go przed kolejnym uruchomieniem skryptu.

  3. Sławomir Szyszło pisze:

    Grzegorz: np. tak:

    # wewnatrz skryptu odpalanego z crona
    LOCK_FILE=/var/lock/subsys/twojprogram.lck

    if [ ! -f ${LOCK_FILE} ]; then
    # można uruchomić zadanie

    else
    # coś za długo idzie…
    # maila ślij!
    #send_log
    exit 1
    fi

    exit 0

    • Oooo, mamy takie same nazwiska 🙂
      co do rozwiązania, zawiera podstawowy błąd. Zakładasz że skrypt zawsze prawidłowo zadziała, tzn. sprawdzi plik typu „lock”, założy plik, wykona zadanie, usunie plik. A co jeśli proces wypadnie a plik zostanie? wtedy masz deadlocka 🙂 rozwiązanie masz wyżej.

Dodaj komentarz