GCC Code Coverage Report


Directory: ./
File: libgsystemservice/service.c
Date: 2024-04-09 14:29:48
Exec Total Coverage
Lines: 89 386 23.1%
Functions: 9 32 28.1%
Branches: 32 222 14.4%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright © 2017 Endless Mobile, 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 <withnall@endlessm.com>
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 <libgsystemservice/service.h>
33 #include <locale.h>
34 #include <polkit/polkit.h>
35 #include <stdio.h>
36 #include <systemd/sd-daemon.h>
37
38
39 /**
40 * SECTION:service
41 * @short_description: System service base class
42 * @stability: Stable
43 * @include: libgsystemservice/service.h
44 *
45 * A skeleton implementation of a system service, which exposes itself on the
46 * bus with a well-known name.
47 *
48 * It follows the implementation recommendations in `man 7 daemon`.
49 *
50 * Since 0.2.0, it registers a D-Bus object at `/org/freedesktop/Debugging`
51 * which exposes controls for debugging the service. Requests to these controls
52 * from peers must be authorized. The default policy is to check against polkit
53 * using the action ID set as #GssService:debug-controller-action-id, if set.
54 *
55 * If #GssService:debug-controller-action-id is not set, requests to the debug
56 * object will be accepted unconditionally if the service is running on the
57 * D-Bus session bus, and rejected unconditionally if the service is running on
58 * the system bus.
59 *
60 * Since: 0.1.0
61 */
62
63 /* These require polkit 0.114 */
64 #ifndef HAVE_POLKIT_114
65 G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthority, g_object_unref)
66 G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitSubject, g_object_unref)
67 G_DEFINE_AUTOPTR_CLEANUP_FUNC (PolkitAuthorizationResult, g_object_unref)
68 #endif /* !HAVE_POLKIT_114 */
69
70 /* These errors do not need to be registered with
71 * g_dbus_error_register_error_domain() as they never go over the bus. */
72 G_DEFINE_QUARK (GssServiceError, gss_service_error)
73
74 /* A way of automatically removing bus names when going out of scope. */
75 typedef guint BusNameId;
76 G_DEFINE_AUTO_CLEANUP_FREE_FUNC (BusNameId, g_bus_unown_name, 0)
77
78 static void gss_service_dispose (GObject *object);
79 static void gss_service_get_property (GObject *object,
80 guint property_id,
81 GValue *value,
82 GParamSpec *pspec);
83 static void gss_service_set_property (GObject *object,
84 guint property_id,
85 const GValue *value,
86 GParamSpec *pspec);
87
88 static void cancel_inactivity_timeout (GssService *self);
89
90 /**
91 * GssService:
92 *
93 * A skeleton implementation of a system service, which exposes itself on the
94 * bus with a well-known name.
95 *
96 * It follows the implementation recommendations in `man 7 daemon`.
97 *
98 * Since: 0.1.0
99 */
100 typedef struct
101 {
102 GPtrArray *option_groups; /* (owned) (element-type GOptionGroup) */
103 gchar *translation_domain; /* (owned) */
104 gchar *parameter_string; /* (owned) */
105 gchar *summary; /* (owned) */
106 GBusType bus_type;
107 gchar *service_id; /* (owned) */
108
109 GCancellable *cancellable; /* (owned) */
110 GDBusConnection *connection; /* (owned) */
111 GError *run_error; /* (nullable) (owned) */
112 gboolean run_exited;
113 int run_exit_signal;
114 gboolean allow_root;
115
116 GMainContext *context; /* (owned) */
117
118 GSource *sigint_source; /* (owned) (nullable) */
119 GSource *sigterm_source; /* (owned) (nullable) */
120
121 guint inactivity_timeout_ms; /* 0 indicates no timeout */
122 GSource *inactivity_timeout_source; /* (owned) (nullable) */
123 guint hold_count;
124
125 GDebugController *debug_controller; /* (owned) (nullable) */
126 gulong debug_controller_authorize_id;
127 gchar *debug_controller_action_id; /* (owned) (nullable) */
128 } GssServicePrivate;
129
130 typedef enum
131 {
132 PROP_TRANSLATION_DOMAIN = 1,
133 PROP_PARAMETER_STRING,
134 PROP_SUMMARY,
135 PROP_BUS_TYPE,
136 PROP_SERVICE_ID,
137 PROP_INACTIVITY_TIMEOUT,
138 PROP_ALLOW_ROOT,
139 PROP_DEBUG_CONTROLLER_ACTION_ID,
140 PROP_DEBUG_CONTROLLER,
141 } GssServiceProperty;
142
143
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 8 times.
42 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GssService, gss_service, G_TYPE_OBJECT)
144
145 static void
146 1 gss_service_class_init (GssServiceClass *klass)
147 {
148 1 GObjectClass *object_class = (GObjectClass *) klass;
149 1 GParamSpec *props[PROP_DEBUG_CONTROLLER + 1] = { NULL, };
150
151 1 object_class->dispose = gss_service_dispose;
152 1 object_class->get_property = gss_service_get_property;
153 1 object_class->set_property = gss_service_set_property;
154
155 /**
156 * GssService:translation-domain:
157 *
158 * The gettext translation domain to use for translating command line help.
159 * This is typically `GETTEXT_PACKAGE`.
160 *
161 * Since: 0.1.0
162 */
163 1 props[PROP_TRANSLATION_DOMAIN] =
164 1 g_param_spec_string ("translation-domain", "Translation Domain",
165 "The gettext translation domain to use for "
166 "translating command line help.",
167 NULL,
168 G_PARAM_READWRITE |
169 G_PARAM_CONSTRUCT_ONLY |
170 G_PARAM_STATIC_STRINGS);
171
172 /**
173 * GssService:parameter-string:
174 *
175 * A string which is displayed on the first line of `--help` output, after the
176 * usage summary. It should be a sentence fragment which describes further
177 * parameters, or summarises the functionality of the program (after an
178 * em-dash).
179 *
180 * Since: 0.1.0
181 */
182 1 props[PROP_PARAMETER_STRING] =
183 1 g_param_spec_string ("parameter-string", "Parameter String",
184 "A string which is displayed on the first line of "
185 "--help output, after the usage summary.",
186 NULL,
187 G_PARAM_READWRITE |
188 G_PARAM_CONSTRUCT_ONLY |
189 G_PARAM_STATIC_STRINGS);
190
191 /**
192 * GssService:summary:
193 *
194 * Summary of the service to display as part of the command line help. This
195 * should be translated, and be one or more complete sentences.
196 *
197 * Since: 0.1.0
198 */
199 1 props[PROP_SUMMARY] =
200 1 g_param_spec_string ("summary", "Summary",
201 "Summary of the service to display as part of the "
202 "command line help.",
203 NULL,
204 G_PARAM_READWRITE |
205 G_PARAM_CONSTRUCT_ONLY |
206 G_PARAM_STATIC_STRINGS);
207
208 /**
209 * GssService:bus-type:
210 *
211 * The type of bus which the service’s well-known name should be exposed on.
212 * This can be overridden on the command line.
213 *
214 * Since: 0.1.0
215 */
216 1 props[PROP_BUS_TYPE] =
217 1 g_param_spec_enum ("bus-type", "Bus Type",
218 "The type of bus which the service’s well-known name "
219 "should be exposed on.",
220 G_TYPE_BUS_TYPE,
221 G_BUS_TYPE_SYSTEM,
222 G_PARAM_READWRITE |
223 G_PARAM_CONSTRUCT_ONLY |
224 G_PARAM_STATIC_STRINGS);
225
226 /**
227 * GssService:service-id:
228 *
229 * The ID of the service, which must be a well-known D-Bus name to uniquely
230 * identify the service.
231 *
232 * Since: 0.1.0
233 */
234 1 props[PROP_SERVICE_ID] =
235 1 g_param_spec_string ("service-id", "Service ID",
236 "The ID of the service, which must be a well-known "
237 "D-Bus name to uniquely identify the service.",
238 NULL,
239 G_PARAM_READWRITE |
240 G_PARAM_CONSTRUCT_ONLY |
241 G_PARAM_STATIC_STRINGS);
242
243 /**
244 * GssService:inactivity-timeout:
245 *
246 * An inactivity timeout (in ms), after which the service will automatically
247 * exit unless its hold count is greater than zero. Increase/Decrease the hold
248 * count by calling gss_service_hold()/gss_service_release().
249 *
250 * A timeout of zero means the service will never automatically exit.
251 *
252 * Since: 0.1.0
253 */
254 1 props[PROP_INACTIVITY_TIMEOUT] =
255 1 g_param_spec_uint ("inactivity-timeout", "Inactivity Timeout",
256 "An inactivity timeout (in ms), after which the "
257 "service will automatically exit.",
258 0, G_MAXUINT, 0,
259 G_PARAM_READWRITE |
260 G_PARAM_STATIC_STRINGS);
261
262 /**
263 * GssService:allow-root:
264 *
265 * If %TRUE, the service can be run by root. Otherwise, and by default,
266 * gss_service_run() will fail if the UID or effective UID is zero.
267 *
268 * Since: 0.1.0
269 */
270 1 props[PROP_ALLOW_ROOT] =
271 1 g_param_spec_boolean ("allow-root", "Allow Root",
272 "Whether the service should be allowed to run "
273 "as root (UID 0).",
274 FALSE,
275 G_PARAM_READWRITE |
276 G_PARAM_CONSTRUCT_ONLY |
277 G_PARAM_STATIC_STRINGS);
278
279 /**
280 * GssService:debug-controller-action-id: (nullable)
281 *
282 * The ID of a polkit action to check for authorization when a peer requests
283 * to change the debug settings.
284 *
285 * polkit will be queried with this action ID and the peer as the subject.
286 *
287 * If this is %NULL, the default security policy will be applied, as
288 * documented in #GssService.
289 *
290 * Since: 0.2.0
291 */
292 1 props[PROP_DEBUG_CONTROLLER_ACTION_ID] =
293 1 g_param_spec_string ("debug-controller-action-id",
294 "Debug Controller Polkit Action ID",
295 "The ID of a polkit action to check for "
296 "authorization when a peer requests to change the "
297 "debug settings.",
298 NULL,
299 G_PARAM_READWRITE |
300 G_PARAM_CONSTRUCT_ONLY |
301 G_PARAM_STATIC_STRINGS);
302
303 /**
304 * GssService:debug-controller: (nullable)
305 *
306 * The debug controller used for the service. This exposes information about
307 * debug settings, such as whether debug output is enabled.
308 *
309 * Since: 0.2.0
310 */
311 1 props[PROP_DEBUG_CONTROLLER] =
312 1 g_param_spec_object ("debug-controller",
313 "Debug Controller",
314 "The debug controller used for the service.",
315 G_TYPE_DEBUG_CONTROLLER,
316 G_PARAM_READABLE |
317 G_PARAM_STATIC_STRINGS);
318
319 1 g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
320 1 }
321
322 static void
323 1 gss_service_init (GssService *self)
324 {
325 1 GssServicePrivate *priv = gss_service_get_instance_private (self);
326
327 1 priv->cancellable = g_cancellable_new ();
328 1 priv->context = g_main_context_ref_thread_default ();
329 1 }
330
331 static void
332 source_destroy_and_unref (GSource *source)
333 {
334 if (source != NULL)
335 {
336 g_source_destroy (source);
337 g_source_unref (source);
338 }
339 }
340
341 static void
342 1 gss_service_dispose (GObject *object)
343 {
344 1 GssService *self = GSS_SERVICE (object);
345 1 GssServicePrivate *priv = gss_service_get_instance_private (self);
346
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_pointer (&priv->sigint_source, source_destroy_and_unref);
348
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_pointer (&priv->sigterm_source, source_destroy_and_unref);
349
350 1 cancel_inactivity_timeout (self);
351
352 1 g_cancellable_cancel (priv->cancellable);
353
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_object (&priv->cancellable);
354
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_pointer (&priv->option_groups, g_ptr_array_unref);
356
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&priv->translation_domain, g_free);
357
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&priv->parameter_string, g_free);
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_pointer (&priv->summary, g_free);
359
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&priv->service_id, g_free);
360 1 g_clear_error (&priv->run_error);
361
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_signal_handler (&priv->debug_controller_authorize_id, priv->debug_controller);
362
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_object (&priv->debug_controller);
363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_pointer (&priv->debug_controller_action_id, g_free);
364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_object (&priv->connection);
365
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 g_clear_pointer (&priv->context, g_main_context_unref);
366
367 /* Chain up to the parent class */
368 1 G_OBJECT_CLASS (gss_service_parent_class)->dispose (object);
369 1 }
370
371 static void
372 gss_service_get_property (GObject *object,
373 guint property_id,
374 GValue *value,
375 GParamSpec *pspec)
376 {
377 GssService *self = GSS_SERVICE (object);
378 GssServicePrivate *priv = gss_service_get_instance_private (self);
379
380 switch ((GssServiceProperty) property_id)
381 {
382 case PROP_TRANSLATION_DOMAIN:
383 g_value_set_string (value, priv->translation_domain);
384 break;
385 case PROP_PARAMETER_STRING:
386 g_value_set_string (value, priv->parameter_string);
387 break;
388 case PROP_SUMMARY:
389 g_value_set_string (value, priv->summary);
390 break;
391 case PROP_BUS_TYPE:
392 g_value_set_enum (value, priv->bus_type);
393 break;
394 case PROP_SERVICE_ID:
395 g_value_set_string (value, priv->service_id);
396 break;
397 case PROP_INACTIVITY_TIMEOUT:
398 g_value_set_uint (value, priv->inactivity_timeout_ms);
399 break;
400 case PROP_ALLOW_ROOT:
401 g_value_set_boolean (value, priv->allow_root);
402 break;
403 case PROP_DEBUG_CONTROLLER_ACTION_ID:
404 g_value_set_string (value, priv->debug_controller_action_id);
405 break;
406 case PROP_DEBUG_CONTROLLER:
407 g_value_set_object (value, priv->debug_controller);
408 break;
409 default:
410 g_assert_not_reached ();
411 }
412 }
413
414 static void
415 7 gss_service_set_property (GObject *object,
416 guint property_id,
417 const GValue *value,
418 GParamSpec *pspec)
419 {
420 7 GssService *self = GSS_SERVICE (object);
421 7 GssServicePrivate *priv = gss_service_get_instance_private (self);
422
423
7/9
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
7 switch ((GssServiceProperty) property_id)
424 {
425 1 case PROP_TRANSLATION_DOMAIN:
426 /* Construct only. */
427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (priv->translation_domain == NULL);
428 1 priv->translation_domain = g_value_dup_string (value);
429 1 break;
430 1 case PROP_PARAMETER_STRING:
431 /* Construct only. */
432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (priv->parameter_string == NULL);
433 1 priv->parameter_string = g_value_dup_string (value);
434 1 break;
435 1 case PROP_SUMMARY:
436 /* Construct only. */
437
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (priv->summary == NULL);
438 1 priv->summary = g_value_dup_string (value);
439 1 break;
440 1 case PROP_BUS_TYPE:
441 /* Construct only. */
442 1 priv->bus_type = g_value_get_enum (value);
443 1 break;
444 1 case PROP_SERVICE_ID:
445 /* Construct only. */
446
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (priv->service_id == NULL);
447 1 priv->service_id = g_value_dup_string (value);
448 1 break;
449 case PROP_INACTIVITY_TIMEOUT:
450 gss_service_set_inactivity_timeout (self, g_value_get_uint (value));
451 break;
452 1 case PROP_ALLOW_ROOT:
453 /* Construct only. */
454 1 priv->allow_root = g_value_get_boolean (value);
455 1 break;
456 1 case PROP_DEBUG_CONTROLLER_ACTION_ID:
457 /* Construct only. */
458
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_assert (priv->debug_controller_action_id == NULL);
459 1 priv->debug_controller_action_id = g_value_dup_string (value);
460 1 break;
461 case PROP_DEBUG_CONTROLLER:
462 /* Read only */
463 G_GNUC_FALLTHROUGH;
464 default:
465 g_assert_not_reached ();
466 }
467 7 }
468
469 /**
470 * gss_service_add_option_group:
471 * @self: a #GssService
472 * @group: (transfer none): an option group to add
473 *
474 * Add an option group to the command line options. The options in this group
475 * will be listed in the help output, and their values will be set when
476 * gss_service_run() is called.
477 *
478 * This is effectively a wrapper around g_option_context_add_group(), so see the
479 * documentation for that for more information.
480 *
481 * Since: 0.1.0
482 */
483 void
484 gss_service_add_option_group (GssService *self,
485 GOptionGroup *group)
486 {
487 g_return_if_fail (GSS_IS_SERVICE (self));
488 g_return_if_fail (group != NULL);
489
490 GssServicePrivate *priv = gss_service_get_instance_private (self);
491
492 if (priv->option_groups == NULL)
493 priv->option_groups = g_ptr_array_new_with_free_func ((GDestroyNotify) g_option_group_unref);
494
495 g_ptr_array_add (priv->option_groups, g_option_group_ref (group));
496 }
497
498 static gboolean
499 signal_sigint_cb (gpointer user_data)
500 {
501 GssService *self = GSS_SERVICE (user_data);
502 GssServicePrivate *priv = gss_service_get_instance_private (self);
503
504 gss_service_exit (self, NULL, SIGINT);
505
506 /* Remove the signal handler so we can re-raise it later without entering a
507 * loop. */
508 g_clear_pointer (&priv->sigint_source, source_destroy_and_unref);
509 return G_SOURCE_REMOVE;
510 }
511
512 static gboolean
513 signal_sigterm_cb (gpointer user_data)
514 {
515 GssService *self = GSS_SERVICE (user_data);
516 GssServicePrivate *priv = gss_service_get_instance_private (self);
517
518 gss_service_exit (self, NULL, SIGTERM);
519
520 /* Remove the signal handler so we can re-raise it later without entering a
521 * loop. */
522 g_clear_pointer (&priv->sigterm_source, source_destroy_and_unref);
523 return G_SOURCE_REMOVE;
524 }
525
526 static void
527 name_acquired_cb (GDBusConnection *connection,
528 const gchar *name,
529 gpointer user_data)
530 {
531 GssService *self = GSS_SERVICE (user_data);
532
533 /* Notify systemd we’re ready. */
534 sd_notify (0, "READY=1");
535
536 /* Potentially start a timeout to exiting due to inactivity. */
537 gss_service_release (self);
538 }
539
540 static void
541 name_lost_cb (GDBusConnection *connection,
542 const gchar *name,
543 gpointer user_data)
544 {
545 GssService *self = GSS_SERVICE (user_data);
546 g_autoptr(GError) error = NULL;
547
548 gss_service_release (self);
549
550 g_set_error (&error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_NAME_UNAVAILABLE,
551 _("Lost D-Bus name ‘%s’; exiting."), name);
552 gss_service_exit (self, error, 0);
553 }
554
555 static gboolean
556 inactivity_timeout_cb (gpointer user_data)
557 {
558 GssService *self = GSS_SERVICE (user_data);
559 GssServicePrivate *priv = gss_service_get_instance_private (self);
560
561 if (priv->hold_count == 0)
562 {
563 g_autoptr(GError) local_error = NULL;
564 g_set_error_literal (&local_error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_TIMEOUT,
565 _("Inactivity timeout reached; exiting."));
566 gss_service_exit (self, local_error, 0);
567 }
568
569 return G_SOURCE_REMOVE;
570 }
571
572 static void
573 1 cancel_inactivity_timeout (GssService *self)
574 {
575 1 GssServicePrivate *priv = gss_service_get_instance_private (self);
576
577
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_debug ("%s: Cancelling inactivity timeout (was %s)",
578 G_STRFUNC, (priv->inactivity_timeout_source != NULL) ? "set" : "unset");
579
580
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 g_clear_pointer (&priv->inactivity_timeout_source,
581 source_destroy_and_unref);
582 1 }
583
584 static void
585 maybe_schedule_inactivity_timeout (GssService *self)
586 {
587 GssServicePrivate *priv = gss_service_get_instance_private (self);
588
589 g_debug ("%s: Maybe scheduling inactivity timeout, hold_count: %u, inactivity_timeout_ms: %u",
590 G_STRFUNC, priv->hold_count, priv->inactivity_timeout_ms);
591
592 if (priv->hold_count == 0)
593 {
594 cancel_inactivity_timeout (self);
595
596 if (priv->inactivity_timeout_ms != 0)
597 {
598 g_debug ("%s: Scheduling inactivity timeout", G_STRFUNC);
599
600 if ((priv->inactivity_timeout_ms % 1000) == 0)
601 priv->inactivity_timeout_source = g_timeout_source_new_seconds (priv->inactivity_timeout_ms / 1000);
602 else
603 priv->inactivity_timeout_source = g_timeout_source_new (priv->inactivity_timeout_ms);
604 g_source_set_callback (priv->inactivity_timeout_source, inactivity_timeout_cb, self, NULL);
605 g_source_attach (priv->inactivity_timeout_source, priv->context);
606 }
607 }
608 }
609
610 static void
611 result_cb (GObject *obj,
612 GAsyncResult *result,
613 gpointer user_data)
614 {
615 GAsyncResult **result_out = user_data;
616
617 *result_out = g_object_ref (result);
618 }
619
620 static gboolean
621 check_for_early_exit (GssService *self,
622 GError **error)
623 {
624 g_return_val_if_fail (GSS_IS_SERVICE (self), FALSE);
625
626 GssServicePrivate *priv = gss_service_get_instance_private (self);
627
628 if (priv->run_exited)
629 {
630 if (priv->run_error != NULL)
631 g_propagate_error (error, g_steal_pointer (&priv->run_error));
632
633 gss_service_release (self);
634 return FALSE;
635 }
636
637 return TRUE;
638 }
639
640 static gboolean
641 debug_controller_authorize_cb (GDebugControllerDBus *debug_controller,
642 GDBusMethodInvocation *invocation,
643 gpointer user_data)
644 {
645 GssService *self = GSS_SERVICE (user_data);
646 GssServicePrivate *priv = gss_service_get_instance_private (self);
647
648 /* Implement a security policy for allowing debug output to be enabled at
649 * runtime.
650 *
651 * If #GssService:debug-controller-action-id is set, polkit will be queried
652 * using that action ID, and its response will determine whether debug output
653 * can be enabled.
654 *
655 * Otherwise, if #GssService:bus-type is %G_BUS_TYPE_SESSION, enabling debug
656 * output will be allowed without authorization.
657 *
658 * Otherwise, enabling debug output will be denied.
659 *
660 * If services require more control over the security policy, #GssService will
661 * have to be extended to allow the service to provide their own
662 * #GDebugController, which they have hooked up to an appropriate security
663 * policy.
664 */
665 if (priv->debug_controller_action_id != NULL)
666 {
667 g_autoptr(PolkitAuthority) authority = NULL;
668 g_autoptr(PolkitSubject) subject = NULL;
669 g_autoptr(PolkitAuthorizationResult) auth_result = NULL;
670 g_autoptr(GError) local_error = NULL;
671 GDBusMessage *message;
672 GDBusMessageFlags message_flags;
673 PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
674
675 message = g_dbus_method_invocation_get_message (invocation);
676 message_flags = g_dbus_message_get_flags (message);
677
678 authority = polkit_authority_get_sync (NULL, &local_error);
679 if (authority == NULL)
680 {
681 g_warning ("Failed to get polkit authority: %s", local_error->message);
682 return FALSE;
683 }
684
685 if (message_flags & G_DBUS_MESSAGE_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION)
686 flags |= POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
687
688 subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (invocation));
689
690 auth_result = polkit_authority_check_authorization_sync (authority,
691 subject,
692 priv->debug_controller_action_id,
693 NULL,
694 flags,
695 NULL,
696 &local_error);
697 if (auth_result == NULL)
698 {
699 g_warning ("Failed to get check polkit authorization: %s", local_error->message);
700 return FALSE;
701 }
702
703 return polkit_authorization_result_get_is_authorized (auth_result);
704 }
705 else if (priv->bus_type == G_BUS_TYPE_SESSION)
706 {
707 /* All peers on the session bus are at the same privilege level anyway. */
708 return TRUE;
709 }
710 else
711 {
712 /* Peers on the system bus (or other buses) may have different privilege
713 * levels, so can’t be trusted to unconditionally enable debug mode on
714 * services. */
715 return FALSE;
716 }
717 }
718
719 /**
720 * gss_service_run:
721 * @self: a #GssService
722 * @argc: number of arguments in @argv
723 * @argv: (array length=argc): argument array
724 * @error: return location for a #GError
725 *
726 * Run the service, and return when the process should exit. If it should exit
727 * with an error status, @error is set; otherwise it should exit with exit code
728 * zero (success).
729 *
730 * This handles UNIX signals and command line parsing. If you wish to schedule
731 * some work to happen asynchronously while gss_service_run() is running in your
732 * `main()` function, use g_idle_add(). This function, like the rest of the
733 * library, is not thread-safe.
734 *
735 * Since: 0.1.0
736 */
737 void
738 gss_service_run (GssService *self,
739 int argc,
740 char **argv,
741 GError **error)
742 {
743 g_return_if_fail (GSS_IS_SERVICE (self));
744 g_return_if_fail (argc > 0);
745 g_return_if_fail (argv != NULL);
746
747 GssServicePrivate *priv = gss_service_get_instance_private (self);
748 GssServiceClass *service_class = GSS_SERVICE_GET_CLASS (self);
749
750 /* Command line parameters. */
751 g_autofree gchar *bus_address = NULL;
752 gint64 inactivity_timeout_ms = priv->inactivity_timeout_ms;
753
754 const GOptionEntry entries[] =
755 {
756 { "bus-address", 'a', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &bus_address,
757 N_("Address of the D-Bus daemon to connect to and own a name on"),
758 N_("ADDRESS") },
759 { "inactivity-timeout", 't', G_OPTION_FLAG_NONE, G_OPTION_ARG_INT64, &inactivity_timeout_ms,
760 N_("Inactivity timeout to wait for before exiting (in milliseconds)"),
761 N_("MS") },
762 { NULL, },
763 };
764
765 /* Localisation */
766 setlocale (LC_ALL, "");
767 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
768 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
769 textdomain (GETTEXT_PACKAGE);
770
771 if (!priv->allow_root)
772 {
773 /* Ensure we are not running as root — we don’t need those privileges. */
774 if (getuid () == 0 || geteuid () == 0)
775 {
776 g_set_error_literal (error, GSS_SERVICE_ERROR,
777 GSS_SERVICE_ERROR_INVALID_ENVIRONMENT,
778 _("This daemon must not be run as root."));
779 return;
780 }
781 }
782
783 gss_service_hold (self);
784
785 /* Set up signal handlers. */
786 priv->sigint_source = g_unix_signal_source_new (SIGINT);
787 g_source_set_callback (priv->sigint_source, signal_sigint_cb, self, NULL);
788 g_source_attach (priv->sigint_source, priv->context);
789 priv->sigterm_source = g_unix_signal_source_new (SIGTERM);
790 g_source_set_callback (priv->sigterm_source, signal_sigterm_cb, self, NULL);
791 g_source_attach (priv->sigterm_source, priv->context);
792
793 /* Handle command line parameters. */
794 g_autoptr(GOptionContext) context = g_option_context_new (priv->parameter_string);
795 g_option_context_set_summary (context, priv->summary);
796 g_option_context_add_main_entries (context, entries,
797 priv->translation_domain);
798
799 if (service_class->get_main_option_entries != NULL)
800 {
801 GOptionGroup *main_group;
802 g_autofree GOptionEntry *main_entries = NULL;
803
804 main_group = g_option_context_get_main_group (context);
805 main_entries = service_class->get_main_option_entries (self);
806 g_option_group_add_entries (main_group, main_entries);
807 }
808
809 for (guint i = 0; priv->option_groups != NULL && i < priv->option_groups->len; i++)
810 g_option_context_add_group (context, priv->option_groups->pdata[i]);
811
812 if (priv->option_groups != NULL)
813 {
814 g_ptr_array_set_free_func (priv->option_groups, NULL);
815 g_ptr_array_set_size (priv->option_groups, 0);
816 }
817
818 g_autoptr(GError) child_error = NULL;
819
820 if (!g_option_context_parse (context, &argc, &argv, &child_error))
821 {
822 g_set_error (error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_INVALID_OPTIONS,
823 _("Option parsing failed: %s"), child_error->message);
824 gss_service_release (self);
825 return;
826 }
827
828 /* Sort out the inactivity timeout. Zero is the default, so ignore that so
829 * that subclasses can set their own defaults at construction time. */
830 if (inactivity_timeout_ms < 0 || inactivity_timeout_ms > G_MAXUINT)
831 {
832 g_autofree gchar *inactivity_timeout_ms_str = NULL;
833 inactivity_timeout_ms_str = g_strdup_printf ("%" G_GINT64_FORMAT, inactivity_timeout_ms);
834
835 g_set_error (error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_INVALID_OPTIONS,
836 _("Invalid inactivity timeout %sms."),
837 inactivity_timeout_ms_str);
838 gss_service_release (self);
839 return;
840 }
841 else if (inactivity_timeout_ms >= 0)
842 {
843 gss_service_set_inactivity_timeout (self, inactivity_timeout_ms);
844 }
845
846 /* Connect to the bus. */
847 if (bus_address == NULL)
848 {
849 bus_address = g_dbus_address_get_for_bus_sync (priv->bus_type,
850 priv->cancellable,
851 &child_error);
852 }
853
854 if (child_error != NULL)
855 {
856 g_set_error (error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_NAME_UNAVAILABLE,
857 _("D-Bus unavailable: %s"), child_error->message);
858 gss_service_release (self);
859 return;
860 }
861
862 g_autoptr(GAsyncResult) connection_result = NULL;
863 g_dbus_connection_new_for_address (bus_address,
864 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
865 G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
866 NULL /* observer */,
867 priv->cancellable,
868 result_cb,
869 &connection_result);
870
871 /* Run the main loop until we get a connection or exit. */
872 while (connection_result == NULL)
873 g_main_context_iteration (NULL, TRUE);
874
875 priv->connection = g_dbus_connection_new_for_address_finish (connection_result,
876 &child_error);
877
878 if (priv->connection == NULL)
879 {
880 g_set_error (error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_NAME_UNAVAILABLE,
881 _("D-Bus bus ‘%s’ unavailable: %s"),
882 bus_address, child_error->message);
883 gss_service_release (self);
884 return;
885 }
886
887 /* Set up the debug controller. */
888 priv->debug_controller = G_DEBUG_CONTROLLER (g_debug_controller_dbus_new (priv->connection, NULL, &child_error));
889 if (priv->debug_controller == NULL)
890 {
891 g_set_error (error, GSS_SERVICE_ERROR, GSS_SERVICE_ERROR_NAME_UNAVAILABLE,
892 _("Could not register debug controller on bus: %s"),
893 child_error->message);
894 gss_service_release (self);
895 return;
896 }
897
898 priv->debug_controller_authorize_id = g_signal_connect (priv->debug_controller,
899 "authorize",
900 G_CALLBACK (debug_controller_authorize_cb),
901 self);
902
903 /* Start up. */
904 g_autoptr(GAsyncResult) startup_result = NULL;
905 g_assert (service_class->startup_async != NULL &&
906 service_class->startup_finish != NULL);
907 service_class->startup_async (self, priv->cancellable, result_cb,
908 &startup_result);
909
910 while (startup_result == NULL)
911 g_main_context_iteration (NULL, TRUE);
912
913 service_class->startup_finish (self, startup_result, &child_error);
914
915 /* If the service exited early, propagate that error rather than the one from
916 * startup_finish() which is likely just G_IO_ERROR_CANCELLED
917 */
918 if (!check_for_early_exit (self, error))
919 return;
920
921 if (child_error != NULL)
922 {
923 g_propagate_error (error, g_steal_pointer (&child_error));
924 gss_service_release (self);
925 return;
926 }
927
928 /* Grab a well-known name. */
929 g_auto (BusNameId) bus_name_id =
930 g_bus_own_name_on_connection (priv->connection,
931 priv->service_id,
932 G_BUS_NAME_OWNER_FLAGS_NONE,
933 name_acquired_cb,
934 name_lost_cb,
935 self, NULL);
936
937 /* Run the main loop until stopped from a callback with gss_service_exit(). */
938 while (priv->run_error == NULL && !priv->run_exited)
939 g_main_context_iteration (NULL, TRUE);
940
941 gss_service_hold (self);
942
943 /* Notify systemd we’re shutting down. */
944 sd_notify (0, "STOPPING=1");
945
946 /* Debug. */
947 g_debug ("Shutting down: cancellable: %s, run_error: %s, run_exited: %s, "
948 "run_exit_signal: %d",
949 g_cancellable_is_cancelled (priv->cancellable) ? "cancelled" : "no",
950 (priv->run_error != NULL) ? "set" : "unset",
951 priv->run_exited ? "yes" : "no",
952 priv->run_exit_signal);
953
954 /* Shut down. */
955 g_assert (service_class->shutdown != NULL);
956 service_class->shutdown (self);
957
958 gss_service_release (self);
959
960 if (priv->run_error != NULL)
961 {
962 g_propagate_error (error, priv->run_error);
963 priv->run_error = NULL;
964 return;
965 }
966 }
967
968 /**
969 * gss_service_exit:
970 * @self: a #GssService
971 * @error: (nullable): error which caused the process to exit, or %NULL for a
972 * successful exit
973 * @signum: signal which caused the process to exit, or 0 for a successful exit
974 *
975 * Cause the service to exit from gss_service_run(). If @error is non-%NULL, the
976 * service will exit with the given error; otherwise it will exit successfully.
977 * If this is called multiple times, all errors except the first will be
978 * ignored, so it may be safely used for error handling in shutdown code.
979 *
980 * It is a programmer error to call this before startup_async() has been called
981 * on your service.
982 *
983 * Since: 0.1.0
984 */
985 void
986 gss_service_exit (GssService *self,
987 const GError *error,
988 int signum)
989 {
990 g_autoptr(GError) allocated_error = NULL;
991
992 g_return_if_fail (GSS_IS_SERVICE (self));
993 g_return_if_fail (error == NULL || signum == 0);
994
995 GssServicePrivate *priv = gss_service_get_instance_private (self);
996
997 if (signum != 0)
998 {
999 g_assert (error == NULL);
1000
1001 g_set_error (&allocated_error, GSS_SERVICE_ERROR,
1002 GSS_SERVICE_ERROR_SIGNALLED,
1003 _("Signalled with signal %d"), signum);
1004 error = allocated_error;
1005 }
1006
1007 if (priv->run_error == NULL)
1008 {
1009 if (error != NULL)
1010 g_debug ("Exiting with error: %s", error->message);
1011 else
1012 g_debug ("Exiting with no error");
1013
1014 if (error != NULL && priv->run_error == NULL)
1015 priv->run_error = g_error_copy (error);
1016 }
1017 else if (error != NULL)
1018 {
1019 g_debug ("Ignoring additional error: %s", error->message);
1020 }
1021
1022 priv->run_exited = TRUE;
1023 priv->run_exit_signal = signum;
1024 g_cancellable_cancel (priv->cancellable);
1025 }
1026
1027 /**
1028 * gss_service_get_dbus_connection:
1029 * @self: a #GssService
1030 *
1031 * Get the #GDBusConnection used to export the service’s well-known name, as
1032 * specified in #GssService:bus-type.
1033 *
1034 * Returns: (transfer none): D-Bus connection
1035 * Since: 0.1.0
1036 */
1037 GDBusConnection *
1038 gss_service_get_dbus_connection (GssService *self)
1039 {
1040 g_return_val_if_fail (GSS_IS_SERVICE (self), NULL);
1041
1042 GssServicePrivate *priv = gss_service_get_instance_private (self);
1043 return priv->connection;
1044 }
1045
1046 /**
1047 * gss_service_get_exit_signal:
1048 * @self: a #GssService
1049 *
1050 * Get the number of the signal which caused the #GssService to exit.
1051 *
1052 * Returns: exit signal number, or 0 if unset
1053 * Since: 0.1.0
1054 */
1055 int
1056 gss_service_get_exit_signal (GssService *self)
1057 {
1058 g_return_val_if_fail (GSS_IS_SERVICE (self), 0);
1059
1060 GssServicePrivate *priv = gss_service_get_instance_private (self);
1061 return priv->run_exit_signal;
1062 }
1063
1064 /**
1065 * gss_service_get_inactivity_timeout:
1066 * @self: a #GssService
1067 *
1068 * Get the value of #GssService:inactivity-timeout.
1069 *
1070 * Returns: inactivity timeout, in milliseconds, or zero if inactivity is ignored
1071 * Since: 0.1.0
1072 */
1073 guint
1074 gss_service_get_inactivity_timeout (GssService *self)
1075 {
1076 g_return_val_if_fail (GSS_IS_SERVICE (self), 0);
1077
1078 GssServicePrivate *priv = gss_service_get_instance_private (self);
1079 return priv->inactivity_timeout_ms;
1080 }
1081
1082 /**
1083 * gss_service_set_inactivity_timeout:
1084 * @self: a #GssService
1085 * @timeout_ms: inactivity timeout (in ms), or zero for no timeout
1086 *
1087 * Set the value of #GssService:inactivity-timeout.
1088 *
1089 * Since: 0.1.0
1090 */
1091 void
1092 gss_service_set_inactivity_timeout (GssService *self,
1093 guint timeout_ms)
1094 {
1095 g_return_if_fail (GSS_IS_SERVICE (self));
1096
1097 GssServicePrivate *priv = gss_service_get_instance_private (self);
1098
1099 if (priv->inactivity_timeout_ms == timeout_ms)
1100 return;
1101
1102 priv->inactivity_timeout_ms = timeout_ms;
1103 g_object_notify (G_OBJECT (self), "inactivity-timeout");
1104
1105 maybe_schedule_inactivity_timeout (self);
1106 }
1107
1108 /**
1109 * gss_service_get_debug_controller:
1110 * @self: a #GssService
1111 *
1112 * Get the value of #GssService:debug-controller.
1113 *
1114 * Returns: (transfer none) (nullable): the service’s debug controller, or
1115 * %NULL if none is available
1116 * Since: 0.2.0
1117 */
1118 GDebugController *
1119 gss_service_get_debug_controller (GssService *self)
1120 {
1121 g_return_val_if_fail (GSS_IS_SERVICE (self), NULL);
1122
1123 GssServicePrivate *priv = gss_service_get_instance_private (self);
1124 return priv->debug_controller;
1125 }
1126
1127 /**
1128 * gss_service_hold:
1129 * @self: a #GssService
1130 *
1131 * Increase the hold count of the service, and hence prevent it from
1132 * automatically exiting after the #GssService:inactivity-timeout period expires
1133 * with no activity.
1134 *
1135 * Call gss_service_release() to decrement the hold count. Calls to these two
1136 * methods must be paired; it is a programmer error not to.
1137 *
1138 * Since: 0.1.0
1139 */
1140 void
1141 gss_service_hold (GssService *self)
1142 {
1143 GssServicePrivate *priv = gss_service_get_instance_private (self);
1144
1145 g_return_if_fail (GSS_IS_SERVICE (self));
1146 g_return_if_fail (priv->hold_count < G_MAXUINT);
1147
1148 priv->hold_count++;
1149 cancel_inactivity_timeout (self);
1150 }
1151
1152 /**
1153 * gss_service_release:
1154 * @self: a #GssService
1155 *
1156 * Decrease the hold count of the service, and hence potentially (if the hold
1157 * count reaches zero) allow it to automatically exit after the
1158 * #GssService:inactivity-timeout period expires with no activity.
1159 *
1160 * Call gss_service_hold() to increment the hold count. Calls to these two
1161 * methods must be paired; it is a programmer error not to.
1162 *
1163 * Since: 0.1.0
1164 */
1165 void
1166 gss_service_release (GssService *self)
1167 {
1168 GssServicePrivate *priv = gss_service_get_instance_private (self);
1169
1170 g_return_if_fail (GSS_IS_SERVICE (self));
1171 g_return_if_fail (priv->hold_count > 0);
1172
1173 priv->hold_count--;
1174 maybe_schedule_inactivity_timeout (self);
1175 }
1176