aboutsummaryrefslogtreecommitdiffstats
path: root/src/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon.c')
-rw-r--r--src/daemon.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/daemon.c b/src/daemon.c
new file mode 100644
index 0000000..8e1249a
--- /dev/null
+++ b/src/daemon.c
@@ -0,0 +1,195 @@
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "runes.h"
+#include "daemon.h"
+
+#include "loop.h"
+#include "term.h"
+
+static int runes_daemon_open_socket(char *sock_name);
+static void runes_daemon_close_socket(RunesDaemon *sock);
+static void runes_daemon_socket_accept(void *t);
+static int runes_daemon_handle_request(void *t);
+
+RunesDaemon *runes_daemon_new(RunesLoop *loop, RunesWindowBackendGlobal *wg)
+{
+ RunesDaemon *daemon;
+
+ daemon = calloc(1, sizeof(RunesDaemon));
+ daemon->loop = loop;
+ daemon->wg = wg;
+ daemon->sock_name = runes_get_daemon_socket_name();
+ daemon->sock = runes_daemon_open_socket(daemon->sock_name);
+ runes_daemon_init_loop(daemon, loop);
+
+ return daemon;
+}
+
+void runes_daemon_init_loop(RunesDaemon *daemon, RunesLoop *loop)
+{
+ runes_loop_start_work(loop, daemon, runes_daemon_socket_accept,
+ runes_daemon_handle_request);
+}
+
+void runes_socket_delete(RunesDaemon *daemon)
+{
+ runes_daemon_close_socket(daemon);
+ free(daemon->sock_name);
+
+ free(daemon);
+}
+
+static int runes_daemon_open_socket(char *sock_name)
+{
+ 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", sock_name);
+ }
+
+ dir = strdup(sock_name);
+ slash = strrchr(dir, '/');
+ if (slash == NULL) {
+ runes_die("socket path %s must be an absolute path", sock_name);
+ }
+ *slash = '\0';
+ runes_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", 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", sock_name,
+ strerror(errno));
+ }
+
+ if (chmod(sock_name, S_IRUSR|S_IWUSR)) {
+ runes_die("couldn't chmod socket %s: %s", sock_name,
+ strerror(errno));
+ }
+
+ if (listen(s, 5)) {
+ runes_die("couldn't listen on socket %s: %s", sock_name,
+ strerror(errno));
+ }
+
+ return s;
+}
+
+static void runes_daemon_close_socket(RunesDaemon *daemon)
+{
+ close(daemon->sock);
+ unlink(daemon->sock_name);
+}
+
+static void runes_daemon_socket_accept(void *t)
+{
+ RunesDaemon *daemon = (RunesDaemon *)t;
+ struct sockaddr_un client;
+ socklen_t len = sizeof(client);
+
+ daemon->client_sock = accept(
+ daemon->sock, (struct sockaddr*)(&client), &len);
+}
+
+static int runes_daemon_handle_request(void *t)
+{
+ RunesDaemon *daemon = (RunesDaemon *)t;
+ ssize_t bytes;
+ uint32_t argc, argv_len;
+ char **argv, *argv_buf;
+
+ if (daemon->client_sock < 0) {
+ runes_die("couldn't accept connection: %s", strerror(errno));
+ }
+
+ bytes = recv(daemon->client_sock, (void*)(&argc), sizeof(argc), 0);
+ if (bytes < (int)sizeof(argc)) {
+ runes_warn("invalid message received: got %d bytes, expected 4",
+ bytes);
+ close(daemon->client_sock);
+ return 1;
+ }
+
+ argc = ntohl(argc);
+ if (argc > 1024) {
+ runes_warn("invalid message received: argc = %d, must be < 1024",
+ argc);
+ close(daemon->client_sock);
+ return 1;
+ }
+ argv = malloc(argc * sizeof(char*));
+
+ bytes = recv(daemon->client_sock, (void*)(&argv_len), sizeof(argv_len), 0);
+ if (bytes < (int)sizeof(argc)) {
+ runes_warn("invalid message received: got %d bytes, expected 4",
+ bytes);
+ close(daemon->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",
+ argv_len, 131072);
+ close(daemon->client_sock);
+ free(argv);
+ return 1;
+ }
+ argv_buf = malloc(argv_len + 1);
+
+ bytes = recv(daemon->client_sock, argv_buf, argv_len, 0);
+ if (bytes < argv_len) {
+ runes_warn("invalid message received: got %d bytes, expected %d",
+ bytes, argv_len);
+ close(daemon->client_sock);
+ free(argv);
+ free(argv_buf);
+ return 1;
+ }
+
+ close(daemon->client_sock);
+
+ if (argc > 0) {
+ size_t offset = 0;
+ int i;
+
+ 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", 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", argc);
+ }
+ offset = next_null - argv_buf + 1;
+ }
+
+ runes_term_register_with_loop(
+ runes_term_new(argc, argv, daemon->wg), daemon->loop);
+ }
+
+ free(argv);
+ free(argv_buf);
+
+ return 1;
+}