goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
add_more = sd_json_variant_ref(v);
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
+
+ current->expect = EXPECT_OBJECT_KEY;
+ break;
+ }
+
+ case _JSON_BUILD_PAIR_VARIANT_NON_EMPTY: {
+ sd_json_variant *v;
+ const char *n;
+
+ if (current->expect != EXPECT_OBJECT_KEY) {
+ r = -EINVAL;
+ goto finish;
+ }
+
+ n = va_arg(ap, const char *);
+ v = va_arg(ap, sd_json_variant *);
+
+ if (v && !sd_json_variant_is_blank_object(v) && current->n_suppress == 0) {
+ r = sd_json_variant_new_string(&add, n);
+ if (r < 0)
+ goto finish;
+
+ add_more = sd_json_variant_ref(v);
+ }
+
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
}
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
goto finish;
}
- n_subtract = 2; /* we generated two item */
+ n_subtract = 2; /* we generated two items */
current->expect = EXPECT_OBJECT_KEY;
break;
}
static int varlink_dispatch_local_error(sd_varlink *v, const char *error) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *empty = NULL;
int r;
assert(v);
if (!v->reply_callback)
return 0;
- r = v->reply_callback(v, NULL, error, SD_VARLINK_REPLY_ERROR|SD_VARLINK_REPLY_LOCAL, v->userdata);
+ r = sd_json_variant_new_object(&empty, NULL, 0);
+ if (r < 0)
+ return r;
+
+ r = v->reply_callback(v, empty, error, SD_VARLINK_REPLY_ERROR|SD_VARLINK_REPLY_LOCAL, v->userdata);
if (r < 0)
varlink_log_errno(v, r, "Reply callback returned error, ignoring: %m");
return 1;
}
-static int varlink_sanitize_parameters(sd_json_variant **v) {
+static int varlink_sanitize_incoming_parameters(sd_json_variant **v) {
int r;
-
assert(v);
- /* Varlink always wants a parameters list, hence make one if the caller doesn't want any */
- if (!*v)
- return sd_json_variant_new_object(v, NULL, 0);
- if (sd_json_variant_is_null(*v)) {
- sd_json_variant *empty;
-
+ /* Convert NULL or JSON null to empty object for method handlers (backward compatibility) */
+ if (!*v || sd_json_variant_is_null(*v)) {
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *empty = NULL;
r = sd_json_variant_new_object(&empty, NULL, 0);
if (r < 0)
return r;
-
+ /* sd_json_variant_unref() is a NOP if *v is NULL */
sd_json_variant_unref(*v);
- *v = empty;
+ *v = TAKE_PTR(empty);
return 0;
}
+
+ /* Ensure we have an object */
if (!sd_json_variant_is_object(*v))
return -EINVAL;
if (error && FLAGS_SET(flags, SD_VARLINK_REPLY_CONTINUES))
goto invalid;
- r = varlink_sanitize_parameters(¶meters);
+ r = varlink_sanitize_incoming_parameters(¶meters);
if (r < 0)
goto invalid;
if (!method)
goto invalid;
- r = varlink_sanitize_parameters(¶meters);
+ r = varlink_sanitize_incoming_parameters(¶meters);
if (r < 0)
goto fail;
if (!v->current)
return -ENODATA;
- p = sd_json_variant_by_key(v->current, "parameters");
- if (!p)
- return -ENODATA;
+ if (!ret)
+ return 0;
- if (ret)
- *ret = sd_json_variant_ref(p);
+ p = sd_json_variant_by_key(v->current, "parameters");
+ if (!p || sd_json_variant_is_null(p))
+ return sd_json_variant_new_object(ret, NULL, 0);
+ *ret = sd_json_variant_ref(p);
return 0;
}
if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY))
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
SD_JSON_BUILD_PAIR("oneway", SD_JSON_BUILD_BOOLEAN(true)));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY))
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
if (v->state != VARLINK_IDLE_CLIENT)
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
SD_JSON_BUILD_PAIR("more", SD_JSON_BUILD_BOOLEAN(true)));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
* that we can assign a new reply shortly. */
varlink_clear_current(v);
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
* that we can assign a new reply shortly. */
varlink_clear_current(v);
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
r = sd_json_buildo(
&m,
SD_JSON_BUILD_PAIR("method", SD_JSON_BUILD_STRING(method)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
SD_JSON_BUILD_PAIR("more", SD_JSON_BUILD_BOOLEAN(true)));
if (r < 0)
return varlink_log_errno(v, r, "Failed to build json message: %m");
VARLINK_PENDING_METHOD, VARLINK_PENDING_METHOD_MORE))
return -EBUSY;
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
- r = sd_json_buildo(&m, SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to build json message: %m");
-
+ /* Validate parameters BEFORE sanitization */
if (v->current_method) {
const char *bad_field = NULL;
v->current_method->name, strna(bad_field));
}
+ r = sd_json_buildo(&m, JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to build json message: %m");
+
r = varlink_enqueue_json(v, m);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
* the callers don't need to do this explicitly. */
sd_varlink_reset_fds(v);
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
- r = sd_json_buildo(
- &m,
- SD_JSON_BUILD_PAIR("error", SD_JSON_BUILD_STRING(error_id)),
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)));
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to build json message: %m");
-
+ /* Validate parameters BEFORE sanitization */
sd_varlink_symbol *symbol = hashmap_get(v->server->symbols, error_id);
if (!symbol)
varlink_log(v, "No interface description defined for error '%s', not validating.", error_id);
error_id, strna(bad_field));
}
+ r = sd_json_buildo(
+ &m,
+ SD_JSON_BUILD_PAIR("error", SD_JSON_BUILD_STRING(error_id)),
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters));
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to build json message: %m");
+
r = varlink_enqueue_json(v, m);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
if (!IN_SET(v->state, VARLINK_PROCESSING_METHOD_MORE, VARLINK_PENDING_METHOD_MORE))
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection busy.");
- r = varlink_sanitize_parameters(¶meters);
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to sanitize parameters: %m");
-
- r = sd_json_buildo(
- &m,
- SD_JSON_BUILD_PAIR("parameters", SD_JSON_BUILD_VARIANT(parameters)),
- SD_JSON_BUILD_PAIR("continues", SD_JSON_BUILD_BOOLEAN(true)));
- if (r < 0)
- return varlink_log_errno(v, r, "Failed to build json message: %m");
-
+ /* Validate parameters BEFORE sanitization */
if (v->current_method) {
const char *bad_field = NULL;
v->current_method->name, strna(bad_field));
}
+ r = sd_json_buildo(
+ &m,
+ JSON_BUILD_PAIR_VARIANT_NON_EMPTY("parameters", parameters),
+ SD_JSON_BUILD_PAIR("continues", SD_JSON_BUILD_BOOLEAN(true)));
+ if (r < 0)
+ return varlink_log_errno(v, r, "Failed to build json message: %m");
+
r = varlink_enqueue_json(v, m);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enqueue json message: %m");
/* A wrapper around json_dispatch_full() that returns a nice InvalidParameter error if we hit a problem with some field. */
+ /* sd_json_dispatch_full() now handles NULL parameters gracefully */
r = sd_json_dispatch_full(parameters, dispatch_table, /* bad= */ NULL, /* flags= */ 0, userdata, &bad_field);
if (r < 0) {
if (bad_field)