From a513d9943fb71607ac8c0f2f3695dec9706f1ebb Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Mon, 2 May 2016 04:21:26 -0400 Subject: start working on a client/server model similar to urxvtd/urxvtc the code is still kind of a mess, and it doesn't quite work properly yet, but it's close enough to be a start, i think --- .gitignore | 2 + Makefile | 23 +++++++- src/runes.h | 2 + src/runesc.c | 66 +++++++++++++++++++++ src/runesd.c | 28 +++++++++ src/socket.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/socket.h | 21 +++++++ src/util.c | 71 +++++++++++++++++++++++ src/util.h | 3 + 9 files changed, 397 insertions(+), 2 deletions(-) create mode 100644 src/runesc.c create mode 100644 src/runesd.c create mode 100644 src/socket.c create mode 100644 src/socket.h diff --git a/.gitignore b/.gitignore index c7267e5..306871e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.o .*.d runes +runesd +runesc diff --git a/Makefile b/Makefile index 271e681..fe7f69d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ OUT = runes +DOUT = runesd +COUT = runesc BUILD = build/ SRC = src/ OBJ = $(BUILD)runes.o \ @@ -9,6 +11,17 @@ OBJ = $(BUILD)runes.o \ $(BUILD)pty-unix.o \ $(BUILD)loop.o \ $(BUILD)util.o +DOBJ = $(BUILD)runesd.o \ + $(BUILD)display.o \ + $(BUILD)term.o \ + $(BUILD)config.o \ + $(BUILD)window-xlib.o \ + $(BUILD)pty-unix.o \ + $(BUILD)loop.o \ + $(BUILD)util.o \ + $(BUILD)socket.o +COBJ = $(BUILD)runesc.o \ + $(BUILD)util.o LIBS = cairo cairo-xlib libuv pangocairo CFLAGS ?= -g -Wall -Wextra -Werror LDFLAGS ?= -g -Wall -Wextra -Werror @@ -18,11 +31,17 @@ ALLLDFLAGS = $(shell pkg-config --libs $(LIBS)) $(LDFLAGS) MAKEDEPEND = $(CC) $(ALLCFLAGS) -M -MP -MT '$@ $(@:$(BUILD)%.o=$(BUILD).%.d)' -build: $(OUT) +build: $(OUT) $(DOUT) $(COUT) $(OUT): $(OBJ) libvt100/libvt100.a $(CC) $(ALLLDFLAGS) -o $@ $^ +$(DOUT): $(DOBJ) libvt100/libvt100.a + $(CC) $(ALLLDFLAGS) -o $@ $^ + +$(COUT): $(COBJ) + $(CC) $(ALLLDFLAGS) -o $@ $^ + libvt100/libvt100.a: cd libvt100 && make static @@ -41,7 +60,7 @@ $(SRC)%.h: $(SRC)%.l clean: cd libvt100 && make clean - rm -f $(OUT) $(OBJ) $(OBJ:$(BUILD)%.o=$(BUILD).%.d) + rm -f $(OUT) $(OBJ) $(OBJ:$(BUILD)%.o=$(BUILD).%.d) $(DOUT) $(DOBJ) $(DOBJ:$(BUILD)%.o=$(BUILD).%.d) $(COUT) $(COBJ) $(COBJ:$(BUILD)%.o=$(BUILD).%.d) @rmdir -p $(BUILD) > /dev/null 2>&1 -include $(OBJ:$(BUILD)%.o=$(BUILD).%.d) diff --git a/src/runes.h b/src/runes.h index 6bdfce1..97a9487 100644 --- a/src/runes.h +++ b/src/runes.h @@ -9,6 +9,7 @@ struct runes_pty; struct runes_config; struct runes_display; struct runes_loop; +struct runes_socket; typedef struct runes_term RunesTerm; typedef struct runes_window RunesWindowBackend; @@ -16,6 +17,7 @@ typedef struct runes_pty RunesPtyBackend; typedef struct runes_config RunesConfig; typedef struct runes_display RunesDisplay; typedef struct runes_loop RunesLoop; +typedef struct runes_socket RunesSocket; #include "util.h" diff --git a/src/runesc.c b/src/runesc.c new file mode 100644 index 0000000..04fc26b --- /dev/null +++ b/src/runesc.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include + +#include "runes.h" +#include "socket.h" + +static int runes_socket_open_client(char *name); + +int main (int argc, char *argv[]) +{ + char *name, *buf; + int s, i; + size_t len, offset; + uint32_t argc32 = argc, argv_len; + + name = runes_get_socket_name(); + s = runes_socket_open_client(name); + + len = sizeof(argc32) + sizeof(argv_len); + for (i = 0; i < argc; ++i) { + len += strlen(argv[i]) + 1; + } + + buf = malloc(len); + ((uint32_t*)buf)[0] = htonl(argc32); + offset = sizeof(argc32) + sizeof(argv_len); + ((uint32_t*)buf)[1] = htonl(len - offset); + + for (i = 0; i < argc; ++i) { + size_t arg_len = strlen(argv[i]) + 1; + memcpy(buf + offset, argv[i], arg_len); + offset += arg_len; + } + + send(s, buf, offset, 0); + free(buf); + shutdown(s, SHUT_RDWR); +} + +static int runes_socket_open_client(char *name) +{ + int s; + struct sockaddr_un client; + + if (strlen(name) + 1 > MAX_SOCKET_PATH_LEN) { + runes_die("socket path %s is too long\n", name); + } + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + runes_die("couldn't create socket: %s\n", strerror(errno)); + } + + client.sun_family = AF_UNIX; + strcpy(client.sun_path, name); + if (connect(s, (struct sockaddr*)(&client), sizeof(struct sockaddr_un))) { + runes_die("couldn't connect to socket at %s: %s\n", name, + strerror(errno)); + } + + return s; +} diff --git a/src/runesd.c b/src/runesd.c new file mode 100644 index 0000000..b24eb6f --- /dev/null +++ b/src/runesd.c @@ -0,0 +1,28 @@ +#include + +#include "runes.h" +#include "socket.h" + +int main (int argc, char *argv[]) +{ + RunesLoop loop; + RunesSocket socket; + + UNUSED(argv); + + if (argc > 1) { + runes_die("runesd takes no arguments; pass them to runesc instead.\n"); + } + + setlocale(LC_ALL, ""); + + runes_loop_init(&loop); + runes_socket_init(&socket, &loop); + + runes_loop_run(&loop); + + runes_socket_cleanup(&socket); + runes_loop_cleanup(&loop); + + return 0; +} diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..24f0260 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include + +#include "runes.h" +#include "socket.h" + +static int runes_socket_open(RunesSocket *sock); +static void runes_socket_close(RunesSocket *sock); +static void runes_socket_accept(RunesTerm *t); +static int runes_socket_handle_request(RunesTerm *t); + +void runes_socket_init(RunesSocket *sock, RunesLoop *loop) +{ + sock->loop = loop; + sock->name = runes_get_socket_name(); + sock->sock = runes_socket_open(sock); + runes_socket_init_loop(sock, loop); +} + +void runes_socket_init_loop(RunesSocket *sock, RunesLoop *loop) +{ + runes_loop_start_work(loop, (RunesTerm *)sock, runes_socket_accept, + runes_socket_handle_request); +} + +void runes_socket_cleanup(RunesSocket *sock) +{ + runes_socket_close(sock); + free(sock->name); +} + +static int runes_socket_open(RunesSocket *sock) +{ + char *dir, *slash; + int s; + struct sockaddr_un server; + + if (strlen(sock->name) + 1 > MAX_SOCKET_PATH_LEN) { + runes_die("socket path %s is too long\n", sock->name); + } + + dir = strdup(sock->name); + slash = strrchr(dir, '/'); + if (slash == NULL) { + runes_die("socket path %s must be an absolute path\n", sock->name); + } + *slash = '\0'; + mkdir_p(dir); + free(dir); + + unlink(sock->name); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + runes_die("couldn't create socket: %s\n", strerror(errno)); + } + + server.sun_family = AF_UNIX; + strcpy(server.sun_path, sock->name); + if (bind(s, (struct sockaddr*)(&server), sizeof(struct sockaddr_un))) { + runes_die("couldn't bind to socket %s: %s\n", sock->name, + strerror(errno)); + } + + if (chmod(sock->name, S_IRUSR|S_IWUSR)) { + runes_die("couldn't chmod socket %s: %s\n", sock->name, + strerror(errno)); + } + + if (listen(s, 5)) { + runes_die("couldn't listen on socket %s: %s\n", sock->name, + strerror(errno)); + } + + return s; +} + +static void runes_socket_close(RunesSocket *sock) +{ + close(sock->sock); + unlink(sock->name); +} + +static void runes_socket_accept(RunesTerm *t) +{ + RunesSocket *sock = (RunesSocket *)t; + struct sockaddr_un client; + socklen_t len = sizeof(client); + + sock->client_sock = accept(sock->sock, (struct sockaddr*)(&client), &len); +} + +static int runes_socket_handle_request(RunesTerm *t) +{ + RunesSocket *sock = (RunesSocket *)t; + ssize_t bytes; + uint32_t argc, argv_len; + char **argv, *argv_buf; + + if (sock->client_sock < 0) { + runes_die("couldn't accept connection: %s", strerror(errno)); + } + + bytes = recv(sock->client_sock, (void*)(&argc), sizeof(argc), 0); + if (bytes < (int)sizeof(argc)) { + runes_warn("invalid message received: got %d bytes, expected 4\n", + bytes); + close(sock->client_sock); + return 1; + } + + argc = ntohl(argc); + if (argc > 1024) { + runes_warn("invalid message received: argc = %d, must be < 1024\n", + argc); + close(sock->client_sock); + return 1; + } + argv = malloc(argc * sizeof(char*)); + + bytes = recv(sock->client_sock, (void*)(&argv_len), sizeof(argv_len), 0); + if (bytes < (int)sizeof(argc)) { + runes_warn("invalid message received: got %d bytes, expected 4\n", + bytes); + close(sock->client_sock); + free(argv); + return 1; + } + + argv_len = ntohl(argv_len); + if (argv_len > 131072) { + runes_warn("invalid message received: argv_len = %d, must be < %d\n", + argv_len, 131072); + close(sock->client_sock); + free(argv); + return 1; + } + argv_buf = malloc(argv_len + 1); + + bytes = recv(sock->client_sock, argv_buf, argv_len, 0); + if (bytes < argv_len) { + runes_warn("invalid message received: got %d bytes, expected %d\n", + bytes, argv_len); + close(sock->client_sock); + free(argv); + free(argv_buf); + return 1; + } + + close(sock->client_sock); + + if (argc > 0) { + size_t offset = 0; + int i; + RunesTerm *t; + + for (i = 0; i < (int)argc; ++i) { + char *next_null; + + if (offset >= argv_len) { + runes_die("args in argv_buf don't match argc of %d\n", argc); + } + argv[i] = argv_buf + offset; + next_null = memchr(argv_buf + offset, '\0', argv_len - offset); + if (!next_null) { + runes_die("args in argv_buf don't match argc of %d\n", argc); + } + offset = next_null - argv_buf + 1; + } + + t = calloc(1, sizeof(RunesTerm)); + runes_term_init(t, sock->loop, argc, argv); + } + + free(argv); + free(argv_buf); + + return 1; +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..e0c5955 --- /dev/null +++ b/src/socket.h @@ -0,0 +1,21 @@ +#ifndef _RUNES_SOCKET_H +#define _RUNES_SOCKET_H + +#include + +struct runes_socket { + RunesLoop *loop; + char *name; + int sock; + int client_sock; +}; + +void runes_socket_init(RunesSocket *sock, RunesLoop *loop); +void runes_socket_init_loop(RunesSocket *sock, RunesLoop *loop); +void runes_socket_send_client_message(int argc, char **argv); +void runes_socket_cleanup(RunesSocket *sock); + +#define MAX_SOCKET_PATH_LEN \ + (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path)) + +#endif diff --git a/src/util.c b/src/util.c index 686f02d..df99149 100644 --- a/src/util.c +++ b/src/util.c @@ -1,6 +1,8 @@ +#include #include #include #include +#include #include "runes.h" @@ -13,6 +15,37 @@ void runes_warn(const char *fmt, ...) va_end(ap); } +void runes_die(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + /* XXX make this do something else on windows */ + vfprintf(stderr, fmt, ap); + va_end(ap); + + exit(1); +} + +char *runes_get_socket_name() +{ + char *home, *runtime_dir, *socket_dir, *socket_file; + + home = getenv("HOME"); + + runtime_dir = getenv("XDG_RUNTIME_DIR"); + if (runtime_dir) { + sprintf_dup(&socket_dir, "%s/runes", runtime_dir); + } + else { + sprintf_dup(&socket_dir, "%s/.runes", home); + } + + sprintf_dup(&socket_file, "%s/runesd", socket_dir); + free(socket_dir); + + return socket_file; +} + int sprintf_dup(char **out, const char *fmt, ...) { int outlen = 0; @@ -30,3 +63,41 @@ int sprintf_dup(char **out, const char *fmt, ...) return outlen; } + +void mkdir_p(char *dir) +{ + char *path_component, *save_ptr, *tok_str, *partial_path; + struct stat st; + + dir = strdup(dir); + partial_path = strdup("/"); + tok_str = dir; + while ((path_component = strtok_r(tok_str, "/", &save_ptr))) { + char *new_path; + + sprintf_dup(&new_path, "%s%s/", partial_path, path_component); + + if (mkdir(new_path, 0755)) { + if (errno != EEXIST) { + runes_die("couldn't create directory %s: %s\n", + new_path, strerror(errno)); + } + } + + free(partial_path); + partial_path = new_path; + tok_str = NULL; + } + + if (stat(partial_path, &st)) { + runes_die("couldn't stat %s: %s\n", partial_path, strerror(errno)); + } + + if (!S_ISDIR(st.st_mode)) { + runes_die("couldn't create directory %s: %s\n", + partial_path, strerror(EEXIST)); + } + + free(partial_path); + free(dir); +} diff --git a/src/util.h b/src/util.h index 9432ed1..b2d0a28 100644 --- a/src/util.h +++ b/src/util.h @@ -4,6 +4,9 @@ #define UNUSED(x) ((void)x) void runes_warn(const char *fmt, ...); +void runes_die(const char *fmt, ...); +char *runes_get_socket_name(); int sprintf_dup(char **out, const char *fmt, ...); +void mkdir_p(char *dir); #endif -- cgit v1.2.3-54-g00ecf