spa: Improve JSON error reporting

Add struct spa_error_location that holds information about some parsing
context such as the line and column number, error and line fragment
with the error.

Make spa_json_get_error() fill in the spa_error_location instead. Add
some error codes to the error state and use this to add a parsing reason
to the location.

Add a debug function to log the error location in a nice way. Also
add a FILE based debug context to log to any FILE.

Replace pw_properties_check_string() with
pw_properties_update_string_checked() and add
pw_properties_new_string_checked(). The check string behaviour can still
be done by setting props to NULL but the main purpose is to be able to
avoid parsing the json file twice in the future.

When using the old pw_properties_update_string(), log a warning to the
log when we fail to parse the complete string.

Use the new checked functions and the debug functions to report about
parsing errors in the tools and conf parsing.

This gives errors like:

```
> pw-loopback --playback-props '{ foo =  [ f : g ] }'
error: syntax error in --playback-props: Invalid array separator
line:      1 | { foo =  [ f : g ] }
col:      14 |              ^
```
This commit is contained in:
Wim Taymans 2024-03-27 15:31:48 +01:00
parent 96fb63dfa1
commit d4581755e6
17 changed files with 328 additions and 116 deletions

View file

@ -26,6 +26,7 @@
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/debug/types.h>
#include <spa/debug/file.h>
#include <pipewire/cleanup.h>
#include <pipewire/pipewire.h>
@ -1606,8 +1607,9 @@ int main(int argc, char *argv[])
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
const char *prog;
int exit_code = EXIT_FAILURE, c, ret, line, col;
int exit_code = EXIT_FAILURE, c, ret;
enum pw_stream_flags flags = 0;
struct spa_error_location loc;
setlocale(LC_ALL, "");
pw_init(&argc, &argv);
@ -1729,11 +1731,12 @@ int main(int argc, char *argv[])
break;
case 'P':
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
fprintf(stderr, "error: syntax error in --properties at line:%d col:%d\n", line, col);
if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
spa_debug_file_error_location(stderr, &loc,
"error: syntax error in --properties: %s",
loc.reason);
goto error_usage;
}
pw_properties_update_string(data.props, optarg, strlen(optarg));
break;
case OPT_TARGET:

View file

@ -1405,17 +1405,13 @@ static bool do_info(struct data *data, const char *cmd, char *args, char **error
static struct pw_properties *properties_new_checked(const char *str, char **error)
{
struct pw_properties *props;
int line, col;
struct spa_error_location loc;
if (!pw_properties_check_string(str, strlen(str), &line, &col)) {
*error = spa_aprintf("syntax error in properties, line:%d col:%d", line, col);
return NULL;
props = pw_properties_new_string_checked(str, strlen(str), &loc);
if (!props) {
*error = spa_aprintf("syntax error in properties, line:%d col:%d: %s",
loc.line, loc.col, loc.reason);
}
props = pw_properties_new_string(str);
if (!props)
*error = spa_aprintf("failed to allocate properties");
return props;
}

View file

@ -18,6 +18,7 @@
#include <spa/debug/pod.h>
#include <spa/debug/format.h>
#include <spa/debug/types.h>
#include <spa/debug/file.h>
#include <pipewire/pipewire.h>
#include <pipewire/extensions/security-context.h>
@ -147,7 +148,8 @@ int main(int argc, char *argv[])
{ "properties", required_argument, NULL, 'P' },
{ NULL, 0, NULL, 0}
};
int c, res, listen_fd, close_fd[2], line, col;
struct spa_error_location loc;
int c, res, listen_fd, close_fd[2];
char temp[PATH_MAX] = "/tmp/pipewire-XXXXXX";
struct sockaddr_un sockaddr = {0};
@ -176,11 +178,12 @@ int main(int argc, char *argv[])
opt_remote = optarg;
break;
case 'P':
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
fprintf(stderr, "error: syntax error in --properties at line:%d col:%d\n", line, col);
if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
spa_debug_file_error_location(stderr, &loc,
"error: syntax error in --properties: %s",
loc.reason);
return -1;
}
pw_properties_update_string(data.props, optarg, strlen(optarg));
break;
default:
show_help(&data, argv[0], true);

