diff --git a/common/os_calls.c b/common/os_calls.c index 2b4e0e9b71..c701bd221a 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -60,6 +60,7 @@ #include #include #include +#include #endif #include @@ -1474,6 +1475,17 @@ g_sleep(int msecs) #endif } +/*****************************************************************************/ +void +g_sleep_secs(int secs) +{ +#if defined(_WIN32) + Sleep(secs * 1000); +#else + sleep(secs); +#endif +} + /*****************************************************************************/ int g_sck_last_error_would_block(int sck) @@ -2105,6 +2117,17 @@ g_memcmp(const void *s1, const void *s2, int len) return memcmp(s1, s2, len); } +/*****************************************************************************/ +int +g_ftruncate(int fd, int length) +{ +#if defined(_WIN32) + return -1; +#else + return ftruncate(fd, length); +#endif +} + /*****************************************************************************/ /* returns -1 on error, else return handle or file descriptor */ int @@ -2246,6 +2269,31 @@ g_file_seek(int fd, int offset) #endif } +/*****************************************************************************/ +/* move file pointer relative to end of file, + * returns offset on success, -1 on failure */ +int +g_file_seek_rel_end(int fd, int offset) +{ +#if defined(_WIN32) + int rv; + + rv = (int)SetFilePointer((HANDLE)fd, offset, 0, FILE_END); + + if (rv == (int)INVALID_SET_FILE_POINTER) + { + return -1; + } + else + { + return rv; + } + +#else + return (int)lseek(fd, offset, SEEK_END); +#endif +} + /*****************************************************************************/ /* do a write lock on a file */ /* return boolean */ @@ -3044,6 +3092,35 @@ g_get_proc_address(long lib, const char *name) #endif } +/*****************************************************************************/ +/* does not work in win32 */ +void * +g_map_file_shared(int fd, int length) +{ + void *mapped; + + mapped = mmap(NULL, + length, + PROT_READ|PROT_WRITE, + MAP_SHARED, + fd, + 0); + + /* MAP_FAILED != NULL but mmap() will not return 0 unless MAP_FIXED is + * specified. */ + if (mapped == MAP_FAILED) + mapped = NULL; + return mapped; +} + +/*****************************************************************************/ +/* does not work in win32 */ +int +g_unmap_file_shared(void *mapped, int length) +{ + return munmap(mapped, length); +} + /*****************************************************************************/ /* does not work in win32 */ int diff --git a/common/os_calls.h b/common/os_calls.h index b52a84df80..40701e2de0 100644 --- a/common/os_calls.h +++ b/common/os_calls.h @@ -86,6 +86,7 @@ int g_sck_can_recv(int sck, int millis); int g_sck_select(int sck1, int sck2); void g_write_ip_address(int rcv_sck, char* ip_address, int bytes); void g_sleep(int msecs); +void g_sleep_secs(int secs); tintptr g_create_wait_obj(const char *name); tintptr g_create_wait_obj_from_socket(tintptr socket, int write); void g_delete_wait_obj_from_socket(tintptr wait_obj); @@ -98,6 +99,7 @@ int g_obj_wait(tintptr* read_objs, int rcount, tintptr* write_objs, void g_random(char* data, int len); int g_abs(int i); int g_memcmp(const void* s1, const void* s2, int len); +int g_ftruncate(int fd, int length); int g_file_open(const char* file_name); int g_file_open_ex(const char *file_name, int aread, int awrite, int acreate, int atrunc); @@ -105,6 +107,7 @@ int g_file_close(int fd); int g_file_read(int fd, char* ptr, int len); int g_file_write(int fd, const char *ptr, int len); int g_file_seek(int fd, int offset); +int g_file_seek_rel_end(int fd, int offset); int g_file_lock(int fd, int start, int len); int g_chmod_hex(const char* filename, int flags); int g_chown(const char* name, int uid, int gid); @@ -142,6 +145,8 @@ int g_strtrim(char* str, int trim_flags); long g_load_library(char* in); int g_free_library(long lib); void* g_get_proc_address(long lib, const char* name); +void* g_map_file_shared(int fd, int length); +int g_unmap_file_shared(void *mapped, int length); int g_system(char* aexec); char* g_get_strerror(void); int g_get_errno(void); diff --git a/common/thread_calls.c b/common/thread_calls.c index 4ce2589728..b55b694ff5 100644 --- a/common/thread_calls.c +++ b/common/thread_calls.c @@ -112,6 +112,34 @@ tc_mutex_create(void) #endif } +/*****************************************************************************/ +int +tc_shared_mutex_create(tbus mutex) +{ +#if defined(_WIN32) + /* Not implemented yet */ + return 1; +#else + pthread_mutexattr_t mutexattr; + int rc; + + rc = pthread_mutexattr_init(&mutexattr); + if (rc != 0) + { + return 1; + } + + rc = pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED); + if (rc != 0) + { + return 1; + } + + rc = pthread_mutex_init((pthread_mutex_t *)mutex, &mutexattr); + return rc; +#endif +} + /*****************************************************************************/ void tc_mutex_delete(tbus mutex) @@ -140,6 +168,22 @@ tc_mutex_lock(tbus mutex) #endif } +/*****************************************************************************/ +int +tc_mutex_timed_lock(tbus mutex, int timeout_ms) +{ +#if defined(_WIN32) + WaitForSingleObject((HANDLE)mutex, timeout_ms); + return 0; +#else + struct timespec timeout; + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_nsec = ((long)timeout_ms % 1000) * 1000; + + return pthread_mutex_timedlock((pthread_mutex_t *)mutex, &timeout); +#endif +} + /*****************************************************************************/ int tc_mutex_unlock(tbus mutex) diff --git a/common/thread_calls.h b/common/thread_calls.h index c8b6640a08..6f4921aedd 100644 --- a/common/thread_calls.h +++ b/common/thread_calls.h @@ -31,11 +31,15 @@ int tc_threadid_equal(tbus tid1, tbus tid2); tbus tc_mutex_create(void); +int +tc_shared_mutex_create(tbus mutex); void tc_mutex_delete(tbus mutex); int tc_mutex_lock(tbus mutex); int +tc_mutex_timed_lock(tbus mutex, int timeout_ms); +int tc_mutex_unlock(tbus mutex); tbus tc_sem_create(int init_count); diff --git a/sesman/Makefile.am b/sesman/Makefile.am index 50425e6365..49afe911eb 100644 --- a/sesman/Makefile.am +++ b/sesman/Makefile.am @@ -59,6 +59,8 @@ xrdp_sesman_SOURCES = \ scp_v1_mng.h \ sesman.c \ sesman.h \ + sesshm.c \ + sesshm.h \ session.c \ session.h \ sig.c \ diff --git a/sesman/sesman.c b/sesman/sesman.c index 73c7d0a29b..be00ad962d 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -29,6 +29,7 @@ #endif #include "sesman.h" +#include "sesshm.h" int g_sck; int g_pid; @@ -432,6 +433,12 @@ main(int argc, char **argv) g_file_close(fd); } + if (session_init_shared()) + log_message(LOG_LEVEL_ERROR, + "error opening session shm file[%s]: %s", + SESMAN_SHAREDMEM_FILENAME, g_get_strerror()); + + /* start program main loop */ log_message(LOG_LEVEL_INFO, "starting xrdp-sesman with pid %d", g_pid); @@ -460,6 +467,7 @@ main(int argc, char **argv) { g_file_delete(pid_file); } + session_close_shared(); g_delete_wait_obj(g_term_event); diff --git a/sesman/sesshm.c b/sesman/sesshm.c new file mode 100644 index 0000000000..256d1c2068 --- /dev/null +++ b/sesman/sesshm.c @@ -0,0 +1,640 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2015 + * Copyright (C) Ben Cohen 2017 + * + * BSD process grouping by: + * Copyright (c) 1995 Tatu Ylonen , 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.c + * @brief Session discovery and shared memory code + * @author Jay Sorg, Simone Fedele, Ben Cohen + * + */ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#ifdef HAVE_SYS_PRCTL_H +#include +#endif + +#include "sesshm.h" +#include "sesman.h" +#include "libscp_types.h" +#include "xauth.h" +#include "xrdp_sockets.h" +#include "thread_calls.h" + +#ifdef DEBUG_SESSION_LOCK +#define sesshm_try_lock() sesshm_try_lock_dbg(__func__, __LINE__) +#endif + +#ifndef PR_SET_NO_NEW_PRIVS +#define PR_SET_NO_NEW_PRIVS 38 +#endif + +#ifndef DONT_USE_SHM +static int g_sesshm_thread_going = 1; +static struct session_shared_data *g_shm_mapping; +#endif + +extern struct session_chain *g_sessions; +extern int g_session_count; +extern int g_pid; /* in sesman.c */ + + +/******************************************************************************/ +/** + * + * @brief obtain lock on the mutex in the shared mapping + * @return 0 on success; non-zero otherwise + * + */ +#ifdef DEBUG_SESSION_LOCK +int +sesshm_lock_dbg(const char *caller_func, int caller_line) +#else +int +sesshm_lock() +#endif +{ +#ifdef DONT_USE_SHM + return 0; +#else + int rc; + +#ifdef DEBUG_SESSION_LOCK + log_message(LOG_LEVEL_DEBUG, + "pid %d tid %d: %s() called from %s at line %d", + g_getpid(), (int)tc_get_threadid(), __func__, + caller_func, caller_line); +#endif + + rc = tc_mutex_lock((tbus)&g_shm_mapping->mutex); + if (rc != 0) + { + log_message(LOG_LEVEL_ERROR, + "tc_mutex_lock() failed (%d)", g_get_errno()); + } + return rc; +#endif +} + + +/******************************************************************************/ +/** + * + * @brief try for up to one second to obtain lock on the mutex in the shared mapping + * @return 0 on success; non-zero otherwise + * + */ +#ifndef DONT_USE_SHM +#ifdef DEBUG_SESSION_LOCK +static int +sesshm_try_lock_dbg(const char *caller_func, int caller_line) +#else +static int +sesshm_try_lock() +#endif +{ + int rc; + +#ifdef DEBUG_SESSION_LOCK + log_message(LOG_LEVEL_DEBUG, + "pid %d tid %d: %s() called from %s at line %d", + g_getpid(), (int)tc_get_threadid(), __func__, + caller_func, caller_line); +#endif + + rc = tc_mutex_timed_lock((tbus)&g_shm_mapping->mutex, 1 * 1000); + if (rc != 0) + { + log_message(LOG_LEVEL_ERROR, + "tc_mutex_timed_lock() failed (%d)", g_get_errno()); + } + return rc; +} +#endif + + +/******************************************************************************/ +/** + * + * @brief release lock on the mutex in the shared mapping + * @return 0 on success; non-zero otherwise + * + */ +#ifdef DEBUG_SESSION_LOCK +int +sesshm_unlock_dbg(const char *caller_func, int caller_line) +#else +int +sesshm_unlock() +#endif +{ +#ifdef DONT_USE_SHM + return 0; +#else + int rc; + +#ifdef DEBUG_SESSION_LOCK + log_message(LOG_LEVEL_DEBUG, + "pid %d tid %d: %s() called from %s at line %d", + g_getpid(), (int)tc_get_threadid(), __func__, + caller_func, caller_line); +#endif + + rc = tc_mutex_unlock((tbus) &g_shm_mapping->mutex); + if (rc != 0) + { + log_message(LOG_LEVEL_ERROR, + "tc_mutex_unlock() failed (%d)", g_get_errno()); + } + return rc; +#endif +} + + +/******************************************************************************/ +/** + * + * @brief thread to poll the shared memory for various changes + * @return NULL + * + */ +#ifndef DONT_USE_SHM +static void * +sesshm_thread(void *arg) +{ + int pid; + + pid = g_getpid(); + while (g_sesshm_thread_going) + { + g_sleep_secs(SESMAN_SHAREDMEM_HEARTBEAT_INTERVAL); + + if (g_pid == pid) + { + int current_time; + struct session_chain *tmp; + struct session_chain *prev; + + /* Daemon process + * Check that the file hasn't been hijacked by a new daemon + * process and that the heartbeat hasn't timed out for the + * sessions. */ + sesshm_lock(); + if (g_pid != g_shm_mapping->daemon_pid) + { + log_message(LOG_LEVEL_ERROR, + "new daemon pid %d entered in shm! quitting (%d)", + g_shm_mapping->daemon_pid, g_pid); + exit(1); + } + + current_time = g_time1(); + tmp = g_sessions; + prev = 0; + while (tmp != 0) + { + if (tmp->item == 0) + { + log_message(LOG_LEVEL_ERROR, "found null session " + "descriptor!"); + } + else if (current_time - tmp->item->shm_heartbeat_time + > SESMAN_SHAREDMEM_HEARTBEAT_TIMEOUT) + { + /* deleting the session */ + log_message(LOG_LEVEL_INFO, "++ terminated session (heartbeat timed out): username %s, display :%d.0, session_pid %d, ip %s", tmp->item->name, tmp->item->display, tmp->item->pid, tmp->item->client_ip); + + tmp->item->shm_is_allocated = 0; + + if (prev == 0) + { + /* prev does no exist, so it's the first element - so we set + g_sessions */ + g_sessions = tmp->next; + } + else + { + prev->next = tmp->next; + } + + g_free(tmp); + g_session_count--; + } + + /* go on */ + prev = tmp; + tmp = tmp->next; + } + sesshm_unlock(); + } + else + { + /* Session process */ + sesshm_lock(); + /* Check that this process hadn't been timed out or otherwise + * told to exit */ + int okay = 0; + int i; + for (i = 0; i < g_shm_mapping->max_sessions; i ++) + { + if (g_shm_mapping->sessions[i].shm_is_allocated + && pid == g_shm_mapping->sessions[i].pid) + { + if (g_shm_mapping->sessions[i].shm_is_allocated) + { + okay = 1; + + /* Update the heartbeat time */ + g_shm_mapping->sessions[i].shm_heartbeat_time = g_time1(); + } + } + } + if (!okay) + { + log_message(LOG_LEVEL_INFO, "++ killed session (deallocated in shm): session_pid %d", pid); + sesshm_unlock(); + + // TODO XXX Kill the child X server and/or window manager + + exit(1); + } + sesshm_unlock(); + } + } + + return NULL; +} +#endif + + +/******************************************************************************/ +/** + * + * @brief create a new session_shared_data shared memory file + * @return 0 on success, nonzero otherwise + * + */ +#ifndef DONT_USE_SHM +static int +sesshm_create_new_shm() +{ + int rc; + int i; + int fd; + + log_message(LOG_LEVEL_INFO, "Creating shm file %s", + SESMAN_SHAREDMEM_FILENAME); + + g_file_delete(SESMAN_SHAREDMEM_FILENAME); + fd = g_file_open_ex(SESMAN_SHAREDMEM_FILENAME, 1, 1, 1, 0); + if (fd == -1) + { + log_message(LOG_LEVEL_ERROR, "open() failed (%d)", g_get_errno()); + return 1; + } + rc = g_ftruncate(fd, SESMAN_SHAREDMEM_LENGTH); + if (rc != 0) + { + log_message(LOG_LEVEL_ERROR, "truncate() failed (%d)", g_get_errno()); + return 2; + } + + /* Map it into memory */ + g_shm_mapping = ((struct session_shared_data *) + g_map_file_shared(fd, SESMAN_SHAREDMEM_LENGTH)); + g_file_close(fd); + if (g_shm_mapping == NULL) + { + log_message(LOG_LEVEL_ERROR, "mmap() failed (%d)", g_get_errno()); + return 3; + } + + /* Initalise mutex */ + rc = tc_shared_mutex_create((tbus) &g_shm_mapping->mutex); + if (rc != 0) + { + log_message(LOG_LEVEL_ERROR, "tc_shared_mutex_create() failed (%d)", + g_get_errno()); + return 6; + } + + /* Initialise data */ + sesshm_lock(); + g_strncpy(g_shm_mapping->tag, SESMAN_SHAREDMEM_TAG, + sizeof(g_shm_mapping->tag)); + g_shm_mapping->data_format_version = SESMAN_SHAREDMEM_FORMAT_VERSION; + g_shm_mapping->max_sessions = SESMAN_SHAREDMEM_MAX_SESSIONS; + g_shm_mapping->daemon_pid = g_pid; + for (i = 0; i < g_shm_mapping->max_sessions; i ++) + { + g_shm_mapping->sessions[i].shm_is_allocated = 0; + } + sesshm_unlock(); + + return 0; +} +#endif + + +/******************************************************************************/ +/** + * + * @brief try to open an existing session_shared_data shared memory file + * @return 0 on success, nonzero otherwise + * + */ +#ifndef DONT_USE_SHM +static int +sesshm_try_open_existing_shm() +{ + int rc; + int fd; + int i; + off_t end; + char tag[SESMAN_SHAREDMEM_TAG_LENGTH]; + + log_message(LOG_LEVEL_INFO, "Looking for existing shm file %s", + SESMAN_SHAREDMEM_FILENAME); + + /* Does the shared file already exist? */ + fd = g_file_open_ex(SESMAN_SHAREDMEM_FILENAME, 1, 1, 0, 0); + if (fd == -1) + { + log_message(LOG_LEVEL_ERROR, "open() failed (%d)", g_get_errno()); + return 1; + } + + rc = g_file_read(fd, tag, SESMAN_SHAREDMEM_TAG_LENGTH); + if (rc != SESMAN_SHAREDMEM_TAG_LENGTH) + { + log_message(LOG_LEVEL_ERROR, "read() failed (%d)", g_get_errno()); + return 2; + } + if (g_strncmp(tag, SESMAN_SHAREDMEM_TAG, SESMAN_SHAREDMEM_TAG_LENGTH)) + { + log_message(LOG_LEVEL_ERROR, "tag is wrong file shm file %s", + SESMAN_SHAREDMEM_FILENAME); + // XXX Should we exit(1) here instead? + return 3; + } + + /* Is it the right size? */ + end = g_file_seek_rel_end(fd, 0); + if (end != SESMAN_SHAREDMEM_LENGTH) + { + log_message(LOG_LEVEL_ERROR, "shm file %s has wrong length", + SESMAN_SHAREDMEM_FILENAME); + return 4; + } + rc = g_file_seek(fd, 0); + if (rc != 0) + { + log_message(LOG_LEVEL_ERROR, "seek() failed (%d)", g_get_errno()); + return 5; + } + + /* Map it into memory */ + g_shm_mapping = ((struct session_shared_data *) + g_map_file_shared(fd, SESMAN_SHAREDMEM_LENGTH)); + g_file_close(fd); + if (g_shm_mapping == NULL) + { + log_message(LOG_LEVEL_ERROR, "mmap() failed (%d)", g_get_errno()); + return 6; + } + + /* Check that it isn't already locked. Otherwise if it was locked by a + * process that crashed then we will wait forever. */ + rc = sesshm_try_lock(); + if (rc) + { + log_message(LOG_LEVEL_ERROR, "sesshm_try_lock() failed (%d)", + g_get_errno()); + // XXX Should we exit(1) here instead? + return 7; + } + + if (g_shm_mapping->data_format_version != SESMAN_SHAREDMEM_FORMAT_VERSION) + { + sesshm_unlock(); + log_message(LOG_LEVEL_ERROR, "wrong data version (%d)", g_get_errno()); + // XXX Should we exit(1) here instead? + return 8; + } + + g_shm_mapping->daemon_pid = g_pid; + for (i = 0; i < g_shm_mapping->max_sessions; i ++) + { + if (g_shm_mapping->sessions[i].shm_is_allocated) + { + struct session_chain *temp; + + temp = + (struct session_chain *)g_malloc(sizeof(struct session_chain), + 0); + temp->item = &g_shm_mapping->sessions[i]; + temp->next = g_sessions; + g_sessions = temp; + g_session_count ++; + } + } + sesshm_unlock(); + log_message(LOG_LEVEL_INFO, "Existing shm file ok: found %d sessions", + g_session_count); + + return 0; +} +#endif + + +/******************************************************************************/ +/** + * + * @brief initialises the session shared data + * @return 0 on success, nonzero otherwise + * + */ +int +session_init_shared() +{ +#ifdef DONT_USE_SHM + return 0; +#else + int rc; + + /* Look for an existing shared file */ + rc = sesshm_try_open_existing_shm(); + + /* If it's not okay then create it */ + if (rc != 0) + { + rc = sesshm_create_new_shm(); + } + + /* Start polling thread */ + if (rc == 0) + { + tc_thread_create(sesshm_thread, 0); + } + + return rc; +#endif +} + + +/******************************************************************************/ +/** + * + * @brief releases the session shared data + * @return 0 on success, nonzero otherwise + * + */ +int +session_close_shared() +{ +#ifdef DONT_USE_SHM + return 0; +#else + int rc; + + g_sesshm_thread_going = 0; + + sesshm_lock(); + if (g_shm_mapping->daemon_pid == g_getpid()) + { + g_shm_mapping->daemon_pid = -1; + } + sesshm_unlock(); + + rc = g_unmap_file_shared(g_shm_mapping, SESMAN_SHAREDMEM_LENGTH); + if (rc != 0) + { + return 1; + } + + return 0; +#endif +} + + +/******************************************************************************/ +/** + * + * @brief allocate a session and mark it as used + * @return the session_item allocated + * + */ +struct session_item * +alloc_session_item() +{ +#ifdef DONT_USE_SHM + return (struct session_item *)g_malloc(sizeof(struct session_item), 0); +#else + int i; + sesshm_lock(); + for (i = 0; i < g_shm_mapping->max_sessions; i ++) + { + if (!g_shm_mapping->sessions[i].shm_is_allocated) + { + g_memset(&g_shm_mapping->sessions[i], + 0, + sizeof(g_shm_mapping->sessions[i])); + g_shm_mapping->sessions[i].shm_is_allocated = 1; + sesshm_unlock(); + return &g_shm_mapping->sessions[i]; + } + } + sesshm_unlock(); + return 0; +#endif +} + + +/******************************************************************************/ +/** + * + * @brief validate whether a session_item pointer is valid + * @param item a pointer to a session_item + * @return 1 if valid or 0 if not + * + */ +#ifndef DONT_USE_SHM +static int +validate_session_item_ptr(struct session_item *item) +{ + char *start; + int diff; + int size; + int valid; + + start = (char *) &g_shm_mapping->sessions[0]; + diff = (char *) item - start; + size = sizeof(g_shm_mapping->sessions[0]); + valid = (diff % size == 0 + && diff >= 0 + && (diff / size < g_shm_mapping->max_sessions)); + + if (!valid) + { + log_message(LOG_LEVEL_ALWAYS, + "validate_session_item_ptr: bad pointer %p", + item); + } + + return valid; +} +#endif + + +/******************************************************************************/ +/** + * + * @brief deallocate a session and mark it as unused + * @param item the session_item to deallocate + * + */ +void +free_session_item(struct session_item *item) +{ +#ifdef DONT_USE_SHM + g_free(item); +#else + /* This assumes that the caller has called sesshm_lock() */ + if (!validate_session_item_ptr(item)) + { + log_message(LOG_LEVEL_ALWAYS, "free_session_item: bad pointer"); + } + else if (!item->shm_is_allocated) + { + log_message(LOG_LEVEL_ALWAYS, "free_session_item: session not active %p", item); + } + else + { + item->shm_is_allocated = 0; + } +#endif +} diff --git a/sesman/sesshm.h b/sesman/sesshm.h new file mode 100644 index 0000000000..806c7992c7 --- /dev/null +++ b/sesman/sesshm.h @@ -0,0 +1,126 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2013 + * Copyright (C) Ben Cohen 2017 + * + * 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.h + * @brief Session discovery and shared memory definitions + * @author Jay Sorg, Simone Fedele, Ben Cohen + * + */ + + +#ifndef SESSHM_H +#define SESSHM_H + +#include "session.h" + +//#define DONT_USE_SHM +//#define DEBUG_SESSION_LOCK + +// XXX No reason why this needs to be a constant +#define SESMAN_SHAREDMEM_MAX_SESSIONS 64 +#define SESMAN_SHAREDMEM_FORMAT_VERSION 1 +#define SESMAN_SHAREDMEM_HEARTBEAT_INTERVAL 15 /* seconds */ +#define SESMAN_SHAREDMEM_HEARTBEAT_TIMEOUT (60 * 3) /* seconds */ +#define SESMAN_SHAREDMEM_FILENAME "/var/run/xrdp-sesman.shm" +#define SESMAN_SHAREDMEM_TAG "XRDP-SESMAN-SHM" +#define SESMAN_SHAREDMEM_TAG_LENGTH 16 +#define SESMAN_SHAREDMEM_LENGTH sizeof(struct session_shared_data) + +struct session_shared_data +{ + char tag[SESMAN_SHAREDMEM_TAG_LENGTH]; + int data_format_version; + pthread_mutex_t mutex; + int daemon_pid; + int max_sessions; + struct session_item sessions[SESMAN_SHAREDMEM_MAX_SESSIONS]; +}; + +/** + * + * @brief initialises the session shared data + * @return 0 on success, nonzero otherwise + * + */ +int +session_init_shared(void); + +/** + * + * @brief releases the session shared data + * @return 0 on success, nonzero otherwise + * + */ +int +session_close_shared(); + +/** + * + * @brief allocate a session and mark it as used + * @return the session_item allocated + * + */ +struct session_item * +alloc_session_item(); + +/** + * + * @brief deallocate a session and mark it as unused + * @param item the session_item to deallocate + * + */ +void +free_session_item(struct session_item *item); + +/** + * + * @brief obtain lock on the mutex in the shared mapping + * @return 0 on success; non-zero otherwise + * + */ +#ifdef DEBUG_SESSION_LOCK +int +sesshm_lock_dbg(const char *caller_func, int caller_line); +#else +int +sesshm_lock(); +#endif + +/** + * + * @brief release lock on the mutex in the shared mapping + * @return 0 on success; non-zero otherwise + * + */ +#ifdef DEBUG_SESSION_LOCK +int +sesshm_unlock_dbg(const char *caller_func, int caller_line); +#else +int +sesshm_unlock(); +#endif + +#ifdef DEBUG_SESSION_LOCK +#define sesshm_lock() sesshm_lock_dbg(__func__, __LINE__) +#define sesshm_unlock() sesshm_unlock_dbg(__func__, __LINE__) +#endif + +#endif diff --git a/sesman/session.c b/sesman/session.c index 9fab039211..ded06d9acf 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -29,6 +29,9 @@ * */ +//#define DONT_USE_SHM +//#define DEBUG_SESSION_LOCK + #if defined(HAVE_CONFIG_H) #include "config_ac.h" #endif @@ -37,10 +40,12 @@ #include #endif +#include "sesshm.h" #include "sesman.h" #include "libscp_types.h" #include "xauth.h" #include "xrdp_sockets.h" +#include "thread_calls.h" #ifndef PR_SET_NO_NEW_PRIVS #define PR_SET_NO_NEW_PRIVS 38 @@ -54,6 +59,8 @@ struct session_chain *g_sessions; int g_session_count; extern tbus g_term_event; /* in sesman.c */ +extern int g_pid; /* in sesman.c */ + /** * Creates a string consisting of all parameters that is hosted in the param list @@ -98,8 +105,6 @@ session_get_bydata(const char *name, int width, int height, int bpp, int type, struct session_chain *tmp; enum SESMAN_CFG_SESS_POLICY policy = g_cfg->sess.policy; - tmp = g_sessions; - /* convert from SCP_SESSION_TYPE namespace to SESMAN_SESSION_TYPE namespace */ switch (type) { @@ -125,6 +130,9 @@ session_get_bydata(const char *name, int width, int height, int bpp, int type, policy, name, width, height, bpp, type, client_ip); #endif + sesshm_lock(); + tmp = g_sessions; + while (tmp != 0) { #if 0 @@ -147,11 +155,13 @@ session_get_bydata(const char *name, int width, int height, int bpp, int type, tmp->item->bpp == bpp && tmp->item->type == type) { + sesshm_unlock(); return tmp->item; } tmp = tmp->next; } + sesshm_unlock(); return 0; } @@ -278,6 +288,7 @@ session_is_display_in_chain(int display) struct session_chain *chain; struct session_item *item; + sesshm_lock(); chain = g_sessions; while (chain != 0) @@ -286,11 +297,13 @@ session_is_display_in_chain(int display) if (item->display == display) { + sesshm_unlock(); return 1; } chain = chain->next; } + sesshm_unlock(); return 0; } @@ -360,6 +373,8 @@ session_start_chansrv(char *username, int display) chansrv_pid = g_fork(); if (chansrv_pid == 0) { + session_close_shared(); + chansrv_params = list_create(); chansrv_params->auto_free = 1; @@ -443,7 +458,7 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, return 0; } - temp->item = (struct session_item *)g_malloc(sizeof(struct session_item), 0); + temp->item = alloc_session_item(); if (temp->item == 0) { @@ -457,7 +472,9 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, if (display == 0) { - g_free(temp->item); + sesshm_lock(); + free_session_item(temp->item); + sesshm_unlock(); g_free(temp); return 0; } @@ -532,6 +549,7 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, } else if (window_manager_pid == 0) { + session_close_shared(); wait_for_xserver(display); env_set_user(s->username, 0, @@ -621,6 +639,7 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, } else if (display_pid == 0) /* child */ { + session_close_shared(); if (type == SESMAN_SESSION_TYPE_XVNC) { env_set_user(s->username, @@ -819,6 +838,10 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, g_sigterm(display_pid); g_sigterm(chansrv_pid); cleanup_sockets(display); + sesshm_lock(); + free_session_item(temp->item); + sesshm_unlock(); + session_close_shared(); g_deinit(); g_exit(0); } @@ -849,14 +872,22 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c, temp->item->type = type; temp->item->status = SESMAN_SESSION_STATUS_ACTIVE; +#ifndef DONT_USE_SHM + temp->item->shm_heartbeat_time = g_time1(); +#endif + + sesshm_lock(); temp->next = g_sessions; g_sessions = temp; g_session_count++; + sesshm_unlock(); return display; } - g_free(temp->item); + sesshm_lock(); + free_session_item(temp->item); + sesshm_unlock(); g_free(temp); return display; } @@ -919,6 +950,7 @@ session_kill(int pid) struct session_chain *tmp; struct session_chain *prev; + sesshm_lock(); tmp = g_sessions; prev = 0; @@ -940,14 +972,15 @@ session_kill(int pid) prev->next = tmp->next; } + sesshm_unlock(); return SESMAN_SESSION_KILL_NULLITEM; } if (tmp->item->pid == pid) { /* deleting the session */ - log_message(LOG_LEVEL_INFO, "++ terminated session: username %s, display :%d.0, session_pid %d, ip %s", tmp->item->name, tmp->item->display, tmp->item->pid, tmp->item->client_ip); - g_free(tmp->item); + log_message(LOG_LEVEL_INFO, "++ terminated session (received SIGCHLD): username %s, display :%d.0, session_pid %d, ip %s", tmp->item->name, tmp->item->display, tmp->item->pid, tmp->item->client_ip); + free_session_item(tmp->item); if (prev == 0) { @@ -962,6 +995,7 @@ session_kill(int pid) g_free(tmp); g_session_count--; + sesshm_unlock(); return SESMAN_SESSION_KILL_OK; } @@ -969,6 +1003,7 @@ session_kill(int pid) prev = tmp; tmp = tmp->next; } + sesshm_unlock(); return SESMAN_SESSION_KILL_NOTFOUND; } @@ -979,6 +1014,7 @@ session_sigkill_all(void) { struct session_chain *tmp; + sesshm_lock(); tmp = g_sessions; while (tmp != 0) @@ -996,6 +1032,7 @@ session_sigkill_all(void) /* go on */ tmp = tmp->next; } + sesshm_unlock(); } /******************************************************************************/ @@ -1013,12 +1050,14 @@ session_get_bypid(int pid) return 0; } + sesshm_lock(); tmp = g_sessions; while (tmp != 0) { if (tmp->item == 0) { + sesshm_unlock(); log_message(LOG_LEVEL_ERROR, "session descriptor for " "pid %d is null!", pid); g_free(dummy); @@ -1028,12 +1067,14 @@ session_get_bypid(int pid) if (tmp->item->pid == pid) { g_memcpy(dummy, tmp->item, sizeof(struct session_item)); + sesshm_unlock(); return dummy; } /* go on */ tmp = tmp->next; } + sesshm_unlock(); g_free(dummy); return 0; @@ -1050,6 +1091,7 @@ session_get_byuser(const char *user, int *cnt, unsigned char flags) count = 0; + sesshm_lock(); tmp = g_sessions; while (tmp != 0) @@ -1071,6 +1113,7 @@ session_get_byuser(const char *user, int *cnt, unsigned char flags) /* go on */ tmp = tmp->next; } + sesshm_unlock(); if (count == 0) { @@ -1087,6 +1130,7 @@ session_get_byuser(const char *user, int *cnt, unsigned char flags) return 0; } + sesshm_lock(); tmp = g_sessions; index = 0; @@ -1134,6 +1178,7 @@ session_get_byuser(const char *user, int *cnt, unsigned char flags) /* go on */ tmp = tmp->next; } + sesshm_unlock(); (*cnt) = count; return sess; diff --git a/sesman/session.h b/sesman/session.h index 42d747cb8e..e26de5f0c7 100644 --- a/sesman/session.h +++ b/sesman/session.h @@ -77,6 +77,12 @@ struct session_item struct session_date idle_time; char client_ip[256]; tui8 guid[16]; + +#ifndef DONT_USE_SHM + /* used for shared memory */ + int shm_is_allocated; + int shm_heartbeat_time; +#endif }; struct session_chain