'sd_json_dispatch_variant',
'sd_json_dispatch_variant_noref'],
''],
+ ['sd_json_parse',
+ '3',
+ ['SD_JSON_PARSE_MUST_BE_ARRAY',
+ 'SD_JSON_PARSE_MUST_BE_OBJECT',
+ 'SD_JSON_PARSE_SENSITIVE',
+ 'sd_json_parse_continue',
+ 'sd_json_parse_file',
+ 'sd_json_parse_file_at',
+ 'sd_json_parse_with_source',
+ 'sd_json_parse_with_source_continue'],
+ ''],
['sd_listen_fds',
'3',
['SD_LISTEN_FDS_START', 'sd_listen_fds_with_names'],
--- /dev/null
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="sd_json_parse" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>sd_json_parse</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>sd_json_parse</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>sd_json_parse</refname>
+ <refname>sd_json_parse_continue</refname>
+ <refname>sd_json_parse_with_source</refname>
+ <refname>sd_json_parse_with_source_continue</refname>
+ <refname>sd_json_parse_file</refname>
+ <refname>sd_json_parse_file_at</refname>
+ <refname>SD_JSON_PARSE_SENSITIVE</refname>
+ <refname>SD_JSON_PARSE_MUST_BE_OBJECT</refname>
+ <refname>SD_JSON_PARSE_MUST_BE_ARRAY</refname>
+
+ <refpurpose>Parse JSON strings and files into JSON variant objects</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include <systemd/sd-json.h></funcsynopsisinfo>
+
+ <funcprototype>
+ <funcdef>int <function>sd_json_parse</function></funcdef>
+ <paramdef>const char *<parameter>string</parameter></paramdef>
+ <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+ <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_json_parse_continue</function></funcdef>
+ <paramdef>const char **<parameter>p</parameter></paramdef>
+ <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+ <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_json_parse_with_source</function></funcdef>
+ <paramdef>const char *<parameter>string</parameter></paramdef>
+ <paramdef>const char *<parameter>source</parameter></paramdef>
+ <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+ <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_json_parse_with_source_continue</function></funcdef>
+ <paramdef>const char **<parameter>p</parameter></paramdef>
+ <paramdef>const char *<parameter>source</parameter></paramdef>
+ <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+ <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_json_parse_file</function></funcdef>
+ <paramdef>FILE *<parameter>f</parameter></paramdef>
+ <paramdef>const char *<parameter>path</parameter></paramdef>
+ <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+ <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+ </funcprototype>
+
+ <funcprototype>
+ <funcdef>int <function>sd_json_parse_file_at</function></funcdef>
+ <paramdef>FILE *<parameter>f</parameter></paramdef>
+ <paramdef>int <parameter>dir_fd</parameter></paramdef>
+ <paramdef>const char *<parameter>path</parameter></paramdef>
+ <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+ <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+ <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><function>sd_json_parse()</function> parses the JSON string in <parameter>string</parameter> and
+ returns the resulting JSON variant object in <parameter>ret</parameter>. The input must contain exactly
+ one JSON value (object, array, string, number, boolean, or null); any trailing non-whitespace content
+ after the first parsed value is considered an error.</para>
+
+ <para>If parsing fails, the <parameter>reterr_line</parameter> and <parameter>reterr_column</parameter>
+ arguments are set to the line and column (both one-based) where the parse error occurred. One or both
+ may be passed as <constant>NULL</constant> if the caller is not interested in error location
+ information. On success, the return value is non-negative and <parameter>ret</parameter> is set to a
+ newly allocated JSON variant object (which must be freed with
+ <citerefentry><refentrytitle>sd_json_variant_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ when no longer needed). <parameter>ret</parameter> may be passed as <constant>NULL</constant>, in which
+ case the input is validated but no object is returned.</para>
+
+ <para><function>sd_json_parse_continue()</function> is similar, but is intended for parsing a sequence of
+ concatenated JSON values from a single input string. Instead of taking a <type>const char *</type> string
+ directly, it takes a pointer to a <type>const char *</type> pointer. After each successful parse, the
+ pointer is advanced past the consumed input, so that subsequent calls will parse the next JSON
+ value. This is useful for parsing newline-delimited JSON (NDJSON) streams or similar concatenated JSON
+ formats. Unlike <function>sd_json_parse()</function>, trailing content after the first JSON value is not
+ considered an error — it is expected to be the beginning of the next value.</para>
+
+ <para><function>sd_json_parse_with_source()</function> and
+ <function>sd_json_parse_with_source_continue()</function> are similar to
+ <function>sd_json_parse()</function> and <function>sd_json_parse_continue()</function>, respectively, but
+ take an additional <parameter>source</parameter> argument. This is a human-readable string (typically a
+ file name or other origin identifier) that is attached to the parsed JSON variant object and can later be
+ retrieved via
+ <citerefentry><refentrytitle>sd_json_variant_get_source</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If
+ <parameter>source</parameter> is <constant>NULL</constant>, no source information is
+ attached. <function>sd_json_parse()</function> and <function>sd_json_parse_continue()</function> are
+ equivalent to calling their <function>_with_source</function> counterparts with
+ <parameter>source</parameter> set to <constant>NULL</constant>.</para>
+
+ <para><function>sd_json_parse_file()</function> reads and parses a JSON value from a file. If the
+ <parameter>f</parameter> argument is non-<constant>NULL</constant>, the JSON text is read from the
+ specified <type>FILE</type> stream. If <parameter>f</parameter> is <constant>NULL</constant>, the file
+ indicated by <parameter>path</parameter> is opened and read instead. The <parameter>path</parameter>
+ argument serves a dual purpose: it is both used for opening the file (if <parameter>f</parameter> is
+ <constant>NULL</constant>) and recorded as source information in the resulting JSON variant (see
+ above).</para>
+
+ <para><function>sd_json_parse_file_at()</function> is similar to
+ <function>sd_json_parse_file()</function>, but takes an additional <parameter>dir_fd</parameter> argument
+ which specifies a file descriptor referring to the directory to resolve relative paths specified in
+ <parameter>path</parameter> against. If set to <constant>AT_FDCWD</constant>, relative paths are resolved
+ against the current working directory, which is the default behaviour of
+ <function>sd_json_parse_file()</function>.</para>
+
+ <para>The <parameter>flags</parameter> argument is a bitmask of zero or more of the following
+ flags:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>SD_JSON_PARSE_SENSITIVE</constant></term>
+
+ <listitem><para>Marks the resulting JSON variant as "sensitive", indicating that it contains secret
+ key material or similar confidential data. Sensitive variants are erased from memory when freed and
+ are excluded from certain debug logging and introspection operations. See
+ <citerefentry><refentrytitle>sd_json_variant_sensitive</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+ for details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>SD_JSON_PARSE_MUST_BE_OBJECT</constant></term>
+
+ <listitem><para>Requires that the top-level JSON value be a JSON object (i.e. <literal>{…}</literal>).
+ If the top-level value is an array, string, number, boolean, or null, parsing fails with
+ <constant>-EINVAL</constant>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>SD_JSON_PARSE_MUST_BE_ARRAY</constant></term>
+
+ <listitem><para>Requires that the top-level JSON value be a JSON array (i.e. <literal>[…]</literal>).
+ If the top-level value is an object, string, number, boolean, or null, parsing fails with
+ <constant>-EINVAL</constant>.</para>
+
+ <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>If both <constant>SD_JSON_PARSE_MUST_BE_OBJECT</constant> and
+ <constant>SD_JSON_PARSE_MUST_BE_ARRAY</constant> are set, both objects and arrays are accepted, but
+ non-container values (strings, numbers, booleans, null) are still refused.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Return Value</title>
+
+ <para>On success, these functions return 0. On failure, they return a negative errno-style error
+ code.</para>
+
+ <refsect2>
+ <title>Errors</title>
+
+ <para>Returned errors may indicate the following problems:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><constant>-EINVAL</constant></term>
+
+ <listitem><para>The input is not valid JSON, the input contains trailing content after the parsed
+ value (only for non-<function>_continue</function> variants), or a top-level type constraint
+ specified via <constant>SD_JSON_PARSE_MUST_BE_OBJECT</constant> or
+ <constant>SD_JSON_PARSE_MUST_BE_ARRAY</constant> was violated.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENODATA</constant></term>
+
+ <listitem><para>The input string is empty or <constant>NULL</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>-ENOMEM</constant></term>
+
+ <listitem><para>Memory allocation failed.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <xi:include href="libsystemd-pkgconfig.xml" />
+
+ <refsect1>
+ <title>History</title>
+ <para><function>sd_json_parse()</function>,
+ <function>sd_json_parse_continue()</function>,
+ <function>sd_json_parse_with_source()</function>,
+ <function>sd_json_parse_with_source_continue()</function>,
+ <function>sd_json_parse_file()</function>, and
+ <function>sd_json_parse_file_at()</function> were added in version 257.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+
+ <para><simplelist type="inline">
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd-json</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_json_variant_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_json_variant_get_source</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ <member><citerefentry><refentrytitle>sd_json_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+ </simplelist></para>
+ </refsect1>
+</refentry>
#include <linux/capability.h>
#include "sd-bus.h"
+#include "sd-json.h"
#include "alloc-util.h"
#include "analyze-verify-util.h"
unsigned line = 0, column = 0;
if (arg_security_policy) {
- r = sd_json_parse_file(/* f= */ NULL, arg_security_policy, /* flags= */ 0, &policy, &line, &column);
+ r = sd_json_parse_file(/* f= */ NULL, arg_security_policy, SD_JSON_PARSE_MUST_BE_OBJECT, &policy, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
} else {
return r;
if (f) {
- r = sd_json_parse_file(f, pp, /* flags= */ 0, &policy, &line, &column);
+ r = sd_json_parse_file(f, pp, SD_JSON_PARSE_MUST_BE_OBJECT, &policy, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
}
if (exe && pkgmeta_json) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = sd_json_parse(pkgmeta_json, 0, &v, NULL, NULL);
+ r = sd_json_parse(pkgmeta_json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0) {
_cleanup_free_ char *esc = cescape(pkgmeta_json);
log_warning_errno(r, "json_parse on \"%s\" failed, ignoring: %m", strnull(esc));
assert(json);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
sd_json_variant *w;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
if (usrptr)
params = *(systemd_tpm2_plugin_params *)usrptr;
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m");
assert(json);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %m");
assert(json);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m");
assert(ret_cid_size);
assert(ret_required);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m");
assert(ret_encrypted_key);
assert(ret_encrypted_key_size);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return r;
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON identity: %m");
r = sd_json_parse_file(
streq(arg_identity, "-") ? stdin : NULL,
- streq(arg_identity, "-") ? "<stdin>" : arg_identity, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ streq(arg_identity, "-") ? "<stdin>" : arg_identity,
+ SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE,
+ &v,
+ &line,
+ &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
} else
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse_file(f, path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file(f, path, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse user record: %m", path, line, column);
r = sd_json_parse_file(
streq(arg_identity, "-") ? stdin : NULL,
- streq(arg_identity, "-") ? "<stdin>" : arg_identity, SD_JSON_PARSE_SENSITIVE, &json, &line, &column);
+ streq(arg_identity, "-") ? "<stdin>" : arg_identity,
+ SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE,
+ &json,
+ &line,
+ &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
if (incomplete)
return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
- r = sd_json_parse(text, SD_JSON_PARSE_SENSITIVE, &json, NULL, NULL);
+ r = sd_json_parse(
+ text,
+ SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE,
+ &json,
+ /* reterr_line= */ NULL,
+ /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON identity: %m");
/* f= */ NULL,
fd,
de->d_name,
- /* flags= */ 0,
+ /* flags= */ SD_JSON_PARSE_MUST_BE_OBJECT,
&identity,
&line,
&column);
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON identity: %m");
if (r < 0)
return r;
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON secret record at %u:%u: %m", line, column);
if (r < 0)
return r;
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON identity record at %u:%u: %m", line, column);
}
unsigned line = 0, column = 0;
- r = sd_json_parse_file(f, "stdout", SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file(f, "stdout", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
goto unlink_this_file;
unsigned line = 0, column = 0;
- r = sd_json_parse_file_at(NULL, dir_fd, fname, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file_at(/* f= */ NULL, dir_fd, fname, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
- return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column);
+ return log_error_errno(r, "Failed to parse identity record at %s:%u:%u: %m", fname, line, column);
if (sd_json_variant_is_blank_object(v))
goto unlink_this_file;
return log_error_errno(r, "Failed to read LUKS token %i: %m", token);
unsigned line = 0, column = 0;
- r = sd_json_parse(text, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse LUKS token JSON data %u:%u: %m", line, column);
decrypted[decrypted_size] = 0;
- r = sd_json_parse(decrypted, SD_JSON_PARSE_SENSITIVE, &rr, NULL, NULL);
+ r = sd_json_parse(decrypted, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &rr, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse decrypted JSON record, refusing.");
return log_oom();
unsigned line = 0, column = 0;
- r = sd_json_parse_file(identity_file, ".identity", SD_JSON_PARSE_SENSITIVE, ret, &line, &column);
+ r = sd_json_parse_file(identity_file, ".identity", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, ret, &line, &column);
if (r < 0)
return log_error_errno(r, "[.identity:%u:%u] Failed to parse JSON data: %m", line, column);
}
unsigned line = 0, column = 0;
- r = sd_json_parse_file(json_file, json_path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file(json_file, json_path, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON data: %m", json_path, line, column);
fresh_data = true;
}
- r = sd_json_parse(json, /* flags= */ 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to parse JSON user record: %m");
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_parse(text, 0, &v, NULL, NULL);
+ r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON: %m");
return 0;
}
-static int pull_job_payload_as_json(PullJob *j, sd_json_variant **ret) {
+static int pull_job_payload_as_json_object(PullJob *j, sd_json_variant **ret) {
int r;
assert(j);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse((char*) j->payload.iov_base, /* flags= */ 0, &v, &line, &column);
+ r = sd_json_parse((char*) j->payload.iov_base, SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON at position %u:%u: %m", line, column);
* https://github.com/opencontainers/image-spec/blob/main/image-index.md */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = pull_job_payload_as_json(j, &v);
+ r = pull_job_payload_as_json_object(j, &v);
if (r < 0)
return r;
* https://github.com/opencontainers/image-spec/blob/main/manifest.md */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = pull_job_payload_as_json(j, &v);
+ r = pull_job_payload_as_json_object(j, &v);
if (r < 0)
return r;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse((char*) i->config.iov_base, /* flags= */ 0, &v, &line, &column);
+ r = sd_json_parse((char*) i->config.iov_base, SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON config data at position %u:%u: %m", line, column);
goto finish;
}
- r = pull_job_payload_as_json(j, &v);
+ r = pull_job_payload_as_json_object(j, &v);
if (r < 0)
goto finish;
/* f= */ NULL,
dir_fd,
path,
- /* flags= */ 0,
+ /* flags= */ SD_JSON_PARSE_MUST_BE_OBJECT,
&v,
/* reterr_line= */ NULL,
/* ret_column= */ NULL);
int r;
assert_return(input, -EINVAL);
- assert_return(ret, -EINVAL);
p = *input;
break;
case JSON_TOKEN_OBJECT_OPEN:
-
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
r = -EINVAL;
goto finish;
}
+ if (n_stack == 1 && FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_ARRAY) && !FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_OBJECT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (!GREEDY_REALLOC(stack, n_stack+1)) {
r = -ENOMEM;
goto finish;
goto finish;
}
+ if (n_stack == 1 && !FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_ARRAY) && FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_OBJECT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (!GREEDY_REALLOC(stack, n_stack+1)) {
r = -ENOMEM;
goto finish;
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_string(&add, string);
if (r < 0)
goto finish;
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_real(&add, value.real);
if (r < 0)
goto finish;
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_integer(&add, value.integer);
if (r < 0)
goto finish;
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_unsigned(&add, value.unsig);
if (r < 0)
goto finish;
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_boolean(&add, value.boolean);
if (r < 0)
goto finish;
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_null(&add);
if (r < 0)
goto finish;
assert(n_stack == 1);
assert(stack[0].n_elements == 1);
- *ret = sd_json_variant_ref(stack[0].elements[0]);
+ if (ret)
+ *ret = sd_json_variant_ref(stack[0].elements[0]);
*input = p;
r = 0;
sz = e - begin + 1;
- r = sd_json_parse(begin, 0, &v->current, NULL, NULL);
+ r = sd_json_parse(begin, SD_JSON_PARSE_MUST_BE_OBJECT, &v->current, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (v->input_sensitive)
explicit_bzero_safe(begin, sz);
if (r < 0) {
/* If we encounter a parse failure flush all data. We cannot possibly recover from this,
* hence drop all buffered data now. */
v->input_buffer_index = v->input_buffer_size = v->input_buffer_unscanned = 0;
- return varlink_log_errno(v, r, "Failed to parse JSON: %m");
+ return varlink_log_errno(v, r, "Failed to parse JSON object: %m");
}
if (v->input_sensitive) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
/* Parse cached record */
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to parse JSON user record: %m");
assert(!strv_isempty(arg_phase));
if (arg_append) {
- r = sd_json_parse_file(NULL, arg_append, 0, &v, NULL, NULL);
+ r = sd_json_parse_file(/* f= */ NULL, arg_append, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to parse '%s': %m", arg_append);
-
- if (!sd_json_variant_is_object(v))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "File '%s' is not a valid JSON object, refusing.", arg_append);
+ return log_error_errno(r, "Failed to parse JSON object '%s': %m", arg_append);
}
/* When signing/building digest we only support JSON output */
int sd_json_variant_normalize(sd_json_variant **v);
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_json_parse_flags_t) {
- SD_JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
+ SD_JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
+ SD_JSON_PARSE_MUST_BE_OBJECT = 1 << 1, /* refuse parsing if top-level is not an object */
+ SD_JSON_PARSE_MUST_BE_ARRAY = 1 << 2, /* refuse parsing if top-level is not an array */
_SD_ENUM_FORCE_S64(JSON_PARSE_FLAGS)
} sd_json_parse_flags_t;
test_json_variant_compare_one("{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":\"b\"}", 1);
}
+TEST(must_be) {
+ ASSERT_OK(sd_json_parse("null", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("null", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("null", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("null", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("true", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("true", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("true", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("true", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("\"foo\"", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("\"foo\"", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("\"foo\"", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("\"foo\"", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("4711", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("4711", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("4711", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("4711", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("-4711", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("-4711", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4711", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4711", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("-4.5", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("-4.5", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4.5", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4.5", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("{}", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_OK(sd_json_parse("{}", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("{}", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_OK(sd_json_parse("{}", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+
+ ASSERT_OK(sd_json_parse("[]", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("[]", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_OK(sd_json_parse("[]", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_OK(sd_json_parse("[]", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse_file_at(NULL, credential_dir_fd, name, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file_at(/* f= */ NULL, credential_dir_fd, name, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse credential '%s' as JSON at %u:%u: %m", name, line, column);
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
const char *fn = streq(optarg, "-") ? NULL : optarg;
unsigned line = 0;
- r = sd_json_parse_file(fn ? NULL : stdin, fn ?: "<stdin>", SD_JSON_PARSE_SENSITIVE, &v, &line, /* reterr_column= */ NULL);
+ r = sd_json_parse_file(fn ? NULL : stdin, fn ?: "<stdin>", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, /* reterr_column= */ NULL);
if (r < 0)
return log_syntax(/* unit= */ NULL, LOG_ERR, fn ?: "<stdin>", line, r, "JSON parse failure.");
source = "<argv[4]>";
/* <argv[4]> is correct, as dispatch_verb() shifts arguments by one for the verb. */
- r = sd_json_parse_with_source(parameter, source, 0, &jp, &line, &column);
+ r = sd_json_parse_with_source(parameter, source, SD_JSON_PARSE_MUST_BE_OBJECT, &jp, &line, &column);
} else {
if (isatty_safe(STDIN_FILENO) && !arg_quiet)
log_notice("Expecting method call parameter JSON object on standard input. (Provide empty string or {} for no parameters.)");
source = "<stdin>";
- r = sd_json_parse_file_at(stdin, AT_FDCWD, source, 0, &jp, &line, &column);
+ r = sd_json_parse_file_at(stdin, AT_FDCWD, source, SD_JSON_PARSE_MUST_BE_OBJECT, &jp, &line, &column);
}
if (r < 0 && r != -ENODATA)
return log_error_errno(r, "Failed to parse parameters at %s:%u:%u: %m", source, line, column);
r = sd_json_parse_file(
/* f= */ NULL,
path,
- /* flags= */ 0,
+ /* flags= */ SD_JSON_PARSE_MUST_BE_OBJECT,
&json,
/* reterr_line= */ NULL,
/* ret_column= */ NULL);