.TH "Plugins" 3elektra "Tue Aug 19 2014" "Version 0.8.7" "Elektra" \" -*- nroff -*- .ad l .nh .SH NAME Plugins \- .PP Elektra plugin framework\&. .SS "Functions" .in +1c .ti -1c .RI "int \fBdocOpen\fP (Plugin *handle, Key *errorKey)" .br .ti -1c .RI "int \fBdocClose\fP (Plugin *handle, Key *errorKey)" .br .ti -1c .RI "int \fBdocGet\fP (Plugin *handle, KeySet *returned, Key *parentKey)" .br .ti -1c .RI "int \fBdocSet\fP (Plugin *handle, KeySet *returned, Key *parentKey)" .br .ti -1c .RI "int \fBdocError\fP (Plugin *handle, KeySet *returned, Key *parentKey)" .br .ti -1c .RI "Plugin * \fBELEKTRA_PLUGIN_EXPORT\fP (doc)" .br .in -1c .SH "Detailed Description" .PP Elektra plugin framework\&. .PP \fBSince:\fP .RS 4 version 0\&.4\&.9, Elektra can dynamically load different key storage plugins\&. .PP version 0\&.7\&.0 Elektra can have multiple backends, mounted at any place in the key database\&. .PP version 0\&.8\&.0 Elektra backends are composed out of multiple plugins\&. .RE .PP \fBOverview\fP .RS 4 There are different types of plugins for different concerns\&. The types of plugins handled in this document: .IP "\(bu" 2 file storage plugins (also called just storage plugins here) .IP "\(bu" 2 filter plugins .br See http://www.libelektra.org/ftp/elektra/thesis.pdf for an detailed explanation and description of other types of plugins\&. .br A plugin can implement anything related to configuration\&. There are 5 possible entry points, as described in this document: .IP "\(bu" 2 elektraDocOpen() .IP "\(bu" 2 elektraDocClose() .IP "\(bu" 2 elektraDocGet() .IP "\(bu" 2 elektraDocSet() .IP "\(bu" 2 elektraDocError() (not needed by storage or filter plugins) .br Depending of the type of plugin you need not to implement all of them\&. .br .PP .RE .PP \fBNote:\fP .RS 4 that the Doc within the name is just because the plugin described here is called doc (see src/plugins/doc/doc\&.c)\&. Always replace Doc with the name of the plugin you are going to implement\&. .RE .PP See the descriptions below what each of them is supposed to do\&. .PP \fBStorage Plugins\fP .RS 4 A filter plugin is a plugin which already receives some keys\&. It may process or change the keyset\&. Or it may reject specific keysets which do not meet some criteria\&. .RE .PP \fBFilter Plugins\fP .RS 4 A storage plugin gets an empty keyset and constructs the information out from a file\&. .br Other persistent storage then a file is not handled within this document because it involves many other issues\&. For files the resolver plugin already takes care for transactions and rollback\&. .RE .PP \fBError and Warnings\fP .RS 4 In any case of trouble, use ELEKTRA_SET_ERROR and return with -1\&. You might add warnings with ELEKTRA_ADD_WARNING if you think it is appropriate\&. .RE .PP \fBNote:\fP .RS 4 some docu in this section might be confusing or not updated, please refer to http://www.libelektra.org/ftp/elektra/thesis.pdf or ask at the mailinglist if something is unclear\&. .RE .PP .SH "Function Documentation" .PP .SS "int docClose (Plugin *handle, Key *errorKey)" Finalize the plugin\&. .PP Called prior to unloading the plugin dynamic module\&. Should ensure that no functions or static/global variables from the module will ever be accessed again\&. .PP Make sure to free all memory that your plugin requested at runtime\&. .PP After this call, libelektra\&.so will unload the plugin library, so this is the point to shutdown any affairs with the storage\&. .PP \fBParameters:\fP .RS 4 \fIhandle\fP contains internal information of the plugin .br \fIerrorKey\fP is needed to add warnings using ELEKTRA_ADD_WARNING .RE .PP \fBReturn values:\fP .RS 4 \fI0\fP on success .RE .PP \fBSee also:\fP .RS 4 \fBkdbClose()\fP .PP elektraPluginGetData(), elektraPluginSetData() and elektraPluginGetConfig() .RE .PP .SS "int docError (Plugin *handle, KeySet *returned, Key *parentKey)" Rollback in case of errors\&. .PP \fBParameters:\fP .RS 4 \fIhandle\fP contains internal information of the plugin .br \fIreturned\fP contains a keyset with relevant keys .br \fIparentKey\fP contains the information where to set the keys .RE .PP \fBReturn values:\fP .RS 4 \fI1\fP on success .br \fI0\fP on success with no action .br \fI-1\fP on failure .RE .PP .SS "int docGet (Plugin *handle, KeySet *returned, Key *parentKey)" Retrieve information from a permanent storage to construct a keyset\&. .SH "Introduction" .PP The elektraDocGet() function handle everything related to receiving keys\&. .SS "Storage Plugins" For storage plugins the filename is written in the value of the parentKey\&. So the first task of the plugin is to open that file\&. Then it should parse its content and construct a keyset with all information of that file\&. .PP You need to be able to reconstruct the same file with the information of the keyset\&. So be sure to copy all comments, whitespaces and so on into some metadata of the keys\&. Otherwise the information is lost after writing the file the next time\&. .PP Now lets look at an example how the typical elektraDocGet() might be implemented\&. To explain we introduce some pseudo functions which do all the work with the storage (which is of course 90% of the work for a real plugin): .IP "\(bu" 2 parse_key will parse a key and a value from an open file handle .PP .PP The typical loop for a storage plugin will be like: .PP .nf int elektraDocGet(Plugin *handle, KeySet *returned, const Key *parentKey) { // contract handling, see below FILE *fp = fopen (keyString(parentKey), "r"); char *key; char *value; while ((n = parse_key(fp, &key, &value)) >= 1) { Key *read = keyNew(0); if (keySetName(read, key) == -1) { fclose (fp); keyDel (read); ELEKTRA_SET_ERROR(59, parentKey, key); return -1; } keySetString(read, value); ksAppendKey (returned, read); free (key); free (value); } if (feof(fp) == 0) { fclose (fp); ELEKTRA_SET_ERROR(60, parentKey, "not at the end of file"); return -1; } fclose (fp); return 1; // success } .fi .PP .SS "Filter Plugins" For filter plugins the actual task is rather unspecified\&. You basically can do anything with the keyset\&. To get roundtrip properties you might want to undo any changes you did in elektraDocSet()\&. .PP The pseudo functions (which do the real work) are: .IP "\(bu" 2 do_action() which processes every key in this filter .PP .PP .PP .nf int elektraDocGet(Plugin *handle, KeySet *returned, Key *parentKey) { // contract handling Key *k; ksRewind (returned); while ((k = ksNext (returned)) != 0) { do_action(k); } return 1; // success } .fi .PP .SH "Conditions" .PP \fBPrecondition:\fP .RS 4 The caller \fBkdbGet()\fP will make sure before you are called that the parentKey: .IP "\(bu" 2 is a valid key (means that it is a system or user key)\&. .IP "\(bu" 2 is below (see \fBkeyIsBelow()\fP) your mountpoint and that your plugin is responsible for it\&. and that the returned: .IP "\(bu" 2 is a valid keyset\&. .IP "\(bu" 2 has \fCall\fP keys with the flag KEY_FLAG_SYNC set\&. .IP "\(bu" 2 contains only valid keys direct below (see \fBkeyIsDirectBelow()\fP) your parentKey\&. That also means, that the parentKey will not be in that keyset\&. .IP "\(bu" 2 is in a sorted order (given implicit by semantics of KeySet) and that the handle: .IP " \(bu" 4 is a valid KDB for your plugin\&. .IP " \(bu" 4 that elektraPluginhGetBackendHandle() contains the same handle for lifetime \fBkdbOpen()\fP until elektraPluginClose() was called\&. .PP .PP .PP The caller \fBkdbGet()\fP will make sure that afterwards you were called, whenever the user requested it with the options, that: .IP "\(bu" 2 hidden keys they will be thrown away\&. .IP "\(bu" 2 dirs or only dirs \fBkdbGet()\fP will remove the other\&. .IP "\(bu" 2 you will be called again recursively with all subdirectories\&. .IP "\(bu" 2 the keyset will be sorted when needed\&. .IP "\(bu" 2 the keys in returned having KEY_FLAG_SYNC will be sorted out\&. .PP .RE .PP \fBInvariant:\fP .RS 4 There are no global variables and elektraPluginSetData() stores all information\&. The handle is to be guaranteed to be the same if it is the same plugin\&. .RE .PP \fBPostcondition:\fP .RS 4 The keyset \fCreturned\fP has the \fCparentKey\fP and all keys direct below (\fBkeyIsDirectBelow()\fP) with all information from the storage\&. Make sure to return all keys, all directories and also all hidden keys\&. If some of them are not wished, the caller \fBkdbGet()\fP will drop these keys, see above\&. .RE .PP .SH "Updating" .PP To get all keys out of the storage over and over again can be very inefficient\&. You might know a more efficient method to know if the key needs update or not, e\&.g\&. by stating it or by an external time stamp info\&. For file storage plugins this is automatically done for you\&. For other types (e\&.g\&. databases) you need to implement your own resolver doing this\&. .PP \fBNote:\fP .RS 4 If any calls you use change errno, make sure to restore the old errno\&. .RE .PP \fBSee also:\fP .RS 4 \fBkdbGet()\fP for caller\&. .RE .PP \fBParameters:\fP .RS 4 \fIhandle\fP contains internal information of \fBopened \fP key database .br \fIreturned\fP contains a keyset where the function need to append the keys got from the storage\&. There might be also some keys inside it, see conditions\&. You may use them to support efficient updating of keys, see \fBUpdating\fP\&. .br \fIparentKey\fP contains the information below which key the keys should be gotten\&. .RE .PP \fBReturns:\fP .RS 4 1 on success .PP 0 when nothing was to do .PP -1 on failure, the current key in returned shows the position\&. use ELEKTRA_SET_ERROR of kdberrors\&.h to define the error code .RE .PP .SS "int docOpen (Plugin *handle, Key *errorKey)" Initialize the plugin\&. .PP This is the first method called after dynamically loading this plugin\&. .PP This method is responsible for: .IP "\(bu" 2 plugin's specific configuration gathering .IP "\(bu" 2 all plugin's internal structs initialization .IP "\(bu" 2 if unavoidable initial setup of all I/O details such as opening a file, connecting to a database, setup connection to a server, etc\&. .PP .PP You may also read the configuration you can get with elektraPluginGetConfig() and transform it into other structures used by your plugin\&. .PP \fBNote:\fP .RS 4 The plugin must not have any global variables\&. If you do Elektra will not be threadsafe\&. It is not a good assumption that your plugin will be opened only once\&. .RE .PP Instead you can use elektraPluginGetData() and elektraPluginSetData() to store and get any information related to your plugin\&. .PP The correct substitute for global variables will be: .PP .nf struct _GlobalData{ int global; }; typedef struct _GlobalData GlobalData; int elektraDocOpen(Plugin *handle, Key *errorKey) { GlobalData *data; data=malloc(sizeof(GlobalData)); data\&.global = 20; elektraPluginSetData(handle,data); } .fi .PP .PP If your plugin has no useful way to startup without config, the module loader would not be able to load the module, too\&. To solve that problem the module loader adds the configuration key /module\&. Even if your plugin is basically not able to startup successfully, it should still provide a fallback when /module is present, so that \fBdocGet()\fP on system/elektra/modules can be called successfully later on\&. .PP \fBNote:\fP .RS 4 Make sure to free everything you allocate here within elektraDocClose()\&. .RE .PP \fBReturns:\fP .RS 4 0 on success .RE .PP \fBParameters:\fP .RS 4 \fIhandle\fP contains internal information of the plugin .br \fIerrorKey\fP defines an errorKey .RE .PP \fBSee also:\fP .RS 4 \fBkdbOpen()\fP which will call elektraDocOpen() .PP elektraPluginGetData(), elektraPluginSetData() and elektraPluginGetConfig() .RE .PP .SS "int docSet (Plugin *handle, KeySet *returned, Key *parentKey)" Store a keyset permanently\&. .PP This function does everything related to set and remove keys in a plugin\&. There is only one function for that purpose to make implementation and locking much easier\&. .PP The keyset \fCreturned\fP was filled in with information from the application using elektra and the task of this function is to store it in a permanent way so that a subsequent call of elektraPluginGet() can rebuild the keyset as it was before\&. See the live cycle of a comment to understand: .PP .nf void usercode (Key *key) { keySetComment (key, "mycomment"); // the usercode stores a comment for the key ksAppendKey(keyset, key); // append the key to the keyset kdbSet (handle, keyset, 0, 0); } // so now kdbSet is called int kdbSet(KDB *handle, KeySet *keyset, Key *parentKey, options) { // find appropriate plugin elektraPluginSet (handle, keyset, 0); // the keyset with the key will be passed to this function } // so now elektraPluginSet(), which is the function described here, is called elektraPluginSet(KDB *handle, KeySet *keyset, Key *parentKey) { // the task of elektraPluginSet is now to store the comment Key *key = ksCurrent (keyset); // get out the key where the user set the comment before char *comment = allocate(size); keyGetComment (key, comment, size); savetodisc (comment); } .fi .PP Of course not only the comment, but all information of every key in the keyset \fCreturned\fP need to be stored permanetly\&. So this specification needs to give an exhaustive list of information present in a key\&. .PP \fBPrecondition:\fP .RS 4 The keyset \fCreturned\fP holds all keys which must be saved permanently for this keyset\&. The keyset is sorted and rewinded\&. All keys having children must be true for \fBkeyIsDir()\fP\&. .PP The \fCparentKey\fP is the key which is the ancestor for all other keys in the keyset\&. The first key of the keyset \fCreturned\fP has the same keyname\&. The parentKey is below the mountpoint, see kdbhGetMountpoint()\&. .PP The caller kdbSet will fulfill following parts: .IP "\(bu" 2 If the user does not want hidden keys they will be thrown away\&. All keys in \fCreturned\fP need to be stored permanently\&. .IP "\(bu" 2 If the user does not want dirs or only dirs \fBkdbGet()\fP will remove the other\&. .IP "\(bu" 2 Sorting of the keyset\&. It is not important in which order the keys are appended\&. So make sure to set all keys, all directories and also all hidden keys\&. If some of them are not wished, the caller \fBkdbSet()\fP will sort them out\&. .PP .RE .PP \fBInvariant:\fP .RS 4 There are no global variables and kdbhGetBackendData() only stores information which can be regenerated any time\&. The handle is the same when it is the same plugin\&. .RE .PP \fBPostcondition:\fP .RS 4 The information of the keyset \fCreturned\fP is stored permanently\&. .RE .PP Lock your permanent storage in an exclusive way, no access of a concurrent elektraPluginSet_plugin() or \fBkdbGet()\fP is possible and these methods block until the function has finished\&. Otherwise declare kdbcGetnoLock()\&. .PP \fBSee also:\fP .RS 4 \fBkdbSet()\fP for caller\&. .RE .PP \fBParameters:\fP .RS 4 \fIhandle\fP contains internal information of the plugin .br \fIreturned\fP contains a keyset with relevant keys .br \fIparentKey\fP contains the information where to set the keys .RE .PP \fBReturns:\fP .RS 4 When everything works gracefully return the number of keys you set\&. The cursor position and the keys remaining in the keyset are not important\&. .RE .PP \fBNote:\fP .RS 4 If any calls you use change errno, make sure to restore the old errno\&. .RE .PP \fBReturn values:\fP .RS 4 \fI1\fP on success .br \fI0\fP on success with no changed key in database .br \fI-1\fP on failure\&. The cause of the error needs to beadded in parentKey .RE .PP You also have to make sure that \fBksGetCursor()\fP shows to the position where the error appeared\&. .SS "Plugin* ELEKTRA_PLUGIN_EXPORT (doc)" All KDB methods implemented by the plugin can have random names, except ELEKTRA_PLUGIN_EXPORT\&. This is the single symbol that will be looked up when loading the plugin, and the first method of the backend implementation that will be called\&. .PP You need to use a macro so that both dynamic and static loading of the plugin works\&. .PP The first parameter is the name of the plugin\&. Then every plugin should have: \fCELEKTRA_PLUGIN_OPEN\fP, \fCELEKTRA_PLUGIN_CLOSE\fP, \fCELEKTRA_PLUGIN_GET\fP, \fCELEKTRA_PLUGIN_SET\fP and optionally \fCELEKTRA_PLUGIN_ERROR\fP\&. .PP The list is terminated with \fCELEKTRA_PLUGIN_END\fP\&. .PP You must use static 'char arrays' in a read only segment\&. Don't allocate storage, it won't be freed\&. .PP \fBReturns:\fP .RS 4 Plugin .RE .PP \fBSee also:\fP .RS 4 elektraPluginExport() .RE .PP .SH "Author" .PP Generated automatically by Doxygen for Elektra from the source code\&.