Сейчас вы снова убедитесь, что знание языка С сопоставимо с навыками самообороны, поскольку в современном мире мега-корпораций и победившего киберпанка на простых пользователей всем и давно наплевать.

Картина "Хром шатает батарею цифрового Ильича".
Картина "Хром шатает батарею цифрового Ильича".

Отдыхаем хорошо

Как и все нормальные люди, я временами смотрю фильмы, сериалы и длинные видео на ноутбуке и чтобы тот случайно не ушел в сон во время просмотра — включаю на нем «режим презентации».

Поскольку за последние годы все кино переместилось в веб, большую часть времени теперь используется не программа-видеоплеер, а обычный браузер Chrome/Chromium.

После одного из обновлений, стал замечать, что браузер-то оборзел научился самостоятельно перехватывать засыпание ноутбука, блокировку экрана и запуск скринсейвера во время просмотра видео, без всяких «режимов презентации».

Затем такое поведение появилось и на обычных сайтах — без видимого видео или аудио-контента.

Без каких-либо сообщений, запросов и подтверждений на подобные действия.

Затем ноутбук впервые разрядился в ноль, будучи оставленным с открытой страницей какого-то левого сайта, ни suspend ни hibernate внезапно не сработали.

После пятой по счету подставы с перехватом управления, автор окончально огорчился с такой наглости, достал любимый топор компилятор и стал изучать проблему в деталях.

Изучение проблемы

Первым делом я полез в поисковик, убедиться что проблема — не результат осеннего обострения или моего специфического окружения, в котором половина системы это разнообразные компиляторы и средства разработки, а другая — горы исходного кода.

Оказалось что сия дичь действительно массовая и хорошо известная:

Разумеется тут не про порно, человек просто собирал ядро из исходников. "Sensitive situation".
Разумеется тут не про порно, человек просто собирал ядро из исходников. "Sensitive situation".

Хотя в статье речь пойдет о Xfce, аналогичным обазом ведут себя все «большие» окружения — KDE, Gnome, Cinnamon и так далее:

Отдельная «шутка юмора» — попытка втащить поддержку такого поведения в.. Sway:

Monitor dbus and inhibit swayidle when Firefox or Chromium request it

Хотя любители тайловых менеджеров — особая раса сверхлюдей, понять мотивы которых обывателю не дано, так что не буду даже пытаться.

Разумеется по этой проблеме есть давно заведенный тикет в трекере, с длиннющей перепиской, с приложенными дампами памяти и техническими деталями, открытый уже 11 лет:

Как видите тут стоит низкий приоритет и не назначен ответственный, ниже станет понятно почему.
Как видите тут стоит низкий приоритет и не назначен ответственный, ниже станет понятно почему.

Помимо означенного тикета в публичном трекере Ubuntu, где тусуются в основном простые пользователи, нашелся еще один, не менее эпичный тикет в т��екере самого Chromium, висящий там аж с 2013 года:

Если прокрутить в самый низ страницы, можно заметить статус «Fixed» и битую ссылку на коммит (поскольку трекер переехал), суть которого — легализация специального API для управления блокировкой экрана и засыпанием.. прямо из кода на странице!

Примерно такого:

// The wake lock sentinel.
let wakeLock = null;

// Function that attempts to request a screen wake lock.
const requestWakeLock = async () => {
  try {
    wakeLock = await navigator.wakeLock.request();
    wakeLock.addEventListener('release', () => {
      console.log('Screen Wake Lock released:', wakeLock.released);
    });
    console.log('Screen Wake Lock released:', wakeLock.released);
  } catch (err) {
    console.error(`${err.name}, ${err.message}`);
  }
};

// Request a screen wake lock…
await requestWakeLock();
// …and release it again after 5s.
window.setTimeout(() => {
  wakeLock.release();
  wakeLock = null;
}, 5000);

Как тебе такое, Илон Маск?

Повторяю для тех, кто еще не понял и не осознал:

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

И защитить от такого может лишь знание языка С и эта замечательная статья.

Механизм работы

Прежде чем «карать и патчить» очередную оборзевшую программу, стоит рассказать широкой аудитории как вся эта кухня вообще работает, хотя‑бы для осознания печальных реалий.

Есть одна неведомая штука в Linux-системах, под названием systemd:

systemd is a suite of basic building blocks for a Linux system. It provides a system and service manager that runs as PID 1 and starts the rest of the system.

