Files
eg25-manager/src/suspend.c
Arnaud Ferraris 4c6625a38d src: don't crash on incomplete user-edited config file
If a user created a custom config file and we added new fields in a
newer version, `eg25-manager` will crash by assuming all config files
are complete and up-to-date.

Moreover, creating a custom config, even to change only one option,
required copying a complete default config file and then editing the
relevant field(s). This could lead to issues when upgrading, as default
values of fields unrelated to the user change could be modified and not
applied to the user config.

This patch addresses both these issues by:
* making sure at least one config file exists
* requiring only the "default" config file (the one under `/usr/share`)
  to include all the required fields
* trying to use user config for each field, falling back to the default
  config file if the field isn't present in the user config
* exiting with a meaningful error message in case the default config
  file is missing a required field or section

That way, it will be possible to have a minimal user config file
containing only the field(s) needing a different value than the default
one, falling back to the values in the default config file.

Fixes #23
2021-10-05 11:19:35 +02:00

301 lines
10 KiB
C

/*
* Copyright (C) 2012 Red Hat, Inc.
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* Copied and adapted from ModemManager's `mm-sleep-monitor.c`:
* https://gitlab.freedesktop.org/mobile-broadband/ModemManager/
* Author: Matthias Clasen <mclasen@redhat.com>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include "manager.h"
#include <gio/gunixfdlist.h>
#define SD_NAME "org.freedesktop.login1"
#define SD_PATH "/org/freedesktop/login1"
#define SD_INTERFACE "org.freedesktop.login1.Manager"
static gboolean check_modem_resume(struct EG25Manager *manager)
{
g_message("Modem wasn't probed in time, restart it!");
manager->modem_recovery_timer = 0;
modem_reset(manager);
return FALSE;
}
static gboolean drop_inhibitor(struct EG25Manager *manager, gboolean block)
{
if (block) {
if (manager->suspend_block_fd >= 0) {
g_message("dropping systemd sleep block inhibitor");
close(manager->suspend_block_fd);
manager->suspend_block_fd = -1;
return TRUE;
}
}
else {
if (manager->suspend_delay_fd >= 0) {
g_message("dropping systemd sleep delay inhibitor");
close(manager->suspend_delay_fd);
manager->suspend_delay_fd = -1;
return TRUE;
}
}
return FALSE;
}
static void inhibit_done_delay(GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
struct EG25Manager *manager = user_data;
g_autoptr (GError) error = NULL;
GVariant *res;
GUnixFDList *fd_list;
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
result, &error);
if (!res) {
g_warning("inhibit failed: %s", error->message);
} else {
if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1)
g_warning("didn't get a single fd back");
manager->suspend_delay_fd = g_unix_fd_list_get(fd_list, 0, NULL);
g_message("inhibitor sleep fd is %d", manager->suspend_delay_fd);
g_object_unref(fd_list);
g_variant_unref(res);
}
}
static void inhibit_done_block(GObject *source,
GAsyncResult *result,
gpointer user_data)
{
GDBusProxy *suspend_proxy = G_DBUS_PROXY(source);
struct EG25Manager *manager = user_data;
g_autoptr (GError) error = NULL;
GVariant *res;
GUnixFDList *fd_list;
res = g_dbus_proxy_call_with_unix_fd_list_finish(suspend_proxy, &fd_list,
result, &error);
if (!res) {
g_warning("inhibit failed: %s", error->message);
} else {
if (!fd_list || g_unix_fd_list_get_length(fd_list) != 1)
g_warning("didn't get a single fd back");
manager->suspend_block_fd = g_unix_fd_list_get(fd_list, 0, NULL);
g_message("inhibitor block fd is %d", manager->suspend_block_fd);
g_object_unref(fd_list);
g_variant_unref(res);
}
}
/*
* After the EG25 modem sends 'RDY', it takes up to 2 minutes before all
* capabilities are operational. If the modem is suspended before that,
* calls and texts may be not recognized properly.
*/
static gboolean modem_fully_booted(struct EG25Manager *manager)
{
g_message("Modem is up for %u seconds and fully ready", manager->modem_boot_timeout);
manager->modem_boot_timer = 0;
drop_inhibitor(manager, TRUE);
return FALSE;
}
static void take_inhibitor(struct EG25Manager *manager, gboolean block)
{
GVariant *variant_arg;
if (block) {
if(manager->suspend_block_fd != -1)
drop_inhibitor(manager, TRUE);
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
"eg25manager needs to wait for modem to be fully booted",
"block");
g_message("taking systemd sleep inhibitor (blocking)");
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
variant_arg, 0, G_MAXINT, NULL, NULL,
inhibit_done_block, manager);
manager->modem_boot_timer = g_timeout_add_seconds(manager->modem_boot_timeout,
G_SOURCE_FUNC(modem_fully_booted),
manager);
}
else {
if(manager->suspend_delay_fd != -1)
drop_inhibitor(manager, FALSE);
variant_arg = g_variant_new ("(ssss)", "sleep", "eg25manager",
"eg25manager needs to prepare modem for sleep",
"delay");
g_message("taking systemd sleep inhibitor");
g_dbus_proxy_call_with_unix_fd_list(manager->suspend_proxy, "Inhibit",
variant_arg, 0, G_MAXINT, NULL, NULL,
inhibit_done_delay, manager);
}
}
static void signal_cb(GDBusProxy *proxy,
const gchar *sendername,
const gchar *signalname,
GVariant *args,
gpointer user_data)
{
struct EG25Manager *manager = user_data;
gboolean is_about_to_suspend;
if (strcmp(signalname, "PrepareForSleep") != 0)
return;
g_variant_get(args, "(b)", &is_about_to_suspend);
if (is_about_to_suspend) {
g_message("system is about to suspend");
manager->modem_state = EG25_STATE_SUSPENDING;
modem_suspend_pre(manager);
} else {
g_message("system is resuming");
take_inhibitor(manager, FALSE);
modem_resume_pre(manager);
if (
#ifdef HAVE_MMGLIB
manager->mm_modem ||
#endif
manager->modem_iface == MODEM_IFACE_OFONO) {
/*
* On some systems ModemManager doesn't handle suspend/resume, so
* we still have a valid/managed modem when resuming. In this case,
* do the whole resume sequence immediately.
*
* If modem is managed by ofono, we also do resume sequence immediately
* as ofono handles resuming from sleep itself.
*/
manager->modem_state = EG25_STATE_CONFIGURED;
modem_resume_post(manager);
} else {
manager->modem_state = EG25_STATE_RESUMING;
manager->modem_recovery_timer = g_timeout_add_seconds(manager->modem_recovery_timeout,
G_SOURCE_FUNC(check_modem_resume),
manager);
}
}
}
static void name_owner_cb(GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY(object);
struct EG25Manager *manager = user_data;
char *owner;
g_assert(proxy == manager->suspend_proxy);
owner = g_dbus_proxy_get_name_owner(proxy);
if (owner) {
take_inhibitor(manager, FALSE);
g_free(owner);
} else {
drop_inhibitor(manager, FALSE);
}
}
static void on_proxy_acquired(GObject *object,
GAsyncResult *res,
struct EG25Manager *manager)
{
g_autoptr (GError) error = NULL;
char *owner;
manager->suspend_proxy = g_dbus_proxy_new_for_bus_finish(res, &error);
if (!manager->suspend_proxy) {
g_warning("failed to acquire logind proxy: %s", error->message);
return;
}
g_signal_connect(manager->suspend_proxy, "notify::g-name-owner",
G_CALLBACK(name_owner_cb), manager);
g_signal_connect(manager->suspend_proxy, "g-signal",
G_CALLBACK(signal_cb), manager);
owner = g_dbus_proxy_get_name_owner(manager->suspend_proxy);
if (owner) {
take_inhibitor(manager, FALSE);
g_free(owner);
}
}
void suspend_init(struct EG25Manager *manager, toml_table_t *config[])
{
toml_table_t *suspend_config[EG25_CONFIG_COUNT];
for (int i = 0; i < EG25_CONFIG_COUNT; i++)
suspend_config[i] = config[i] ? toml_table_in(config[i], "suspend") : NULL;
/*
* The `suspend` section is optional in both the user and system config files,
* so let's make sure suspend_config[EG25_CONFIG_SYS] is valid if one of the
* files has it.
*/
if (suspend_config[EG25_CONFIG_USER] && !suspend_config[EG25_CONFIG_SYS]) {
suspend_config[EG25_CONFIG_SYS] = suspend_config[EG25_CONFIG_USER];
suspend_config[EG25_CONFIG_USER] = NULL;
}
if (suspend_config[EG25_CONFIG_SYS]) {
config_get_uint(suspend_config, "boot_timeout", &manager->modem_boot_timeout);
config_get_uint(suspend_config, "recovery_timeout", &manager->modem_recovery_timeout);
}
if (manager->modem_boot_timeout == 0)
manager->modem_boot_timeout = 120;
if (manager->modem_recovery_timeout == 0)
manager->modem_recovery_timeout = 9;
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL, SD_NAME, SD_PATH, SD_INTERFACE, NULL,
(GAsyncReadyCallback)on_proxy_acquired, manager);
}
void suspend_destroy(struct EG25Manager *manager)
{
drop_inhibitor(manager, FALSE);
drop_inhibitor(manager, TRUE);
if (manager->modem_recovery_timer) {
g_source_remove(manager->modem_recovery_timer);
manager->modem_recovery_timer = 0;
}
if (manager->modem_boot_timer) {
g_source_remove(manager->modem_boot_timer);
manager->modem_boot_timer = 0;
}
if (manager->suspend_proxy) {
g_object_unref(manager->suspend_proxy);
manager->suspend_proxy = NULL;
}
}
void suspend_inhibit(struct EG25Manager *manager, gboolean inhibit, gboolean block)
{
if (inhibit)
take_inhibitor(manager, block);
else
drop_inhibitor(manager, block);
}