/*
* Copyright (c) Likewise Software. All rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library 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 Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Module Name:
*
* session-shared.c
*
* Abstract:
*
* Session management API
* Shared (thread-safe) session manager implementation
*
* Authors: Brian Koropoff (bkoropoff@likewisesoftware.com)
*
*/
#include
#include "session-private.h"
#include "util-private.h"
#include
#include
#include
#include
typedef struct SharedSession
{
LWMsgSession base;
/* Remote session identifier */
LWMsgSessionID rsmid;
/* Security token of session creator */
LWMsgSecurityToken* sec_token;
/* Reference count */
size_t volatile refs;
/* Pointer to linked list of handles */
struct SharedHandle* volatile handles;
/* Number of handles */
size_t num_handles;
/* Links to other sessions in the manager */
struct SharedSession * volatile next, * volatile prev;
/* Lock */
pthread_mutex_t lock;
/* Next handle ID */
unsigned long volatile next_hid;
/* User data pointer */
void* data;
/* Destruct function */
LWMsgSessionDestructFunction destruct;
} SharedSession;
typedef struct SharedHandle
{
/* Pointer to associated session */
SharedSession* session;
/* Validity vit */
LWMsgBool volatile valid;
/* Reference count */
size_t volatile refs;
/* Handle type */
const char* type;
/* Handle pointer */
void* pointer;
/* Handle locality */
LWMsgHandleType locality;
/* Handle id */
unsigned long hid;
/* Handle cleanup function */
void (*cleanup)(void*);
/* Links to other handles in the session */
struct SharedHandle* next, *prev;
} SharedHandle;
typedef struct SharedManager
{
LWMsgSessionManager base;
SharedSession* volatile sessions;
pthread_mutex_t lock;
} SharedManager;
#define SHARED_MANAGER(obj) ((SharedManager*) (obj))
#define SHARED_SESSION(obj) ((SharedSession*) (obj))
static inline
void
shared_lock(
SharedManager* priv
)
{
pthread_mutex_lock(&priv->lock);
}
static inline
void
shared_unlock(
SharedManager* priv
)
{
pthread_mutex_unlock(&priv->lock);
}
static inline
void
session_lock(
SharedSession* session
)
{
pthread_mutex_lock(&session->lock);
}
static inline
void
session_unlock(
SharedSession* session
)
{
pthread_mutex_unlock(&session->lock);
}
static void
shared_free_handle(
SharedHandle* entry
)
{
if (entry->cleanup)
{
entry->cleanup(entry->pointer);
}
if (entry->prev)
{
entry->prev->next = entry->next;
}
if (entry->next)
{
entry->next->prev = entry->prev;
}
free(entry);
}
static
LWMsgStatus
shared_add_handle(
SharedSession* session,
const char* type,
LWMsgHandleType locality,
void* pointer,
unsigned long hid,
void (*cleanup)(void*),
SharedHandle** out_handle
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
handle = calloc(1, sizeof(*handle));
if (!handle)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
handle->type = type;
handle->valid = LWMSG_TRUE;
handle->refs = 1;
handle->pointer = pointer ? pointer : handle;
handle->cleanup = cleanup;
handle->hid = hid;
handle->locality = locality;
handle->next = my_session->handles;
if (my_session->handles)
{
my_session->handles->prev = handle;
}
my_session->handles = handle;
my_session->num_handles++;
*out_handle = handle;
error:
return status;
}
static
SharedSession*
shared_find_session(
SharedManager* priv,
const LWMsgSessionID* rsmid
)
{
SharedSession* entry = NULL;
for (entry = priv->sessions; entry; entry = entry->next)
{
if (!memcmp(rsmid->bytes, entry->rsmid.bytes, sizeof(rsmid->bytes)))
{
return entry;
}
}
return NULL;
}
static
void
shared_free_session(
SharedSession* session
)
{
SharedHandle* handle, *next;
for (handle = session->handles; handle; handle = next)
{
next = handle->next;
shared_free_handle(handle);
}
if (session->destruct && session->data)
{
session->destruct(session->sec_token, session->data);
}
if (session->sec_token)
{
lwmsg_security_token_delete(session->sec_token);
}
pthread_mutex_destroy(&session->lock);
free(session);
}
static
void
shared_delete(
LWMsgSessionManager* manager
)
{
SharedManager* priv = SHARED_MANAGER(manager);
SharedSession* entry, *next;
for (entry = priv->sessions; entry; entry = next)
{
next = entry->next;
shared_free_session(entry);
}
pthread_mutex_destroy(&priv->lock);
free(priv);
}
static
LWMsgStatus
shared_enter_session(
LWMsgSessionManager* manager,
const LWMsgSessionID* rsmid,
LWMsgSecurityToken* rtoken,
LWMsgSessionConstructFunction construct,
LWMsgSessionDestructFunction destruct,
void* construct_data,
LWMsgSession** out_session
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedManager* priv = SHARED_MANAGER(manager);
SharedSession* session;
shared_lock(priv);
session = shared_find_session(priv, rsmid);
if (session)
{
if (!session->sec_token || !lwmsg_security_token_can_access(session->sec_token, rtoken))
{
session = NULL;
BAIL_ON_ERROR(status = LWMSG_STATUS_SECURITY);
}
session->refs++;
}
else
{
session = calloc(1, sizeof(*session));
if (!session)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
session->base.manager = manager;
BAIL_ON_ERROR(status = lwmsg_error_map_errno(pthread_mutex_init(&session->lock, NULL)));
memcpy(session->rsmid.bytes, rsmid->bytes, sizeof(rsmid->bytes));
if (rtoken)
{
BAIL_ON_ERROR(status = lwmsg_security_token_copy(rtoken, &session->sec_token));
}
session->refs = 1;
session->destruct = destruct;
if (construct)
{
BAIL_ON_ERROR(status = construct(
session->sec_token,
construct_data,
&session->data));
}
if (priv->sessions)
{
priv->sessions->prev = session;
}
session->next = priv->sessions;
priv->sessions = session;
}
*out_session = LWMSG_SESSION(session);
done:
shared_unlock(priv);
return status;
error:
if (session)
{
shared_free_session(session);
}
goto done;
}
static
LWMsgStatus
shared_leave_session(
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedManager* priv = SHARED_MANAGER(manager);
SharedSession* my_session = SHARED_SESSION(session);
shared_lock(priv);
my_session->refs--;
if (my_session->refs == 0)
{
if (priv->sessions == my_session)
{
priv->sessions = priv->sessions->next;
}
if (my_session->next)
{
my_session->next->prev = my_session->prev;
}
if (my_session->prev)
{
my_session->prev->next = my_session->next;
}
shared_free_session(my_session);
}
shared_unlock(priv);
return status;
}
static
void
shared_retain_session(
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
SharedManager* priv = SHARED_MANAGER(manager);
SharedSession* my_session = SHARED_SESSION(session);
shared_lock(priv);
my_session->refs++;
shared_unlock(priv);
}
static
LWMsgSecurityToken*
shared_get_session_peer_security_token (
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
SharedSession* my_session = SHARED_SESSION(session);
return my_session->sec_token;
}
static
LWMsgStatus
shared_register_handle_remote(
LWMsgSessionManager* manager,
LWMsgSession* session,
const char* type,
LWMsgHandleID hid,
void (*cleanup)(void* ptr),
void** ptr)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
BAIL_ON_ERROR(status = shared_add_handle(
my_session,
type,
LWMSG_HANDLE_REMOTE,
NULL,
hid,
cleanup,
&handle));
if (ptr)
{
*ptr = handle->pointer;
}
error:
session_unlock(my_session);
return status;
}
static
LWMsgStatus
shared_register_handle_local(
LWMsgSessionManager* manager,
LWMsgSession* session,
const char* type,
void* pointer,
void (*cleanup)(void* ptr),
LWMsgHandleID* hid
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
BAIL_ON_ERROR(status = shared_add_handle(
my_session,
type,
LWMSG_HANDLE_LOCAL,
pointer,
my_session->next_hid++,
cleanup,
&handle));
if (hid)
{
*hid = handle->hid;
}
error:
session_unlock(my_session);
return status;
}
static
LWMsgStatus
shared_retain_handle(
LWMsgSessionManager* manager,
LWMsgSession* session,
void* ptr
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
for (handle = my_session->handles; handle; handle = handle->next)
{
if (handle->pointer == ptr)
{
handle->refs++;
goto done;
}
}
BAIL_ON_ERROR(status = LWMSG_STATUS_INVALID_HANDLE);
done:
session_unlock(my_session);
return status;
error:
goto done;
}
static
LWMsgStatus
shared_release_handle(
LWMsgSessionManager* manager,
LWMsgSession* session,
void* ptr
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
for (handle = my_session->handles; handle; handle = handle->next)
{
if (handle->pointer == ptr)
{
if (--handle->refs == 0)
{
if (handle == my_session->handles)
{
my_session->handles = my_session->handles->next;
}
shared_free_handle(handle);
my_session->num_handles--;
}
goto done;
}
}
BAIL_ON_ERROR(status = LWMSG_STATUS_INVALID_HANDLE);
done:
session_unlock(my_session);
return status;
error:
goto done;
}
static
LWMsgStatus
shared_unregister_handle(
LWMsgSessionManager* manager,
LWMsgSession* session,
void* ptr
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
for (handle = my_session->handles; handle; handle = handle->next)
{
if (handle->pointer == ptr)
{
if (!handle->valid)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_INVALID_HANDLE);
}
handle->valid = LWMSG_FALSE;
if (--handle->refs == 0)
{
if (handle == my_session->handles)
{
my_session->handles = my_session->handles->next;
}
shared_free_handle(handle);
my_session->num_handles--;
}
goto done;
}
}
BAIL_ON_ERROR(status = LWMSG_STATUS_NOT_FOUND);
done:
session_unlock(my_session);
return status;
error:
goto done;
}
static
LWMsgStatus
shared_handle_pointer_to_id(
LWMsgSessionManager* manager,
LWMsgSession* session,
void* pointer,
const char** type,
LWMsgHandleType* htype,
LWMsgHandleID* hid
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
for (handle = my_session->handles; handle; handle = handle->next)
{
if (handle->pointer == pointer)
{
if (!handle->valid)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_NOT_FOUND);
}
if (type)
{
*type = handle->type;
}
if (htype)
{
*htype = handle->locality;
}
if (hid)
{
*hid = handle->hid;
}
goto done;
}
}
BAIL_ON_ERROR(status = LWMSG_STATUS_NOT_FOUND);
done:
session_unlock(my_session);
return status;
error:
goto done;
}
static
LWMsgStatus
shared_handle_id_to_pointer(
LWMsgSessionManager* manager,
LWMsgSession* session,
const char* type,
LWMsgHandleType htype,
LWMsgHandleID hid,
void** pointer
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedHandle* handle = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
for (handle = my_session->handles; handle; handle = handle->next)
{
if (handle->hid == hid && handle->locality == htype)
{
if (!handle->valid)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_NOT_FOUND);
}
if (type && strcmp(type, handle->type))
{
BAIL_ON_ERROR(status = LWMSG_STATUS_NOT_FOUND);
}
*pointer = handle->pointer;
handle->refs++;
goto done;
}
}
BAIL_ON_ERROR(status = LWMSG_STATUS_NOT_FOUND);
done:
session_unlock(my_session);
return status;
error:
goto done;
}
static
void*
shared_get_session_data (
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
void* data = NULL;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
data = my_session->data;
session_unlock(my_session);
return data;
}
static
const LWMsgSessionID*
shared_get_session_id(
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
return &SHARED_SESSION(session)->rsmid;
}
static
size_t
shared_get_session_assoc_count(
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
size_t refs;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
refs = my_session->refs;
session_unlock(my_session);
return refs;
}
static
size_t
shared_get_session_handle_count(
LWMsgSessionManager* manager,
LWMsgSession* session
)
{
size_t handles;
SharedSession* my_session = SHARED_SESSION(session);
session_lock(my_session);
handles = my_session->num_handles;
session_unlock(my_session);
return handles;
}
static LWMsgSessionManagerClass shared_class =
{
.delete = shared_delete,
.enter_session = shared_enter_session,
.leave_session = shared_leave_session,
.retain_session = shared_retain_session,
.register_handle_local = shared_register_handle_local,
.register_handle_remote = shared_register_handle_remote,
.retain_handle = shared_retain_handle,
.release_handle = shared_release_handle,
.unregister_handle = shared_unregister_handle,
.handle_pointer_to_id = shared_handle_pointer_to_id,
.handle_id_to_pointer = shared_handle_id_to_pointer,
.get_session_data = shared_get_session_data,
.get_session_id = shared_get_session_id,
.get_session_assoc_count = shared_get_session_assoc_count,
.get_session_handle_count = shared_get_session_handle_count,
.get_session_peer_security_token = shared_get_session_peer_security_token
};
LWMsgStatus
lwmsg_shared_session_manager_new(
LWMsgSessionManager** manager
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
SharedManager* my_manager = NULL;
BAIL_ON_ERROR(status = LWMSG_ALLOC(&my_manager));
BAIL_ON_ERROR(status = lwmsg_session_manager_init(&my_manager->base, &shared_class));
BAIL_ON_ERROR(status = lwmsg_error_map_errno(pthread_mutex_init(&my_manager->lock, NULL)));
*manager = LWMSG_SESSION_MANAGER(my_manager);
done:
return status;
error:
if (my_manager)
{
free(my_manager);
}
goto done;
}