This commit is contained in:
Tobias Stoeckmann 2022-12-07 19:46:29 +01:00 committed by GitHub
commit df9b88befb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 86 deletions

View file

@ -1,4 +1,5 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@ -94,9 +95,15 @@ struct ipc_response *ipc_recv_response(int socketfd) {
goto error_1; goto error_1;
} }
memcpy(&response->size, data + sizeof(ipc_magic), sizeof(uint32_t)); uint32_t size;
memcpy(&size, data + sizeof(ipc_magic), sizeof(uint32_t));
response->size = size;
memcpy(&response->type, data + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t)); memcpy(&response->type, data + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t));
if (response->size >= SSIZE_MAX) {
sway_abort("Unable to receive overly long IPC response");
}
char *payload = malloc(response->size + 1); char *payload = malloc(response->size + 1);
if (!payload) { if (!payload) {
goto error_2; goto error_2;
@ -126,11 +133,16 @@ void free_ipc_response(struct ipc_response *response) {
free(response); free(response);
} }
char *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len) { char *ipc_single_command(int socketfd, uint32_t type, const char *payload, size_t *len) {
char data[IPC_HEADER_SIZE]; char data[IPC_HEADER_SIZE];
if (*len > UINT32_MAX) {
sway_abort("Unable to send overly long IPC payload");
}
uint32_t size = *len;
memcpy(data, ipc_magic, sizeof(ipc_magic)); memcpy(data, ipc_magic, sizeof(ipc_magic));
memcpy(data + sizeof(ipc_magic), len, sizeof(*len)); memcpy(data + sizeof(ipc_magic), &size, sizeof(size));
memcpy(data + sizeof(ipc_magic) + sizeof(*len), &type, sizeof(type)); memcpy(data + sizeof(ipc_magic) + sizeof(size), &type, sizeof(type));
if (write(socketfd, data, IPC_HEADER_SIZE) == -1) { if (write(socketfd, data, IPC_HEADER_SIZE) == -1) {
sway_abort("Unable to send IPC header"); sway_abort("Unable to send IPC header");
@ -141,9 +153,15 @@ char *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint3
} }
struct ipc_response *resp = ipc_recv_response(socketfd); struct ipc_response *resp = ipc_recv_response(socketfd);
char *response = resp->payload; char *response;
if (resp == NULL) {
response = NULL;
*len = 0;
} else {
response = resp->payload;
*len = resp->size; *len = resp->size;
free(resp); free(resp);
}
return response; return response;
} }

View file

@ -15,7 +15,7 @@
* encoded payload string. * encoded payload string.
*/ */
struct ipc_response { struct ipc_response {
uint32_t size; size_t size;
uint32_t type; uint32_t type;
char *payload; char *payload;
}; };
@ -32,7 +32,7 @@ int ipc_open_socket(const char *socket_path);
* Issues a single IPC command and returns the buffer. len will be updated with * Issues a single IPC command and returns the buffer. len will be updated with
* the length of the buffer returned from sway. * the length of the buffer returned from sway.
*/ */
char *ipc_single_command(int socketfd, uint32_t type, const char *payload, uint32_t *len); char *ipc_single_command(int socketfd, uint32_t type, const char *payload, size_t *len);
/** /**
* Receives a single IPC response and returns an ipc_response. * Receives a single IPC response and returns an ipc_response.
*/ */

View file

