Revert "Add preliminary support for Opera Mobile"
[browser-switch] / launcher.c
index f229469..77f2ded 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * launcher.c -- functions for launching web browsers for browser-switchboard
  *
 /*
  * launcher.c -- functions for launching web browsers for browser-switchboard
  *
- * Copyright (C) 2009 Steven Luo
+ * Copyright (C) 2009-2010 Steven Luo
  * Derived from a Python implementation by Jason Simpson and Steven Luo
  *
  * This program is free software; you can redistribute it and/or
  * Derived from a Python implementation by Jason Simpson and Steven Luo
  *
  * This program is free software; you can redistribute it and/or
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+#include <errno.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <dbus/dbus-glib.h>
 
 #ifdef FREMANTLE
 #include <dbus/dbus-glib.h>
 
 #ifdef FREMANTLE
-#include <signal.h>
-#include <dbus/dbus-glib-lowlevel.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus.h>
+#include <signal.h>
+#include <sys/ptrace.h>
 #include <sys/inotify.h>
 #include <sys/inotify.h>
-#include <poll.h>
+
+#define DEFAULT_HOMEDIR "/home/user"
+#define MICROB_PROFILE_DIR "/.mozilla/microb"
+#define MICROB_LOCKFILE "lock"
 #endif
 
 #include "browser-switchboard.h"
 #include "launcher.h"
 #include "dbus-server-bindings.h"
 #endif
 
 #include "browser-switchboard.h"
 #include "launcher.h"
 #include "dbus-server-bindings.h"
+#include "log.h"
 
 #define LAUNCH_DEFAULT_BROWSER launch_microb
 
 #ifdef FREMANTLE
 static int microb_started = 0;
 
 #define LAUNCH_DEFAULT_BROWSER launch_microb
 
 #ifdef FREMANTLE
 static int microb_started = 0;
-static int kill_microb = 0;
 
 /* Check to see whether MicroB is ready to handle D-Bus requests yet
    See the comments in launch_microb to understand how this works. */
 
 /* Check to see whether MicroB is ready to handle D-Bus requests yet
    See the comments in launch_microb to understand how this works. */