Описание, взятое с официального сайта, столь расплывчато не потому, что это перевод с языка рептилоидов, как могло показаться. Просто такого рода системные сервисы крайне непросто описать простыми словами, доступными обывателю.

Для примера, вот так выглядит набор системных сервисов Windows:

Ни один из этих сервисов не поддается интерпретации обывателем.
Ни один из этих сервисов не поддается интерпретации обывателем.

У замечательного systemd с недавних пор появился абсолютно «сказочный» функционал, созданный для перехвата управления процессами засыпания и выключения системы:

systemd 183 and newer include a logic to inhibit system shutdowns and sleep states. This is implemented as part of systemd-logind.daemon(8)

Отключаемый, разумеется.

Но только с последствиями, вроде сломанного процесса автоматического засыпания из менеджеров управления питанием.

И риском загнуть систему целиком при обновлении, поскольку например менеджеры пакетов выставляют подобную блокировку при установке пакета.

Можете конечно попробовать заблокировать механизм inhibit в systemd, но все последствия — на вас и вашей совести.

Мы же пойдем немного другим, менее радикальным путем.

Менеджер управления питанием Xfce

Этой командой можно посмотреть список запущенных перехватчиков:

systemd-inhibit --list

В моей системе (Linux Manjaro) вывод выглядит следующим образом:

Обратите внимание, что самого браузера Chrome в списке нет, зато есть xfce4-power-manager — менеджер управления питанием из Xfce, который принимает входящие запросы на перехват и решает что делать дальше.

Остальные сервисы обрабатывают только события засыпания (sleep).

Так что наша цель это xfce4-power-manager , именно туда мы сейчас и залезем, для нанесения правок.

xfce4-power-manager — по большей части фоновое приложение, автоматически запускаемое при старте среды Xfce. Но в отличие от прошлого поциента, тут есть некоторый интерфейс и взаимодействие с пользователем, которое происходит с помощью иконки в трее:

По нажатию правой кнопки мыши, появится меню со списком дерзких приложений, которые в данный момент перехватывают управление питанием:

Так что ответственный за весь этот электронный беспредел был наконец четко определен.

Кровавый патчинг

Исходный код менеджера управлени�� питанием находится в основном репозитории Xfce, исправляемая версия должна совпадать с установленной локально, чтобы не словить феерические проблемы совместимости.

Автор использовал версию 4.20, установленную на момент написания статьи.

Место предстоящей правки — файл xfpm-inhibit.c, в который вынесена вся логика по обработке перехватов (inhibit).

Нас интересует метод xfpm_inhibit_inhibit,строка 370, где начинается обработка входящего запроса на перехват управления.

Код метода небольшой, поэтому привожу его целиком:

static gboolean
xfpm_inhibit_inhibit (XfpmInhibit *inhibit,
                      GDBusMethodInvocation *invocation,
                      const gchar *IN_appname,
                      const gchar *IN_reason,
                      gpointer user_data)
{
  const gchar *sender;
  guint cookie;

  if (IN_appname == NULL || IN_reason == NULL)
  {
    g_dbus_method_invocation_return_error (invocation, 
                                  XFPM_ERROR, 
                                  XFPM_ERROR_INVALID_ARGUMENTS,
                                           _("Invalid arguments"));
    return TRUE;
  }

  sender = g_dbus_method_invocation_get_sender (invocation);
  cookie = xfpm_inhibit_add_application (inhibit, IN_appname, sender);

  XFPM_DEBUG ("Inhibit send application name=%s reason=%s sender=%s", 
                                          IN_appname, IN_reason, sender);

  xfpm_inhibit_has_inhibit_changed (inhibit);
  xfpm_dbus_monitor_add_unique_name (inhibit->priv->monitor, 
                                       G_BUS_TYPE_SESSION, sender);
  xfpm_power_management_inhibit_complete_inhibit (user_data, 
                                       invocation, cookie);
  return TRUE;
}

Обратите внимание на вызов метода XFPM_DEBUG, содержащего текст отладочного сообщения. Все подобные сообщения становятся видны только если запустить xfce4-power-manager с ключом --debug.

Именно так и было найдено место будущей правки, после сообщения в консоли:

xfpm_inhibit_inhibit(): Inhibit send application name=/usr/lib/chromium/chromium reason=Video Wake Lock sender=:1.459

