Introduce the GET_CONFIG IPC request

This introduces memory usage by one copy of the config file, which is an
acceptable trade-off for being able to easily revert data loss.
The default config is 6KB, user configs will be in the same ballpark.

fixes #2856
This commit is contained in:
Michael Stapelberg
2017-08-19 17:29:03 +02:00
parent 6bb9c9e708
commit a6d8ed9b1a
8 changed files with 173 additions and 13 deletions

View File

@ -119,6 +119,43 @@ static yajl_callbacks reply_callbacks = {
.yajl_end_map = reply_end_map_cb,
};
/*******************************************************************************
* Config reply callbacks
*******************************************************************************/
static char *config_last_key = NULL;
static int config_string_cb(void *params, const unsigned char *val, size_t len) {
char *str = scalloc(len + 1, 1);
strncpy(str, (const char *)val, len);
if (strcmp(config_last_key, "config") == 0) {
fprintf(stdout, "%s", str);
}
free(str);
return 1;
}
static int config_start_map_cb(void *params) {
return 1;
}
static int config_end_map_cb(void *params) {
return 1;
}
static int config_map_key_cb(void *params, const unsigned char *keyVal, size_t keyLen) {
config_last_key = scalloc(keyLen + 1, 1);
strncpy(config_last_key, (const char *)keyVal, keyLen);
return 1;
}
static yajl_callbacks config_callbacks = {
.yajl_string = config_string_cb,
.yajl_start_map = config_start_map_cb,
.yajl_map_key = config_map_key_cb,
.yajl_end_map = config_end_map_cb,
};
int main(int argc, char *argv[]) {
#if defined(__OpenBSD__)
if (pledge("stdio rpath unix", NULL) == -1)
@ -150,25 +187,27 @@ int main(int argc, char *argv[]) {
free(socket_path);
socket_path = sstrdup(optarg);
} else if (o == 't') {
if (strcasecmp(optarg, "command") == 0)
if (strcasecmp(optarg, "command") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_COMMAND;
else if (strcasecmp(optarg, "get_workspaces") == 0)
} else if (strcasecmp(optarg, "get_workspaces") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
else if (strcasecmp(optarg, "get_outputs") == 0)
} else if (strcasecmp(optarg, "get_outputs") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_OUTPUTS;
else if (strcasecmp(optarg, "get_tree") == 0)
} else if (strcasecmp(optarg, "get_tree") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
else if (strcasecmp(optarg, "get_marks") == 0)
} else if (strcasecmp(optarg, "get_marks") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
else if (strcasecmp(optarg, "get_bar_config") == 0)
} else if (strcasecmp(optarg, "get_bar_config") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
else if (strcasecmp(optarg, "get_binding_modes") == 0)
} else if (strcasecmp(optarg, "get_binding_modes") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES;
else if (strcasecmp(optarg, "get_version") == 0)
} else if (strcasecmp(optarg, "get_version") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
else {
} else if (strcasecmp(optarg, "get_config") == 0) {
message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG;
} else {
printf("Unknown message type\n");
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version\n");
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config\n");
exit(EXIT_FAILURE);
}
} else if (o == 'q') {
@ -241,7 +280,7 @@ int main(int argc, char *argv[]) {
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
/* For the reply of commands, have a look if that command was successful.
* If not, nicely format the error message. */
if (reply_type == I3_IPC_MESSAGE_TYPE_COMMAND) {
if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
yajl_free(handle);
@ -256,8 +295,24 @@ int main(int argc, char *argv[]) {
/* NB: We still fall-through and print the reply, because even if one
* command failed, that doesnt mean that all commands failed. */
} else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) {
yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
yajl_free(handle);
switch (state) {
case yajl_status_ok:
break;
case yajl_status_client_canceled:
case yajl_status_error:
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
}
goto exit;
}
printf("%.*s\n", reply_length, reply);
exit:
free(reply);
close(sockfd);