From: Fulup Ar Foll Date: Mon, 21 Dec 2015 15:33:23 +0000 (+0100) Subject: Hack --plugins=path X-Git-Tag: blowfish_2.0.1~332 X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=commitdiff_plain;h=5472e85501418302959040eacb819c57f849d63e Hack --plugins=path --- diff --git a/.gitignore b/.gitignore index b47de8ff..6f88c4eb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ dist/* .dep.inc CMakeFiles/ CMakeCache.txt -nbproject/** +nbproject/private/* diff --git a/include/local-def.h b/include/local-def.h index 961fdd2c..8f297c53 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -107,6 +107,13 @@ typedef struct { typedef enum {AFB_POST_NONE=0, AFB_POST_JSON, AFB_POST_FORM} AFB_PostType; +// Post Upload File Handle +typedef struct { + int fd; + char *path; + json_object* jerror; +} AFB_PostCtx; + typedef struct { int len; // post element size char *data; // post data in raw format diff --git a/include/proto-def.h b/include/proto-def.h index 937d6930..ac4f21b9 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -17,19 +17,20 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: $ */ -// Rest-api - -PUBLIC json_object* apiPingTest(AFB_request *request); +// helper-api +PUBLIC json_object* getPingTest(AFB_request *request); PUBLIC const char* getQueryValue (AFB_request * request, char *name); PUBLIC int getQueryAll(AFB_request * request, char *query, size_t len); +PUBLIC AFB_PostHandle* getPostHandle (AFB_request *request); +PUBLIC json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) ; +PUBLIC AFB_PostCtx* getPostContext (AFB_request *request); +// rest-api PUBLIC void endPostRequest(AFB_PostHandle *posthandle); PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char* url, const char *method , const char *upload_data, size_t *upload_data_size, void **con_cls); -PUBLIC AFB_PostHandle* getPostHandle (AFB_request *request); void initPlugins (AFB_session *session); diff --git a/plugins/samples/SamplePost.c b/plugins/samples/SamplePost.c index 223f74e8..56c3f24b 100644 --- a/plugins/samples/SamplePost.c +++ b/plugins/samples/SamplePost.c @@ -19,14 +19,6 @@ #include "local-def.h" - -// In this case or handle is quite basic -typedef struct { - int fd; - char *path; - json_object* jerror; -} appPostCtx; - // With content-type=json data are directly avaliable in request->post->data STATIC json_object* GetJsonByPost (AFB_request *request) { json_object* jresp; @@ -46,122 +38,72 @@ STATIC json_object* GetJsonByPost (AFB_request *request) { return (jresp); } -// This function is call when PostForm processing is completed -STATIC void DonePostForm (AFB_request *request) { - - // Retrieve PostHandle Context from request - AFB_PostHandle *postHandle = getPostHandle(request); - appPostCtx *appCtx= (appPostCtx*) postHandle->ctx; - - if (verbose) fprintf (stderr, "DonePostForm file=[%s]upload done\n", appCtx->path); - - -} -// PostForm callback is called multiple times (one or each key within form, or once per file buffer) -// When processing POST_FORM request->data holds a PostHandle and not data directly as for POST_JSON -// When file has been fully uploaded call is call with item==NULL it is application responsibility to free appPostCtx -STATIC json_object* UploadFile (AFB_request *request, AFB_PostItem *item) { +// Upload a file and execute a function when upload is done +STATIC json_object* UploadAppli (AFB_request *request, AFB_PostItem *item) { + + char *destination = "applications"; - AFB_PostHandle *postHandle = getPostHandle(request); - appPostCtx *appCtx; - char filepath[512]; - int len; - // This is called after PostForm and then after DonePostForm if (item == NULL) { - json_object* jresp; - appCtx = (appPostCtx*) postHandle->ctx; - - // No Post Application Context [something really bad happen] - if (appCtx == NULL) { - request->errcode = MHD_HTTP_EXPECTATION_FAILED; - return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n")); + AFB_PostCtx *postFileCtx = getPostContext(request); + if (postFileCtx != NULL) { + + // request Application Framework to install application + + request->errcode = MHD_HTTP_OK; // or error is something went wrong; + request->jresp = jsonNewMessage(AFB_FAIL,"UploadFile Post Request file=[%s] done", postFileCtx->path); } - - // We have a context but last Xform iteration fail. - if (appCtx->jerror != NULL) { - request->errcode = MHD_HTTP_EXPECTATION_FAILED; - jresp = appCtx->jerror; // retrieve previous error from postCtx - } else jresp = jsonNewMessage(AFB_FAIL,"UploadFile Post Request file=[%s] done", appCtx->path); - - // Error or not let's free all resources - close(appCtx->fd); - free (appCtx->path); - free (appCtx); - return (jresp); } - // Make sure it's a valid PostForm request - if (!request->post && request->post->type != AFB_POST_FORM) { - appCtx->jerror= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n"); - goto ExitOnError; - } - - // Check this is a file element - if (item->filename == NULL) { - appCtx->jerror= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key); - goto ExitOnError; - } - - // Check we got something in buffer - if (item->len <= 0) { - appCtx->jerror= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key); - goto ExitOnError; - } + // upload multi iteration logic is handle by getPostedFile + return (getPostFile (request, item, destination)); +} - // Extract Application Context from posthandle [NULL == 1st iteration] - appCtx = (appPostCtx*) postHandle->ctx; +// Simples Upload case just upload a file +STATIC json_object* UploadMusic (AFB_request *request, AFB_PostItem *item) { + + char *destination = "musics"; - // This is the 1st Item iteration let's open output file and allocate necessary resources - if (appCtx == NULL) { - // Create an application specific context - appCtx = calloc (1, sizeof(appPostCtx)); // May place anything here until post->completeCB handle resources liberation - appCtx->path = strdup (filepath); - - // attach application to postHandle - postHandle->ctx = (void*) appCtx; // May place anything here until post->completeCB handle resources liberation - - // Allocate an application specific handle to this post - strncpy (filepath, request->config->sessiondir, sizeof(filepath)); - strncat (filepath, "/", sizeof(filepath)); - strncat (filepath, item->filename, sizeof(filepath)); + // upload multi iteration logic is handle by getPostedFile + return (getPostFile (request, item, destination)); +} - if((appCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) < 0) { - appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to Create destination=[%s] error=%s\n", filepath, strerror(errno)); - goto ExitOnError; - } - } else { - // reuse existing application context - appCtx = (appPostCtx*) postHandle->ctx; - } +// PostForm callback is called multiple times (one or each key within form, or once per file buffer) +// When file has been fully uploaded call is call with item==NULL +STATIC json_object* UploadImage (AFB_request *request, AFB_PostItem *item) { + + // note if directory is relative it will be prefixed by request->config->sessiondir + char *destination = "images"; - // Check we successfully wrote full buffer - len = write (appCtx->fd, item->data, item->len); - if (item->len != len) { - appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno)); - goto ExitOnError; + // This is called after PostForm and then after DonePostForm + if (item == NULL) { + AFB_PostCtx *postFileCtx = getPostContext(request); + + // if postFileCtx == NULL then an error happen [getPostedFile automatically reports errors] + if (postFileCtx != NULL) { + // Do something with your newly upload filepath=postFileCtx->path + request->errcode = MHD_HTTP_OK; + request->jresp = jsonNewMessage(AFB_FAIL,"UploadFile Post Request file=[%s] done", postFileCtx->path); + + // Note: should not return here in order getPostedFile to clear Post resources. + } } - - // every intermediary iteration should return Success & NULL - request->errcode = MHD_HTTP_OK; - return NULL; -ExitOnError: - request->errcode = MHD_HTTP_EXPECTATION_FAILED; - return NULL; + // upload multi iteration logic is handle by getPostedFile + return (getPostFile (request, item, destination)); } // NOTE: this sample does not use session to keep test a basic as possible // in real application upload-xxx should be protected with AFB_SESSION_CHECK STATIC AFB_restapi pluginApis[]= { - {"ping" , AFB_SESSION_NONE , (AFB_apiCB)apiPingTest ,"Ping Rest Test Service"}, + {"ping" , AFB_SESSION_NONE , (AFB_apiCB)getPingTest ,"Ping Rest Test Service"}, {"upload-json" , AFB_SESSION_NONE , (AFB_apiCB)GetJsonByPost ,"Demo for Json Buffer on Post"}, - {"upload-image" , AFB_SESSION_NONE , (AFB_apiCB)UploadFile ,"Demo for file upload"}, - {"upload-music" , AFB_SESSION_NONE , (AFB_apiCB)UploadFile ,"Demo for file upload"}, - {"upload-appli" , AFB_SESSION_NONE , (AFB_apiCB)UploadFile ,"Demo for file upload"}, + {"upload-image" , AFB_SESSION_NONE , (AFB_apiCB)UploadImage ,"Demo for file upload"}, + {"upload-music" , AFB_SESSION_NONE , (AFB_apiCB)UploadMusic ,"Demo for file upload"}, + {"upload-appli" , AFB_SESSION_NONE , (AFB_apiCB)UploadAppli ,"Demo for file upload"}, {NULL} }; diff --git a/plugins/session/token-api.c b/plugins/session/token-api.c index ba6d2761..585c3432 100644 --- a/plugins/session/token-api.c +++ b/plugins/session/token-api.c @@ -86,7 +86,7 @@ STATIC void clientContextFree(AFB_clientCtx *client) { } STATIC AFB_restapi pluginApis[]= { - {"ping" , AFB_SESSION_NONE , (AFB_apiCB)apiPingTest ,"Ping Rest Test Service"}, + {"ping" , AFB_SESSION_NONE , (AFB_apiCB)getPingTest ,"Ping Rest Test Service"}, {"create" , AFB_SESSION_CREATE, (AFB_apiCB)clientContextCreate ,"Request Client Context Creation"}, {"refresh" , AFB_SESSION_RENEW , (AFB_apiCB)clientContextRefresh,"Refresh Client Context Token"}, {"check" , AFB_SESSION_CHECK , (AFB_apiCB)clientContextCheck ,"Check Client Context Token"}, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ebf023af..6f7117dc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,5 +5,5 @@ IF(librtlsdr_FOUND) ADD_DEFINITIONS(-DHAVE_RADIO_PLUGIN=1) ENDIF(librtlsdr_FOUND) -ADD_LIBRARY(src OBJECT main.c config.c session.c http-svc.c rest-api.c) +ADD_LIBRARY(src OBJECT main.c config.c session.c http-svc.c rest-api.c helper-api.c) INCLUDE_DIRECTORIES(${include_dirs}) diff --git a/src/config.c b/src/config.c index 2e7611a5..815d11f3 100644 --- a/src/config.c +++ b/src/config.c @@ -114,8 +114,8 @@ PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) { session->config->smack= cliconfig->smack; } - if (cliconfig->smack == NULL) { - session->config->plugins = "all"; + if (cliconfig->plugins == NULL) { + session->config->plugins = PLUGIN_INSTALL_DIR; } else { session->config->plugins= cliconfig->plugins; } diff --git a/src/helper-api.c b/src/helper-api.c new file mode 100644 index 00000000..670c9a1d --- /dev/null +++ b/src/helper-api.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2015 "IoT.bzh" + * Author "Fulup Ar Foll" + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../include/local-def.h" + + +// handle to hold queryAll values +typedef struct { + char *msg; + int idx; + size_t len; +} queryHandleT; + +// Sample Generic Ping Debug API +PUBLIC json_object* getPingTest(AFB_request *request) { + static pingcount = 0; + json_object *response; + char query [256]; + char session[256]; + + int len; + AFB_clientCtx *client=request->client; // get client context from request + + // request all query key/value + len = getQueryAll (request, query, sizeof(query)); + if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query)); + + // check if we have some post data + if (request->post == NULL) request->post->data="NoData"; + + // check is we have a session and a plugin handle + if (client == NULL) strncpy (session,"NoSession", sizeof(session)); + else snprintf(session, sizeof(session),"uuid=%s token=%s ctx=0x%x handle=0x%x", client->uuid, client->token, client->ctx, client->ctx); + + // return response to caller + response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d CtxtId=%d query={%s} session={%s} PostData: [%s] " + , pingcount++, request->client->cid, query, session, request->post->data); + return (response); +} + + +// Helper to retrieve argument from connection +PUBLIC const char* getQueryValue(AFB_request * request, char *name) { + const char *value; + + value = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name); + return (value); +} + +STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) { + queryHandleT *query = (queryHandleT*)handle; + + query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value); +} + +// Helper to retrieve argument from connection +PUBLIC int getQueryAll(AFB_request * request, char *buffer, size_t len) { + queryHandleT query; + buffer[0] = '\0'; // start with an empty string + query.msg= buffer; + query.len= len; + query.idx= 0; + + MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query); + return (len); +} + +// Helper to retrieve POST handle +PUBLIC AFB_PostHandle* getPostHandle (AFB_request *request) { + if (request->post == NULL) return (NULL); + return ((AFB_PostHandle*) request->post->data); +} + +// Helper to retrieve POST file context +PUBLIC AFB_PostCtx* getPostContext (AFB_request *request) { + AFB_PostHandle* postHandle; + if (request->post == NULL) return (NULL); + + postHandle = (AFB_PostHandle*) request->post->data; + if (postHandle == NULL) return NULL; + + return ((AFB_PostCtx*) postHandle->ctx); +} + +PUBLIC json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) { + + AFB_PostHandle *postHandle = getPostHandle(request); + AFB_PostCtx *appCtx; + char filepath[512]; + int len; + + // This is called after PostForm and then after DonePostForm + if (item == NULL) { + json_object* jresp; + appCtx = (AFB_PostCtx*) postHandle->ctx; + + // No Post Application Context [something really bad happen] + if (appCtx == NULL) { + request->errcode = MHD_HTTP_EXPECTATION_FAILED; + return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n")); + } + + // We have a context but last Xform iteration fail. + if (appCtx->jerror != NULL) { + // request->errcode = appCtx->errcode; + jresp = appCtx->jerror; // retrieve previous error from postCtx + } else jresp = jsonNewMessage(AFB_FAIL,"UploadFile Post Request file=[%s] done", appCtx->path); + + // Error or not let's free all resources + close(appCtx->fd); + free (appCtx->path); + free (appCtx); + return (jresp); + } + + // Make sure it's a valid PostForm request + if (!request->post && request->post->type != AFB_POST_FORM) { + appCtx->jerror= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n"); + goto ExitOnError; + } + + // Check this is a file element + if (item->filename == NULL) { + appCtx->jerror= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key); + goto ExitOnError; + } + + // Check we got something in buffer + if (item->len <= 0) { + appCtx->jerror= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key); + goto ExitOnError; + } + + // Extract Application Context from posthandle [NULL == 1st iteration] + appCtx = (AFB_PostCtx*) postHandle->ctx; + + // This is the 1st Item iteration let's open output file and allocate necessary resources + if (appCtx == NULL) { + int destDir; + + // Create an application specific context + appCtx = calloc (1, sizeof(AFB_PostCtx)); // May place anything here until post->completeCB handle resources liberation + appCtx->path = strdup (filepath); + + // attach application to postHandle + postHandle->ctx = (void*) appCtx; // May place anything here until post->completeCB handle resources liberation + + // Build destination directory full path + if (destination[0] != '/') { + strncpy (filepath, request->config->sessiondir, sizeof(filepath)); + strncat (filepath, destination, sizeof(filepath)); + strncat (filepath, "/", sizeof(filepath)); + strncat (filepath, destination, sizeof(filepath)); + } else strncpy (filepath, destination, sizeof(filepath)); + + + // make sure destination directory exist + destDir = openat (filepath, request->plugin, O_DIRECTORY); + if (destDir < 0) { + destDir = mkdir(filepath,O_RDWR | S_IRWXU | S_IRGRP); + if (destDir < 0) { + appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to Create destination directory=[%s] error=%s\n", filepath, strerror(errno)); + goto ExitOnError; + } + } else close (destDir); + + strncat (filepath, "/", sizeof(filepath)); + strncat (filepath, item->filename, sizeof(filepath)); + + if((appCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) < 0) { + appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to Create destination=[%s] error=%s\n", filepath, strerror(errno)); + goto ExitOnError; + } + } else { + // reuse existing application context + appCtx = (AFB_PostCtx*) postHandle->ctx; + } + + // Check we successfully wrote full buffer + len = write (appCtx->fd, item->data, item->len); + if (item->len != len) { + appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno)); + goto ExitOnError; + } + + // every intermediary iteration should return Success & NULL + request->errcode = MHD_HTTP_OK; + return NULL; + +ExitOnError: + request->errcode = MHD_HTTP_EXPECTATION_FAILED; + return NULL; +} diff --git a/src/main.c b/src/main.c index aa0e9bcd..dddc3e15 100644 --- a/src/main.c +++ b/src/main.c @@ -108,7 +108,7 @@ static AFB_options cliOptions [] = { {SET_CONFIG_EXIT ,0,"saveonly" , "Save config on disk and then exit"}, {SET_SMACK ,1,"smack" , "Set Smack Label [default demo]"}, - {SET_PLUGINS ,1,"mods" , "Enable module [default all]"}, + {SET_PLUGINS ,1,"plugins" , "Load Plugins from dir [default = PLUGIN_INSTALL_DIR"}, {SET_AUTH_TOKEN ,1,"token" , "Initial Secret [default=no-session, --token="" for session without authentication]"}, {DISPLAY_VERSION ,0,"version" , "Display version and copyright"}, @@ -159,7 +159,7 @@ void signalQuit (int signum) { fprintf (stderr," --%-15s %s\n", command, cliOptions[ind].help); } } - fprintf (stderr,"Example:\n %s\\\n --verbose --port=1234 --smack=xxxx --token='azerty' --mods=alsa:dbus\n", name); + fprintf (stderr,"Example:\n %s\\\n --verbose --port=1234 --smack=xxxx --token='azerty' --plugins=build/plugins\n", name); } // end printHelp /*---------------------------------------------------------- @@ -357,7 +357,6 @@ int main(int argc, char *argv[]) { case SET_PLUGINS: if (optarg == 0) goto needValueForOption; - fprintf (stderr, "Not Implemented yet\n"); cliconfig.plugins = optarg; break; diff --git a/src/rest-api.c b/src/rest-api.c index da331678..0a31e20e 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -30,77 +30,10 @@ #define AFB_MSG_JTYPE "AJB_reply" -// handle to hold queryAll values -typedef struct { - char *msg; - int idx; - size_t len; -} queryHandleT; static json_object *afbJsonType; -// Sample Generic Ping Debug API -PUBLIC json_object* apiPingTest(AFB_request *request) { - static pingcount = 0; - json_object *response; - char query [256]; - char session[256]; - - int len; - AFB_clientCtx *client=request->client; // get client context from request - - // request all query key/value - len = getQueryAll (request, query, sizeof(query)); - if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query)); - - // check if we have some post data - if (request->post == NULL) request->post->data="NoData"; - - // check is we have a session and a plugin handle - if (client == NULL) strcpy (session,"NoSession"); - else snprintf(session, sizeof(session),"uuid=%s token=%s ctx=0x%x handle=0x%x", client->uuid, client->token, client->ctx, client->ctx); - - // return response to caller - response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d CtxtId=%d query={%s} session={%s} PostData: [%s] " - , pingcount++, request->client->cid, query, session, request->post->data); - return (response); -} - - -// Helper to retrieve argument from connection -PUBLIC const char* getQueryValue(AFB_request * request, char *name) { - const char *value; - - value = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name); - return (value); -} - -STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) { - queryHandleT *query = (queryHandleT*)handle; - - query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value); -} - -// Helper to retrieve argument from connection -PUBLIC int getQueryAll(AFB_request * request, char *buffer, size_t len) { - queryHandleT query; - buffer[0] = '\0'; // start with an empty string - query.msg= buffer; - query.len= len; - query.idx= 0; - - MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query); - return (len); -} - - -// Helper to retreive POST handle -PUBLIC AFB_PostHandle* getPostHandle (AFB_request *request) { - if (request->post == NULL) return (NULL); - return ((AFB_PostHandle*) request->post->data); -} - // Because of POST call multiple time requestApi we need to free POST handle here // Note this method is called from http-svc just before closing session PUBLIC void endPostRequest(AFB_PostHandle *postHandle) { @@ -640,8 +573,8 @@ void initPlugins(AFB_session *session) { afbJsonType = json_object_new_string (AFB_MSG_JTYPE); int i = 0; - if ((dir = opendir(PLUGIN_INSTALL_DIR)) == NULL) { - fprintf(stderr, "Could not open plugin directory=%s\n", PLUGIN_INSTALL_DIR); + if ((dir = opendir(session->config->plugins)) == NULL) { + fprintf(stderr, "Could not open plugin directory=%s\n", session->config->plugins); return; } @@ -650,7 +583,7 @@ void initPlugins(AFB_session *session) { if (!strstr (pluginDir->d_name, ".so")) continue; - asprintf (&pluginPath, PLUGIN_INSTALL_DIR "/%s", pluginDir->d_name); + asprintf (&pluginPath, "%s/%s", session->config->plugins, pluginDir->d_name); plugin = dlopen (pluginPath, RTLD_NOW | RTLD_LOCAL); pluginRegisterFct = dlsym (plugin, "pluginRegister"); free (pluginPath);