Files
wireplumber/modules/module-file-monitor-api.c
George Kiagiadakis 4736d56557 log: implement a log topics system, like pipewire
The intention is to make checks for enabled log topics faster.

Every topic has its own structure that is statically defined in the file
where the logs are printed from. The structure is initialized transparently
when it is first used and it contains all the log level flags for the levels
that this topic should print messages. It is then checked on the wp_log()
macro before printing the message.

Topics from SPA/PipeWire are also handled natively, so messages are printed
directly without checking if the topic is enabled, since the PipeWire and SPA
macros do the checking themselves.

Messages coming from GLib are checked inside the handler.

An internal WpLogFields object is used to manage the state of each log
message, populating all the fields appropriately from the place they
are coming from (wp_log, spa_log, glib log), formatting the message and
then printing it. For printing to the journald, we still use the glib
message handler, converting all the needed fields to GLogField on demand.
That message handler does not do any checks for the topic or the level, so
we can just call it to send the message.
2023-05-16 20:42:28 +03:00

214 lines
5.7 KiB
C

/* WirePlumber
*
* Copyright © 2021 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <wp/wp.h>
WP_DEFINE_LOCAL_LOG_TOPIC ("m-file-monitor-api")
struct _WpFileMonitorApi
{
WpPlugin parent;
GHashTable *monitors;
};
enum {
ACTION_ADD_WATCH,
ACTION_REMOVE_WATCH,
SIGNAL_CHANGED,
N_SIGNALS
};
static guint signals[N_SIGNALS] = {0};
G_DECLARE_FINAL_TYPE (WpFileMonitorApi, wp_file_monitor_api, WP,
FILE_MONITOR_API, WpPlugin)
G_DEFINE_TYPE (WpFileMonitorApi, wp_file_monitor_api, WP_TYPE_PLUGIN)
static void
wp_file_monitor_api_init (WpFileMonitorApi * self)
{
self->monitors = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
g_object_unref);
}
static void
wp_file_monitor_api_finalize (GObject * object)
{
WpFileMonitorApi * self = WP_FILE_MONITOR_API (object);
g_clear_pointer (&self->monitors, g_hash_table_unref);
G_OBJECT_CLASS (wp_file_monitor_api_parent_class)->finalize (object);
}
static void
on_file_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other,
GFileMonitorEvent evtype, gpointer data)
{
WpFileMonitorApi * self = WP_FILE_MONITOR_API (data);
g_autofree char *fpath = g_file_get_path (file);
g_autofree char *opath = NULL;
const gchar *evtype_str = NULL;
if (other)
opath = g_file_get_path (other);
switch(evtype) {
case G_FILE_MONITOR_EVENT_CHANGED:
evtype_str = "changed";
break;
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
evtype_str = "changes-done-hint";
break;
case G_FILE_MONITOR_EVENT_DELETED:
evtype_str = "deleted";
break;
case G_FILE_MONITOR_EVENT_CREATED:
evtype_str = "created";
break;
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
evtype_str = "attribute-changed";
break;
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
evtype_str = "pre-unmount";
break;
case G_FILE_MONITOR_EVENT_UNMOUNTED:
evtype_str = "unmounted";
break;
case G_FILE_MONITOR_EVENT_MOVED:
evtype_str = "moved";
break;
case G_FILE_MONITOR_EVENT_RENAMED:
evtype_str = "renamed";
break;
case G_FILE_MONITOR_EVENT_MOVED_IN:
evtype_str = "moved-in";
break;
case G_FILE_MONITOR_EVENT_MOVED_OUT:
evtype_str = "moved-out";
break;
default:
wp_warning_object (self, "Unknown event type %d", evtype);
break;
}
g_signal_emit (self, signals[SIGNAL_CHANGED], 0, fpath, opath, evtype_str);
}
static gboolean
wp_file_monitor_api_add_watch (WpFileMonitorApi * self, const gchar *path,
const gchar *flags_str)
{
g_autoptr (GError) e = NULL;
g_autoptr (GFileMonitor) fm = NULL;
g_autoptr (GFile) f = NULL;
GFileMonitorFlags flags = G_FILE_MONITOR_NONE;
/* don't do anything if the path is already being watched */
if (g_hash_table_contains (self->monitors, path))
return TRUE;
/* get path */
f = g_file_new_for_path (path);
if (!f) {
wp_warning_object (self, "Invalid path '%s'", path);
return FALSE;
}
/* parse flags */
for (guint i = 0; flags_str && i < strlen (flags_str); i++) {
switch (flags_str[i]) {
case 'o': flags |= G_FILE_MONITOR_WATCH_MOUNTS; break;
case 's': flags |= G_FILE_MONITOR_SEND_MOVED; break;
case 'h': flags |= G_FILE_MONITOR_WATCH_HARD_LINKS; break;
case 'm': flags |= G_FILE_MONITOR_WATCH_MOVES; break;
default:
break;
}
}
/* create the file monitor for that path */
fm = g_file_monitor (f, flags, NULL, &e);
if (e) {
wp_warning_object (self, "Failed to add watch for path '%s': %s", path,
e->message);
return FALSE;
}
/* handle changed signal and add it to monitors table */
g_signal_connect (fm, "changed", G_CALLBACK (on_file_monitor_changed), self);
g_hash_table_insert (self->monitors, g_strdup (path), g_steal_pointer (&fm));
return TRUE;
}
static void
wp_file_monitor_api_remove_watch (WpFileMonitorApi * self, const gchar *path)
{
g_hash_table_remove (self->monitors, path);
}
static void
wp_file_monitor_api_enable (WpPlugin * plugin, WpTransition * transition)
{
WpFileMonitorApi * self = WP_FILE_MONITOR_API (plugin);
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
}
static void
wp_file_monitor_api_disable (WpPlugin * plugin)
{
WpFileMonitorApi * self = WP_FILE_MONITOR_API (plugin);
g_hash_table_remove_all (self->monitors);
}
static void
wp_file_monitor_api_class_init (WpFileMonitorApiClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpPluginClass *plugin_class = (WpPluginClass *) klass;
object_class->finalize = wp_file_monitor_api_finalize;
plugin_class->enable = wp_file_monitor_api_enable;
plugin_class->disable = wp_file_monitor_api_disable;
signals[ACTION_ADD_WATCH] = g_signal_new_class_handler (
"add-watch", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
(GCallback) wp_file_monitor_api_add_watch,
NULL, NULL, NULL,
G_TYPE_BOOLEAN, 2, G_TYPE_STRING, G_TYPE_STRING);
signals[ACTION_REMOVE_WATCH] = g_signal_new_class_handler (
"remove-watch", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
(GCallback) wp_file_monitor_api_remove_watch,
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_STRING);
signals[SIGNAL_CHANGED] = g_signal_new (
"changed", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
}
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, GVariant * args, GError ** error)
{
return G_OBJECT (g_object_new (wp_file_monitor_api_get_type (),
"name", "file-monitor-api",
"core", core,
NULL));
}