/* Spa */ /* SPDX-FileCopyrightText: Copyright © 2026 Arun Raghavan */ /* SPDX-License-Identifier: MIT */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include 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 \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; }