Respect XDG and dynamically find steamclient.so

XDG Base Directory Specification is now followed.
See globals.h for details.

With XDG respected, steamclient.so is dynamically
detected with a best-effort approach. SamRewritten
must dynamically hook into it at runtime, and must
use the running version, so now the git repo doesn't
need to be updated everytime steamclient.so updates.

Using the same steamclient.so for both app detection
and game launching causes stale shared object state to be
present in the child upon fork, so explictly unload the
parent's steamclient.so so we can get a clean one.

32bit and SteamBeta detection is not implemented at
this time, but can be in the future if there is interest.

Misc:
Disallow running as root
Wrap directory creation
Move most directory creation to main()
Fix some typos
This commit is contained in:
William Pierce 2019-09-24 23:23:31 -07:00
parent 799edb845f
commit c145c38567
11 changed files with 93 additions and 42 deletions

Binary file not shown.

View File

@ -1,5 +1,6 @@
#include "GameServerManager.h"
#include "MyGameServer.h"
#include "MySteamClient.h"
// TODO: shouldn't really need MySteam inside here
#include "MySteam.h"
@ -20,6 +21,12 @@ GameServerManager::quick_server_create(AppId_t appid)
if((pid = fork()) == 0) {
// Son's process: server role
// We need to delete this to unload steamclient.so from the
// child's process map, otherwise we pick up on a steam client
// state from the app detection stage that doesn't have an appid
// associated with it.
delete g_steamclient;
MyGameServer server(appid);
server.run();

View File

@ -13,7 +13,7 @@ MyGameServer::run()
if (!SteamAPI_Init()) {
std::cerr << "An error occurred launching the Steam API. Aborting." << std::endl;
std::cerr << "Make sure you are trying to run an app you own, and running with lauch.sh" << std::endl;
std::cerr << "Make sure you are trying to run an app you own, and running with launch.sh" << std::endl;
exit(EXIT_FAILURE);
}

View File

@ -86,11 +86,6 @@ MySteam::quit_game() {
*/
void
MySteam::refresh_owned_apps() {
/*
Hypothesis: the steamclient.so file must be from the same version than the currently installed
Steam version.
*/
if(m_owned_games_lock.try_lock()) {
Game_t game;
SteamAppDAO* appDAO = SteamAppDAO::get_instance();
@ -124,18 +119,28 @@ MySteam::refresh_owned_apps() {
* Tries to locate the steam folder in multiple locations,
* which is not a failsafe implementation.
*
* The original steamclient library path is tthe returned path + "/linux64/steamclient.so"
* The original steamclient library path is the returned path + "/linux64/steamclient.so"
*/
std::string
std::string
MySteam::get_steam_install_path() {
static const std::string home_path(getenv("HOME"));
if(file_exists(home_path + "/.local/share/Steam/appcache/appinfo.vdf")) {
return std::string(home_path + "/.local/share/Steam");
std::string data_home_path;
if (getenv("XDG_DATA_HOME") != NULL) {
data_home_path = getenv("XDG_DATA_HOME");
} else {
data_home_path = getenv("HOME") + std::string("/.local/share");
}
else if(file_exists(home_path + "/.steam/appcache/appinfo.vdf")) {
if (file_exists(data_home_path + "/Steam/appcache/appinfo.vdf")) {
return std::string(data_home_path + "/Steam");
}
std::cerr << "Trying to find Steam install with legacy Steam paths" << std::endl;
const std::string home_path = getenv("HOME");
if (file_exists(home_path + "/.steam/appcache/appinfo.vdf")) {
return std::string(home_path + "/.steam");
}
else if(file_exists(home_path + "/.steam/steam/appcache/appinfo.vdf")) {
else if (file_exists(home_path + "/.steam/steam/appcache/appinfo.vdf")) {
return std::string(home_path + "/.steam/steam");
}
else {

View File

@ -2,8 +2,9 @@
#include <iostream>
#include <dlfcn.h>
#include "../steam/steam_api.h"
#include "MySteam.h"
#define STEAM_CLIENT_LIB_PATH "steamclient.so"
#define RELATIVE_STEAM_CLIENT_LIB_PATH "/linux64/steamclient.so"
/**
* Purpose:
@ -39,20 +40,21 @@ public:
m_steamclient->BShutdownIfAllPipesClosed();
dlclose(m_handle);
}
MySteamClient() {
MySteamClient() {
char* error;
m_handle = dlopen(STEAM_CLIENT_LIB_PATH, RTLD_LAZY);
const std::string steam_client_lib_path = MySteam::get_steam_install_path() + RELATIVE_STEAM_CLIENT_LIB_PATH;
m_handle = dlopen(steam_client_lib_path.c_str(), RTLD_LAZY);
if (!m_handle) {
std::cerr << "Error opening the Steam Client library. Exitting. Info:" << std::endl;
std::cerr << "Error opening the Steam Client library. Exiting. Info:" << std::endl;
std::cerr << dlerror() << std::endl;
exit(EXIT_FAILURE);
}
ISteamClient* (*CreateInterface)(const char* version, void*);
CreateInterface = (ISteamClient* (*)(const char*, void*))dlsym(m_handle, "CreateInterface");
if ((error = dlerror()) != NULL) {
std::cerr << "Error reading the CreateInterface symbol from the Steam Client library. Exitting. Info:" << std::endl;
std::cerr << "Error reading the CreateInterface symbol from the Steam Client library. Exiting. Info:" << std::endl;
std::cerr << error << std::endl;
exit(EXIT_FAILURE);
}

View File

@ -45,14 +45,6 @@ SteamAppDAO::update_name_database() {
static const char* local_file_name = concat(g_cache_folder, "/app_names");
const std::time_t current_time(std::time(0));
// Make sure the cache folder is there
const int mkdir_error = mkdir(g_cache_folder, S_IRWXU | S_IRWXG | S_IROTH);
if(mkdir_error != 0 && errno != EEXIST) {
std::cerr << "Unable to create the cache folder ( ~/.SamRewritten/, errno " << errno << ")." << std::endl;
std::cerr << "Don't tell me you're running this as root.." << std::endl;
exit(EXIT_FAILURE);
}
// Check if the file is already there
if (file_exists(local_file_name)) {
//Check the last time it was updated
@ -70,7 +62,7 @@ SteamAppDAO::update_name_database() {
}
}
else {
std::cerr << "~/.SamRewritten/app_names exists but an error occurred analyzing it. To avoid further complications, ";
std::cerr << "~/.cache/SamRewritten/app_names exists but an error occurred analyzing it. To avoid further complications, ";
std::cerr << "the program will stop here. Before retrying make sure you have enough privilege to read and write to ";
std::cerr << "your home folder folder." << std::endl;
@ -99,11 +91,7 @@ SteamAppDAO::download_app_icon(const AppId_t& app_id) {
const std::string local_path(local_folder + "/banner");
const std::string url("http://cdn.akamai.steamstatic.com/steam/apps/" + std::to_string(app_id) + "/header_292x136.jpg");
const int mkdir_error = mkdir(local_folder.c_str(), S_IRWXU | S_IRWXG | S_IROTH);
if(mkdir_error != 0 && errno != EEXIST) {
std::cerr << "Unable to create the cache folder (" << local_folder << ", errno " << errno << ")." << std::endl;
exit(EXIT_FAILURE);
}
mkdir_default(local_folder.c_str());
Downloader::get_instance()->download_file_async(url, local_path, app_id);
}

View File

@ -60,7 +60,6 @@ char* concat(const char *s1, const char *s2)
return result;
}
bool strstri(const std::string & strHaystack, const std::string & strNeedle)
{
auto it = std::search(
@ -70,3 +69,12 @@ bool strstri(const std::string & strHaystack, const std::string & strNeedle)
);
return (it != strHaystack.end() );
}
void mkdir_default(const char *pathname)
{
int mkdir_error = mkdir(pathname, S_IRWXU | S_IRWXG | S_IROTH);
if (mkdir_error != 0 && errno != EEXIST) {
std::cerr << "Unable to create the folder " << pathname << ", errno " << errno << ")." << std::endl;
exit(EXIT_FAILURE);
}
}

View File

@ -31,3 +31,9 @@ char* concat(const char *s1, const char *s2);
* Insensitive "string in string"
*/
bool strstri(const std::string & strHaystack, const std::string & strNeedle);
/**
* Make a directory with reasonable defaults
* or fail hard on error
*/
void mkdir_default(const char *pathname);

View File

@ -31,12 +31,26 @@ extern MainPickerWindow *g_main_gui;
/**
* The absolute path to the cache folder. It's global, because everyone will
* need to write or read into this folder at some point.
* As of right now the default path is:
*
* ~/.SamRewritten
* XDG Base Directory Specification is followed:
* If present, this variable uses XDG_CACHE_HOME and takes the value
* $XDG_CACHE_HOME/SamRewritten
* Otherwise, this variable properly defaults to
* ~/.cache/SamRewritten
*/
extern char *g_cache_folder;
/**
* The absolute path to the runtime folder. It's global, because everyone will
* need to write or read into this folder at some point. It's used for storing
* sockets used by the program.
* XDG Base Directory Specification is followed:
* If present, this variable uses XDG_RUNTIME_DIR and takes the value
* $XDG_RUNTIME_DIR/SamRewritten
* Warnings are properly issued if it is not set, but still defaults to the
* cache folder for simplicity
*/
extern char *g_runtime_folder;
/**
* A basic performance monitor
*/

View File

@ -5,6 +5,7 @@
#include <iostream>
#include <gmodule.h>
#include <sys/stat.h>
#include "MySteam.h"
#include "MySteamClient.h"
#include "MyGameServer.h"
@ -20,6 +21,7 @@
MySteam* g_steam = nullptr;
MainPickerWindow* g_main_gui = nullptr;
char* g_cache_folder = nullptr;
char* g_runtime_folder = nullptr;
MySteamClient* g_steamclient = nullptr;
PerfMon* g_perfmon = nullptr;
@ -31,16 +33,35 @@ int
main(int argc, char *argv[])
{
// Test if glib2 is installed, gtk will not work without it.
if( !g_module_supported() ) {
if ( !g_module_supported() ) {
std::cout << "You are missing the Gnome libraries. Please read the README to know which libraries to install." << std::endl;
exit(EXIT_FAILURE);
}
if (getuid() == 0) {
std::cout << "Do not run this application as root" << std::endl;
exit(EXIT_FAILURE);
}
g_perfmon = new PerfMon();
gtk_init(&argc, &argv);
g_steamclient = new MySteamClient();
g_cache_folder = concat( getenv("HOME"), "/.SamRewritten" );
if (getenv("XDG_CACHE_HOME")) {
g_cache_folder = concat( getenv("XDG_CACHE_HOME"), "/SamRewritten" );
} else {
g_cache_folder = concat( getenv("HOME"), "/.cache/SamRewritten" );
}
mkdir_default(g_cache_folder);
if (getenv("XDG_RUNTIME_DIR")) {
g_runtime_folder = concat( getenv("XDG_RUNTIME_DIR"), "/SamRewritten" );
mkdir_default(g_runtime_folder);
} else {
std::cerr << "XDG_RUNTIME_DIR is not set! Your distribution is improper... falling back to cache dir" << std::endl;
g_runtime_folder = g_cache_folder;
}
g_steam = MySteam::get_instance();
g_main_gui = new MainPickerWindow();
g_perfmon->log("Globals initialized.");

View File

@ -6,8 +6,8 @@
#include <iostream>
MySocket::MySocket(AppId_t appid) : m_appid(appid), m_socket_fd(-1)
{
m_socket_path = std::string(g_cache_folder) + "/" + std::to_string(m_appid) + "/ipc.sock";
{
m_socket_path = std::string(g_runtime_folder) + "/" + std::to_string(m_appid) + "-ipc.sock";
}
MySocket::~MySocket()