X-Git-Url: http://git.maemo.org/git/?p=browser-switch;a=blobdiff_plain;f=launcher.c;h=fe57eb94b7640de4f80443d340d5b02101e3871b;hp=0e48d4220edd1c57788b164902385c42046f2aa3;hb=91c97925f7ddac0da4100ea72c5ca17128a93543;hpb=870b63f0602922c0f135d3982f1febfe2706c243 diff --git a/launcher.c b/launcher.c index 0e48d42..fe57eb9 100644 --- a/launcher.c +++ b/launcher.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -32,7 +33,6 @@ #ifdef FREMANTLE #include -#include #include #include #include @@ -45,8 +45,14 @@ #include "browser-switchboard.h" #include "launcher.h" #include "dbus-server-bindings.h" +#include "log.h" -#define LAUNCH_DEFAULT_BROWSER launch_microb +struct browser_launcher { + char *name; + void (*launcher)(struct swb_context *, char *); + char *other_browser_cmd; + char *binary; +}; #ifdef FREMANTLE static int microb_started = 0; @@ -59,21 +65,21 @@ static DBusHandlerResult check_microb_started(DBusConnection *connection, 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)) { - 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) { - printf("MicroB ready\n"); + log_msg("MicroB ready\n"); microb_started = 1; } @@ -120,7 +126,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 @@ -133,10 +139,10 @@ static void launch_tear(struct swb_context *ctx, char *uri) { if (!tear_proxy) { if (!(tear_proxy = dbus_g_proxy_new_for_name( ctx->session_bus, - "com.nokia.tear", + "com.nokia.tear", "/com/nokia/tear", "com.nokia.Tear"))) { - printf("Failed to create proxy for com.nokia.Tear D-Bus interface\n"); + log_msg("Failed to create proxy for com.nokia.Tear D-Bus interface\n"); exit(1); } } @@ -144,7 +150,7 @@ static void launch_tear(struct swb_context *ctx, char *uri) { if (!dbus_g_proxy_call(tear_proxy, "OpenAddress", &error, G_TYPE_STRING, uri, G_TYPE_INVALID, G_TYPE_INVALID)) { - printf("Opening window failed: %s\n", error->message); + log_msg("Opening window failed: %s\n", error->message); exit(1); } if (!ctx->continuous_mode) @@ -153,7 +159,7 @@ static void launch_tear(struct swb_context *ctx, char *uri) { 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 */ @@ -188,7 +194,7 @@ void launch_microb(struct swb_context *ctx, char *uri) { 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 browserd > /dev/null"); @@ -210,7 +216,7 @@ void launch_microb(struct swb_context *ctx, char *uri) { homedir = DEFAULT_HOMEDIR; len = strlen(homedir) + strlen(MICROB_PROFILE_DIR) + 1; if (!(microb_profile_dir = calloc(len, sizeof(char)))) { - printf("calloc() failed\n"); + log_msg("calloc() failed\n"); exit(1); } snprintf(microb_profile_dir, len, "%s%s", @@ -218,7 +224,7 @@ void launch_microb(struct swb_context *ctx, char *uri) { len = strlen(homedir) + strlen(MICROB_PROFILE_DIR) + strlen("/") + strlen(MICROB_LOCKFILE) + 1; if (!(microb_lockfile = calloc(len, sizeof(char)))) { - printf("calloc() failed\n"); + log_msg("calloc() failed\n"); exit(1); } snprintf(microb_lockfile, len, "%s%s/%s", @@ -229,18 +235,51 @@ void launch_microb(struct swb_context *ctx, char *uri) { 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"); + log_perror(errno, "inotify_init"); exit(1); } if ((inot_wd = inotify_add_watch(fd, microb_profile_dir, IN_CREATE)) == -1) { - perror("inotify_add_watch"); + 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); } @@ -249,41 +288,9 @@ void launch_microb(struct swb_context *ctx, char *uri) { /* 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; - 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)); @@ -306,7 +313,7 @@ void launch_microb(struct swb_context *ctx, char *uri) { "/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")) { @@ -317,8 +324,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)) { - printf("Opening window failed: %s\n", - gerror->message); + log_msg("Opening window failed: %s\n", + gerror->message); exit(1); } #endif @@ -327,8 +334,8 @@ void launch_microb(struct swb_context *ctx, char *uri) { 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 { @@ -337,8 +344,8 @@ void launch_microb(struct swb_context *ctx, char *uri) { 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); } } @@ -357,7 +364,7 @@ void launch_microb(struct swb_context *ctx, char *uri) { appreciated. */ /* Wait for the MicroB browserd lockfile to be created */ - printf("Waiting for browserd lockfile to be created\n"); + 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) { @@ -378,44 +385,44 @@ void launch_microb(struct swb_context *ctx, char *uri) { pos += len; else /* All events processed */ - pos = buf + bytes_read; + 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) - printf("Profile lockfile link lacks PID\n"); + log_msg("Profile lockfile link lacks PID\n"); else - printf("readlink() on lockfile failed: %s\n", - strerror(-browserd_pid)); + log_perror(-browserd_pid, + "readlink() on lockfile failed"); exit(1); } free(microb_lockfile); /* Wait for the browserd to close */ - printf("Waiting for MicroB (browserd pid %d) to finish\n", - browserd_pid); + 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) { - perror("clearing SIGCHLD handler failed"); + 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) { - perror("PTRACE_ATTACH"); + log_perror(errno, "PTRACE_ATTACH"); exit(1); } ptrace(PTRACE_CONT, browserd_pid, NULL, NULL); @@ -437,14 +444,14 @@ void launch_microb(struct swb_context *ctx, char *uri) { immediately after we start tracing the process, and won't be followed by a SIGCONT at any point */ - printf("Ignoring first SIGSTOP\n"); + log_msg("Ignoring first SIGSTOP\n"); ptrace(PTRACE_CONT, browserd_pid, NULL, NULL); ignore_sigstop = 0; continue; } - printf("Forwarding signal %d to browserd\n", - WSTOPSIG(status)); + log_msg("Forwarding signal %d to browserd\n", + WSTOPSIG(status)); ptrace(PTRACE_CONT, browserd_pid, NULL, WSTOPSIG(status)); } @@ -455,17 +462,20 @@ void launch_microb(struct swb_context *ctx, char *uri) { 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"); + log_msg("Killing MicroB\n"); kill(pid, SIGTERM); waitpid(pid, &status, 0); /* Restore old SIGCHLD handler */ if (sigaction(SIGCHLD, &oldact, NULL) == -1) { - perror("restoring old SIGCHLD handler failed"); + 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(); @@ -479,7 +489,7 @@ void launch_microb(struct swb_context *ctx, char *uri) { } #else /* !FREMANTLE */ if ((pid = fork()) == -1) { - perror("fork"); + log_perror(errno, "fork"); exit(1); } @@ -524,7 +534,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 */ @@ -573,7 +583,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) { @@ -590,56 +600,83 @@ static void launch_other_browser(struct swb_context *ctx, char *uri) { execl("/bin/sh", "/bin/sh", "-c", command, (char *)NULL); } -/* Use launch_other_browser as the default browser launcher, with the string - passed in as the other_browser_cmd - Resulting other_browser_cmd is always safe to free(), even if a pointer - to a string constant is passed in */ -static void use_other_browser_cmd(struct swb_context *ctx, char *cmd) { - size_t len = strlen(cmd); - - free(ctx->other_browser_cmd); - ctx->other_browser_cmd = calloc(len+1, sizeof(char)); - if (!ctx->other_browser_cmd) { - printf("malloc failed!\n"); - ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER; - } else { - ctx->other_browser_cmd = strncpy(ctx->other_browser_cmd, - cmd, len+1); - ctx->default_browser_launcher = launch_other_browser; + +/* The list of known browsers and how to launch them */ +static struct browser_launcher browser_launchers[] = { + { "microb", launch_microb, NULL, NULL }, /* First entry is the default! */ + { "tear", launch_tear, NULL, "/usr/bin/tear" }, + { "fennec", NULL, "fennec %s", "/usr/bin/fennec" }, + { "opera", NULL, "opera %s", "/usr/bin/opera" }, + { "midori", NULL, "midori %s", "/usr/bin/midori" }, + { NULL, NULL, NULL, NULL }, +}; + +static void use_launcher_as_default(struct swb_context *ctx, + struct browser_launcher *browser) { + if (!ctx || !browser) + return; + + if (browser->launcher) + ctx->default_browser_launcher = browser->launcher; + else if (browser->other_browser_cmd) { + free(ctx->other_browser_cmd); + + /* Make a copy of the string constant so that + ctx->other_browser_cmd is safe to free() */ + ctx->other_browser_cmd = strdup(browser->other_browser_cmd); + if (!ctx->other_browser_cmd) { + log_msg("malloc failed!\n"); + /* Ideally, we'd configure the built-in default here -- + but it's possible we could be called in that path */ + exit(1); + } else + ctx->default_browser_launcher = launch_other_browser; } + + return; } void update_default_browser(struct swb_context *ctx, char *default_browser) { + struct browser_launcher *browser; + if (!ctx) return; - if (!default_browser) { + /* Configure the built-in default to start -- that way, we can + handle errors by just returning */ + use_launcher_as_default(ctx, &browser_launchers[0]); + + if (!default_browser) /* No default_browser configured -- use built-in default */ - ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER; return; - } - if (!strcmp(default_browser, "tear")) - ctx->default_browser_launcher = launch_tear; - else if (!strcmp(default_browser, "microb")) - ctx->default_browser_launcher = launch_microb; - else if (!strcmp(default_browser, "fennec")) - /* Cheat and reuse launch_other_browser, since we don't appear - to need to do anything special */ - use_other_browser_cmd(ctx, "fennec %s"); - else if (!strcmp(default_browser, "midori")) - use_other_browser_cmd(ctx, "midori %s"); - else if (!strcmp(default_browser, "other")) { + /* Go through the list of known browser launchers and use one if + it matches */ + for (browser = browser_launchers; browser->name; ++browser) + if (!strcmp(default_browser, browser->name)) { + /* Make sure the user's choice is installed on the + system */ + if (browser->binary && access(browser->binary, X_OK)) { + log_msg("%s appears not to be installed\n", + default_browser); + } else { + use_launcher_as_default(ctx, browser); + return; + } + } + + /* Deal with default_browser = "other" */ + if (!strcmp(default_browser, "other")) { 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"); - ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER; - } - } else { - printf("Unknown default_browser %s, using default", default_browser); - ctx->default_browser_launcher = LAUNCH_DEFAULT_BROWSER; + else + log_msg("default_browser is 'other', but no other_browser_cmd set -- using default\n"); + return; } + + /* Unknown value of default_browser */ + log_msg("Unknown default_browser %s, using default\n", default_browser); + return; } void launch_browser(struct swb_context *ctx, char *uri) {