3e38d9be80
The start session method is reworked to pass a response back to the client. If the method was successful, a session list entry is created. This is different to the provious behaviour, where we created the session list entry unconditionally. This new arrangement means that we need a different way to avoid a race condition where two users may try to create a session at the same time, and end up with the same display. We do this by keeping track of newly allocated displays as part of the SCP connections. When we allocate a display, the SCP connection displays are also taken into account.
432 lines
11 KiB
C
432 lines
11 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Jay Sorg 2004-2015
|
|
*
|
|
* BSD process grouping by:
|
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland.
|
|
* Copyright (c) 2000-2001 Markus Friedl.
|
|
* Copyright (c) 2011-2015 Koichiro Iwao, Kyushu Institute of Technology.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @file session_list.c
|
|
* @brief Session list management code
|
|
* @author Jay Sorg, Simone Fedele
|
|
*
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config_ac.h"
|
|
#endif
|
|
|
|
#include "arch.h"
|
|
#include "session_list.h"
|
|
#include "trans.h"
|
|
|
|
#include "sesman_config.h"
|
|
#include "list.h"
|
|
#include "log.h"
|
|
#include "os_calls.h"
|
|
#include "sesman.h"
|
|
#include "set_int.h"
|
|
#include "string_calls.h"
|
|
#include "xrdp_sockets.h"
|
|
|
|
static struct list *g_session_list = NULL;
|
|
|
|
#define SESSION_IN_USE(si) \
|
|
((si) != NULL && \
|
|
(si)->sesexec_trans != NULL && \
|
|
(si)->sesexec_trans->status == TRANS_STATUS_UP)
|
|
|
|
/******************************************************************************/
|
|
int
|
|
session_list_init(void)
|
|
{
|
|
int rv = 0;
|
|
if (g_session_list == NULL)
|
|
{
|
|
g_session_list = list_create_sized(g_cfg->sess.max_sessions);
|
|
if (g_session_list == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't allocate session list");
|
|
rv = 1;
|
|
}
|
|
else
|
|
{
|
|
g_session_list->auto_free = 0;
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Frees resources allocated to a session_item
|
|
*
|
|
* @param si Session item
|
|
*
|
|
* @note Any pointer to this item on g_session_list will be invalid
|
|
* after this call.
|
|
*/
|
|
static void
|
|
free_session(struct session_item *si)
|
|
{
|
|
if (si != NULL)
|
|
{
|
|
if (si->sesexec_trans != NULL)
|
|
{
|
|
trans_delete(si->sesexec_trans);
|
|
}
|
|
g_free(si);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
session_list_cleanup(void)
|
|
{
|
|
if (g_session_list != NULL)
|
|
{
|
|
int i;
|
|
for (i = 0 ; i < g_session_list->count ; ++i)
|
|
{
|
|
struct session_item *si;
|
|
si = (struct session_item *)list_get_item(g_session_list, i);
|
|
free_session(si);
|
|
}
|
|
list_delete(g_session_list);
|
|
g_session_list = NULL;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
unsigned int
|
|
session_list_get_count(void)
|
|
{
|
|
return (g_session_list == NULL) ? 0 : g_session_list->count;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
unsigned int
|
|
session_list_get_count_by_state(enum session_state state)
|
|
{
|
|
unsigned int result = 0;
|
|
int i;
|
|
for (i = 0 ; i < g_session_list->count ; ++i)
|
|
{
|
|
struct session_item *si;
|
|
si = (struct session_item *)list_get_item(g_session_list, i);
|
|
if (si->state == state)
|
|
{
|
|
++result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
struct session_item *
|
|
session_list_new(void)
|
|
{
|
|
struct session_item *result = g_new0(struct session_item, 1);
|
|
if (result != NULL)
|
|
{
|
|
result->state = E_SESSION_STARTING;
|
|
if (!list_add_item(g_session_list, (tintptr)result))
|
|
{
|
|
g_free(result);
|
|
result = NULL;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
session_list_get_session_displays(struct set_int *alloc_displays)
|
|
{
|
|
int count = (g_session_list == NULL) ? 0 : g_session_list->count;
|
|
|
|
int i = 0;
|
|
for (i = 0 ; i < count ; ++i)
|
|
{
|
|
struct session_item *si;
|
|
si = (struct session_item *)list_get_item(g_session_list, i);
|
|
|
|
if (SESSION_IN_USE(si))
|
|
{
|
|
set_int_add(alloc_displays, si->display);
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
struct session_item *
|
|
session_list_get_bydata(uid_t uid,
|
|
enum scp_session_type type,
|
|
unsigned short width,
|
|
unsigned short height,
|
|
unsigned char bpp,
|
|
const char *ip_addr)
|
|
{
|
|
char policy_str[64];
|
|
int policy = g_cfg->sess.policy;
|
|
int i;
|
|
|
|
if (ip_addr == NULL)
|
|
{
|
|
ip_addr = "";
|
|
}
|
|
|
|
if ((policy & SESMAN_CFG_SESS_POLICY_DEFAULT) != 0)
|
|
{
|
|
/* Before xrdp v0.9.14, the default
|
|
* session policy varied by type. If this is needed again
|
|
* in the future, here is the place to add it */
|
|
policy = SESMAN_CFG_SESS_POLICY_U | SESMAN_CFG_SESS_POLICY_B;
|
|
}
|
|
|
|
config_output_policy_string(policy, policy_str, sizeof(policy_str));
|
|
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: search policy=%s type=%s U=%d B=%d D=(%dx%d) I=%s",
|
|
__func__,
|
|
policy_str, SCP_SESSION_TYPE_TO_STR(type),
|
|
uid, bpp, width, height,
|
|
ip_addr);
|
|
|
|
/* 'Separate' policy never matches */
|
|
if (policy & SESMAN_CFG_SESS_POLICY_SEPARATE)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "%s: No matches possible", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0 ; i < g_session_list->count ; ++i)
|
|
{
|
|
struct session_item *si;
|
|
si = (struct session_item *)list_get_item(g_session_list, i);
|
|
if (!SESSION_IN_USE(si))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: try %p type=%s U=%d B=%d D=(%dx%d) I=%s",
|
|
__func__,
|
|
si,
|
|
SCP_SESSION_TYPE_TO_STR(si->type),
|
|
si->uid, si->bpp,
|
|
si->start_width, si->start_height,
|
|
si->start_ip_addr);
|
|
|
|
if (si->type != type)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "%s: Type doesn't match", __func__);
|
|
continue;
|
|
}
|
|
|
|
if ((policy & SESMAN_CFG_SESS_POLICY_U) && uid != si->uid)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: UID doesn't match for 'U' policy", __func__);
|
|
continue;
|
|
}
|
|
|
|
if ((policy & SESMAN_CFG_SESS_POLICY_B) && si->bpp != bpp)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: bpp doesn't match for 'B' policy", __func__);
|
|
continue;
|
|
}
|
|
|
|
if ((policy & SESMAN_CFG_SESS_POLICY_D) &&
|
|
(si->start_width != width ||
|
|
si->start_height != height))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: Dimensions don't match for 'D' policy", __func__);
|
|
continue;
|
|
}
|
|
|
|
if ((policy & SESMAN_CFG_SESS_POLICY_I) &&
|
|
g_strcmp(si->start_ip_addr, ip_addr) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: IPs don't match for 'I' policy", __func__);
|
|
continue;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"%s: Got match, display=%d", __func__, si->display);
|
|
return si;
|
|
}
|
|
|
|
LOG(LOG_LEVEL_DEBUG, "%s: No matches found", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
struct scp_session_info *
|
|
session_list_get_byuid(const uid_t *uid, unsigned int *cnt, unsigned int flags)
|
|
{
|
|
int i;
|
|
struct scp_session_info *sess;
|
|
int count;
|
|
int index;
|
|
|
|
count = 0;
|
|
|
|
if (uid != NULL)
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "searching for session by UID: %d", (int)*uid);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "searching for all sessions");
|
|
}
|
|
|
|
for (i = 0 ; i < g_session_list->count ; ++i)
|
|
{
|
|
const struct session_item *si;
|
|
si = (const struct session_item *)list_get_item(g_session_list, i);
|
|
if (SESSION_IN_USE(si) && (uid == NULL || *uid == si->uid))
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
if (count == 0)
|
|
{
|
|
(*cnt) = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* malloc() an array of disconnected sessions */
|
|
sess = g_new0(struct scp_session_info, count);
|
|
|
|
if (sess == 0)
|
|
{
|
|
(*cnt) = 0;
|
|
return 0;
|
|
}
|
|
|
|
index = 0;
|
|
for (i = 0 ; i < g_session_list->count ; ++i)
|
|
{
|
|
const struct session_item *si;
|
|
si = (const struct session_item *)list_get_item(g_session_list, i);
|
|
|
|
if (SESSION_IN_USE(si) && (uid == NULL || *uid == si->uid))
|
|
{
|
|
(sess[index]).sid = si->sesexec_pid;
|
|
(sess[index]).display = si->display;
|
|
(sess[index]).type = si->type;
|
|
(sess[index]).height = si->start_height;
|
|
(sess[index]).width = si->start_width;
|
|
(sess[index]).bpp = si->bpp;
|
|
(sess[index]).start_time = si->start_time;
|
|
(sess[index]).uid = si->uid;
|
|
(sess[index]).start_ip_addr = g_strdup(si->start_ip_addr);
|
|
|
|
/* Check for string allocation failures */
|
|
if ((sess[index]).start_ip_addr == NULL)
|
|
{
|
|
free_session_info_list(sess, *cnt);
|
|
(*cnt) = 0;
|
|
return 0;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
|
|
(*cnt) = count;
|
|
return sess;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
free_session_info_list(struct scp_session_info *sesslist, unsigned int cnt)
|
|
{
|
|
if (sesslist != NULL && cnt > 0)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0 ; i < cnt ; ++i)
|
|
{
|
|
g_free(sesslist[i].start_ip_addr);
|
|
}
|
|
}
|
|
|
|
g_free(sesslist);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
session_list_get_wait_objs(tbus robjs[], int *robjs_count)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i < g_session_list->count; ++i)
|
|
{
|
|
const struct session_item *si;
|
|
si = (const struct session_item *)list_get_item(g_session_list, i);
|
|
if (SESSION_IN_USE(si))
|
|
{
|
|
robjs[(*robjs_count)++] = si->sesexec_trans->sck;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
session_list_check_wait_objs(void)
|
|
{
|
|
int i = 0;
|
|
|
|
while (i < g_session_list->count)
|
|
{
|
|
struct session_item *si;
|
|
si = (struct session_item *)list_get_item(g_session_list, i);
|
|
if (SESSION_IN_USE(si))
|
|
{
|
|
if (trans_check_wait_objs(si->sesexec_trans) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "sesman_check_wait_objs: "
|
|
"trans_check_wait_objs failed, removing trans");
|
|
si->sesexec_trans->status = TRANS_STATUS_DOWN;
|
|
}
|
|
}
|
|
|
|
if (SESSION_IN_USE(si))
|
|
{
|
|
++i;
|
|
}
|
|
else
|
|
{
|
|
free_session(si);
|
|
list_remove_item(g_session_list, i);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|