1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
|
/*-------------------------------------------------------------------------
* C-Pluff, a plug-in framework for C
* Copyright 2007 Johannes Lehtinen
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*-----------------------------------------------------------------------*/
/**
* @page cMainProgram Main program
*
* @section cMainProgramOverview Overview
*
* The main program is the part of executable that is located outside the
* plug-in framework. The main program is responsible for setting up
* the plug-in framework and for loading the desired set of
* @ref plugin "plug-ins". The main program should preferably be very
* thin, a mere plug-in loader, because it can not fully participate in plug-in
* interaction. C-Pluff distribution provides a plug-in loader,
* @ref cpluff-loader, which can be used as a generic main program for
* arbitrary plug-in collections.
*
* @section cMainProgramResponsibilities Responsibilities
*
* The main program has several responsibilities:
*
* - @ref cMainProgramInitFramework "initializing the plug-in framework"
* - @ref cMainProgramCreateContext "creating a plug-in context"
* - @ref cMainProgramLoad "loading plug-ins"
* - @ref cMainProgramExec "controlling plug-in execution"
* - @ref cMainProgramChange "changing plug-in configuration" (opt.)
* - @ref cMainProgramDestroyFramework "destroying the plug-in framework"
*
* @subsection cMainProgramInitFramework Initializing the plug-in framework
*
* Plug-in framework, or the C-Pluff library, must be initialized before its
* services can be used.
* Initialization is not a thread-safe operation and should generally be
* done by the main program before any additional plug-in framework accessing
* threads are started. Initialization is done by calling ::cp_init.
* Additionally, the main program can use ::cp_set_fatal_error_handler to register
* a function that is called when a fatal error occurs. A fatal error is one
* that prevents the framework from continuing operation. For example,
* errors in operating system locking operations and a NULL pointer being
* passed as an argument which is expected to have a non-NULL value are fatal
* erors.
*
* Here is an example of possible initialization code.
*
* @code
* #include <locale.h>
* #include <cpluff.h>
*
* void handle_fatal_error(const char *msg) {
*
* // ... log error, flush logs, send bug report, etc. ...
*
* fprintf(stderr, "A fatal error occurred: %s\n", msg);
* abort();
* }
*
* void initialize(void) {
* cp_status_t status;
*
* setlocale(LC_ALL, "");
* cp_set_fatal_error_handler(handle_fatal_error);
* status = cp_init();
* if (status != CP_OK) {
* // ... handle initialization failure ...
* }
* }
* @endcode
*
* @subsection cMainProgramCreateContext Creating a plug-in context
*
* A plug-in context represents the co-operation environment of a set of
* plug-ins from the perspective of a particular participating plug-in or
* the perspective of the main program. From main program perspective, a
* plug-in context is a container for a set of plug-ins. A plug-in can interact
* with other plug-ins in the same container.
*
* An extensible application can have more than one plug-in container but
* usually one container should suffice. Due to the nature of C programs,
* plug-ins deployed to different containers are not very well insulated from
* each other. For example, global variables provided by a plug-in in one
* container are visible to all plug-ins in all containers. Also, by placing
* all plug-ins in the same container they can more efficiently share common
* base components which themselves might provide extensibility.
*
* A main program creates a plug-in context, to be used as a container for
* plugins, using ::cp_create_context.
*
* @code
* #include <cpluff.h>
*
* cp_context_t *ctx;
*
* void create_context(void) {
* cp_status_t status;
*
* ctx = cp_create_context(&status);
* if (ctx == NULL) {
* // ... handle initialization failure ...
* }
* }
* @endcode
*
* @subsection cMainProgramLoad Loading plug-ins
*
* An extensible application is made of plug-ins that can be added and removed
* dynamically. The plug-ins are loaded by the main program using the services
* provided by the framework. The framework provides couple of alternative
* ways of loading plug-ins.
*
* As a lowest level operation, the main program can
* load individual plug-ins from known locations using
* ::cp_load_plugin_descriptor and ::cp_install_plugin. Here is example code
* that loads a set of plug-ins from file system locations listed in a file.
*
* @code
* #include <stdio.h>
* #include <cpluff.h>
*
* extern cp_context_t *ctx;
* static const char pluginListFile[] = "/etc/example/plugins.list";
*
* void load_plugins(void) {
* char plugindir[128];
* FILE *lf;
*
* // Open plug-in list file
* lf = fopen(pluginListFile, "r");
* if (lf == NULL) {
* // ... handle loading failure ...
* }
*
* // Load each listed plug-in
* while (fgets(plugindir, 128, lf) != NULL) {
* cp_plugin_info_t *plugininfo;
* cp_status_t status;
* int i;
*
* // Remove possible trailing newline from plug-in location
* for (i = 0; plugindir[i + 1] != '\0'; i++);
* if (plugindir[i] == '\n') {
* plugindir[i] = '\0';
* }
*
* // Load plug-in descriptor
* plugininfo = cp_load_plugin_descriptor(ctx, plugindir, &status);
* if (pinfo == NULL) {
* // ... handle loading failure ...
* }
*
* // Install plug-in descriptor
* status = cp_install_plugin(ctx, plugininfo);
* if (status != CP_OK) {
* // ... handle loading failure ...
* }
*
* // Release plug-in descriptor information
* cp_release_info(ctx, plugininfo);
* }
*
* // Close plug-in list file
* fclose(lf);
* }
* @endcode
*
* Alternatively, the main program can register and load plug-in collections.
* A plug-in collection is a file system directory which includes individual
* plug-ins in subdirectories, one plug-in in each subdirectory. Plug-in
* collections can be registered with a plug-in context using
* ::cp_register_pcollection. Plug-ins of the collection can then be scanned
* and loaded using ::cp_scan_plugins. Here is example code loading plug-ins
* from a plug-in collection.
*
* @code
* #include <cpluff.h>
*
* extern cp_context_t *ctx;
* static const char pluginCollectionDir[] = "/etc/example/plugins";
*
* void load_plugins(void) {
* cp_status_t status;
*
* status = cp_register_pcollection(ctx, pluginCollectionDir);
* if (status != CP_OK) {
* // ... handle loading failure ...
* }
* status = cp_scan_plugins(ctx, 0);
* if (status != CP_OK) {
* // ... handle loading failure ...
* // (notice that some plug-ins might have been loaded)
* }
* }
* @endcode
*
* @subsection cMainProgramExec Controlling plug-in execution
*
* The main program controls plug-in execution by starting and stopping
* plug-ins and by executing run functions registered by plug-ins.
* Additionally, the main program can pass startup arguments to plug-ins.
*
* When plug-ins are installed they are not yet activated and their
* runtime library is not even loaded at that point. The main program
* typically activates plug-ins by starting a main plug-in
* responsible for user interface or core application logic. This plug-in
* then implicitly causes other plug-ins to be activated via dependencies and
* by dynamically resolving symbols provided by other plug-ins. Plug-ins
* recursively activate each other until all initially needed plug-ins have
* been started. Some plug-ins might be activated at a later time when their
* functionality is needed, for example due to user action.
*
* If a plug-in needs to perform background operations, that is operations
* executed outside the invocation of plug-in provided interface functions,
* then it can either start a new thread or it can register a run function.
* A run function is a function that is typically executed as part of the
* main loop by the main program.
*
* The following example code shows how a main program might initialize
* plug-in startup arguments using ::cp_set_context_args, start the core
* plug-in using ::cp_start_plugin and then execute plug-in run functions
* using ::cp_run_plugins.
*
* @code
* #include <cpluff.h>
*
* extern cp_context_t *ctx;
* static const char corePluginId[] = "org.example.core";
*
* void run_plugins(char *argv[]) {
* cp_status_t status;
*
* // Set plug-in startup arguments
* cp_set_context_args(ctx, argv);
*
* // Start the core plug-in, possibly activating other plug-ins as well
* status = cp_start_plugin(ctx, corePluginId);
* if (status != CP_OK) {
* // ... handle startup failure ...
* }
*
* // Execute plug-ins until there is no more work to be done
* cp_run_plugins(ctx);
* }
*
* int main(int argc, char *argv[]) {
* // ... do initialization and load plug-ins ...
*
* run_plugins(argv);
*
* // ... do destruction ...
* }
* @endcode
*
* Alternatively, if the main program has some operations it must perform
* as part of the main loop, the call to ::cp_run_plugins can be replaced
* by code using ::cp_run_plugins_step like in the following example.
*
* @code
* void mainloop(void) {
* int finished = 0;
*
* while (!finished) {
* // ... do main program specific operations ...
*
* finished = !cp_run_plugins_step(ctx);
* }
* }
* @endcode
*
* @subsection cMainProgramChange Changing plug-in configuration
*
* C-Pluff has been designed to allow dynamic changes to the plug-in
* configuration, that is plug-ins being added or removed without shutting
* down the application or the framework. It is the responsibility of the
* main program to manage such changes if the application is to support
* dynamic configuration changes.
*
* Adding plug-ins is straightforward because there is no need to
* consider dependencies of active plug-ins. For example, if one uses
* plug-in collections as introduced above then new plug-ins can be
* deployed under the plug-in collection directory while the application is
* running and the main program can load them incrementally by calling
* ::cp_scan_plugins again. This call might be activated by some user interface
* element, for example a plug-in manager component which just downloaded and
* installed new plug-ins as requested by the user. The flags
* #CP_SP_STOP_ALL_ON_INSTALL and #CP_SP_RESTART_ACTIVE
* orred together can be used to cause all active plug-ins to be restarted
* if they do not otherwise notice the extensions provided by new plug-ins.
*
* Upgrading plug-ins is almost as straightforward because the C-Pluff
* framework manages plug-in dependencies (assuming the plug-ins have
* declared their dependencies properly). The new version of a plug-in
* can be deployed under the plug-in collection directory in a
* new subdirectory parallel to the old version while the application is
* running. The main program can then call ::cp_scan_plugins with
* #CP_SP_UPGRADE and #CP_SP_RESTART_ACTIVE orred together. This will stop
* the old version of the upgraded plug-in (implicitly stopping all plug-ins
* that depend on it), unload the plug-in from the framework, install the
* new version of the plug-in and finally restart plug-ins that were
* active before the operation. The old version of the plug-in can now
* be removed from the plug-in collection. Again, #CP_SP_STOP_ALL_ON_UPGRADE
* can be added to restart all active plug-ins.
*
* Deleting plug-ins must be done by first stopping and unloading the
* plug-in to be deleted using ::cp_uninstall_plugin. The the plug-in can
* be removed from the plug-in collection.
*
* @subsection cMainProgramDestroyFramework Destroying the plug-in framework
*
* The plug-in framework can be destroyed and all resources released by
* calling ::cp_destroy as many times as ::cp_init has been called. This
* is not a thread-safe operation and should generally be done by the main
* program just before application exits. The destroy function
* stops and unloads all plug-ins and destroys all plug-in contexts before
* destroying the core framework.
*
* Individual plug-in contexts can be destroyed by calling
* ::cp_destroy_context. The destroy function stops and unloads all plug-ins
* before destroying the context itself.
*/
|