ibus-daemon: use GFileMonitor instead of polling
[platform/upstream/ibus.git] / bus / main.c
index 13b13cc..791a524 100644 (file)
@@ -1,6 +1,8 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
 /* vim:set et sts=4: */
 /* ibus - The Input Bus
- * Copyright (C) 2008-2009 Huang Peng <shawn.p.huang@gmail.com>
+ * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
+ * Copyright (C) 2008-2010 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Boston, MA 02111-1307, USA.
  */
 #include <config.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <ibus.h>
+#include <locale.h>
 #include <pwd.h>
+#include <signal.h>
 #include <stdlib.h>
-#include <locale.h>
-#include "server.h"
-#include "ibusimpl.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
-gchar **g_argv = NULL;
+#include "global.h"
+#include "ibusimpl.h"
+#include "server.h"
 
 static gboolean daemonize = FALSE;
 static gboolean single = FALSE;
 static gboolean xim = FALSE;
 static gboolean replace = FALSE;
+static gboolean restart = FALSE;
 static gchar *panel = "default";
 static gchar *config = "default";
 static gchar *desktop = "gnome";
-static gchar *address = "";
-gboolean g_rescan = FALSE;
-static gboolean verbose = FALSE;
+
+static void
+show_version_and_quit (void)
+{
+    g_print ("%s - Version %s\n", g_get_application_name (), VERSION);
+    exit (EXIT_SUCCESS);
+}
 
 static const GOptionEntry entries[] =
 {
-    { "daemonize", 'd', 0, G_OPTION_ARG_NONE, &daemonize, "run ibus as background process.", NULL },
-    { "single", 's', 0, G_OPTION_ARG_NONE, &single, "do not execute panel and config module.", NULL },
-    { "xim", 'x', 0, G_OPTION_ARG_NONE, &xim, "execute ibus XIM server.", NULL },
-    { "desktop", 'n', 0, G_OPTION_ARG_STRING, &desktop, "specify the name of desktop session. [default=gnome]", "name" },
-    { "panel", 'p', 0, G_OPTION_ARG_STRING, &panel, "specify the cmdline of panel program.", "cmdline" },
-    { "config", 'c', 0, G_OPTION_ARG_STRING, &config, "specify the cmdline of config program.", "cmdline" },
-    { "address", 'a', 0, G_OPTION_ARG_STRING, &address, "specify the address of ibus daemon.", "address" },
-    { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "if there is an old ibus-daemon is running, it will be replaced.", NULL },
-    { "re-scan", 't', 0, G_OPTION_ARG_NONE, &g_rescan, "force to re-scan components, and re-create registry cache.", NULL },
-    { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "verbose.", NULL },
+    { "version",   'V', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_version_and_quit, "Show the application's version.", NULL },
+    { "daemonize", 'd', 0, G_OPTION_ARG_NONE,   &daemonize, "run ibus as background process.", NULL },
+    { "single",    's', 0, G_OPTION_ARG_NONE,   &single,    "do not execute panel and config module.", NULL },
+    { "xim",       'x', 0, G_OPTION_ARG_NONE,   &xim,       "execute ibus XIM server.", NULL },
+    { "desktop",   'n', 0, G_OPTION_ARG_STRING, &desktop,   "specify the name of desktop session. [default=gnome]", "name" },
+    { "panel",     'p', 0, G_OPTION_ARG_STRING, &panel,     "specify the cmdline of panel program. pass 'disable' not to start a panel program.", "cmdline" },
+    { "config",    'c', 0, G_OPTION_ARG_STRING, &config,    "specify the cmdline of config program. pass 'disable' not to start a config program.", "cmdline" },
+    { "address",   'a', 0, G_OPTION_ARG_STRING, &g_address,   "specify the address of ibus daemon.", "address" },
+    { "replace",   'r', 0, G_OPTION_ARG_NONE,   &replace,   "if there is an old ibus-daemon is running, it will be replaced.", NULL },
+    { "cache",     't', 0, G_OPTION_ARG_STRING, &g_cache,   "specify the cache mode. [auto/refresh/none]", NULL },
+    { "timeout",   'o', 0, G_OPTION_ARG_INT,    &g_gdbus_timeout, "gdbus reply timeout in milliseconds. pass -1 to use the default timeout of gdbus.", "timeout [default is 5000]" },
+    { "mem-profile", 'm', 0, G_OPTION_ARG_NONE,   &g_mempro,   "enable memory profile, send SIGUSR2 to print out the memory profile.", NULL },
+    { "restart",     'R', 0, G_OPTION_ARG_NONE,   &restart,    "restart panel and config processes when they die.", NULL },
+    { "verbose",   'v', 0, G_OPTION_ARG_NONE,   &g_verbose,   "verbose.", NULL },
     { NULL },
 };
 
