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