Как в OpenBSD не пропустить всухую день взятия Бастилии

Опубликовано 05.09.2025 в OpenBSD

Наткнулся я тут давеча на достаточно любопытную статью в духе цифрового минимализма:
Calendar.txt
Автор пропагандирует ведение календарных напоминаний в определенном формате в обычном текстовом файле сalendar.txt: редактировать плейнтекст можно чем угодно, синхронизировать между устройствами тоже как удобно (да хоть бы через github с версионированием), грепать стандартным grep и не пользоваться перегруженными календарными программами.

Не то чтоб это было для меня особенно актуально (я лично предпочитаю пользоваться календарём с напоминалкой в смартфоне), но при прочтении испытал я подозрение, что имею я дело с велосипедостроением (пусть и облегченным), ведь что-то подобное я уже где-то видел...

И точно: в UNIX-based системах, начиная с Version 7 AT&T UNIX, присутствует из коробки консольная утилита calendar, делающая буквально следующее: при запуске она ищет в текущем каталоге (а если не найдёт, то в ~/.calendar/calendar или в любом другом месте, заданном переменной CALENDAR_DIR) файл, содержащий определённого формата записи о грядущих календарных событиях. И выводит в stdout строки с записями, соответствующими текущему или завтрашнему дню (а если сегодня пятница - то с пятницы по понедельник включительно). Ключами командной строки (см. man calendar) можно задать любое другое количество дней до/после или даже заставить программу считать текущей датой любое другое число. Опционально, вывод может отправляться на email (отправку можно отключить, создав файл ~/.calendar/nomail).

Формат файла calendar несложен и вариативен, вот несколько простых примеров:

 *  Jun. 15     Событие происходит 15 июня каждого года.
 *  15 June     Событие происходит 15 июня каждого года.
 *  Thursday    Событие происходит каждый четверг.
 *  June        1 июня каждого года.
 *  15 *        15 числа каждого месяца.
 *  May Sun+2   Второе воскресенье мая каждого года.
 *  04/SunLast  Последнее воскресенье апреля каждого года.
 *  Paskha-2    За два дня до православной пасхи каждого года.

За валидную запись считается строка, начинающаяся с правильного указания даты, если строка начинается с табуляции - оно считается продолжением описания предыдущего события (многострочное описание).

Важный нюанс: дата должна отделяться от описания события символом табуляции, а не пробелами, иначе запись валидной не считается. У себя в Neovim я привык по умолчанию заменять табуляции пробелами, и для файла с типом "calendar" (а Neovim умеет определять такой тип файла) пришлось отключить эту фичу, добавив файл ~/.config/nvim/after/ftplugin/calendar.lua следующего содержания:

vim.opt.expandtab = false

Собственно, этого уже достаточно для ведения личных напоминалок, но есть у calendar еще одна интересная возможность: можно в свой календарный файлик заинклудить что-нибудь из готовых календарей, поставляемых вместе с программой. А их у меня в OpenBSD поставляется богато, например:

/usr/share/calendar/calendar.birhtday - даты рождения и смерти различных исторических (и не очень) персонажей.
/usr/share/calendar/calendar.christian - различные христианские праздники (есть также православные, иудейские, неоязыческие и даже дискордианские)
/usr/share/calendar/calendar.space - даты событий, связанных с освоением космоса
/usr/share/calendar/calendar.openbsd - значимые даты, связанные с любимой операционной системой (только давненько не обновлялось)
/usr/share/calendar/calendar.world - различные события мировой истории, там есть день взятия Бастилии!
/usr/share/calendar/calendar.computer - события из области цифровых технологий

ну и много чего ещё, но что особенно мило - есть и национальные события, calendar.russian - содержит некоторые наборы отечественных событий (/usr/share/calendar/ru_RU.UTF-8/calendar.common - более-менее праздники, history - даты из истории России, orthodox - православный календарь, pagan - набор забавных выдуманных неоязыческих праздников вроде "дня отбора жертв Перуну" или "дня Кащея", primety - календарь народных примет).

Соответственно, интересующие готовые календари можно заинклудить в свой личный календарный файл (поддерживается С-синтаксис), например, так:

/*
 * допустимы комментарии в C-стиле
 */
#include </usr/share/calendar/ru_RU.UTF-8/calendar.primety>
#include </usr/share/calendar/ru_RU.UTF-8/calendar.orthodox>
#include </usr/share/calendar/ru_RU.UTF-8/calendar.history>
#include </usr/share/calendar/ru_RU.UTF-8/calendar.common>

А теперь пасхалочка. В man calendar сказано:

To enforce special date calculation mode for Cyrillic calendars you
should specify “LANG=<local_name>” and “BODUN=<bodun_prefix>” where
<local_name> can be ru_RU.UTF-8, uk_UA.UTF-8 or by_BY.UTF-8.

И этот самый "special date calculation mode" для русской, украинской и белорусской локали представляет собой следующее: с вероятностью 1/3 в выводе calendar вам могут быть показаны события вчерашнего дня с настраиваемым префиксом BODUN, по умолчанию заданным в некоторых файлах как "Бодун на утро от:". Например:

Sep 05  Бодун на утро от: Свергнут Наполеон III, 1870г.

Вот так проект OpenBSD распространяет шовинистские стереотипы о славянских народах. :)
Меня несколько утешает, что согласно man calendar, не поддерживаются также в полном объеме иудейские праздники и лунный календарь для мусульман. Ну хоть не одних славян дискриминируют!

В OpenBSD по умолчанию обработка календарных файлов пользователей с отправкой результата на email (вызов calendar -a) включена в дефолтный скрипт ежедневного обслуживания системы /etc/daily (запускается из crontab от root в 1:30 ночи).

И чтоб два раза не вставать: демон cron, включаемый по умолчанию в базовую систему OpenBSD, не обработает событие, если в запланированный момент компьютер выключен (или ноутбук находится в suspend/hibernate). Что хорошо для включенного 24/7 сервера - то не очень хорошо для ноутбука. К счастью, для решения проблемы гарантированного запуска заданий в заданном интервале в пакетной базе есть anacron. После установки он потребует минимальной настройки:

Во-первых, создадим файл конфигурации /etc/anacrontab со следующим содержимым:

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
HOME=/var/log
# format: period delay job-identifier command
1   5   cron.daily      /bin/sh /etc/daily
7   10  cron.weekly     /bin/sh /etc/weekly
30  15  cron.monthly    /bin/sh /etc/monthly

Здесь первая цифра - частота запуска задания в днях (ежедневно, еженедельно и ежемесячно, соответственно), вторая - пауза в минутах до старта задания, чтобы они не стартовали сразу после запуска anacron все разом.

Далее, в crontab пользователя root добавим строки (используя doas crontab -e):

# run anacron after reboot
@reboot /usr/local/sbin/anacron -ds
# run anacron at 01h00 if the system is up
0 1 * * * /usr/local/sbin/anacron -ds

Anacron здесь запускается с ключами -s (serialize, не запускать новое задание до завершения предыдущего) и -d (не уходить в бэкграунд). Любопытный нюанс: ни в man cron, ни в man crontab вы не найдете упоминаний ключа "@reboot", но судя по коду OpenBSD-шного cron - он таки обрабатывается.

И, наконец, для запуска anacron при возвращении ноутбука из suspend/resume имеет смысл добавить в скрипт /etc/amp/resume его вызов: /usr/local/sbin/anacron -ds

Всё, теперь на почту будут падать напоминания о календарных вехах и день взятия Бастилии точно не пройдет всухую (а заодно станет понятно, по какому поводу с утра бодун).