cli: set user-id per command line

This commit is contained in:
Megamouse 2021-04-02 00:54:32 +02:00
parent 98687d474b
commit 9c7230e79f
11 changed files with 165 additions and 119 deletions

View File

@ -503,24 +503,31 @@ namespace
};
}
bool Emulator::SetUsr(const std::string& user)
u32 Emulator::CheckUsr(const std::string& user)
{
if (user.empty())
u32 id = 0;
if (user.size() == 8)
{
return false;
std::from_chars(&user.front(), &user.back() + 1, id);
}
u32 id = 0;
std::from_chars(&user.front(), &user.back() + 1, id);
return id;
}
void Emulator::SetUsr(const std::string& user)
{
sys_log.notice("Setting user ID '%s'", user);
const u32 id = CheckUsr(user);
if (id == 0)
{
return false;
fmt::throw_exception("Failed to set user ID '%s'", user);
}
m_usrid = id;
m_usr = user;
return true;
}
std::string Emulator::GetBackgroundPicturePath() const
@ -2015,7 +2022,14 @@ bool Emulator::Quit(bool force_quit)
{
Emu.CleanUp();
};
return GetCallbacks().try_to_quit(force_quit, on_exit);
if (GetCallbacks().try_to_quit)
{
return GetCallbacks().try_to_quit(force_quit, on_exit);
}
on_exit();
return true;
}
void Emulator::CleanUp()

View File

@ -196,7 +196,9 @@ public:
return m_usrid;
}
bool SetUsr(const std::string& user);
static u32 CheckUsr(const std::string& user);
void SetUsr(const std::string& user);
std::string GetBackgroundPicturePath() const;

View File