View file

@ -16,6 +16,7 @@
#include <spa/utils/string.h>
#include <spa/utils/json.h>
#include <spa/debug/types.h>
#include <spa/debug/file.h>
#include <pipewire/pipewire.h>
@ -1277,7 +1278,7 @@ static int get_data_from_json(struct data *data, const char *json_path)
struct stat sbuf;
struct spa_json it[2];
const char *value;
int line, col;
struct spa_error_location loc;
if ((fd = open(json_path, O_CLOEXEC | O_RDONLY)) < 0) {
fprintf(stderr, "error opening file '%s': %m\n", json_path);
@ -1314,8 +1315,10 @@ static int get_data_from_json(struct data *data, const char *json_path)
munmap(json, sbuf.st_size);
if (spa_json_get_error(&it[0], json, &line, &col)) {
fprintf(stderr, "JSON syntax error on line:%d col:%d\n", line, col);
if (spa_json_get_error(&it[0], json, &loc)) {
spa_debug_file_error_location(stderr, &loc,
"JSON syntax error: %s\n",
loc.reason);
return -1;
}

View file

@ -13,6 +13,7 @@
#include <spa/utils/result.h>
#include <spa/utils/string.h>
#include <spa/utils/defs.h>
#include <spa/debug/file.h>
#include <pipewire/pipewire.h>
#include <pipewire/filter.h>
@ -873,7 +874,8 @@ static int run(int argc, char *argv[])
.objects = SPA_LIST_INIT(&data.objects),
.target_links = SPA_LIST_INIT(&data.target_links),
};
int res = 0, c, line, col;
int res = 0, c;
struct spa_error_location loc;
static const struct option long_options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
@ -942,11 +944,11 @@ static int run(int argc, char *argv[])
pw_properties_set(data.props, PW_KEY_LINK_PASSIVE, "true");
break;
case 'p':
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
fprintf(stderr, "error: syntax error in --props at line:%d col:%d\n", line, col);
if (pw_properties_update_string_checked(data.props, optarg, strlen(optarg), &loc) < 0) {
spa_debug_file_error_location(stderr, &loc,
"error: syntax error in --props: %s", loc.reason);
return -1;
}
pw_properties_update_string(data.props, optarg, strlen(optarg));
break;
case 'd':
data.opt_mode = MODE_DISCONNECT;

View file

@ -16,6 +16,7 @@
#include <spa/param/audio/format-utils.h>
#include <spa/param/audio/raw.h>
#include <spa/utils/json.h>
#include <spa/debug/file.h>
#include <pipewire/pipewire.h>
#include <pipewire/impl.h>
@ -110,7 +111,8 @@ int main(int argc, char *argv[])
{ "playback-props", required_argument, NULL, 'o' },
{ NULL, 0, NULL, 0}
};
int c, res = -1, line, col;
int c, res = -1;
struct spa_error_location loc;
setlocale(LC_ALL, "");
pw_init(&argc, &argv);
@ -170,18 +172,22 @@ int main(int argc, char *argv[])
pw_properties_set(data.playback_props, PW_KEY_TARGET_OBJECT, optarg);
break;
case 'i':
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
fprintf(stderr, "error: syntax error in --capture-props at line:%d col:%d\n", line, col);
if (pw_properties_update_string_checked(data.capture_props,
optarg, strlen(optarg), &loc) < 0) {
spa_debug_file_error_location(stderr, &loc,
"error: syntax error in --capture-props: %s",
loc.reason);
return -1;
}
pw_properties_update_string(data.capture_props, optarg, strlen(optarg));
break;
case 'o':
if (!pw_properties_check_string(optarg, strlen(optarg), &line, &col)) {
fprintf(stderr, "error: syntax error in --playback-props at line:%d col:%d\n", line, col);
if (pw_properties_update_string_checked(data.playback_props,
optarg, strlen(optarg), &loc) < 0) {
spa_debug_file_error_location(stderr, &loc,
"error: syntax error in --playback-props: %s",
loc.reason);
return -1;
}
pw_properties_update_string(data.playback_props, optarg, strlen(optarg));
break;
default:
show_help(&data, argv[0], true);