diff --git a/.gitignore b/.gitignore index 01aa4ca..fb3350f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ aqhome.db aqhome.log aqhomed.vg aqhome-mqttlog.vg +aqhome-storage.vg diff --git a/apps/aqhome-storage/0BUILD b/apps/aqhome-storage/0BUILD index d7f0f5a..8194cf6 100644 --- a/apps/aqhome-storage/0BUILD +++ b/apps/aqhome-storage/0BUILD @@ -42,6 +42,7 @@ fini.h loop.h loop_http.h + cleanup.h u_login.h u_rooms.h aqhomehttp.h @@ -57,6 +58,7 @@ fini.c loop.c loop_http.c + cleanup.c u_login.c u_rooms.c main.c diff --git a/apps/aqhome-storage/aqhomehttp.c b/apps/aqhome-storage/aqhomehttp.c index 7812a33..8e20ee4 100644 --- a/apps/aqhome-storage/aqhomehttp.c +++ b/apps/aqhome-storage/aqhomehttp.c @@ -48,7 +48,7 @@ void AqHomeHttpService_Extend(AQH_SERVICE *sv) GWEN_INHERIT_SETDATA(AQH_SERVICE, AQHOME_HTTP, sv, xsv, _freeData); xsv->contentTree=AQH_HttpContent_new("root"); - + xsv->storageMutex=GWEN_Mutex_new(); } @@ -62,6 +62,7 @@ void _freeData(void *bp, void *p) xsv->storage=NULL; AQH_HttpContent_free(xsv->contentTree); xsv->contentTree=NULL; + GWEN_Mutex_free(xsv->storageMutex); GWEN_FREE_OBJECT(xsv); } @@ -124,6 +125,65 @@ void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c) +void AqHomeHttpService_MarkStorageChanged(AQH_SERVICE *sv) +{ + if (sv) { + AQHOME_HTTP *xsv; + + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); + if (xsv) { + AQH_Storage_AddRuntimeFlags(xsv->storage, AQH_STORAGE_RTFLAGS_MODIFIED); + } + } +} + + + +int AqHomeHttpService_LockStorage(AQH_SERVICE *sv) +{ + if (sv) { + AQHOME_HTTP *xsv; + + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); + if (xsv) { + int rv; + + rv=GWEN_Mutex_Lock(xsv->storageMutex); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex"); + return rv; + } + return 0; + } + } + return GWEN_ERROR_GENERIC; +} + + + +int AqHomeHttpService_UnlockStorage(AQH_SERVICE *sv) +{ + if (sv) { + AQHOME_HTTP *xsv; + + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQHOME_HTTP, sv); + if (xsv) { + int rv; + + rv=GWEN_Mutex_Unlock(xsv->storageMutex); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex"); + return rv; + } + return 0; + } + } + + return GWEN_ERROR_GENERIC; +} + + + diff --git a/apps/aqhome-storage/aqhomehttp.h b/apps/aqhome-storage/aqhomehttp.h index f56d78f..26e58fd 100644 --- a/apps/aqhome-storage/aqhomehttp.h +++ b/apps/aqhome-storage/aqhomehttp.h @@ -50,7 +50,9 @@ AQH_HTTP_CONTENT *AqHomeHttpService_GetContentTree(const AQH_SERVICE *sv); void AqHomeHttpService_SetContentTree(AQH_SERVICE *sv, AQH_HTTP_CONTENT *c); - +int AqHomeHttpService_LockStorage(AQH_SERVICE *sv); +int AqHomeHttpService_UnlockStorage(AQH_SERVICE *sv); +void AqHomeHttpService_MarkStorageChanged(AQH_SERVICE *sv); #endif diff --git a/apps/aqhome-storage/aqhomehttp_p.h b/apps/aqhome-storage/aqhomehttp_p.h index 96359c2..564d6bb 100644 --- a/apps/aqhome-storage/aqhomehttp_p.h +++ b/apps/aqhome-storage/aqhomehttp_p.h @@ -12,10 +12,14 @@ #include "./aqhomehttp.h" +#include + + typedef struct AQHOME_HTTP AQHOME_HTTP; struct AQHOME_HTTP { - AQH_STORAGE *storage; /* do not release */ + AQH_STORAGE *storage; /* do not release */ + GWEN_MUTEX *storageMutex; AQH_HTTP_CONTENT *contentTree; }; diff --git a/apps/aqhome-storage/aqhomestorage.c b/apps/aqhome-storage/aqhomestorage.c index e207056..accbbc5 100644 --- a/apps/aqhome-storage/aqhomestorage.c +++ b/apps/aqhome-storage/aqhomestorage.c @@ -36,16 +36,16 @@ AQHOME_STORAGE *AqHomeStorage_new() void AqHomeStorage_free(AQHOME_STORAGE *aqh) { if (aqh) { + AQH_Service_free(aqh->httpService); + aqh->httpService=NULL; AQH_Storage_free(aqh->storage); - GWEN_MsgEndpoint_free(aqh->rootEndpoint); - GWEN_MsgEndpoint_free(aqh->rootEndpoint); - GWEN_DB_Group_free(aqh->dbArgs); - aqh->storage=NULL; + GWEN_MsgEndpoint_free(aqh->rootEndpoint); aqh->rootEndpoint=NULL; aqh->ipcdEndpoint=NULL; aqh->mqttEndpoint=NULL; aqh->httpdEndpoint=NULL; + GWEN_DB_Group_free(aqh->dbArgs); aqh->dbArgs=NULL; free(aqh->pidFile); @@ -114,6 +114,13 @@ void AqHomeStorage_SetPidFile(AQHOME_STORAGE *aqh, const char *s) +int AqHomeStorage_GetTimeout(const AQHOME_STORAGE *aqh) +{ + return aqh?aqh->timeout:0; +} + + + diff --git a/apps/aqhome-storage/aqhomestorage.h b/apps/aqhome-storage/aqhomestorage.h index a68ed5c..f3aa214 100644 --- a/apps/aqhome-storage/aqhomestorage.h +++ b/apps/aqhome-storage/aqhomestorage.h @@ -35,6 +35,8 @@ AQH_STORAGE *AqHomeStorage_GetStorage(const AQHOME_STORAGE *aqh); const char *AqHomeStorage_GetPidFile(const AQHOME_STORAGE *aqh); void AqHomeStorage_SetPidFile(AQHOME_STORAGE *aqh, const char *s); +int AqHomeStorage_GetTimeout(const AQHOME_STORAGE *aqh); + #endif diff --git a/apps/aqhome-storage/aqhomestorage_p.h b/apps/aqhome-storage/aqhomestorage_p.h index 16801fa..7eef60e 100644 --- a/apps/aqhome-storage/aqhomestorage_p.h +++ b/apps/aqhome-storage/aqhomestorage_p.h @@ -27,6 +27,8 @@ #define AQHOME_STORAGE_DEFAULT_STATEFILE "/var/lib/aqhomestorage/config/statefile" +#define AQHOME_STORAGE_DEFAULT_MAXSESSIONAGE (30*60) + #define AQHOME_STORAGE_SITEHEADER "site-header.html" #define AQHOME_STORAGE_SITEFOOTER "site-footer.html" @@ -35,9 +37,9 @@ struct AQHOME_STORAGE { GWEN_MSG_ENDPOINT *rootEndpoint; - GWEN_MSG_ENDPOINT *ipcdEndpoint; - GWEN_MSG_ENDPOINT *mqttEndpoint; - GWEN_MSG_ENDPOINT *httpdEndpoint; + GWEN_MSG_ENDPOINT *ipcdEndpoint; /* don't release, will be released by freeing rootEndpoint! */ + GWEN_MSG_ENDPOINT *mqttEndpoint; /* don't release, will be released by freeing rootEndpoint! */ + GWEN_MSG_ENDPOINT *httpdEndpoint; /* don't release, will be released by freeing rootEndpoint! */ GWEN_DB_NODE *dbArgs; @@ -45,6 +47,10 @@ struct AQHOME_STORAGE { AQH_STORAGE *storage; char *pidFile; + + int maxSessionAgeInSeconds; + + int timeout; /* timeout for run e.g. inside valgrind */ }; #endif diff --git a/apps/aqhome-storage/cleanup.c b/apps/aqhome-storage/cleanup.c new file mode 100644 index 0000000..d2cfcae --- /dev/null +++ b/apps/aqhome-storage/cleanup.c @@ -0,0 +1,61 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + +#include "./cleanup.h" +#include "./aqhomehttp.h" +#include "./aqhomestorage_p.h" +#include "aqhome/http/httpservice_conf.h" + +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + + + +void AqHomeStorage_Cleanup(AQHOME_STORAGE *aqh) +{ + + int rv; + + rv=AQH_HttpService_CleanupSessions(aqh->httpService, aqh->maxSessionAgeInSeconds); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + } +} + + + + diff --git a/apps/aqhome-storage/cleanup.h b/apps/aqhome-storage/cleanup.h new file mode 100644 index 0000000..3d3b295 --- /dev/null +++ b/apps/aqhome-storage/cleanup.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQHOME_STORAGE_CLEANUP_H +#define AQHOME_STORAGE_CLEANUP_H + + +#include "./aqhomestorage.h" + + +void AqHomeStorage_Cleanup(AQHOME_STORAGE *aqh); + + + +#endif + + + + + diff --git a/apps/aqhome-storage/init.c b/apps/aqhome-storage/init.c index fcd6fa0..d656862 100644 --- a/apps/aqhome-storage/init.c +++ b/apps/aqhome-storage/init.c @@ -96,6 +96,9 @@ int AqHomeStorage_Init(AQHOME_STORAGE *aqh, int argc, char **argv) } aqh->dbArgs=dbArgs; + aqh->maxSessionAgeInSeconds=GWEN_DB_GetIntValue(dbArgs, "maxSessionAge", 0, AQHOME_STORAGE_DEFAULT_MAXSESSIONAGE); + aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0); + s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_STORAGE_DEFAULT_PIDFILE); if (s && *s) { AqHomeStorage_SetPidFile(aqh, s); @@ -415,6 +418,17 @@ int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) I18S("Specify the port to listen on for HTTP connections"), I18S("Specify the port to listen on for HTTP connections") }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "maxSessionAge", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + NULL, /* short option */ + "maxsessionage", /* long option */ + I18S("Specify maximum session age in seconds (default: 30mins)"), + I18S("Specify maximum session age in seconds (default: 30mins)") + }, { GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ GWEN_ArgsType_Char, /* type */ diff --git a/apps/aqhome-storage/init_http.c b/apps/aqhome-storage/init_http.c index d85375f..3b59ad6 100644 --- a/apps/aqhome-storage/init_http.c +++ b/apps/aqhome-storage/init_http.c @@ -151,7 +151,7 @@ int _setupHttpService(AQHOME_STORAGE *aqh, GWEN_DB_NODE *dbArgs) DBG_ERROR(NULL, "Error loading config for HTTP service (%d)", rv); return GWEN_ERROR_GENERIC; } - + AQH_HttpService_LoadAllSessions(aqh->httpService); return 0; } diff --git a/apps/aqhome-storage/loop.c b/apps/aqhome-storage/loop.c index 44c1e41..aba35b9 100644 --- a/apps/aqhome-storage/loop.c +++ b/apps/aqhome-storage/loop.c @@ -13,7 +13,9 @@ #include "./loop.h" #include "./loop_http.h" +#include "./aqhomehttp.h" #include "./aqhomestorage_p.h" +#include "aqhome/http/httpservice_conf.h" #include #include @@ -34,6 +36,7 @@ * ------------------------------------------------------------------------------------------------ */ +static int _writeModifiedSessions(AQHOME_STORAGE *aqh); @@ -48,9 +51,97 @@ void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs) GWEN_MsgEndpoint_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs); AqHomeStorage_ReadAndHandleHttpMessages(aqh); // AqHomeStorage_ReadAndHandleIpcMessages(aqh); - } } +int AqHomeStorage_WriteStorageIfChanged(AQHOME_STORAGE *aqh) +{ + if (AQH_Storage_GetRuntimeFlags(aqh->storage) & AQH_STORAGE_RTFLAGS_MODIFIED) { + int rv; + + DBG_INFO(NULL, "Storage modified, writing statefile"); + rv=AqHomeHttpService_LockStorage(aqh->httpService); + if (rv<0) { + DBG_INFO(NULL, "Error locking storage (%d)", rv); + return rv; + } + rv=AQH_Storage_WriteState(aqh->storage); + if (rv<0) { + DBG_INFO(NULL, "Error writing state file (%d)", rv); + AqHomeHttpService_UnlockStorage(aqh->httpService); + return rv; + } + + rv=AqHomeHttpService_UnlockStorage(aqh->httpService); + if (rv<0) { + DBG_INFO(NULL, "Error unlocking storage (%d)", rv); + return rv; + } + } + + return 0; +} + + + +int AqHomeStorage_WriteServiceIfChanged(AQHOME_STORAGE *aqh) +{ + int rv; + + rv=_writeModifiedSessions(aqh); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + return 0; +} + + + +int _writeModifiedSessions(AQHOME_STORAGE *aqh) +{ + AQH_SESSION_LIST *sessionList; + int rv; + + rv=AQH_HttpService_LockSessions(aqh->httpService); + if (rv<0) { + DBG_INFO(NULL, "Error locking sessions (%d)", rv); + return rv; + } + + sessionList=AQH_Service_GetSessionList(aqh->httpService); + if (sessionList) { + AQH_SESSION *session; + + session=AQH_Session_List_First(sessionList); + while(session) { + if (AQH_Session_GetRuntimeFlags(session) & AQH_SESSION_RTFLAGS_MODIFIED) { + DBG_INFO(NULL, "Session \"%s\" modified, writing", AQH_Session_GetUid(session)); + rv=AQH_HttpService_SaveSession(aqh->httpService, session); + if (rv<0) { + DBG_INFO(NULL, "Error writing session \"%s\" (%d)", AQH_Session_GetUid(session), rv); + } + else { + DBG_DEBUG(NULL, "Session \"%s\" written", AQH_Session_GetUid(session)); + AQH_Session_SubRuntimeFlags(session, AQH_SESSION_RTFLAGS_MODIFIED); + } + } + session=AQH_Session_List_Next(session); + } + } + + rv=AQH_HttpService_UnlockSessions(aqh->httpService); + if (rv<0) { + DBG_INFO(NULL, "Error unlocking sessions (%d)", rv); + return rv; + } + + return 0; +} + + + + diff --git a/apps/aqhome-storage/loop.h b/apps/aqhome-storage/loop.h index 9bca8e9..01cd530 100644 --- a/apps/aqhome-storage/loop.h +++ b/apps/aqhome-storage/loop.h @@ -15,6 +15,9 @@ void AqHomeStorage_Loop(AQHOME_STORAGE *aqh, int timeoutInMsecs); +int AqHomeStorage_WriteStorageIfChanged(AQHOME_STORAGE *aqh); + +int AqHomeStorage_WriteServiceIfChanged(AQHOME_STORAGE *aqh); #endif diff --git a/apps/aqhome-storage/main.c b/apps/aqhome-storage/main.c index 4f274c6..d241c03 100644 --- a/apps/aqhome-storage/main.c +++ b/apps/aqhome-storage/main.c @@ -17,6 +17,7 @@ #include "./init.h" #include "./fini.h" #include "./loop.h" +#include "./cleanup.h" #include #include @@ -29,6 +30,14 @@ +//#define CLEANUP_INTERVAL_IN_SECS (5*60) +//#define WRITE_INTERVAL_IN_SECS (5*60) + +#define CLEANUP_INTERVAL_IN_SECS (60) +#define WRITE_INTERVAL_IN_SECS (60) + + + /* ------------------------------------------------------------------------------------------------ * defines * ------------------------------------------------------------------------------------------------ @@ -48,6 +57,9 @@ static int _setupSigAction(struct sigaction *sa, int sig); static void _signalHandler(int s); #endif +static void _runService(AQHOME_STORAGE *aqh); +static void _writeCurrentState(AQHOME_STORAGE *aqh); + /* ------------------------------------------------------------------------------------------------ @@ -106,10 +118,7 @@ int main(int argc, char **argv) return 2; } - while(!stopService) { - DBG_DEBUG(NULL, "Next loop"); - AqHomeStorage_Loop(aqh, 2000); - } + _runService(aqh); AqHomeStorage_Fini(aqh); AqHomeStorage_free(aqh); @@ -122,6 +131,67 @@ int main(int argc, char **argv) +void _runService(AQHOME_STORAGE *aqh) +{ + time_t timeStart; + time_t timeLastCleanup; + time_t timeLastWrite; + int timeout; + + timeout=AqHomeStorage_GetTimeout(aqh); + timeStart=time(NULL); + timeLastCleanup=time(NULL); + timeLastWrite=time(NULL); + + while(!stopService) { + time_t now; + + DBG_DEBUG(NULL, "Next loop"); + AqHomeStorage_Loop(aqh, 2000); + + now=time(NULL); + + if (((int)difftime(now, timeLastCleanup))>CLEANUP_INTERVAL_IN_SECS) { + DBG_INFO(NULL, "Cleanup time"); + AqHomeStorage_Cleanup(aqh); + timeLastCleanup=now; + } + + if (((int)difftime(now, timeLastWrite))>WRITE_INTERVAL_IN_SECS) { + DBG_INFO(NULL, "Write time"); + _writeCurrentState(aqh); + timeLastWrite=now; + } + + + if (timeout && ((int)difftime(now, timeStart))>timeout) { + DBG_INFO(NULL, "Timeout"); + _writeCurrentState(aqh); + break; + } + } /* while */ +} + + + +void _writeCurrentState(AQHOME_STORAGE *aqh) +{ + int rv; + + rv=AqHomeStorage_WriteStorageIfChanged(aqh); + if (rv<0) { + DBG_ERROR(NULL, "ATTENTION: Could not write storage statefile (%d)", rv); + } + + rv=AqHomeStorage_WriteServiceIfChanged(aqh); + if (rv<0) { + DBG_ERROR(NULL, "ATTENTION: Could not write current config (%d)", rv); + } +} + + + + int _setSignalHandlers(void) { #ifdef HAVE_SIGNAL_H diff --git a/apps/aqhome-storage/u_login.c b/apps/aqhome-storage/u_login.c index 7054f97..a179219 100644 --- a/apps/aqhome-storage/u_login.c +++ b/apps/aqhome-storage/u_login.c @@ -308,8 +308,9 @@ AQH_SESSION *_generateSessionForUser(AQH_SERVICE *sv, AQH_USER *u) session=AQH_Session_new(); AQH_Session_SetUid(session, GWEN_Buffer_GetStart(buf)); - ts=GWEN_Timestamp_NowInGmTime(); + ts=GWEN_Timestamp_NowInLocalTime(); AQH_Session_SetTimestampCreation(session, ts); + AQH_Session_SetTimestampLastAccess(session, ts); GWEN_Timestamp_free(ts); AQH_Session_SetUserAlias(session, AQH_User_GetAlias(u)); diff --git a/apps/aqhome-storage/u_rooms.c b/apps/aqhome-storage/u_rooms.c index 90b8738..b261e8a 100644 --- a/apps/aqhome-storage/u_rooms.c +++ b/apps/aqhome-storage/u_rooms.c @@ -36,14 +36,15 @@ */ static int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); -static int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); -static int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static void _handleGetList(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf); static void _handleGetAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_BUFFER *pageBuf); -static int _handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); +static GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); static int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *dbValues, GWEN_BUFFER *pageBuf); +static GWEN_MSG *_addRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq); @@ -68,7 +69,7 @@ int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { const char *protocol; const char *cmd; - GWEN_MSG *msgOut=NULL; + GWEN_MSG *msgOut; AQH_HttpService_SetupModuleAndPerms(AQH_HttpUrlHandler_GetHttpService(uh), rq, "aqhome"); AQH_HttpRequest_SetupUrlPathMembers(rq); @@ -76,26 +77,14 @@ int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) protocol=AQH_HttpRequest_GetProtocol(rq); cmd=AQH_HttpRequest_GetCommand(rq); if (cmd && *cmd) { - int rv; - - if (strcasecmp(cmd, "GET")==0) { - rv=_handleGet(uh, rq); - if (rv<0) { - DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); - return rv; - } - } - else if (strcasecmp(cmd, "POST")==0) { - rv=_handlePost(uh, rq); - if (rv<0) { - DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); - return rv; - } - } + if (strcasecmp(cmd, "GET")==0) + msgOut=_handleGet(uh, rq); + else if (strcasecmp(cmd, "POST")==0) + msgOut=_handlePost(uh, rq); else { msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 405, "Method not allowed", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); } + AQH_HttpRequest_SetResponseMsg(rq, msgOut); return 0; } else { @@ -106,7 +95,7 @@ int _handleUrl(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) -int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +GWEN_MSG *_handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { GWEN_BUFFER *pageBuf; int rv; @@ -120,9 +109,7 @@ int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) if (rv<0) { DBG_ERROR(AQH_LOGDOMAIN, "Error adding headers"); GWEN_Buffer_free(pageBuf); - msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); } /* handle middle part (header - middle - footer) */ @@ -140,38 +127,35 @@ int _handleGet(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) _handleGetAdd(uh, rq, pageBuf); else { DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s); - msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); GWEN_Buffer_free(pageBuf); - return 0; + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); } } + else { + DBG_ERROR(NULL, "No list of url members"); + GWEN_Buffer_free(pageBuf); + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); + } rv=AQH_HttpUrlHandler_AddContentFooters(uh, AQH_HTTP_CONTENT_MODE_DESKTOP, pageBuf); if (rv<0) { DBG_ERROR(AQH_LOGDOMAIN, "Error adding footers"); - msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); GWEN_Buffer_free(pageBuf); - return rv; + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 500, "Internal Error", protocol, NULL); } msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 200, "OK", protocol, GWEN_Buffer_GetStart(pageBuf)); GWEN_Buffer_free(pageBuf); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - - return 0; + return msgOut; } -int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +GWEN_MSG *_handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { GWEN_DB_NODE *db; const GWEN_STRINGLIST *sl; const char *protocol; - GWEN_MSG *msgOut=NULL; - int rv; DBG_ERROR(NULL, "POST:"); db=AQH_HttpRequest_GetDbPostBody(rq); @@ -185,24 +169,21 @@ int _handlePost(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) s=GWEN_StringList_StringAt(sl, 1); if (s && *s) { if (strcasecmp(s, "add")==0) - rv=_handlePostAdd(uh, rq); + return _handlePostAdd(uh, rq); else { DBG_ERROR(NULL, "Invalid url (2nd member is [%s])", s); - msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); } - return rv; } else { DBG_ERROR(NULL, "Invalid url (2nd member missing)"); - msgOut=AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); } } - - return 0; + else { + DBG_ERROR(NULL, "No list of url members"); + return AQH_HttpService_CreateResponseMsg(AQH_HttpUrlHandler_GetHttpService(uh), 404, "Not found", protocol, NULL); + } } @@ -316,65 +297,89 @@ int _writeAddPage(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq, GWEN_DB_NODE *d -int _handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +GWEN_MSG *_handlePostAdd(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) { const char *protocol; AQH_SERVICE *sv; uint32_t perms; - GWEN_MSG *msgOut=NULL; protocol=AQH_HttpRequest_GetProtocol(rq); sv=AQH_HttpUrlHandler_GetHttpService(uh); perms=AQH_HttpRequest_GetModulePerms(rq); if (perms & AQHOME_HTTP_PERMS_ADD_ROOM) { - GWEN_DB_NODE *db; - const char *roomName; - const char *roomDescr; AQH_STORAGE *sto; - db=AQH_HttpRequest_GetDbPostBody(rq); - - roomName=GWEN_DB_GetCharValue(db, "name", 0, NULL); - roomDescr=GWEN_DB_GetCharValue(db, "description", 0, NULL); - - if (!(roomName && *roomName)) { - msgOut=AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Missing room name"), 1, db, _writeAddPage); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; - } - sto=AqHomeHttpService_GetStorage(sv); if (sto) { - AQH_ROOM *r; - - r=AQH_Room_new(); - AQH_Room_SetName(r, roomName); - if (roomDescr && *roomDescr) - AQH_Room_SetDescription(r, roomDescr); - if (AQH_Storage_GetRoomByName(sto, roomName)!=NULL) { - msgOut=AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Room already exists"), 1, db, _writeAddPage); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; - } - AQH_Storage_AddRoom(sto, r); - msgOut=AQH_HttpService_CreateRedirectingResponseMsg(sv, protocol, "/rooms/list"); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + return _addRoomCreateResponse(uh, rq); } else { DBG_ERROR(NULL, "No storage"); - msgOut=AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL); } } else { - msgOut=AQH_HttpService_CreateResponseMsg(sv, 403, "Forbidden", protocol, NULL); - AQH_HttpRequest_SetResponseMsg(rq, msgOut); - return 0; + return AQH_HttpService_CreateResponseMsg(sv, 403, "Forbidden", protocol, NULL); } } +GWEN_MSG *_addRoomCreateResponse(AQH_HTTP_URLHANDLER *uh, AQH_HTTP_REQUEST *rq) +{ + AQH_SERVICE *sv; + const char *protocol; + GWEN_DB_NODE *db; + const char *roomName; + const char *roomDescr; + AQH_STORAGE *sto; + + sv=AQH_HttpUrlHandler_GetHttpService(uh); + protocol=AQH_HttpRequest_GetProtocol(rq); + db=AQH_HttpRequest_GetDbPostBody(rq); + + roomName=GWEN_DB_GetCharValue(db, "name", 0, NULL); + roomDescr=GWEN_DB_GetCharValue(db, "description", 0, NULL); + + if (!(roomName && *roomName)) { + DBG_INFO(NULL, "Missing room name"); + return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Missing room name"), 1, db, _writeAddPage); + } + + sto=AqHomeHttpService_GetStorage(sv); + if (sto) { + AQH_ROOM *r; + int rv; + + r=AQH_Room_new(); + AQH_Room_SetName(r, roomName); + if (roomDescr && *roomDescr) + AQH_Room_SetDescription(r, roomDescr); + + rv=AqHomeHttpService_LockStorage(sv); + if (rv<0) { + DBG_ERROR(NULL, "Error locking storage"); + return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL); + } + if (AQH_Storage_GetRoomByName(sto, roomName)!=NULL) { + DBG_INFO(NULL, "Room \"%s\" already exists", roomName); + AqHomeHttpService_UnlockStorage(sv); + return AQH_HttpUrlHandler_CreatePageMessage(uh, rq, "red", I18N("Room already exists"), 1, db, _writeAddPage); + } + AQH_Storage_AddRoom(sto, r); + AQH_Storage_AddRuntimeFlags(sto, AQH_STORAGE_RTFLAGS_MODIFIED); + + rv=AqHomeHttpService_UnlockStorage(sv); + if (rv<0) { + DBG_ERROR(NULL, "Error unlocking storage"); + return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL); + } + return AQH_HttpService_CreateRedirectingResponseMsg(sv, protocol, "/rooms/list"); + } + else + return AQH_HttpService_CreateResponseMsg(sv, 500, "Internal error", protocol, NULL); +} + + + diff --git a/aqhome-storage.sh b/aqhome-storage.sh index b6312bb..35003b2 100755 --- a/aqhome-storage.sh +++ b/aqhome-storage.sh @@ -11,5 +11,5 @@ export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH" --statefile=apps/aqhome-storage/test/config/state \ -ma 192.168.117.192 -mp 1883 --mqttclientid=AQHOMESTORAGETEST \ -ha 127.0.0.1 -hp 1884 \ - -p ./aqhome-storage.pid - + -p ./aqhome-storage.pid \ + $* diff --git a/aqhome/data/mqtttopic.t2d b/aqhome/data/mqtttopic.t2d index 6f1ddfb..231bbdc 100644 --- a/aqhome/data/mqtttopic.t2d +++ b/aqhome/data/mqtttopic.t2d @@ -48,7 +48,7 @@ 0 0 public - with_getbymember + own with_getbymember diff --git a/aqhome/data/room.t2d b/aqhome/data/room.t2d index c08dad5..23df86b 100644 --- a/aqhome/data/room.t2d +++ b/aqhome/data/room.t2d @@ -41,14 +41,14 @@ 0 0 public - with_getbymember + own with_getbymember 0 0 public - + own diff --git a/aqhome/data/storage.c b/aqhome/data/storage.c index ee79688..59423b3 100644 --- a/aqhome/data/storage.c +++ b/aqhome/data/storage.c @@ -204,6 +204,37 @@ AQH_VALUE *AQH_Storage_GetValueById(const AQH_STORAGE *sto, uint64_t id) +uint32_t AQH_Storage_GetRuntimeFlags(const AQH_STORAGE *sto) +{ + return sto?sto->runtimeFlags:0; +} + + + +void AQH_Storage_SetRuntimeFlags(AQH_STORAGE *sto, uint32_t flags) +{ + if (sto) + sto->runtimeFlags=flags; +} + + + +void AQH_Storage_AddRuntimeFlags(AQH_STORAGE *sto, uint32_t flags) +{ + if (sto) + sto->runtimeFlags|=flags; +} + + + +void AQH_Storage_SubRuntimeFlags(AQH_STORAGE *sto, uint32_t flags) +{ + if (sto) + sto->runtimeFlags&=~flags; +} + + + void AQH_Storage_HandleMqttPublish(AQH_STORAGE *sto, const char *topic, const char *value) { /* TODO */ @@ -216,7 +247,10 @@ int AQH_Storage_Init(AQH_STORAGE *sto) int rv; rv=GWEN_Directory_GetPath(sto->stateFile, - GWEN_PATH_FLAGS_CHECKROOT | GWEN_PATH_FLAGS_PATHMUSTEXIST | GWEN_PATH_FLAGS_NAMEMUSTEXIST); + GWEN_PATH_FLAGS_CHECKROOT | + GWEN_PATH_FLAGS_PATHMUSTEXIST | + GWEN_PATH_FLAGS_NAMEMUSTEXIST | + GWEN_PATH_FLAGS_VARIABLE); if (rv==0) { rv=AQH_Storage_ReadStateFile(sto, sto->stateFile); if (rv<0) { @@ -225,7 +259,7 @@ int AQH_Storage_Init(AQH_STORAGE *sto) } } else { - DBG_WARN(AQH_LOGDOMAIN, "State file \"%s\" not available, will try to create it later", sto->stateFile); + DBG_WARN(AQH_LOGDOMAIN, "State file \"%s\" not available, will try to create it later (%d)", sto->stateFile, rv); } return 0; @@ -242,7 +276,7 @@ int AQH_Storage_WriteState(AQH_STORAGE *sto) DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); return rv; } - + sto->runtimeFlags&=~AQH_STORAGE_RTFLAGS_MODIFIED; return 0; } diff --git a/aqhome/data/storage.h b/aqhome/data/storage.h index 587c8f9..a2b0576 100644 --- a/aqhome/data/storage.h +++ b/aqhome/data/storage.h @@ -34,6 +34,9 @@ typedef struct AQH_STORAGE AQH_STORAGE; #include "aqhome/data/datapoint.h" +#define AQH_STORAGE_RTFLAGS_MODIFIED 0x0001 + + #ifdef __cplusplus extern "C" { @@ -67,6 +70,11 @@ AQHOME_API AQH_VALUE *AQH_Storage_GetValueById(const AQH_STORAGE *sto, uint64_t AQHOME_API const char *AQH_Storage_GetStateFile(const AQH_STORAGE *sto); AQHOME_API void AQH_Storage_SetStateFile(AQH_STORAGE *sto, const char *s); +AQHOME_API uint32_t AQH_Storage_GetRuntimeFlags(const AQH_STORAGE *sto); +AQHOME_API void AQH_Storage_SetRuntimeFlags(AQH_STORAGE *sto, uint32_t flags); +AQHOME_API void AQH_Storage_AddRuntimeFlags(AQH_STORAGE *sto, uint32_t flags); +AQHOME_API void AQH_Storage_SubRuntimeFlags(AQH_STORAGE *sto, uint32_t flags); + AQHOME_API int AQH_Storage_Init(AQH_STORAGE *sto); AQHOME_API int AQH_Storage_WriteState(AQH_STORAGE *sto); diff --git a/aqhome/data/storage_p.h b/aqhome/data/storage_p.h index 082262a..c5f0fed 100644 --- a/aqhome/data/storage_p.h +++ b/aqhome/data/storage_p.h @@ -41,6 +41,8 @@ struct AQH_STORAGE { uint64_t lastValueId; char *stateFile; + + uint32_t runtimeFlags; }; diff --git a/aqhome/data/storage_readxml.c b/aqhome/data/storage_readxml.c index 0767351..294cd3b 100644 --- a/aqhome/data/storage_readxml.c +++ b/aqhome/data/storage_readxml.c @@ -57,6 +57,8 @@ int AQH_Storage_ReadStateFile(AQH_STORAGE *sto, const char *sFilename) _readTopicsFromXml(sto, rootNode); _readValuesFromXml(sto, rootNode); + GWEN_XMLNode_free(rootNode); + return 0; } diff --git a/aqhome/data/storage_writexml.c b/aqhome/data/storage_writexml.c index dac9aed..8798ea3 100644 --- a/aqhome/data/storage_writexml.c +++ b/aqhome/data/storage_writexml.c @@ -125,7 +125,7 @@ void _writeDevicesToXml(const AQH_STORAGE *sto, GWEN_XMLNODE *rootNode) nElems=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, AQH_STORAGE_XML_ELEMENTNAME_DEVICES); - elem=AQH_Device_List_First(sto->roomList); + elem=AQH_Device_List_First(sto->deviceList); while(elem) { GWEN_XMLNODE *nElem; diff --git a/aqhome/data/value.t2d b/aqhome/data/value.t2d index 9198386..fb7c6ea 100644 --- a/aqhome/data/value.t2d +++ b/aqhome/data/value.t2d @@ -55,14 +55,14 @@ 0 0 public - + own 0 0 public - + own diff --git a/aqhome/http/content.c b/aqhome/http/content.c index 6ed3e6b..51a5205 100644 --- a/aqhome/http/content.c +++ b/aqhome/http/content.c @@ -45,6 +45,9 @@ void AQH_HttpContent_free(AQH_HTTP_CONTENT *cp) if (cp) { GWEN_TREE2_FINI(AQH_HTTP_CONTENT, cp, AQH_HttpContent); GWEN_INHERIT_FINI(AQH_HTTP_CONTENT, cp); + + free(cp->name); + GWEN_FREE_OBJECT(cp); } } diff --git a/aqhome/http/endpoint_http.c b/aqhome/http/endpoint_http.c index 9ba4ad8..9113416 100644 --- a/aqhome/http/endpoint_http.c +++ b/aqhome/http/endpoint_http.c @@ -119,7 +119,7 @@ void _addSockets(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET } /* if socket */ } else if (xep->addSocketsFn) { - DBG_INFO(AQH_LOGDOMAIN, "Endpoint %s: Not connected, calling base function", GWEN_MsgEndpoint_GetName(ep)); + DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Not connected, calling base function", GWEN_MsgEndpoint_GetName(ep)); xep->addSocketsFn(ep, readSet, writeSet, xSet); } } /* if (xep) */ @@ -172,7 +172,7 @@ void _checkSockets(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSE } } /* if connected */ else if (xep->checkSocketsFn) { - DBG_INFO(AQH_LOGDOMAIN, "Endpoint %s: Not connected, calling base function", GWEN_MsgEndpoint_GetName(ep)); + DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint %s: Not connected, calling base function", GWEN_MsgEndpoint_GetName(ep)); xep->checkSocketsFn(ep, readSet, writeSet, xSet); } } @@ -208,7 +208,7 @@ int _writeCurrentMessage(GWEN_MSG_ENDPOINT *ep) } GWEN_Msg_IncCurrentPos(msg, rv); if (rv==remaining) { - DBG_INFO(AQH_LOGDOMAIN, "Message completely sent"); + DBG_DEBUG(AQH_LOGDOMAIN, "Message completely sent"); /* end current message */ GWEN_Msg_List_Del(msg); GWEN_Msg_free(msg); @@ -216,7 +216,7 @@ int _writeCurrentMessage(GWEN_MSG_ENDPOINT *ep) } } else { - DBG_INFO(AQH_LOGDOMAIN, "Nothing to send"); + DBG_DEBUG(AQH_LOGDOMAIN, "Nothing to send"); } return 0; } @@ -309,7 +309,7 @@ int _distributeBufferInCommandMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferP const char *s; /* line complete */ - DBG_INFO(AQH_LOGDOMAIN, "Command line complete"); + DBG_DEBUG(AQH_LOGDOMAIN, "Command line complete"); rv=-rv; xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep); @@ -319,21 +319,21 @@ int _distributeBufferInCommandMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferP DBG_INFO(AQH_LOGDOMAIN, "Error parsing command line [%s]", GWEN_Buffer_GetStart(xep->currentReadBuffer)); return GWEN_ERROR_BAD_DATA; } - DBG_ERROR(AQH_LOGDOMAIN, "Command line received: %s", GWEN_Buffer_GetStart(xep->currentReadBuffer)); + DBG_DEBUG(AQH_LOGDOMAIN, "Command line received: %s", GWEN_Buffer_GetStart(xep->currentReadBuffer)); s=GWEN_DB_GetCharValue(xep->dbCurrentReadCommand, "protocol", 0, "HTTP/0.9"); if (s && *s && strcasecmp(s, "HTTP/0.9")==0) { DBG_INFO(AQH_LOGDOMAIN, "HTTP 0.9, no header, message finished"); _finishMessageAndStartNext(ep); return rv; } - DBG_INFO(AQH_LOGDOMAIN, + DBG_DEBUG(AQH_LOGDOMAIN, "Command line complete, advancing to header read mode (start: %d)", GWEN_Buffer_GetPos(xep->currentReadBuffer)); xep->readMode=AQH_EndpointHttpd_ReadMode_Headers; xep->currentHeaderPos=GWEN_Buffer_GetPos(xep->currentReadBuffer); } else { - DBG_INFO(AQH_LOGDOMAIN, "Line not yet finished (%d)", rv); + DBG_DEBUG(AQH_LOGDOMAIN, "Line not yet finished (%d)", rv); } return rv; @@ -352,7 +352,7 @@ int _distributeBufferInStatusMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPt /* line complete, TODO: parse status/command line */ rv=-rv; xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep); - DBG_INFO(AQH_LOGDOMAIN, "Line complete, advancing to header read mode"); + DBG_DEBUG(AQH_LOGDOMAIN, "Line complete, advancing to header read mode"); xep->readMode=AQH_EndpointHttpd_ReadMode_Headers; xep->currentBodyPos=GWEN_Buffer_GetPos(xep->currentReadBuffer); } @@ -382,7 +382,7 @@ int _distributeBufferInHeaderMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPt int contentLength; /* Empty line received, TODO: parse header */ - DBG_INFO(AQH_LOGDOMAIN, "Empty header line received, end of header reached (header pos: %d).", xep->currentHeaderPos); + DBG_DEBUG(AQH_LOGDOMAIN, "Empty header line received, end of header reached (header pos: %d).", xep->currentHeaderPos); copyOfHeader=strdup(GWEN_Buffer_GetStart(xep->currentReadBuffer)+xep->currentHeaderPos); xep->dbCurrentReadHeader=GWEN_DB_Group_new("header"); if (_parseHeader(copyOfHeader, xep->dbCurrentReadHeader)<0) { @@ -393,7 +393,7 @@ int _distributeBufferInHeaderMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPt free(copyOfHeader); contentLength=GWEN_DB_GetIntValue(xep->dbCurrentReadHeader, "Content-Length", 0, -1); if (contentLength==0 || contentLength==-1) { - DBG_INFO(AQH_LOGDOMAIN, "Message has no body, done"); + DBG_DEBUG(AQH_LOGDOMAIN, "Message has no body, done"); _finishMessageAndStartNext(ep); } else { @@ -425,7 +425,7 @@ int _distributeBufferInBodyMode(GWEN_MSG_ENDPOINT *ep, const uint8_t *bufferPtr, GWEN_Buffer_AppendBytes(xep->currentReadBuffer, (const char*) bufferPtr, len); xep->remainingBodySize-=len; if (xep->remainingBodySize==0) { - DBG_INFO(AQH_LOGDOMAIN, "Body completely received"); + DBG_DEBUG(AQH_LOGDOMAIN, "Body completely received"); _finishMessageAndStartNext(ep); } return len; @@ -475,7 +475,7 @@ void _finishMessageAndStartNext(GWEN_MSG_ENDPOINT *ep) GWEN_MSG *msg; GWEN_DB_NODE *dbParsedData; - DBG_INFO(AQH_LOGDOMAIN, "Message completely received."); + DBG_DEBUG(AQH_LOGDOMAIN, "Message completely received."); xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep); msg=GWEN_Msg_fromBytes((const uint8_t*)GWEN_Buffer_GetStart(xep->currentReadBuffer), GWEN_Buffer_GetUsedBytes(xep->currentReadBuffer)); @@ -502,11 +502,11 @@ void _finishMessageAndStartNext(GWEN_MSG_ENDPOINT *ep) xep->dbCurrentReadHeader=NULL; if (xep->flags & AQH_ENDPOINT_HTTP_FLAGS_PASSIVE) { - DBG_INFO(AQH_LOGDOMAIN, "Passive connection"); + DBG_DEBUG(AQH_LOGDOMAIN, "Passive connection"); xep->readMode=AQH_EndpointHttpd_ReadMode_Command; } else { - DBG_INFO(AQH_LOGDOMAIN, "Active connection"); + DBG_DEBUG(AQH_LOGDOMAIN, "Active connection"); xep->readMode=AQH_EndpointHttpd_ReadMode_Status; } } @@ -517,7 +517,7 @@ void _abortMessage(GWEN_MSG_ENDPOINT *ep) { AQH_ENDPOINT_HTTP *xep; - DBG_INFO(AQH_LOGDOMAIN, "Message completely received."); + DBG_DEBUG(AQH_LOGDOMAIN, "Aborting message (if any)."); xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT, AQH_ENDPOINT_HTTP, ep); GWEN_Buffer_Reset(xep->currentReadBuffer); @@ -587,7 +587,7 @@ int _parseHeader(char *bufferPtr, GWEN_DB_NODE *db) while (*p && (*p==32 || *p==9)) p++; if (*p) { - DBG_ERROR(AQH_LOGDOMAIN, "Setting header variable: [%s] = [%s]", pVarBegin, p); + DBG_DEBUG(AQH_LOGDOMAIN, "Setting header variable: [%s] = [%s]", pVarBegin, p); GWEN_DB_SetCharValue(db, GWEN_PATH_FLAGS_CREATE_VAR, pVarBegin, p); } } diff --git a/aqhome/http/httpservice_conf.c b/aqhome/http/httpservice_conf.c index 8cd00c9..d9e1871 100644 --- a/aqhome/http/httpservice_conf.c +++ b/aqhome/http/httpservice_conf.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -54,11 +55,15 @@ static GWEN_BUFFER *_getConfigFilePath(const AQH_SERVICE *sv, const char *fileNa static GWEN_BUFFER *_getModuleFilePath(const AQH_SERVICE *sv, const char *modName); static GWEN_BUFFER *_getUserFilePath(const AQH_SERVICE *sv, const char *userAlias); static GWEN_BUFFER *_getSessionFilePath(const AQH_SERVICE *sv, const char *sessionUid); +static GWEN_BUFFER *_getSessionFolder(const AQH_SERVICE *sv); static int _checkUser(const AQH_USER *user, int ignoreMissingId); static int _checkSession(const AQH_SESSION *session); static int _checkModule(const AQH_MODULE *m, int ignoreMissingId); static int _checkRoleList(const AQH_ROLE_LIST *roleList, int ignoreMissingId); static int _writeDbFile(const char *fname, GWEN_DB_NODE *db); +static GWEN_STRINGLIST *_getConfFileList(const char *folder, const char *mask); +static AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv); +static AQH_SESSION *_readSessionFromFile(const char *filename); @@ -278,6 +283,141 @@ AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid) +int AQH_HttpService_LockSessions(AQH_SERVICE *sv) +{ + AQH_HTTP_SERVICE *xsv; + int rv; + + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv); + if (xsv==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object"); + return GWEN_ERROR_GENERIC; + } + + rv=GWEN_Mutex_Lock(xsv->sessionMutex); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on session mutex"); + return rv; + } + + return 0; +} + + + +int AQH_HttpService_UnlockSessions(AQH_SERVICE *sv) +{ + AQH_HTTP_SERVICE *xsv; + int rv; + + xsv=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_HTTP_SERVICE, sv); + if (xsv==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "Not a AQH_HttpService object"); + return GWEN_ERROR_GENERIC; + } + + rv=GWEN_Mutex_Unlock(xsv->sessionMutex); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on session mutex"); + return rv; + } + + return 0; +} + + + +int AQH_HttpService_CleanupSessions(AQH_SERVICE *sv, int maxAgeInSecs) +{ + time_t tNow; + AQH_SESSION_LIST *sessionList; + int rv; + + rv=AQH_HttpService_LockSessions(sv); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "Error locking sessions (%d)", rv); + return rv; + } + + tNow=time(NULL); + + sessionList=AQH_Service_GetSessionList(sv); + if (sessionList) { + AQH_SESSION *session; + + session=AQH_Session_List_First(sessionList); + while(session) { + AQH_SESSION *next; + const GWEN_TIMESTAMP *ts; + + next=AQH_Session_List_Next(session); + + ts=AQH_Session_GetTimestampLastAccess(session); + if (ts==NULL) + ts=AQH_Session_GetTimestampCreation(session); + if (ts) { + time_t diff; + + diff=tNow-GWEN_Timestamp_toTimeT(ts); + if (((int)diff)>maxAgeInSecs) { + DBG_INFO(AQH_LOGDOMAIN, "Session \"%s\" expired (%d secs)", AQH_Session_GetUid(session), (int) diff); + rv=AQH_HttpService_DelSession(sv, session); /* frees session!! */ + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + } + } + } + + session=next; + } + } + + rv=AQH_HttpService_UnlockSessions(sv); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "Error unlocking sessions (%d)", rv); + return rv; + } + return 0; +} + + +void AQH_HttpService_LoadAllSessions(AQH_SERVICE *sv) +{ + AQH_SESSION_LIST *sessionList; + + sessionList=_readAllSessionsIntoList(sv); + if (sessionList) { + AQH_SESSION *session; + + while( (session=AQH_Session_List_First(sessionList)) ) { + const char *sessionUid; + const char *userAlias; + + AQH_Session_List_Del(session); + sessionUid=AQH_Session_GetUid(session); + userAlias=AQH_Session_GetUserAlias(session); + if (userAlias && *userAlias) { + AQH_USER *user; + + user=AQH_HttpService_GetUser(sv, userAlias); + if (user==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "User \"%s\" for session \"%s\" not available", userAlias, sessionUid); + AQH_Session_free(session); + } + else { + DBG_INFO(AQH_LOGDOMAIN, "Adding session \"%s\" (user=%s)", sessionUid, userAlias); + AQH_Session_SetUser(session, user); + AQH_Service_AddSession(sv, session); + } + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "Session has no user, not adding"); + AQH_Session_free(session); + } + } /* while */ + AQH_Session_List_free(sessionList); + } +} @@ -518,29 +658,21 @@ AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const char *sess { GWEN_BUFFER *nameBuf; int rv; - GWEN_DB_NODE *db; AQH_SESSION *session; - db=GWEN_DB_Group_new("user"); - nameBuf=_getSessionFilePath(sv, sessionUid); - rv=GWEN_DB_ReadFile(db, GWEN_Buffer_GetStart(nameBuf), GWEN_DB_FLAGS_DEFAULT); - if (rv<0) { - DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + if (nameBuf==NULL) { + DBG_INFO(AQH_LOGDOMAIN, "here"); + return NULL; + } + session=_readSessionFromFile(GWEN_Buffer_GetStart(nameBuf)); + if (session==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "Error loading session \"%s\" from config group", sessionUid); GWEN_Buffer_free(nameBuf); - GWEN_DB_Group_free(db); return NULL; } GWEN_Buffer_free(nameBuf); - session=AQH_Session_fromDb(db); - if (session==NULL) { - DBG_ERROR(AQH_LOGDOMAIN, "Error loading session \"%s\" from config group", sessionUid); - GWEN_DB_Group_free(db); - return NULL; - } - GWEN_DB_Group_free(db); - rv=_checkSession(session); if (rv<0) { DBG_INFO(AQH_LOGDOMAIN, "Invalid data for session \"%s\"", sessionUid); @@ -686,7 +818,6 @@ AQH_SESSION *_ensureSession(AQH_SERVICE *sv, const char *sessionUid) AQH_Session_free(session); return NULL; } - AQH_User_Attach(user); AQH_Session_SetUser(session, user); AQH_Service_AddSession(sv, session); } @@ -773,23 +904,16 @@ GWEN_BUFFER *_getUserFilePath(const AQH_SERVICE *sv, const char *userAlias) GWEN_BUFFER *_getSessionFilePath(const AQH_SERVICE *sv, const char *sessionUid) { - const char *configFolder; + GWEN_BUFFER *nameBuf; - configFolder=AQH_HttpService_GetConfigFolder(sv); - if (!(configFolder && *configFolder)) { - DBG_ERROR(AQH_LOGDOMAIN, "No config folder given"); - return NULL; - } - else { - GWEN_BUFFER *nameBuf; - - nameBuf=GWEN_Buffer_new(0, 256, 0, 1); - GWEN_Buffer_AppendString(nameBuf, configFolder); - GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_SESSIONS GWEN_DIR_SEPARATOR_S); + nameBuf=_getSessionFolder(sv); + if (nameBuf) { + GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S); GWEN_Text_EscapeToBuffer(sessionUid, nameBuf); GWEN_Buffer_AppendString(nameBuf, ".conf"); return nameBuf; } + return NULL; } @@ -1012,6 +1136,132 @@ int _writeDbFile(const char *fname, GWEN_DB_NODE *db) +GWEN_BUFFER *_getSessionFolder(const AQH_SERVICE *sv) +{ + const char *configFolder; + + configFolder=AQH_HttpService_GetConfigFolder(sv); + if (!(configFolder && *configFolder)) { + DBG_ERROR(AQH_LOGDOMAIN, "No config folder given"); + return NULL; + } + else { + GWEN_BUFFER *nameBuf; + + nameBuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendString(nameBuf, configFolder); + GWEN_Buffer_AppendString(nameBuf, GWEN_DIR_SEPARATOR_S AQH_HTTP_SERVICE_DIR_SESSIONS); + return nameBuf; + } +} + + + +GWEN_STRINGLIST *_getConfFileList(const char *folder, const char *mask) +{ + GWEN_STRINGLIST *sl; + int rv; + + sl=GWEN_StringList_new(); + rv=GWEN_Directory_GetFileEntries(folder, sl, mask); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + GWEN_StringList_free(sl); + return NULL; + } + if (GWEN_StringList_Count(sl)<1) { + DBG_INFO(AQH_LOGDOMAIN, "Empty string list"); + GWEN_StringList_free(sl); + return NULL; + } + + return sl; +} + + + +AQH_SESSION_LIST *_readAllSessionsIntoList(const AQH_SERVICE *sv) +{ + GWEN_BUFFER *folderBuf; + + folderBuf=_getSessionFolder(sv); + if (folderBuf) { + GWEN_STRINGLIST *fileList; + + fileList=_getConfFileList(GWEN_Buffer_GetStart(folderBuf), "*.conf"); + if (fileList) { + AQH_SESSION_LIST *sessionList; + uint32_t pos; + GWEN_STRINGLISTENTRY *se; + + sessionList=AQH_Session_List_new(); + GWEN_Buffer_AppendString(folderBuf, GWEN_DIR_SEPARATOR_S); + pos=GWEN_Buffer_GetPos(folderBuf); + se=GWEN_StringList_FirstEntry(fileList); + while(se) { + const char *s; + + s=GWEN_StringListEntry_Data(se); + if (s && *s) { + AQH_SESSION *session; + + GWEN_Buffer_AppendString(folderBuf, s); + session=_readSessionFromFile(GWEN_Buffer_GetStart(folderBuf)); + if (session==NULL) { + DBG_INFO(AQH_LOGDOMAIN, "here"); + } + else { + AQH_Session_List_Add(session, sessionList); + } + GWEN_Buffer_Crop(folderBuf, 0, pos); + } + se=GWEN_StringListEntry_Next(se); + } + if (AQH_Session_List_GetCount(sessionList)<1) { + DBG_INFO(NULL, "Empty session list"); + AQH_Session_List_free(sessionList); + GWEN_StringList_free(fileList); + GWEN_Buffer_free(folderBuf); + return NULL; + } + GWEN_StringList_free(fileList); + GWEN_Buffer_free(folderBuf); + return sessionList; + } + GWEN_Buffer_free(folderBuf); + } + return NULL; +} + + + +AQH_SESSION *_readSessionFromFile(const char *filename) +{ + int rv; + GWEN_DB_NODE *db; + AQH_SESSION *session; + + db=GWEN_DB_Group_new("session"); + + rv=GWEN_DB_ReadFile(db, filename, GWEN_DB_FLAGS_DEFAULT); + if (rv<0) { + DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv); + GWEN_DB_Group_free(db); + return NULL; + } + + session=AQH_Session_fromDb(db); + if (session==NULL) { + DBG_ERROR(AQH_LOGDOMAIN, "Error loading session from file \"%s\"", filename); + GWEN_DB_Group_free(db); + return NULL; + } + GWEN_DB_Group_free(db); + return session; +} + + + diff --git a/aqhome/http/httpservice_conf.h b/aqhome/http/httpservice_conf.h index 243ab98..3104eb0 100644 --- a/aqhome/http/httpservice_conf.h +++ b/aqhome/http/httpservice_conf.h @@ -32,7 +32,9 @@ AQHOME_API AQH_USER *AQH_HttpService_GetUser(AQH_SERVICE *sv, const char *alias) AQHOME_API int AQH_HttpService_WriteUser(const AQH_SERVICE *sv, const AQH_USER *user); AQHOME_API AQH_SESSION *AQH_HttpService_GetSession(AQH_SERVICE *sv, const char *sessionUid); - +AQHOME_API int AQH_HttpService_LockSessions(AQH_SERVICE *sv); +AQHOME_API int AQH_HttpService_UnlockSessions(AQH_SERVICE *sv); +AQHOME_API int AQH_HttpService_CleanupSessions(AQH_SERVICE *sv, int maxAgeInSecs); AQHOME_API AQH_MODULE *AQH_HttpService_LoadModule(const AQH_SERVICE *sv, const char *modName); AQHOME_API int AQH_HttpService_SaveModule(const AQH_SERVICE *sv, const AQH_MODULE *m); @@ -47,6 +49,7 @@ AQHOME_API AQH_SESSION *AQH_HttpService_LoadSession(const AQH_SERVICE *sv, const AQHOME_API int AQH_HttpService_SaveSession(const AQH_SERVICE *sv, const AQH_SESSION *session); AQHOME_API int AQH_HttpService_AddSession(AQH_SERVICE *sv, AQH_SESSION *session); AQHOME_API int AQH_HttpService_DelSession(AQH_SERVICE *sv, AQH_SESSION *session); +AQHOME_API void AQH_HttpService_LoadAllSessions(AQH_SERVICE *sv); #endif diff --git a/aqhome/http/httpservice_http.c b/aqhome/http/httpservice_http.c index fda88dc..781151b 100644 --- a/aqhome/http/httpservice_http.c +++ b/aqhome/http/httpservice_http.c @@ -94,7 +94,13 @@ GWEN_MSG *AQH_HttpService_HandleHttpRequest(AQH_SERVICE *sv, GWEN_MSG_ENDPOINT * DBG_INFO(AQH_LOGDOMAIN, "Session \"%s\" not found", s); } else { + GWEN_TIMESTAMP *ts; + DBG_INFO(AQH_LOGDOMAIN, "Found session \"%s\"", s); + ts=GWEN_Timestamp_NowInLocalTime(); + AQH_Session_SetTimestampLastAccess(session, ts); + GWEN_Timestamp_free(ts); + AQH_Session_AddRuntimeFlags(session, AQH_SESSION_RTFLAGS_MODIFIED); AQH_HttpRequest_SetSession(rq, session); } } diff --git a/aqhome/service/0BUILD b/aqhome/service/0BUILD index 1214ebe..20ac23e 100644 --- a/aqhome/service/0BUILD +++ b/aqhome/service/0BUILD @@ -31,6 +31,7 @@ role.t2d user.t2d session.t2d + permdef.t2d diff --git a/aqhome/service/module.t2d b/aqhome/service/module.t2d index d4e1927..790af45 100644 --- a/aqhome/service/module.t2d +++ b/aqhome/service/module.t2d @@ -15,12 +15,15 @@ with_db with_list1 with_list2 + nodup + nocopy
aqhome/api.h
gwenhywfar/error.h
aqhome/service/role.h
+
aqhome/service/permdef.h
@@ -42,14 +45,14 @@ 0 0 public - with_getbymember + own with_getbymember 0 0 public - + own @@ -68,6 +71,15 @@ none + + NULL + NULL + public + own + none + none + + diff --git a/aqhome/service/permdef.t2d b/aqhome/service/permdef.t2d new file mode 100644 index 0000000..63df78b --- /dev/null +++ b/aqhome/service/permdef.t2d @@ -0,0 +1,65 @@ + + + + + + + + AQH_PERMDEF + AQH_PermDef + permdef + + + with_xml + with_db + with_list1 + nodup + nocopy + + + +
aqhome/api.h
+
+ + + + +
+ + + + + + NULL + NULL + public + own with_getbymember + + + + NULL + NULL + public + own with_getbymember + + + + 0 + 0 + public + own with_getbymember + + + + NULL + NULL + public + own + + + + +
+ +
+ diff --git a/aqhome/service/role.t2d b/aqhome/service/role.t2d index 3e1d536..da88084 100644 --- a/aqhome/service/role.t2d +++ b/aqhome/service/role.t2d @@ -40,7 +40,7 @@ 0 0 public - + own diff --git a/aqhome/service/service.c b/aqhome/service/service.c index 116a1fe..06f32d4 100644 --- a/aqhome/service/service.c +++ b/aqhome/service/service.c @@ -44,10 +44,12 @@ void AQH_Service_free(AQH_SERVICE *sv) { if (sv) { GWEN_LIST_FINI(AQH_SERVICE, sv); + GWEN_INHERIT_FINI(AQH_SERVICE, sv); + AQH_User_List_free(sv->userList); AQH_Module_List_free(sv->moduleList); AQH_Session_List_free(sv->sessionList); - GWEN_INHERIT_FINI(AQH_SERVICE, sv); + GWEN_FREE_OBJECT(sv); } } diff --git a/aqhome/service/session.t2d b/aqhome/service/session.t2d index b541269..1b14585 100644 --- a/aqhome/service/session.t2d +++ b/aqhome/service/session.t2d @@ -31,13 +31,21 @@ + + + + + + + + 0 0 public - with_getbymember + own with_getbymember @@ -51,7 +59,7 @@ NULL NULL public - + own @@ -65,7 +73,7 @@ 0 0 public - + own @@ -75,6 +83,13 @@ own + + NULL + NULL + public + own + + NULL @@ -85,6 +100,15 @@ volatile nodup nocopy + + + + 0 + 0 + public + with_flags volatile + + diff --git a/aqhome/service/user.t2d b/aqhome/service/user.t2d index 9bff515..1c46e13 100644 --- a/aqhome/service/user.t2d +++ b/aqhome/service/user.t2d @@ -86,35 +86,35 @@ 0 0 public - + own 0 0 public - with_getbymember + own with_getbymember 0 0 public - + own 0 0 public - + own 0 0 public - + own diff --git a/vg_aqhomed.sh b/vg_aqhomed.sh new file mode 100755 index 0000000..e64f381 --- /dev/null +++ b/vg_aqhomed.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +export AQHOME_LOGLEVEL=info +export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH" + +valgrind \ + --tool=memcheck --trace-children=yes -v --log-file=aqhomed.vg --leak-check=full --show-reachable=yes \ + --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free \ + 0-build/apps/aqhomed/aqhomed \ + -l aqhome.log \ + -db aqhome.db \ + -ma 192.168.117.192 -mp 1883 --mqttclientid=AQHOMELOGTEST \ + -t 127.0.0.1 \ + --pidfile=./aqhomed.pid \ + -T 300 + diff --git a/vg_mqttlog.sh b/vg_mqttlog.sh new file mode 100755 index 0000000..2f351a2 --- /dev/null +++ b/vg_mqttlog.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +export AQHOME_LOGLEVEL=info +export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH" + +# valgrind --tool=memcheck --trace-children=yes -v --log-file=aqf15 --leak-check=full --show-reachable=yes --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free --suppressions=../../../../debug/valgrind.supp /usr/local/bin/aqfinance + +# valgrind --tool=memcheck --trace-children=yes -v --log-file=aqhomed.vg --leak-check=full --show-reachable=yes --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free 0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 -T 300 + +valgrind \ + --tool=memcheck --trace-children=yes -v --log-file=aqhome-mqttlog.vg --leak-check=full --show-reachable=yes \ + --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free \ + 0-build/apps/aqhome-mqttlog/aqhome-mqttlog \ + -ma 192.168.117.192 -mp 1883 --mqttclientid=AQHOMEMQTTLOGTEST \ + -W /tmp/aqhome/mqttlog \ + -i apps/aqhome-mqttlog/mqttlog.conf \ + -T 30 + diff --git a/vg_storage.sh b/vg_storage.sh new file mode 100755 index 0000000..68de835 --- /dev/null +++ b/vg_storage.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +export AQHOME_LOGLEVEL=info +export LD_LIBRARY_PATH="0-build/aqhome/:$LD_LIBRARY_PATH" + +# valgrind --tool=memcheck --trace-children=yes -v --log-file=aqf15 --leak-check=full --show-reachable=yes --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free --suppressions=../../../../debug/valgrind.supp /usr/local/bin/aqfinance + +# valgrind --tool=memcheck --trace-children=yes -v --log-file=aqhomed.vg --leak-check=full --show-reachable=yes --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free 0-build/apps/aqhomed/aqhomed -l aqhome.log -db aqhome.db -ma 192.168.117.192 -mp 1883 -t 127.0.0.1 -T 300 + +valgrind \ + --tool=memcheck --trace-children=yes -v --log-file=aqhome-storage.vg --leak-check=full --show-reachable=yes \ + --track-origins=yes --num-callers=50 --keep-stacktraces=alloc-and-free \ + 0-build/apps/aqhome-storage/aqhome-storage \ + --sourcefolder=apps/aqhome-storage/test/html \ + -D apps/aqhome-storage/test/config \ + --statefile=apps/aqhome-storage/test/config/state \ + -ma 192.168.117.192 -mp 1883 --mqttclientid=AQHOMESTORAGEVG \ + -ha 127.0.0.1 -hp 1884 \ + -p ./aqhome-storage.pid \ + -T 90 +