X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fafb-session.c;h=70e8c3cdb8ee4f8f5ac07a7342e53e8daac6986a;hb=a2cf84ecde926adeebf09bc2c284401513d3fab3;hp=88d5b0d2b62d0718ebd9fa31eb54484cd21c1599;hpb=d45426257d5149c735e33e3055220625a919e7bc;p=src%2Fapp-framework-binder.git diff --git a/src/afb-session.c b/src/afb-session.c index 88d5b0d2..70e8c3cd 100644 --- a/src/afb-session.c +++ b/src/afb-session.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Copyright (C) 2015-2019 "IoT.bzh" * Author "Fulup Ar Foll" * Author: José Bollo * @@ -22,14 +22,16 @@ #include #include #include +#include #include #include #include - -#include +#include #include "afb-session.h" +#include "afb-hook.h" #include "verbose.h" +#include "pearson.h" #define SIZEUUID 37 #define HEADCOUNT 16 @@ -39,43 +41,48 @@ #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)) +#define NOW (time_now()) -/* structure for a cookie added to sessions */ +/** + * structure for a cookie added to sessions + */ struct cookie { - struct cookie *next; /* link to next cookie */ - const void *key; /* pointer key */ - void *value; /* value */ - void (*freecb)(void*); /* function to call when session is closed */ + struct cookie *next; /**< link to next cookie */ + const void *key; /**< pointer key */ + void *value; /**< value */ + void (*freecb)(void*); /**< function to call when session is closed */ }; -/* +/** * structure for session */ struct afb_session { - struct afb_session *next; /* link to the next */ - unsigned refcount; /* external reference count of the session */ - int timeout; /* timeout of the session */ - time_t expiration; /* expiration time of the token */ - pthread_mutex_t mutex; /* mutex of the session */ - struct cookie *cookies[COOKIECOUNT]; /* cookies of the session */ - uint8_t closed: 1; /* is the session closed ? */ - uint8_t autoclose: 1; /* close the session when unreferenced */ - uint8_t notinset: 1; /* session removed from the set of sessions */ - char uuid[SIZEUUID]; /* long term authentication of remote client */ - char token[SIZEUUID]; /* short term authentication of remote client */ + struct afb_session *next; /**< link to the next */ + unsigned refcount; /**< count of reference to the session */ + int timeout; /**< timeout of the session */ + time_t expiration; /**< expiration time of the token */ + pthread_mutex_t mutex; /**< mutex of the session */ + struct cookie *cookies[COOKIECOUNT]; /**< cookies of the session */ + char *lang; /**< current language setting for the session */ + uint8_t closed: 1; /**< is the session closed ? */ + uint8_t autoclose: 1; /**< close the session when unreferenced */ + uint8_t notinset: 1; /**< session removed from the set of sessions */ + 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] */ +/** + * structure for managing sessions + */ static struct { - int count; /* current number of sessions */ - int max; /* maximum count of sessions */ - int timeout; /* common initial timeout */ - struct afb_session *heads[HEADCOUNT]; /* sessions */ - char initok[SIZEUUID]; /* common initial token */ - pthread_mutex_t mutex; /* declare a mutex to protect hash table */ + int count; /**< current number of sessions */ + int max; /**< maximum count of sessions */ + int timeout; /**< common initial timeout */ + struct afb_session *heads[HEADCOUNT]; /**< sessions */ + char initok[SIZEUUID]; /**< common initial token */ + pthread_mutex_t mutex; /**< declare a mutex to protect hash table */ } sessions = { .count = 0, .max = 10, @@ -85,32 +92,73 @@ static struct { .mutex = PTHREAD_MUTEX_INITIALIZER }; -/* generate a new fresh 'uuid' */ -static void new_uuid(char uuid[SIZEUUID]) +/** + * Get the actual raw time + */ +static inline time_t time_now() { - uuid_t newuuid; - uuid_generate(newuuid); - uuid_unparse_lower(newuuid, uuid); + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + return ts.tv_sec; } -/* - * Returns a tiny hash value for the 'text'. - * - * Tiny hash function inspired from pearson +/** + * generate a new fresh 'uuid' */ -static uint8_t pearson4(const char *text) +static void new_uuid(char uuid[SIZEUUID]) { - static uint8_t T[16] = { - 4, 1, 6, 0, 9, 14, 11, 5, - 2, 3, 12, 15, 10, 7, 8, 13 - }; - uint8_t r, c; + uuid_t newuuid; - for (r = 0; (c = (uint8_t)*text) ; text++) { - r = T[r ^ (15 & c)]; - r = T[r ^ (c >> 4)]; +#if defined(USE_UUID_GENERATE) + uuid_generate(newuuid); +#else + struct timespec ts; + static uint16_t pid; + static uint16_t counter; + static char state[32]; + static struct random_data rdata; + + int32_t x; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + if (pid == 0) { + pid = (uint16_t)getpid(); + counter = (uint16_t)(ts.tv_nsec >> 8); + rdata.state = NULL; + initstate_r((((unsigned)pid) << 16) + ((unsigned)counter), + state, sizeof state, &rdata); } - return r; // % HEADCOUNT; + ts.tv_nsec ^= (long)ts.tv_sec; + if (++counter == 0) + counter = 1; + + newuuid[0] = (char)(ts.tv_nsec >> 24); + newuuid[1] = (char)(ts.tv_nsec >> 16); + newuuid[2] = (char)(ts.tv_nsec >> 8); + newuuid[3] = (char)(ts.tv_nsec); + + newuuid[4] = (char)(pid >> 8); + newuuid[5] = (char)(pid); + + random_r(&rdata, &x); + newuuid[6] = (char)(((x >> 16) & 0x0f) | 0x40); /* pseudo-random version */ + newuuid[7] = (char)(x >> 8); + + random_r(&rdata, &x); + newuuid[8] = (char)(((x >> 16) & 0x3f) | 0x80); /* variant RFC4122 */ + newuuid[9] = (char)(x >> 8); + + random_r(&rdata, &x); + newuuid[10] = (char)(x >> 16); + newuuid[11] = (char)(x >> 8); + + random_r(&rdata, &x); + newuuid[12] = (char)(x >> 16); + newuuid[13] = (char)(x >> 8); + + newuuid[14] = (char)(counter >> 8); + newuuid[15] = (char)(counter); +#endif + uuid_unparse_lower(newuuid, uuid); } /* lock the set of sessions for exclusive access */ @@ -145,7 +193,7 @@ static struct afb_session *sessionset_search(const char *uuid, uint8_t hashidx) static int sessionset_add(struct afb_session *session, uint8_t hashidx) { /* check availability */ - if (sessions.count >= sessions.max) { + if (sessions.max && sessions.count >= sessions.max) { errno = EBUSY; return -1; } @@ -189,9 +237,15 @@ static void session_close(struct afb_session *session) /* close only one time */ if (!session->closed) { + /* close it now */ session->closed = 1; - /* free cookies */ +#if WITH_AFB_HOOK + /* emit the hook */ + afb_hook_session_close(session); +#endif + + /* release cookies */ for (idx = 0 ; idx < COOKIECOUNT ; idx++) { while ((cookie = session->cookies[idx])) { session->cookies[idx] = cookie->next; @@ -206,28 +260,23 @@ static void session_close(struct afb_session *session) /* destroy the 'session' */ static void session_destroy (struct afb_session *session) { +#if WITH_AFB_HOOK + afb_hook_session_destroy(session); +#endif pthread_mutex_destroy(&session->mutex); + free(session->lang); free(session); } /* update expiration of 'session' according to 'now' */ static void session_update_expiration(struct afb_session *session, time_t now) { - int timeout; time_t expiration; /* compute expiration */ - timeout = session->timeout; - if (timeout == AFB_SESSION_TIMEOUT_INFINITE) + expiration = now + afb_session_timeout(session); + if (expiration < 0) 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 expiration */ session->expiration = expiration; @@ -271,6 +320,10 @@ static struct afb_session *session_add(const char *uuid, int timeout, time_t now return NULL; } +#if WITH_AFB_HOOK + afb_hook_session_create(session); +#endif + return session; } @@ -313,7 +366,7 @@ static time_t sessionset_cleanup (int force) * @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) { @@ -338,6 +391,28 @@ int afb_session_init (int max_session_count, int timeout, const char *initok) return 0; } +/** + * Iterate the sessions and call 'callback' with + * the 'closure' for each session. + */ +void afb_session_foreach(void (*callback)(void *closure, struct afb_session *session), void *closure) +{ + struct afb_session *session; + int idx; + + /* Loop on Sessions Table and remove anything that is older than timeout */ + sessionset_lock(); + for (idx = 0 ; idx < HEADCOUNT; idx++) { + session = sessions.heads[idx]; + while (session) { + if (!session->closed) + callback(closure, session); + session = session->next; + } + } + sessionset_unlock(); +} + /** * Cleanup the sessionset of its closed or expired sessions */ @@ -378,6 +453,30 @@ struct afb_session *afb_session_create (int timeout) return afb_session_get(NULL, timeout, NULL); } +/** + * Returns the timeout of 'session' in seconds + */ +int afb_session_timeout(struct afb_session *session) +{ + int timeout; + + /* compute timeout */ + timeout = session->timeout; + if (timeout == AFB_SESSION_TIMEOUT_DEFAULT) + timeout = sessions.timeout; + if (timeout < 0) + timeout = INT_MAX; + return timeout; +} + +/** + * Returns the second remaining before expiration of 'session' + */ +int afb_session_what_remains(struct afb_session *session) +{ + return (int)(session->expiration - NOW); +} + /* This function will return exiting session or newly created session */ struct afb_session *afb_session_get (const char *uuid, int timeout, int *created) { @@ -419,8 +518,14 @@ end: /* increase the use count on 'session' (can be NULL) */ struct afb_session *afb_session_addref(struct afb_session *session) { - if (session != NULL) - __atomic_add_fetch(&session->refcount, 1, __ATOMIC_RELAXED); + if (session != NULL) { +#if WITH_AFB_HOOK + afb_hook_session_addref(session); +#endif + session_lock(session); + session->refcount++; + session_unlock(session); + } return session; } @@ -430,8 +535,11 @@ void afb_session_unref(struct afb_session *session) if (session == NULL) return; +#if WITH_AFB_HOOK + afb_hook_session_unref(session); +#endif session_lock(session); - if (!__atomic_sub_fetch(&session->refcount, 1, __ATOMIC_RELAXED)) { + if (!--session->refcount) { if (session->autoclose) session_close(session); if (session->notinset) { @@ -454,7 +562,7 @@ void afb_session_close (struct afb_session *session) * Set the 'autoclose' flag of the 'session' * * A session whose autoclose flag is true will close as - * soon as it is no more referenced. + * soon as it is no more referenced. * * @param session the session to set * @param autoclose the value to set @@ -478,7 +586,7 @@ int afb_session_check_token (struct afb_session *session, const char *token) { int r; - session_unlock(session); + session_lock(session); r = !session->closed && session->expiration >= NOW && !(session->token[0] && strcmp (token, session->token)); @@ -489,11 +597,13 @@ int afb_session_check_token (struct afb_session *session, const char *token) /* generate a new token and update client context */ void afb_session_new_token (struct afb_session *session) { - /* Old token was valid let's regenerate a new one */ + session_lock(session); new_uuid(session->token); - - /* keep track of time for session timeout and further clean up */ session_update_expiration(session, NOW); +#if WITH_AFB_HOOK + afb_hook_session_renew(session); +#endif + session_unlock(session); } /* Returns the uuid of 'session' */ @@ -635,11 +745,44 @@ void *afb_session_get_cookie(struct afb_session *session, const void *key) * @param value the value to store at key * @param freecb a function to use when the cookie value is to remove (or null) * - * @return the data staored for the key or NULL if the key isn't found - * + * @return 0 in case of success or -1 in case of error */ int afb_session_set_cookie(struct afb_session *session, const void *key, void *value, void (*freecb)(void*)) { return -(value != afb_session_cookie(session, key, NULL, freecb, value, 1)); } +/** + * Set the language attached to the session + * + * @param session the session to set + * @param lang the language specifiction to set to session + * + * @return 0 in case of success or -1 in case of error + */ +int afb_session_set_language(struct afb_session *session, const char *lang) +{ + char *oldl, *newl; + + newl = strdup(lang); + if (newl == NULL) + return -1; + + oldl = session->lang; + session->lang = newl; + free(oldl); + return 0; +} + +/** + * Get the language attached to the session + * + * @param session the session to query + * @param lang a default language specifiction + * + * @return the langauage specification to use for session + */ +const char *afb_session_get_language(struct afb_session *session, const char *lang) +{ + return session->lang ?: lang; +}