Что мы имеем в итоге:

  • есть единственная точка входа (метод) в менеджере управления питанием, с которой начинается регистрация перехватчика управления;

  • метод принимает на вход название приложения (полный путь), посягнувшего на такой перехват.

Думаю не надо иметь высшее техническое образование, чтобы догадаться как будет выглядеть финальное решение:

if (strstr(IN_appname,"chrom") != NULL ) {
	XFPM_DEBUG ("Chrome уходи!");  
	return TRUE;  
}

Для не знающих и не владеющих:

метод strstr проверяет на входжение слова «chrom» в названии дерзкого приложения, которое отправило запрос на перехват управления питанием, если оно там есть — происходит немедленный выход из этого метода, а запрос игнорируется.

Вот так, всего лишь 4 строчки на С, вставленные в нужном месте, сразу после проверки на пустоту обламывают рога оборзевшему браузеру, созданному мировой корпорацией, которая решила, что «мы знаем как лучше».

Так это выглядит в действии после наложения моего «кровавого» патча:

В этот знаменательный день браузер Chrome.. пошел лесом.
В этот знаменательный день браузер Chrome.. пошел лесом.

Сборка

Теперь поговорим о печальном — о сборке всего этого цирка с конями.

Проект xfce4-power-manager это уже существенная часть Xfce, чтобы собрать его из исходников и заставить работать — придется постараться.

Во-первых, не стоит забирать исходники непосредственно из репозитория, поскольку в проекте используется кодогенерация и в этом случае придется заниматься еще и ей, устанавливая дополнительные пакеты в систему.

Куда проще скачать готовый архив со специально подготовленными исходниками релизной версии.

Напоминаю что мы патчим версию 4.20.

Во-вторых, придется установить в систему довольно много библиотек и утилит для разработки:

  • A working GNU toolchain

  • Gtk+ and Glib headers, in some distributions called the -devel packages

    • Xfce 4.20 requires Gtk+ 3.24 and Glib-2.0 >= 2.72 (See also: 4.20 dependencies)

      • Same version for gmodule-2.0, gobject-2.0, gthread-2.0, gio-2.0 and gdbus

    • gdk-pixbuf-2.0 >= 2.42.8

    • gobject-introspection >= 1.72

    • gtk-layer-shell 0.7.0

    • pkgconfig

Это не весь список, тут внизу страницы находится специальная таблица с описанием зависимостей между компонентами Xfce, часть из которых также придется установить.

Таков путь джедая, что поделать.

Распаковываем скачанный архив с исходниками и запускаем скрипт configure:

tar xvjf ~/Downloads/xfce4-power-manager-4.20.0.tar.bz2 
cd fce4-power-manager-4.20.0
./configure --disable-wayland

Поскольку автор не использует Wayland — тут отключена зависимость от него при сборке, но если вам оно актуально, придется установить дополнительные библиотеки:

  • wayland 1.20

  • wayland-protocols 1.25

Добавляем описанный выше блок в файл src/xfpm-inhibit.c и наконец запускаем сборку:

make

Если сборка завершится успешно, в каталоге src будет готовый бинарник с патчем, проверить который можно так:

pkill -f xfce4-power-manager
./src/xfce4-power-manager --debug

Дальше открываем в Chrome любую страницу с видео и смотрим выдаваемые сообщения:

Победа.
Победа.

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

Также подобным образом можно «обламывать рога» и другим интересным приложениям, дерзнувшим покуситься на управление питанием — с недавних пор за подобным неблаговидным делом был замечен и Firefox.

Эпило��

Грустно наблюдать (в который уж раз), как некогда хороший софт, по мере роста популярности скатывается в полное УГ отрицание пользовательского опыта и начинает считать своих пользователей полными идиотами:

неотключаемые опции, неизменяемое поведение и просто наглая ложь — все это стало, к великому сожалению, постоянными спутниками самого популярного браузера на планете.

И лишь владение языком С и навыки системного программирования все еще позволяют ставить на место оборзевшие программы — изучайте же инженерное дело настоящим образом!

В одной из следующих статей расскажу о патчах для самого браузера Chrome: «как убрать detach вкладок при таскании», «как вернуть пустую страницу для новой вкладки» и многое другое — следите за анонсами на нашем канале.

Менее цензурный оригинал статьи как обычно в нашем блоге.