#include "afb-session.h"
#include "verbose.h"
-#define HEADCOUNT 16
-#define COOKEYCOUNT 8
-#define COOKEYMASK (COOKEYCOUNT - 1)
+#define SIZEUUID 37
+#define HEADCOUNT 16
+#define COOKIECOUNT 8
+#define COOKIEMASK (COOKIECOUNT - 1)
-#define NOW (time(NULL))
+#define _MAXEXP_ ((time_t)(~(time_t)0))
+#define _MAXEXP2_ ((time_t)((((unsigned long long)_MAXEXP_) >> 1)))
+#define MAX_EXPIRATION (_MAXEXP_ >= 0 ? _MAXEXP_ : _MAXEXP2_)
+#define NOW (time(NULL))
struct cookie
{
struct afb_session *next; /* link to the next */
unsigned refcount;
int timeout;
- time_t expiration; // expiration time of the token
+ time_t expiration; // expiration time of the token
pthread_mutex_t mutex;
+ struct cookie *cookies[COOKIECOUNT];
+ char autoclose;
char idx;
- char uuid[37]; // long term authentication of remote client
- char token[37]; // short term authentication of remote client
- struct cookie *cookies[COOKEYCOUNT];
+ char uuid[SIZEUUID]; // long term authentication of remote client
+ char token[SIZEUUID]; // short term authentication of remote client
};
// Session UUID are store in a simple array [for 10 sessions this should be enough]
static struct {
- pthread_mutex_t mutex; // declare a mutex to protect hash table
+ pthread_mutex_t mutex; // declare a mutex to protect hash table
struct afb_session *heads[HEADCOUNT]; // sessions
- int count; // current number of sessions
+ int count; // current number of sessions
int max;
int timeout;
- char initok[37];
+ char initok[SIZEUUID];
} sessions;
/* generate a uuid */
-static void new_uuid(char uuid[37])
+static void new_uuid(char uuid[SIZEUUID])
{
uuid_t newuuid;
uuid_generate(newuuid);
}
// Free context [XXXX Should be protected again memory abort XXXX]
-static void remove_all_cookies(struct afb_session *session)
+static void close_session(struct afb_session *session)
{
int idx;
- struct cookie *cookie, *next;
-
- // free cookies
- for (idx = 0 ; idx < COOKEYCOUNT ; idx++) {
- cookie = session->cookies[idx];
- session->cookies[idx] = NULL;
- while (cookie != NULL) {
- next = cookie->next;
+ struct cookie *cookie;
+
+ /* free cookies */
+ for (idx = 0 ; idx < COOKIECOUNT ; idx++) {
+ while ((cookie = session->cookies[idx])) {
+ session->cookies[idx] = cookie->next;
if (cookie->freecb != NULL)
cookie->freecb(cookie->value);
free(cookie);
- cookie = next;
}
}
}
return r; // % HEADCOUNT;
}
-// Create a new store in RAM, not that is too small it will be automatically extended
-void afb_session_init (int max_session_count, int timeout, const char *initok)
+/**
+ * Initialize the session manager with a 'max_session_count',
+ * an initial common 'timeout' and an initial common token 'initok'.
+ *
+ * @param max_session_count maximum allowed session count in the same time
+ * @param timeout the initial default timeout of sessions
+ * @param initok the initial default token of sessions
+ *
+ */
+int afb_session_init (int max_session_count, int timeout, const char *initok)
{
pthread_mutex_init(&sessions.mutex, NULL);
sessions.max = max_session_count;
strcpy(sessions.initok, initok);
else {
ERROR("initial token '%s' too long (max length %d)", initok, ((int)(sizeof sessions.initok)) - 1);
- exit(1);
+ errno = EINVAL;
+ return -1;
}
+ return 0;
}
const char *afb_session_initial_token()
assert (session != NULL);
+ close_session(session);
pthread_mutex_lock(&sessions.mutex);
prv = &sessions.heads[(int)session->idx];
while (*prv)
pthread_mutex_unlock(&sessions.mutex);
}
-// Check if context timeout or not
-static int is_expired (struct afb_session *ctx, time_t now)
-{
- assert (ctx != NULL);
- return ctx->expiration < now;
-}
-
-// Check if context is active or not
-static int is_active (struct afb_session *ctx, time_t now)
-{
- assert (ctx != NULL);
- return ctx->uuid[0] != 0 && ctx->expiration >= now;
-}
-
// Loop on every entry and remove old context sessions.hash
static time_t cleanup ()
{
session = sessions.heads[idx];
while (session) {
next = session->next;
- if (is_expired(session, now))
+ if (session->expiration < now)
afb_session_close(session);
session = next;
}
return now;
}
+static void update_timeout(struct afb_session *session, time_t now, int timeout)
+{
+ time_t expiration;
+
+ /* compute expiration */
+ if (timeout == AFB_SESSION_TIMEOUT_INFINITE)
+ expiration = MAX_EXPIRATION;
+ else {
+ if (timeout == AFB_SESSION_TIMEOUT_DEFAULT)
+ expiration = now + sessions.timeout;
+ else
+ expiration = now + timeout;
+ if (expiration < 0)
+ expiration = MAX_EXPIRATION;
+ }
+
+ /* record the values */
+ session->timeout = timeout;
+ session->expiration = expiration;
+}
+
+static void update_expiration(struct afb_session *session, time_t now)
+{
+ update_timeout(session, now, session->timeout);
+}
+
static struct afb_session *add_session (const char *uuid, int timeout, time_t now, int idx)
{
struct afb_session *session;
+ /* check arguments */
if (!AFB_SESSION_TIMEOUT_IS_VALID(timeout)
|| (uuid && strlen(uuid) >= sizeof session->uuid)) {
errno = EINVAL;
return NULL;
}
+ /* check session count */
if (sessions.count >= sessions.max) {
errno = EBUSY;
return NULL;
return NULL;
}
+ /* initialize */
pthread_mutex_init(&session->mutex, NULL);
session->refcount = 1;
strcpy(session->uuid, uuid);
strcpy(session->token, sessions.initok);
+ update_timeout(session, now, timeout);
- /* init timeout */
- if (timeout == AFB_SESSION_TIMEOUT_DEFAULT)
- timeout = sessions.timeout;
- session->timeout = timeout;
- session->expiration = now + timeout;
- if (timeout == AFB_SESSION_TIMEOUT_INFINITE || session->expiration < 0) {
- session->expiration = (time_t)(~(time_t)0);
- if (session->expiration < 0)
- session->expiration = (time_t)(((unsigned long long)session->expiration) >> 1);
- }
-
+ /* link */
session->idx = (char)idx;
session->next = sessions.heads[idx];
sessions.heads[idx] = session;
return session;
}
+/* create a new session for the given timeout */
static struct afb_session *new_session (int timeout, time_t now)
{
int idx;
- char uuid[37];
+ char uuid[SIZEUUID];
do {
new_uuid(uuid);
pthread_mutex_lock(&sessions.mutex);
cleanup();
session = search(uuid, pearson4(uuid));
+ if (session)
+ __atomic_add_fetch(&session->refcount, 1, __ATOMIC_RELAXED);
pthread_mutex_unlock(&sessions.mutex);
return session;
else {
idx = pearson4(uuid);
session = search(uuid, idx);
- if (session != NULL) {
+ if (session) {
__atomic_add_fetch(&session->refcount, 1, __ATOMIC_RELAXED);
pthread_mutex_unlock(&sessions.mutex);
if (created)
if (session != NULL) {
assert(session->refcount != 0);
if (!__atomic_sub_fetch(&session->refcount, 1, __ATOMIC_RELAXED)) {
- if (session->uuid[0] == 0)
+ pthread_mutex_lock(&session->mutex);
+ if (session->autoclose || session->uuid[0] == 0)
destroy (session);
+ else
+ pthread_mutex_unlock(&session->mutex);
}
}
}
-// Free Client Session Context
+// close Client Session Context
void afb_session_close (struct afb_session *session)
{
assert(session != NULL);
+ pthread_mutex_lock(&session->mutex);
if (session->uuid[0] != 0) {
session->uuid[0] = 0;
- remove_all_cookies(session);
- if (session->refcount == 0)
+ if (session->refcount)
+ close_session(session);
+ else {
destroy (session);
+ return;
+ }
}
+ pthread_mutex_unlock(&session->mutex);
+}
+
+/* set the autoclose flag */
+void afb_session_set_autoclose(struct afb_session *session, int autoclose)
+{
+ assert(session != NULL);
+ session->autoclose = (char)!!autoclose;
+}
+
+// is the session active?
+int afb_session_is_active (struct afb_session *session)
+{
+ assert(session != NULL);
+ return !!session->uuid[0];
+}
+
+// is the session closed?
+int afb_session_is_closed (struct afb_session *session)
+{
+ assert(session != NULL);
+ return !session->uuid[0];
}
// Sample Generic Ping Debug API
assert(session != NULL);
assert(token != NULL);
- // compare current token with previous one
- if (!is_active (session, NOW))
+ if (!session->uuid[0])
+ return 0;
+
+ if (session->expiration < NOW)
return 0;
if (session->token[0] && strcmp (token, session->token) != 0)
new_uuid(session->token);
// keep track of time for session timeout and further clean up
- if (session->timeout != 0)
- session->expiration = NOW + session->timeout;
+ update_expiration(session, NOW);
}
/* Returns the uuid of 'session' */
{
intptr_t x = (intptr_t)key;
unsigned r = (unsigned)((x >> 5) ^ (x >> 15));
- return r & COOKEYMASK;
+ return r & COOKIEMASK;
}
/**
*
* The behaviour of this function depends on its parameters:
*
- * @param session the session
- * @param key the key of the cookie
- * @param makecb the creation function
- * @param freecb the release function
- * @param closure an argument
- * @param replace a boolean enforcing replecement of the previous value
+ * @param session the session
+ * @param key the key of the cookie
+ * @param makecb the creation function or NULL
+ * @param freecb the release function or NULL
+ * @param closure an argument for makecb or the value if makecb==NULL
+ * @param replace a boolean enforcing replecement of the previous value
*
* @return the value of the cookie
*