diff --git a/include/sway/config.h b/include/sway/config.h index 909b6827..4fc3eadb 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -308,6 +308,7 @@ enum focus_wrapping_mode { * The configuration struct. The result of loading a config file. */ struct sway_config { + pid_t swaynag_pid; list_t *symbols; list_t *modes; list_t *bars; @@ -403,17 +404,18 @@ struct sway_config { * Loads the main config from the given path. is_active should be true when * reloading the config. */ -bool load_main_config(const char *path, bool is_active); +bool load_main_config(const char *path, bool is_active, char **errors); /** * Loads an included config. Can only be used after load_main_config. */ -bool load_include_configs(const char *path, struct sway_config *config); +bool load_include_configs(const char *path, struct sway_config *config, + char **errors); /** * Reads the config from the given FILE. */ -bool read_config(FILE *file, struct sway_config *config); +bool read_config(FILE *file, struct sway_config *config, char **errors); /** * Free config struct @@ -422,6 +424,8 @@ void free_config(struct sway_config *config); void free_sway_variable(struct sway_variable *var); +void spawn_swaynag_config_errors(struct sway_config *config, char *errors); + /** * Does variable replacement for a string based on the config's currently loaded variables. */ diff --git a/sway/commands.c b/sway/commands.c index fdae1961..81e9ea42 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -511,11 +511,14 @@ struct cmd_results *cmd_results_new(enum cmd_status status, results->input = NULL; } if (format) { - char *error = malloc(256); va_list args; va_start(args, format); + size_t length = vsnprintf(NULL, 0, format, args) + 1; + char *error = malloc(length); + va_end(args); + va_start(args, format); if (error) { - vsnprintf(error, 256, format, args); + vsnprintf(error, length, format, args); } va_end(args); results->error = error; diff --git a/sway/commands/include.c b/sway/commands/include.c index 1ba9a10d..72fec7cc 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -7,8 +7,19 @@ struct cmd_results *cmd_include(int argc, char **argv) { return error; } - if (!load_include_configs(argv[0], config)) { - return cmd_results_new(CMD_INVALID, "include", "Failed to include sub configuration file: %s", argv[0]); + char *errors = NULL; + if (!load_include_configs(argv[0], config, &errors)) { + struct cmd_results *result = cmd_results_new(CMD_INVALID, "include", + "Failed to include sub configuration file: %s", argv[0]); + free(errors); + return result; + } + + if (errors) { + struct cmd_results *result = cmd_results_new(CMD_INVALID, "include", + "There are errors in the included config\n%s", errors); + free(errors); + return result; } return cmd_results_new(CMD_SUCCESS, NULL, NULL); diff --git a/sway/commands/reload.c b/sway/commands/reload.c index 5c1b19b4..9bf671d9 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c @@ -1,4 +1,5 @@ #define _XOPEN_SOURCE 500 +#include #include #include "sway/commands.h" #include "sway/config.h" @@ -19,8 +20,11 @@ struct cmd_results *cmd_reload(int argc, char **argv) { list_add(bar_ids, strdup(bar->id)); } - if (!load_main_config(config->current_config_path, true)) { - return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); + char *errors = NULL; + if (!load_main_config(config->current_config_path, true, &errors)) { + free(errors); + return cmd_results_new(CMD_FAILURE, "reload", + "Error(s) reloading config."); } ipc_event_workspace(NULL, NULL, "reload"); @@ -42,5 +46,16 @@ struct cmd_results *cmd_reload(int argc, char **argv) { list_free(bar_ids); arrange_windows(&root_container); + + if (config->swaynag_pid > 0) { + kill(config->swaynag_pid, SIGTERM); + config->swaynag_pid = -1; + } + + if (errors) { + spawn_swaynag_config_errors(config, errors); + free(errors); + } + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } diff --git a/sway/config.c b/sway/config.c index 2afffab1..bd282541 100644 --- a/sway/config.c +++ b/sway/config.c @@ -158,6 +158,7 @@ static void set_color(float dest[static 4], uint32_t color) { } static void config_defaults(struct sway_config *config) { + config->swaynag_pid = -1; if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; if (!(config->bars = create_list())) goto cleanup; @@ -319,7 +320,8 @@ static char *get_config_path(void) { return NULL; // Not reached } -static bool load_config(const char *path, struct sway_config *config) { +static bool load_config(const char *path, struct sway_config *config, + char **errors) { if (path == NULL) { wlr_log(WLR_ERROR, "Unable to find a config file!"); return false; @@ -338,7 +340,7 @@ static bool load_config(const char *path, struct sway_config *config) { return false; } - bool config_load_success = read_config(f, config); + bool config_load_success = read_config(f, config, errors); fclose(f); if (!config_load_success) { @@ -348,7 +350,7 @@ static bool load_config(const char *path, struct sway_config *config) { return true; } -bool load_main_config(const char *file, bool is_active) { +bool load_main_config(const char *file, bool is_active, char **errors) { char *path; if (file != NULL) { path = strdup(file); @@ -365,6 +367,7 @@ bool load_main_config(const char *file, bool is_active) { config_defaults(config); if (is_active) { wlr_log(WLR_DEBUG, "Performing configuration file reload"); + config->swaynag_pid = old_config->swaynag_pid; config->reloading = true; config->active = true; create_default_output_configs(); @@ -423,7 +426,7 @@ bool load_main_config(const char *file, bool is_active) { } */ - success = success && load_config(path, config); + success = success && load_config(path, config, errors); if (is_active) { for (int i = 0; i < config->output_configs->length; i++) { @@ -441,7 +444,7 @@ bool load_main_config(const char *file, bool is_active) { } static bool load_include_config(const char *path, const char *parent_dir, - struct sway_config *config) { + struct sway_config *config, char **errors) { // save parent config const char *parent_config = config->current_config_path; @@ -485,7 +488,7 @@ static bool load_include_config(const char *path, const char *parent_dir, list_add(config->config_chain, real_path); int index = config->config_chain->length - 1; - if (!load_config(real_path, config)) { + if (!load_config(real_path, config, errors)) { free(real_path); config->current_config_path = parent_config; list_del(config->config_chain, index); @@ -497,7 +500,8 @@ static bool load_include_config(const char *path, const char *parent_dir, return true; } -bool load_include_configs(const char *path, struct sway_config *config) { +bool load_include_configs(const char *path, struct sway_config *config, + char **errors) { char *wd = getcwd(NULL, 0); char *parent_path = strdup(config->current_config_path); const char *parent_dir = dirname(parent_path); @@ -519,7 +523,7 @@ bool load_include_configs(const char *path, struct sway_config *config) { char **w = p.we_wordv; size_t i; for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config); + load_include_config(w[i], parent_dir, config, errors); } free(parent_path); wordfree(&p); @@ -575,7 +579,26 @@ static char *expand_line(const char *block, const char *line, bool add_brace) { return expanded; } -bool read_config(FILE *file, struct sway_config *config) { +static void log_error(char **errors, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + size_t length = vsnprintf(NULL, 0, fmt, args) + 1; + va_end(args); + + int offset = *errors ? strlen(*errors) : 0; + char *temp = realloc(*errors, offset + length + 1); + if (!temp) { + wlr_log(WLR_ERROR, "Failed to realloc error log"); + return; + } + *errors = temp; + + va_start(args, fmt); + vsnprintf(*errors + offset, length, fmt, args); + va_end(args); +} + +bool read_config(FILE *file, struct sway_config *config, char **errors) { bool reading_main_config = false; char *this_config = NULL; size_t config_size = 0; @@ -665,6 +688,8 @@ bool read_config(FILE *file, struct sway_config *config) { case CMD_INVALID: wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number, line, res->error, config->current_config_path); + log_error(errors, "Error on line %i (%s) '%s': %s\n", line_number, + config->current_config_path, line, res->error); success = false; break; @@ -713,6 +738,38 @@ bool read_config(FILE *file, struct sway_config *config) { return success; } +void spawn_swaynag_config_errors(struct sway_config *config, char *errors) { + char *command = "swaynag " + "--type error " + "--message 'There are errors in your config file' " + "--detailed-message " + "--button 'Exit sway' 'swaymsg exit' " + "--button 'Reload sway' 'swaymsg reload'"; + + int fd[2]; + if (pipe(fd) != 0) { + wlr_log(WLR_ERROR, "Failed to create pipe for swaynag"); + return; + } + + pid_t pid; + if ((pid = fork()) == 0) { + close(fd[1]); + dup2(fd[0], STDIN_FILENO); + close(fd[0]); + execl("/bin/sh", "/bin/sh", "-c", command, NULL); + _exit(0); + } else if (pid < 0) { + wlr_log(WLR_ERROR, "Failed to create fork for swaynag"); + } + + close(fd[0]); + write(fd[1], errors, strlen(errors)); + close(fd[1]); + + config->swaynag_pid = pid; +} + char *do_var_replacement(char *str) { int i; char *find = str; diff --git a/sway/main.c b/sway/main.c index 477ffa5a..de2445a8 100644 --- a/sway/main.c +++ b/sway/main.c @@ -415,12 +415,14 @@ int main(int argc, char **argv) { ipc_init(&server); log_env(); + char *errors = NULL; if (validate) { - bool valid = load_main_config(config_path, false); + bool valid = load_main_config(config_path, false, &errors); + free(errors); return valid ? 0 : 1; } - if (!load_main_config(config_path, false)) { + if (!load_main_config(config_path, false, &errors)) { sway_terminate(EXIT_FAILURE); } @@ -433,6 +435,7 @@ int main(int argc, char **argv) { setenv("WAYLAND_DISPLAY", server.socket, true); if (!terminate_request) { if (!server_start_backend(&server)) { + free(errors); sway_terminate(EXIT_FAILURE); } } @@ -452,6 +455,11 @@ int main(int argc, char **argv) { } transaction_commit_dirty(); + if (errors) { + spawn_swaynag_config_errors(config, errors); + free(errors); + } + if (!terminate_request) { server_run(&server); }