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/timer-service.h>
38 : : #include <libmalcontent-timer/timer-store.h>
39 : :
40 : :
41 : : static void mct_timer_service_dispose (GObject *object);
42 : : static void mct_timer_service_finalize (GObject *object);
43 : :
44 : : static void mct_timer_service_startup_async (GssService *service,
45 : : GCancellable *cancellable,
46 : : GAsyncReadyCallback callback,
47 : : gpointer user_data);
48 : : static void mct_timer_service_startup_finish (GssService *service,
49 : : GAsyncResult *result,
50 : : GError **error);
51 : : static void mct_timer_service_shutdown (GssService *service);
52 : :
53 : : static void notify_busy_cb (GObject *obj,
54 : : GParamSpec *pspec,
55 : : gpointer user_data);
56 : :
57 : : /**
58 : : * MctTimerService:
59 : : *
60 : : * The core implementation of the timer daemon, which exposes its D-Bus
61 : : * API on the bus.
62 : : *
63 : : * Since: 0.14.0
64 : : */
65 : : struct _MctTimerService
66 : : {
67 : : GssService parent;
68 : :
69 : : char *state_directory_path; /* (owned) (nullable) */
70 : : MctTimerStore *timer_store; /* (owned) */
71 : : MctChildTimerService *child_timer_service; /* (owned) */
72 : : gulong child_timer_service_notify_busy_id;
73 : :
74 : : gboolean busy;
75 : : };
76 : :
77 [ + + + - : 10 : G_DEFINE_TYPE (MctTimerService, mct_timer_service, GSS_TYPE_SERVICE)
+ + ]
78 : :
79 : : static void
80 : 1 : mct_timer_service_class_init (MctTimerServiceClass *klass)
81 : : {
82 : 1 : GObjectClass *object_class = (GObjectClass *) klass;
83 : 1 : GssServiceClass *service_class = (GssServiceClass *) klass;
84 : :
85 : 1 : object_class->dispose = mct_timer_service_dispose;
86 : 1 : object_class->finalize = mct_timer_service_finalize;
87 : :
88 : 1 : service_class->startup_async = mct_timer_service_startup_async;
89 : 1 : service_class->startup_finish = mct_timer_service_startup_finish;
90 : 1 : service_class->shutdown = mct_timer_service_shutdown;
91 : 1 : }
92 : :
93 : : static void
94 : 1 : mct_timer_service_init (MctTimerService *self)
95 : : {
96 : 1 : g_autoptr(GOptionGroup) group = NULL;
97 : 1 : const GOptionEntry options[] =
98 : : {
99 : 1 : { "state-directory", 0, G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &self->state_directory_path,
100 : : N_("Directory to store data in (default: $STATE_DIRECTORY)"), N_("DIR") },
101 : : { NULL}
102 : : };
103 : :
104 : : /* Add an additional command line option for the state directory. */
105 : 1 : group = g_option_group_new ("service", _("Service Options:"), _("Show service help options"), NULL, NULL);
106 : 1 : g_option_group_add_entries (group, options);
107 : 1 : gss_service_add_option_group (GSS_SERVICE (self), group);
108 : 1 : }
109 : :
110 : : static void
111 : 1 : mct_timer_service_dispose (GObject *object)
112 : : {
113 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (object);
114 : :
115 [ + - ]: 1 : g_clear_signal_handler (&self->child_timer_service_notify_busy_id, self->child_timer_service);
116 : :
117 [ + - ]: 1 : g_clear_object (&self->child_timer_service);
118 [ + - ]: 1 : g_clear_object (&self->timer_store);
119 : :
120 : : /* Chain up to the parent class */
121 : 1 : G_OBJECT_CLASS (mct_timer_service_parent_class)->dispose (object);
122 : 1 : }
123 : :
124 : : static void
125 : 1 : mct_timer_service_finalize (GObject *object)
126 : : {
127 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (object);
128 : :
129 [ + - ]: 1 : g_clear_pointer (&self->state_directory_path, g_free);
130 : :
131 : : /* Chain up to the parent class */
132 : 1 : G_OBJECT_CLASS (mct_timer_service_parent_class)->finalize (object);
133 : 1 : }
134 : :
135 : : static void
136 : 1 : mct_timer_service_startup_async (GssService *service,
137 : : GCancellable *cancellable,
138 : : GAsyncReadyCallback callback,
139 : : gpointer user_data)
140 : : {
141 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (service);
142 : 1 : GDBusConnection *connection = gss_service_get_dbus_connection (GSS_SERVICE (self));
143 : : const char *state_directory_env;
144 [ + - ]: 1 : g_autoptr(GFile) state_directory = NULL;
145 [ + - ]: 1 : g_autoptr(GFile) store_directory = NULL;
146 [ + - ]: 1 : g_autoptr(GssPeerManager) peer_manager = NULL;
147 [ + - ]: 1 : g_autoptr(MctManager) policy_manager = NULL;
148 [ + - ]: 1 : g_autoptr(GTask) task = NULL;
149 [ + - ]: 1 : g_autoptr(GError) local_error = NULL;
150 : :
151 : 1 : task = g_task_new (service, cancellable, callback, user_data);
152 [ + - ]: 1 : g_task_set_source_tag (task, mct_timer_service_startup_async);
153 : :
154 : : /* Work out where the state directory is. If running under systemd, it’ll be
155 : : * passed in as `STATE_DIRECTORY` due to our use of the `StateDirectory=`
156 : : * option (see systemd.exec(5)). We prefer the command line option, if
157 : : * specified, as that’s used for debugging. */
158 : 1 : state_directory_env = g_getenv ("STATE_DIRECTORY");
159 [ + - ]: 1 : if (self->state_directory_path != NULL)
160 : 1 : state_directory = g_file_new_for_commandline_arg (self->state_directory_path);
161 [ # # ]: 0 : else if (state_directory_env != NULL)
162 : 0 : state_directory = g_file_new_for_commandline_arg (state_directory_env);
163 : : else
164 : : {
165 : 0 : g_task_return_new_error (task, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_INVALID_ENVIRONMENT,
166 : 0 : _("No state directory specified using $STATE_DIRECTORY or --state-directory"));
167 : 0 : return;
168 : : }
169 : :
170 : 1 : store_directory = g_file_get_child (state_directory, "store");
171 : 1 : self->timer_store = mct_timer_store_new (store_directory);
172 : 1 : peer_manager = GSS_PEER_MANAGER (gss_peer_manager_dbus_new (connection));
173 : 1 : policy_manager = mct_manager_new (connection);
174 : 1 : self->child_timer_service =
175 : 1 : mct_child_timer_service_new (connection,
176 : : "/org/freedesktop/MalcontentTimer1",
177 : : self->timer_store,
178 : : peer_manager,
179 : : policy_manager);
180 : 1 : self->child_timer_service_notify_busy_id =
181 : 1 : g_signal_connect (self->child_timer_service, "notify::busy",
182 : : (GCallback) notify_busy_cb, self);
183 : 1 : notify_busy_cb (G_OBJECT (self->child_timer_service), NULL, self);
184 : :
185 [ - + ]: 1 : if (!mct_child_timer_service_register (self->child_timer_service, &local_error))
186 : 0 : g_task_return_error (task, g_steal_pointer (&local_error));
187 : : else
188 : 1 : g_task_return_boolean (task, TRUE);
189 : : }
190 : :
191 : : static void
192 : 1 : mct_timer_service_startup_finish (GssService *service,
193 : : GAsyncResult *result,
194 : : GError **error)
195 : : {
196 : 1 : g_task_propagate_boolean (G_TASK (result), error);
197 : 1 : }
198 : :
199 : : static void
200 : 3 : notify_busy_cb (GObject *obj,
201 : : GParamSpec *pspec,
202 : : gpointer user_data)
203 : : {
204 : 3 : MctTimerService *self = MCT_TIMER_SERVICE (user_data);
205 : :
206 : 3 : gboolean was_busy = self->busy;
207 : 3 : gboolean now_busy = mct_child_timer_service_get_busy (self->child_timer_service);
208 : :
209 [ - + - + ]: 3 : g_debug ("%s: was_busy: %s, now_busy: %s",
210 : : G_STRFUNC, was_busy ? "yes" : "no", now_busy ? "yes" : "no");
211 : :
212 [ - + - - ]: 3 : if (was_busy && !now_busy)
213 : 0 : gss_service_release (GSS_SERVICE (self));
214 [ + - - + ]: 3 : else if (!was_busy && now_busy)
215 : 0 : gss_service_hold (GSS_SERVICE (self));
216 : :
217 : 3 : self->busy = now_busy;
218 : 3 : }
219 : :
220 : : static void
221 : 1 : mct_timer_service_shutdown (GssService *service)
222 : : {
223 : 1 : MctTimerService *self = MCT_TIMER_SERVICE (service);
224 : :
225 : 1 : mct_child_timer_service_unregister (self->child_timer_service);
226 : 1 : }
227 : :
228 : : /**
229 : : * mct_timer_service_new:
230 : : *
231 : : * Create a new [class@Malcontent.TimerService].
232 : : *
233 : : * Returns: (transfer full): a new [class@Malcontent.TimerService]
234 : : * Since: 0.14.0
235 : : */
236 : : MctTimerService *
237 : 1 : mct_timer_service_new (void)
238 : : {
239 : 1 : return g_object_new (MCT_TYPE_TIMER_SERVICE,
240 : : "bus-type", G_BUS_TYPE_SYSTEM,
241 : : "service-id", "org.freedesktop.MalcontentTimer1",
242 : : "inactivity-timeout", 30000 /* ms */,
243 : : "translation-domain", GETTEXT_PACKAGE,
244 : 1 : "parameter-string", _("— record session and app usage information for parental controls"),
245 : 1 : "summary", _("Record session and app usage information "
246 : : "provided by user login sessions to "
247 : : "monitor and enforce configured parental "
248 : : "controls screen time limits on accounts."),
249 : : NULL);
250 : : }
251 : :
|