aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Laurence Withers <lwithers@amethyst.(none)>2009-07-22 19:46:43 +0000
committerGravatar Laurence Withers <lwithers@amethyst.(none)>2009-07-22 19:46:43 +0000
commit353472953ada54d5e1b5d235ca472254478d17ad (patch)
treea761128a993525d30acc5950fb133e31e3302aff
parente0a3e5e4ff2b2d038df52936c5ccd4b3b40e198d (diff)
Add talk_to_socket handler
This handler talks directly to a daemon using a Unix SOCK_SEQPACKET socket, allowing e.g. cookie handlers to be implemented without having to fork and and execute an external program or script interpreter. A little explanation of the functioning of talk_to_socket(): 1. We receive our argument as a string in the format of "HANDLER EXTRA_ARGS". 2. Copy "HANDLER" into our Unix socket address structure and connect to that address (blocking). Error out if connection fails. 3. Write "EXTRA_ARGS" to the socket. Error out if writing fails. 4. Use poll() to wait for a response, timing out after 500ms of inactivity. 5. Use ioctl(FIONREAD) to find out how long the pending datagram is. 6. Allocate an appropriately sized buffer (len+1) and write a null at the end, so our output is always null-terminated no matter what. 7. Read from socket into buffer. Error out if read fails. 8. Close socket.
-rw-r--r--uzbl.c114
-rw-r--r--uzbl.h3
2 files changed, 116 insertions, 1 deletions
diff --git a/uzbl.c b/uzbl.c
index 84e496a..77d76e9 100644
--- a/uzbl.c
+++ b/uzbl.c
@@ -54,6 +54,8 @@
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
+#include <poll.h>
+#include <sys/ioctl.h>
#include "uzbl.h"
#include "config.h"
@@ -808,6 +810,7 @@ struct {char *key; CommandInfo value;} cmdlist[] =
{ "sync_spawn", {spawn_sync, 0} }, // needed for cookie handler
{ "sh", {spawn_sh, 0} },
{ "sync_sh", {spawn_sh_sync, 0} }, // needed for cookie handler
+ { "talk_to_socket", {talk_to_socket, TRUE} },
{ "exit", {close_uzbl, 0} },
{ "search", {search_forward_text, TRUE} },
{ "search_reverse", {search_reverse_text, TRUE} },
@@ -1394,6 +1397,114 @@ spawn_sh_sync(WebKitWebView *web_view, GArray *argv, GString *result) {
}
void
+talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result) {
+ (void)web_view; (void)result;
+
+ int fd, len;
+ struct sockaddr_un sa;
+ char* sockpath, * cmd;
+ ssize_t cmd_len, ret;
+ struct pollfd pfd;
+
+ if(uzbl.comm.sync_stdout) uzbl.comm.sync_stdout = strfree(uzbl.comm.sync_stdout);
+
+ /* This function could be optimised by storing a hash table of socket paths
+ and associated connected file descriptors rather than closing and
+ re-opening for every call. Also we could launch a script if socket connect
+ fails. */
+
+ /* Test arguments passed in correctly: argv should be an array of one element,
+ which should be a string consisting of the socket path followed by a space
+ followed by the command to write to the socket. Check that the socket path
+ is valid (starts with '/' and fits in a sockaddr_un.sun_path[]). */
+ if(argv->len != 1) {
+ g_printerr("talk_to_socket called with argv->len %d\n", (int)(argv->len));
+ return;
+ }
+
+ sockpath = g_array_index(argv, char*, 0);
+ cmd = strchr(sockpath, ' ');
+ if(!cmd || *sockpath != '/' || (cmd - sockpath) >= (int)sizeof(sa.sun_path)) {
+ g_printerr("talk_to_socket called incorrectly (%s)\n", sockpath);
+ return;
+ }
+
+ /* copy socket path, null terminate result */
+ memcpy(sa.sun_path, sockpath, (cmd - sockpath));
+ sa.sun_path[cmd - sockpath] = 0;
+ sa.sun_family = AF_UNIX;
+
+ /* point to start of command, find its length in bytes */
+ ++cmd;
+ cmd_len = strlen(cmd);
+
+ /* create socket file descriptor and connect it to path */
+ fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if(fd == -1) {
+ g_printerr("talk_to_socket: creating socket failed (%s)\n", strerror(errno));
+ return;
+ }
+ if(connect(fd, (struct sockaddr*)&sa, sizeof(sa))) {
+ g_printerr("talk_to_socket: connect failed (%s)\n", strerror(errno));
+ close(fd);
+ return;
+ }
+
+ /* write request */
+ ret = write(fd, cmd, cmd_len);
+ if(ret == -1) {
+ g_printerr("talk_to_socket: write failed (%s)\n", strerror(errno));
+ close(fd);
+ return;
+ }
+
+ /* wait for a response, with a 500ms timeout */
+ pfd.fd = fd;
+ pfd.events = POLLIN;
+ while(1) {
+ ret = poll(&pfd, 1, 500);
+ if(ret == 1) break;
+ if(ret == 0) errno = ETIMEDOUT;
+ if(errno == EINTR) continue;
+ g_printerr("talk_to_socket: poll failed while waiting for input (%s)\n",
+ strerror(errno));
+ close(fd);
+ return;
+ }
+
+ /* get length of response */
+ if(ioctl(fd, FIONREAD, &len) == -1) {
+ g_printerr("talk_to_socket: cannot find daemon response length, "
+ "ioctl failed (%s)\n", strerror(errno));
+ close(fd);
+ return;
+ }
+
+ /* if there is a response, read it */
+ if(len) {
+ uzbl.comm.sync_stdout = g_malloc(len + 1);
+ if(!uzbl.comm.sync_stdout) {
+ g_printerr("talk_to_socket: failed to allocate %d bytes\n", len);
+ close(fd);
+ return;
+ }
+ uzbl.comm.sync_stdout[len] = 0; /* ensure result is null terminated */
+
+ ret = read(fd, uzbl.comm.sync_stdout, len);
+ if(ret == -1) {
+ g_printerr("talk_to_socket: failed to read from socket (%s)\n",
+ strerror(errno));
+ close(fd);
+ return;
+ }
+ }
+
+ /* clean up */
+ close(fd);
+ return;
+}
+
+void
parse_command(const char *cmd, const char *param, GString *result) {
CommandInfo *c;
@@ -2251,7 +2362,8 @@ inject_handler_args(const gchar *actname, const gchar *origargs, const gchar *ne
if ((g_strcmp0(actname, "spawn") == 0) ||
(g_strcmp0(actname, "sh") == 0) ||
(g_strcmp0(actname, "sync_spawn") == 0) ||
- (g_strcmp0(actname, "sync_sh") == 0)) {
+ (g_strcmp0(actname, "sync_sh") == 0) ||
+ (g_strcmp0(actname, "talk_to_socket") == 0)) {
guint i;
GString *a = g_string_new("");
gchar **spawnparts = split_quoted(origargs, FALSE);
diff --git a/uzbl.h b/uzbl.h
index c9d81b2..4b0a98b 100644
--- a/uzbl.h
+++ b/uzbl.h
@@ -338,6 +338,9 @@ char*
build_progressbar_ascii(int percent);
void
+talk_to_socket(WebKitWebView *web_view, GArray *argv, GString *result);
+
+void
spawn(WebKitWebView *web_view, GArray *argv, GString *result);
void