/ src / plugin.c
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