sd-varlink: export sd_varlink_set_sentinel
I tried to implement a varlink service using sd-varlink, and not being able to use the approach with sentinel is exteremely painful. This is useful internally and likewise externally.
This commit is contained in:
committed by
Daan De Meyer
parent
34c82cbe2f
commit
ab3a2f375f
@@ -22,7 +22,6 @@
|
||||
#include "pretty-print.h"
|
||||
#include "string-util.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
static int status_entries(
|
||||
const BootConfig *config,
|
||||
@@ -705,7 +704,7 @@ int vl_method_list_boot_entries(sd_varlink *link, sd_json_variant *parameters, s
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.BootControl.NoSuchBootEntry");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.BootControl.NoSuchBootEntry");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "uid-classification.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink-dynamic-user.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
typedef struct LookupParameters {
|
||||
const char *user_name;
|
||||
@@ -78,7 +77,7 @@ int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_
|
||||
if (!streq_ptr(p.service, "io.systemd.DynamicUser"))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -182,7 +181,7 @@ int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ int vl_method_enqueue_marked_jobs_manager(sd_varlink *link, sd_json_variant *par
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, NULL);
|
||||
r = sd_varlink_set_sentinel(link, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ int vl_method_list_units(sd_varlink *link, sd_json_variant *parameters, sd_varli
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
r = varlink_set_sentinel(link, VARLINK_ERROR_UNIT_NO_SUCH_UNIT);
|
||||
r = sd_varlink_set_sentinel(link, VARLINK_ERROR_UNIT_NO_SUCH_UNIT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "user-record.h"
|
||||
#include "user-record-util.h"
|
||||
#include "user-util.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
typedef struct LookupParameters {
|
||||
const char *user_name;
|
||||
@@ -104,7 +103,7 @@ int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_
|
||||
if (!streq_ptr(p.service, m->userdb_service))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -212,7 +211,7 @@ int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd
|
||||
if (!streq_ptr(p.service, m->userdb_service))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -277,7 +276,7 @@ int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_
|
||||
if (!streq_ptr(p.service, m->userdb_service))
|
||||
return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -1837,7 +1837,7 @@ static int vl_method_list_transfers(sd_varlink *link, sd_json_variant *parameter
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.Import.NoTransfers");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.Import.NoTransfers");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "strv.h"
|
||||
#include "unit-name.h" /* IWYU pragma: keep */
|
||||
#include "user-util.h"
|
||||
#include "varlink-util.h"
|
||||
|
||||
typedef struct GetEntriesParameters {
|
||||
char **units;
|
||||
@@ -100,7 +99,7 @@ int vl_method_get_entries(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.JournalAccess.NoEntries");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.JournalAccess.NoEntries");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -1090,3 +1090,8 @@ LIBSYSTEMD_260 {
|
||||
global:
|
||||
sd_session_get_extra_device_access;
|
||||
} LIBSYSTEMD_259;
|
||||
|
||||
LIBSYSTEMD_261 {
|
||||
global:
|
||||
sd_varlink_set_sentinel;
|
||||
} LIBSYSTEMD_260;
|
||||
|
||||
@@ -2945,6 +2945,31 @@ _public_ void* sd_varlink_get_userdata(sd_varlink *v) {
|
||||
return v->userdata;
|
||||
}
|
||||
|
||||
_public_ int sd_varlink_set_sentinel(sd_varlink *v, const char *error_id) {
|
||||
assert_return(v, -EINVAL);
|
||||
|
||||
/* If the caller doesn't want a reply, then don't set a sentinel. */
|
||||
if (v->state == VARLINK_PROCESSING_METHOD_ONEWAY)
|
||||
return 0;
|
||||
|
||||
/* This has to be called during a callback, and not after it has exited. */
|
||||
assert_return(IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE),
|
||||
-EUCLEAN);
|
||||
|
||||
char *s = NULL;
|
||||
if (error_id) {
|
||||
s = strdup(error_id);
|
||||
if (!s)
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
if (v->sentinel != POINTER_MAX)
|
||||
free(v->sentinel);
|
||||
|
||||
v->sentinel = s ?: POINTER_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int varlink_acquire_ucred(sd_varlink *v) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -205,31 +205,6 @@ int varlink_check_privileged_peer(sd_varlink *vl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int varlink_set_sentinel(sd_varlink *v, const char *error_id) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
|
||||
assert(v);
|
||||
|
||||
/* If the caller doesn't want a reply, then don't set a sentinel. */
|
||||
if (v->state == VARLINK_PROCESSING_METHOD_ONEWAY)
|
||||
return 0;
|
||||
|
||||
/* This has to be called during a callback, and not after it has exited. */
|
||||
assert(IN_SET(v->state, VARLINK_PROCESSING_METHOD, VARLINK_PROCESSING_METHOD_MORE));
|
||||
|
||||
if (error_id) {
|
||||
s = strdup(error_id);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (v->sentinel != POINTER_MAX)
|
||||
free(v->sentinel);
|
||||
|
||||
v->sentinel = s ? TAKE_PTR(s) : POINTER_MAX;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
varlink_hash_ops,
|
||||
void,
|
||||
|
||||
@@ -28,6 +28,4 @@ int varlink_server_new(
|
||||
|
||||
int varlink_check_privileged_peer(sd_varlink *vl);
|
||||
|
||||
int varlink_set_sentinel(sd_varlink *v, const char *error_id);
|
||||
|
||||
extern const struct hash_ops varlink_hash_ops;
|
||||
|
||||
@@ -534,7 +534,7 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, VARLINK_ERROR_MACHINE_NO_SUCH_MACHINE);
|
||||
r = sd_varlink_set_sentinel(link, VARLINK_ERROR_MACHINE_NO_SUCH_MACHINE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -681,7 +681,7 @@ static int vl_method_list_images(sd_varlink *link, sd_json_variant *parameters,
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, VARLINK_ERROR_MACHINE_IMAGE_NO_SUCH_IMAGE);
|
||||
r = sd_varlink_set_sentinel(link, VARLINK_ERROR_MACHINE_IMAGE_NO_SUCH_IMAGE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -5420,7 +5420,7 @@ static int vl_method_read_event_log(sd_varlink *link, sd_json_variant *parameter
|
||||
return r;
|
||||
|
||||
// FIXME: We can't use a NULL sentinel here because the output fields in the IDL are non-nullable.
|
||||
r = varlink_set_sentinel(link, NULL);
|
||||
r = sd_varlink_set_sentinel(link, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+1
-1
@@ -10427,7 +10427,7 @@ static int vl_method_list_candidate_devices(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.Repart.NoCandidateDevices");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.Repart.NoCandidateDevices");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ int metrics_method_describe(
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.Metrics.NoSuchMetric");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.Metrics.NoSuchMetric");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -127,7 +127,7 @@ int metrics_method_list(
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.Metrics.NoSuchMetric");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.Metrics.NoSuchMetric");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
+1
-1
@@ -2778,7 +2778,7 @@ static int vl_method_list(sd_varlink *link, sd_json_variant *parameters, sd_varl
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.sysext.NoImagesFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.sysext.NoImagesFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -205,6 +205,11 @@ int sd_varlink_bind_reply(sd_varlink *v, sd_varlink_reply_t reply);
|
||||
void* sd_varlink_set_userdata(sd_varlink *v, void *userdata);
|
||||
void* sd_varlink_get_userdata(sd_varlink *v);
|
||||
|
||||
/* Queue a reply to be sent if no other reply was sent by a method callback.
|
||||
* Useful when implementing services which send a (possibly empty) series
|
||||
* of objects and terminate. */
|
||||
int sd_varlink_set_sentinel(sd_varlink *v, const char *error_id);
|
||||
|
||||
int sd_varlink_get_peer_uid(sd_varlink *v, uid_t *ret);
|
||||
int sd_varlink_get_peer_gid(sd_varlink *v, gid_t *ret);
|
||||
int sd_varlink_get_peer_pid(sd_varlink *v, pid_t *ret);
|
||||
|
||||
@@ -443,7 +443,7 @@ TEST(invalid_parameter) {
|
||||
|
||||
static int method_with_error_sentinel(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
/* Set an error sentinel and return without sending a reply. The sentinel error should be sent automatically. */
|
||||
ASSERT_OK(varlink_set_sentinel(link, "io.test.SentinelError"));
|
||||
ASSERT_OK(sd_varlink_set_sentinel(link, "io.test.SentinelError"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -482,7 +482,7 @@ TEST(sentinel_error) {
|
||||
|
||||
static int method_with_empty_sentinel(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
/* Set an empty sentinel and return without sending a reply. An empty reply should be sent automatically. */
|
||||
ASSERT_OK(varlink_set_sentinel(link, /* error_id= */ NULL));
|
||||
ASSERT_OK(sd_varlink_set_sentinel(link, /* error_id= */ NULL));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -522,7 +522,7 @@ TEST(sentinel_empty) {
|
||||
|
||||
static int method_with_sentinel_but_reply(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
/* Set a sentinel but also send a reply. The sentinel should not be used. */
|
||||
ASSERT_OK(varlink_set_sentinel(link, "io.test.SentinelError"));
|
||||
ASSERT_OK(sd_varlink_set_sentinel(link, "io.test.SentinelError"));
|
||||
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_STRING("result", "explicit-reply"));
|
||||
}
|
||||
|
||||
@@ -561,10 +561,10 @@ TEST(sentinel_with_explicit_reply) {
|
||||
}
|
||||
|
||||
static int method_with_oneway_sentinel(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
/* The method was called oneway, so varlink_set_sentinel() should be a no-op and the server should
|
||||
/* The method was called oneway, so sd_varlink_set_sentinel() should be a no-op and the server should
|
||||
* transition back to idle without sending any reply. */
|
||||
ASSERT_TRUE(FLAGS_SET(flags, SD_VARLINK_METHOD_ONEWAY));
|
||||
ASSERT_OK(varlink_set_sentinel(link, "io.test.SentinelError"));
|
||||
ASSERT_OK(sd_varlink_set_sentinel(link, "io.test.SentinelError"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -619,7 +619,7 @@ static int method_with_fd_sentinel(sd_varlink *link, sd_json_variant *parameters
|
||||
|
||||
/* Set a sentinel so sd_varlink_reply() defers sending: each reply and its pushed fds are captured in
|
||||
* the queue, and the last one is sent as the final reply when the callback returns. */
|
||||
ASSERT_OK(varlink_set_sentinel(link, /* error_id= */ NULL));
|
||||
ASSERT_OK(sd_varlink_set_sentinel(link, /* error_id= */ NULL));
|
||||
|
||||
/* First reply: push one fd with "alpha" content */
|
||||
ASSERT_OK(fd1 = memfd_new_and_seal_string("data", "alpha"));
|
||||
|
||||
@@ -172,7 +172,7 @@ static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *paramete
|
||||
* we are done'; == 0 means 'not processed, caller should process now' */
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -313,7 +313,7 @@ static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *paramet
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -401,7 +401,7 @@ static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *paramete
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user