@@ -54,59 +60,58 @@ static DBusHandlerResult check_microb_started(DBusConnection *connection,
        DBusError error;
        char *name, *old, *new;
 
        DBusError error;
        char *name, *old, *new;
 
-       printf("Checking to see if MicroB is ready\n");
+       log_msg("Checking to see if MicroB is ready\n");
        dbus_error_init(&error);
        if (!dbus_message_get_args(message, &error,
                                   DBUS_TYPE_STRING, &name,
                                   DBUS_TYPE_STRING, &old,
                                   DBUS_TYPE_STRING, &new,
                                   DBUS_TYPE_INVALID)) {
        dbus_error_init(&error);
        if (!dbus_message_get_args(message, &error,
                                   DBUS_TYPE_STRING, &name,
                                   DBUS_TYPE_STRING, &old,
                                   DBUS_TYPE_STRING, &new,
                                   DBUS_TYPE_INVALID)) {
-               printf("%s\n", error.message);
+               log_msg("%s\n", error.message);
                dbus_error_free(&error);
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
        }
        /* If old is an empty string, then the name has been acquired, and
           MicroB should be ready to handle our request */
        if (strlen(old) == 0) {
                dbus_error_free(&error);
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
        }
        /* If old is an empty string, then the name has been acquired, and
           MicroB should be ready to handle our request */
        if (strlen(old) == 0) {
-               printf("MicroB ready\n");
+               log_msg("MicroB ready\n");
                microb_started = 1;
        }
 
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
                microb_started = 1;
        }
 
        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
-/* Check to see whether the last MicroB window has closed
-   See the comments in launch_microb to understand how this works. */
-static DBusHandlerResult check_microb_finished(DBusConnection *connection,
-                                    DBusMessage *message,
-                                    void *user_data) {
-       DBusError error;
-       char *name, *old, *new;
+/* Get a browserd PID from the corresponding Mozilla profile lockfile */
+static pid_t get_browserd_pid(const char *lockfile) {
+       char buf[256], *tmp;
 
 
-       printf("Checking to see if we should kill MicroB\n");
-       /* Check to make sure that the Mozilla.MicroB name is being released,
-          not acquired -- if it's being acquired, we might be seeing an event
-          at MicroB startup, in which case killing the browser isn't
-          appropriate */
-       dbus_error_init(&error);
-       if (!dbus_message_get_args(message, &error,
-                                  DBUS_TYPE_STRING, &name,
-                                  DBUS_TYPE_STRING, &old,
-                                  DBUS_TYPE_STRING, &new,
-                                  DBUS_TYPE_INVALID)) {
-               printf("%s\n", error.message);
-               dbus_error_free(&error);
-               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-       }
-       /* If old isn't an empty string, the name is being released, and
-          we should now kill MicroB */
-       if (strlen(old) > 0)
-               kill_microb = 1;
+       /* The lockfile is a symlink pointing to "[ipaddr]:+[pid]", so read in
+          the target of the symlink and parse it that way */
+       memset(buf, '\0', 256);
+       if (readlink(lockfile, buf, 255) == -1)
+               return -errno;
+       if (!(tmp = strstr(buf, ":+")))
+               return 0;
+       tmp += 2; /* Skip over the ":+" */
 
 
-       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       return atoi(tmp);
 }
 #endif
 
 }
 #endif
 
+/* Close stdin/stdout/stderr and replace with /dev/null */
+static int close_stdio(void) {
+       int fd;
+
+       if ((fd = open("/dev/null", O_RDWR)) == -1)
+               return -1;
+
+       if (dup2(fd, 0) == -1 || dup2(fd, 1) == -1 || dup2(fd, 2) == -1)
+               return -1;
+
+       close(fd);
+       return 0;
+}
+
 static void launch_tear(struct swb_context *ctx, char *uri) {
        int status;
        static DBusGProxy *tear_proxy = NULL;
 static void launch_tear(struct swb_context *ctx, char *uri) {
        int status;
        static DBusGProxy *tear_proxy = NULL;
@@ -116,7 +121,7 @@ static void launch_tear(struct swb_context *ctx, char *uri) {
        if (!uri)
                uri = "new_window";
 
        if (!uri)
                uri = "new_window";
 
-       printf("launch_tear with uri '%s'\n", uri);
+       log_msg("launch_tear with uri '%s'\n", uri);
 
        /* We should be able to just call the D-Bus service to open Tear ...
           but if Tear's not open, that cuases D-Bus to start Tear and then
 
        /* We should be able to just call the D-Bus service to open Tear ...
           but if Tear's not open, that cuases D-Bus to start Tear and then
@@ -126,23 +131,35 @@ static void launch_tear(struct swb_context *ctx, char *uri) {
           around by just invoking Tear with exec() if it's not running. */
        status = system("pidof tear > /dev/null");
        if (WIFEXITED(status) && !WEXITSTATUS(status)) {
           around by just invoking Tear with exec() if it's not running. */
        status = system("pidof tear > /dev/null");
        if (WIFEXITED(status) && !WEXITSTATUS(status)) {
-               if (!tear_proxy)
-                       tear_proxy = dbus_g_proxy_new_for_name(ctx->session_bus,
-                                       "com.nokia.tear", "/com/nokia/tear",
-                                       "com.nokia.Tear");
-               dbus_g_proxy_call(tear_proxy, "OpenAddress", &error,
-                                 G_TYPE_STRING, uri, G_TYPE_INVALID);
+               if (!tear_proxy) {
+                       if (!(tear_proxy = dbus_g_proxy_new_for_name(
+                                               ctx->session_bus,
+                                               "com.nokia.tear",
+                                               "/com/nokia/tear",
+                                               "com.nokia.Tear"))) {
+                               log_msg("Failed to create proxy for com.nokia.Tear D-Bus interface\n");
+                               exit(1);
+                       }
+               }
+
+               if (!dbus_g_proxy_call(tear_proxy, "OpenAddress", &error,
+                                      G_TYPE_STRING, uri, G_TYPE_INVALID,
+                                      G_TYPE_INVALID)) {
+                       log_msg("Opening window failed: %s\n", error->message);
+                       exit(1);
+               }
                if (!ctx->continuous_mode)
                        exit(0);
        } else {
                if (ctx->continuous_mode) {
                        if ((pid = fork()) != 0) {
                                /* Parent process or error in fork() */
                if (!ctx->continuous_mode)
                        exit(0);
        } else {
                if (ctx->continuous_mode) {
                        if ((pid = fork()) != 0) {
                                /* Parent process or error in fork() */
-                               printf("child: %d\n", (int)pid);
+                               log_msg("child: %d\n", (int)pid);
                                return;
                        }
                        /* Child process */
                        setsid();
                                return;
                        }
                        /* Child process */
                        setsid();
+                       close_stdio();
                }
                execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL);
        }
                }
                execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL);
        }
@@ -153,77 +170,122 @@ void launch_microb(struct swb_context *ctx, char *uri) {
        int status;
        pid_t pid;
 #ifdef FREMANTLE
        int status;
        pid_t pid;
 #ifdef FREMANTLE
+       char *homedir, *microb_profile_dir, *microb_lockfile;
+       size_t len;
+       int fd, inot_wd;
        DBusConnection *raw_connection;
        DBusError dbus_error;
        DBusHandleMessageFunction filter_func;
        DBusGProxy *g_proxy;
        GError *gerror = NULL;
        DBusConnection *raw_connection;
        DBusError dbus_error;
        DBusHandleMessageFunction filter_func;
        DBusGProxy *g_proxy;
        GError *gerror = NULL;
+       int bytes_read;
+       char buf[256], *pos;
+       struct inotify_event *event;
+       pid_t browserd_pid, waited_pid;
+       struct sigaction act, oldact;
+       int ignore_sigstop;
 #endif
 
        if (!uri)
                uri = "new_window";
 
 #endif
 
        if (!uri)
                uri = "new_window";
 
-       printf("launch_microb with uri '%s'\n", uri);
+       log_msg("launch_microb with uri '%s'\n", uri);
 
        /* Launch browserd if it's not running */
 
        /* Launch browserd if it's not running */
-       status = system("pidof /usr/sbin/browserd > /dev/null");
+       status = system("pidof browserd > /dev/null");
        if (WIFEXITED(status) && WEXITSTATUS(status)) {
                kill_browserd = 1;
 #ifdef FREMANTLE
        if (WIFEXITED(status) && WEXITSTATUS(status)) {
                kill_browserd = 1;
 #ifdef FREMANTLE
-               system("/usr/sbin/browserd -d -b");
+               system("/usr/sbin/browserd -d -b > /dev/null 2>&1");
 #else
 #else
-               system("/usr/sbin/browserd -d");
+               system("/usr/sbin/browserd -d > /dev/null 2>&1");
 #endif
        }
 
        /* Release the osso_browser D-Bus name so that MicroB can take it */
        dbus_release_osso_browser_name(ctx);
 
 #endif
        }
 
        /* Release the osso_browser D-Bus name so that MicroB can take it */
        dbus_release_osso_browser_name(ctx);
 
+#ifdef FREMANTLE
+       /* Put together the path to the MicroB browserd lockfile */
+       if (!(homedir = getenv("HOME")))
+               homedir = DEFAULT_HOMEDIR;
+       len = strlen(homedir) + strlen(MICROB_PROFILE_DIR) + 1;
+       if (!(microb_profile_dir = calloc(len, sizeof(char)))) {
+               log_msg("calloc() failed\n");
+               exit(1);
+       }
+       snprintf(microb_profile_dir, len, "%s%s",
+                homedir, MICROB_PROFILE_DIR);
+       len = strlen(homedir) + strlen(MICROB_PROFILE_DIR) +
+             strlen("/") + strlen(MICROB_LOCKFILE) + 1;
+       if (!(microb_lockfile = calloc(len, sizeof(char)))) {
+               log_msg("calloc() failed\n");
+               exit(1);
+       }
+       snprintf(microb_lockfile, len, "%s%s/%s",
+                homedir, MICROB_PROFILE_DIR, MICROB_LOCKFILE);
+
+       /* Watch for the creation of a MicroB browserd lockfile
+          NB: The watch has to be set up here, before the browser
+          is launched, to make sure there's no race between browserd
+          starting and us creating the watch */
+       if ((fd = inotify_init()) == -1) {
+               log_perror(errno, "inotify_init");
+               exit(1);
+       }
+       if ((inot_wd = inotify_add_watch(fd, microb_profile_dir,
+                                        IN_CREATE)) == -1) {
+               log_perror(errno, "inotify_add_watch");
+               exit(1);
+       }
+       free(microb_profile_dir);
+
+       /* Set up the D-Bus eavesdropping we'll use to watch for MicroB
+          acquiring the com.nokia.osso_browser D-Bus name.  Again, this needs
+          to happen before the browser is launched, so that there's no race
+          between establishing the watch and browser startup.
+
+          Ideas for how to do this monitoring derived from the dbus-monitor
+          code (tools/dbus-monitor.c in the D-Bus codebase). */
+       dbus_error_init(&dbus_error);
+
+       raw_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &dbus_error);
+       if (!raw_connection) {
+               log_msg("Failed to open connection to session bus: %s\n",
+                       dbus_error.message);
+               dbus_error_free(&dbus_error);
+               exit(1);
+       }
+
+       dbus_bus_add_match(raw_connection,
+                          "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.osso_browser'",
+                          &dbus_error);
+       if (dbus_error_is_set(&dbus_error)) {
+               log_msg("Failed to set up watch for browser UI start: %s\n",
+                       dbus_error.message);
+               dbus_error_free(&dbus_error);
+               exit(1);
+       }
+       filter_func = check_microb_started;
+       if (!dbus_connection_add_filter(raw_connection,
+                                       filter_func, NULL, NULL)) {
+               log_msg("Failed to set up watch filter!\n");
+               exit(1);
+       }
+
        if ((pid = fork()) == -1) {
        if ((pid = fork()) == -1) {
-               perror("fork");
+               log_perror(errno, "fork");
                exit(1);
        }
                exit(1);
        }
-#ifdef FREMANTLE
+
        if (pid > 0) {
                /* Parent process */
                /* Wait for our child to start the browser UI process and
                   for it to acquire the com.nokia.osso_browser D-Bus name,
                   then make the appropriate method call to open the browser
        if (pid > 0) {
                /* Parent process */
                /* Wait for our child to start the browser UI process and
                   for it to acquire the com.nokia.osso_browser D-Bus name,
                   then make the appropriate method call to open the browser
-                  window.
-
-                  Ideas for how to do this monitoring derived from the
-                  dbus-monitor code (tools/dbus-monitor.c in the D-Bus
-                  codebase). */
+                  window. */
                microb_started = 0;
                microb_started = 0;
-               dbus_error_init(&dbus_error);
-
-               raw_connection = dbus_bus_get_private(DBUS_BUS_SESSION,
-                                                     &dbus_error);
-               if (!raw_connection) {
-                       fprintf(stderr,
-                               "Failed to open connection to session bus: %s\n",
-                               dbus_error.message);
-                       dbus_error_free(&dbus_error);
-                       exit(1);
-               }
-
-               dbus_bus_add_match(raw_connection,
-                                  "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.osso_browser'",
-                                  &dbus_error);
-               if (dbus_error_is_set(&dbus_error)) {
-                       fprintf(stderr,
-                               "Failed to set up watch for browser UI start: %s\n",
-                               dbus_error.message);
-                       dbus_error_free(&dbus_error);
-                       exit(1);
-               }
-               filter_func = check_microb_started;
-               if (!dbus_connection_add_filter(raw_connection,
-                                               filter_func, NULL, NULL)) {
-                       fprintf(stderr, "Failed to set up watch filter!\n");
-                       exit(1);
-               }
-               printf("Waiting for MicroB to start\n");
+               log_msg("Waiting for MicroB to start\n");
                while (!microb_started &&
                       dbus_connection_read_write_dispatch(raw_connection,
                                                           -1));
                while (!microb_started &&
                       dbus_connection_read_write_dispatch(raw_connection,
                                                           -1));
@@ -232,13 +294,12 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                dbus_bus_remove_match(raw_connection,
                                      "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.osso_browser'",
                                      &dbus_error);
                dbus_bus_remove_match(raw_connection,
                                      "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.osso_browser'",
                                      &dbus_error);
-               if (dbus_error_is_set(&dbus_error)) {
-                       fprintf(stderr,
-                               "Failed to remove watch for browser UI start: %s\n",
-                               dbus_error.message);
+               if (dbus_error_is_set(&dbus_error))
+                       /* Don't really care -- about to disconnect from the
+                          bus anyhow */
                        dbus_error_free(&dbus_error);
                        dbus_error_free(&dbus_error);
-                       exit(1);
-               }
+               dbus_connection_close(raw_connection);
+               dbus_connection_unref(raw_connection);
 
                /* Browser UI's started, send it the request for a new window
                   via D-Bus */
 
                /* Browser UI's started, send it the request for a new window
                   via D-Bus */
@@ -247,7 +308,7 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                                "/com/nokia/osso_browser/request",
                                "com.nokia.osso_browser");
                if (!g_proxy) {
                                "/com/nokia/osso_browser/request",
                                "com.nokia.osso_browser");
                if (!g_proxy) {
-                       printf("Couldn't get a com.nokia.osso_browser proxy\n");
+                       log_msg("Couldn't get a com.nokia.osso_browser proxy\n");
                        exit(1);
                }
                if (!strcmp(uri, "new_window")) {
                        exit(1);
                }
                if (!strcmp(uri, "new_window")) {
@@ -258,8 +319,8 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                        if (!dbus_g_proxy_call(g_proxy, "top_application",
                                               &gerror, G_TYPE_INVALID,
                                               G_TYPE_INVALID)) {
                        if (!dbus_g_proxy_call(g_proxy, "top_application",
                                               &gerror, G_TYPE_INVALID,
                                               G_TYPE_INVALID)) {
-                               printf("Opening window failed: %s\n",
-                                      gerror->message);
+                               log_msg("Opening window failed: %s\n",
+                                       gerror->message);
                                exit(1);
                        }
 #endif
                                exit(1);
                        }
 #endif
@@ -268,8 +329,8 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                                               G_TYPE_STRING, "about:blank",
                                               G_TYPE_INVALID,
                                               G_TYPE_INVALID)) {
                                               G_TYPE_STRING, "about:blank",
                                               G_TYPE_INVALID,
                                               G_TYPE_INVALID)) {
-                               printf("Opening window failed: %s\n",
-                                      gerror->message);
+                               log_msg("Opening window failed: %s\n",
+                                       gerror->message);
                                exit(1);
                        }
                } else {
                                exit(1);
                        }
                } else {
@@ -278,90 +339,141 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                                               G_TYPE_STRING, uri,
                                               G_TYPE_INVALID,
                                               G_TYPE_INVALID)) {
                                               G_TYPE_STRING, uri,
                                               G_TYPE_INVALID,
                                               G_TYPE_INVALID)) {
-                               printf("Opening window failed: %s\n",
-                                      gerror->message);
+                               log_msg("Opening window failed: %s\n",
+                                       gerror->message);
                                exit(1);
                        }
                }
                                exit(1);
                        }
                }
