1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* ibus - The Input Bus
4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
6 * Copyright (C) 2008-2018 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
24 #include "ibusshare.h"
25 #include <glib.h>
26 #include <glib/gstdio.h>
27 #include <glib-object.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <ibus.h>
35
36 static gchar *_display = NULL;
37
38 const gchar *
ibus_get_local_machine_id(void)39 ibus_get_local_machine_id (void)
40 {
41 static gchar *machine_id = NULL;
42
43 if (machine_id == NULL) {
44 GError *error = NULL;
45 if (!g_file_get_contents ("/var/lib/dbus/machine-id",
46 &machine_id,
47 NULL,
48 &error) &&
49 !g_file_get_contents ("/etc/machine-id",
50 &machine_id,
51 NULL,
52 NULL)) {
53 g_warning ("Unable to load /var/lib/dbus/machine-id: %s", error->message);
54 machine_id = "machine-id";
55 }
56 else {
57 g_strstrip (machine_id);
58 }
59 if (error != NULL) {
60 g_error_free (error);
61 }
62 }
63
64 return machine_id;
65 }
66
67 void
ibus_set_display(const gchar * display)68 ibus_set_display (const gchar *display)
69 {
70 if (_display != NULL)
71 g_free (_display);
72 _display = g_strdup (display);
73 }
74
75 const gchar *
ibus_get_user_name(void)76 ibus_get_user_name (void)
77 {
78 return g_get_user_name ();
79 }
80
81 glong
ibus_get_daemon_uid(void)82 ibus_get_daemon_uid (void)
83 {
84 return getuid ();
85 }
86
87 const gchar *
ibus_get_session_id(void)88 ibus_get_session_id (void)
89 {
90 return g_getenv("IBUS_SESSION_ID");
91 }
92
93 const gchar *
ibus_get_socket_path(void)94 ibus_get_socket_path (void)
95 {
96 static gchar *path = NULL;
97
98 if (path == NULL) {
99 gchar *hostname = "unix";
100 gchar *display;
101 gchar *displaynumber = "0";
102 /* gchar *screennumber = "0"; */
103 gboolean is_wayland = FALSE;
104 gchar *p;
105
106 path = g_strdup (g_getenv ("IBUS_ADDRESS_FILE"));
107 if (path != NULL) {
108 return path;
109 }
110
111 if (_display == NULL) {
112 display = g_strdup (g_getenv ("WAYLAND_DISPLAY"));
113 if (display)
114 is_wayland = TRUE;
115 else
116 display = g_strdup (g_getenv ("DISPLAY"));
117 }
118 else {
119 display = g_strdup (_display);
120 }
121
122 if (is_wayland) {
123 displaynumber = display;
124 } else if (display) {
125 p = display;
126 hostname = display;
127 for (; *p != ':' && *p != '\0'; p++);
128
129 if (*p == ':') {
130 *p = '\0';
131 p++;
132 displaynumber = p;
133 }
134
135 for (; *p != '.' && *p != '\0'; p++);
136
137 if (*p == '.') {
138 *p = '\0';
139 p++;
140 /* Do not use screennumber
141 screennumber = p; */
142 }
143 }
144
145 if (hostname[0] == '\0')
146 hostname = "unix";
147
148 p = g_strdup_printf ("%s-%s-%s",
149 ibus_get_local_machine_id (),
150 hostname,
151 displaynumber);
152 /* Qt5 IBus module has a hard-coded path and we cannot change this
153 * for the back compatibility.
154 * XDG_RUNTIME_DIR is not useful because it's generated by
155 * login but not `su` command and ibus-daemon can be run with `su`
156 * and we may change the path to XDG_CACHE_HOME in the future.
157 */
158 path = g_build_filename (g_get_user_config_dir (),
159 "ibus",
160 "bus",
161 p,
162 NULL);
163 g_free (p);
164 g_free (display);
165 }
166 return path;
167 }
168
169 gint
ibus_get_timeout(void)170 ibus_get_timeout (void)
171 {
172 /* 16000 ms is the default timeout on the ibus-daemon side
173 * (15 sec) plus 1. */
174 static const gint default_timeout = 16000;
175
176 static gint64 timeout = -2;
177 if (timeout == -2) {
178 const gchar *timeout_str = g_getenv ("IBUS_TIMEOUT");
179 if (timeout_str == NULL) {
180 timeout = default_timeout;
181 } else {
182 timeout = g_ascii_strtoll(timeout_str, NULL, 10);
183 if (timeout < -1 || timeout == 0 || timeout > G_MAXINT) {
184 timeout = default_timeout;
185 }
186 }
187 }
188 return timeout;
189 }
190
191 const gchar *
ibus_get_address(void)192 ibus_get_address (void)
193 {
194 static gchar *address = NULL;
195 pid_t pid = -1;
196 static gchar buffer[1024];
197 FILE *pf;
198
199 /* free address */
200 if (address != NULL) {
201 g_free (address);
202 address = NULL;
203 }
204
205 /* get address from env variable */
206 address = g_strdup (g_getenv ("IBUS_ADDRESS"));
207 if (address) {
208 return address;
209 }
210
211 /* read address from ~/.config/ibus/bus/soketfile */
212 pf = fopen (ibus_get_socket_path (), "r");
213 if (pf == NULL) {
214 return NULL;
215 }
216
217 while (!feof (pf)) {
218 gchar *p = buffer;
219 if (fgets (buffer, sizeof (buffer), pf) == NULL)
220 break;
221
222 /* skip comment line */
223 if (p[0] == '#')
224 continue;
225 /* parse IBUS_ADDRESS */
226 if (strncmp (p, "IBUS_ADDRESS=", sizeof ("IBUS_ADDRESS=") - 1) == 0) {
227 address = p + sizeof ("IBUS_ADDRESS=") - 1;
228 for (p = (gchar *)address; *p != '\n' && *p != '\0'; p++);
229 if (*p == '\n')
230 *p = '\0';
231 address = g_strdup (address);
232 continue;
233 }
234
235 /* parse IBUS_DAEMON_PID */
236 if (strncmp (p, "IBUS_DAEMON_PID=", sizeof ("IBUS_DAEMON_PID=") - 1) == 0) {
237 pid = atoi(p + sizeof ("IBUS_DAEMON_PID=") - 1);
238 continue;
239 }
240
241 }
242 fclose (pf);
243
244 if (pid == -1 || kill (pid, 0) != 0) {
245 return NULL;
246 }
247
248 return address;
249 }
250
251 void
ibus_write_address(const gchar * address)252 ibus_write_address (const gchar *address)
253 {
254 FILE *pf;
255 gchar *path;
256 g_return_if_fail (address != NULL);
257
258 path = g_path_get_dirname (ibus_get_socket_path ());
259 g_mkdir_with_parents (path, 0700);
260 g_free (path);
261
262 g_unlink (ibus_get_socket_path ());
263 pf = fopen (ibus_get_socket_path (), "w");
264 g_return_if_fail (pf != NULL);
265
266 fprintf (pf,
267 "# This file is created by ibus-daemon, please do not modify it.\n"
268 "# This file allows processes on the machine to find the\n"
269 "# ibus session bus with the below address.\n"
270 "# If the IBUS_ADDRESS environment variable is set, it will\n"
271 "# be used rather than this file.\n"
272 "IBUS_ADDRESS=%s\n"
273 "IBUS_DAEMON_PID=%ld\n",
274 address, (glong) getpid ());
275 fclose (pf);
276 }
277
278 void
ibus_free_strv(gchar ** strv)279 ibus_free_strv (gchar **strv)
280 {
281 gchar **p;
282
283 if (strv == NULL)
284 return;
285
286 for (p = strv; *p != NULL; p++) {
287 g_free (*p);
288 }
289
290 g_free (strv);
291 }
292
293 void
ibus_init(void)294 ibus_init (void)
295 {
296 #if !GLIB_CHECK_VERSION(2,35,0)
297 g_type_init ();
298 #endif
299 IBUS_ERROR;
300 IBUS_TYPE_TEXT;
301 IBUS_TYPE_ATTRIBUTE;
302 IBUS_TYPE_ATTR_LIST;
303 IBUS_TYPE_LOOKUP_TABLE;
304 IBUS_TYPE_COMPONENT;
305 IBUS_TYPE_ENGINE_DESC;
306 IBUS_TYPE_OBSERVED_PATH;
307 IBUS_TYPE_REGISTRY;
308 IBUS_TYPE_X_EVENT;
309 }
310
311 static GMainLoop *main_loop = NULL;
312
313 void
ibus_main(void)314 ibus_main (void)
315 {
316 main_loop = g_main_loop_new (NULL, FALSE);
317
318 g_main_loop_run (main_loop);
319
320 g_main_loop_unref (main_loop);
321 main_loop = NULL;
322 }
323
324 void
ibus_quit(void)325 ibus_quit (void)
326 {
327 if (main_loop) {
328 g_main_loop_quit (main_loop);
329 }
330 }
331
332 static gboolean ibus_log_handler_is_verbose = FALSE;
333 static guint ibus_log_handler_id = 0;
334
335 static void
ibus_log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)336 ibus_log_handler (const gchar *log_domain,
337 GLogLevelFlags log_level,
338 const gchar *message,
339 gpointer user_data)
340 {
341 // In the quiet mode (i.e. not verbose), we'll ignore DEBUG and
342 // WARNING messages.
343 if (!ibus_log_handler_is_verbose &&
344 ((log_level & G_LOG_LEVEL_DEBUG) ||
345 (log_level & G_LOG_LEVEL_WARNING))) {
346 return;
347 }
348 // Add timing info like "17:34:57.680038" (hour, min, sec, microsecond).
349 struct timeval time_val;
350 gettimeofday (&time_val, NULL);
351 struct tm local_time;
352 localtime_r (&time_val.tv_sec, &local_time);
353 char* new_message =
354 g_strdup_printf ("%02d:%02d:%02d.%6d: %s",
355 local_time.tm_hour,
356 local_time.tm_min,
357 local_time.tm_sec,
358 (int)time_val.tv_usec,
359 message);
360 g_log_default_handler (log_domain, log_level, new_message, user_data);
361 g_free (new_message);
362 }
363
364 void
ibus_set_log_handler(gboolean verbose)365 ibus_set_log_handler (gboolean verbose)
366 {
367 if (ibus_log_handler_id != 0) {
368 g_log_remove_handler (G_LOG_DOMAIN, ibus_log_handler_id);
369 }
370
371 ibus_log_handler_is_verbose = verbose;
372 ibus_log_handler_id = g_log_set_handler (G_LOG_DOMAIN,
373 G_LOG_LEVEL_MASK,
374 ibus_log_handler,
375 NULL);
376 }
377
378 void
ibus_unset_log_handler(void)379 ibus_unset_log_handler (void)
380 {
381 if (ibus_log_handler_id != 0) {
382 g_log_remove_handler (G_LOG_DOMAIN, ibus_log_handler_id);
383 ibus_log_handler_id = 0;
384 }
385 }
386