From: Steven Luo Date: Mon, 22 Feb 2010 10:03:38 +0000 (-0800) Subject: Merge commit 'v3.2' into test-branch X-Git-Tag: diablo-package-3.2-1~3 X-Git-Url: http://git.maemo.org/git/?p=browser-switch;a=commitdiff_plain;h=f14721a0488449df06a6b8dbeedfdb18ba0d8745;hp=a39df128ef39e554add0303e49d19265ba3e0767 Merge commit 'v3.2' into test-branch --- diff --git a/Changelog b/Changelog index 02456b9..4eb2902 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,23 @@ +version 3.2: +* make the "Web" menu entry and /usr/bin/browser open the default browser, and + provide a new "MicroB" menu entry and /usr/bin/microb script for launching + MicroB; the previous behavior was counterintuitive and difficult to discover +* Fremantle: fix launching and closing MicroB on Fremantle; thanks Faheem + Pervez and Uwe Kaminski for extensive testing +* Fremantle: force the Ovi Store bookmark to open in MicroB, since Ovi Store + doesn't load in other browsers; thanks ToJa92 of t.m.o for reporting +* add ability to send debug output to syslog and new config setting ("logging") + to control where debug output goes; thanks Faheem Pervez for the suggestion +* close stdin/stdout/stderr in child processes before the exec(), to make debug + output on stdout less noisy +* fix some abuses of the D-Bus API in launcher.c:launch_tear() +* make sure a running browserd is detected correctly on all devices so that we + don't try to launch another one; thanks Faheem Pervez for reporting +* ensure that only one browser-switchboard is active at any time +* update build system; we now have "diablo" and "fremantle" targets that build + binaries for the selected OS release +* link binaries with -Wl,--as-needed to eliminate unnecessary dependencies + version 3.1: * add a new, more finger-friendly GUI for Fremantle, based on work by Faheem Pervez (build UI with EXTRA_CPPFLAGS=-DFREMANTLE) diff --git a/Makefile b/Makefile index 33a52b8..65bb61e 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,25 @@ CC = gcc CFLAGS = -Wall -Os $(EXTRA_CFLAGS) CPPFLAGS = `pkg-config --cflags dbus-glib-1` $(EXTRA_CPPFLAGS) -LDFLAGS = `pkg-config --libs dbus-glib-1` $(EXTRA_LDFLAGS) +LDFLAGS = -Wl,--as-needed `pkg-config --libs dbus-glib-1` $(EXTRA_LDFLAGS) PREFIX = /usr APP = browser-switchboard -obj = main.o launcher.o dbus-server-bindings.o configfile.o +obj = main.o launcher.o dbus-server-bindings.o configfile.o log.o + +all: + @echo 'Usage:' + @echo ' make diablo -- build for Diablo' + @echo ' make fremantle -- build for Fremantle' +diablo: $(APP) +fremantle: + @$(MAKE) \ + EXTRA_CPPFLAGS='-DFREMANTLE `pkg-config --cflags dbus-1` $(EXTRA_CPPFLAGS)' \ + EXTRA_LDFLAGS='`pkg-config --libs dbus-1` $(EXTRA_LDFLAGS)' $(APP) -all: $(APP) $(APP): dbus-server-glue.h $(obj) - $(CC) $(CFLAGS) $(LDFLAGS) -o $(APP) $(obj) + $(CC) $(CFLAGS) -o $(APP) $(obj) $(LDFLAGS) dbus-server-glue.h: dbus-binding-tool --mode=glib-server --prefix="osso_browser" \ @@ -19,14 +28,17 @@ dbus-server-glue.h: strip: $(APP) strip $(APP) -install: all +install: $(APP) mkdir -p $(DESTDIR)$(PREFIX)/bin mkdir -p $(DESTDIR)$(PREFIX)/share/dbus-1/services - install -c -m 0755 browser-switchboard $(DESTDIR)$(PREFIX)/bin + mkdir -p $(DESTDIR)$(PREFIX)/share/applications/hildon + install -c -m 0755 $(APP) $(DESTDIR)$(PREFIX)/bin install -c -m 0644 com.nokia.osso_browser.service $(DESTDIR)$(PREFIX)/share/dbus-1/services install -c -m 0755 browser $(DESTDIR)$(PREFIX)/bin + install -c -m 0755 microb $(DESTDIR)$(PREFIX)/bin + install -c -m 0644 microb.desktop $(DESTDIR)$(PREFIX)/share/applications/hildon clean: - rm -f $(APP) *.o dbus-server-glue.h + rm -f $(APP) $(obj) dbus-server-glue.h -.PHONY: strip install +.PHONY: strip install diablo fremantle diff --git a/README b/README index 0d8f327..d7e51d8 100644 --- a/README +++ b/README @@ -1,5 +1,5 @@ BROWSER SWITCHBOARD -version 3.1 +version 3.2 Browser Switchboard is a program which allows you to choose which browser to use as the default browser. It supports MicroB, Tear, @@ -23,18 +23,20 @@ You can now select the default browser by using the Browser Switchboard applet in the Control Panel. Links in most applications, locally-saved web pages opened from the file manager, and (for Maemo 4.x) entries in the Web sidebar panel will open in the browser that you select as the -default. If you don't configure a default browser, MicroB will continue -to be used as the default browser. +default. Opening the "Web" menu entry and running "browser" from the +shell will also cause your chosen default browser to open. If you don't +configure a default browser, MicroB will continue to be used as the +default browser. No matter which browser you select as the default, MicroB can always be -opened via the Web menu entry in the applications menu (for Maemo 4.x, -located in the Internet menu by default), or by running "browser" from -the shell. While MicroB is open, it will receive all links from other -applications; closing MicroB will restore your chosen default browser. +opened via the MicroB menu entry in the applications menu (for Maemo +4.x, installed in the Extras menu by default), or by running "microb" +from the shell. While MicroB is open, it will receive all links from +other applications; closing MicroB will restore your chosen default +browser. -Some users have reported that a restart may be necessary to ensure that -Browser Switchboard is functioning. If you experience trouble after -installing the package, try rebooting your device first. +If you experience trouble after installing the package, try rebooting +your device. If that fixes things, please report this as a bug. Configuring the Default Browser by Hand: @@ -72,10 +74,12 @@ default_browser = "tear" # other_browser_cmd: If default browser is "other", what program # to run (%s will be replaced by URI) #other_browser_cmd = "some_browser %s" +# logging: Where log output should go: "stdout", "syslog", "none" +#logging = "stdout" # END SAMPLE CONFIG FILE Lines beginning with # characters are comments and are ignored by the -script. [Each option corresponds directly to an option or option group +script. [Most options correspond directly to an option or option group in the configuration UI.] In continuous mode, Browser Switchboard keeps running in the background @@ -104,6 +108,14 @@ at a shell. [In the UI, setting "Default brower" to "Other" activates the "Command (%s for URI)" setting, which corresponds to the value of other_browser_cmd.] +The logging option controls where Browser Switchboard sends its debug +logging output to. You should not need to change this unless you're +debugging Browser Switchboard, and there is no UI for this option. The +default option is "stdout", which means you won't see output unless you +run Browser Switchboard from the shell. "syslog" will send the output +to the system log (assuming you have a syslogd set up on your device), +and "none" disables debug logging entirely. + Browser Switchboard and MicroB's browserd: @@ -119,9 +131,9 @@ memory, but add a few seconds to MicroB's load time. Uninstalling Browser Switchboard: Remove the Browser Switchboard package using the Application Manager, -and everything should be back to normal. A reboot may be necessary for -changes to take effect -- if you experience problems, try restarting -your device first. +and everything should be back to normal. If you experience problems +after uninstalling, try restarting your device first; if that fixes +things, please report this as a bug. Compiling Browser Switchboard: @@ -140,12 +152,15 @@ SDK$ cd browser-switchboard-X.Y 4. Compile: -SDK$ make -SDK$ make -C config-ui +SDK$ make diablo +SDK$ make -C config-ui diablo-plugin + +(Replace "diablo" with "fremantle" and "diablo-plugin" with +"fremantle-plugin" if compiling for Fremantle. -(If you want the standalone config application instead of the Control +If you want the standalone config application instead of the Control Panel plugin, do -SDK$ make -C config-ui hildon-app +SDK$ make -C config-ui diablo-hildon-app instead. If you're using the Scratchbox2-based SDK+, you want @@ -189,10 +204,10 @@ Source code is hosted in a Git (http://git-scm.com/) repository on git.maemo.org. You can get a copy of the current development version by cloning the repository: -$ git clone https://git.maemo.org/projects/browser-switch +$ git clone http://git.maemo.org/projects/browser-switch or you can browse the source using gitweb -(https://git.maemo.org/projects/browser-switch/?p=browser-switch;a=summary). +(http://git.maemo.org/projects/browser-switch/?p=browser-switch;a=summary). Maintainer: diff --git a/browser b/browser index 334523f..a94b7cd 100755 --- a/browser +++ b/browser @@ -9,11 +9,5 @@ case "$1" in ;; esac -if pidof browser > /dev/null 2>&1; then - method=open_new_window -else - method=switchboard_launch_microb -fi - -dbus-send --session --type=method_call --print-reply --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.$method string:${url:-"new_window"} > /dev/null 2>&1 +dbus-send --session --type=method_call --print-reply --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.open_new_window string:${url:-"new_window"} > /dev/null 2>&1 exit 0 diff --git a/config-ui/Makefile b/config-ui/Makefile index 42f2003..8bc3749 100644 --- a/config-ui/Makefile +++ b/config-ui/Makefile @@ -5,7 +5,7 @@ CPPFLAGS = -I../ `pkg-config --cflags gtk+-2.0` $(EXTRA_CPPFLAGS) CPPFLAGS_HILDON = -DHILDON `pkg-config --cflags hildon-1` CPPFLAGS_PLUGIN = $(CPPFLAGS_HILDON) -DHILDON_CP_APPLET \ `pkg-config --cflags libosso` `pkg-config --cflags hildon-control-panel` -LDFLAGS = `pkg-config --libs gtk+-2.0` $(EXTRA_LDFLAGS) +LDFLAGS = -Wl,--as-needed `pkg-config --libs gtk+-2.0` $(EXTRA_LDFLAGS) LDFLAGS_HILDON = `pkg-config --libs hildon-1` LDFLAGS_PLUGIN = -shared $(LDFLAGS_HILDON) \ `pkg-config --libs libosso` `pkg-config --libs hildon-control-panel` @@ -20,44 +20,55 @@ happ_obj = $(APP).happ.o $(other_obj) PLUGIN = lib$(APP).so plugin_obj = $(APP).plugin.o ../configfile.plugin.o -all: plugin -plugin: $(PLUGIN) +all: + @echo 'Usage:' + @echo ' make app -- build standalone GTK+ application' + @echo ' make diablo-hildon-app -- build standalone Diablo Hildon application' + @echo ' make diablo-plugin -- build Diablo hildon-control-panel plugin' + @echo ' make fremantle-hildon-app -- build standalone Fremantle Hildon application' + @echo ' make fremantle-plugin -- build Fremantle hildon-control-panel plugin' app: $(APP) -hildon-app: $(HILDON_APP) +diablo-hildon-app: $(HILDON_APP) +diablo-plugin: $(PLUGIN) +fremantle-hildon-app: + @$(MAKE) EXTRA_CPPFLAGS='-DFREMANTLE $(EXTRA_CPPFLAGS)' $(HILDON_APP) +fremantle-plugin: + @$(MAKE) EXTRA_CPPFLAGS='-DFREMANTLE $(EXTRA_CPPFLAGS)' $(PLUGIN) $(APP): $(app_obj) - $(CC) $(CFLAGS) $(LDFLAGS) -o $(APP) $(app_obj) + $(CC) $(CFLAGS) -o $(APP) $(app_obj) $(LDFLAGS) %.app.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< $(HILDON_APP): $(happ_obj) - $(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_HILDON) \ - -o $(HILDON_APP) $(happ_obj) + $(CC) $(CFLAGS) -o $(HILDON_APP) $(happ_obj) \ + $(LDFLAGS) $(LDFLAGS_HILDON) %.happ.o: %.c $(CC) $(CFLAGS) $(CPPFLAGS) $(CPPFLAGS_HILDON) -c -o $@ $< $(PLUGIN): $(plugin_obj) - $(CC) $(CFLAGS) $(CFLAGS_PLUGIN) $(LDFLAGS) $(LDFLAGS_PLUGIN) \ - -o $(PLUGIN) $(plugin_obj) + $(CC) $(CFLAGS) $(CFLAGS_PLUGIN) -o $(PLUGIN) $(plugin_obj) \ + $(LDFLAGS) $(LDFLAGS_PLUGIN) + %.plugin.o: %.c $(CC) $(CFLAGS) $(CFLAGS_PLUGIN) $(CPPFLAGS) $(CPPFLAGS_PLUGIN) \ -c -o $@ $< strip: strip-plugin -strip-plugin: plugin +strip-plugin: $(PLUGIN) strip $(PLUGIN) install: install-plugin -install-plugin: plugin +install-plugin: $(PLUGIN) mkdir -p $(DESTDIR)$(PREFIX)/lib/hildon-control-panel mkdir -p $(DESTDIR)$(PREFIX)/share/applications/hildon-control-panel install -c -m 0755 $(PLUGIN) $(DESTDIR)$(PREFIX)/lib/hildon-control-panel install -c -m 0644 $(APP).desktop $(DESTDIR)$(PREFIX)/share/applications/hildon-control-panel clean: - rm -f $(APP) $(HILDON_APP) $(PLUGIN) *.o ../configfile.o + rm -f $(APP) $(HILDON_APP) $(PLUGIN) $(app_obj) $(happ_obj) $(plugin_obj) -.PHONY: strip strip-plugin install install-plugin plugin app hildon-app +.PHONY: strip strip-plugin install install-plugin app diablo-hildon-app diablo-plugin fremantle-hildon-app fremantle-plugin diff --git a/config-ui/browser-switchboard-cp.c b/config-ui/browser-switchboard-cp.c index 13b0b48..c34778b 100644 --- a/config-ui/browser-switchboard-cp.c +++ b/config-ui/browser-switchboard-cp.c @@ -72,6 +72,8 @@ struct browser_entry browsers[] = { { NULL, NULL }, }; +char *logger_name = NULL; + struct config_widgets { #if defined(HILDON) && defined(FREMANTLE) GtkWidget *continuous_mode_selector; @@ -169,20 +171,25 @@ static void load_config(void) { set_continuous_mode(atoi(line.value)); continuous_mode_seen = 1; } + free(line.value); } else if (!strcmp(line.key, "default_browser")) { if (!default_browser_seen) { set_default_browser(line.value); default_browser_seen = 1; } + free(line.value); } else if (!strcmp(line.key, "other_browser_cmd")) { if (!other_browser_cmd_seen) { set_other_browser_cmd(line.value); other_browser_cmd_seen = 1; } + free(line.value); + } else if (!strcmp(line.key, "logging")) { + if (!logger_name) + logger_name = line.value; } } free(line.key); - free(line.value); } parse_config_file_end(); @@ -261,6 +268,15 @@ static void save_config(void) { get_other_browser_cmd()); other_browser_cmd_seen = 1; } + } else if (!strcmp(line.key, + "logging")) { + if (logger_name) { + fprintf(tmpfp, "%s = \"%s\"\n", + line.key, + logger_name); + free(logger_name); + logger_name = NULL; + } } } else { /* Just copy the old line over */ @@ -282,6 +298,9 @@ static void save_config(void) { if (!other_browser_cmd_seen && strlen(get_other_browser_cmd()) > 0) fprintf(tmpfp, "%s = \"%s\"\n", "other_browser_cmd", get_other_browser_cmd()); + if (logger_name) + fprintf(tmpfp, "%s = \"%s\"\n", + "logging", logger_name); /* Replace the old config file with the new one */ fclose(tmpfp); diff --git a/dbus-server-bindings.c b/dbus-server-bindings.c index 3875929..f20a690 100644 --- a/dbus-server-bindings.c +++ b/dbus-server-bindings.c @@ -29,6 +29,7 @@ #include "browser-switchboard.h" #include "launcher.h" #include "dbus-server-bindings.h" +#include "log.h" extern struct swb_context ctx; @@ -67,7 +68,7 @@ static void open_address(const char *uri) { /* Not much to do in this case ... */ return; - printf("open_address '%s'\n", uri); + log_msg("open_address '%s'\n", uri); if (uri[0] == '/') { /* URI begins with a '/' -- assume it points to a local file and prefix with "file://" */ @@ -81,6 +82,15 @@ static void open_address(const char *uri) { we need to clean up after ourselves */ free(new_uri); } else { +#ifdef FREMANTLE + if (!strcmp(uri, "http://link.ovi.mobi/n900ovistore")) { + /* Ovi Store webpage will not open correctly in + any browser other than MicroB, so force the + link in the provided bookmark to open in MicroB */ + launch_microb(&ctx, (char *)uri); + return; + } +#endif launch_browser(&ctx, (char *)uri); } } @@ -117,12 +127,12 @@ gboolean osso_browser_top_application(OssoBrowser *obj, GError **error) { if (!ctx.continuous_mode) ignore_reconfig_requests(); - launch_microb(&ctx, "new_window"); + launch_browser(&ctx, "new_window"); return TRUE; } /* This is a "undocumented", non-standard extension to the API, ONLY - for use by /usr/bin/browser wrapper to implement --url */ + for use by /usr/bin/microb wrapper */ gboolean osso_browser_switchboard_launch_microb(OssoBrowser *obj, const char *uri, GError **error) { if (!ctx.continuous_mode) @@ -146,11 +156,11 @@ void dbus_request_osso_browser_name(struct swb_context *ctx) { G_TYPE_INVALID, G_TYPE_UINT, &result, G_TYPE_INVALID)) { - printf("Couldn't acquire name com.nokia.osso_browser\n"); + log_msg("Couldn't acquire name com.nokia.osso_browser\n"); exit(1); } if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - printf("Couldn't acquire name com.nokia.osso_browser\n"); + log_msg("Couldn't acquire name com.nokia.osso_browser\n"); exit(1); } } diff --git a/launcher.c b/launcher.c index f738c63..77f2ded 100644 --- a/launcher.c +++ b/launcher.c @@ -1,7 +1,7 @@ /* * 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 @@ -23,17 +23,95 @@ #include #include #include +#include #include #include #include +#include +#include #include +#ifdef FREMANTLE +#include +#include +#include +#include + +#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" +#include "log.h" #define LAUNCH_DEFAULT_BROWSER launch_microb +#ifdef FREMANTLE +static int microb_started = 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. */ +static DBusHandlerResult check_microb_started(DBusConnection *connection, + DBusMessage *message, + void *user_data) { + DBusError error; + char *name, *old, *new; + + 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)) { + 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) { + log_msg("MicroB ready\n"); + microb_started = 1; + } + + 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; + + /* 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 atoi(tmp); +} +#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; @@ -43,7 +121,7 @@ static void launch_tear(struct swb_context *ctx, char *uri) { 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 @@ -53,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)) { - 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() */ - printf("child: %d\n", (int)pid); + log_msg("child: %d\n", (int)pid); return; } /* Child process */ setsid(); + close_stdio(); } execl("/usr/bin/tear", "/usr/bin/tear", uri, (char *)NULL); } @@ -79,31 +169,332 @@ void launch_microb(struct swb_context *ctx, char *uri) { int kill_browserd = 0; 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) 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 */ - status = system("pidof /usr/sbin/browserd > /dev/null"); + status = system("pidof browserd > /dev/null"); if (WIFEXITED(status) && WEXITSTATUS(status)) { kill_browserd = 1; - system("/usr/sbin/browserd -d"); +#ifdef FREMANTLE + system("/usr/sbin/browserd -d -b > /dev/null 2>&1"); +#else + 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); +#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) { - perror("fork"); + log_perror(errno, "fork"); exit(1); } + 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. */ + microb_started = 0; + log_msg("Waiting for MicroB to start\n"); + while (!microb_started && + 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='com.nokia.osso_browser'", + &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_connection_close(raw_connection); + dbus_connection_unref(raw_connection); + + /* Browser UI's started, send it the request for a new window + via D-Bus */ + g_proxy = dbus_g_proxy_new_for_name(ctx->session_bus, + "com.nokia.osso_browser", + "/com/nokia/osso_browser/request", + "com.nokia.osso_browser"); + if (!g_proxy) { + log_msg("Couldn't get a com.nokia.osso_browser proxy\n"); + exit(1); + } + if (!strcmp(uri, "new_window")) { +#if 0 /* Since we can't detect when the bookmark window closes, we'd have a + corner case where, if the user just closes the bookmark window + without opening any browser windows, we don't kill off MicroB or + resume handling com.nokia.osso_browser */ + if (!dbus_g_proxy_call(g_proxy, "top_application", + &gerror, G_TYPE_INVALID, + G_TYPE_INVALID)) { + log_msg("Opening window failed: %s\n", + gerror->message); + exit(1); + } +#endif + if (!dbus_g_proxy_call(g_proxy, "load_url", + &gerror, + G_TYPE_STRING, "about:blank", + G_TYPE_INVALID, + G_TYPE_INVALID)) { + log_msg("Opening window failed: %s\n", + gerror->message); + exit(1); + } + } else { + if (!dbus_g_proxy_call(g_proxy, "load_url", + &gerror, + G_TYPE_STRING, uri, + G_TYPE_INVALID, + G_TYPE_INVALID)) { + log_msg("Opening window failed: %s\n", + gerror->message); + 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. + + 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 */ + 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); + } + 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); + } + + /* 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); + } + 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)); + } + } + + /* 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 */ + 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 */ + /* Launch the browser in the background -- our parent will + wait for it to claim the D-Bus name and then display the + window using D-Bus */ + 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 */ + 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 */ @@ -115,10 +506,11 @@ void launch_microb(struct swb_context *ctx, char *uri) { "browser", "--url", uri, (char *)NULL); } } +#endif /* FREMANTLE */ /* 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); @@ -137,7 +529,7 @@ static void launch_other_browser(struct swb_context *ctx, char *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 */ @@ -186,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); - printf("command: '%s'\n", command); + log_msg("command: '%s'\n", command); if (ctx->continuous_mode) { if (fork() != 0) { @@ -198,6 +590,7 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) { } /* Child process */ setsid(); + close_stdio(); } execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL); } @@ -212,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) { - 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, @@ -245,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 { - 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 { - 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; } } diff --git a/log.c b/log.c new file mode 100644 index 0000000..36d0fd5 --- /dev/null +++ b/log.c @@ -0,0 +1,96 @@ +/* + * log.c -- logging functions for browser-switchboard + * + * Copyright (C) 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 + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include +#include +#include +#include + +#include "log.h" + +#define DEFAULT_LOGGER LOGTO_STDOUT +static enum { + LOGTO_NONE, + LOGTO_STDOUT, + LOGTO_SYSLOG, +} logger = DEFAULT_LOGGER; + +/* Configure the logging target, performing any required setup for that + target */ +void log_config(char *logger_name) { + if (!logger_name) { + /* No logger configured, use the default log target */ + logger = DEFAULT_LOGGER; + return; + } + + if (!strcmp(logger_name, "stdout")) + logger = LOGTO_STDOUT; + else if (!strcmp(logger_name, "syslog")) { + /* XXX allow syslog facility to be configured? */ + openlog("browser-switchboard", LOG_PID, LOG_USER); + logger = LOGTO_SYSLOG; + } + else if (!strcmp(logger_name, "none")) + logger = LOGTO_NONE; + else + /* Invalid logger configured, use the default log target */ + logger = DEFAULT_LOGGER; + + return; +} + +/* Log output to the chosen log target */ +void log_msg(const char *format, ...) { + va_list ap; + + if (!format) + return; + + va_start(ap, format); + switch (logger) { + case LOGTO_NONE: + break; + case LOGTO_SYSLOG: + /* XXX allow syslog priority to be set by caller? */ + vsyslog(LOG_DEBUG, format, ap); + break; + case LOGTO_STDOUT: + default: + vprintf(format, ap); + break; + } + va_end(ap); +} + +/* Log strerror(errnum), with the string in prefix appended + Behaves like perror() except that it logs to chosen target, not stderr */ +void log_perror(int errnum, const char *prefix) { + char *errmsg; + + if (!prefix) + return; + if (!(errmsg = strerror(errnum))) + return; + + log_msg("%s: %s\n", prefix, errmsg); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..350dbcc --- /dev/null +++ b/log.h @@ -0,0 +1,30 @@ +/* + * log.h -- definitions for the logging functions + * + * Copyright (C) 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 + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#ifndef _LOG_H +#define _LOG_H 1 + +void log_config(char *logger_name); +void log_msg(const char *format, ...); +void log_perror(int errnum, const char *prefix); + +#endif /* _LOG_H */ diff --git a/main.c b/main.c index 5296631..093b535 100644 --- a/main.c +++ b/main.c @@ -1,7 +1,7 @@ /* * main.c -- config file parsing and main loop 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 @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include "launcher.h" #include "dbus-server-bindings.h" #include "configfile.h" +#include "log.h" struct swb_context ctx; @@ -46,14 +46,14 @@ static void set_config_defaults(struct swb_context *ctx) { static void waitforzombies(int signalnum) { while (waitpid(-1, NULL, WNOHANG) > 0) - printf("Waited for a zombie\n"); + log_msg("Waited for a zombie\n"); } static void read_config(int signalnum) { FILE *fp; int continuous_mode_seen = 0; struct swb_config_line line; - char *default_browser = NULL; + char *default_browser = NULL, *logger_name = NULL; set_config_defaults(&ctx); @@ -78,6 +78,9 @@ static void read_config(int signalnum) { } else if (!strcmp(line.key, "other_browser_cmd")) { if (!ctx.other_browser_cmd) ctx.other_browser_cmd = line.value; + } else if (!strcmp(line.key, "logging")) { + if (!logger_name) + logger_name = line.value; } else { /* Don't need this line's contents */ free(line.value); @@ -87,14 +90,21 @@ static void read_config(int signalnum) { } parse_config_file_end(); - printf("continuous_mode: %d\n", ctx.continuous_mode); - printf("default_browser: '%s'\n", default_browser?default_browser:"NULL"); - printf("other_browser_cmd: '%s'\n", ctx.other_browser_cmd?ctx.other_browser_cmd:"NULL"); - out: fclose(fp); out_noopen: + log_config(logger_name); update_default_browser(&ctx, default_browser); + + log_msg("continuous_mode: %d\n", ctx.continuous_mode); + log_msg("default_browser: '%s'\n", + default_browser?default_browser:"NULL"); + log_msg("other_browser_cmd: '%s'\n", + ctx.other_browser_cmd?ctx.other_browser_cmd:"NULL"); + log_msg("logging: '%s'\n", + logger_name?logger_name:"NULL"); + + free(logger_name); free(default_browser); return; } @@ -103,6 +113,7 @@ int main() { OssoBrowser *obj_osso_browser, *obj_osso_browser_req; GMainLoop *mainloop; GError *error = NULL; + int reqname_result; read_config(0); @@ -115,14 +126,14 @@ int main() { /* SIGCHLD -- clean up after zombies */ act.sa_handler = waitforzombies; if (sigaction(SIGCHLD, &act, NULL) == -1) { - printf("Installing signal handler failed\n"); + log_msg("Installing signal handler failed\n"); return 1; } /* SIGHUP -- reread config file */ act.sa_handler = read_config; if (sigaction(SIGHUP, &act, NULL) == -1) { - printf("Installing signal handler failed\n"); + log_msg("Installing signal handler failed\n"); return 1; } } @@ -135,14 +146,35 @@ int main() { /* Get a connection to the D-Bus session bus */ ctx.session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error); if (!ctx.session_bus) { - printf("Couldn't get a D-Bus bus connection\n"); + log_msg("Couldn't get a D-Bus bus connection\n"); return 1; } ctx.dbus_proxy = dbus_g_proxy_new_for_name(ctx.session_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus"); if (!ctx.dbus_proxy) { - printf("Couldn't get an org.freedesktop.DBus proxy\n"); + log_msg("Couldn't get an org.freedesktop.DBus proxy\n"); + return 1; + } + + /* Get the org.maemo.garage.browser-switchboard name from D-Bus, as + a form of locking to ensure that not more than one + browser-switchboard process is active at any time. With + DBUS_NAME_FLAG_DO_NOT_QUEUE set and DBUS_NAME_FLAG_REPLACE_EXISTING + not set, getting the name succeeds if and only if no other + process owns the name. */ + if (!dbus_g_proxy_call(ctx.dbus_proxy, "RequestName", &error, + G_TYPE_STRING, "org.maemo.garage.browser-switchboard", + G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE, + G_TYPE_INVALID, + G_TYPE_UINT, &reqname_result, + G_TYPE_INVALID)) { + log_msg("Couldn't acquire browser-switchboard lock: %s\n", + error->message); + return 1; + } + if (reqname_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { + log_msg("Another browser-switchboard already running\n"); return 1; } @@ -158,9 +190,9 @@ int main() { G_OBJECT(obj_osso_browser_req)); mainloop = g_main_loop_new(NULL, FALSE); - printf("Starting main loop\n"); + log_msg("Starting main loop\n"); g_main_loop_run(mainloop); - printf("Main loop completed\n"); + log_msg("Main loop completed\n"); return 0; } diff --git a/microb b/microb new file mode 100755 index 0000000..334523f --- /dev/null +++ b/microb @@ -0,0 +1,19 @@ +#!/bin/sh + +case "$1" in + --url=* ) + url="${1#--url=}" + ;; + --url ) + url="$2" + ;; +esac + +if pidof browser > /dev/null 2>&1; then + method=open_new_window +else + method=switchboard_launch_microb +fi + +dbus-send --session --type=method_call --print-reply --dest="com.nokia.osso_browser" /com/nokia/osso_browser/request com.nokia.osso_browser.$method string:${url:-"new_window"} > /dev/null 2>&1 +exit 0 diff --git a/microb.desktop b/microb.desktop new file mode 100644 index 0000000..ecaae47 --- /dev/null +++ b/microb.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Name=MicroB +GenericName=Browser +Comment=weba_ap_web_browser_thumb +Exec=/usr/bin/microb +Icon=qgn_list_browser +Terminal=false +Type=Application +Categories=Application;Internet; +X-HildonDesk-ShowInToolbar=true +X-Osso-Type=application/x-executable