+               g_object_unref(g_proxy);
 
                /* Workaround: the browser process we started is going to want
                   to hang around forever, hogging the com.nokia.osso_browser
                   D-Bus interface while at it.  To fix this, we notice that
                   when the last browser window closes, the browser UI restarts
 
                /* Workaround: the browser process we started is going to want
                   to hang around forever, hogging the com.nokia.osso_browser
                   D-Bus interface while at it.  To fix this, we notice that
                   when the last browser window closes, the browser UI restarts
-                  its attached browserd process, which causes an observable
-                  change in the ownership of the Mozilla.MicroB D-Bus name.
-                  Watch for this change and kill off the browser UI process
-                  when it happens.
+                  its attached browserd process.  Get the browserd process's
+                  PID and use ptrace() to watch for process termination.
 
                   This has the problem of not being able to detect whether
                   the bookmark window is open and/or in use, but it's the best
                   that I can think of.  Better suggestions would be greatly
                   appreciated. */
 
                   This has the problem of not being able to detect whether
                   the bookmark window is open and/or in use, but it's the best
                   that I can think of.  Better suggestions would be greatly
                   appreciated. */
-               kill_microb = 0;
-               dbus_bus_add_match(raw_connection,
-                                  "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='Mozilla.MicroB'",
-                                  &dbus_error);
-               if (dbus_error_is_set(&dbus_error)) {
-                       fprintf(stderr,
-                               "Failed to set up watch for browserd restart: %s\n",
-                               dbus_error.message);
-                       dbus_error_free(&dbus_error);
+
+               /* Wait for the MicroB browserd lockfile to be created */
+               log_msg("Waiting for browserd lockfile to be created\n");
+               memset(buf, '\0', 256);
+               /* read() blocks until there are events to be read */
+               while ((bytes_read = read(fd, buf, 255)) > 0) {
+                       pos = buf;
+                       /* Loop until we see the event we're looking for
+                          or until all the events are processed */
+                       while (pos && (pos-buf) < bytes_read) {
+                               event = (struct inotify_event *)pos;
+                               len = sizeof(struct inotify_event)
+                                     + event->len;
+                               if (!strcmp(MICROB_LOCKFILE,
+                                           event->name)) {
+                                       /* Lockfile created */
+                                       pos = NULL;
+                                       break;
+                               } else if ((pos-buf) + len < bytes_read)
+                                       /* More events to process */
+                                       pos += len;
+                               else
+                                       /* All events processed */
+                                       break;
+                       }
+                       if (!pos)
+                               /* Event found, stop looking */
+                               break;
+                       memset(buf, '\0', 256);
+                }
+               inotify_rm_watch(fd, inot_wd);
+               close(fd);
+
+               /* Get the PID of the browserd from the lockfile */
+               if ((browserd_pid = get_browserd_pid(microb_lockfile)) <= 0) {
+                       if (browserd_pid == 0)
+                               log_msg("Profile lockfile link lacks PID\n");
+                       else
+                               log_perror(-browserd_pid,
+                                          "readlink() on lockfile failed");
                        exit(1);
                }
                        exit(1);
                }