@ -3,6 +3,9 @@
#define event_mask(ev) (1 << (ev & 0x7F)) #define event_mask(ev) (1 << (ev & 0x7F))
// maximum size of payload is 4 MB
#define IPC_MAX_SIZE 4e6
enum ipc_command_type { enum ipc_command_type {
// i3 command types - see i3's I3_REPLY_TYPE constants // i3 command types - see i3's I3_REPLY_TYPE constants
IPC_COMMAND = 0, IPC_COMMAND = 0,

View file

@ -48,6 +48,9 @@ struct ipc_client {
struct sway_server *server; struct sway_server *server;
int fd; int fd;
enum ipc_command_type subscribed_events; enum ipc_command_type subscribed_events;
size_t read_buffer_len;
size_t read_buffer_size;
char *read_buffer;
size_t write_buffer_len; size_t write_buffer_len;
size_t write_buffer_size; size_t write_buffer_size;
char *write_buffer; char *write_buffer;
@ -186,11 +189,23 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) {
client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client); client_fd, WL_EVENT_READABLE, ipc_client_handle_readable, client);
client->writable_event_source = NULL; client->writable_event_source = NULL;
client->read_buffer_size = 128;
client->read_buffer_len = 0;
client->read_buffer = malloc(client->read_buffer_size);
if (!client->read_buffer) {
sway_log(SWAY_ERROR, "Unable to allocate ipc client read buffer");
free(client);
close(client_fd);
return 0;
}
client->write_buffer_size = 128; client->write_buffer_size = 128;
client->write_buffer_len = 0; client->write_buffer_len = 0;
client->write_buffer = malloc(client->write_buffer_size); client->write_buffer = malloc(client->write_buffer_size);
if (!client->write_buffer) { if (!client->write_buffer) {
sway_log(SWAY_ERROR, "Unable to allocate ipc client write buffer"); sway_log(SWAY_ERROR, "Unable to allocate ipc client write buffer");
free(client->read_buffer);
free(client);
close(client_fd); close(client_fd);
return 0; return 0;
} }
@ -220,49 +235,67 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
if ((size_t)read_available + 1 > SIZE_MAX - client->read_buffer_len) {
// Wait for the rest of the command payload in case the header has already been read sway_log_errno(SWAY_INFO, "Receiving too much data from IPC client");
if (client->pending_length > 0) { ipc_client_disconnect(client);
if ((uint32_t)read_available >= client->pending_length) {
// Reset pending values.
uint32_t pending_length = client->pending_length;
enum ipc_command_type pending_type = client->pending_type;
client->pending_length = 0;
ipc_client_handle_command(client, pending_length, pending_type);
}
return 0; return 0;
} }
if (read_available < (int) IPC_HEADER_SIZE) { if (client->read_buffer_len + read_available >= client->read_buffer_size) {
size_t new_size = client->read_buffer_len + read_available + 1;
char *new_buffer = realloc(client->read_buffer, new_size);
if (new_buffer == NULL) {
sway_log_errno(SWAY_INFO, "Unable to increase read buffer for IPC client");
ipc_client_disconnect(client);
return 0; return 0;
} }
client->read_buffer = new_buffer;
client->read_buffer_size = new_size;
}
uint8_t buf[IPC_HEADER_SIZE]; ssize_t received = recv(client_fd, client->read_buffer + client->read_buffer_len, read_available, 0);
// Should be fully available, because read_available >= IPC_HEADER_SIZE
ssize_t received = recv(client_fd, buf, IPC_HEADER_SIZE, 0);
if (received == -1) { if (received == -1) {
sway_log_errno(SWAY_INFO, "Unable to receive header from IPC client"); sway_log_errno(SWAY_INFO, "Unable to receive header from IPC client");
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
client->read_buffer_len += received;
if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { if (client->pending_length == 0) {
if (client->read_buffer_len < (size_t) IPC_HEADER_SIZE) {
return 0;
}
if (memcmp(client->read_buffer, ipc_magic, sizeof(ipc_magic)) != 0) {
sway_log(SWAY_DEBUG, "IPC header check failed"); sway_log(SWAY_DEBUG, "IPC header check failed");
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
memcpy(&client->pending_length, buf + sizeof(ipc_magic), sizeof(uint32_t)); memcpy(&client->pending_length,
memcpy(&client->pending_type, buf + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t)); client->read_buffer + sizeof(ipc_magic), sizeof(uint32_t));
memcpy(&client->pending_type,
if (read_available - received >= (long)client->pending_length) { client->read_buffer + sizeof(ipc_magic) + sizeof(uint32_t), sizeof(uint32_t));
// Reset pending values. if (client->pending_length > IPC_MAX_SIZE) {
uint32_t pending_length = client->pending_length; sway_log_errno(SWAY_INFO, "Receiving too much payload from IPC client");
enum ipc_command_type pending_type = client->pending_type; ipc_client_disconnect(client);
client->pending_length = 0; return 0;
ipc_client_handle_command(client, pending_length, pending_type); }
} }
if (client->read_buffer_len >= IPC_HEADER_SIZE + client->pending_length) {
uint32_t pending_length = client->pending_length;
enum ipc_command_type pending_type = client->pending_type;
char c = client->read_buffer[IPC_HEADER_SIZE + client->pending_length];
client->read_buffer[IPC_HEADER_SIZE + client->pending_length] = '\0';
ipc_client_handle_command(client, pending_length, pending_type);
// Reset values.
client->read_buffer[IPC_HEADER_SIZE + client->pending_length] = c;
memmove(client->read_buffer, client->read_buffer + IPC_HEADER_SIZE + client->pending_length,
client->read_buffer_len - IPC_HEADER_SIZE - client->pending_length);
client->read_buffer_len -= IPC_HEADER_SIZE + client->pending_length;
client->pending_length = 0;
}
return 0; return 0;
} }
@ -565,6 +598,7 @@ void ipc_client_disconnect(struct ipc_client *client) {
i++; i++;
} }
list_del(ipc_client_list, i); list_del(ipc_client_list, i);
free(client->read_buffer);
free(client->write_buffer); free(client->write_buffer);
close(client->fd); close(client->fd);
free(client); free(client);
@ -603,24 +637,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
return; return;
} }
char *buf = malloc(payload_length + 1); char *buf = client->read_buffer + IPC_HEADER_SIZE;
if (!buf) {
sway_log_errno(SWAY_INFO, "Unable to allocate IPC payload");
ipc_client_disconnect(client);
return;
}
if (payload_length > 0) {
// Payload should be fully available
ssize_t received = recv(client->fd, buf, payload_length, 0);
if (received == -1)
{
sway_log_errno(SWAY_INFO, "Unable to receive payload from IPC client");
ipc_client_disconnect(client);
free(buf);
return;
}
}
buf[payload_length] = '\0';
switch (payload_type) { switch (payload_type) {
case IPC_COMMAND: case IPC_COMMAND:
@ -647,14 +664,14 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
list_del(res_list, 0); list_del(res_list, 0);
} }
list_free(res_list); list_free(res_list);
goto exit_cleanup; break;
} }
case IPC_SEND_TICK: case IPC_SEND_TICK:
{ {
ipc_event_tick(buf); ipc_event_tick(buf);
ipc_send_reply(client, payload_type, "{\"success\": true}", 17); ipc_send_reply(client, payload_type, "{\"success\": true}", 17);
goto exit_cleanup; break;
} }
case IPC_GET_OUTPUTS: case IPC_GET_OUTPUTS:
@ -695,7 +712,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(outputs); // free json_object_put(outputs); // free
goto exit_cleanup; break;
} }
case IPC_GET_WORKSPACES: case IPC_GET_WORKSPACES:
@ -706,7 +723,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(workspaces); // free json_object_put(workspaces); // free
goto exit_cleanup; break;
} }
case IPC_SUBSCRIBE: case IPC_SUBSCRIBE:
@ -717,7 +734,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
const char msg[] = "{\"success\": false}"; const char msg[] = "{\"success\": false}";
ipc_send_reply(client, payload_type, msg, strlen(msg)); ipc_send_reply(client, payload_type, msg, strlen(msg));
sway_log(SWAY_INFO, "Failed to parse subscribe request"); sway_log(SWAY_INFO, "Failed to parse subscribe request");
goto exit_cleanup; break;
} }
bool is_tick = false; bool is_tick = false;
@ -748,7 +765,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, msg, strlen(msg)); ipc_send_reply(client, payload_type, msg, strlen(msg));
json_object_put(request); json_object_put(request);
sway_log(SWAY_INFO, "Unsupported event type in subscribe request"); sway_log(SWAY_INFO, "Unsupported event type in subscribe request");
goto exit_cleanup; return;
} }
} }
@ -760,7 +777,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, IPC_EVENT_TICK, tickmsg, ipc_send_reply(client, IPC_EVENT_TICK, tickmsg,
strlen(tickmsg)); strlen(tickmsg));
} }
goto exit_cleanup; break;
} }
case IPC_GET_INPUTS: case IPC_GET_INPUTS:
@ -774,7 +791,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(inputs); // free json_object_put(inputs); // free
goto exit_cleanup; break;
} }
case IPC_GET_SEATS: case IPC_GET_SEATS:
@ -788,7 +805,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(seats); // free json_object_put(seats); // free
goto exit_cleanup; break;
} }
case IPC_GET_TREE: case IPC_GET_TREE:
@ -798,7 +815,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(tree); json_object_put(tree);
goto exit_cleanup; break;
} }
case IPC_GET_MARKS: case IPC_GET_MARKS:
@ -809,7 +826,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(marks); json_object_put(marks);
goto exit_cleanup; break;
} }
case IPC_GET_VERSION: case IPC_GET_VERSION:
@ -819,7 +836,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(version); // free json_object_put(version); // free
goto exit_cleanup; break;
} }
case IPC_GET_BAR_CONFIG: case IPC_GET_BAR_CONFIG:
@ -849,7 +866,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }";
ipc_send_reply(client, payload_type, error, ipc_send_reply(client, payload_type, error,
(uint32_t)strlen(error)); (uint32_t)strlen(error));
goto exit_cleanup; break;
} }
json_object *json = ipc_json_describe_bar_config(bar); json_object *json = ipc_json_describe_bar_config(bar);
const char *json_string = json_object_to_json_string(json); const char *json_string = json_object_to_json_string(json);
@ -857,7 +874,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(json); // free json_object_put(json); // free
} }
goto exit_cleanup; break;
} }
case IPC_GET_BINDING_MODES: case IPC_GET_BINDING_MODES:
@ -871,7 +888,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(modes); // free json_object_put(modes); // free
goto exit_cleanup; break;
} }
case IPC_GET_BINDING_STATE: case IPC_GET_BINDING_STATE:
@ -881,7 +898,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(current_mode); // free json_object_put(current_mode); // free
goto exit_cleanup; break;
} }
case IPC_GET_CONFIG: case IPC_GET_CONFIG:
@ -892,7 +909,7 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
json_object_put(json); // free json_object_put(json); // free
goto exit_cleanup; break;
} }
case IPC_SYNC: case IPC_SYNC:
@ -900,17 +917,13 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
// It was decided sway will not support this, just return success:false // It was decided sway will not support this, just return success:false
const char msg[] = "{\"success\": false}"; const char msg[] = "{\"success\": false}";
ipc_send_reply(client, payload_type, msg, strlen(msg)); ipc_send_reply(client, payload_type, msg, strlen(msg));
goto exit_cleanup; break;
} }
default: default:
sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type); sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type);
goto exit_cleanup; break;
} }
exit_cleanup:
free(buf);
return;
} }
bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type,
@ -928,7 +941,7 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ
client->write_buffer_size *= 2; client->write_buffer_size *= 2;
} }
if (client->write_buffer_size > 4e6) { // 4 MB if (client->write_buffer_size > IPC_MAX_SIZE) {
sway_log(SWAY_ERROR, "Client write buffer too big (%zu), disconnecting client", sway_log(SWAY_ERROR, "Client write buffer too big (%zu), disconnecting client",
client->write_buffer_size); client->write_buffer_size);
ipc_client_disconnect(client); ipc_client_disconnect(client);

View file

@ -89,9 +89,9 @@ void detect_proprietary(int allow_unsupported_gpu) {
void run_as_ipc_client(char *command, char *socket_path) { void run_as_ipc_client(char *command, char *socket_path) {
int socketfd = ipc_open_socket(socket_path); int socketfd = ipc_open_socket(socket_path);
uint32_t len = strlen(command); size_t len = strlen(command);
char *resp = ipc_single_command(socketfd, IPC_COMMAND, command, &len); char *resp = ipc_single_command(socketfd, IPC_COMMAND, command, &len);
printf("%s\n", resp); printf("%s\n", resp == NULL ? "(null)" : resp);
free(resp); free(resp);
close(socketfd); close(socketfd);
} }

View file

@ -19,7 +19,7 @@
#include "util.h" #include "util.h"
void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { void ipc_send_workspace_command(struct swaybar *bar, const char *ws) {
uint32_t size = strlen("workspace \"\"") + strlen(ws); size_t size = strlen("workspace \"\"") + strlen(ws);
for (size_t i = 0; i < strlen(ws); ++i) { for (size_t i = 0; i < strlen(ws); ++i) {
if (ws[i] == '"' || ws[i] == '\\') { if (ws[i] == '"' || ws[i] == '\\') {
++size; ++size;
@ -343,9 +343,12 @@ bool ipc_get_workspaces(struct swaybar *bar) {
free_workspaces(&output->workspaces); free_workspaces(&output->workspaces);
output->focused = false; output->focused = false;
} }
uint32_t len = 0; size_t len = 0;
char *res = ipc_single_command(bar->ipc_socketfd, char *res = ipc_single_command(bar->ipc_socketfd,
IPC_GET_WORKSPACES, NULL, &len); IPC_GET_WORKSPACES, NULL, &len);
if (res == NULL) {
return false;
}
json_object *results = json_tokener_parse(res); json_object *results = json_tokener_parse(res);
if (!results) { if (!results) {
free(res); free(res);
@ -411,16 +414,16 @@ bool ipc_get_workspaces(struct swaybar *bar) {
void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) { void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) {
sway_log(SWAY_DEBUG, "Executing binding for button %u (release=%d): `%s`", sway_log(SWAY_DEBUG, "Executing binding for button %u (release=%d): `%s`",
bind->button, bind->release, bind->command); bind->button, bind->release, bind->command);
uint32_t len = strlen(bind->command); size_t len = strlen(bind->command);
free(ipc_single_command(bar->ipc_socketfd, free(ipc_single_command(bar->ipc_socketfd,
IPC_COMMAND, bind->command, &len)); IPC_COMMAND, bind->command, &len));
} }
bool ipc_initialize(struct swaybar *bar) { bool ipc_initialize(struct swaybar *bar) {
uint32_t len = strlen(bar->id); size_t len = strlen(bar->id);
char *res = ipc_single_command(bar->ipc_socketfd, char *res = ipc_single_command(bar->ipc_socketfd,
IPC_GET_BAR_CONFIG, bar->id, &len); IPC_GET_BAR_CONFIG, bar->id, &len);
if (!ipc_parse_config(bar->config, res)) { if (res == NULL || !ipc_parse_config(bar->config, res)) {
free(res); free(res);
return false; return false;
} }

View file

@ -550,8 +550,16 @@ int main(int argc, char **argv) {
int socketfd = ipc_open_socket(socket_path); int socketfd = ipc_open_socket(socket_path);
struct timeval timeout = {.tv_sec = 3, .tv_usec = 0}; struct timeval timeout = {.tv_sec = 3, .tv_usec = 0};
ipc_set_recv_timeout(socketfd, timeout); ipc_set_recv_timeout(socketfd, timeout);
uint32_t len = strlen(command); size_t len = strlen(command);
char *resp = ipc_single_command(socketfd, type, command, &len); char *resp = ipc_single_command(socketfd, type, command, &len);
if (resp == NULL) {
if (!quiet) {
sway_log(SWAY_ERROR, "Failed to receive reply");
}
close(socketfd);
free(socket_path);
return 1;
}
// pretty print the json // pretty print the json
json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH); json_tokener *tok = json_tokener_new_ex(JSON_MAX_DEPTH);