Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright 2024 GNOME Foundation, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : : *
21 : : * Authors:
22 : : * - Philip Withnall <pwithnall@gnome.org>
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #include <glib.h>
28 : : #include <glib-object.h>
29 : : #include <glib-unix.h>
30 : : #include <glib/gi18n-lib.h>
31 : : #include <gio/gio.h>
32 : : #include <locale.h>
33 : : #include <libgsystemservice/peer-manager.h>
34 : : #include <libgsystemservice/peer-manager-dbus.h>
35 : : #include <libgsystemservice/service.h>
36 : : #include <libmalcontent-timer/child-timer-service.h>
37 : : #include <libmalcontent-timer/parent-timer-service.h>
38 : : #include <libmalcontent-timer/timer-service.h>
39 : : #include <libmalcontent-timer/timer-store.h>
40 : :
41 : :
42 : : static void mct_timer_service_dispose (GObject *object);
43 : : static void mct_timer_service_finalize (GObject *object);
44 : :
45 : : static void mct_timer_service_startup_async (GssService *service,
46 : : GCancellable *cancellable,
47 : : GAsyncReadyCallback callback,
48 : : gpointer user_data);
49 : : static void mct_timer_service_startup_finish (GssService *service,
50 : : GAsyncResult *result,
51 : : GError **error);
52 : : static void mct_timer_service_shutdown (GssService *service);
53 : :
54 : : static void notify_busy_cb (GObject *obj,
55 : : GParamSpec *pspec,
56 : : gpointer user_data);
57 : :
58 : : /**
59 : : * MctTimerService:
60 : : *
61 : : * The core implementation of the timer daemon, which exposes its D-Bus
62 : : * API on the bus.
63 : : *
64 : : * Since: 0.14.0
65 : : */
66 : : struct _MctTimerService
67 : : {
68 : : GssService parent;
69 : :
70 : : char *state_directory_path; /* (owned) (nullable) */
71 : : MctTimerStore *timer_store; /* (owned) */
72 : : MctChildTimerService *child_timer_service; /* (owned) */
73 : : gulong child_timer_service_notify_busy_id;
74 : : MctParentTimerService *parent_timer_service; /* (owned) */
75 : : unsigned long parent_timer_service_notify_busy_id;
76 : :
77 : : gboolean busy;
78 : : };
79 : :
80 [ + + + - : 13 : G_DEFINE_TYPE (MctTimerService, mct_timer_service, GSS_TYPE_SERVICE)
+ + ]
81 : :
82 : : static void
83 : 1 : mct_timer_service_class_init (MctTimerServiceClass *klass)
84 : : {
85 : 1 : GObjectClass *object_class = (GObjectClass *) klass;
86 : 1 : GssServiceClass *service_class = (GssServiceClass *) klass;
87 : :
88 : 1 : object_class->dispose = mct_timer_service_dispose;
89 : 1 : object_class->finalize = mct_timer_service_finalize;
90 : :
91 : 1 : service_class->startup_async = mct_timer_service_startup_async;
92 : 1 : service_class->startup_finish = mct_timer_service_startup_finish;
93 : 1 : service_class->shutdown = mct_timer_service_shutdown;
94 : 1 : }
95 : :
96 : : static void
97 : 1 : mct_timer_service_init (MctTimerService *self)
98 : : {
99 : 1 : g_autoptr(GOptionGroup) group = NULL;
100 : 1 : const GOptionEntry options[] =
101 : : {
102 : 1 : { "state-directory", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &self->state_directory_path,
103 : : N_("Directory to store data in (default: $STATE_DIRECTORY)"), N_("DIR") },
104 : : { NULL}
105 : : };
106 : :
107 : : /* Add an additional command line option for the state directory. */
108 : 1 : group = g_option_group_new ("service", _("Service Options:"), _("Show service help options"), NULL, NULL);
109 : 1 : g_option_group_add_entries (group, options);
110 : 1 : gss_service_add_option_group (GSS_SERVICE (self), group);
111 : 1 : }
112 : :
113 : : static void
114 : 1 : mct_timer_service_dispose (GObject *object)
115 : : {
116 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (object);
117 : :
118 [ + - ]: 1 : g_clear_signal_handler (&self->child_timer_service_notify_busy_id, self->child_timer_service);
119 [ + - ]: 1 : g_clear_object (&self->child_timer_service);
120 : :
121 [ + - ]: 1 : g_clear_signal_handler (&self->parent_timer_service_notify_busy_id, self->parent_timer_service);
122 [ + - ]: 1 : g_clear_object (&self->parent_timer_service);
123 : :
124 [ + - ]: 1 : g_clear_object (&self->timer_store);
125 : :
126 : : /* Chain up to the parent class */
127 : 1 : G_OBJECT_CLASS (mct_timer_service_parent_class)->dispose (object);
128 : 1 : }
129 : :
130 : : static void
131 : 1 : mct_timer_service_finalize (GObject *object)
132 : : {
133 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (object);
134 : :
135 [ + - ]: 1 : g_clear_pointer (&self->state_directory_path, g_free);
136 : :
137 : : /* Chain up to the parent class */
138 : 1 : G_OBJECT_CLASS (mct_timer_service_parent_class)->finalize (object);
139 : 1 : }
140 : :
141 : : static void startup_user_manager_cb (GObject *source_object,
142 : : GAsyncResult *result,
143 : : void *user_data);
144 : :
145 : : static void
146 : 1 : mct_timer_service_startup_async (GssService *service,
147 : : GCancellable *cancellable,
148 : : GAsyncReadyCallback callback,
149 : : gpointer user_data)
150 : : {
151 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (service);
152 : 1 : GDBusConnection *connection = gss_service_get_dbus_connection (GSS_SERVICE (self));
153 : 1 : g_autoptr(MctUserManager) user_manager = NULL;
154 : 1 : g_autoptr(GTask) task = NULL;
155 : :
156 : 1 : task = g_task_new (service, cancellable, callback, user_data);
157 [ + - ]: 1 : g_task_set_source_tag (task, mct_timer_service_startup_async);
158 : :
159 : : /* Load the user manager first as it’s the only async operation we need to do */
160 : 1 : user_manager = mct_user_manager_new (connection);
161 : :
162 : 1 : mct_user_manager_load_async (user_manager, cancellable, startup_user_manager_cb, g_steal_pointer (&task));
163 : 1 : }
164 : :
165 : : static void
166 : 1 : startup_user_manager_cb (GObject *source_object,
167 : : GAsyncResult *result,
168 : : void *user_data)
169 : : {
170 : 1 : MctUserManager *user_manager = MCT_USER_MANAGER (source_object);
171 [ + - ]: 2 : g_autoptr(GTask) task = g_steal_pointer (&user_data);
172 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (g_task_get_source_object (task));
173 : 1 : GDBusConnection *connection = gss_service_get_dbus_connection (GSS_SERVICE (self));
174 : : const char *state_directory_env;
175 [ + - ]: 1 : g_autoptr(GFile) state_directory = NULL;
176 [ + - ]: 1 : g_autoptr(GFile) store_directory = NULL;
177 [ + - ]: 1 : g_autoptr(GssPeerManager) peer_manager = NULL;
178 [ + - ]: 1 : g_autoptr(MctManager) policy_manager = NULL;
179 [ + - ]: 1 : g_autoptr(GError) local_error = NULL;
180 : :
181 [ - + ]: 1 : if (!mct_user_manager_load_finish (user_manager, result, &local_error))
182 : : {
183 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
184 : 0 : return;
185 : : }
186 : :
187 : : /* Work out where the state directory is. If running under systemd, it’ll be
188 : : * passed in as `STATE_DIRECTORY` due to our use of the `StateDirectory=`
189 : : * option (see systemd.exec(5)). We prefer the command line option, if
190 : : * specified, as that’s used for debugging. */
191 : 1 : state_directory_env = g_getenv ("STATE_DIRECTORY");
192 [ + - ]: 1 : if (self->state_directory_path != NULL)
193 : 1 : state_directory = g_file_new_for_commandline_arg (self->state_directory_path);
194 [ # # ]: 0 : else if (state_directory_env != NULL)
195 : 0 : state_directory = g_file_new_for_commandline_arg (state_directory_env);
196 : : else
197 : : {
198 : 0 : g_task_return_new_error (task, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_INVALID_ENVIRONMENT,
199 : 0 : _("No state directory specified using $STATE_DIRECTORY or --state-directory"));
200 : 0 : return;
201 : : }
202 : :
203 : 1 : store_directory = g_file_get_child (state_directory, "store");
204 : 1 : self->timer_store = mct_timer_store_new (store_directory);
205 : 1 : peer_manager = GSS_PEER_MANAGER (gss_peer_manager_dbus_new (connection));
206 : 1 : policy_manager = mct_manager_new (connection);
207 : :
208 : 1 : self->child_timer_service =
209 : 1 : mct_child_timer_service_new (connection,
210 : : "/org/freedesktop/MalcontentTimer1",
211 : : self->timer_store,
212 : : peer_manager,
213 : : policy_manager);
214 : 1 : self->child_timer_service_notify_busy_id =
215 : 1 : g_signal_connect (self->child_timer_service, "notify::busy",
216 : : (GCallback) notify_busy_cb, self);
217 : :
218 : 1 : self->parent_timer_service =
219 : 1 : mct_parent_timer_service_new (connection,
220 : : "/org/freedesktop/MalcontentTimer1",
221 : : self->timer_store,
222 : : user_manager,
223 : : peer_manager);
224 : 1 : self->parent_timer_service_notify_busy_id =
225 : 1 : g_signal_connect (self->parent_timer_service, "notify::busy",
226 : : (GCallback) notify_busy_cb, self);
227 : :
228 : 1 : notify_busy_cb (NULL, NULL, self);
229 : :
230 [ + - - + ]: 2 : if (!mct_child_timer_service_register (self->child_timer_service, &local_error) ||
231 : 1 : !mct_parent_timer_service_register (self->parent_timer_service, &local_error))
232 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
233 : : else
234 : 1 : g_task_return_boolean (task, TRUE);
235 : : }
236 : :
237 : : static void
238 : 1 : mct_timer_service_startup_finish (GssService *service,
239 : : GAsyncResult *result,
240 : : GError **error)
241 : : {
242 : 1 : g_task_propagate_boolean (G_TASK (result), error);
243 : 1 : }
244 : :
245 : : static void
246 : 5 : notify_busy_cb (GObject *obj,
247 : : GParamSpec *pspec,
248 : : gpointer user_data)
249 : : {
250 : 5 : MctTimerService *self = MCT_TIMER_SERVICE (user_data);
251 : :
252 : 5 : gboolean was_busy = self->busy;
253 : 5 : gboolean child_now_busy = mct_child_timer_service_get_busy (self->child_timer_service);
254 : 5 : gboolean parent_now_busy = mct_parent_timer_service_get_busy (self->parent_timer_service);
255 [ + - - + ]: 5 : gboolean now_busy = child_now_busy || parent_now_busy;
256 : :
257 [ - + - + : 5 : g_debug ("%s: was_busy: %s, now_busy: %s (child_now_busy: %s, parent_now_busy: %s)",
- + - + ]
258 : : G_STRFUNC,
259 : : was_busy ? "yes" : "no",
260 : : now_busy ? "yes" : "no",
261 : : child_now_busy ? "yes" : "no",
262 : : parent_now_busy ? "yes" : "no");
263 : :
264 [ - + - - ]: 5 : if (was_busy && !now_busy)
265 : 0 : gss_service_release (GSS_SERVICE (self));
266 [ + - - + ]: 5 : else if (!was_busy && now_busy)
267 : 0 : gss_service_hold (GSS_SERVICE (self));
268 : :
269 : 5 : self->busy = now_busy;
270 : 5 : }
271 : :
272 : : static void
273 : 1 : mct_timer_service_shutdown (GssService *service)
274 : : {
275 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (service);
276 : :
277 : 1 : mct_child_timer_service_unregister (self->child_timer_service);
278 : 1 : mct_parent_timer_service_unregister (self->parent_timer_service);
279 : 1 : }
280 : :
281 : : /**
282 : : * mct_timer_service_new:
283 : : *
284 : : * Create a new [class@Malcontent.TimerService].
285 : : *
286 : : * Returns: (transfer full): a new [class@Malcontent.TimerService]
287 : : * Since: 0.14.0
288 : : */
289 : : MctTimerService *
290 : 1 : mct_timer_service_new (void)
291 : : {
292 : 1 : return g_object_new (MCT_TYPE_TIMER_SERVICE,
293 : : "bus-type", G_BUS_TYPE_SYSTEM,
294 : : "service-id", "org.freedesktop.MalcontentTimer1",
295 : : "inactivity-timeout", 30000 /* ms */,
296 : : "translation-domain", GETTEXT_PACKAGE,
297 : 1 : "parameter-string", _("— record session and app usage information for parental controls"),
298 : 1 : "summary", _("Record session and app usage information "
299 : : "provided by user login sessions to "
300 : : "monitor and enforce configured parental "
301 : : "controls screen time limits on accounts."),
302 : : NULL);
303 : : }
304 : :
|