-               /* Maemo 5 PR1.1 seems to have changed the name browserd takes
-                  to com.nokia.microb-engine; look for this too */
-               dbus_bus_add_match(raw_connection,
-                                  "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.microb-engine'",
-                                  &dbus_error);
-               if (dbus_error_is_set(&dbus_error)) {
-                       fprintf(stderr,
-                               "Failed to set up watch for browserd restart: %s\n",
-                               dbus_error.message);
-                       dbus_error_free(&dbus_error);
+               free(microb_lockfile);
+
+               /* Wait for the browserd to close */
+               log_msg("Waiting for MicroB (browserd pid %d) to finish\n",
+                       browserd_pid);
+               /* Clear any existing SIGCHLD handler to prevent interference
+                  with our wait() */
+               act.sa_handler = SIG_DFL;
+               act.sa_flags = 0;
+               sigemptyset(&(act.sa_mask));
+               if (sigaction(SIGCHLD, &act, &oldact) == -1) {
+                       log_perror(errno, "clearing SIGCHLD handler failed");
                        exit(1);
                }
                        exit(1);
                }
-               filter_func = check_microb_finished;
-               if (!dbus_connection_add_filter(raw_connection,
-                                               filter_func, NULL, NULL)) {
-                       fprintf(stderr, "Failed to set up watch filter!\n");
+
+               /* Trace the browserd to get a close notification */
+               ignore_sigstop = 1;
+               if (ptrace(PTRACE_ATTACH, browserd_pid, NULL, NULL) == -1) {
+                       log_perror(errno, "PTRACE_ATTACH");
                        exit(1);
                }
                        exit(1);
                }