@ -16,7 +16,7 @@ headless_application::headless_application(int& argc, char** argv) : QCoreApplic
bool headless_application::Init()
{
// Force init the emulator
InitializeEmulator("00000001", true, false); // TODO: get user from cli args if possible
InitializeEmulator(m_active_user.empty() ? "00000001" : m_active_user, false);
// Create callbacks from the emulator, which reference the handlers.
InitializeCallbacks();
@ -24,7 +24,7 @@ bool headless_application::Init()
// Create connects to propagate events throughout Gui.
InitializeConnects();
// As per QT recommendations to avoid conflicts for POSIX functions
// As per Qt recommendations to avoid conflicts for POSIX functions
std::setlocale(LC_NUMERIC, "C");
return true;

View File

@ -208,6 +208,7 @@ constexpr auto arg_config = "config";
constexpr auto arg_q_debug = "qDebug";
constexpr auto arg_error = "error";
constexpr auto arg_updating = "updating";
constexpr auto arg_user_id = "user-id";
constexpr auto arg_installfw = "installfw";
constexpr auto arg_installpkg = "installpkg";
constexpr auto arg_commit_db = "get-commit-db";
@ -521,6 +522,8 @@ int main(int argc, char** argv)
parser.addOption(installfw_option);
const QCommandLineOption installpkg_option(arg_installpkg, "Forces the emulator to install this pkg file.", "path", "");
parser.addOption(installpkg_option);
const QCommandLineOption user_id_option(arg_user_id, "Start RPCS3 as this user.", "user id", "");
parser.addOption(user_id_option);
parser.addOption(QCommandLineOption(arg_q_debug, "Log qDebug to RPCS3.log."));
parser.addOption(QCommandLineOption(arg_error, "For internal usage."));
parser.addOption(QCommandLineOption(arg_updating, "For internal usage."));
@ -716,6 +719,19 @@ int main(int argc, char** argv)
return 0;
}
std::string active_user;
if (parser.isSet(arg_user_id))
{
active_user = parser.value(arg_user_id).toStdString();
if (Emulator::CheckUsr(active_user) == 0)
{
report_fatal_error(fmt::format("Failed to set user ID '%s'.\nThe user ID must consist of 8 digits and cannot be 00000000.", active_user));
return 1;
}
}
s_no_gui = parser.isSet(arg_no_gui);
if (auto gui_app = qobject_cast<gui_application*>(app.data()))
@ -727,6 +743,7 @@ int main(int argc, char** argv)
gui_app->SetShowGui(!s_no_gui);
gui_app->SetUseCliStyle(use_cli_style);
gui_app->SetWithCliBoot(parser.isSet(arg_installfw) || parser.isSet(arg_installpkg) || !parser.positionalArguments().isEmpty());
gui_app->SetActiveUser(active_user);
if (!gui_app->Init())
{
@ -738,6 +755,8 @@ int main(int argc, char** argv)
{
s_headless = true;
headless_app->SetActiveUser(active_user);
if (!headless_app->Init())
{
Emu.Quit(true);
@ -774,7 +793,7 @@ int main(int argc, char** argv)
if (!fs::is_file(config_override_path))
{
report_fatal_error(fmt::format("No config file found: %s", config_override_path));
return 0;
return 1;
}
Emu.SetConfigOverride(config_override_path);

View File

@ -33,21 +33,12 @@
LOG_CHANNEL(sys_log, "SYS");
/** Emu.Init() wrapper for user manager */
bool main_application::InitializeEmulator(const std::string& user, bool force_init, bool show_gui)
/** Emu.Init() wrapper for user management */
void main_application::InitializeEmulator(const std::string& user, bool show_gui)
{
Emu.SetHasGui(show_gui);
// try to set a new user
const bool user_was_set = Emu.SetUsr(user);
// only init the emulation if forced or a user was set
if (user_was_set || force_init)
{
Emu.Init();
}
return user_was_set;
Emu.SetUsr(user);
Emu.Init();
}
/** RPCS3 emulator has functions it desires to call from the GUI at times. Initialize them in here. */

View File

@ -10,12 +10,18 @@ class main_application
public:
virtual bool Init() = 0;
static bool InitializeEmulator(const std::string& user, bool force_init, bool show_gui);
static void InitializeEmulator(const std::string& user, bool show_gui);
void SetActiveUser(std::string user)
{
m_active_user = user;
}
protected:
virtual QThread* get_thread() = 0;
EmuCallbacks CreateCallbacks();
std::string m_active_user;
QWindow* m_game_window = nullptr; // (Currently) only needed so that pad handlers have a valid target for event filtering.
};

View File

@ -63,14 +63,18 @@ bool gui_application::Init()
return false;
}
// Get deprecated active user (before August 2nd 2020)
QString active_user = m_gui_settings->GetValue(gui::um_active_user).toString();
// The user might be set by cli arg. If not, set another user.
if (m_active_user.empty())
{
// Get deprecated active user (before August 2nd 2020)
const QString active_user = m_gui_settings->GetValue(gui::um_active_user).toString();
// Get active user with deprecated active user as fallback
active_user = m_persistent_settings->GetCurrentUser(active_user.isEmpty() ? "00000001" : active_user);
// Get active user with deprecated active user as fallback
m_active_user = m_persistent_settings->GetCurrentUser(active_user.isEmpty() ? "00000001" : active_user).toStdString();
}
// Force init the emulator
InitializeEmulator(active_user.toStdString(), true, m_show_gui);
InitializeEmulator(m_active_user, m_show_gui);
// Create the main window
if (m_show_gui)

View File

@ -7,7 +7,7 @@
LOG_CHANNEL(gui_log, "GUI");
UserAccount::UserAccount(const std::string& user_id)
user_account::user_account(const std::string& user_id)
{
// Setting userId.
m_user_id = user_id;
@ -16,8 +16,7 @@ UserAccount::UserAccount(const std::string& user_id)
m_user_dir = Emulator::GetHddDir() + "home/" + m_user_id + "/";
// Setting userName.
fs::file file;
if (file.open(m_user_dir + "localusername", fs::read))
if (fs::file file; file.open(m_user_dir + "localusername", fs::read))
{
m_username = file.to_string();
file.close();
@ -25,15 +24,48 @@ UserAccount::UserAccount(const std::string& user_id)
if (m_username.length() > 16) // max of 16 chars on real PS3
{
m_username = m_username.substr(0, 16);
gui_log.warning("UserAccount: localusername of userId=%s was too long, cropped to: %s", m_user_id, m_username);
gui_log.warning("user_account: localusername of userId=%s was too long, cropped to: %s", m_user_id, m_username);
}
}
else
{
gui_log.error("UserAccount: localusername file read error (userId=%s, userDir=%s).", m_user_id, m_user_dir);
gui_log.error("user_account: localusername file read error (userId=%s, userDir=%s).", m_user_id, m_user_dir);
}
}
UserAccount::~UserAccount()
user_account::~user_account()
{
}
std::map<u32, user_account> user_account::GetUserAccounts(const std::string& base_dir)
{
std::map<u32, user_account> user_list;
// I believe this gets the folder list sorted alphabetically by default,
// but I can't find proof of this always being true.
for (const auto& user_folder : fs::dir(base_dir))
{
if (!user_folder.is_directory)
{
continue;
}
// Is the folder name exactly 8 all-numerical characters long?
const u32 key = Emulator::CheckUsr(user_folder.name);
if (key == 0)
{
continue;
}
// Does the localusername file exist?
if (!fs::is_file(base_dir + "/" + user_folder.name + "/localusername"))
{
continue;
}
user_list.emplace(key, user_account(user_folder.name));
}
return user_list;
}

View File

@ -1,20 +1,25 @@
#pragma once
#include "util/types.hpp"
#include <map>
#include <string>
// Do not confuse this with the "user" in Emu/System.h.
// That user is read from config.yml, and it only represents the currently "logged in" user.
// The UserAccount class will represent all users in the home directory for the User Manager dialog.
// The user_account class will represent all users in the home directory for the User Manager dialog.
// Selecting a user account in this dialog and saving writes it to config.yml.
class UserAccount
class user_account
{
public:
explicit UserAccount(const std::string& user_id = "00000001");
explicit user_account(const std::string& user_id = "00000001");
std::string GetUserId() { return m_user_id; }
std::string GetUserDir() { return m_user_dir; }
std::string GetUsername() { return m_username; }
~UserAccount();
std::string GetUserId() const { return m_user_id; }
std::string GetUserDir() const { return m_user_dir; }
std::string GetUsername() const { return m_username; }
~user_account();
static std::map<u32, user_account> GetUserAccounts(const std::string& base_dir);
private:
std::string m_user_id;

View File

@ -28,43 +28,6 @@ constexpr auto qstr = QString::fromStdString;
LOG_CHANNEL(gui_log, "GUI");
namespace
{
std::map<u32, UserAccount> GetUserAccounts(const std::string& base_dir)
{
std::map<u32, UserAccount> user_list;
// I believe this gets the folder list sorted alphabetically by default,
// but I can't find proof of this always being true.
for (const auto& user_folder : fs::dir(base_dir))
{
if (!user_folder.is_directory)
{
continue;
}
// Is the folder name exactly 8 all-numerical characters long?
// We use strtoul to find any non-numeric characters in folder name.
char* non_numeric_char;
const u32 key = static_cast<u32>(std::strtoul(user_folder.name.c_str(), &non_numeric_char, 10));
if (key == 0 || user_folder.name.length() != 8 || *non_numeric_char != '\0')
{
continue;
}
// Does the localusername file exist?
if (!fs::is_file(base_dir + "/" + user_folder.name + "/localusername"))
{
continue;
}
user_list.emplace(key, UserAccount(user_folder.name));
}
return user_list;
}
}
user_manager_dialog::user_manager_dialog(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget* parent)
: QDialog(parent)
, m_gui_settings(gui_settings)
@ -94,16 +57,16 @@ void user_manager_dialog::Init()
m_table->horizontalHeader()->setDefaultSectionSize(150);
m_table->installEventFilter(this);
QPushButton* push_remove_user = new QPushButton(tr("Delete User"), this);
QPushButton* push_remove_user = new QPushButton(tr("&Delete User"), this);
push_remove_user->setAutoDefault(false);
QPushButton* push_create_user = new QPushButton(tr("Create User"), this);
QPushButton* push_create_user = new QPushButton(tr("&Create User"), this);
push_create_user->setAutoDefault(false);
QPushButton* push_login_user = new QPushButton(tr("Log In User"), this);
QPushButton* push_login_user = new QPushButton(tr("&Log In User"), this);
push_login_user->setAutoDefault(false);
QPushButton* push_rename_user = new QPushButton(tr("Rename User"), this);
QPushButton* push_rename_user = new QPushButton(tr("&Rename User"), this);
push_rename_user->setAutoDefault(false);
QPushButton* push_close = new QPushButton(tr("&Close"), this);
@ -139,12 +102,18 @@ void user_manager_dialog::Init()
}
}
// Get the real active user (might differ, set by cli)
if (m_active_user != Emu.GetUsr())
{
m_active_user = Emu.GetUsr();
}
UpdateTable();
restoreGeometry(m_gui_settings->GetValue(gui::um_geometry).toByteArray());
// Use this in multiple connects to protect the current user from deletion/rename.
auto enableButtons = [=, this]()
const auto enable_buttons = [=, this]()
{
const u32 key = GetUserKey();
if (key == 0)
@ -162,7 +131,7 @@ void user_manager_dialog::Init()
push_remove_user->setEnabled(enable);
};
enableButtons();
enable_buttons();
// Connects and events
connect(push_close, &QAbstractButton::clicked, this, &user_manager_dialog::close);
@ -170,11 +139,11 @@ void user_manager_dialog::Init()
connect(push_rename_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserRename);
connect(push_create_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserCreate);
connect(push_login_user, &QAbstractButton::clicked, this, &user_manager_dialog::OnUserLogin);
connect(this, &user_manager_dialog::OnUserLoginSuccess, this, enableButtons);
connect(this, &user_manager_dialog::OnUserLoginSuccess, this, enable_buttons);
connect(m_table->horizontalHeader(), &QHeaderView::sectionClicked, this, &user_manager_dialog::OnSort);
connect(m_table, &QTableWidget::customContextMenuRequested, this, &user_manager_dialog::ShowContextMenu);
connect(m_table, &QTableWidget::itemDoubleClicked, this, &user_manager_dialog::OnUserLogin);
connect(m_table, &QTableWidget::itemSelectionChanged, this, enableButtons);
connect(m_table, &QTableWidget::itemSelectionChanged, this, enable_buttons);
}
void user_manager_dialog::UpdateTable(bool mark_only)
@ -185,7 +154,7 @@ void user_manager_dialog::UpdateTable(bool mark_only)
if (mark_only)
{
QString active_user = qstr(m_active_user);
const QString active_user = qstr(m_active_user);
for (int i = 0; i < m_table->rowCount(); i++)
{
@ -209,26 +178,26 @@ void user_manager_dialog::UpdateTable(bool mark_only)
// Get the user folders in the home directory and the currently logged in user.
m_user_list.clear();
m_user_list = GetUserAccounts(Emulator::GetHddDir() + "home");
m_user_list = user_account::GetUserAccounts(Emulator::GetHddDir() + "home");
// Clear and then repopulate the table with the list gathered above.
m_table->setRowCount(static_cast<int>(m_user_list.size()));
int row = 0;
for (auto& user : m_user_list)
for (auto& [id, account] : m_user_list)
{
QTableWidgetItem* user_id_item = new QTableWidgetItem(qstr(user.second.GetUserId()));
user_id_item->setData(Qt::UserRole, user.first); // For sorting to work properly
QTableWidgetItem* user_id_item = new QTableWidgetItem(qstr(account.GetUserId()));
user_id_item->setData(Qt::UserRole, id); // For sorting to work properly
user_id_item->setFlags(user_id_item->flags() & ~Qt::ItemIsEditable);
m_table->setItem(row, 0, user_id_item);
QTableWidgetItem* username_item = new QTableWidgetItem(qstr(user.second.GetUsername()));
username_item->setData(Qt::UserRole, user.first); // For sorting to work properly
QTableWidgetItem* username_item = new QTableWidgetItem(qstr(account.GetUsername()));
username_item->setData(Qt::UserRole, id); // For sorting to work properly
username_item->setFlags(username_item->flags() & ~Qt::ItemIsEditable);
m_table->setItem(row, 1, username_item);
// Compare current config value with the one in this user (only 8 digits in userId)
if (m_active_user.starts_with(user.second.GetUserId()))
if (m_active_user.starts_with(account.GetUserId()))
{
user_id_item->setFont(bold_font);
username_item->setFont(bold_font);
@ -240,12 +209,12 @@ void user_manager_dialog::UpdateTable(bool mark_only)
m_table->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
m_table->verticalHeader()->resizeSections(QHeaderView::ResizeToContents);
QSize table_size = QSize(
const QSize table_size(
m_table->verticalHeader()->width() + m_table->horizontalHeader()->length() + m_table->frameWidth() * 2,
m_table->horizontalHeader()->height() + m_table->verticalHeader()->length() + m_table->frameWidth() * 2);
QSize preferred_size = minimumSize().expandedTo(sizeHint() - m_table->sizeHint() + table_size).expandedTo(size());
QSize max_size = QSize(preferred_size.width(), static_cast<int>(QGuiApplication::primaryScreen()->size().height() * 0.6));
const QSize preferred_size = minimumSize().expandedTo(sizeHint() - m_table->sizeHint() + table_size).expandedTo(size());
const QSize max_size(preferred_size.width(), static_cast<int>(QGuiApplication::primaryScreen()->size().height() * 0.6));
resize(preferred_size.boundedTo(max_size));
}
@ -253,7 +222,7 @@ void user_manager_dialog::UpdateTable(bool mark_only)
// Remove a user folder, needs to be confirmed.
void user_manager_dialog::OnUserRemove()
{
u32 key = GetUserKey();
const u32 key = GetUserKey();
if (key == 0)
{
return;
@ -274,6 +243,8 @@ void user_manager_dialog::OnUserRemove()
void user_manager_dialog::GenerateUser(const std::string& user_id, const std::string& username)
{
ensure(Emulator::CheckUsr(user_id) > 0);
// Create user folders and such.
const std::string home_dir = Emulator::GetHddDir() + "home/";
const std::string user_dir = home_dir + user_id;
@ -298,7 +269,7 @@ bool user_manager_dialog::ValidateUsername(const QString& text_to_validate)
void user_manager_dialog::OnUserRename()
{
u32 key = GetUserKey();
const u32 key = GetUserKey();
if (key == 0)
{
return;
@ -318,7 +289,7 @@ void user_manager_dialog::OnUserRename()
{
dialog->resize(200, 100);
QString text_to_validate = dialog->textValue();
const QString text_to_validate = dialog->textValue();
if (!ValidateUsername(text_to_validate))
{
QMessageBox::warning(this, tr("Error"), tr("Name must be between 3 and 16 characters and only consist of letters, numbers, underscores, and hyphens."));
@ -359,7 +330,14 @@ void user_manager_dialog::OnUserCreate()
}
}
if (smallest >= 100000000) // Only 8 digits allowed
{
QMessageBox::warning(this, tr("Error"), tr("Cannot add more users."));
return;
}
const std::string next_user_id = fmt::format("%08d", smallest);
ensure(Emulator::CheckUsr(next_user_id) > 0);
QInputDialog* dialog = new QInputDialog(this);
dialog->setWindowTitle(tr("New User"));
@ -397,11 +375,7 @@ void user_manager_dialog::OnUserLogin()
const u32 key = GetUserKey();
const std::string new_user = m_user_list[key].GetUserId();
if (!main_application::InitializeEmulator(new_user, false, Emu.HasGui()))
{
gui_log.fatal("Failed to login user! username=%s key=%d", new_user, key);
return;
}
main_application::InitializeEmulator(new_user, Emu.HasGui());
m_active_user = new_user;
m_persistent_settings->SetValue(gui::persistent::active_user, qstr(m_active_user));
@ -429,7 +403,7 @@ void user_manager_dialog::OnSort(int logicalIndex)
void user_manager_dialog::ShowContextMenu(const QPoint &pos)
{
u32 key = GetUserKey();
const u32 key = GetUserKey();
if (key == 0)
{
return;
@ -448,8 +422,7 @@ void user_manager_dialog::ShowContextMenu(const QPoint &pos)
QAction* show_dir_act = menu->addAction(tr("&Open User Directory"));
// Only enable actions if selected user is not logged in user.
std::string idx_user = m_user_list[key].GetUserId();
bool enable = idx_user != m_active_user;
const bool enable = m_user_list[key].GetUserId() != m_active_user;
remove_act->setEnabled(enable);
rename_act->setEnabled(enable);
@ -458,14 +431,14 @@ void user_manager_dialog::ShowContextMenu(const QPoint &pos)
connect(remove_act, &QAction::triggered, this, &user_manager_dialog::OnUserRemove);
connect(rename_act, &QAction::triggered, this, &user_manager_dialog::OnUserRename);
connect(login_act, &QAction::triggered, this, &user_manager_dialog::OnUserLogin);
connect(show_dir_act, &QAction::triggered, [=, this]()
connect(show_dir_act, &QAction::triggered, this, [this, key]()
{
QString path = qstr(m_user_list[key].GetUserDir());
const QString path = qstr(m_user_list[key].GetUserDir());
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});
connect(user_id_act, &QAction::triggered, this, [=, this] {OnSort(0); });
connect(username_act, &QAction::triggered, this, [=, this] {OnSort(1); });
connect(user_id_act, &QAction::triggered, this, [this] {OnSort(0); });
connect(username_act, &QAction::triggered, this, [this] {OnSort(1); });
menu->exec(m_table->viewport()->mapToGlobal(pos));
}
@ -473,20 +446,20 @@ void user_manager_dialog::ShowContextMenu(const QPoint &pos)
// Returns the current user's key > 0. if no user is selected, return 0
u32 user_manager_dialog::GetUserKey()
{
int idx = m_table->currentRow();
const int idx = m_table->currentRow();
if (idx < 0)
{
return 0;
}
QTableWidgetItem* item = m_table->item(idx, 0);
const QTableWidgetItem* item = m_table->item(idx, 0);
if (!item)
{
return 0;
}
const u32 idx_real = item->data(Qt::UserRole).toUInt();
if (m_user_list.find(idx_real) == m_user_list.end())
if (!m_user_list.contains(idx_real))
{
return 0;
}

View File

@ -40,7 +40,7 @@ private:
QTableWidget* m_table;
std::string m_active_user;
std::map<u32, UserAccount> m_user_list;
std::map<u32, user_account> m_user_list;
std::shared_ptr<gui_settings> m_gui_settings;
std::shared_ptr<persistent_settings> m_persistent_settings;