summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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];