-               while (!kill_microb &&
-                      dbus_connection_read_write_dispatch(raw_connection,
-                                                          -1));
-               dbus_connection_remove_filter(raw_connection,
-                                             filter_func, NULL);
-               dbus_bus_remove_match(raw_connection,
-                                  "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='Mozilla.MicroB'",
-                                  &dbus_error);
-               if (dbus_error_is_set(&dbus_error))
-                       /* Don't really care -- about to disconnect from the
-                          bus anyhow */
-                       dbus_error_free(&dbus_error);
-               dbus_bus_remove_match(raw_connection,
-                                  "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='com.nokia.microb-engine'",
-                                  &dbus_error);
-               if (dbus_error_is_set(&dbus_error))
-                       dbus_error_free(&dbus_error);
-               dbus_connection_close(raw_connection);
-               dbus_connection_unref(raw_connection);
-
-               /* Tell browser UI to exit nicely */
-               printf("Closing MicroB\n");
-               if (!dbus_g_proxy_call(g_proxy, "exit_browser", &gerror,
-                                      G_TYPE_INVALID, G_TYPE_INVALID)) {
-                       /* We don't expect a reply; any other error indicates
-                          a problem */
-                       if (gerror->domain != DBUS_GERROR ||
-                           gerror->code != DBUS_GERROR_NO_REPLY) {
-                               printf("exit_browser failed: %s\n",
-                                      gerror->message);
-                               exit(1);
+               ptrace(PTRACE_CONT, browserd_pid, NULL, NULL);
+               while ((waited_pid = wait(&status)) > 0) {
+                       if (waited_pid != browserd_pid)
+                               /* Not interested in other processes */
+                               continue;
+                       if (WIFEXITED(status) || WIFSIGNALED(status))
+                               /* browserd exited */
+                               break;
+                       else if (WIFSTOPPED(status)) {
+                               /* browserd was sent a signal
+                                  We're responsible for making sure this
+                                  signal gets delivered */
+                               if (ignore_sigstop &&
+                                   WSTOPSIG(status) == SIGSTOP) {
+                                       /* Ignore the first SIGSTOP received
+                                          This is raised for some reason
+                                          immediately after we start tracing
+                                          the process, and won't be followed
+                                          by a SIGCONT at any point */
+                                       log_msg("Ignoring first SIGSTOP\n");
+                                       ptrace(PTRACE_CONT, browserd_pid,
+                                              NULL, NULL);
+                                       ignore_sigstop = 0;
+                                       continue;
+                               }
+                               log_msg("Forwarding signal %d to browserd\n",
+                                       WSTOPSIG(status));
+                               ptrace(PTRACE_CONT, browserd_pid,
+                                      NULL, WSTOPSIG(status));
                        }
                }
                        }
                }
