2 * Copyright (C) 2015 "IoT.bzh"
3 * Author "Fulup Ar Foll"
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * http://stackoverflow.com/questions/25971505/how-to-delete-element-from-hsearch
29 #include <uuid/uuid.h>
37 #include <sys/types.h>
45 #define NOW (time(NULL))
47 // Session UUID are store in a simple array [for 10 sessions this should be enough]
49 pthread_mutex_t mutex; // declare a mutex to protect hash table
50 AFB_clientCtx **store; // sessions store
51 int count; // current number of sessions
58 static const char key_uuid[] = "uuid";
59 static const char key_token[] = "token";
61 // Free context [XXXX Should be protected again memory abort XXXX]
62 static void ctxUuidFreeCB (AFB_clientCtx *client)
66 // If application add a handle let's free it now
67 if (client->contexts != NULL) {
69 // Free client handle with a standard Free function, with app callback or ignore it
70 for (idx=0; idx < sessions.apicount; idx ++) {
71 if (client->contexts[idx] != NULL) {
72 afb_apis_free_context(idx, client->contexts[idx]);
78 // Create a new store in RAM, not that is too small it will be automatically extended
79 void ctxStoreInit (int nbSession, int timeout, int apicount, const char *initok)
81 // let's create as store as hashtable does not have any
82 sessions.store = calloc (1 + (unsigned)nbSession, sizeof(AFB_clientCtx));
83 sessions.max = nbSession;
84 sessions.timeout = timeout;
85 sessions.apicount = apicount;
86 if (strlen(initok) >= 37) {
87 fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
90 sessions.initok = initok;
93 static AFB_clientCtx *ctxStoreSearch (const char* uuid)
96 AFB_clientCtx *client;
98 assert (uuid != NULL);
100 pthread_mutex_lock(&sessions.mutex);
102 for (idx=0; idx < sessions.max; idx++) {
103 client = sessions.store[idx];
104 if (client && (0 == strcmp (uuid, client->uuid)))
110 pthread_mutex_unlock(&sessions.mutex);
114 static int ctxStoreDel (AFB_clientCtx *client)
119 assert (client != NULL);
121 pthread_mutex_lock(&sessions.mutex);
123 for (idx=0; idx < sessions.max; idx++) {
124 if (sessions.store[idx] == client) {
125 sessions.store[idx]=NULL;
127 ctxUuidFreeCB (client);
134 pthread_mutex_unlock(&sessions.mutex);
138 static int ctxStoreAdd (AFB_clientCtx *client)
143 assert (client != NULL);
145 //fprintf (stderr, "ctxStoreAdd request uuid=%s count=%d\n", client->uuid, sessions.count);
147 pthread_mutex_lock(&sessions.mutex);
149 for (idx=0; idx < sessions.max; idx++) {
150 if (NULL == sessions.store[idx]) {
151 sessions.store[idx]= client;
159 pthread_mutex_unlock(&sessions.mutex);
163 // Check if context timeout or not
164 static int ctxStoreTooOld (AFB_clientCtx *ctx, time_t now)
166 return ctx->timeStamp <= now;
169 // Loop on every entry and remove old context sessions.hash
170 void ctxStoreGarbage ()
176 // Loop on Sessions Table and remove anything that is older than timeout
177 for (idx=0; idx < sessions.max; idx++) {
178 ctx = sessions.store[idx];
179 if (ctx != NULL && ctxStoreTooOld(ctx, now)) {
185 // This function will return exiting client context or newly created client context
186 AFB_clientCtx *ctxClientGet (const char *uuid)
189 AFB_clientCtx *clientCtx;
191 /* search for an existing one not too old */
192 clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL;
194 if (!ctxStoreTooOld (clientCtx, NOW))
196 ctxStoreDel (clientCtx);
199 /* mimic old behaviour */
200 if (sessions.initok == NULL)
203 /* cleanup before creating */
204 if(2 * sessions.count >= sessions.max)
207 /* returns a new one */
208 clientCtx = calloc(1, sizeof(AFB_clientCtx)); // init NULL clientContext
209 if (clientCtx != NULL) {
210 clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof (void*));
211 if (clientCtx->contexts != NULL) {
212 /* generate the uuid */
213 uuid_generate(newuuid);
214 uuid_unparse_lower(newuuid, clientCtx->uuid);
215 clientCtx->timeStamp = time(NULL) + sessions.timeout;
216 strcpy(clientCtx->token, sessions.initok);
217 if (ctxStoreAdd (clientCtx))
219 free(clientCtx->contexts);
226 // Free Client Session Context
227 int ctxClientClose (AFB_clientCtx *clientCtx)
229 assert(clientCtx != NULL);
230 return ctxStoreDel (clientCtx);
233 // Sample Generic Ping Debug API
234 int ctxTokenCheck (AFB_clientCtx *clientCtx, const char *token)
236 assert(clientCtx != NULL);
237 assert(token != NULL);
239 // compare current token with previous one
240 if (ctxStoreTooOld (clientCtx, NOW))
242 if (!clientCtx->token[0] || 0 == strcmp (token, clientCtx->token)) {
243 clientCtx->timeStamp = time(NULL) + sessions.timeout;
247 // Token is not valid let move level of assurance to zero and free attached client handle
251 // generate a new token and update client context
252 int ctxTokenNew (AFB_clientCtx *clientCtx)
256 assert(clientCtx != NULL);
258 // Old token was valid let's regenerate a new one
259 uuid_generate(newuuid); // create a new UUID
260 uuid_unparse_lower(newuuid, clientCtx->token);
262 // keep track of time for session timeout and further clean up
263 clientCtx->timeStamp = time(NULL) + sessions.timeout;