plugin.c
1 // SPDX-FileCopyrightText: 2023-2025 Le'Sec Core collective 2 // 3 // SPDX-License-Identifier: LGPL-3.0-or-later 4 5 #include <stdlib.h> 6 #include <stdarg.h> 7 #include <stdbool.h> 8 #include <string.h> 9 #include <lecore/plugin.h> 10 11 // Get bread&butter implementations 12 #define LeCore_t LeCore_plugin_t 13 #define do_get_application_data_reference_fn get_plugin_appdata_reference_fn 14 #define do_set_application_data do_set_plugin_appdata 15 #define do_set_application_data_args LeCore_set_plugin_application_data_args 16 #define do_get_application_data do_get_plugin_appdata 17 #define do_get_application_data_args LeCore_get_plugin_application_data_args 18 #define LE_CORE_IMPL_APPDATA_FUNCTIONS 19 #include <lecore/core_impl.inc> 20 21 struct plugin_data_t { 22 LeCore_env_t parent_env; 23 24 // Appdata, to be populated by diverse applications, among others the plugin 25 // itself, for which slot zero is reserved. 26 size_t appdata_elems; 27 void **appdata; 28 }; 29 30 static LE_STATUS appdata_slot_int(LeCore_plugin_t subject, _Bool is_plugin, 31 int index, void ***slot) { 32 struct plugin_data_t *data = subject.le_data; 33 34 if (data == NULL || index < 0) 35 return LE_STS_ERROR; 36 37 // Index zero is reserved for plugins 38 if (!is_plugin && index == 0) 39 return LE_STS_ERROR; 40 41 if (index >= data->appdata_elems) { 42 void **appdata = data->appdata; 43 if ((appdata = realloc(appdata, (index + 1) * sizeof(*appdata))) == NULL) 44 return LE_STS_FATAL_ERROR; 45 data->appdata_elems = index + 1; 46 data->appdata = appdata; 47 } 48 49 *slot = &data->appdata[index]; 50 return LE_STS_SUCCESS; 51 } 52 static LE_STATUS appdata_plugin_slot(LeCore_plugin_t subject, int index, 53 void ***slot) { 54 return appdata_slot_int(subject, true, index, slot); 55 } 56 static LE_STATUS appdata_slot(LeCore_plugin_t subject, int index, 57 void ***slot) { 58 return appdata_slot_int(subject, false, index, slot); 59 } 60 61 static LE_STATUS plugin_dispatch(LeCore_plugin_t plugin, int num, ...) 62 { 63 LE_STATUS sts = LE_STS_WARNING; // TODO: Set more precise status 64 struct plugin_data_t *p = plugin.le_data; 65 va_list ap; 66 67 va_start(ap, num); 68 switch (num) { 69 case LeCore_NR_revoke_plugin: { 70 if (plugin.le_data != NULL) { 71 struct plugin_data_t *p = plugin.le_data; 72 free(p->appdata); 73 p->appdata_elems = 0; 74 p->appdata = NULL; 75 free(p); 76 } 77 sts = LE_STS_SUCCESS; 78 break; 79 } 80 case LeCore_NR_set_plugin_application_data: 81 return do_set_plugin_appdata( 82 plugin, va_arg(ap, LeCore_set_plugin_application_data_args), 83 appdata_slot); 84 case LeCore_NR_get_plugin_application_data: 85 return do_get_plugin_appdata( 86 plugin, va_arg(ap, LeCore_get_plugin_application_data_args), 87 appdata_slot); 88 case LeCore_NR_set_plugin_data: { 89 LeCore_set_plugin_data_args args = va_arg(ap, LeCore_set_plugin_data_args); 90 sts = LE_STS_ERROR; 91 if (args.data == NULL) 92 break; // TODO: Set more precise status 93 94 void **slotptr = NULL; 95 if (!LE_status_is_OK(sts = appdata_plugin_slot(plugin, 0, &slotptr))) 96 break; 97 98 *slotptr = args.data; 99 sts = LE_STS_SUCCESS; 100 break; 101 } 102 case LeCore_NR_get_plugin_data: { 103 LeCore_get_plugin_data_args args = va_arg(ap, LeCore_get_plugin_data_args); 104 sts = LE_STS_ERROR; 105 if (args.data == NULL) 106 break; // TODO: Set more precise status 107 108 void **slotptr = NULL; 109 if (!LE_status_is_OK(sts = appdata_plugin_slot(plugin, 0, &slotptr))) 110 break; 111 112 *args.data = *slotptr; 113 sts = LE_STS_SUCCESS; 114 break; 115 } 116 case LeCore_NR_get_plugin_parent_environment: { 117 LeCore_get_plugin_parent_environment_args args = 118 va_arg(ap, LeCore_get_plugin_parent_environment_args); 119 120 sts = LE_STS_ERROR; 121 if (args.parent_env != NULL) { 122 *args.parent_env = p->parent_env; 123 sts = LE_STS_SUCCESS; 124 } 125 break; 126 } 127 } 128 129 va_end(ap); 130 return sts; 131 } 132 133 LE_CORE_EXPORT LE_STATUS LeCore_invoke_plugin(LeCore_plugin_start_fn *start, 134 LeCore_plugin_t *plugin, 135 LeCore_env_t env) 136 { 137 LE_STATUS sts = LE_STS_ERROR; 138 size_t allocation_size = sizeof(struct plugin_data_t); 139 140 if ((plugin->le_data = malloc(allocation_size)) != NULL) { 141 plugin->le_dispatch = plugin_dispatch; 142 plugin->le_dispatch_data = NULL; 143 144 struct plugin_data_t *p = plugin->le_data; 145 146 p->parent_env = env; 147 p->appdata_elems = 0; 148 p->appdata = NULL; 149 150 if (start != NULL && !LE_status_is_OK(sts = start(*plugin))) 151 sts = LeCore_revoke_plugin(NULL, plugin); 152 } 153 return sts; 154 } 155 156 #ifdef TEST_PLUGIN 157 158 // This is the plugin itself 159 160 static LeCore_plugin_start_fn start; 161 static LeCore_plugin_stop_fn stop; 162 163 static LE_STATUS start(LeCore_plugin_t plugin) 164 { 165 LeCore_env_t parent; 166 LE_STATUS sts = LE_STS_ERROR; 167 // This is a very silly throwaway bag 168 static const char *bag_data = "silly bag"; 169 static LeCore_env_bag_t bag = { NULL, NULL, &bag_data }; 170 171 if (LE_status_is_OK(sts = LeCore_get_plugin_parent_environment(plugin, &parent)) 172 && LE_status_is_OK(sts = LeCore_set_plugin_data(plugin, "active"))) 173 sts = LeCore_add_environment_bag(parent, "foo", bag); 174 return sts; 175 } 176 177 static LE_STATUS stop(LeCore_plugin_t plugin) 178 { 179 LeCore_env_t parent; 180 static LeCore_env_bag_t bag; 181 LE_STATUS sts; 182 183 if (LE_status_is_OK(sts = LeCore_get_plugin_parent_environment(plugin, &parent)) 184 && LE_status_is_OK(sts = LeCore_remove_environment_bag(parent, "foo", &bag))) 185 *(const char **)bag.le_data = "EXTERMINATE"; 186 187 LeCore_set_plugin_data(plugin, "inactive"); 188 return sts; 189 } 190 191 int main(int argc, char *argv[]) 192 { 193 LeCore_env_t env; 194 LeCore_env_bag_t bag; 195 LeCore_plugin_t plugin; 196 void *plugin_data = NULL; 197 LE_STATUS sts; 198 int e = 0; 199 200 // Setup 201 if (!LE_status_is_OK(sts = LeCore_create_environment(&env)) 202 || !LE_status_is_OK(sts = LeCore_invoke_plugin(start, &plugin, env)) 203 || !LE_status_is_OK(sts = LeCore_get_plugin_data(plugin, &plugin_data)) 204 || plugin_data == NULL 205 || strcmp(plugin_data, "active") != 0) 206 e++; 207 208 // Find the bag that was added by the plugin start and check it 209 if (!LE_status_is_OK(sts = LeCore_find_environment_bag(env, "foo", &bag)) 210 || bag.le_data == NULL 211 || strcmp(*(const char **)bag.le_data, "silly bag") != 0) 212 e++; 213 214 // Cleanup and check. We still have pointers to that bag 215 if (!LE_status_is_OK(sts = LeCore_revoke_plugin(stop, &plugin)) 216 || !LE_status_is_OK(sts = LeCore_destroy_environment(&env)) 217 || strcmp(*(const char **)bag.le_data, "EXTERMINATE") != 0) 218 e++; 219 220 exit(e); 221 } 222 #endif