1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright 2019 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include "gmemorymonitor.h"
22 #include "gmemorymonitordbus.h"
23 #include "gioerror.h"
24 #include "ginitable.h"
25 #include "giomodule-priv.h"
26 #include "glibintl.h"
27 #include "glib/gstdio.h"
28 #include "gcancellable.h"
29 #include "gdbusproxy.h"
30 #include "gdbusnamewatching.h"
31 
32 #define G_MEMORY_MONITOR_DBUS_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
33 
34 static void g_memory_monitor_dbus_iface_init (GMemoryMonitorInterface *iface);
35 static void g_memory_monitor_dbus_initable_iface_init (GInitableIface *iface);
36 
37 struct _GMemoryMonitorDBus
38 {
39   GObject parent_instance;
40 
41   guint watch_id;
42   GCancellable *cancellable;
43   GDBusProxy *proxy;
44   gulong signal_id;
45 };
46 
47 G_DEFINE_TYPE_WITH_CODE (GMemoryMonitorDBus, g_memory_monitor_dbus, G_TYPE_OBJECT,
48                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
49                                                 g_memory_monitor_dbus_initable_iface_init)
50                          G_IMPLEMENT_INTERFACE (G_TYPE_MEMORY_MONITOR,
51                                                 g_memory_monitor_dbus_iface_init)
52                          _g_io_modules_ensure_extension_points_registered ();
53                          g_io_extension_point_implement (G_MEMORY_MONITOR_EXTENSION_POINT_NAME,
54                                                          g_define_type_id,
55                                                          "dbus",
56                                                          30))
57 
58 static void
g_memory_monitor_dbus_init(GMemoryMonitorDBus * dbus)59 g_memory_monitor_dbus_init (GMemoryMonitorDBus *dbus)
60 {
61 }
62 
63 static void
proxy_signal_cb(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters,GMemoryMonitorDBus * dbus)64 proxy_signal_cb (GDBusProxy         *proxy,
65                  const gchar        *sender_name,
66                  const gchar        *signal_name,
67                  GVariant           *parameters,
68                  GMemoryMonitorDBus *dbus)
69 {
70   guint8 level;
71 
72   if (g_strcmp0 (signal_name, "LowMemoryWarning") != 0)
73     return;
74   if (parameters == NULL)
75     return;
76 
77   g_variant_get (parameters, "(y)", &level);
78   g_signal_emit_by_name (dbus, "low-memory-warning", level);
79 }
80 
81 static void
lmm_proxy_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)82 lmm_proxy_cb (GObject      *source_object,
83               GAsyncResult *res,
84               gpointer      user_data)
85 {
86   GMemoryMonitorDBus *dbus = user_data;
87   GDBusProxy *proxy;
88   GError *error = NULL;
89 
90   proxy = g_dbus_proxy_new_finish (res, &error);
91   if (!proxy)
92     {
93       g_debug ("Failed to create LowMemoryMonitor D-Bus proxy: %s",
94                error->message);
95       g_error_free (error);
96       return;
97     }
98 
99   dbus->signal_id = g_signal_connect (G_OBJECT (proxy), "g-signal",
100                                       G_CALLBACK (proxy_signal_cb), dbus);
101   dbus->proxy = proxy;
102 
103 }
104 
105 static void
lmm_appeared_cb(GDBusConnection * connection,const gchar * name,const gchar * name_owner,gpointer user_data)106 lmm_appeared_cb (GDBusConnection *connection,
107                  const gchar     *name,
108                  const gchar     *name_owner,
109                  gpointer         user_data)
110 {
111   GMemoryMonitorDBus *dbus = user_data;
112 
113   g_dbus_proxy_new (connection,
114                     G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
115                     NULL,
116                     "org.freedesktop.LowMemoryMonitor",
117                     "/org/freedesktop/LowMemoryMonitor",
118                     "org.freedesktop.LowMemoryMonitor",
119                     dbus->cancellable,
120                     lmm_proxy_cb,
121                     dbus);
122 }
123 
124 static void
lmm_vanished_cb(GDBusConnection * connection,const gchar * name,gpointer user_data)125 lmm_vanished_cb (GDBusConnection *connection,
126                  const gchar     *name,
127                  gpointer         user_data)
128 {
129   GMemoryMonitorDBus *dbus = user_data;
130 
131   g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
132   g_clear_object (&dbus->proxy);
133 }
134 
135 static gboolean
g_memory_monitor_dbus_initable_init(GInitable * initable,GCancellable * cancellable,GError ** error)136 g_memory_monitor_dbus_initable_init (GInitable     *initable,
137                                      GCancellable  *cancellable,
138                                      GError       **error)
139 {
140   GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (initable);
141 
142   dbus->cancellable = g_cancellable_new ();
143   dbus->watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
144                                      "org.freedesktop.LowMemoryMonitor",
145                                      G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
146                                      lmm_appeared_cb,
147                                      lmm_vanished_cb,
148                                      dbus,
149                                      NULL);
150 
151   return TRUE;
152 }
153 
154 static void
g_memory_monitor_dbus_finalize(GObject * object)155 g_memory_monitor_dbus_finalize (GObject *object)
156 {
157   GMemoryMonitorDBus *dbus = G_MEMORY_MONITOR_DBUS (object);
158 
159   g_cancellable_cancel (dbus->cancellable);
160   g_clear_object (&dbus->cancellable);
161   g_clear_signal_handler (&dbus->signal_id, dbus->proxy);
162   g_clear_object (&dbus->proxy);
163   g_clear_handle_id (&dbus->watch_id, g_bus_unwatch_name);
164 
165   G_OBJECT_CLASS (g_memory_monitor_dbus_parent_class)->finalize (object);
166 }
167 
168 static void
g_memory_monitor_dbus_class_init(GMemoryMonitorDBusClass * nl_class)169 g_memory_monitor_dbus_class_init (GMemoryMonitorDBusClass *nl_class)
170 {
171   GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
172 
173   gobject_class->finalize = g_memory_monitor_dbus_finalize;
174 }
175 
176 static void
g_memory_monitor_dbus_iface_init(GMemoryMonitorInterface * monitor_iface)177 g_memory_monitor_dbus_iface_init (GMemoryMonitorInterface *monitor_iface)
178 {
179 }
180 
181 static void
g_memory_monitor_dbus_initable_iface_init(GInitableIface * iface)182 g_memory_monitor_dbus_initable_iface_init (GInitableIface *iface)
183 {
184   iface->init = g_memory_monitor_dbus_initable_init;
185 }
186