diff --git a/Bugs.md b/Bugs.md index 7845cca..f39de1c 100644 --- a/Bugs.md +++ b/Bugs.md @@ -94,6 +94,6 @@ difficult to reproduce. * github issue number : N/A * Fixed: 1.2.1 -Our fuzzing framework up to and including release 1.2.0 does not the limit line +Our fuzzing framework up to and including release 1.2.0 does not limit line lengths. This can crash the fuzzing framework with a segfault due to running out of memory. diff --git a/README.md b/README.md index 9e47706..19a1f02 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,9 @@ Release checklist * [ ] Update signature of the binary * [ ] Signature * [ ] Branching Strategy + * [ ] git archive --prefix=cagebreak/ -o release_version.tar.gz tags/version . + * [ ] gpg --detach-sign -u keyid release_version.tar.gz + * [ ] upload build assets ### Signing Key @@ -180,6 +183,11 @@ ninja -C build For every release after 1.0.5, hashes will be provided. +1.3.0 + + * sha 256: aecc5292f56f7250d777110cee1f11a92018a9eb2117d7d37cd51d7d3a5cbae0 + * sha 512: 1d2d220f44787a97359a32f305e33185f0369551e61a9d7066023bfa43de50849eba5bcbb0083d2dc563ac993b8d51dd733afa0adb2a823ef50ecfee5624bbc0 + 1.2.1 * sha 256: 803f7667dc4997062f9ec95afcdca0cac68c82a7cf057cef83fe1ccfee33b8bc @@ -256,6 +264,10 @@ Unifies commands and actions. See Issue 4 in Bugs.md. Adds output configuration as described in the man pages. +### Release 1.3.0 + +Adds IPC as described in the man pages. + ## License Please see [LICENSE](https://github.com/project-repo/cagebreak/blob/master/LICENSE) diff --git a/cagebreak.c b/cagebreak.c index 223beae..f908117 100644 --- a/cagebreak.c +++ b/cagebreak.c @@ -47,6 +47,7 @@ #endif #include "idle_inhibit_v1.h" +#include "ipc_server.h" #include "keybinding.h" #include "message.h" #include "output.h" @@ -189,16 +190,19 @@ set_configuration(struct cg_server *server, config_file_path); return -1; } - size_t max_line_size = 256; - char line[max_line_size * sizeof(char)]; + char line[MAX_LINE_SIZE * sizeof(char)]; for(unsigned int line_num = 1; - fgets(line, max_line_size, config_file) != NULL; ++line_num) { + fgets(line, MAX_LINE_SIZE, config_file) != NULL; ++line_num) { line[strcspn(line, "\n")] = '\0'; if(*line != '\0' && *line != '#') { - if(parse_rc_line(server, line) != 0) { + char *errstr; + if(parse_rc_line(server, line, &errstr) != 0) { wlr_log(WLR_ERROR, "Error in config file \"%s\", line %d\n", config_file_path, line_num); fclose(config_file); + if(errstr != NULL) { + free(errstr); + } return -1; } } @@ -289,7 +293,7 @@ main(int argc, char *argv[]) { if(server.modes[0] == NULL || server.modes[1] == NULL || server.modes[2] == NULL) { wlr_log(WLR_ERROR, "Error allocating default modes"); - goto end; + return 1; } server.nws = 1; @@ -512,6 +516,12 @@ main(int argc, char *argv[]) { wlr_xwayland_set_seat(xwayland, server.seat->seat); #endif + if(ipc_init(&server) != 0) { + wlr_log(WLR_ERROR, "Failed to initialize IPC"); + ret = 1; + goto end; + } + { // config_file should only be visible as long as it is valid char *config_file = get_config_file(); if(config_file == NULL) { @@ -534,7 +544,7 @@ main(int argc, char *argv[]) { } } - /* Place the cursor to the topl left of the output layout. */ + /* Place the cursor to the top left of the output layout. */ wlr_cursor_warp(server.seat->cursor, NULL, 0, 0); wl_display_run(server.wl_display); diff --git a/fuzz/fuzz-parse.c b/fuzz/fuzz-parse.c index 2434feb..45e74da 100644 --- a/fuzz/fuzz-parse.c +++ b/fuzz/fuzz-parse.c @@ -406,7 +406,11 @@ set_configuration(struct cg_server *server, char *content) { (line = strtok_r(NULL, "\n", &content)) != NULL; ++line_num) { line[strcspn(line, "\n")] = '\0'; if(*line != '\0' && *line != '#') { - if(parse_rc_line(server, line) != 0) { + char *errstr; + if(parse_rc_line(server, line, &errstr) != 0) { + if(errstr != NULL) { + free(errstr); + } return -1; } } diff --git a/ipc_server.c b/ipc_server.c new file mode 100644 index 0000000..a7c6f21 --- /dev/null +++ b/ipc_server.c @@ -0,0 +1,277 @@ +/* + * Cagebreak: A Wayland tiling compositor. + * + * Copyright (C) 2020 The Cagebreak Authors + * Copyright (C) 2018-2020 Jente Hidskes + * + * See the LICENSE file accompanying this file. + */ + +#define _POSIX_C_SOURCE 200112L + +#include "ipc_server.h" +#include "message.h" +#include "parse.h" +#include "server.h" + +#include +#include +#include +#include +#include +#include +#include + +static void +handle_display_destroy(struct wl_listener *listener, void *data) { + struct cg_ipc_handle *ipc = wl_container_of(listener, ipc, display_destroy); + if(ipc->event_source != NULL) { + wl_event_source_remove(ipc->event_source); + } + close(ipc->socket); + unlink(ipc->sockaddr->sun_path); + + struct cg_ipc_client *tmp_client, *client; + wl_list_for_each_safe(client, tmp_client, &ipc->client_list, link) { + ipc_client_disconnect(client); + } + + free(ipc->sockaddr); + + wl_list_remove(&ipc->display_destroy.link); +} + +int +ipc_init(struct cg_server *server) { + struct cg_ipc_handle *ipc = &server->ipc; + ipc->socket = socket(AF_UNIX, SOCK_STREAM, 0); + if(ipc->socket == -1) { + wlr_log(WLR_ERROR, "Unable to create IPC socket"); + return -1; + } + if(fcntl(ipc->socket, F_SETFD, FD_CLOEXEC) == -1) { + wlr_log(WLR_ERROR, "Unable to set CLOEXEC on IPC socket"); + return -1; + } + if(fcntl(ipc->socket, F_SETFL, O_NONBLOCK) == -1) { + wlr_log(WLR_ERROR, "Unable to set NONBLOCK on IPC socket"); + return -1; + } + + ipc->sockaddr = malloc(sizeof(struct sockaddr_un)); + + if(ipc->sockaddr == NULL) { + wlr_log(WLR_ERROR, "Unable to allocate socket address"); + return -1; + } + + ipc->sockaddr->sun_family = AF_UNIX; + int max_path_size = sizeof(ipc->sockaddr->sun_path); + const char *sockdir = getenv("XDG_RUNTIME_DIR"); + if(sockdir == NULL) { + sockdir = "/tmp"; + } + + if(max_path_size <= snprintf(ipc->sockaddr->sun_path, max_path_size, + "%s/cagebreak-ipc.%i.%i.sock", sockdir, + getuid(), getpid())) { + wlr_log(WLR_ERROR, "Unable to write socket path to " + "ipc->sockaddr->sun_path. Path too long"); + free(ipc->sockaddr); + return -1; + } + + unlink(ipc->sockaddr->sun_path); + + if(bind(ipc->socket, (struct sockaddr *)ipc->sockaddr, + sizeof(*ipc->sockaddr)) == -1) { + wlr_log(WLR_ERROR, "Unable to bind IPC socket"); + free(ipc->sockaddr); + return -1; + } + + if(listen(ipc->socket, 3) == -1) { + wlr_log(WLR_ERROR, "Unable to listen on IPC socket"); + free(ipc->sockaddr); + return -1; + } + + setenv("CAGEBREAK_SOCKET", ipc->sockaddr->sun_path, 1); + + wl_list_init(&ipc->client_list); + + ipc->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(server->wl_display, &ipc->display_destroy); + + ipc->event_source = + wl_event_loop_add_fd(server->event_loop, ipc->socket, WL_EVENT_READABLE, + ipc_handle_connection, server); + return 0; +} + +int +ipc_handle_connection(int fd, uint32_t mask, void *data) { + (void)fd; + struct cg_server *server = data; + struct cg_ipc_handle *ipc = &server->ipc; + if(mask != WL_EVENT_READABLE) { + wlr_log(WLR_ERROR, "Expected to receive a WL_EVENT_READABLE"); + return 0; + } + + int client_fd = accept(ipc->socket, NULL, NULL); + if(client_fd == -1) { + wlr_log(WLR_ERROR, "Unable to accept IPC client connection"); + return 0; + } + + int flags; + if((flags = fcntl(client_fd, F_GETFD)) == -1 || + fcntl(client_fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + wlr_log(WLR_ERROR, "Unable to set CLOEXEC on IPC client socket"); + close(client_fd); + return 0; + } + if((flags = fcntl(client_fd, F_GETFL)) == -1 || + fcntl(client_fd, F_SETFL, flags | O_NONBLOCK) == -1) { + wlr_log(WLR_ERROR, "Unable to set NONBLOCK on IPC client socket"); + close(client_fd); + return 0; + } + + struct cg_ipc_client *client = malloc(sizeof(struct cg_ipc_client)); + if(!client) { + wlr_log(WLR_ERROR, "Unable to allocate ipc client"); + close(client_fd); + return 0; + } + // +1 for \n and +1 for \0 + client->read_buffer = malloc(sizeof(char) * (MAX_LINE_SIZE + 2)); + client->read_buf_len = 0; + client->read_discard = 0; + client->server = server; + client->fd = client_fd; + client->event_source = + wl_event_loop_add_fd(server->event_loop, client_fd, WL_EVENT_READABLE, + ipc_client_handle_readable, client); + client->writable_event_source = NULL; + + client->write_buffer_size = 128; + client->write_buffer_len = 0; + client->write_buffer = malloc(client->write_buffer_size); + if(!client->write_buffer) { + wlr_log(WLR_ERROR, "Unable to allocate ipc client write buffer"); + close(client_fd); + return 0; + } + + wl_list_insert(&ipc->client_list, &client->link); + return 0; +} + +int +ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { + struct cg_ipc_client *client = data; + + if(mask & WL_EVENT_ERROR) { + wlr_log(WLR_ERROR, "IPC Client socket error, removing client"); + ipc_client_disconnect(client); + return 0; + } + + if(mask & WL_EVENT_HANGUP) { + ipc_client_disconnect(client); + return 0; + } + + int read_available; + if(ioctl(client_fd, FIONREAD, &read_available) < 0) { + wlr_log(WLR_ERROR, "Unable to read IPC socket buffer size"); + ipc_client_disconnect(client); + return 0; + } + + int read_size = read_available < MAX_LINE_SIZE + 1 - client->read_buf_len + ? read_available + : MAX_LINE_SIZE + 1 - client->read_buf_len; + // Append to buffer + ssize_t received = recv( + client_fd, client->read_buffer + client->read_buf_len, read_size, 0); + if(received == -1) { + wlr_log(WLR_ERROR, "Unable to receive data from IPC client"); + ipc_client_disconnect(client); + return 0; + } + client->read_buf_len += received; + + ipc_client_handle_command(client); + + return 0; +} + +void +ipc_client_disconnect(struct cg_ipc_client *client) { + if(client == NULL) { + wlr_log(WLR_ERROR, + "Client \"NULL\" was passed to ipc_client_disconnect"); + return; + } + + shutdown(client->fd, SHUT_RDWR); + + wl_event_source_remove(client->event_source); + if(client->writable_event_source) { + wl_event_source_remove(client->writable_event_source); + } + wl_list_remove(&client->link); + free(client->write_buffer); + free(client->read_buffer); + close(client->fd); + free(client); +} + +void +ipc_client_handle_command(struct cg_ipc_client *client) { + if(client == NULL) { + wlr_log(WLR_ERROR, + "Client \"NULL\" was passed to ipc_client_handle_command"); + return; + } + client->read_buffer[client->read_buf_len] = '\0'; + char *nl_pos; + uint32_t offset = 0; + while((nl_pos = strchr(client->read_buffer + offset, '\n')) != NULL) { + if(client->read_discard) { + client->read_discard = 0; + } else { + *nl_pos = '\0'; + char *line = client->read_buffer + offset; + if(*line != '\0' && *line != '#') { + message_clear(client->server->curr_output); + char *errstr; + if(parse_rc_line(client->server, line, &errstr) != 0) { + if(errstr != NULL) { + message_printf(client->server->curr_output, "%s", + errstr); + wlr_log(WLR_ERROR, "%s", errstr); + free(errstr); + } + wlr_log(WLR_ERROR, "Error parsing input from IPC socket"); + } + } + } + offset = (nl_pos - client->read_buffer) + 1; + } + if(offset == 0) { + wlr_log(WLR_ERROR, "Line received was longer that %d, discarding it", + MAX_LINE_SIZE); + client->read_buf_len = 0; + client->read_discard = 1; + return; + } + if(offset < client->read_buf_len) { + memmove(client->read_buffer, client->read_buffer + offset, + client->read_buf_len - offset); + } + client->read_buf_len -= offset; +} diff --git a/ipc_server.h b/ipc_server.h new file mode 100644 index 0000000..5e66ea4 --- /dev/null +++ b/ipc_server.h @@ -0,0 +1,50 @@ +#ifndef CG_IPC_SERVER_H +#define CG_IPC_SERVER_H + +#include "config.h" + +#include +#include +#include + +struct cg_server; + +struct cg_ipc_client { + struct wl_event_source *event_source; + struct wl_event_source *writable_event_source; + struct cg_server *server; + struct wl_list link; + int fd; + uint32_t security_policy; + size_t write_buffer_len; + size_t write_buffer_size; + char *write_buffer; + // The following is for storing data between event_loop calls + uint16_t read_buf_len; + uint8_t read_discard; // 1 if the current line is to be discarded + char *read_buffer; +}; + +struct cg_ipc_handle { + int socket; + struct wl_event_source *event_source; + struct wl_list client_list; + struct wl_listener display_destroy; + struct sockaddr_un *sockaddr; +}; + +int +ipc_init(struct cg_server *server); +int +ipc_handle_connection(int fd, uint32_t mask, void *data); +int +ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); +// int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); +void +ipc_client_disconnect(struct cg_ipc_client *client); +void +ipc_client_handle_command(struct cg_ipc_client *client); +// bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t +// payload_length); + +#endif diff --git a/man/cagebreak-config.5.md b/man/cagebreak-config.5.md index ed82205..96aff5f 100644 --- a/man/cagebreak-config.5.md +++ b/man/cagebreak-config.5.md @@ -1,4 +1,4 @@ -% CAGEBREAK-CONFIG(1) Version 1.2.1 | Cagebreak Manual +% CAGEBREAK-CONFIG(1) Version 1.3.0 | Cagebreak Manual # NAME diff --git a/man/cagebreak.1.md b/man/cagebreak.1.md index a2731d3..c72323f 100644 --- a/man/cagebreak.1.md +++ b/man/cagebreak.1.md @@ -1,4 +1,4 @@ -% CAGEBREAK(1) Version 1.2.1 | Cagebreak Manual +% CAGEBREAK(1) Version 1.3.0 | Cagebreak Manual # NAME @@ -18,10 +18,19 @@ in contrast to the original `ratpoison`, has native support for multi-workspace operation. All interactions between the user and `cagebreak` are done via -the keyboard. Configuration of this behaviour is specified -in the **\$XDG_CONFIG_PATH/cagebreak/config** file (See **cagebreak-config(5)**). +the keyboard. -## OPTIONS +Configuration of this behaviour is specified in the +**\$XDG_CONFIG_PATH/cagebreak/config** file (See **cagebreak-config(5)**). + +Scripting support is provided through the IPC +socket specified in the environment variable **\$CAGEBREAK_SOCKET**. +The syntax accepted through this socket is identical to +that of the configuration file (see **cagebreak-config(5)**). +Errors which occur during interaction over IPC channel +are displayed in a message box at the top right of the screen. + +# OPTIONS -h @@ -37,6 +46,11 @@ in the **\$XDG_CONFIG_PATH/cagebreak/config** file (See **cagebreak-config(5)**) # ENVIRONMENT +`CAGEBREAK_SOCKET` + +: The IPC unix domain socket address accepting +commands as specified in **cagebreak-config(5)** + `XKB_DEFAULT_LAYOUT` : The keyboard layout to be used (See **xkeyboard-config(7)**) diff --git a/meson.build b/meson.build index 4c8643b..2a2d0ba 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('cagebreak', 'c', - version: '1.2.1', + version: '1.3.0', license: 'MIT', default_options: [ 'c_std=c11', @@ -123,6 +123,7 @@ conf_data.set_quoted('CG_VERSION', version) cagebreak_main_file = [ 'cagebreak.c', ] cagebreak_source_strings = [ 'idle_inhibit_v1.c', + 'ipc_server.c', 'keybinding.c', 'workspace.c', 'output.c', @@ -139,6 +140,7 @@ cagebreak_source_strings = [ cagebreak_header_strings = [ 'idle_inhibit_v1.h', + 'ipc_server.h', 'keybinding.h', 'workspace.h', 'output.h', diff --git a/parse.c b/parse.c index 82cc0f2..1cdcd0c 100644 --- a/parse.c +++ b/parse.c @@ -11,12 +11,35 @@ #include "server.h" #include "workspace.h" +char * +malloc_vsprintf(const char *fmt, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + int len = vsnprintf(NULL, 0, fmt, ap); + char *ret = malloc(sizeof(char) * (len + 1)); + vsnprintf(ret, len + 1, fmt, ap2); + va_end(ap2); + return ret; +} + +char * +log_error(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char *ret = malloc_vsprintf(fmt, args); + if(ret != NULL) { + wlr_log(WLR_ERROR, "%s", ret); + } + va_end(args); + return ret; +} + /* parses a key definition (e.g. "S-Tab") and sets key and modifiers in * keybinding respectivly */ int -parse_key(struct keybinding *keybinding, const char *key_def) { +parse_key(struct keybinding *keybinding, const char *key_def, char **errstr) { if(key_def == NULL) { - wlr_log(WLR_ERROR, "Expected key definition, got nothing."); + *errstr = log_error("Expected key definition, got nothing."); return -1; } keybinding->modifiers = 0; @@ -44,14 +67,14 @@ parse_key(struct keybinding *keybinding, const char *key_def) { keybinding->modifiers |= WLR_MODIFIER_MOD5; break; default: - wlr_log(WLR_ERROR, "Unknown modifier \"%c\"", key_def[0]); + *errstr = log_error("Unknown modifier \"%c\"", key_def[0]); return -1; } key_def += 2; } xkb_keysym_t keysym = xkb_keysym_from_name(key_def, XKB_KEYSYM_NO_FLAGS); if(keysym == XKB_KEY_NoSymbol) { - wlr_log(WLR_ERROR, "Could not convert key \"%s\" to keysym.", key_def); + *errstr = log_error("Could not convert key \"%s\" to keysym.", key_def); return -1; } keybinding->key = keysym; @@ -60,25 +83,25 @@ parse_key(struct keybinding *keybinding, const char *key_def) { int parse_command(struct cg_server *server, struct keybinding *keybinding, - char *saveptr); + char *saveptr, char **errstr); /* Parse a keybinding definition and return it if successful, else return NULL */ struct keybinding * -parse_keybinding(struct cg_server *server, char **saveptr) { +parse_keybinding(struct cg_server *server, char **saveptr, char **errstr) { struct keybinding *keybinding = malloc(sizeof(struct keybinding)); if(keybinding == NULL) { - wlr_log(WLR_ERROR, - "Failed to allocate memory for keybinding in parse_keybinding"); + *errstr = log_error( + "Failed to allocate memory for keybinding in parse_keybinding"); return NULL; } char *key = strtok_r(NULL, " ", saveptr); - if(parse_key(keybinding, key) != 0) { + if(parse_key(keybinding, key, errstr) != 0) { wlr_log(WLR_ERROR, "Could not parse key definition \"%s\"", key); free(keybinding); return NULL; } - if(parse_command(server, keybinding, *saveptr) != 0) { + if(parse_command(server, keybinding, *saveptr, errstr) != 0) { free(keybinding); return NULL; } @@ -86,8 +109,8 @@ parse_keybinding(struct cg_server *server, char **saveptr) { } struct keybinding * -parse_bind(struct cg_server *server, char **saveptr) { - struct keybinding *keybinding = parse_keybinding(server, saveptr); +parse_bind(struct cg_server *server, char **saveptr, char **errstr) { + struct keybinding *keybinding = parse_keybinding(server, saveptr, errstr); if(keybinding == NULL) { wlr_log(WLR_ERROR, "Could not parse keybinding for \"bind\"."); return NULL; @@ -97,18 +120,19 @@ parse_bind(struct cg_server *server, char **saveptr) { } struct keybinding * -parse_definekey(struct cg_server *server, char **saveptr) { +parse_definekey(struct cg_server *server, char **saveptr, char **errstr) { char *mode = strtok_r(NULL, " ", saveptr); if(mode == NULL) { - wlr_log(WLR_ERROR, "Too few arguments to \"definekey\". Expected mode"); + *errstr = + log_error("Too few arguments to \"definekey\". Expected mode"); return NULL; } int mode_idx = get_mode_index_from_name(server->modes, mode); if(mode_idx == -1) { - wlr_log(WLR_ERROR, "Unknown mode \"%s\"", mode); + *errstr = log_error("Unknown mode \"%s\"", mode); return NULL; } - struct keybinding *keybinding = parse_keybinding(server, saveptr); + struct keybinding *keybinding = parse_keybinding(server, saveptr, errstr); if(keybinding == NULL) { wlr_log(WLR_ERROR, "Could not parse keybinding for \"definekey\""); return NULL; @@ -118,16 +142,16 @@ parse_definekey(struct cg_server *server, char **saveptr) { } int -parse_background(struct cg_server *server, float *color, char **saveptr) { +parse_background(struct cg_server *server, float *color, char **saveptr, + char **errstr) { /* Read rgb numbers */ for(unsigned int i = 0; i < 3; ++i) { char *nstr = strtok_r(NULL, " \n", saveptr); int nstrlen; if(nstr == NULL || (nstrlen = strlen(nstr)) == 0) { - wlr_log(WLR_ERROR, - "Expected three space-separated numbers (rgb) for " - "background color setting. Got %d.", - i); + *errstr = log_error("Expected three space-separated numbers (rgb) " + "for background color setting. Got %d.", + i); return -1; } if(nstr[nstrlen - 1] == '\n') { @@ -137,17 +161,15 @@ parse_background(struct cg_server *server, float *color, char **saveptr) { char *endptr = NULL; float nval = strtof(nstr, &endptr); if(endptr != nstr + nstrlen) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Could not parse number \"%s\" for background color setting.", nstr); return -1; } if(nval < 0 || nval > 1) { - wlr_log(WLR_ERROR, - "Expected a number between 0 and 1 for setting of " - "background color. Got %f.", - nval); + *errstr = log_error("Expected a number between 0 and 1 for setting " + "of background color. Got %f.", + nval); return -1; } color[i] = nval; @@ -156,15 +178,15 @@ parse_background(struct cg_server *server, float *color, char **saveptr) { } struct keybinding * -parse_escape(char **saveptr) { +parse_escape(char **saveptr, char **errstr) { struct keybinding *keybinding = malloc(sizeof(struct keybinding)); if(keybinding == NULL) { - wlr_log(WLR_ERROR, - "Failed to allocate memory for keybinding in parse_escape"); + *errstr = log_error( + "Failed to allocate memory for keybinding in parse_escape"); return NULL; } char *key = strtok_r(NULL, " ", saveptr); - if(parse_key(keybinding, key) != 0) { + if(parse_key(keybinding, key, errstr) != 0) { wlr_log(WLR_ERROR, "Could not parse key definition \"%s\" for \"escape\"", key); free(keybinding); @@ -177,27 +199,27 @@ parse_escape(char **saveptr) { } char * -parse_definemode(char **saveptr) { +parse_definemode(char **saveptr, char **errstr) { char *mode = strtok_r(NULL, " ", saveptr); if(mode == NULL) { - wlr_log(WLR_ERROR, "Expected mode to succeed \"definemode\" keyword."); + *errstr = log_error("Expected mode to succeed \"definemode\" keyword."); return NULL; } return strdup(mode); } int -parse_workspaces(char **saveptr) { +parse_workspaces(char **saveptr, char **errstr) { char *nws_str = strtok_r(NULL, " ", saveptr); if(nws_str == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"workspaces\" command, got none."); + *errstr = log_error( + "Expected argument for \"workspaces\" command, got none."); return -1; } long nws = strtol(nws_str, NULL, 10); if(!(1 <= nws && nws <= 30)) { - wlr_log(WLR_ERROR, - "More than 30 workspaces are not supported. Received %li", nws); + *errstr = log_error( + "More than 30 workspaces are not supported. Received %li", nws); return -1; } return nws; @@ -240,23 +262,23 @@ parse_float(char **saveptr, const char *delim) { } int -parse_output_config(struct wl_list *config_list, char **saveptr) { +parse_output_config(struct wl_list *config_list, char **saveptr, + char **errstr) { struct cg_output_config *cfg = malloc(sizeof(struct cg_output_config)); if(cfg == NULL) { - wlr_log(WLR_ERROR, - "Failed to allocate memory for output configuration"); + *errstr = + log_error("Failed to allocate memory for output configuration"); goto error; } char *name = strtok_r(NULL, " ", saveptr); if(name == NULL) { - wlr_log(WLR_ERROR, - "Expected name of output to be configured, got none"); + *errstr = + log_error("Expected name of output to be configured, got none"); goto error; } char *pos_str = strtok_r(NULL, " ", saveptr); if(pos_str == NULL || strcmp(pos_str, "pos") != 0) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Expected keyword \"pos\" in output configuration for output %s", name); goto error; @@ -264,8 +286,7 @@ parse_output_config(struct wl_list *config_list, char **saveptr) { cfg->pos.x = parse_uint(saveptr, " "); if(cfg->pos.x < 0) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Error parsing x coordinate of output configuration for output %s", name); goto error; @@ -273,8 +294,7 @@ parse_output_config(struct wl_list *config_list, char **saveptr) { cfg->pos.y = parse_uint(saveptr, " "); if(cfg->pos.y < 0) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Error parsing y coordinate of output configuration for output %s", name); goto error; @@ -282,8 +302,7 @@ parse_output_config(struct wl_list *config_list, char **saveptr) { char *res_str = strtok_r(NULL, " ", saveptr); if(res_str == NULL || strcmp(res_str, "res") != 0) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Expected keyword \"res\" in output configuration for output %s", name); goto error; @@ -291,26 +310,23 @@ parse_output_config(struct wl_list *config_list, char **saveptr) { cfg->pos.width = parse_uint(saveptr, "x"); if(cfg->pos.width <= 0) { - wlr_log(WLR_ERROR, - "Error parsing width of output configuration for output %s " - "(hint: width must be larger than 0)", - name); + *errstr = log_error("Error parsing width of output configuration for " + "output %s (hint: width must be larger than 0)", + name); goto error; } cfg->pos.height = parse_uint(saveptr, " "); if(cfg->pos.height <= 0) { - wlr_log(WLR_ERROR, - "Error parsing height of output configuration for output %s " - "(hint: height must e larger than 0)", - name); + *errstr = log_error("Error parsing height of output configuration for " + "output %s (hint: height must e larger than 0)", + name); goto error; } char *rate_str = strtok_r(NULL, " ", saveptr); if(rate_str == NULL || strcmp(rate_str, "rate") != 0) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Expected keyword \"rate\" in output configuration for output %s", name); goto error; @@ -318,8 +334,7 @@ parse_output_config(struct wl_list *config_list, char **saveptr) { cfg->refresh_rate = parse_float(saveptr, " "); if(cfg->refresh_rate <= 0.0) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Error parsing refresh rate of output configuration for output %s", name); goto error; @@ -346,10 +361,10 @@ error: int parse_command(struct cg_server *server, struct keybinding *keybinding, - char *saveptr) { + char *saveptr, char **errstr) { char *action = strtok_r(NULL, " ", &saveptr); if(action == NULL) { - wlr_log(WLR_ERROR, "Expected an action to parse, got none."); + *errstr = log_error("Expexted an action to parse, got none."); return -1; } keybinding->data = (union keybinding_params){.c = NULL}; @@ -386,8 +401,8 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, } else if(strcmp(action, "exec") == 0) { keybinding->action = KEYBINDING_RUN_COMMAND; if(saveptr == NULL) { - wlr_log(WLR_ERROR, "Not enough paramaters to \"exec\". Expected " - "string to execute."); + *errstr = log_error("Not enough paramaters to \"exec\". Expected " + "string to execute."); return -1; } keybinding->data.c = strdup(saveptr); @@ -407,17 +422,16 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, keybinding->action = KEYBINDING_SWITCH_WORKSPACE; char *nws_str = strtok_r(NULL, " ", &saveptr); if(nws_str == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"workspace\" action, got none."); + *errstr = log_error( + "Expected argument for \"workspace\" action, got none."); return -1; } long ws = strtol(nws_str, NULL, 10); if(ws < 1) { - wlr_log(WLR_ERROR, - "Workspace number must be a integer number larger or equal " - "to 1. Got %ld", - ws); + *errstr = log_error("Workspace number must be an integer number " + "larger or equal to 1. Got %ld", + ws); return -1; } keybinding->data.u = ws - 1; @@ -425,17 +439,16 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, keybinding->action = KEYBINDING_MOVE_VIEW_TO_WORKSPACE; char *nws_str = strtok_r(NULL, " ", &saveptr); if(nws_str == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"workspace\" action, got none."); + *errstr = log_error( + "Expected argument for \"workspace\" action, got none."); return -1; } long ws = strtol(nws_str, NULL, 10); if(ws < 1) { - wlr_log(WLR_ERROR, - "Workspace number must be an integer larger or equal to 1. " - "Got %ld", - ws); + *errstr = log_error("Workspace number must be an integer larger or " + "equal to 1. Got %ld", + ws); return -1; } keybinding->data.u = ws - 1; @@ -461,8 +474,8 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, keybinding->action = KEYBINDING_CHANGE_TTY; char *ntty = strtok_r(NULL, " ", &saveptr); if(ntty == NULL) { - wlr_log(WLR_ERROR, - "Expected argument for \"switchvt\" command, got none."); + *errstr = log_error( + "Expected argument for \"switchvt\" command, got none."); return -1; } long tty = strtol(ntty, NULL, 10); @@ -471,13 +484,13 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, keybinding->action = KEYBINDING_SWITCH_MODE; char *mode = strtok_r(NULL, " ", &saveptr); if(mode == NULL) { - wlr_log(WLR_ERROR, - "Expected mode after \"switch_mode\". Got nothing."); + *errstr = + log_error("Expected mode after \"switch_mode\". Got nothing."); return -1; } int mode_idx = get_mode_index_from_name(server->modes, mode); if(mode_idx == -1) { - wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_mode", mode); + *errstr = log_error("Unknown mode \"%s\" for switch_mode", mode); return -1; } keybinding->data.u = (unsigned int)mode_idx; @@ -485,78 +498,78 @@ parse_command(struct cg_server *server, struct keybinding *keybinding, keybinding->action = KEYBINDING_SWITCH_DEFAULT_MODE; char *mode = strtok_r(NULL, " ", &saveptr); if(mode == NULL) { - wlr_log( - WLR_ERROR, + *errstr = log_error( "Expected mode after \"switch_default_mode\". Got nothing."); return -1; } int mode_idx = get_mode_index_from_name(server->modes, mode); if(mode_idx == -1) { - wlr_log(WLR_ERROR, "Unknown mode \"%s\" for switch_default_mode", - mode); + *errstr = + log_error("Unknown mode \"%s\" for switch_default_mode", mode); return -1; } keybinding->data.u = (unsigned int)mode_idx; } else if(strcmp(action, "bind") == 0) { keybinding->action = KEYBINDING_DEFINEKEY; - keybinding->data.kb = parse_bind(server, &saveptr); + keybinding->data.kb = parse_bind(server, &saveptr, errstr); if(keybinding->data.kb == NULL) { return -1; } } else if(strcmp(action, "definekey") == 0) { keybinding->action = KEYBINDING_DEFINEKEY; - keybinding->data.kb = parse_definekey(server, &saveptr); + keybinding->data.kb = parse_definekey(server, &saveptr, errstr); if(keybinding->data.kb == NULL) { return -1; } } else if(strcmp(action, "background") == 0) { keybinding->action = KEYBINDING_BACKGROUND; - if(parse_background(server, keybinding->data.color, &saveptr) != 0) { + if(parse_background(server, keybinding->data.color, &saveptr, errstr) != + 0) { return -1; } } else if(strcmp(action, "escape") == 0) { keybinding->action = KEYBINDING_DEFINEKEY; - keybinding->data.kb = parse_escape(&saveptr); + keybinding->data.kb = parse_escape(&saveptr, errstr); if(keybinding->data.kb == NULL) { return -1; } } else if(strcmp(action, "definemode") == 0) { keybinding->action = KEYBINDING_DEFINEMODE; - keybinding->data.c = parse_definemode(&saveptr); + keybinding->data.c = parse_definemode(&saveptr, errstr); if(keybinding->data.c == NULL) { return -1; } } else if(strcmp(action, "workspaces") == 0) { keybinding->action = KEYBINDING_WORKSPACES; - keybinding->data.i = parse_workspaces(&saveptr); + keybinding->data.i = parse_workspaces(&saveptr, errstr); if(keybinding->data.i < 0) { return -1; } } else if(strcmp(action, "output") == 0) { - if(parse_output_config(&server->output_config, &saveptr) != 0) { + if(parse_output_config(&server->output_config, &saveptr, errstr) != 0) { return -1; } } else { - wlr_log(WLR_ERROR, "Error, unsupported action \"%s\".", action); + *errstr = log_error("Error, unsupported action \"%s\".", action); return -1; } return 0; } int -parse_rc_line(struct cg_server *server, char *line) { +parse_rc_line(struct cg_server *server, char *line, char **errstr) { char *saveptr = strdup(line); // Used internally by strtok_r struct keybinding *keybinding = malloc(sizeof(struct keybinding)); if(keybinding == NULL) { - wlr_log(WLR_ERROR, - "Failed to allocate memory for temporary keybinding struct."); + *errstr = log_error( + "Failed to allocate memory for temporary keybinding struct."); free(keybinding); free(saveptr); return -1; } - if(parse_command(server, keybinding, saveptr) != 0) { - wlr_log(WLR_ERROR, "Error parsing config file."); + if(parse_command(server, keybinding, saveptr, errstr) != 0) { + wlr_log(WLR_ERROR, "Error parsing command."); free(keybinding); free(saveptr); return -1; diff --git a/parse.h b/parse.h index 622697f..ba1b2e8 100644 --- a/parse.h +++ b/parse.h @@ -2,9 +2,11 @@ #define PARSE_H +#define MAX_LINE_SIZE 256 + struct cg_server; int -parse_rc_line(struct cg_server *server, char *line); +parse_rc_line(struct cg_server *server, char *line, char **errstr); #endif /* end of include guard PARSE_H */ diff --git a/server.h b/server.h index 930cb56..24c2fb1 100644 --- a/server.h +++ b/server.h @@ -2,6 +2,7 @@ #define CG_SERVER_H #include "config.h" +#include "ipc_server.h" #include #include @@ -40,6 +41,8 @@ struct cg_server { enum wl_output_transform output_transform; + struct cg_ipc_handle ipc; + bool running; char **modes; uint16_t nws; diff --git a/signatures/1.2.1.sig b/signatures/1.2.1.sig new file mode 100644 index 0000000..a37d71e Binary files /dev/null and b/signatures/1.2.1.sig differ diff --git a/signatures/cagebreak.sig b/signatures/cagebreak.sig index a37d71e..3c5c359 100644 Binary files a/signatures/cagebreak.sig and b/signatures/cagebreak.sig differ