#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.h>
-#include <errno.h>
-#include <signal.h>
-#include <sys/ptrace.h>
-#include <sys/inotify.h>
-
-#define DEFAULT_HOMEDIR "/home/user"
-#define MICROB_PROFILE_DIR "/.mozilla/microb"
-#define MICROB_LOCKFILE "lock"
#endif
#include "browser-switchboard.h"
#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. */
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
-/* Get a browserd PID from the corresponding Mozilla profile lockfile */
-static pid_t get_browserd_pid(const char *lockfile) {
- char buf[256], *tmp;
+/* 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;
- /* 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 ":+" */
+ 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;
- return atoi(tmp);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
#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;
}
/* Child process */
setsid();
- close_stdio();
}
execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL);
}
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;
- 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)
printf("launch_microb with uri '%s'\n", uri);
/* Launch browserd if it's not running */
- status = system("pidof browserd > /dev/null");
+ status = system("pidof /usr/sbin/browserd > /dev/null");
if (WIFEXITED(status) && WEXITSTATUS(status)) {
kill_browserd = 1;
#ifdef FREMANTLE
- system("/usr/sbin/browserd -d -b > /dev/null 2>&1");
+ system("/usr/sbin/browserd -d -b");
#else
- system("/usr/sbin/browserd -d > /dev/null 2>&1");
+ system("/usr/sbin/browserd -d");
#endif
}
exit(1);
}
#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)))) {
- printf("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)))) {
- printf("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) {
- perror("inotify_init");
- exit(1);
- }
- if ((inot_wd = inotify_add_watch(fd, microb_profile_dir,
- IN_CREATE)) == -1) {
- perror("inotify_add_watch");
- exit(1);
- }
- free(microb_profile_dir);
-
if (pid > 0) {
/* Parent process */
/* Wait for our child to start the browser UI process and
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))
- /* Don't really care -- about to disconnect from the
- bus anyhow */
+ if (dbus_error_is_set(&dbus_error)) {
+ fprintf(stderr,
+ "Failed to remove watch for browser UI start: %s\n",
+ dbus_error.message);
dbus_error_free(&dbus_error);
- dbus_connection_close(raw_connection);
- dbus_connection_unref(raw_connection);
+ exit(1);
+ }
/* Browser UI's started, send it the request for a new window
via D-Bus */
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
- its attached browserd process. Get the browserd process's
- PID and use ptrace() to watch for process termination.
+ 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.
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. */
-
- /* Wait for the MicroB browserd lockfile to be created */
- printf("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 */
- pos = buf + bytes_read;
- }
- 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)
- printf("Profile lockfile link lacks PID\n");
- else
- printf("readlink() on lockfile failed: %s\n",
- strerror(-browserd_pid));
+ 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);
exit(1);
}
- free(microb_lockfile);
-
- /* Wait for the browserd to close */
- printf("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) {
- perror("clearing SIGCHLD handler failed");
+ /* 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);
exit(1);
}
-
- /* Trace the browserd to get a close notification */
- ignore_sigstop = 1;
- if (ptrace(PTRACE_ATTACH, browserd_pid, NULL, NULL) == -1) {
- perror("PTRACE_ATTACH");
+ 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");
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 */
- printf("Ignoring first SIGSTOP\n");
- ptrace(PTRACE_CONT, browserd_pid,
- NULL, NULL);
- ignore_sigstop = 0;
- continue;
- }
- printf("Forwarding signal %d to browserd\n",
- WSTOPSIG(status));
- ptrace(PTRACE_CONT, browserd_pid,
- NULL, WSTOPSIG(status));
- }
- }
-
- /* 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! */
- printf("Killing MicroB\n");
- kill(pid, SIGTERM);
- waitpid(pid, &status, 0);
+ 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);
- /* Restore old SIGCHLD handler */
- if (sigaction(SIGCHLD, &oldact, NULL) == -1) {
- perror("restoring old SIGCHLD handler failed");
- exit(1);
+ /* 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);
+ }
}
+ g_object_unref(g_proxy);
} else {
/* Child process */
- 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 */
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 */
/* Kill off browserd if we started it */
if (kill_browserd)
- system("kill `pidof browserd`");
+ system("kill `pidof /usr/sbin/browserd`");
if (!ctx || !ctx->continuous_mode)
exit(0);
}
/* Child process */
setsid();
- close_stdio();
}
execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL);
}