Merge pull request #2400 from RedSoxFan/swaynag-config-errors

Show swaynag on config errors
This commit is contained in:
Drew DeVault 2018-08-03 15:08:38 -04:00 committed by GitHub
commit 38675eba7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 222 additions and 19 deletions

View file

@ -150,6 +150,7 @@ sway_cmd cmd_splitt;
sway_cmd cmd_splitv; sway_cmd cmd_splitv;
sway_cmd cmd_sticky; sway_cmd cmd_sticky;
sway_cmd cmd_swaybg_command; sway_cmd cmd_swaybg_command;
sway_cmd cmd_swaynag_command;
sway_cmd cmd_swap; sway_cmd cmd_swap;
sway_cmd cmd_title_format; sway_cmd cmd_title_format;
sway_cmd cmd_unmark; sway_cmd cmd_unmark;

View file

@ -7,6 +7,7 @@
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include "list.h" #include "list.h"
#include "swaynag.h"
#include "tree/layout.h" #include "tree/layout.h"
#include "tree/container.h" #include "tree/container.h"
#include "wlr-layer-shell-unstable-v1-protocol.h" #include "wlr-layer-shell-unstable-v1-protocol.h"
@ -308,6 +309,8 @@ enum focus_wrapping_mode {
* The configuration struct. The result of loading a config file. * The configuration struct. The result of loading a config file.
*/ */
struct sway_config { struct sway_config {
char *swaynag_command;
struct swaynag_instance swaynag_config_errors;
list_t *symbols; list_t *symbols;
list_t *modes; list_t *modes;
list_t *bars; list_t *bars;
@ -345,6 +348,7 @@ struct sway_config {
bool failed; bool failed;
bool reloading; bool reloading;
bool reading; bool reading;
bool validating;
bool auto_back_and_forth; bool auto_back_and_forth;
bool show_marks; bool show_marks;
@ -403,17 +407,19 @@ struct sway_config {
* Loads the main config from the given path. is_active should be true when * Loads the main config from the given path. is_active should be true when
* reloading the config. * reloading the config.
*/ */
bool load_main_config(const char *path, bool is_active); bool load_main_config(const char *path, bool is_active, bool validating);
/** /**
* Loads an included config. Can only be used after load_main_config. * 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,
struct swaynag_instance *swaynag);
/** /**
* Reads the config from the given FILE. * 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,
struct swaynag_instance *swaynag);
/** /**
* Free config struct * Free config struct

29
include/sway/swaynag.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef _SWAY_SWAYNAG_H
#define _SWAY_SWAYNAG_H
struct swaynag_instance {
const char *args;
pid_t pid;
int fd[2];
bool detailed;
};
// Spawn swaynag. If swaynag->detailed, then swaynag->fd[1] will left open
// so it can be written to. Call swaynag_show when done writing. This will
// be automatically called by swaynag_log if the instance is not spawned and
// swaynag->detailed is true.
bool swaynag_spawn(const char *swaynag_command,
struct swaynag_instance *swaynag);
// Kill the swaynag instance
void swaynag_kill(struct swaynag_instance *swaynag);
// Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed
// is false.
void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
const char *fmt, ...);
// If swaynag->detailed, close swaynag->fd[1] so swaynag displays
void swaynag_show(struct swaynag_instance *swaynag);
#endif

View file

@ -132,6 +132,7 @@ static struct cmd_handler handlers[] = {
static struct cmd_handler config_handlers[] = { static struct cmd_handler config_handlers[] = {
{ "default_orientation", cmd_default_orientation }, { "default_orientation", cmd_default_orientation },
{ "swaybg_command", cmd_swaybg_command }, { "swaybg_command", cmd_swaybg_command },
{ "swaynag_command", cmd_swaynag_command },
{ "workspace_layout", cmd_workspace_layout }, { "workspace_layout", cmd_workspace_layout },
}; };

View file

@ -7,8 +7,10 @@ struct cmd_results *cmd_include(int argc, char **argv) {
return error; return error;
} }
if (!load_include_configs(argv[0], config)) { if (!load_include_configs(argv[0], config,
return cmd_results_new(CMD_INVALID, "include", "Failed to include sub configuration file: %s", argv[0]); &config->swaynag_config_errors)) {
return cmd_results_new(CMD_INVALID, "include",
"Failed to include sub configuration file: %s", argv[0]);
} }
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);

View file

@ -19,8 +19,9 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
list_add(bar_ids, strdup(bar->id)); list_add(bar_ids, strdup(bar->id));
} }
if (!load_main_config(config->current_config_path, true)) { if (!load_main_config(config->current_config_path, true, false)) {
return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); return cmd_results_new(CMD_FAILURE, "reload",
"Error(s) reloading config.");
} }
ipc_event_workspace(NULL, NULL, "reload"); ipc_event_workspace(NULL, NULL, "reload");
@ -42,5 +43,6 @@ struct cmd_results *cmd_reload(int argc, char **argv) {
list_free(bar_ids); list_free(bar_ids);
arrange_windows(&root_container); arrange_windows(&root_container);
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }

View file

@ -0,0 +1,20 @@
#include <string.h>
#include "sway/commands.h"
#include "log.h"
#include "stringop.h"
struct cmd_results *cmd_swaynag_command(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "swaynag_command", EXPECTED_AT_LEAST, 1))) {
return error;
}
if (config->swaynag_command) {
free(config->swaynag_command);
}
config->swaynag_command = join_args(argv, argc);
wlr_log(WLR_DEBUG, "Using custom swaynag command: %s",
config->swaynag_command);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

View file

@ -25,6 +25,7 @@
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/criteria.h" #include "sway/criteria.h"
#include "sway/swaynag.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/layout.h" #include "sway/tree/layout.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
@ -72,6 +73,8 @@ void free_config(struct sway_config *config) {
memset(&config->handler_context, 0, sizeof(config->handler_context)); memset(&config->handler_context, 0, sizeof(config->handler_context));
free(config->swaynag_command);
// TODO: handle all currently unhandled lists as we add implementations // TODO: handle all currently unhandled lists as we add implementations
if (config->symbols) { if (config->symbols) {
for (int i = 0; i < config->symbols->length; ++i) { for (int i = 0; i < config->symbols->length; ++i) {
@ -158,6 +161,17 @@ static void set_color(float dest[static 4], uint32_t color) {
} }
static void config_defaults(struct sway_config *config) { static void config_defaults(struct sway_config *config) {
config->swaynag_command = strdup("swaynag");
config->swaynag_config_errors = (struct swaynag_instance){
.args = "--type error "
"--message 'There are errors in your config file' "
"--detailed-message "
"--button 'Exit sway' 'swaymsg exit' "
"--button 'Reload sway' 'swaymsg reload'",
.pid = -1,
.detailed = true,
};
if (!(config->symbols = create_list())) goto cleanup; if (!(config->symbols = create_list())) goto cleanup;
if (!(config->modes = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup;
if (!(config->bars = create_list())) goto cleanup; if (!(config->bars = create_list())) goto cleanup;
@ -204,6 +218,7 @@ static void config_defaults(struct sway_config *config) {
config->focus_follows_mouse = true; config->focus_follows_mouse = true;
config->mouse_warping = true; config->mouse_warping = true;
config->focus_wrapping = WRAP_YES; config->focus_wrapping = WRAP_YES;
config->validating = false;
config->reloading = false; config->reloading = false;
config->active = false; config->active = false;
config->failed = false; config->failed = false;
@ -319,7 +334,8 @@ static char *get_config_path(void) {
return NULL; // Not reached 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,
struct swaynag_instance *swaynag) {
if (path == NULL) { if (path == NULL) {
wlr_log(WLR_ERROR, "Unable to find a config file!"); wlr_log(WLR_ERROR, "Unable to find a config file!");
return false; return false;
@ -338,7 +354,7 @@ static bool load_config(const char *path, struct sway_config *config) {
return false; return false;
} }
bool config_load_success = read_config(f, config); bool config_load_success = read_config(f, config, swaynag);
fclose(f); fclose(f);
if (!config_load_success) { if (!config_load_success) {
@ -348,7 +364,7 @@ static bool load_config(const char *path, struct sway_config *config) {
return true; return true;
} }
bool load_main_config(const char *file, bool is_active) { bool load_main_config(const char *file, bool is_active, bool validating) {
char *path; char *path;
if (file != NULL) { if (file != NULL) {
path = strdup(file); path = strdup(file);
@ -363,10 +379,17 @@ bool load_main_config(const char *file, bool is_active) {
} }
config_defaults(config); config_defaults(config);
config->validating = validating;
if (is_active) { if (is_active) {
wlr_log(WLR_DEBUG, "Performing configuration file reload"); wlr_log(WLR_DEBUG, "Performing configuration file reload");
config->reloading = true; config->reloading = true;
config->active = true; config->active = true;
swaynag_kill(&old_config->swaynag_config_errors);
memcpy(&config->swaynag_config_errors,
&old_config->swaynag_config_errors,
sizeof(struct swaynag_instance));
create_default_output_configs(); create_default_output_configs();
} }
@ -423,13 +446,17 @@ bool load_main_config(const char *file, bool is_active) {
} }
*/ */
success = success && load_config(path, config); success = success && load_config(path, config,
&config->swaynag_config_errors);
if (is_active) { if (is_active) {
for (int i = 0; i < config->output_configs->length; i++) { for (int i = 0; i < config->output_configs->length; i++) {
apply_output_config_to_outputs(config->output_configs->items[i]); apply_output_config_to_outputs(config->output_configs->items[i]);
} }
config->reloading = false; config->reloading = false;
if (config->swaynag_config_errors.pid > 0) {
swaynag_show(&config->swaynag_config_errors);
}
} }
if (old_config) { if (old_config) {
@ -441,7 +468,7 @@ bool load_main_config(const char *file, bool is_active) {
} }
static bool load_include_config(const char *path, const char *parent_dir, static bool load_include_config(const char *path, const char *parent_dir,
struct sway_config *config) { struct sway_config *config, struct swaynag_instance *swaynag) {
// save parent config // save parent config
const char *parent_config = config->current_config_path; const char *parent_config = config->current_config_path;
@ -485,7 +512,7 @@ static bool load_include_config(const char *path, const char *parent_dir,
list_add(config->config_chain, real_path); list_add(config->config_chain, real_path);
int index = config->config_chain->length - 1; int index = config->config_chain->length - 1;
if (!load_config(real_path, config)) { if (!load_config(real_path, config, swaynag)) {
free(real_path); free(real_path);
config->current_config_path = parent_config; config->current_config_path = parent_config;
list_del(config->config_chain, index); list_del(config->config_chain, index);
@ -497,7 +524,8 @@ static bool load_include_config(const char *path, const char *parent_dir,
return true; return true;
} }
bool load_include_configs(const char *path, struct sway_config *config) { bool load_include_configs(const char *path, struct sway_config *config,
struct swaynag_instance *swaynag) {
char *wd = getcwd(NULL, 0); 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); const char *parent_dir = dirname(parent_path);
@ -519,7 +547,7 @@ bool load_include_configs(const char *path, struct sway_config *config) {
char **w = p.we_wordv; char **w = p.we_wordv;
size_t i; size_t i;
for (i = 0; i < p.we_wordc; ++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, swaynag);
} }
free(parent_path); free(parent_path);
wordfree(&p); wordfree(&p);
@ -575,7 +603,8 @@ static char *expand_line(const char *block, const char *line, bool add_brace) {
return expanded; return expanded;
} }
bool read_config(FILE *file, struct sway_config *config) { bool read_config(FILE *file, struct sway_config *config,
struct swaynag_instance *swaynag) {
bool reading_main_config = false; bool reading_main_config = false;
char *this_config = NULL; char *this_config = NULL;
size_t config_size = 0; size_t config_size = 0;
@ -665,6 +694,11 @@ bool read_config(FILE *file, struct sway_config *config) {
case CMD_INVALID: case CMD_INVALID:
wlr_log(WLR_ERROR, "Error on line %i '%s': %s (%s)", line_number, wlr_log(WLR_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);
}
success = false; success = false;
break; break;

View file

@ -22,6 +22,7 @@
#include "sway/debug.h" #include "sway/debug.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/swaynag.h"
#include "sway/tree/layout.h" #include "sway/tree/layout.h"
#include "sway/ipc-server.h" #include "sway/ipc-server.h"
#include "ipc-client.h" #include "ipc-client.h"
@ -416,11 +417,12 @@ int main(int argc, char **argv) {
log_env(); log_env();
if (validate) { if (validate) {
bool valid = load_main_config(config_path, false); bool valid = load_main_config(config_path, false, true);
return valid ? 0 : 1; return valid ? 0 : 1;
} }
if (!load_main_config(config_path, false)) { setenv("WAYLAND_DISPLAY", server.socket, true);
if (!load_main_config(config_path, false, false)) {
sway_terminate(EXIT_FAILURE); sway_terminate(EXIT_FAILURE);
} }
@ -430,7 +432,6 @@ int main(int argc, char **argv) {
security_sanity_check(); security_sanity_check();
setenv("WAYLAND_DISPLAY", server.socket, true);
if (!terminate_request) { if (!terminate_request) {
if (!server_start_backend(&server)) { if (!server_start_backend(&server)) {
sway_terminate(EXIT_FAILURE); sway_terminate(EXIT_FAILURE);
@ -452,6 +453,10 @@ int main(int argc, char **argv) {
} }
transaction_commit_dirty(); transaction_commit_dirty();
if (config->swaynag_config_errors.pid > 0) {
swaynag_show(&config->swaynag_config_errors);
}
if (!terminate_request) { if (!terminate_request) {
server_run(&server); server_run(&server);
} }

View file

@ -9,6 +9,7 @@ sway_sources = files(
'ipc-server.c', 'ipc-server.c',
'scratchpad.c', 'scratchpad.c',
'security.c', 'security.c',
'swaynag.c',
'desktop/desktop.c', 'desktop/desktop.c',
'desktop/idle_inhibit_v1.c', 'desktop/idle_inhibit_v1.c',
@ -78,6 +79,7 @@ sway_sources = files(
'commands/split.c', 'commands/split.c',
'commands/sticky.c', 'commands/sticky.c',
'commands/swaybg_command.c', 'commands/swaybg_command.c',
'commands/swaynag_command.c',
'commands/swap.c', 'commands/swap.c',
'commands/title_format.c', 'commands/title_format.c',
'commands/unmark.c', 'commands/unmark.c',

View file

@ -59,6 +59,13 @@ The following commands may only be used in the configuration file.
Executes custom background _command_. Default is _swaybg_. Refer to Executes custom background _command_. Default is _swaybg_. Refer to
*output* below for more information. *output* below for more information.
*swaynag\_command* <command>
Executes custom command for _swaynag_. Default is _swaynag_. Additional
arguments may be appended to the end. This should only be used to either
direct sway to call swaynag from a custom path or to provide additional
arguments. This should be placed at the top of the config for the best
results.
The following commands cannot be used directly in the configuration file. The following commands cannot be used directly in the configuration file.
They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).

94
sway/swaynag.c Normal file
View file

@ -0,0 +1,94 @@
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "log.h"
#include "sway/swaynag.h"
bool swaynag_spawn(const char *swaynag_command,
struct swaynag_instance *swaynag) {
if (swaynag->detailed) {
if (pipe(swaynag->fd) != 0) {
wlr_log(WLR_ERROR, "Failed to create pipe for swaynag");
return false;
}
fcntl(swaynag->fd[1], F_SETFD, FD_CLOEXEC);
}
pid_t pid;
if ((pid = fork()) == 0) {
if (swaynag->detailed) {
close(swaynag->fd[1]);
dup2(swaynag->fd[0], STDIN_FILENO);
close(swaynag->fd[0]);
}
size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2;
char *cmd = malloc(length);
snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args);
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
_exit(0);
} else if (pid < 0) {
wlr_log(WLR_ERROR, "Failed to create fork for swaynag");
if (swaynag->detailed) {
close(swaynag->fd[0]);
close(swaynag->fd[1]);
}
return false;
}
if (swaynag->detailed) {
close(swaynag->fd[0]);
}
swaynag->pid = pid;
return true;
}
void swaynag_kill(struct swaynag_instance *swaynag) {
if (swaynag->pid > 0) {
kill(swaynag->pid, SIGTERM);
swaynag->pid = -1;
}
}
void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag,
const char *fmt, ...) {
if (!swaynag->detailed) {
wlr_log(WLR_ERROR, "Attempting to write to non-detailed swaynag inst");
return;
}
if (swaynag->pid <= 0 && !swaynag_spawn(swaynag_command, swaynag)) {
return;
}
va_list args;
va_start(args, fmt);
size_t length = vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args);
char *temp = malloc(length + 1);
if (!temp) {
wlr_log(WLR_ERROR, "Failed to allocate buffer for swaynag log entry.");
return;
}
va_start(args, fmt);
vsnprintf(temp, length, fmt, args);
va_end(args);
write(swaynag->fd[1], temp, length);
free(temp);
}
void swaynag_show(struct swaynag_instance *swaynag) {
if (swaynag->detailed && swaynag->pid > 0) {
close(swaynag->fd[1]);
}
}