Files
eg25-manager/src/mm-iface.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

229 lines
7.4 KiB
C

/*
* Copyright (C) 2019 Purism SPC
* Copyright (C) 2020 Arnaud Ferraris <arnaud.ferraris@gmail.com>
*
* Copied and adapted from Wys' `main.c`: https://source.puri.sm/Librem5/wys/
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "mm-iface.h"
#include <string.h>
static void state_changed_cb(MMModem *modem,
MMModemState old,
MMModemState new,
MMModemStateChangeReason reason,
struct EG25Manager *manager)
{
if (manager->modem_state >= EG25_STATE_CONFIGURED)
modem_update_state(manager, new);
}
static void add_modem(struct EG25Manager *manager, GDBusObject *object)
{
const gchar *path;
MmGdbusModem *gdbus_modem;
path = g_dbus_object_get_object_path(object);
g_message("Adding new modem `%s'", path);
g_assert(MM_IS_OBJECT (object));
manager->mm_modem = mm_object_get_modem(MM_OBJECT(object));
g_assert_nonnull(manager->mm_modem);
if (manager->modem_state == EG25_STATE_RESUMING) {
if (manager->modem_recovery_timer) {
g_source_remove(manager->modem_recovery_timer);
manager->modem_recovery_timer = 0;
}
modem_resume_post(manager);
manager->modem_state = EG25_STATE_CONFIGURED;
}
if (manager->modem_state < EG25_STATE_ACQUIRED)
manager->modem_state = EG25_STATE_ACQUIRED;
if (manager->modem_state < EG25_STATE_CONFIGURED)
modem_configure(manager);
path = mm_modem_get_device(manager->mm_modem);
if (manager->modem_usb_id)
g_free(manager->modem_usb_id);
manager->modem_usb_id = g_strdup(strrchr(path, '/') + 1);
gdbus_modem = MM_GDBUS_MODEM(manager->mm_modem);
g_signal_connect(gdbus_modem, "state-changed", G_CALLBACK(state_changed_cb), manager);
}
static void add_modem_location(struct EG25Manager *manager, GDBusObject *object)
{
const gchar *path;
path = g_dbus_object_get_object_path(object);
g_message("Adding new modem with location capabilities `%s'", path);
manager->mm_location = mm_object_get_modem_location(MM_OBJECT(object));
g_assert_nonnull(manager->mm_location);
}
static void interface_added_cb (struct EG25Manager *manager,
GDBusObject *object,
GDBusInterface *interface)
{
GDBusInterfaceInfo *info;
info = g_dbus_interface_get_info(interface);
g_message("ModemManager interface `%s' found on object `%s'",
info->name, g_dbus_object_get_object_path(object));
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0)
add_modem(manager, object);
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM_LOCATION) == 0)
add_modem_location(manager, object);
}
static void interface_removed_cb(struct EG25Manager *manager,
GDBusObject *object,
GDBusInterface *interface)
{
const gchar *path;
GDBusInterfaceInfo *info;
path = g_dbus_object_get_object_path(object);
info = g_dbus_interface_get_info(interface);
g_message("ModemManager interface `%s' removed on object `%s'", info->name, path);
if (g_strcmp0(info->name, MM_DBUS_INTERFACE_MODEM) == 0)
manager->mm_modem = NULL;
}
static void add_mm_object(struct EG25Manager *manager, GDBusObject *object)
{
GList *ifaces, *node;
ifaces = g_dbus_object_get_interfaces(object);
for (node = ifaces; node; node = node->next)
interface_added_cb(manager, object, G_DBUS_INTERFACE(node->data));
g_list_free_full(ifaces, g_object_unref);
}
static void add_mm_objects(struct EG25Manager *manager)
{
GList *objects, *node;
objects = g_dbus_object_manager_get_objects(G_DBUS_OBJECT_MANAGER(manager->mm_manager));
for (node = objects; node; node = node->next)
add_mm_object(manager, G_DBUS_OBJECT(node->data));
g_list_free_full(objects, g_object_unref);
}
static void object_added_cb(struct EG25Manager *manager, GDBusObject *object)
{
g_message("ModemManager object `%s' added", g_dbus_object_get_object_path(object));
add_mm_object(manager, object);
}
static void object_removed_cb(struct EG25Manager *manager, GDBusObject *object)
{
const gchar *path;
path = g_dbus_object_get_object_path(object);
g_message("ModemManager object `%s' removed", path);
manager->mm_modem = NULL;
}
static void mm_manager_new_cb(GDBusConnection *connection,
GAsyncResult *res,
struct EG25Manager *manager)
{
g_autoptr (GError) error = NULL;
manager->mm_manager = mm_manager_new_finish(res, &error);
if (!manager->mm_manager)
g_critical("Error creating ModemManager Manager: %s", error->message);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"interface-added", G_CALLBACK(interface_added_cb), manager);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"interface-removed", G_CALLBACK(interface_removed_cb), manager);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"object-added", G_CALLBACK(object_added_cb), manager);
g_signal_connect_swapped(G_DBUS_OBJECT_MANAGER(manager->mm_manager),
"object-removed", G_CALLBACK(object_removed_cb), manager);
add_mm_objects(manager);
}
static void mm_appeared_cb(GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
struct EG25Manager *manager)
{
g_message("ModemManager appeared on D-Bus");
if (manager->modem_iface != MODEM_IFACE_NONE) {
g_critical("Modem interface already found! Make sure to only run either of ModemManager or oFono.");
return;
}
manager->modem_iface = MODEM_IFACE_MODEMMANAGER;
mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
NULL, (GAsyncReadyCallback)mm_manager_new_cb, manager);
}
static void mm_iface_clean(struct EG25Manager *manager)
{
if (manager->mm_manager) {
g_clear_object(&manager->mm_manager);
manager->mm_manager = NULL;
}
if (manager->modem_usb_id) {
g_free(manager->modem_usb_id);
manager->modem_usb_id = NULL;
}
if (manager->modem_iface == MODEM_IFACE_MODEMMANAGER) {
manager->modem_iface = MODEM_IFACE_NONE;
}
}
static void mm_vanished_cb(GDBusConnection *connection,
const gchar *name,
struct EG25Manager *manager)
{
g_message("ModemManager vanished from D-Bus");
mm_iface_clean(manager);
}
void mm_iface_init(struct EG25Manager *manager, toml_table_t *config[])
{
manager->mm_watch = g_bus_watch_name(G_BUS_TYPE_SYSTEM, MM_DBUS_SERVICE,
G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
(GBusNameAppearedCallback)mm_appeared_cb,
(GBusNameVanishedCallback)mm_vanished_cb,
manager, NULL);
}
void mm_iface_destroy(struct EG25Manager *manager)
{
mm_iface_clean(manager);
if (manager->mm_watch != 0) {
g_bus_unwatch_name(manager->mm_watch);
manager->mm_watch = 0;
}
}