1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* bus - The Input Bus
4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2011-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
6 * Copyright (C) 2008-2019 Red Hat, Inc.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23 #include "server.h"
24
25 #include <errno.h>
26 #include <glib/gstdio.h>
27 #include <gio/gio.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <string.h>
32
33 #include "dbusimpl.h"
34 #include "ibusimpl.h"
35 #include "global.h"
36
37
38 static GDBusServer *server = NULL;
39 static GMainLoop *mainloop = NULL;
40 static BusDBusImpl *dbus = NULL;
41 static BusIBusImpl *ibus = NULL;
42 static gchar *address = NULL;
43 static gboolean _restart = FALSE;
44
45 static void
_restart_server(void)46 _restart_server (void)
47 {
48 gchar *exe;
49 gint fd;
50 ssize_t r;
51 int MAXSIZE = 0xFFF;
52 char proclnk[MAXSIZE];
53 char filename[MAXSIZE];
54
55 exe = g_file_read_link ("/proc/self/exe", NULL);
56
57 if (exe == NULL)
58 exe = g_strdup (BINDIR "/ibus-daemon");
59
60 /* close all fds except stdin, stdout, stderr */
61 for (fd = 3; fd <= sysconf (_SC_OPEN_MAX); fd ++) {
62 errno = 0;
63 /* only close valid fds */
64 if (fcntl (fd, F_GETFD) != -1 || errno != EBADF) {
65 g_sprintf (proclnk, "/proc/self/fd/%d", fd);
66 r = readlink (proclnk, filename, MAXSIZE);
67 if (r < 0) {
68 continue;
69 }
70 filename[r] = '\0';
71
72 /* Do not close 'anon_inode:inotify' fds, that may crash in glib */
73 if (g_strcmp0 (filename, "anon_inode:inotify") != 0) {
74 close (fd);
75 }
76 }
77 }
78
79 _restart = FALSE;
80 execv (exe, g_argv);
81
82 /* If the server binary is replaced while the server is running,
83 * "readlink /proc/[pid]/exe" might return a path with " (deleted)"
84 * suffix. */
85 const gchar suffix[] = " (deleted)";
86 if (g_str_has_suffix (exe, suffix)) {
87 exe [strlen (exe) - sizeof (suffix) + 1] = '\0';
88 execv (exe, g_argv);
89 }
90 g_warning ("execv %s failed!", g_argv[0]);
91 g_free (exe);
92 exit (-1);
93 }
94
95 /**
96 * bus_allow_mechanism_cb:
97 * @observer: A #GDBusAuthObserver.
98 * @mechanism: The name of the mechanism.
99 * @user_data: always %NULL.
100 *
101 * Check if @mechanism can be used to authenticate the other peer.
102 * Returns: %TRUE if the peer's mechanism is allowed.
103 */
104 static gboolean
bus_allow_mechanism_cb(GDBusAuthObserver * observer,const gchar * mechanism,G_GNUC_UNUSED gpointer user_data)105 bus_allow_mechanism_cb (GDBusAuthObserver *observer,
106 const gchar *mechanism,
107 G_GNUC_UNUSED gpointer user_data)
108 {
109 if (g_strcmp0 (mechanism, "EXTERNAL") == 0)
110 return TRUE;
111 return FALSE;
112 }
113
114 /**
115 * bus_authorize_authenticated_peer_cb:
116 * @observer: A #GDBusAuthObserver.
117 * @stream: A #GIOStream.
118 * @credentials: A #GCredentials.
119 * @user_data: always %NULL.
120 *
121 * Check if a peer who has already authenticated should be authorized.
122 * Returns: %TRUE if the peer's credential is authorized.
123 */
124 static gboolean
bus_authorize_authenticated_peer_cb(GDBusAuthObserver * observer,GIOStream * stream,GCredentials * credentials,G_GNUC_UNUSED gpointer user_data)125 bus_authorize_authenticated_peer_cb (GDBusAuthObserver *observer,
126 GIOStream *stream,
127 GCredentials *credentials,
128 G_GNUC_UNUSED gpointer user_data)
129 {
130 gboolean authorized = FALSE;
131 if (credentials) {
132 GCredentials *own_credentials = g_credentials_new ();
133 if (g_credentials_is_same_user (credentials, own_credentials, NULL))
134 authorized = TRUE;
135 g_object_unref (own_credentials);
136 }
137 return authorized;
138 }
139
140 /**
141 * bus_new_connection_cb:
142 * @observer: A #GDBusAuthObserver.
143 * @dbus_connection: A #GDBusconnection.
144 * @user_data: always %NULL.
145 *
146 * Handle incoming connections.
147 * Returns: %TRUE when the function can handle the connection.
148 */
149 static gboolean
bus_new_connection_cb(GDBusServer * server,GDBusConnection * dbus_connection,G_GNUC_UNUSED gpointer user_data)150 bus_new_connection_cb (GDBusServer *server,
151 GDBusConnection *dbus_connection,
152 G_GNUC_UNUSED gpointer user_data)
153 {
154 BusConnection *connection = bus_connection_new (dbus_connection);
155 bus_dbus_impl_new_connection (dbus, connection);
156
157 if (g_object_is_floating (connection)) {
158 /* bus_dbus_impl_new_connection couldn't handle the connection. just delete the connection and return TRUE
159 * (so that other connection handler will not handle the deleted connection.) */
160 ibus_object_destroy ((IBusObject *)connection);
161 g_object_unref (connection);
162 }
163 return TRUE;
164 }
165
166 static void
_server_connect_start_portal_cb(GObject * source_object,GAsyncResult * res,G_GNUC_UNUSED gpointer user_data)167 _server_connect_start_portal_cb (GObject *source_object,
168 GAsyncResult *res,
169 G_GNUC_UNUSED gpointer user_data)
170 {
171 GVariant *result;
172 GError *error = NULL;
173
174 result = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
175 res,
176 &error);
177 if (result != NULL) {
178 g_variant_unref (result);
179 } else {
180 g_print ("portal is not running: %s\n", error->message);
181 g_error_free (error);
182 }
183 }
184
185 static void
bus_acquired_handler(GDBusConnection * connection,const gchar * name,G_GNUC_UNUSED gpointer user_data)186 bus_acquired_handler (GDBusConnection *connection,
187 const gchar *name,
188 G_GNUC_UNUSED gpointer user_data)
189 {
190 g_dbus_connection_call (connection,
191 IBUS_SERVICE_PORTAL,
192 IBUS_PATH_IBUS,
193 "org.freedesktop.DBus.Peer",
194 "Ping",
195 g_variant_new ("()"),
196 G_VARIANT_TYPE ("()"),
197 G_DBUS_CALL_FLAGS_NONE,
198 -1,
199 NULL /* cancellable */,
200 (GAsyncReadyCallback)
201 _server_connect_start_portal_cb,
202 NULL);
203 }
204
205 static gchar *
_bus_extract_address(void)206 _bus_extract_address (void)
207 {
208 gchar *socket_address = g_strdup (g_address);
209 gchar *p;
210
211 #define IF_REPLACE_VARIABLE_WITH_FUNC(variable, func, format) \
212 if ((p = g_strstr_len (socket_address, -1, (variable)))) { \
213 gchar *sub1 = g_strndup (socket_address, p - socket_address); \
214 gchar *sub2 = g_strdup (p + strlen (variable)); \
215 gchar *tmp = g_strdup_printf ("%s" format "%s", \
216 sub1, (func) (), sub2); \
217 g_free (sub1); \
218 g_free (sub2); \
219 g_free (socket_address); \
220 socket_address = tmp; \
221 }
222
223 IF_REPLACE_VARIABLE_WITH_FUNC ("$XDG_RUNTIME_DIR",
224 g_get_user_runtime_dir,
225 "%s")
226 else
227 IF_REPLACE_VARIABLE_WITH_FUNC ("$XDG_CACHE_HOME",
228 g_get_user_cache_dir,
229 "%s")
230 else
231 IF_REPLACE_VARIABLE_WITH_FUNC ("$UID", getuid, "%d")
232
233 #undef IF_REPLACE_VARIABLE_WITH_FUNC
234
235 return socket_address;
236 }
237
238 void
bus_server_init(void)239 bus_server_init (void)
240 {
241 #define IBUS_UNIX_TMPDIR "unix:tmpdir="
242 #define IBUS_UNIX_PATH "unix:path="
243 #define IBUS_UNIX_ABSTRACT "unix:abstract="
244 #define IBUS_UNIX_DIR "unix:dir="
245
246 gchar *socket_address;
247 GDBusServerFlags flags = G_DBUS_SERVER_FLAGS_NONE;
248 gchar *guid;
249 GDBusAuthObserver *observer;
250 GError *error = NULL;
251 gchar *unix_dir = NULL;
252
253 dbus = bus_dbus_impl_get_default ();
254 ibus = bus_ibus_impl_get_default ();
255 bus_dbus_impl_register_object (dbus, (IBusService *)ibus);
256
257 /* init server */
258 socket_address = _bus_extract_address ();
259
260 #define IF_GET_UNIX_DIR(prefix) \
261 if (g_str_has_prefix (socket_address, (prefix))) { \
262 unix_dir = g_strdup (socket_address + strlen (prefix)); \
263 }
264
265 IF_GET_UNIX_DIR (IBUS_UNIX_TMPDIR)
266 else
267 IF_GET_UNIX_DIR (IBUS_UNIX_PATH)
268 else
269 IF_GET_UNIX_DIR (IBUS_UNIX_ABSTRACT)
270 else
271 IF_GET_UNIX_DIR (IBUS_UNIX_DIR)
272 else {
273 g_error ("Your socket address \"%s\" does not correspond with "
274 "one of the following formats; "
275 IBUS_UNIX_TMPDIR "DIR, " IBUS_UNIX_PATH "FILE, "
276 IBUS_UNIX_ABSTRACT "FILE, " IBUS_UNIX_DIR "DIR.",
277 socket_address);
278 }
279 if (!g_file_test (unix_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
280 /* Require mkdir for BSD system.
281 * The mode 0700 can eliminate malicious users change the mode.
282 * `chmod` runs for the last directory only not to change the modes
283 * of the parent directories. E.g. "/tmp/ibus".
284 */
285 if (g_mkdir_with_parents (unix_dir, 0700) != 0) {
286 g_error ("mkdir is failed in: %s: %s",
287 unix_dir, g_strerror (errno));
288 }
289 }
290 g_free (unix_dir);
291 guid = g_dbus_generate_guid ();
292 observer = g_dbus_auth_observer_new ();
293 server = g_dbus_server_new_sync (
294 /* the place where the socket file lives, e.g. /tmp,
295 * abstract namespace, etc. */
296 socket_address,
297 flags, guid,
298 observer,
299 NULL /* cancellable */,
300 &error);
301 if (server == NULL) {
302 g_error ("g_dbus_server_new_sync() is failed with address %s "
303 "and guid %s: %s",
304 socket_address, guid, error->message);
305 }
306 g_free (socket_address);
307 g_free (guid);
308
309 g_signal_connect (observer, "allow-mechanism",
310 G_CALLBACK (bus_allow_mechanism_cb), NULL);
311 g_signal_connect (observer, "authorize-authenticated-peer",
312 G_CALLBACK (bus_authorize_authenticated_peer_cb), NULL);
313 g_object_unref (observer);
314 g_signal_connect (server, "new-connection",
315 G_CALLBACK (bus_new_connection_cb), NULL);
316
317 g_dbus_server_start (server);
318
319 address = g_strdup_printf ("%s,guid=%s",
320 g_dbus_server_get_client_address (server),
321 g_dbus_server_get_guid (server));
322
323 /* write address to file */
324 ibus_write_address (address);
325
326 /* own a session bus name so that third parties can easily track our life-cycle */
327 g_bus_own_name (G_BUS_TYPE_SESSION, IBUS_SERVICE_IBUS,
328 G_BUS_NAME_OWNER_FLAGS_NONE,
329 bus_acquired_handler,
330 NULL, NULL, NULL, NULL);
331
332 #undef IF_GET_UNIX_DIR
333 #undef IBUS_UNIX_TMPDIR
334 #undef IBUS_UNIX_PATH
335 #undef IBUS_UNIX_ABSTRACT
336 #undef IBUS_UNIX_DIR
337 }
338
339 const gchar *
bus_server_get_address(void)340 bus_server_get_address (void)
341 {
342 return address;
343 }
344
345 void
bus_server_run(void)346 bus_server_run (void)
347 {
348 g_return_if_fail (server);
349
350 /* create and run main loop */
351 mainloop = g_main_loop_new (NULL, FALSE);
352 g_main_loop_run (mainloop);
353
354 /* bus_server_quit is called. stop server */
355 g_dbus_server_stop (server);
356
357 ibus_object_destroy ((IBusObject *)dbus);
358 ibus_object_destroy ((IBusObject *)ibus);
359
360 /* release resources */
361 g_object_unref (server);
362 g_main_loop_unref (mainloop);
363 mainloop = NULL;
364 g_free (address);
365 address = NULL;
366
367 /* When _ibus_exit() is called, bus_ibus_impl_destroy() needs
368 * to be called so that waitpid() prevents the processes from
369 * becoming the daemons. So we run execv() after
370 * ibus_object_destroy(ibus) is called here. */
371 if (_restart) {
372 _restart_server ();
373
374 /* should not reach here */
375 g_assert_not_reached ();
376 }
377 }
378
379 void
bus_server_quit(gboolean restart)380 bus_server_quit (gboolean restart)
381 {
382 _restart = restart;
383 if (mainloop)
384 g_main_loop_quit (mainloop);
385 }
386