-               g_object_unref(g_proxy);
+
+               /* Kill off browser UI
+                  XXX: There is a race here with the restarting of the closed
+                  browserd; if that happens before we kill the browser UI, the
+                  newly started browserd may not close with the UI
+                  XXX: Hope we don't cause data loss here! */
+               log_msg("Killing MicroB\n");
+               kill(pid, SIGTERM);
+               waitpid(pid, &status, 0);
+
+               /* Restore old SIGCHLD handler */
+               if (sigaction(SIGCHLD, &oldact, NULL) == -1) {
+                       log_perror(errno,
+                                  "restoring old SIGCHLD handler failed");
+                       exit(1);
+               }
        } else {
                /* Child process */
        } else {
                /* Child process */
+               dbus_connection_close(raw_connection);
+               dbus_connection_unref(raw_connection);
+               close(fd);
+               close_stdio();
+
                /* exec maemo-invoker directly instead of relying on the
                   /usr/bin/browser symlink, since /usr/bin/browser may have
                   been replaced with a shell script calling us via D-Bus */
                /* exec maemo-invoker directly instead of relying on the
                   /usr/bin/browser symlink, since /usr/bin/browser may have
                   been replaced with a shell script calling us via D-Bus */
@@ -371,11 +483,18 @@ void launch_microb(struct swb_context *ctx, char *uri) {
                execl("/usr/bin/maemo-invoker", "browser", (char *)NULL);
        }
 #else /* !FREMANTLE */
                execl("/usr/bin/maemo-invoker", "browser", (char *)NULL);
        }
 #else /* !FREMANTLE */
