pipewire/spa/tests/varlink-call.c
2026-03-09 16:52:27 -07:00

260 lines
6.5 KiB
C

/* Spa */
/* SPDX-FileCopyrightText: Copyright © 2026 Arun Raghavan */
/* SPDX-License-Identifier: MIT */
#include "config.h"
#include <dlfcn.h>
#include <spa/support/log.h>
#include <spa/support/loop.h>
#include <spa/support/plugin.h>
#include <spa/support/varlink.h>
#include <spa/utils/dict.h>
#include <spa/utils/hook.h>
#include <spa/utils/names.h>
#include <spa/utils/json-core.h>
#include <spa/utils/result.h>
#include <time.h>
struct data {
const char *plugin_dir;
struct spa_support support[16];
uint32_t n_support;
struct spa_log *log;
struct spa_loop *loop;
struct spa_loop_control *loop_control;
struct spa_loop_utils *loop_utils;
struct spa_system *system;
struct spa_varlink *varlink;
struct spa_varlink_client *client;
struct spa_hook listener;
bool running;
};
static int load_handle(struct data *data, struct spa_handle **handle, const
char *lib, const char *name, struct spa_dict *info)
{
int res;
void *hnd;
spa_handle_factory_enum_func_t enum_func;
uint32_t i;
char *path;
if ((path = spa_aprintf("%s/%s", data->plugin_dir, lib)) == NULL)
return -ENOMEM;
hnd = dlopen(path, RTLD_NOW);
free(path);
if (hnd == NULL) {
printf("can't load %s: %s\n", lib, dlerror());
return -ENOENT;
}
if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
printf("can't find enum function\n");
res = -ENOENT;
goto exit_cleanup;
}
for (i = 0;;) {
const struct spa_handle_factory *factory;
if ((res = enum_func(&factory, &i)) <= 0) {
if (res != 0)
printf("can't enumerate factories: %s\n", spa_strerror(res));
break;
}
if (factory->version < 1)
continue;
if (!spa_streq(factory->name, name))
continue;
*handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
if ((res = spa_handle_factory_init(factory, *handle,
info, data->support,
data->n_support)) < 0) {
printf("can't make factory instance: %d\n", res);
goto exit_cleanup;
}
return 0;
}
return -EBADF;
exit_cleanup:
dlclose(hnd);
return res;
}
static int init(struct data *data)
{
struct spa_handle *handle;
struct spa_dict_item items[1];
struct spa_dict info;
const char *str;
void *iface;
int res;
if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
str = PLUGINDIR;
data->plugin_dir = str;
/* enable the debug messages in SPA */
items[0] = SPA_DICT_ITEM_INIT(SPA_KEY_LOG_TIMESTAMP, "true");
info = SPA_DICT_ARRAY(items);
if ((res = load_handle(data, &handle, "support/libspa-support.so",
SPA_NAME_SUPPORT_LOG, &info)) < 0)
return res;
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Log, &iface)) < 0) {
printf("can't get System interface %d\n", res);
return res;
}
data->log = iface;
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data->log);
if ((str = getenv("SPA_DEBUG")))
data->log->level = atoi(str);
/* load and set support system */
if ((res = load_handle(data, &handle,
"support/libspa-support.so",
SPA_NAME_SUPPORT_SYSTEM, NULL)) < 0)
return res;
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) {
printf("can't get System interface %d\n", res);
return res;
}
data->system = iface;
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, data->system);
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, data->system);
/* load and set support loop and loop control */
if ((res = load_handle(data, &handle,
"support/libspa-support.so",
SPA_NAME_SUPPORT_LOOP, NULL)) < 0)
return res;
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) {
printf("can't get interface %d\n", res);
return res;
}
data->loop = iface;
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, data->loop);
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, data->loop);
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopControl, &iface)) < 0) {
printf("can't get interface %d\n", res);
return res;
}
data->loop_control = iface;
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopControl, data->loop_control);
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopUtils, &iface)) < 0) {
printf("can't get interface %d\n", res);
return res;
}
data->loop_utils = iface;
data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopUtils, data->loop_utils);
/* load varlink */
if ((res = load_handle(data, &handle,
"support/libspa-varlink.so",
SPA_NAME_SUPPORT_VARLINK, NULL)) < 0)
return res;
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Varlink, &iface)) < 0) {
printf("can't get interface %d\n", res);
return res;
}
data->varlink = iface;
return 0;
}
static void
on_reply(void *userdata, const char *params, const char *error, size_t len, bool continues)
{
struct data *data = userdata;
if (params) {
printf("Got reply: params: %.*s, continues: %s\n",
(int) len, params, continues ? "true" : "false");
} else {
printf("Got reply: error: %.*s\n", (int) len, error);
}
data->running = false;
}
static void on_disconnect(void *userdata)
{
struct data *data = userdata;
printf("Disconnected\n");
data->running = false;
}
static void on_destroy(void *userdata)
{
printf("Destroyed\n");
}
int main(int argc, char *argv[])
{
struct data data = { 0 };
struct spa_varlink_client_events events = {
.disconnect = on_disconnect,
.destroy = on_destroy,
};
int res;
bool sync = true;
if (argc < 4 || argc > 5) {
printf("usage: %s <socket> <method> <parameters> <sync: 1|0>\n", argv[0]);
return -1;
}
res = init(&data);
if (res < 0)
return res;
if (argc == 5)
sync = argv[4][0] == '1';
data.running = true;
data.client = spa_varlink_connect(data.varlink, argv[1]);
if (data.client == NULL) {
printf("Could not connect to socket: %s\n", spa_strerror(errno));
return -1;
}
spa_varlink_client_add_listener(data.client, &data.listener, &events, &data);
if (sync) {
char *reply;
res = spa_varlink_client_call_sync(data.client, argv[2], argv[3], &reply);
if (res < 0) {
printf("Call failed: %s\n", spa_strerror(res));
return -1;
}
printf("Got reply (%d): %s\n", res, reply);
} else {
res = spa_varlink_client_call(data.client, argv[2], argv[3], false, false, on_reply, &data);
if (res < 0) {
printf("Could not connect to socket: %s\n", spa_strerror(res));
return -1;
}
while (data.running) {
spa_loop_control_iterate(data.loop_control, 1000);
}
}
spa_varlink_client_destroy(data.client);
return 0;
}