Предположим у нас есть некий unix-blackbox без часов реального времени (без батарейки в них) и без NTP (вообще без сети). Тогда при каждом запуске (включении и загрузки) на системных часах такого unix-box будет примерно одинаковое время (или совсем одинаковое).
Задача: Переносимым способом определять такую ситуацию (для тех кто в танке: «переносимо» == «не только на linux, а на любом POSIX/Unix»).
Дополнение в 2019-10-31 20:25 (исправляю упущение в исходной формулировке задачи): Очевидный вариант «увеличивать счетчик в файле при старте» не подходит, так как TL;DR должно работать в библиотеке без изменения её API и без вмешательства в процесс загрузки системы.
У меня есть вариант решения (назовём его «900»), но хочется услышать идеи от молодых и талантливых.
Для понимания: Всё это нужно (т.е. полная постановка задачи) для принятия решения «откатывать или нет» последние транзакции при открытии БД внутри libmdbx (встраиваемый движок БД, замена Berkeley DB), что требует переносимого аналога /proc/sys/kernel/random/boot_id
, что в свою очередь требует определения boot time. И вот тут-то и нужно понять что это самое «boot time» не будет одинаковым при каждой загрузке.
Итоговый вариант решения был исходно обозначен как «900» и не изменился:
- минимальная разница между часовыми поясами 900 секунд (15 минут);
- Контролируем разницу между CLOCK_REALTIME и CLOCK_MONOTONIC;
- Если она примерно кратна 900 секундам, то считаем что в системе нет CMOS-часов и/или синхронизации по NTP;
Как и почему это работает:
- Если в системе нет cmos/NTP, то нет повода чтобы CLOCK_REALTIME и CLOCK_MONOTONIC шли в разнобой, т.е. разница будет равна некоторому часовому поясу (в том числе нулю в случае TZ=UTC).
- Шаг «квантования» временных зон 15 минут, т.е. любая тайм-зона (в том числе еще не определенная) будет иметь смещение кратное 15 минутам.
- Поэтому, если разность между CLOCK_REALTIME и CLOCK_MONOTONIC не кратна 15 минутам, то их разность не может быть объяснена только использованием какого-либо часового пояса. Следовательно CLOCK_REALTIME включает сдвиг для соответствия реальному времени и в системе где-то есть источник реального времени (CMOS или NTP).
Эта схема не идеальна и может давать ложно-отрицательный результат. Например, «алгоритм» посчитает что «время не настоящее»:
- Система вправе инициализировать CLOCK_MONOTONIC от любого удобного источника, включая CMOS-часы. Тогда при наличии СMOS-часов работающих в нужной TZ получается всегда CLOCK_MONOTONIC == CLOCK_REALTIME.
- С некоторой вероятностью разность может быть кратной 15 минутам.
Тем не менее, субъективно этот вариант лучше остальных (проверке условия «текущая время меньше даты исходников или сборки»). В контексте полной постановки задачи, важно что этот способ не даёт ложно-положительных результатов, и не имеет абсолютных точек привязки ко времени (идемпотентен по временной оси).
Исходники PoC для баловства = https://abf.io/erthink/poc4-boottime-nearly-constant
Добавлено 2019-11-01 в 12:18: В целом тредик/срач для меня полезен благодаря выводам:
- на embedded-like платформах (как минимум на Linux) следует полагаться только на нативный bootid, ибо boottime может превращаться в тыкву распространенными рецептами в rc-скриптах.
- генерацию bootid из boottime следует вынести в опцию, ибо нет 100% гарантии выявить все варианты предыдущего пункта.
Всем еще раз спасибо за дискус/срачик. Всё.