+       if ((pid = fork()) == -1) {
+               log_perror(errno, "fork");
+               exit(1);
+       }
+
        if (pid > 0) {
                /* Parent process */
                waitpid(pid, &status, 0);
        } else {
                /* Child process */
        if (pid > 0) {
                /* Parent process */
                waitpid(pid, &status, 0);
        } else {
                /* Child process */
+               close_stdio();
+
                /* exec maemo-invoker directly instead of relying on the
                   /usr/bin/browser symlink, since /usr/bin/browser may have
                   been replaced with a shell script calling us via D-Bus */
                /* exec maemo-invoker directly instead of relying on the
                   /usr/bin/browser symlink, since /usr/bin/browser may have
                   been replaced with a shell script calling us via D-Bus */
@@ -391,7 +510,7 @@ void launch_microb(struct swb_context *ctx, char *uri) {
 
        /* Kill off browserd if we started it */
        if (kill_browserd)
 
        /* Kill off browserd if we started it */
        if (kill_browserd)
-               system("kill `pidof /usr/sbin/browserd`");
+               system("kill `pidof browserd`");
 
        if (!ctx || !ctx->continuous_mode) 
                exit(0);
 
        if (!ctx || !ctx->continuous_mode) 
                exit(0);
@@ -410,7 +529,7 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) {
        if (!uri || !strcmp(uri, "new_window"))
                uri = "";
 
        if (!uri || !strcmp(uri, "new_window"))
                uri = "";
 
-       printf("launch_other_browser with uri '%s'\n", uri);
+       log_msg("launch_other_browser with uri '%s'\n", uri);
 
        if ((urilen = strlen(uri)) > 0) {
                /* Quote the URI to prevent the shell from interpreting it */
 
        if ((urilen = strlen(uri)) > 0) {
                /* Quote the URI to prevent the shell from interpreting it */
@@ -459,7 +578,7 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) {
        if (!(command = calloc(cmdlen+urilen+1, sizeof(char))))
                exit(1);
        snprintf(command, cmdlen+urilen+1, ctx->other_browser_cmd, quoted_uri);
        if (!(command = calloc(cmdlen+urilen+1, sizeof(char))))
                exit(1);
        snprintf(command, cmdlen+urilen+1, ctx->other_browser_cmd, quoted_uri);
-       printf("command: '%s'\n", command);
+       log_msg("command: '%s'\n", command);
 
        if (ctx->continuous_mode) {
                if (fork() != 0) {
 
        if (ctx->continuous_mode) {
                if (fork() != 0) {
@@ -471,6 +590,7 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) {
                }
                /* Child process */
                setsid();
                }
                /* Child process */
                setsid();
+               close_stdio();
        }
        execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
 }
        }
        execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
 }
@@ -485,7 +605,7 @@ static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) {
        free(ctx->other_browser_cmd);
        ctx->other_browser_cmd = calloc(len+1, sizeof(char));
        if (!ctx->other_browser_cmd) {
        free(ctx->other_browser_cmd);
        ctx->other_browser_cmd = calloc(len+1, sizeof(char));
        if (!ctx->other_browser_cmd) {
-               printf("malloc failed!\n");
+               log_msg("malloc failed!\n");
                ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
        } else {
                ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd,
                ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
        } else {
                ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd,
@@ -518,11 +638,12 @@ void update_default_browser(struct swb_context *ctx, char *default_browser) {
                if (ctx->other_browser_cmd)
                        ctx->default_browser_launcher = launch_other_browser;
                else {
                if (ctx->other_browser_cmd)
                        ctx->default_browser_launcher = launch_other_browser;
                else {
-                       printf("default_browser is 'other', but no other_browser_cmd set -- using default\n");
+                       log_msg("default_browser is 'other', but no other_browser_cmd set -- using default\n");
                        ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
                }
        } else {
                        ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
                }
        } else {
-               printf("Unknown default_browser %s, using default", default_browser);
+               log_msg("Unknown default_browser %s, using default",
+                       default_browser);
                ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
        }
 }
                ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER;
        }
 }