+/**
+ * execute_cmdline:
+ * @cmdline: An absolute path of the executable and its parameters, e.g.  "/usr/lib/ibus/ibus-x11 --kill-daemon".
+ * @returns: TRUE if both parsing cmdline and executing the command succeed.
+ *
+ * Execute cmdline. Child process's stdin, stdout, and stderr are attached to /dev/null.
+ * You don't have to handle SIGCHLD from the child process since glib will do.
+ */
 static gboolean
 execute_cmdline (const gchar *cmdline)
 {
     g_assert (cmdline);
 
-    gint argc;
-    gchar **argv;
-    gboolean retval;
-    GError *error;
-
-    error = NULL;
+    gint argc = 0;
+    gchar **argv = NULL;
+    GError *error = NULL;
     if (!g_shell_parse_argv (cmdline, &argc, &argv, &error)) {
         g_warning ("Can not parse cmdline `%s` exec: %s", cmdline, error->message);
         g_error_free (error);
@@ -74,7 +94,7 @@ execute_cmdline (const gchar *cmdline)
     }
 
     error = NULL;
-    retval = g_spawn_async (NULL, argv, NULL,
+    gboolean retval = g_spawn_async (NULL, argv, NULL,
                             G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL,
                             NULL, NULL,
                             NULL, &error);
@@ -90,75 +110,92 @@ execute_cmdline (const gchar *cmdline)
 }
 
 #ifndef HAVE_DAEMON
-void closeall(int fd)
+static void
+closeall (gint fd)
 {
-    int fdlimit = sysconf(_SC_OPEN_MAX);
+    gint fdlimit = sysconf(_SC_OPEN_MAX);
 
-    while (fd < fdlimit)
+    while (fd < fdlimit) {
       close(fd++);
+    }
 }
 
-int daemon(int nochdir, int noclose)
+static gint
+daemon (gint nochdir, gint noclose)
 {
-    switch (fork())
-    {
+    switch (fork()) {
         case 0:  break;
         case -1: return -1;
         default: _exit(0);
     }
 
-    if (setsid() < 0)
+    if (setsid() < 0) {
       return -1;
+    }
 
-    switch (fork())
-    {
+    switch (fork()) {
         case 0:  break;
         case -1: return -1;
         default: _exit(0);
     }
 
-    if (!nochdir)
+    if (!nochdir) {
       chdir("/");
+    }
 
-    if (!noclose)
-    {
+    if (!noclose) {
         closeall(0);
         open("/dev/null",O_RDWR);
         dup(0); dup(0);
     }
-
     return 0;
 }
 #endif
 
+/*
+ * _sig_usr2_handler:
+ * @sig: the signal number, which is usually SIGUSR2.
+ *
+ * A signal handler for SIGUSR2 signal. Dump a summary of memory usage to stderr.
+ */
+static void
+_sig_usr2_handler (int sig)
+{
+    g_mem_profile ();
+}
+
 gint
 main (gint argc, gchar **argv)
 {
-    GOptionContext *context;
-    BusServer *server;
-    IBusBus *bus;
-
-    GError *error = NULL;
-
     setlocale (LC_ALL, "");
 
-    context = g_option_context_new ("- ibus daemon");
-
+    GOptionContext *context = g_option_context_new ("- ibus daemon");
     g_option_context_add_main_entries (context, entries, "ibus-daemon");
 
     g_argv = g_strdupv (argv);
+    GError *error = NULL;
     if (!g_option_context_parse (context, &argc, &argv, &error)) {
         g_printerr ("Option parsing failed: %s\n", error->message);
+       g_error_free (error);
+        exit (-1);
+    }
+    if (g_gdbus_timeout < -1) {
+        g_printerr ("Bad timeout (must be >= -1): %d\n", g_gdbus_timeout);
         exit (-1);
     }
 
+    if (g_mempro) {
+        g_mem_set_vtable (glib_mem_profiler_table);
+        signal (SIGUSR2, _sig_usr2_handler);
+    }
+
     /* check uid */
     {
         const gchar *username = ibus_get_user_name ();
         uid_t uid = getuid ();
         struct passwd *pwd = getpwuid (uid);
 
-        if (pwd == NULL || strcmp (pwd->pw_name, username) != 0) {
+        if (pwd == NULL || g_strcmp0 (pwd->pw_name, username) != 0) {
             g_printerr ("Please run ibus-daemon with login user! Do not run ibus-daemon with sudo or su.\n");
             exit (-1);
         }
@@ -172,37 +209,43 @@ main (gint argc, gchar **argv)
         }
     }
 
-    /* create a new process group */
-    setpgrp ();
+    /* create a new process group. this is important to kill all of its children by SIGTERM at a time in bus_ibus_impl_destroy. */
+    setpgid (0, 0);
 
-    g_type_init ();
+    ibus_init ();
+
+#ifdef G_THREADS_ENABLED
+    g_thread_init (NULL);
+#endif
+    ibus_set_log_handler (g_verbose);
 
     /* check if ibus-daemon is running in this session */
-    bus = ibus_bus_new ();
+    if (ibus_get_address () != NULL) {
+        IBusBus *bus = ibus_bus_new ();
 
-    if (ibus_bus_is_connected (bus)) {
-        if (!replace) {
-            g_printerr ("current session already has an ibus-daemon.\n");
-            exit (-1);
-        }
-        ibus_bus_exit (bus, FALSE);
-        while (ibus_bus_is_connected (bus)) {
-            g_main_context_iteration (NULL, TRUE);
+        if (ibus_bus_is_connected (bus)) {
+            if (!replace) {
+                g_printerr ("current session already has an ibus-daemon.\n");
+                exit (-1);
+            }
+            ibus_bus_exit (bus, FALSE);
+            while (ibus_bus_is_connected (bus)) {
+                g_main_context_iteration (NULL, TRUE);
+            }
         }
+        g_object_unref (bus);
     }
-    g_object_unref (bus);
-    bus = NULL;
-
-    /* create ibus server */
-    server = bus_server_get_default ();
-    bus_server_listen (server);
 
+    bus_server_init ();
     if (!single) {
         /* execute config component */
         if (g_strcmp0 (config, "default") == 0) {
-            IBusComponent *component;
+            BusComponent *component;
             component = bus_registry_lookup_component_by_name (BUS_DEFAULT_REGISTRY, IBUS_SERVICE_CONFIG);
-            if (component == NULL || !ibus_component_start (component)) {
+            if (component) {
+                bus_component_set_restart (component, restart);
+            }
+            if (component == NULL || !bus_component_start (component, g_verbose)) {
                 g_printerr ("Can not execute default config program\n");
                 exit (-1);
             }
@@ -211,11 +254,14 @@ main (gint argc, gchar **argv)
                 exit (-1);
         }
 
-        /* execut panel component */
+        /* execute panel component */
         if (g_strcmp0 (panel, "default") == 0) {
-            IBusComponent *component;
+            BusComponent *component;
             component = bus_registry_lookup_component_by_name (BUS_DEFAULT_REGISTRY, IBUS_SERVICE_PANEL);
-            if (component == NULL || !ibus_component_start (component)) {
+            if (component) {
+                bus_component_set_restart (component, restart);
+            }
+            if (component == NULL || !bus_component_start (component, g_verbose)) {
                 g_printerr ("Can not execute default panel program\n");
                 exit (-1);
             }
@@ -227,11 +273,10 @@ main (gint argc, gchar **argv)
 
     /* execute ibus xim server */
     if (xim) {
-        if (!execute_cmdline (LIBEXECDIR"/ibus-x11 --kill-daemon"))
+        if (!execute_cmdline (LIBEXECDIR "/ibus-x11 --kill-daemon"))
             exit (-1);
     }
 
-    bus_server_run (server);
-
+    bus_server_run ();
     return 0;
 }