From 694165b551be27d98e500b47a34106889dcdb0fd Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Sun, 24 Dec 2023 20:53:51 -0800 Subject: [PATCH] ipc: return all included configs in GET_CONFIG The corresponding i3 feature[1] was added in i3 4.20. `variable_replaced_contents` is not implemented, because Sway does variable substitutions differently and it's much harder to obtain a copy of the config data with substitutions. Fixes: #5559 [1]: https://github.com/i3/i3/pull/4528 --- include/sway/config.h | 10 ++- sway/commands/output/background.c | 2 +- sway/commands/reload.c | 4 +- sway/config.c | 117 +++++++++++++++--------------- sway/ipc-json.c | 2 +- sway/ipc-server.c | 15 +++- 6 files changed, 83 insertions(+), 67 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 0be1cd229..690a5a284 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -481,6 +481,11 @@ enum xwayland_mode { XWAYLAND_MODE_IMMEDIATE, }; +struct sway_config_file { + const char *data; + const char *path; +}; + /** * The configuration struct. The result of loading a config file. */ @@ -550,10 +555,9 @@ struct sway_config { int gaps_inner; struct side_gaps gaps_outer; - list_t *config_chain; + list_t *config_chain; /* list of struct sway_config_file* */ bool user_config_path; - const char *current_config_path; - const char *current_config; + struct sway_config_file *current_config; int current_config_line_number; char *current_config_line; diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 55bd7671f..ae879347f 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -92,7 +92,7 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { if (config->reading && *src != '/') { // src file is inside configuration dir - char *conf = strdup(config->current_config_path); + char *conf = strdup(config->current_config->path); if (!conf) { sway_log(SWAY_ERROR, "Failed to duplicate string"); free(src); diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 6c0aac261..80153ec5d 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -22,7 +22,7 @@ static void do_reload(void *data) { const char *path = NULL; if (config->user_config_path) { - path = config->current_config_path; + path = config->current_config->path; } if (!load_main_config(path, true, false)) { @@ -59,7 +59,7 @@ struct cmd_results *cmd_reload(int argc, char **argv) { const char *path = NULL; if (config->user_config_path) { - path = config->current_config_path; + path = config->current_config->path; } if (!load_main_config(path, true, true)) { diff --git a/sway/config.c b/sway/config.c index f9131e0f7..bf7337aa3 100644 --- a/sway/config.c +++ b/sway/config.c @@ -99,6 +99,15 @@ static void free_mode(struct sway_mode *mode) { free(mode); } +static void free_config_file(struct sway_config_file *config_file) { + if (!config_file) { + return; + } + free((char *)config_file->data); + free((char *)config_file->path); + free(config_file); +} + void free_config(struct sway_config *config) { if (!config) { return; @@ -165,9 +174,14 @@ void free_config(struct sway_config *config) { } list_free(config->criteria); } + if (config->config_chain) { + for (int i = 0; i < config->config_chain->length; ++i) { + free_config_file(config->config_chain->items[i]); + } + list_free(config->config_chain); + } list_free(config->no_focus); list_free(config->active_bar_modifiers); - list_free_items_and_destroy(config->config_chain); free(config->floating_scroll_up_cmd); free(config->floating_scroll_down_cmd); free(config->floating_scroll_left_cmd); @@ -175,8 +189,6 @@ void free_config(struct sway_config *config) { free(config->font); free(config->swaybg_command); free(config->swaynag_command); - free((char *)config->current_config_path); - free((char *)config->current_config); keysym_translation_state_destroy(config->keysym_translation_state); free(config); } @@ -295,7 +307,6 @@ static void config_defaults(struct sway_config *config) { if (!(config->swaybg_command = strdup("swaybg"))) goto cleanup; if (!(config->config_chain = create_list())) goto cleanup; - config->current_config_path = NULL; config->current_config = NULL; // borders @@ -411,24 +422,24 @@ static char *get_config_path(void) { return path; } -static bool load_config(const char *path, struct sway_config *config, - struct swaynag_instance *swaynag) { - if (path == NULL) { +static bool load_config(struct sway_config_file *cf, + struct sway_config *config, struct swaynag_instance *swaynag) { + if (cf->path == NULL) { sway_log(SWAY_ERROR, "Unable to find a config file!"); return false; } - sway_log(SWAY_INFO, "Loading config from %s", path); + sway_log(SWAY_INFO, "Loading config from %s", cf->path); struct stat sb; - if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { - sway_log(SWAY_ERROR, "%s is a directory not a config file", path); + if (stat(cf->path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + sway_log(SWAY_ERROR, "%s is a directory not a config file", cf->path); return false; } - FILE *f = fopen(path, "r"); + FILE *f = fopen(cf->path, "r"); if (!f) { - sway_log(SWAY_ERROR, "Unable to open %s for reading", path); + sway_log(SWAY_ERROR, "Unable to open %s for reading", cf->path); return false; } @@ -498,13 +509,15 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } } + config->current_config = malloc(sizeof(struct sway_config_file)); + config->current_config->path = real_path; config->user_config_path = file ? true : false; - config->current_config_path = path; - list_add(config->config_chain, real_path); + list_add(config->config_chain, config->current_config); config->reading = true; - bool success = load_config(path, config, &config->swaynag_config_errors); + bool success = load_config(config->current_config, config, + &config->swaynag_config_errors); if (validating) { free_config(config); @@ -552,7 +565,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { static bool load_include_config(const char *path, const char *parent_dir, struct sway_config *config, struct swaynag_instance *swaynag) { // save parent config - const char *parent_config = config->current_config_path; + struct sway_config_file *parent_config = config->current_config; char *full_path; int len = strlen(path); @@ -580,8 +593,8 @@ static bool load_include_config(const char *path, const char *parent_dir, // check if config has already been included int j; for (j = 0; j < config->config_chain->length; ++j) { - char *old_path = config->config_chain->items[j]; - if (strcmp(real_path, old_path) == 0) { + struct sway_config_file *old = config->config_chain->items[j]; + if (strcmp(real_path, old->path) == 0) { sway_log(SWAY_DEBUG, "%s already included once, won't be included again.", real_path); @@ -590,26 +603,27 @@ static bool load_include_config(const char *path, const char *parent_dir, } } - config->current_config_path = real_path; - list_add(config->config_chain, real_path); + config->current_config = calloc(1, sizeof(struct sway_config_file)); + config->current_config->path = real_path; + list_add(config->config_chain, config->current_config); int index = config->config_chain->length - 1; - if (!load_config(real_path, config, swaynag)) { - free(real_path); - config->current_config_path = parent_config; + if (!load_config(config->current_config, config, swaynag)) { + free_config_file(config->current_config); + config->current_config = parent_config; list_del(config->config_chain, index); return false; } - // restore current_config_path - config->current_config_path = parent_config; + // restore current_config + config->current_config = parent_config; return true; } void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag) { char *wd = getcwd(NULL, 0); - char *parent_path = strdup(config->current_config_path); + char *parent_path = strdup(config->current_config->path); const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { @@ -744,27 +758,24 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { bool read_config(FILE *file, struct sway_config *config, struct swaynag_instance *swaynag) { - bool reading_main_config = false; char *this_config = NULL; - size_t config_size = 0; - if (config->current_config == NULL) { - reading_main_config = true; + int fd = fileno(file); + struct stat sb; - int ret_seek = fseek(file, 0, SEEK_END); - long ret_tell = ftell(file); - if (ret_seek == -1 || ret_tell == -1) { - sway_log(SWAY_ERROR, "Unable to get size of config file"); - return false; - } - config_size = ret_tell; - rewind(file); - - config->current_config = this_config = calloc(1, config_size + 1); - if (this_config == NULL) { - sway_log(SWAY_ERROR, "Unable to allocate buffer for config contents"); - return false; - } + if (fd == -1 || fstat(fd, &sb) == -1) { + sway_log(SWAY_ERROR, "Unable to get size of config file"); + return false; } + config->current_config->data = this_config = calloc(1, sb.st_size + 1); + if (this_config == NULL) { + sway_log(SWAY_ERROR, "Unable to allocate buffer for config contents"); + return false; + } + if (fread(this_config, 1, sb.st_size, file) != (size_t)sb.st_size) { + sway_log(SWAY_ERROR, "Config file read error"); + return false; + } + rewind(file); bool success = true; int line_number = 0; @@ -772,20 +783,8 @@ bool read_config(FILE *file, struct sway_config *config, size_t line_size = 0; ssize_t nread; list_t *stack = create_list(); - size_t read = 0; int nlines = 0; while ((nread = getline_with_cont(&line, &line_size, file, &nlines)) != -1) { - if (reading_main_config) { - if (read + nread > config_size) { - sway_log(SWAY_ERROR, "Config file changed during reading"); - success = false; - break; - } - - strcpy(&this_config[read], line); - read += nread; - } - if (line[nread - 1] == '\n') { line[nread - 1] = '\0'; } @@ -825,11 +824,11 @@ bool read_config(FILE *file, struct sway_config *config, case CMD_FAILURE: case CMD_INVALID: sway_log(SWAY_ERROR, "Error on line %i '%s': %s (%s)", line_number, - line, res->error, config->current_config_path); + line, res->error, config->current_config->path); if (!config->validating) { swaynag_log(config->swaynag_command, swaynag, "Error on line %i (%s) '%s': %s", line_number, - config->current_config_path, line, res->error); + config->current_config->path, line, res->error); } success = false; break; @@ -893,7 +892,7 @@ void config_add_swaynag_warning(char *fmt, ...) { swaynag_log(config->swaynag_command, &config->swaynag_config_errors, "Warning on line %i (%s) '%s': %s", - config->current_config_line_number, config->current_config_path, + config->current_config_line_number, config->current_config->path, config->current_config_line, str); free(str); diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 81ca34831..310cf668a 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -231,7 +231,7 @@ json_object *ipc_json_get_version(void) { json_object_object_add(version, "major", json_object_new_int(major)); json_object_object_add(version, "minor", json_object_new_int(minor)); json_object_object_add(version, "patch", json_object_new_int(patch)); - json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config_path)); + json_object_object_add(version, "loaded_config_file_name", json_object_new_string(config->current_config->path)); return version; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 7f353c0ec..668e40eb4 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -901,7 +901,20 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt case IPC_GET_CONFIG: { json_object *json = json_object_new_object(); - json_object_object_add(json, "config", json_object_new_string(config->current_config)); + json_object_object_add(json, "config", + json_object_new_string(config->current_config->data)); + json_object *includes = + json_object_new_array_ext(config->config_chain->length); + for (int i = 0; i < config->config_chain->length; ++i) { + struct sway_config_file *cf = config->config_chain->items[i]; + json_object *include = json_object_new_object(); + json_object_object_add(include, "path", + json_object_new_string(cf->path)); + json_object_object_add(include, "raw_contents", + json_object_new_string(cf->data)); + json_object_array_add(includes, include); + } + json_object_object_add(json, "included_configs", includes); const char *json_string = json_object_to_json_string(json); ipc_send_reply(client, payload_type, json_string, (uint32_t)strlen(json_string));