summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2019-03-02 19:09:07 -0600
committerTimothy Pearson <tpearson@raptorengineering.com>2019-03-02 19:18:32 -0600
commit58e06a0aa7db8e0762b31304ead0c3e0df5a7be4 (patch)
tree8f4dc9b6a69b5d34ad7e4bcb99e6aafeb052e6b7
parent4ff3578d7aa8116b19ac4d34c5459f61205e8bfa (diff)
downloadxrdp-proprietary-58e06a0aa7db8e0762b31304ead0c3e0df5a7be4.tar.gz
xrdp-proprietary-58e06a0aa7db8e0762b31304ead0c3e0df5a7be4.zip
Add preliminary Raptor session management
Raptorsmiface pulled from latest old master, changelog merged down to single commit Due to the latest raptorsmiface code being used, this will not compile (yet)
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac1
-rw-r--r--raptorsmiface/Makefile.am32
-rw-r--r--raptorsmiface/libraptorsmiface.c1278
-rw-r--r--raptorsmiface/libraptorsmiface.h66
-rw-r--r--sesman/Makefile.am4
-rw-r--r--sesman/chansrv/Makefile.am4
-rw-r--r--sesman/chansrv/chansrv.c36
-rw-r--r--sesman/sesman.ini.in2
-rw-r--r--sesman/session.c45
-rw-r--r--xrdp/Makefile.am4
-rw-r--r--xrdp/xrdp_mm.c37
-rw-r--r--xrdp/xrdp_types.h1
13 files changed, 1496 insertions, 15 deletions
diff --git a/Makefile.am b/Makefile.am
index 2d15c7d7..b5f26454 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,6 +49,7 @@ endif
SUBDIRS = \
common \
+ raptorsmiface \
vnc \
xup \
mc \
diff --git a/configure.ac b/configure.ac
index 4dbcf230..5a00ed59 100644
--- a/configure.ac
+++ b/configure.ac
@@ -342,6 +342,7 @@ AC_CHECK_HEADERS([sys/prctl.h])
AC_CONFIG_FILES([
common/Makefile
+ raptorsmiface/Makefile
docs/Makefile
docs/man/Makefile
genkeymap/Makefile
diff --git a/raptorsmiface/Makefile.am b/raptorsmiface/Makefile.am
new file mode 100644
index 00000000..0e4d7d1c
--- /dev/null
+++ b/raptorsmiface/Makefile.am
@@ -0,0 +1,32 @@
+EXTRA_DIST = libraptorsmiface.h
+
+EXTRA_DEFINES =
+EXTRA_INCLUDES =
+EXTRA_LIBS =
+EXTRA_FLAGS =
+
+EXTRA_INCLUDES += -I$(prefix)/include
+EXTRA_FLAGS += -L$(prefix)/lib -Wl,-rpath -Wl,$(prefix)/lib
+
+AM_CFLAGS = \
+ -DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \
+ -DXRDP_SBIN_PATH=\"${sbindir}\" \
+ -DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \
+ -DXRDP_PID_PATH=\"${localstatedir}/run\" \
+ $(EXTRA_DEFINES)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/common \
+ $(EXTRA_INCLUDES)
+
+lib_LTLIBRARIES = \
+ libraptorsmiface.la
+
+libraptorsmiface_la_SOURCES = \
+ libraptorsmiface.c
+
+libraptorsmiface_la_LDFLAGS = \
+ $(EXTRA_FLAGS) -lmysqlclient
+
+libraptorsmiface_la_LIBADD = \
+ $(EXTRA_LIBS)
diff --git a/raptorsmiface/libraptorsmiface.c b/raptorsmiface/libraptorsmiface.c
new file mode 100644
index 00000000..bb934b39
--- /dev/null
+++ b/raptorsmiface/libraptorsmiface.c
@@ -0,0 +1,1278 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ (c) 2012 Timothy Pearson
+ (c) 2012 Raptor Engineering
+*/
+
+#define _GNU_SOURCE
+#define HAVE_STDINT_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <limits.h>
+
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+
+#define list_delete mysql_list_delete
+#include <mysql/mysql.h>
+#undef list_delete
+
+#include "list.h"
+#include "libraptorsmiface.h"
+
+#define STATISTICS_SERVER_START_EVENT 0
+#define STATISTICS_SERVER_STOP_EVENT 1
+#define STATISTICS_NEW_CONNECTION_EVENT 2
+#define STATISTICS_CONNECTION_STATUS_EVENT 3
+#define STATISTICS_DISCONNECTION_EVENT 4
+
+#define RAPTORSMIFACE_CFG_DATABASE "Database"
+#define RAPTORSMIFACE_CFG_DATABASE_SERVER "Server"
+#define RAPTORSMIFACE_CFG_DATABASE_NAME "Database"
+#define RAPTORSMIFACE_CFG_DATABASE_USER "User"
+#define RAPTORSMIFACE_CFG_DATABASE_PASSWORD "Password"
+
+#define RAPTORSMIFACE_CFG_DATABASE_SCHEMA_VERSION 1
+
+char *server = NULL;
+char *user = NULL;
+char *password = NULL;
+char *database = NULL;
+
+char *local_machine_fqdn = NULL;
+
+void dprint(const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+
+#if 0
+ vprintf(fmt, argp);
+#else
+ char debug[512];
+ vsprintf(debug, fmt, argp);
+ FILE *fp = fopen("/raptorsmiface.debug", "a");
+ if (fp != NULL)
+ {
+ fputs(debug, fp);
+ fclose(fp);
+ }
+#endif
+
+ va_end(argp);
+}
+
+void raptorsmiface_config_read_database(int file, struct list* param_n, struct list* param_v) {
+ int i;
+ char* buf;
+ char* temp_buf;
+
+ // Set defaults
+ if (!server) server = strdup("localhost");
+ if (!user) user = strdup("remotelab");
+ if (!password) password = strdup("");
+ if (!database) database = strdup("remotelab_sm");
+
+ list_clear(param_v);
+ list_clear(param_n);
+
+ file_read_section(file, RAPTORSMIFACE_CFG_DATABASE, param_n, param_v);
+ for (i = 0; i < param_n->count; i++) {
+ buf = (char*)list_get_item(param_n, i);
+ if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_SERVER)) {
+ free(server);
+ server = strdup((char*)list_get_item(param_v, i));
+ }
+ if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_NAME)) {
+ free(database);
+ database = strdup((char*)list_get_item(param_v, i));
+ }
+ if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_USER)) {
+ free(user);
+ user = strdup((char*)list_get_item(param_v, i));
+ }
+ if (0 == g_strcasecmp(buf, RAPTORSMIFACE_CFG_DATABASE_PASSWORD)) {
+ free(password);
+ password = strdup((char*)list_get_item(param_v, i));
+ }
+ }
+
+ g_printf("raptorsmiface configuration:\r\n");
+ g_printf("\tServer: %s\r\n", server);
+ g_printf("\tDatabase: %s\r\n", database);
+ g_printf("\tUser: %s\r\n", user);
+}
+
+void read_ini_configuration() {
+ int fd;
+ struct list* sec;
+ struct list* param_n;
+ struct list* param_v;
+
+ /* set global variables */
+ local_machine_fqdn = raptor_sm_get_local_machine_fqdn();
+
+ /* open configuration file */
+ char cfg_file[256];
+ g_snprintf(cfg_file, 255, "%s/xrdp.ini", XRDP_CFG_PATH);
+
+ fd = g_file_open(cfg_file);
+ if (-1 == fd) {
+ dprint("[ERROR] Unable to open configuration file [%s]", cfg_file);
+ return;
+ }
+
+ sec = list_create();
+ sec->auto_free = 1;
+ file_read_sections(fd, sec);
+ param_n = list_create();
+ param_n->auto_free = 1;
+ param_v = list_create();
+ param_v->auto_free = 1;
+
+ /* read database config */
+ raptorsmiface_config_read_database(fd, param_n, param_v);
+
+ /* cleanup */
+ list_delete(sec);
+ list_delete(param_v);
+ list_delete(param_n);
+ g_file_close(fd);
+}
+
+MYSQL * connect_if_needed() {
+ MYSQL *conn = mysql_init(NULL);
+ read_ini_configuration();
+ if (!mysql_real_connect(conn, server, user, password, database, 0, NULL, 0)) {
+ dprint("[ERROR] MySQL connection FAILED [%s]\n\r", mysql_error(conn));
+ conn = 0;
+ }
+ else {
+ // Check schema version
+ char* query;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ asprintf(&query, "SELECT value FROM dbschema WHERE skey='revision'");
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ dprint("[ERROR] MySQL connection FAILED [%s]\n\r", mysql_error(conn));
+ conn = 0;
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ if ((row = mysql_fetch_row(res)) == NULL) {
+ dprint("[ERROR] Mandatory schema version key not found\n\r");
+ conn = 0;
+ }
+ else if (!row[0]) {
+ dprint("[ERROR] Mandatory schema version key not found\n\r");
+ conn = 0;
+ }
+ else {
+ int schema_version = atoi(row[0]);
+ if (schema_version != RAPTORSMIFACE_CFG_DATABASE_SCHEMA_VERSION) {
+ dprint("[ERROR] Schema version key mismatch (%d:%d)\n\r", schema_version, RAPTORSMIFACE_CFG_DATABASE_SCHEMA_VERSION);
+ conn = 0;
+ }
+ }
+ }
+ }
+ return conn;
+}
+
+char* get_mysql_escaped_string(MYSQL *sqlcn, char* rawstr) {
+ unsigned int minlen = strlen(rawstr);
+ unsigned int maxlen = ((minlen*2)+1);
+ char* escstr = malloc(maxlen*sizeof(char));
+ mysql_real_escape_string(sqlcn, escstr, rawstr, minlen);
+ return escstr;
+}
+
+char mutex;
+int mysql_query_internal(MYSQL *conn, const char * query) {
+ // For some reason this can hang rather badly
+ // It might be related to concurrent access to the same conn object though
+ return mysql_query(conn, query);
+}
+
+char* get_group_for_user(char* username) {
+ struct passwd* pwd;
+ pwd = getpwnam(username);
+ if (!pwd) {
+ return NULL;
+ }
+ gid_t groupid = pwd->pw_gid;
+ struct group* primarygroup;
+ primarygroup = getgrgid(groupid);
+ if (!primarygroup) {
+ return NULL;
+ }
+
+ return strdup(primarygroup->gr_name);
+}
+
+int raptor_sm_get_uid_for_user(char* username) {
+ struct passwd *pwd = calloc(1, sizeof(struct passwd));
+ if (pwd == NULL) {
+ return -1;
+ }
+ size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
+ char *buffer = malloc(buffer_len);
+ if (buffer == NULL) {
+ return -2;
+ }
+ getpwnam_r(username, pwd, buffer, buffer_len, &pwd);
+ if (pwd == NULL) {
+ return -3;
+ }
+ uid_t uid = pwd->pw_uid;
+ free(buffer);
+ free(pwd);
+ return uid;
+}
+
+char raptor_sm_deallocate_session(char* username) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ MYSQL_RES *svr_res;
+ MYSQL_ROW svr_row;
+ MYSQL_RES *cnt_res;
+ MYSQL_ROW cnt_row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return 1;
+ }
+
+ int display = raptor_sm_get_display_for_username(username);
+ char* hostname = raptor_sm_get_hostname_for_username(username, false);
+ pid_t serverpid = raptor_sm_get_pid_for_username(username, RAPTOR_SM_SERVER_PID_FIELD);
+ if (serverpid >= 0) {
+ // Verify non-existence of PID on remote server before removing session information from the database
+ char* ip = raptor_sm_get_ip_for_hostname(hostname, 0);
+ char* command_string;
+ asprintf(&command_string, "ssh root@%s \'ps -p %d | grep %d\'", ip, serverpid, serverpid);
+ FILE *fp;
+ char output[1024];
+ // Open the command for reading
+ fp = popen(command_string, "r");
+ if (fp == NULL) {
+ free(ip);
+ mysql_close(conn);
+ return -1;
+ }
+ // Read the output a line at a time
+ fgets(output, sizeof(output)-1, fp);
+ // Close output
+ pclose(fp);
+ free(command_string);
+ free(ip);
+ if (strcmp(output, "") != 0) {
+ mysql_free_result(res);
+ mysql_close(conn);
+ return 0;
+ }
+ }
+
+#ifndef RAPTOR_SM_DISABLE_KERBEROS
+ char* command_string;
+ char* ip = raptor_sm_get_ip_for_hostname(hostname, 0);
+ asprintf(&command_string, "ssh root@%s \'rm -f /tmp/krb5cc_%d\'", ip, raptor_sm_get_uid_for_user(username));
+ dprint("Running command %s...\n\r", command_string);
+ system(command_string);
+ free(command_string);
+ free(ip);
+#endif
+
+ // Remove the user from the system
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "DELETE FROM sessions WHERE username='%s'", safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(hostname);
+ free(query);
+ mysql_close(conn);
+ return 2;
+ }
+ else {
+ free(query);
+
+ // Insert connection information into the statistics database
+ char* safe_servername = get_mysql_escaped_string(conn, hostname);
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ long long timestamp = time(NULL);
+ asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_DISCONNECTION_EVENT, safe_local_machine_fqdn, safe_servername, display, -1, safe_username);
+ free(safe_servername);
+ free(safe_username);
+ free(safe_local_machine_fqdn);
+ free(hostname);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn));
+ }
+ free(query);
+
+ mysql_close(conn);
+ return 0;
+ }
+}
+
+char* raptor_sm_allocate_session(char* username) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ MYSQL_RES *svr_res;
+ MYSQL_ROW svr_row;
+ MYSQL_RES *per_res;
+ MYSQL_ROW per_row;
+ MYSQL_RES *cnt_res;
+ MYSQL_ROW cnt_row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return strdup("ERROR");
+ }
+
+ // Verify that this user is not already on the system
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "SELECT servername FROM sessions WHERE username='%s'", safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return strdup("ERROR");
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ if ((row = mysql_fetch_row(res)) == NULL) {
+ // User is not on a system
+ // Find the least utilized node
+ if (mysql_query_internal(conn, "SELECT name FROM servers WHERE online='1'")) {
+ // Server error
+ mysql_free_result(res);
+ mysql_close(conn);
+ return strdup("ERROR");
+ }
+ else {
+ svr_res = mysql_store_result(conn);
+
+ // Get group for user
+ char* groupname = get_group_for_user(username);
+ if (!groupname) {
+ return strdup("ERROR");
+ }
+ char* safe_groupname = get_mysql_escaped_string(conn, groupname);
+ free(groupname);
+ // Get the list of allowed nodes for this group
+ asprintf(&query, "SELECT server FROM allowed_servers WHERE groupname='%s'", safe_groupname);
+ free(safe_groupname);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ mysql_free_result(res);
+ mysql_free_result(svr_res);
+ mysql_close(conn);
+ return strdup("ERROR");
+ }
+ else {
+ per_res = mysql_store_result(conn);
+ char* bestserver = strdup("");
+ int bestusage = INT_MAX;
+ while ((svr_row = mysql_fetch_row(svr_res)) != NULL) {
+ // Am I allowed to use this server?
+ bool can_use_server = false;
+ while ((per_row = mysql_fetch_row(per_res)) != NULL) {
+ if (strcmp(per_row[0], svr_row[0]) == 0) {
+ can_use_server = true;
+ }
+ }
+ mysql_data_seek(per_res, 0);
+ if (can_use_server) {
+ char* safe_servername = get_mysql_escaped_string(conn, svr_row[0]);
+ asprintf(&query, "SELECT username FROM sessions WHERE servername='%s'", safe_servername);
+ free(safe_servername);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ free(bestserver);
+ mysql_free_result(res);
+ mysql_free_result(svr_res);
+ mysql_close(conn);
+ return strdup("ERROR");
+ }
+ else {
+ free(query);
+ cnt_res = mysql_store_result(conn);
+ int usagecount = 0;
+ while ((cnt_row = mysql_fetch_row(cnt_res)) != NULL) {
+ usagecount++;
+ }
+ mysql_free_result(cnt_res);
+ if (usagecount < bestusage) {
+ free(bestserver);
+ bestserver = strdup(svr_row[0]);
+ bestusage = usagecount;
+ }
+ }
+ }
+ }
+ mysql_free_result(res);
+ mysql_free_result(svr_res);
+ mysql_free_result(per_res);
+
+ if (strcmp(bestserver, "") != 0) {
+ // Insert new information into the sessions database and set status to ALLOCATED
+ char* safe_servername = get_mysql_escaped_string(conn, bestserver);
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ asprintf(&query, "INSERT INTO sessions (username, arbiter, servername, state) VALUES ('%s', '%s', '%s', '%d')", safe_username, safe_local_machine_fqdn, safe_servername, SM_STATUS_ALLOCATED);
+ free(safe_local_machine_fqdn);
+ free(safe_servername);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return strdup("ERROR");
+ }
+ else {
+ free(query);
+
+ // Insert connection information into the statistics database
+ char* safe_servername = get_mysql_escaped_string(conn, bestserver);
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ long long timestamp = time(NULL);
+ asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_NEW_CONNECTION_EVENT, safe_local_machine_fqdn, safe_servername, -1, SM_STATUS_ALLOCATED, safe_username);
+ free(safe_local_machine_fqdn);
+ free(safe_servername);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn));
+ }
+ free(query);
+
+ mysql_close(conn);
+ return strdup(bestserver);
+ }
+ }
+ else {
+ // No usable server found!
+ mysql_close(conn);
+ return strdup("ERROR");
+ }
+ }
+ }
+ }
+ else {
+ char* ret = strdup(row[0]);
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ }
+}
+
+char* raptor_sm_get_local_machine_fqdn() {
+ struct addrinfo hints, *res;
+ int err;
+
+ char hostname[1024];
+ hostname[1023] = '\0';
+ gethostname(hostname, 1023);
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
+ return strdup("");
+ }
+
+ char* ret = strdup(res->ai_canonname);
+ freeaddrinfo(res);
+ return ret;
+}
+
+char* raptor_sm_get_ip_for_hostname(char* hostname, char* error) {
+ struct addrinfo hints, *res;
+ struct in_addr addr;
+ int err;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_INET;
+
+ if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
+ if (error) *error = 1;
+ return strdup("");
+ }
+
+ addr.s_addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
+
+ char* ret = strdup(inet_ntoa(addr));
+ freeaddrinfo(res);
+ if (error) *error = 0;
+ return ret;
+}
+
+char* raptor_sm_get_hostname_for_username(char* username, bool create) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return strdup("SQLERR100");
+ }
+
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "SELECT servername FROM sessions WHERE username='%s'", safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return strdup("SQLERR101");
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ char* ret = strdup(row[0]);
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ // Nothing in the DB
+ mysql_free_result(res);
+ if (create) {
+ // Try to allocate a new session on a node
+ mysql_close(conn);
+ return raptor_sm_allocate_session(username);
+ }
+ else {
+ mysql_close(conn);
+ return strdup("");
+ }
+ }
+}
+
+char* raptor_sm_get_hostname_for_display(int display) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return strdup("SQLERR100");
+ }
+
+ asprintf(&query, "SELECT servername FROM sessions WHERE display='%d'", display);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return strdup("SQLERR101");
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ char* ret = strdup(row[0]);
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ // Nothing in the DB
+ mysql_free_result(res);
+ mysql_close(conn);
+ return strdup("");
+ }
+}
+
+char* raptor_sm_get_ip_for_username(char* username, bool create) {
+ char* hostname = raptor_sm_get_hostname_for_username(username, create);
+ char err;
+ char* ip = raptor_sm_get_ip_for_hostname(hostname, &err);
+ free(hostname);
+ if (err) {
+ raptor_sm_deallocate_session(username);
+ return strdup("ERROR");
+ }
+ return ip;
+}
+
+bool raptor_sm_sesslimit_reached(char* username) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return true;
+ }
+
+ // Respect maximum session number for the group for this user
+ int sesslimit = 0; // Default to denying all sessions
+
+ // Get group for user
+ char* groupname = get_group_for_user(username);
+ if (!groupname) {
+ return true;
+ }
+ char* safe_groupname = get_mysql_escaped_string(conn, groupname);
+ asprintf(&query, "SELECT sesslimit FROM groups WHERE groupname='%s'", safe_groupname);
+ free(safe_groupname);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(groupname);
+ free(query);
+ mysql_close(conn);
+ return true;
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ row = mysql_fetch_row(res);
+ if (row[0]) {
+ sesslimit = atoi(row[0]);
+ }
+ mysql_free_result(res);
+ }
+
+ // Figure out how many users are online from this group
+ int sesscount = 0;
+ asprintf(&query, "SELECT username FROM sessions WHERE state<>'%d'", SM_STATUS_ALLOCATED);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(groupname);
+ free(query);
+ mysql_close(conn);
+ return true;
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ if (row[0]) {
+ char* test_groupname = get_group_for_user(row[0]);
+ if (!test_groupname) {
+ free(groupname);
+ return true;
+ }
+ if (strcmp(groupname, test_groupname) == 0) {
+ sesscount++;
+ }
+ free(test_groupname);
+ }
+ }
+ mysql_free_result(res);
+
+ if (sesscount < sesslimit) {
+ free(groupname);
+ mysql_close(conn);
+ return false;
+ }
+ free(groupname);
+ mysql_close(conn);
+ return true;
+ }
+
+ // We should never end up here!
+ free(groupname);
+ mysql_close(conn);
+ return true;
+}
+
+pid_t raptor_sm_run_remote_server(char* username, char *const argv[], char* dbfield, int display) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ if (strcmp(dbfield, RAPTOR_SM_SERVER_PID_FIELD) == 0) {
+ // Respect maximum session number for the group for this user
+ if (raptor_sm_sesslimit_reached(username)) {
+ mysql_close(conn);
+ return -5;
+ }
+ }
+
+ // Make sure a server is not already running for this user
+ // Return the existing PID if it is
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ if (strcmp(dbfield, RAPTOR_SM_SERVER_PID_FIELD) == 0) {
+ asprintf(&query, "SELECT %s,servername FROM sessions WHERE username='%s' AND state<>'%d'", dbfield, safe_username, SM_STATUS_ALLOCATED);
+ }
+ else {
+ asprintf(&query, "SELECT %s,servername FROM sessions WHERE username='%s'", dbfield, safe_username);
+ }
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ if (row[0]) {
+ int ret = atoi(row[0]);
+ if (ret >= 0) {
+ // Verify existence of PID on remote server
+ dprint("Verifying process %d on %s...\n\r", ret, row[1]);
+ char* ip = raptor_sm_get_ip_for_hostname(row[1], 0);
+ char* command_string;
+ asprintf(&command_string, "ssh root@%s \'ps -p %d | grep %d\'", ip, ret, ret);
+ FILE *fp;
+ char output[1024];
+ // Open the command for reading
+ fp = popen(command_string, "r");
+ if (fp == NULL) {
+ free(ip);
+ mysql_close(conn);
+ return -1;
+ }
+ // Read the output a line at a time
+ fgets(output, sizeof(output)-1, fp);
+ // Close output
+ pclose(fp);
+ free(command_string);
+ free(ip);
+ dprint("...result was %s\n\r", output);
+ if (strcmp(output, "") != 0) {
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ }
+ }
+ }
+ mysql_free_result(res);
+ }
+
+ int i;
+ int n_commands;
+
+ n_commands = 0;
+ while (argv[n_commands] != NULL) {
+ n_commands++;
+ }
+
+ char* ipaddr = raptor_sm_get_ip_for_username(username, true);
+
+ // This is HORRIBLY inefficient
+ char* command_string = strdup("");
+ for (i=0; i<n_commands; i++) {
+ char* origstr = command_string;
+ asprintf(&command_string, "%s %s", origstr, argv[i]);
+ free(origstr);
+ }
+ char* origstr = command_string;
+
+#ifndef RAPTOR_SM_DISABLE_KERBEROS
+ if (display >= 0) {
+ uid_t uid = raptor_sm_get_uid_for_user(username);
+ asprintf(&command_string, "rsync -a /tmp/krb5cc_%d root@%s:/tmp/krb5cc_%d", uid, ipaddr, uid);
+ dprint("Running command %s...\n\r", command_string);
+ system(command_string);
+ free(command_string);
+ asprintf(&command_string, "rm -f /tmp/krb5cc_%d", uid);
+ dprint("Running command %s...\n\r", command_string);
+ system(command_string);
+ free(command_string);
+ }
+#endif
+
+#ifndef RAPTOR_SM_DISABLE_PULSEAUDIO
+ if (display >= 0) {
+ asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && pulseaudio -D --load=\\\"module-native-protocol-tcp listen=0.0.0.0 auth-ip-acl=%s port=%d\\\"\' &> /dev/null\" &", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), RAPTOR_SM_MANAGEMENT_SERVER_IP_NETRANGE, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display));
+ dprint("Running command %s...\n\r", command_string);
+ system(command_string);
+ free(command_string);
+ }
+#endif
+
+ if (strcmp(dbfield, RAPTOR_SM_SERVER_PID_FIELD) == 0) {
+ asprintf(&command_string, "ssh root@%s \'%s & echo $! &\'", ipaddr, origstr);
+ }
+ else {
+#if RAPTOR_SM_DISABLE_PULSEAUDIO
+ asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && %s\' &> /dev/null & echo \\$!\"", ipaddr, username, display, origstr);
+#else
+ //asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && %s\' &> /dev/null & echo \\$!\"", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), origstr);
+ asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && %s\' &> /var/log/%s_wm_session.log & echo \\$!\"", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), origstr, username);
+#endif
+ }
+ dprint("Running command %s...\n\r", command_string);
+ free(origstr);
+ free(ipaddr);
+
+ FILE *fp;
+ char output[1024];
+
+ // Open the command for reading
+ fp = popen(command_string, "r");
+ if (fp == NULL) {
+ mysql_close(conn);
+ return -1;
+ }
+
+ // Read the output a line at a time
+ fgets(output, sizeof(output)-1, fp);
+
+ // Close output
+ pclose(fp);
+
+ free(command_string);
+
+ mysql_close(conn);
+ return atoi(output);
+}
+
+pid_t raptor_sm_get_pid_for_username(char* username, char* dbfield) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Make sure a server is not already running for this user
+ // Return the existing PID if it is
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "SELECT %s FROM sessions WHERE username='%s'", dbfield, safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ if (row[0]) {
+ int ret = atoi(row[0]);
+ if (ret >= 0) {
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ }
+ }
+ mysql_free_result(res);
+ }
+
+ mysql_close(conn);
+ return -3;
+}
+
+int raptor_sm_server_started(char* username, pid_t pid, int display, char* dbfield) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ long long timestamp = time(NULL);
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Update new information into the sessions database and set status to ALLOCATED
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "UPDATE sessions SET %s='%d', stamp_start='%lld', state='%d', display='%d', stamp_statechange='%lld' WHERE username='%s' AND state='%d'", dbfield, pid, timestamp, SM_STATUS_RUNNING, display, timestamp, safe_username, SM_STATUS_ALLOCATED);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+
+ // Insert connection information into the statistics database
+ char* current_server = raptor_sm_get_hostname_for_display(display);
+ char* safe_servername = get_mysql_escaped_string(conn, current_server);
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ long long timestamp = time(NULL);
+ asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_CONNECTION_STATUS_EVENT, safe_local_machine_fqdn, safe_servername, display, SM_STATUS_RUNNING, safe_username);
+ free(safe_servername);
+ free(safe_username);
+ free(safe_local_machine_fqdn);
+ free(current_server);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn));
+ }
+ free(query);
+
+ mysql_close(conn);
+ return 0;
+ }
+}
+
+int raptor_sm_wm_started(char* username, pid_t pid, char* dbfield) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Update new information into the sessions database and set status to ALLOCATED
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "UPDATE sessions SET %s='%d' WHERE username='%s'", dbfield, pid, safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+ mysql_close(conn);
+ return 0;
+ }
+}
+
+int raptor_sm_get_display_for_username(char* username) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "SELECT display FROM sessions WHERE username='%s'", safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ if (row[0]) {
+ int ret = atoi(row[0]);
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ else {
+ mysql_free_result(res);
+ mysql_close(conn);
+ return -3;
+ }
+ }
+ // Nothing in the DB
+ mysql_free_result(res);
+ mysql_close(conn);
+ return -4;
+ }
+}
+
+char* raptor_sm_get_username_for_display_and_hostname(int display, char* hostname) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return strdup("");
+ }
+
+ char* safe_hostname = get_mysql_escaped_string(conn, hostname);
+ asprintf(&query, "SELECT username FROM sessions WHERE display='%d' AND servername='%s'", display, safe_hostname);
+ free(safe_hostname);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return strdup("");
+ }
+ else {
+ free(query);
+ res = mysql_store_result(conn);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ if (row[0]) {
+ char* ret = strdup(row[0]);
+ mysql_free_result(res);
+ mysql_close(conn);
+ return ret;
+ }
+ else {
+ mysql_free_result(res);
+ mysql_close(conn);
+ return strdup("");
+ }
+ }
+ // Nothing in the DB
+ mysql_free_result(res);
+ mysql_close(conn);
+ return strdup("");
+ }
+}
+
+void raptor_sm_wait_for_pid_exit(char* username, pid_t pid) {
+ char* ipaddr = raptor_sm_get_ip_for_username(username, false);
+
+ char* command_string;
+ asprintf(&command_string, "ssh root@%s \'while [[ `ps -p %d | grep %d` != \"\" ]]; do sleep 1; done\'", ipaddr, pid, pid);
+ system(command_string);
+ free(command_string);
+}
+
+void raptor_sm_session_terminated(char* username) {
+ raptor_sm_deallocate_session(username);
+}
+
+int raptor_sm_wm_terminated(char* username) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ long long timestamp = time(NULL);
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Update new information into the sessions database
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ asprintf(&query, "UPDATE sessions SET %s=NULL WHERE username='%s'", RAPTOR_SM_WM_PID_FIELD, safe_username);
+ free(safe_username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+ mysql_close(conn);
+ return 0;
+ }
+}
+
+int raptor_sm_get_new_unique_display(int mindisplay, int maxdisplay) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ asprintf(&query, "SELECT display FROM sessions");
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ dprint("[ERROR] MySQL connection FAILED [%s]\n\r", mysql_error(conn));
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ res = mysql_store_result(conn);
+ int freedisp;
+ bool dispinuse;
+ for (freedisp=mindisplay; freedisp<maxdisplay; freedisp++) {
+ dispinuse = false;
+ mysql_data_seek(res, 0);
+ while ((row = mysql_fetch_row(res)) != NULL) {
+ if (row[0]) {
+ if (atoi(row[0]) == freedisp) {
+ dispinuse = true;
+ }
+ }
+ }
+ if (dispinuse == false) {
+ break;
+ }
+ }
+ mysql_free_result(res);
+ mysql_close(conn);
+ return freedisp;
+ }
+}
+
+char raptor_sm_set_session_state(int display, int state) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ long long timestamp = time(NULL);
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Update new state into the sessions database
+ asprintf(&query, "UPDATE sessions SET state='%d', stamp_statechange='%lld' WHERE display='%d'", state, timestamp, display);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ free(query);
+ mysql_close(conn);
+ return -2;
+ }
+ else {
+ free(query);
+
+ // Insert connection information into the statistics database
+ char* hostname = raptor_sm_get_hostname_for_display(display);
+ char* username = raptor_sm_get_username_for_display_and_hostname(display, hostname);
+ char* safe_servername = get_mysql_escaped_string(conn, hostname);
+ char* safe_username = get_mysql_escaped_string(conn, username);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ long long timestamp = time(NULL);
+ asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid, username) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d', '%s')", timestamp, STATISTICS_CONNECTION_STATUS_EVENT, safe_local_machine_fqdn, safe_servername, display, state, safe_username);
+ free(safe_servername);
+ free(safe_username);
+ free(safe_local_machine_fqdn);
+ free(hostname);
+ free(username);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn));
+ }
+ free(query);
+
+ mysql_close(conn);
+ return 0;
+ }
+}
+
+void raptor_sm_run_remote_desktop(char* username, int display, char* executable) {
+ char* ipaddr = raptor_sm_get_ip_for_username(username, true);
+ char* command_string;
+#if RAPTOR_SM_DISABLE_PULSEAUDIO
+ asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && %s && exit\' &> /dev/null\"", ipaddr, username, display, executable);
+#else
+ asprintf(&command_string, "ssh root@%s \"su %s -c \'export DISPLAY=:%d && export PULSE_SERVER=tcp:%s:%d && %s && exit\' &> /dev/null\"", ipaddr, username, display, ipaddr, (RAPTOR_SM_BASE_PULSEAUDIO_PORT+display), executable);
+#endif
+ system(command_string);
+ free(command_string);
+}
+
+void raptor_sm_terminate_server(char* username) {
+ char* ipaddr = raptor_sm_get_ip_for_username(username, true);
+ char* command_string;
+
+ // Terminate remote X server
+ pid_t pid = raptor_sm_get_pid_for_username(username, RAPTOR_SM_SERVER_PID_FIELD);
+ if (pid > 0) {
+ asprintf(&command_string, "ssh root@%s \'kill -9 %ld\'", ipaddr, pid);
+ system(command_string);
+ free(command_string);
+ }
+}
+
+int raptor_sm_stats_report_server_start(char* hostname) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Insert information into the statistics database
+ char* safe_servername = get_mysql_escaped_string(conn, hostname);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ long long timestamp = time(NULL);
+ asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d')", timestamp, STATISTICS_SERVER_START_EVENT, safe_local_machine_fqdn, safe_servername, -1, -1);
+ free(safe_servername);
+ free(safe_local_machine_fqdn);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn));
+ }
+ free(query);
+ mysql_close(conn);
+
+ return 0;
+}
+
+int raptor_sm_stats_report_server_stop(char* hostname) {
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ char* query;
+
+ MYSQL *conn = connect_if_needed();
+ if (!conn) {
+ return -1;
+ }
+
+ // Insert information into the statistics database
+ char* safe_servername = get_mysql_escaped_string(conn, hostname);
+ char* safe_local_machine_fqdn = get_mysql_escaped_string(conn, local_machine_fqdn);
+ long long timestamp = time(NULL);
+ asprintf(&query, "INSERT INTO statistics (timestamp, eventtypeid, arbiter, servername, display, typeid) VALUES ('%lld', '%d', '%s', '%s', '%d', '%d')", timestamp, STATISTICS_SERVER_STOP_EVENT, safe_local_machine_fqdn, safe_servername, -1, -1);
+ free(safe_servername);
+ free(safe_local_machine_fqdn);
+ if (mysql_query_internal(conn, query)) {
+ // Server error
+ dprint("Unable to insert data into statistics database! [%s]\n\r", mysql_error(conn));
+ }
+ free(query);
+ mysql_close(conn);
+
+ return 0;
+}
diff --git a/raptorsmiface/libraptorsmiface.h b/raptorsmiface/libraptorsmiface.h
new file mode 100644
index 00000000..79e4c2e1
--- /dev/null
+++ b/raptorsmiface/libraptorsmiface.h
@@ -0,0 +1,66 @@
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ (c) 2012 Timothy Pearson
+ (c) 2012 Raptor Engineering
+*/
+
+#include <unistd.h>
+
+#include <sys/types.h>
+
+typedef unsigned char bool;
+#define true 1
+#define false 0
+
+// SM_STATUS_ALLOCATED: Server is not yet started
+// SM_STATUS_RUNNING: Server is up, but client is not connected
+// SM_STATUS_CONNECTED: Server is up and client is connected
+enum raptor_sm_status {
+ SM_STATUS_ALLOCATED,
+ SM_STATUS_RUNNING,
+ SM_STATUS_CONNECTED,
+ SM_STATUS_FORCEKILL
+};
+
+#define RAPTOR_SM_SERVER_PID_FIELD "server_pid"
+#define RAPTOR_SM_WM_PID_FIELD "wm_pid"
+
+#define RAPTOR_SM_BASE_PULSEAUDIO_PORT 2000
+#define RAPTOR_SM_MANAGEMENT_SERVER_IP_NETRANGE "10.0.0.0/8"
+
+char* raptor_sm_get_local_machine_fqdn();
+
+char* raptor_sm_get_ip_for_hostname(char* hostname, char* err);
+char* raptor_sm_get_hostname_for_username(char* username, bool create);
+
+char* raptor_sm_get_ip_for_username(char* username, bool create);
+pid_t raptor_sm_run_remote_server(char* username, char *const argv[], char* dbfield, int display);
+pid_t raptor_sm_get_pid_for_username(char* username, char* dbfield);
+int raptor_sm_server_started(char* username, pid_t pid, int display, char* dbfield);
+int raptor_sm_wm_started(char* username, pid_t pid, char* dbfield);
+int raptor_sm_get_display_for_username(char* username);
+void raptor_sm_wait_for_pid_exit(char* username, pid_t pid);
+char* raptor_sm_get_username_for_display_and_hostname(int display, char* hostname);
+void raptor_sm_session_terminated(char* username);
+int raptor_sm_wm_terminated(char* username);
+int raptor_sm_get_new_unique_display(int mindisplay, int maxdisplay);
+bool raptor_sm_sesslimit_reached(char* username);
+char raptor_sm_set_session_state(int display, int state);
+void raptor_sm_run_remote_desktop(char* username, int display, char* executable);
+void raptor_sm_terminate_server(char* username);
+char* raptor_sm_get_hostname_for_display(int display);
+int raptor_sm_stats_report_server_start(char* hostname);
+int raptor_sm_stats_report_server_stop(char* hostname);
diff --git a/sesman/Makefile.am b/sesman/Makefile.am
index 50425e63..50a9fafc 100644
--- a/sesman/Makefile.am
+++ b/sesman/Makefile.am
@@ -9,7 +9,8 @@ AM_CPPFLAGS = \
-DXRDP_PID_PATH=\"${localstatedir}/run\" \
-DXRDP_SOCKET_PATH=\"${socketdir}\" \
-I$(top_srcdir)/common \
- -I$(top_srcdir)/sesman/libscp
+ -I$(top_srcdir)/sesman/libscp \
+ -I$(top_srcdir)/raptorsmiface
if XRDP_DEBUG
AM_CPPFLAGS += -DXRDP_DEBUG
@@ -70,6 +71,7 @@ xrdp_sesman_SOURCES = \
xrdp_sesman_LDADD = \
$(top_builddir)/common/libcommon.la \
$(top_builddir)/sesman/libscp/libscp.la \
+ $(top_builddir)/raptorsmiface/libraptorsmiface.la \
$(AUTH_LIB) \
-lpthread
diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am
index 05007757..2b22312c 100644
--- a/sesman/chansrv/Makefile.am
+++ b/sesman/chansrv/Makefile.am
@@ -9,7 +9,8 @@ AM_CPPFLAGS = \
-DXRDP_SHARE_PATH=\"${datadir}/xrdp\" \
-DXRDP_PID_PATH=\"${localstatedir}/run\" \
-DXRDP_SOCKET_PATH=\"${socketdir}\" \
- -I$(top_srcdir)/common
+ -I$(top_srcdir)/common \
+ -I$(top_srcdir)/raptorsmiface
if XRDP_DEBUG
AM_CPPFLAGS += -DXRDP_DEBUG
@@ -77,5 +78,6 @@ xrdp_chansrv_LDFLAGS = \
xrdp_chansrv_LDADD = \
$(top_builddir)/common/libcommon.la \
+ $(top_builddir)/raptorsmiface/libraptorsmiface.la \
$(X_PRE_LIBS) -lXfixes -lXrandr -lX11 $(X_EXTRA_LIBS) \
$(CHANSRV_EXTRA_LIBS)
diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c
index 7a0de556..12614f51 100644
--- a/sesman/chansrv/chansrv.c
+++ b/sesman/chansrv/chansrv.c
@@ -1,6 +1,7 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
+ * Copyright (C) Timothy Pearson 2012-2019
* Copyright (C) Jay Sorg 2009-2013
* Copyright (C) Laxmikant Rashinkar 2009-2012
*
@@ -38,6 +39,8 @@
#include "chansrv_fuse.h"
#include "xrdp_sockets.h"
+#include "libraptorsmiface.h"
+
static struct trans *g_lis_trans = 0;
static struct trans *g_con_trans = 0;
static struct trans *g_api_lis_trans = 0;
@@ -60,6 +63,32 @@ int g_rdpsnd_chan_id = -1; /* rdpsnd */
int g_rdpdr_chan_id = -1; /* rdpdr */
int g_rail_chan_id = -1; /* rail */
+#if 0
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+void dprint(const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ char debug[1024];
+ vsprintf(debug, fmt, argp);
+ FILE *fp = fopen("/chansrv.debug", "a");
+ if (fp != NULL)
+ {
+ fputs(debug, fp);
+ fclose(fp);
+ }
+ va_end(argp);
+}
+#undef LOG
+#define LOG(_a, _params) \
+{ \
+ dprint _params; \
+ dprint("\n"); \
+}
+#endif
+
char *g_exec_name;
tbus g_exec_event;
tbus g_exec_mutex;
@@ -414,6 +443,8 @@ process_message_channel_setup(struct stream *s)
rail_init();
}
+ // Use the display number to mark session connected in the Raptor session management database
+ raptor_sm_set_session_state(g_display_num, SM_STATUS_CONNECTED);
return rv;
}
@@ -1435,6 +1466,9 @@ channel_thread_loop(void *in_val)
/* delete g_con_trans */
trans_delete(g_con_trans);
g_con_trans = 0;
+ /* use the display number to mark session disconnected in the Raptor session management database */
+ raptor_sm_set_session_state(g_display_num, SM_STATUS_RUNNING);
+ exit(0); // RAPTOR session management
/* create new listener */
error = setup_listen();
@@ -1904,6 +1938,8 @@ main(int argc, char **argv)
}
}
+ /* use the display number to mark session disconnected in the Raptor session management database */
+ raptor_sm_set_session_state(g_display_num, SM_STATUS_RUNNING);
/* cleanup */
main_cleanup();
LOGM((LOG_LEVEL_INFO, "main: app exiting pid %d(0x%8.8x)", pid, pid));
diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in
index 9af7a100..d8d8ea94 100644
--- a/sesman/sesman.ini.in
+++ b/sesman/sesman.ini.in
@@ -29,7 +29,7 @@ X11DisplayOffset=10
;; MaxSessions - maximum number of connections to an xrdp server
; Type: integer
; Default: 0
-MaxSessions=50
+MaxSessions=1000000
;; KillDisconnected - kill disconnected sessions
; Type: boolean
diff --git a/sesman/session.c b/sesman/session.c
index 0d9fdc70..6fa63c3a 100644
--- a/sesman/session.c
+++ b/sesman/session.c
@@ -42,6 +42,8 @@
#include "xauth.h"
#include "xrdp_sockets.h"
+#include "libraptorsmiface.h"
+
#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif
@@ -448,7 +450,21 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c,
return 0;
}
- display = session_get_avail_display_from_chain();
+ char session_was_already_running = 0;
+ int allocdisplay = raptor_sm_get_display_for_username(s->username);
+ if (allocdisplay >= 0) {
+ session_was_already_running = 1;
+ display = allocdisplay;
+ }
+ else {
+ int allocdisplay = raptor_sm_get_new_unique_display(g_cfg->sess.x11_display_offset, g_cfg->sess.max_sessions);
+ if (allocdisplay < 0) {
+ display = 0;
+ }
+ else {
+ display = allocdisplay;
+ }
+ }
if (display == 0)
{
@@ -533,6 +549,9 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c,
display,
g_cfg->env_names,
g_cfg->env_values);
+ if (session_was_already_running) {
+ g_exit(0);
+ }
if (x_server_running(display))
{
auth_set_env(data);
@@ -705,7 +724,29 @@ session_start_fork(tbus data, tui8 type, struct SCP_CONNECTION *c,
g_setenv("XRDP_START_HEIGHT", geometry, 1);
/* fire up Xorg */
- g_execvp(xserver, pp1);
+ pid_t serverpid;
+ serverpid = raptor_sm_run_remote_server(s->username, pp1);
+
+ if (serverpid >= 0) {
+ if (!session_was_already_running) {
+ char *friendlyscreen = g_strdup(screen);
+ friendlyscreen[0] = ' ';
+ raptor_sm_server_started(s->username, serverpid, atoi(friendlyscreen));
+ g_free(friendlyscreen);
+
+ // Wait for PID exit and remove information from the session database
+ raptor_sm_wait_for_pid_exit(s->username, serverpid);
+ raptor_sm_session_terminated(s->username);
+ }
+ }
+ else {
+ raptor_sm_session_terminated(s->username);
+ log_message(LOG_LEVEL_WARNING, "max concurrent session limit "
+ "exceeded in group. login for user %s denied", s->username);
+ g_exit(1);
+ }
+
+ g_exit(0);
}
else if (type == SESMAN_SESSION_TYPE_XVNC)
{
diff --git a/xrdp/Makefile.am b/xrdp/Makefile.am
index fe14d88d..5179ef68 100644
--- a/xrdp/Makefile.am
+++ b/xrdp/Makefile.am
@@ -12,7 +12,8 @@ AM_CPPFLAGS = \
-DXRDP_SOCKET_PATH=\"${socketdir}\" \
-I$(top_builddir) \
-I$(top_srcdir)/common \
- -I$(top_srcdir)/libxrdp
+ -I$(top_srcdir)/libxrdp \
+ -I$(top_srcdir)/raptorsmiface
XRDP_EXTRA_LIBS =
@@ -63,6 +64,7 @@ xrdp_SOURCES = \
xrdp_LDADD = \
$(top_builddir)/common/libcommon.la \
$(top_builddir)/libxrdp/libxrdp.la \
+ $(top_builddir)/raptorsmiface/libraptorsmiface.la \
$(XRDP_EXTRA_LIBS)
xrdpsysconfdir=$(sysconfdir)/xrdp
diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c
index 7d204b5b..1bb7ea15 100644
--- a/xrdp/xrdp_mm.c
+++ b/xrdp/xrdp_mm.c
@@ -24,6 +24,8 @@
#include "xrdp.h"
#include "log.h"
+#include "libraptorsmiface.h"
+
#ifndef USE_NOPAM
#if defined(HAVE__PAM_TYPES_H)
#define LINUXPAM 1
@@ -59,6 +61,7 @@ xrdp_mm_create(struct xrdp_wm *owner)
self->wm = owner;
self->login_names = list_create();
self->login_names->auto_free = 1;
+ self->login_username = 0;
self->login_values = list_create();
self->login_values->auto_free = 1;
@@ -190,6 +193,7 @@ xrdp_mm_send_login(struct xrdp_mm *self)
if (g_strcasecmp(name, "username") == 0)
{
username = value;
+ self->login_username = g_strdup(username);
}
else if (g_strcasecmp(name, "password") == 0)
{
@@ -521,16 +525,30 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self, tui8 *guid)
}
else if (self->code == 10 || self->code == 20) /* X11rdp/Xorg */
{
- use_uds = 1;
-
- if (xrdp_mm_get_value(self, "ip", text, 255) == 0)
- {
- if (g_strcmp(text, "127.0.0.1") != 0)
- {
+ char* rsmip = raptor_sm_get_ip_for_username(self->login_username, true);
+ int allocdisplay = raptor_sm_get_display_for_username(self->login_username);
+ if ((raptor_sm_sesslimit_reached(self->login_username)) && (allocdisplay < 0)) {
+ g_snprintf(text, 255, "[LICENSE] Maximum concurrent session");
+ xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, text);
+ g_snprintf(text, 255, "[LICENSE] limit exceeded for group.");
+ xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, text);
+ g_snprintf(text, 255, "[LICENSE] Login for user %s denied.", self->login_username);
+ xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, text);
+ raptor_sm_session_terminated(self->login_username);
+ return 1;
+ }
+ else {
+ if (allocdisplay >= 0) {
+ self->display = allocdisplay;
+ }
+ self->mod->mod_set_param(self->mod, "ip", rsmip);
+ use_uds = 1;
+ if (g_strcmp(rsmip, "127.0.0.1") != 0) {
use_uds = 0;
}
}
+ g_free(rsmip);
if (use_uds)
{
g_snprintf(text, 255, XRDP_X11RDP_STR, self->display);
@@ -576,7 +594,9 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self, tui8 *guid)
{
name = (const char *) list_get_item(self->login_names, i);
value = (const char *) list_get_item(self->login_values, i);
- self->mod->mod_set_param(self->mod, name, value);
+ if (strcmp(name, "ip") != 0) {
+ self->mod->mod_set_param(self->mod, name, value);
+ }
}
/* connect */
@@ -1546,8 +1566,7 @@ xrdp_mm_process_login_response(struct xrdp_mm *self, struct stream *s)
if (ok)
{
self->display = display;
- xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO,
- "login successful for display %d", display);
+ xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "login successful on display %d", display);
if (xrdp_mm_setup_mod1(self) == 0)
{
diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h
index 7e416125..2cb51b80 100644
--- a/xrdp/xrdp_types.h
+++ b/xrdp/xrdp_types.h
@@ -294,6 +294,7 @@ struct xrdp_mm
int chan_trans_up; /* true once connected to chansrv */
int delete_chan_trans; /* boolean set when done with channel connection */
int usechansrv; /* true if chansrvport is set in xrdp.ini or using sesman */
+ char* login_username; /* RAPTOR */
struct xrdp_encoder *encoder;
int cs2xr_cid_map[256];
int xr2cr_cid_map[256];