blob: 4cfc055cce136bacb434893227cedd97c2c790b3 [file]
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
* Copyright (C) 2022 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
#include <sys/stat.h>
#include <ModemManager.h>
#include "mm-errors-types.h"
#include "mm-utils.h"
#include "mm-log-object.h"
#include "mm-dispatcher-connection.h"
#if !defined CONNECTIONDIRPACKAGE
# error CONNECTIONDIRPACKAGE must be defined at build time
#endif
#if !defined CONNECTIONDIRUSER
# error CONNECTIONDIRUSER must be defined at build time
#endif
#define OPERATION_DESCRIPTION "connection status report"
/* Maximum time a connection dispatcher command is allowed to run before
* us killing it */
#define MAX_CONNECTION_EXEC_TIME_SECS 5
struct _MMDispatcherConnection {
MMDispatcher parent;
};
struct _MMDispatcherConnectionClass {
MMDispatcherClass parent;
};
G_DEFINE_TYPE (MMDispatcherConnection, mm_dispatcher_connection, MM_TYPE_DISPATCHER)
/*****************************************************************************/
typedef struct {
gchar *modem_dbus_path;
gchar *bearer_dbus_path;
gchar *data_port;
MMDispatcherConnectionEvent event;
GList *dispatcher_scripts;
GFile *current;
guint n_failures;
} ConnectionRunContext;
static gchar *
mm_dispatcher_connection_event_to_string (MMDispatcherConnectionEvent event)
{
switch (event) {
case MM_DISPATCHER_CONNECTION_EVENT_CONNECTED:
return g_strdup ("connected");
case MM_DISPATCHER_CONNECTION_EVENT_DISCONNECTED:
return g_strdup ("disconnected");
case MM_DISPATCHER_CONNECTION_EVENT_DISCONNECT_REQUEST:
return g_strdup ("disconnect-request");
default:
return NULL;
}
}
static void
connection_run_context_free (ConnectionRunContext *ctx)
{
g_assert (!ctx->current);
g_free (ctx->modem_dbus_path);
g_free (ctx->bearer_dbus_path);
g_free (ctx->data_port);
g_list_free_full (ctx->dispatcher_scripts, (GDestroyNotify)g_object_unref);
g_slice_free (ConnectionRunContext, ctx);
}
gboolean
mm_dispatcher_connection_run_finish (MMDispatcherConnection *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void connection_run_next (GTask *task);
static void
dispatcher_run_ready (MMDispatcher *self,
GAsyncResult *res,
GTask *task)
{
ConnectionRunContext *ctx;
g_autoptr(GError) error = NULL;
ctx = g_task_get_task_data (task);
if (!mm_dispatcher_run_finish (self, res, &error)) {
ctx->n_failures++;
mm_obj_warn (self, "Cannot run " OPERATION_DESCRIPTION " operation from %s: %s",
g_file_peek_path (ctx->current), error->message);
} else
mm_obj_dbg (self, OPERATION_DESCRIPTION " operation successfully from %s",
g_file_peek_path (ctx->current));
g_clear_object (&ctx->current);
connection_run_next (task);
}
static void
connection_run_next (GTask *task)
{
MMDispatcherConnection *self;
ConnectionRunContext *ctx;
g_autofree gchar *path = NULL;
GPtrArray *aux;
g_auto(GStrv) argv = NULL;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
if (!ctx->dispatcher_scripts) {
if (ctx->n_failures)
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Failed %u " OPERATION_DESCRIPTION " operations",
ctx->n_failures);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
}
/* store current file reference in context */
ctx->current = ctx->dispatcher_scripts->data;
ctx->dispatcher_scripts = g_list_delete_link (ctx->dispatcher_scripts, ctx->dispatcher_scripts);
path = g_file_get_path (ctx->current);
/* build argv */
aux = g_ptr_array_new ();
g_ptr_array_add (aux, g_steal_pointer (&path));
g_ptr_array_add (aux, g_strdup (ctx->modem_dbus_path));
g_ptr_array_add (aux, g_strdup (ctx->bearer_dbus_path));
g_ptr_array_add (aux, g_strdup (ctx->data_port));
g_ptr_array_add (aux, mm_dispatcher_connection_event_to_string (ctx->event));
g_ptr_array_add (aux, NULL);
argv = (GStrv) g_ptr_array_free (aux, FALSE);
/* run */
mm_dispatcher_run (MM_DISPATCHER (self),
argv,
MAX_CONNECTION_EXEC_TIME_SECS,
g_task_get_cancellable (task),
(GAsyncReadyCallback) dispatcher_run_ready,
task);
}
static gint
dispatcher_script_cmp (GFile *a,
GFile *b)
{
g_autofree gchar *a_name = NULL;
g_autofree gchar *b_name = NULL;
a_name = g_file_get_basename (a);
b_name = g_file_get_basename (b);
return g_strcmp0 (a_name, b_name);
}
void
mm_dispatcher_connection_run (MMDispatcherConnection *self,
const gchar *modem_dbus_path,
const gchar *bearer_dbus_path,
const gchar *data_port,
MMDispatcherConnectionEvent event,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
ConnectionRunContext *ctx;
guint i;
const gchar *enabled_dirs[] = {
CONNECTIONDIRUSER, /* sysconfdir */
CONNECTIONDIRPACKAGE, /* libdir */
};
task = g_task_new (self, cancellable, callback, user_data);
ctx = g_slice_new0 (ConnectionRunContext);
ctx->modem_dbus_path = g_strdup (modem_dbus_path);
ctx->bearer_dbus_path = g_strdup (bearer_dbus_path);
ctx->data_port = g_strdup (data_port);
ctx->event = event;
g_task_set_task_data (task, ctx, (GDestroyNotify)connection_run_context_free);
/* Iterate over all enabled dirs and collect all dispatcher script paths */
for (i = 0; i < G_N_ELEMENTS (enabled_dirs); i++) {
g_autoptr(GFile) dir_file = NULL;
g_autoptr(GFileEnumerator) enumerator = NULL;
GFile *child;
dir_file = g_file_new_for_path (enabled_dirs[i]);
enumerator = g_file_enumerate_children (dir_file,
G_FILE_ATTRIBUTE_STANDARD_NAME,
G_FILE_QUERY_INFO_NONE,
cancellable,
NULL);
if (!enumerator)
continue;
while (g_file_enumerator_iterate (enumerator, NULL, &child, cancellable, NULL) && child)
ctx->dispatcher_scripts = g_list_prepend (ctx->dispatcher_scripts, g_object_ref (child));
}
/* Sort all by filename, regardless of the directory where they're in */
ctx->dispatcher_scripts = g_list_sort (ctx->dispatcher_scripts, (GCompareFunc)dispatcher_script_cmp);
connection_run_next (task);
}
/*****************************************************************************/
static void
mm_dispatcher_connection_init (MMDispatcherConnection *self)
{
}
static void
mm_dispatcher_connection_class_init (MMDispatcherConnectionClass *class)
{
}
MM_DEFINE_SINGLETON_GETTER (MMDispatcherConnection, mm_dispatcher_connection_get, MM_TYPE_DISPATCHER_CONNECTION,
MM_DISPATCHER_OPERATION_DESCRIPTION, OPERATION_DESCRIPTION)