Implement Polkit agent for platforms that lack them (#244)

* attempt to set up polkit listener

* implement polkit agent functionality

* finalized polkit implementation

* add warning for installing polkit rule

* docker: install libpolkit-agent-1-dev

* add uninstall polkit option

---------

Co-authored-by: MattKC <mattkc@noreply.git.mattkc.com>
This commit is contained in:
MattKCandMattKC authored and GitHub committed 2025-10-03 16:25:00 -07:00
1 parent 6bf8d4898b
commit 61e2a91028
30 files changed
+894 -260

No files matched your search

-1
View File
@@ -23,7 +23,6 @@ OPTION(VANILLA_BUILD_GUI "Build SDL2 GUI app" ON)
OPTION(VANILLA_BUILD_TESTS "Build unit tests for Vanilla" OFF)
OPTION(VANILLA_BUILD_PIPE "Build vanilla-pipe for connecting to Wii U (Linux only)" ${LINUX})
OPTION(VANILLA_BUILD_VENDORED "Build Vanilla with \"vendored\" third-party libraries" ${vendored_default})
OPTION(VANILLA_INSTALL_POLKIT_RULE "Install vanilla-pipe with Polkit rule to allow use without entering password" ON)
add_subdirectory(lib)
if (VANILLA_BUILD_PIPE)
+2 -1
View File
@@ -45,7 +45,8 @@ RUN if [ "${ARCH}" = "aarch64" ]; then export DEBARCH="arm64"; export DEBGCC="gc
&& apt -y install libice-dev:$DEBARCH \
&& apt -y install libgbm-dev:$DEBARCH \
&& apt -y install libva-dev:$DEBARCH \
&& apt -y install libxkbcommon-dev:$DEBARCH
&& apt -y install libxkbcommon-dev:$DEBARCH \
&& apt -y install libpolkit-agent-1-dev:$DEBARCH
# Install newer version of CMake
ENV CMAKE_VER=3.31.5
+12 -2
View File
@@ -251,6 +251,8 @@ list(APPEND VANILLA_GUI_SRC
menu/menu_region.c
menu/menu_rename.c
menu/menu_settings.c
menu/menu_sudo.c
menu/menu_sudo_warning.c
menu/menu_sync.c
pipemgmt.c
platform.c
@@ -318,10 +320,18 @@ if (APPLE)
target_link_libraries(vanilla PRIVATE "-framework VideoToolbox" "-framework CoreMedia" iconv)
endif()
if (LINUX)
# Assume this platform is capable of running vanilla-pipe
if (VANILLA_BUILD_PIPE)
# Assume this platform is capable of running vanilla-pipe
target_compile_definitions(vanilla PRIVATE VANILLA_PIPE_AVAILABLE)
# Find Polkit and GLIB libraries
find_package(PkgConfig)
pkg_check_modules(PC_POLKIT polkit-agent-1 REQUIRED IMPORTED_TARGET)
target_include_directories(vanilla PRIVATE ${PC_POLKIT_INCLUDE_DIRS})
target_link_libraries(vanilla PRIVATE ${PC_POLKIT_LIBRARIES})
endif()
if (LINUX)
# Link with DRM if available
find_package(DRM)
if (DRM_FOUND)
+7
View File
@@ -45,6 +45,13 @@ static const char *lang_str[__VPI_LANG_T_COUNT] = {
"America",
"Europe",
"Quit",
"Authentication failed. Please try again.",
"Don't ask again",
"WARNING",
"This option will install a Polkit rule that will allow Vanilla's backend to run as root without password entry.\n\nThere are inherent risks to bypassing security systems. Only enable this if you are willing to accept those risks.\n\nNOTE: If you are using the Steam Deck, this will also disable read-only access to your system partition.",
"I acknowledge",
"Enable Root Password Skip",
"Disable Root Password Skip"
};
const char *lang(vpi_lang_t id)
+7
View File
@@ -46,6 +46,13 @@ typedef enum {
VPI_LANG_REGION_AMERICA,
VPI_LANG_REGION_EUROPE,
VPI_LANG_QUIT,
VPI_LANG_AUTH_FAIL,
VPI_LANG_AUTH_DONT_ASK_AGAIN,
VPI_LANG_AUTH_WARNING,
VPI_LANG_AUTH_WARNING_MESSAGE,
VPI_LANG_AUTH_WARNING_ACKNOWLEDGE,
VPI_LANG_ENABLE_PASSWORD_SKIP,
VPI_LANG_DISABLE_PASSWORD_SKIP,
__VPI_LANG_T_COUNT
} vpi_lang_t;
+1
View File
@@ -59,6 +59,7 @@ int SDL_main(int argc, const char **argv)
vpi_menu_init(vui);
while (vui_update_sdl(vui)) {
vpi_update_pipe();
}
ret = 0;
+5 -2
View File
@@ -6,6 +6,7 @@
#include "lang.h"
#include "menu_connection.h"
#include "menu_main.h"
#include "menu_sudo.h"
#include "menu_sync.h"
#include "pipemgmt.h"
#include "ui/ui_anim.h"
@@ -23,7 +24,7 @@ void vpi_menu_create_background(vui_context_t *vui, int layer, vui_rect_t *bkg_r
bkg_rect->y = m;
bkg_rect->w = scrw-m-m;
bkg_rect->h = scrh-m-m;
vui_rect_create(vui, bkg_rect->x, bkg_rect->y, bkg_rect->w, bkg_rect->h, scrh*1/10, vui_color_create(0, 0, 0, 0.66f), layer);
}
@@ -78,6 +79,8 @@ void vpi_menu_start_pipe(vui_context_t *vui, int fade_fglayer, vui_callback_t su
int r = vpi_start_pipe();
if (r == VANILLA_SUCCESS) {
success_action(vui, success_data);
} else if (r == VANILLA_REQUIRES_PASSWORD_HANDLING) {
vpi_menu_sudo(vui, success_action, success_data, cancel_action, cancel_data, fade_fglayer);
} else {
if (cancel_action) {
cancel_action(vui, cancel_data);
@@ -132,4 +135,4 @@ static void do_quit(vui_context_t *vui, void *v)
void vpi_menu_quit_vanilla(vui_context_t *vui)
{
vui_transition_fade_black_in(vui, do_quit, 0);
}
}
+5 -5
View File
@@ -142,7 +142,7 @@ static void local_connection_menu(vui_context_t *vui, void *v)
update_wireless_buttons(vui);
vpi_menu_create_back_button(vui, fglayer, return_to_connection, (void *) (intptr_t) fglayer);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
@@ -193,11 +193,11 @@ static void via_server_connection_menu(vui_context_t *vui, void *v)
ip_text = inet_ntoa(in);
}
ip_textedit = vui_textedit_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 3 / 7, textedit_w, textedit_h, ip_text, VUI_FONT_SIZE_NORMAL, fglayer);
ip_textedit = vui_textedit_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 3 / 7, textedit_w, textedit_h, ip_text, VUI_FONT_SIZE_NORMAL, 0, fglayer);
error_lbl = vui_label_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 4 / 7, textedit_w, BTN_SZ, "Invalid IP address", vui_color_create(1,0,0,1), VUI_FONT_SIZE_SMALL, fglayer);
vui_label_update_visible(vui, error_lbl, 0);
vui_button_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 5 / 7, textedit_w, BTN_SZ, lang(VPI_LANG_OK_BTN), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, check_ip_address, (void *) (intptr_t) fglayer);
vpi_menu_create_back_button(vui, fglayer, return_to_connection, (void *) (intptr_t) fglayer);
@@ -259,7 +259,7 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
// Back button
vpi_menu_create_back_button(vui, fglayer, return_to_settings, (void *) (intptr_t) bglayer);
vui_transition_fade_layer_in(vui, fade_fglayer ? fglayer : bglayer, 0, 0);
g_continue_callback = success_callback;
@@ -271,4 +271,4 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
void vpi_menu_connection(vui_context_t *vui, void *v)
{
vpi_menu_connection_and_return_to(vui, (intptr_t) v, 0, 0, 0, 0);
}
}
+2 -2
View File
@@ -41,7 +41,7 @@ void vpi_menu_rename(vui_context_t *vui, void *v)
const int textedit_w = bkg_rect.w / 2;
const int textedit_h = vui_get_font_height(vui, VUI_FONT_SIZE_NORMAL);
textedit = vui_textedit_create(vui, bkg_rect.x + bkg_rect.w/2 - textedit_w/2, bkg_rect.y + bkg_rect.h * 1 / 3, textedit_w, textedit_h, vpi_config.connected_console_entries[console].name, VUI_FONT_SIZE_NORMAL, fglayer);
textedit = vui_textedit_create(vui, bkg_rect.x + bkg_rect.w/2 - textedit_w/2, bkg_rect.y + bkg_rect.h * 1 / 3, textedit_w, textedit_h, vpi_config.connected_console_entries[console].name, VUI_FONT_SIZE_NORMAL, 0, fglayer);
static const int ok_btn_w = BTN_SZ * 3;
static const int cancel_btn_w = BTN_SZ * 3;
@@ -50,4 +50,4 @@ void vpi_menu_rename(vui_context_t *vui, void *v)
vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - (ok_btn_w + cancel_btn_w)/2 + ok_btn_w, bkg_rect.y + bkg_rect.h * 3 / 4, cancel_btn_w, BTN_SZ, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, vpi_menu_rename_return_to_edit, v);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
}
+83 -28
View File
@@ -1,11 +1,17 @@
#include "menu_settings.h"
#include <vanilla.h>
#include <unistd.h>
#include "config.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_connection.h"
#include "menu_gamepad.h"
#include "menu_main.h"
#include "menu_region.h"
#include "menu_sudo_warning.h"
#include "pipe/def.h"
#include "ui/ui_anim.h"
static void return_to_main(vui_context_t *vui, int btn, void *v)
@@ -37,56 +43,105 @@ static void thunk_to_quit(vui_context_t *vui, int button, void *v)
vpi_menu_quit_vanilla(vui);
}
static void do_polkit_install(vui_context_t *vui, void *v)
{
int install = (intptr_t) v;
if (install) {
vanilla_install_polkit(vpi_config.server_address);
} else {
vanilla_uninstall_polkit(vpi_config.server_address);
}
vpi_menu_settings(vui, 0);
}
static void thunk_to_start_pipe_for_polkit_rule_install(vui_context_t *vui, void *v)
{
vpi_menu_start_pipe(vui, 0, do_polkit_install, v, 0, 0);
}
int tmp_fglayer;
static void install_polkit_rule_ack(vui_context_t *vui, int button, void *v)
{
vui_transition_fade_layer_out(vui, tmp_fglayer, thunk_to_start_pipe_for_polkit_rule_install, v);
}
static void install_polkit_rule_cancel(vui_context_t *vui, int button, void *v)
{
vui_transition_fade_layer_out(vui, tmp_fglayer, vpi_menu_settings, 0);
}
static void install_polkit_rule(vui_context_t *vui, void *v)
{
vui_reset(vui);
tmp_fglayer = vui_layer_create(vui);
vpi_menu_create_sudo_warning(vui, tmp_fglayer, install_polkit_rule_ack, v, install_polkit_rule_cancel, 0);
vui_transition_fade_layer_in(vui, tmp_fglayer, 0, 0);
}
static void transition_to_install_polkit_rule(vui_context_t *vui, int button, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, install_polkit_rule, (void *) (intptr_t) 1);
}
static void transition_to_uninstall_polkit_rule(vui_context_t *vui, int button, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, thunk_to_start_pipe_for_polkit_rule_install, 0);
}
void vpi_menu_settings(vui_context_t *vui, void *v)
{
vui_reset(vui);
int fglayer = vui_layer_create(vui);
uint8_t console = (uintptr_t) v;
int scrw, scrh;
vui_get_screen_size(vui, &scrw, &scrh);
static const int SETTINGS_NAMES[] = {
const int PW_SKIP_SETTING = 3;
static int SETTINGS_NAMES[] = {
VPI_LANG_CONNECTION,
VPI_LANG_GAMEPAD,
VPI_LANG_REGION,
VPI_LANG_QUIT,
-1,
};
// Highlight currently implemented functionality
static const int SETTINGS_ENABLED[] = {
1,
1,
1,
1,
};
// Highlight currently implemented functionality
static const vui_button_callback_t SETTINGS_ACTION[] = {
static vui_button_callback_t SETTINGS_ACTION[] = {
transition_to_connection,
transition_to_gamepad,
transition_to_region,
thunk_to_quit,
0,
};
const int settings_cols = 2;
const int settings_rows = 2;
const int settings_w = scrw/3;
const int settings_h = scrh/3;
const int settings_x = scrw/2 - (settings_w*settings_cols)/2;
const int settings_y = scrh/2 - (settings_h*settings_rows)/2;
for (int y = 0; y < settings_rows; y++) {
for (int x = 0; x < settings_cols; x++) {
int index = y * settings_cols + x;
int b = vui_button_create(vui, settings_x + settings_w * x, settings_y + settings_h * y, settings_w, settings_h, lang(SETTINGS_NAMES[index]), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, SETTINGS_ACTION[index], (void *) (intptr_t) fglayer);
vui_button_update_enabled(vui, b, SETTINGS_ENABLED[index]);
}
}
#ifdef VANILLA_PIPE_AVAILABLE
int pw_skip_str;
vui_button_callback_t pw_skip_action;
if (access(POLKIT_ACTION_DST, F_OK) == 0) {
SETTINGS_NAMES[PW_SKIP_SETTING] = VPI_LANG_DISABLE_PASSWORD_SKIP;
SETTINGS_ACTION[PW_SKIP_SETTING] = transition_to_uninstall_polkit_rule;
} else {
SETTINGS_NAMES[PW_SKIP_SETTING] = VPI_LANG_ENABLE_PASSWORD_SKIP;
SETTINGS_ACTION[PW_SKIP_SETTING] = transition_to_install_polkit_rule;
}
#endif
int btnw = scrw*3/4;
int btnx = scrw/2 - btnw/2;
int btny = scrh/10;
for (int index = 0; index < sizeof(SETTINGS_NAMES)/sizeof(int); index++) {
if (SETTINGS_ACTION[index]) {
vui_button_create(vui, btnx, btny + BTN_SZ * index, btnw, BTN_SZ, lang(SETTINGS_NAMES[index]), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, SETTINGS_ACTION[index], (void *) (intptr_t) fglayer);
}
}
// Back button
vpi_menu_create_back_button(vui, fglayer, return_to_main, (void *) (intptr_t) fglayer);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
}
+162
View File
@@ -0,0 +1,162 @@
#include "menu_sudo.h"
#ifdef VANILLA_PIPE_AVAILABLE
#include "config.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_main.h"
#include "menu_sudo_warning.h"
#include "ui/ui_anim.h"
#include "pipemgmt.h"
static int fglayer;
static int textedit;
static vui_callback_t g_success_action;
static void *g_success_data;
static vui_callback_t g_cancel_action;
static void *g_cancel_data;
static int ok_btn;
static int cancel_btn;
static int err_lbl;
static int remember_btn;
static int warning_layer;
static int remember_warning_displayed = 0;
static void return_to_main(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
vpi_cancel_pw();
if (g_cancel_action) {
g_cancel_action(vui, g_cancel_data);
}
vui_transition_fade_layer_out(vui, layer, vpi_menu_main, (void *) (intptr_t) 1);
}
static void show_error(vui_context_t *vui, void *v)
{
vpi_menu_show_error(vui, (int) (intptr_t) v, 1, return_to_main, (void *) (intptr_t) fglayer);
}
static void pw_callback(int result, void *v)
{
vui_context_t *vui = (vui_context_t *) v;
switch (result) {
case VPI_POLKIT_RESULT_SUCCESS:
{
int r = vpi_start_epilog();
if (vui_button_get_checked(vui, remember_btn)) {
vanilla_install_polkit(vpi_config.server_address);
}
if (r == VANILLA_SUCCESS) {
vui_transition_fade_layer_out(vui, fglayer, g_success_action, g_success_data);
} else {
vui_transition_fade_layer_out(vui, fglayer, show_error, (void *) (intptr_t) r);
}
break;
}
case VPI_POLKIT_RESULT_RETRY:
vui_label_update_text(vui, err_lbl, lang(VPI_LANG_AUTH_FAIL));
vui_textedit_update_text(vui, textedit, "");
vui_button_update_enabled(vui, ok_btn, 1);
vui_button_update_enabled(vui, cancel_btn, 1);
vui_button_update_enabled(vui, remember_btn, 1);
vui_textedit_update_enabled(vui, textedit, 1);
break;
case VPI_POLKIT_RESULT_FAIL:
vui_transition_fade_layer_out(vui, fglayer, show_error, (void *) (intptr_t) VANILLA_PASSWORD_FAILED);
break;
}
}
static void submit_pw(vui_context_t *vui, int btn, void *v)
{
char buf[1024];
vui_textedit_get_text(vui, textedit, buf, sizeof(buf));
vpi_submit_pw(buf, pw_callback, vui);
vui_button_update_enabled(vui, ok_btn, 0);
vui_button_update_enabled(vui, cancel_btn, 0);
vui_button_update_enabled(vui, remember_btn, 0);
vui_textedit_update_enabled(vui, textedit, 0);
vui_label_update_text(vui, err_lbl, "");
}
static void hide_warning(vui_context_t *vui, int enable)
{
vui_layer_set_enabled(vui, warning_layer, 0);
vui_layer_set_enabled(vui, fglayer, 1);
vui_button_update_checked(vui, remember_btn, enable);
if (enable) remember_warning_displayed = 1;
}
static void hide_warning_and_ack(vui_context_t *vui, int btn, void *v)
{
hide_warning(vui, 1);
}
static void hide_warning_and_cancel(vui_context_t *vui, int btn, void *v)
{
hide_warning(vui, 0);
}
static void dont_ask_again_btn_pressed(vui_context_t *vui, int btn, void *userdata)
{
int v = !vui_button_get_checked(vui, btn);
if (v == 1 && !remember_warning_displayed) {
vui_layer_set_enabled(vui, warning_layer, 1);
vui_layer_set_enabled(vui, fglayer, 0);
} else {
vui_button_update_checked(vui, btn, v);
}
}
void vpi_menu_sudo(vui_context_t *vui, vui_callback_t success_action, void *success_data, vui_callback_t cancel_action, void *cancel_data, int fade_fglayer)
{
vui_reset(vui);
int bglayer = vui_layer_create(vui);
fglayer = vui_layer_create(vui);
vui_rect_t bkg_rect;
int margin;
vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin);
vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*1/10, bkg_rect.w, bkg_rect.h, lang(VPI_LANG_SUDO_HELP_1), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, fglayer);
vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*3/10, bkg_rect.w, bkg_rect.h, lang(VPI_LANG_SUDO_HELP_2), vui_color_create(1,1,1,1), VUI_FONT_SIZE_SMALL, fglayer);
const int textedit_w = bkg_rect.w / 2;
const int textedit_h = vui_get_font_height(vui, VUI_FONT_SIZE_NORMAL);
textedit = vui_textedit_create(vui, bkg_rect.x + bkg_rect.w/2 - textedit_w/2, bkg_rect.y + bkg_rect.h * 4/10, textedit_w, textedit_h, "", VUI_FONT_SIZE_NORMAL, 1, fglayer);
g_success_action = success_action;
g_success_data = success_data;
g_cancel_action = cancel_action;
g_cancel_data = cancel_data;
err_lbl = vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*11/20, bkg_rect.w, bkg_rect.h, "", vui_color_create(1,0,0,1), VUI_FONT_SIZE_TINY, fglayer);
static const int ok_btn_w = BTN_SZ * 3;
static const int cancel_btn_w = BTN_SZ * 3;
remember_btn = vui_button_create(vui, bkg_rect.w/2 - textedit_w/2, bkg_rect.y + bkg_rect.h * 6 / 10, textedit_w, BTN_SZ * 3 / 4, lang(VPI_LANG_AUTH_DONT_ASK_AGAIN), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, dont_ask_again_btn_pressed, 0);
vui_button_update_checkable(vui, remember_btn, 1);
ok_btn = vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - (ok_btn_w + cancel_btn_w)/2, bkg_rect.y + bkg_rect.h * 3 / 4, ok_btn_w, BTN_SZ, lang(VPI_LANG_OK_BTN), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, submit_pw, 0);
cancel_btn = vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - (ok_btn_w + cancel_btn_w)/2 + ok_btn_w, bkg_rect.y + bkg_rect.h * 3 / 4, cancel_btn_w, BTN_SZ, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, return_to_main, (void *) (intptr_t) fglayer);
warning_layer = vui_layer_create(vui);
vpi_menu_create_sudo_warning(vui, warning_layer, hide_warning_and_ack, 0, hide_warning_and_cancel, 0);
vui_layer_set_enabled(vui, warning_layer, 0);
vui_transition_fade_layer_in(vui, fade_fglayer ? fglayer : bglayer, 0, 0);
}
#endif // VANILLA_PIPE_AVAILABLE
+8
View File
@@ -0,0 +1,8 @@
#ifndef VANILLA_PI_MENU_SUDO_H
#define VANILLA_PI_MENU_SUDO_H
#include "ui/ui.h"
void vpi_menu_sudo(vui_context_t *vui, vui_callback_t success_action, void *success_data, vui_callback_t cancel_action, void *cancel_data, int fade_fglayer);
#endif // VANILLA_PI_MENU_SUDO_H
+25
View File
@@ -0,0 +1,25 @@
#include "menu_sudo_warning.h"
#include "lang.h"
#include "menu_common.h"
void vpi_menu_create_sudo_warning(vui_context_t *vui, int layer, vui_button_callback_t ack_action, void *ack_action_userdata, vui_button_callback_t cancel_action, void *cancel_action_userdata)
{
vui_rect_t bkg_rect;
int margin;
vpi_menu_create_background(vui, layer, &bkg_rect, &margin);
int warning_lbl_w = bkg_rect.w * 3 / 4;
int warning_lbl_x = bkg_rect.x + bkg_rect.w/2 - warning_lbl_w/2;
vui_label_create(vui, warning_lbl_x, bkg_rect.y + bkg_rect.h*1/15, warning_lbl_w, bkg_rect.h, lang(VPI_LANG_AUTH_WARNING), vui_color_create(1,0,0,1), VUI_FONT_SIZE_NORMAL, layer);
vui_label_create(vui, warning_lbl_x, bkg_rect.y + bkg_rect.h*4/20, warning_lbl_w, bkg_rect.h, lang(VPI_LANG_AUTH_WARNING_MESSAGE), vui_color_create(1,1,1,1), VUI_FONT_SIZE_SMALL, layer);
const int ack_btn_w = BTN_SZ * 4;
const int cancel_btn_w = BTN_SZ * 3;
const int btn_x = bkg_rect.x + bkg_rect.w/2 - (ack_btn_w+cancel_btn_w)/2;
const int btn_y = bkg_rect.y + bkg_rect.h * 3 / 4;
vui_button_create(vui, btn_x, btn_y, ack_btn_w, BTN_SZ, lang(VPI_LANG_AUTH_WARNING_ACKNOWLEDGE), 0, VUI_BUTTON_STYLE_BUTTON, layer, ack_action, ack_action_userdata);
vui_button_create(vui, btn_x + ack_btn_w, btn_y, cancel_btn_w, BTN_SZ, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_BUTTON, layer, cancel_action, cancel_action_userdata);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef VANILLA_PI_MENU_SUDO_WARNING_H
#define VANILLA_PI_MENU_SUDO_WARNING_H
#include "ui/ui.h"
void vpi_menu_create_sudo_warning(vui_context_t *vui, int layer, vui_button_callback_t ack_action, void *ack_action_userdata, vui_button_callback_t cancel_action, void *cancel_action_userdata);
#endif // VANILLA_PI_MENU_SUDO_WARNING_H
+329 -32
View File
@@ -15,14 +15,171 @@
#include <unistd.h>
#include <vanilla.h>
#define POLKIT_AGENT_I_KNOW_API_IS_SUBJECT_TO_CHANGE
#include <polkit/polkit.h>
#include <polkitagent/polkitagent.h>
#include "config.h"
#include "platform.h"
static pid_t pipe_pid = -1;
static pid_t potential_pipe_pid = -1;
static int pipe_input;
static int pipe_err;
static pthread_t pipe_log_thread = 0;
static int err_pipes[2], in_pipes[2];
static gpointer polkit_handle = 0;
static GMainContext *gmainctx = 0;
static PolkitAgentSession *pk_session = 0;
typedef struct {
PolkitAgentListener parent;
} VanillaPolkitListener;
typedef struct {
PolkitAgentListenerClass parent_class;
} VanillaPolkitListenerClass;
static VanillaPolkitListener *listener = 0;
static vpi_pw_callback pk_pw_callback = 0;
static void *pk_pw_callback_userdata = 0;
static PolkitIdentity *pk_identity = 0;
static GTask *pk_task = 0;
static int pk_attempts = 0;
static int pk_ignore_complete = 0;
static char pk_cookie[256]; // TODO: No actual idea how long a Polkit cookie usually is
G_DEFINE_TYPE(VanillaPolkitListener, vanilla_polkit_listener, POLKIT_AGENT_TYPE_LISTENER)
static void try_again();
static void on_request(PolkitAgentSession *s, const gchar *prompt, gboolean echo_on, gpointer user_data)
{
vpilog("Polkit authentication requested\n");
pk_session = s;
}
static void on_show_info(PolkitAgentSession *s, const gchar *text, gpointer user_data)
{
vpilog("Info\n");
if (text) vpilog("%s\n", text);
}
static void on_show_error(PolkitAgentSession *s, const gchar *text, gpointer user_data)
{
vpilog("Error\n");
if (text) vpilog("Error: %s\n", text);
}
static void on_complete(PolkitAgentSession *s, gboolean gained, gpointer data)
{
vpilog("Complete: %i\n", gained);
g_object_unref(pk_session);
pk_session = 0;
if (!pk_ignore_complete) {
pk_attempts++;
if (pk_pw_callback) {
int cb_ret;
if (gained) {
cb_ret = VPI_POLKIT_RESULT_SUCCESS;
g_task_return_boolean(pk_task, gained);
g_object_unref(pk_task);
pk_task = 0;
} else if (pk_attempts < 3) {
cb_ret = VPI_POLKIT_RESULT_RETRY;
try_again();
} else {
cb_ret = VPI_POLKIT_RESULT_FAIL;
g_task_return_error(pk_task, g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, "Maximum retries exceeded"));
// g_task_return_boolean(pk_task, gained);
g_object_unref(pk_task);
pk_task = 0;
}
pk_pw_callback(cb_ret, pk_pw_callback_userdata);
}
}
}
static void try_again()
{
pk_ignore_complete = 0;
g_main_context_push_thread_default(gmainctx);
PolkitAgentSession *session = polkit_agent_session_new(pk_identity, pk_cookie);
g_signal_connect(session, "request", G_CALLBACK(on_request), NULL);
g_signal_connect(session, "show-info", G_CALLBACK(on_show_info), NULL);
g_signal_connect(session, "show-error", G_CALLBACK(on_show_error), NULL);
// When session completes, call the async callback with success/failure.
g_signal_connect(session, "completed", G_CALLBACK(on_complete), NULL);
// Start PAM conversation
polkit_agent_session_initiate(session);
vpilog("Polkit session initiated\n");
g_main_context_pop_thread_default(gmainctx);
}
static void vanilla_polkit_initiate_authentication(PolkitAgentListener *listener,
const gchar *action_id,
const gchar *message,
const gchar *icon_name,
PolkitDetails *details,
const gchar *cookie,
GList *identities,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_main_context_push_thread_default(gmainctx);
pk_identity = identities->data;
g_object_ref(pk_identity);
pk_task = g_task_new(listener, cancellable, callback, user_data);
strncpy(pk_cookie, cookie, sizeof(pk_cookie));
try_again();
g_main_context_pop_thread_default(gmainctx);
}
static gboolean vanilla_polkit_initiate_authentication_finish(PolkitAgentListener *listener,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean(G_TASK(res), error);
}
static void vanilla_polkit_listener_finalize(GObject *object)
{
g_return_if_fail(object != NULL);
g_return_if_fail(G_TYPE_CHECK_INSTANCE_TYPE((object), vanilla_polkit_listener_get_type()));
G_OBJECT_CLASS(vanilla_polkit_listener_parent_class)->finalize(object);
}
static void vanilla_polkit_listener_class_init(VanillaPolkitListenerClass *self)
{
GObjectClass *g_object_class = G_OBJECT_CLASS(self);
g_object_class->finalize = vanilla_polkit_listener_finalize;
PolkitAgentListenerClass *lklass = POLKIT_AGENT_LISTENER_CLASS(self);
lklass->initiate_authentication = vanilla_polkit_initiate_authentication;
lklass->initiate_authentication_finish = vanilla_polkit_initiate_authentication_finish;
}
static void vanilla_polkit_listener_init(VanillaPolkitListener *self)
{
}
void *vpi_pipe_log_thread(void *data)
{
char buf[512];
@@ -69,6 +226,105 @@ void sigint_handler(int signal)
raise(signal);
}
void vpi_close_polkit_session()
{
if (polkit_handle) {
pk_ignore_complete = 1;
if (pk_session) {
polkit_agent_session_cancel(pk_session);
pk_session = 0;
}
if (pk_task) {
g_task_return_boolean(pk_task, 0);
g_object_unref(pk_task);
pk_task = 0;
}
vpilog("Unregistering Polkit listener\n");
polkit_agent_listener_unregister(polkit_handle);
polkit_handle = 0;
g_object_unref(listener);
listener = 0;
g_main_context_unref(gmainctx);
gmainctx = 0;
if (pk_identity) {
g_object_unref(pk_identity);
pk_identity = 0;
}
}
}
void vpi_cancel_pw()
{
// Close existing pkexec process
if (potential_pipe_pid != -1) {
kill(potential_pipe_pid, SIGTERM);
potential_pipe_pid = -1;
close(in_pipes[1]);
close(err_pipes[0]);
close(err_pipes[1]);
close(in_pipes[0]);
}
vpi_close_polkit_session();
}
void vpi_submit_pw(const char *s, vpi_pw_callback callback, void *userdata)
{
pk_pw_callback = callback;
pk_pw_callback_userdata = userdata;
polkit_agent_session_response(pk_session, s);
}
int vpi_start_epilog()
{
int ret = VANILLA_ERR_GENERIC;
char ready_buf[500];
memset(ready_buf, 0, sizeof(ready_buf));
size_t read_count = 0;
while (read_count < sizeof(ready_buf)) {
ssize_t this_read = read(err_pipes[0], ready_buf + read_count, sizeof(ready_buf));
if (this_read > 0) {
read_count += this_read;
break;
}
if (this_read <= 0) {
break;
}
}
if (!memcmp(ready_buf, "READY", 5)) {
ret = VANILLA_SUCCESS;
pipe_input = in_pipes[1];
pipe_err = err_pipes[0];
pthread_create(&pipe_log_thread, 0, vpi_pipe_log_thread, (void *) (intptr_t) pipe_err);
pipe_pid = potential_pipe_pid;
} else {
vpilog("GOT INVALID SIGNAL: %.*s\n", sizeof(ready_buf), ready_buf);
// Kill seems to break a lot of things so I guess we'll just leave it orphaned
// kill(pipe_pid, SIGKILL);
pipe_pid = -1;
close(in_pipes[1]);
close(err_pipes[0]);
}
close(err_pipes[1]);
close(in_pipes[0]);
vpi_close_polkit_session();
return ret;
}
int vpi_start_pipe()
{
if (pipe_pid != -1) {
@@ -76,8 +332,51 @@ int vpi_start_pipe()
return VANILLA_SUCCESS;
}
{
PolkitAuthority *auth = polkit_authority_get_sync(NULL, NULL);
PolkitSubject *sub = polkit_unix_process_new_for_owner(getpid(), 0, -1);
PolkitAuthorizationResult *res =
polkit_authority_check_authorization_sync(
auth, sub, "com.mattkc.vanilla", NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE, NULL, NULL);
gboolean ok = res && polkit_authorization_result_get_is_authorized(res);
if (res) g_object_unref(res);
g_object_unref(sub);
g_object_unref(auth);
// gboolean ok = polkit_authorization_result_get_is_authorized(res);
// gboolean chall = polkit_authorization_result_get_is_challenge(res);
if (!ok) {
g_autoptr(PolkitSubject) subject = polkit_unix_session_new_for_process_sync(getpid(), NULL, NULL);
g_autoptr(GError) error = NULL;
pk_session = 0;
pk_attempts = 0;
gmainctx = g_main_context_new();
g_main_context_push_thread_default(gmainctx);
listener = g_object_new(vanilla_polkit_listener_get_type(), NULL);
vpilog("Registered Polkit listener\n");
polkit_handle = polkit_agent_listener_register(
POLKIT_AGENT_LISTENER(listener),
POLKIT_AGENT_REGISTER_FLAGS_NONE,
subject,
NULL, NULL, &error
);
g_main_context_pop_thread_default(gmainctx);
if (!polkit_handle) {
g_main_context_unref(gmainctx);
gmainctx = 0;
vpilog("Failed to register Polkit listener: %s\n", error->message);
}
}
}
// Set up pipes so child stdout can be read by the parent process
int err_pipes[2], in_pipes[2];
pipe(in_pipes);
pipe(err_pipes);
@@ -121,6 +420,7 @@ int vpi_start_pipe()
strcat(exe, "/vanilla-pipe");
r = execlp(pkexec, pkexec, exe, "-local", vpi_config.wireless_interface, (const char *) 0);
// r = execlp(pkexec, pkexec, "/usr/bin/gparted", (const char *) 0);
// Handle failure to execute, use _exit so we don't interfere with the host
_exit(1);
@@ -138,44 +438,35 @@ int vpi_start_pipe()
sigaction(SIGINT, &sa, &old_sigint_action);
sigaction(SIGTERM, &sa, &old_sigterm_action);
char ready_buf[500];
memset(ready_buf, 0, sizeof(ready_buf));
size_t read_count = 0;
while (read_count < sizeof(ready_buf)) {
ssize_t this_read = read(err_pipes[0], ready_buf + read_count, sizeof(ready_buf));
if (this_read > 0) {
read_count += this_read;
break;
}
potential_pipe_pid = pid;
if (this_read <= 0) {
break;
}
}
if (!memcmp(ready_buf, "READY", 5)) {
ret = VANILLA_SUCCESS;
pipe_pid = pid;
if (polkit_handle) {
// We'll need to handle this...
ret = VANILLA_REQUIRES_PASSWORD_HANDLING;
pipe_input = in_pipes[1];
pipe_err = err_pipes[0];
pthread_create(&pipe_log_thread, 0, vpi_pipe_log_thread, (void *) (intptr_t) pipe_err);
} else {
vpilog("GOT INVALID SIGNAL: %.*s\n", sizeof(ready_buf), ready_buf);
// Kill seems to break a lot of things so I guess we'll just leave it orphaned
// kill(pipe_pid, SIGKILL);
close(in_pipes[1]);
close(err_pipes[0]);
}
close(err_pipes[1]);
close(in_pipes[0]);
// Keep handling synchronous by waiting for polkit to send us a
// "request" signal before returning to the password prompt
while (!pk_session) {
g_main_context_iteration(gmainctx, 1);
}
} else {
// Polkit will be handled for us, wait for it to finish
ret = vpi_start_epilog();
}
return ret;
}
}
void vpi_update_pipe()
{
if (gmainctx) {
while (g_main_context_pending(gmainctx)) {
g_main_context_iteration(gmainctx, 0);
}
}
}
void vpi_stop_pipe()
{
if (pipe_pid != -1) {
@@ -196,6 +487,7 @@ void vpi_stop_pipe()
sigaction(SIGINT, &old_sigint_action, NULL);
sigaction(SIGTERM, &old_sigterm_action, NULL);
}
vpi_close_polkit_session();
}
#else
@@ -205,4 +497,9 @@ void vpi_stop_pipe()
{
}
// Empty function
void vpi_update_pipe()
{
}
#endif
+14 -1
View File
@@ -1,9 +1,22 @@
#ifndef VANILLA_PI_PIPE_MGMT_H
#define VANILLA_PI_PIPE_MGMT_H
#define VANILLA_REQUIRES_PASSWORD_HANDLING -1000
#define VANILLA_PASSWORD_FAILED -1001
#ifdef VANILLA_PIPE_AVAILABLE
int vpi_start_pipe();
int vpi_start_epilog();
void vpi_cancel_pw();
#define VPI_POLKIT_RESULT_SUCCESS 0
#define VPI_POLKIT_RESULT_RETRY 1
#define VPI_POLKIT_RESULT_FAIL 2
typedef void (*vpi_pw_callback)(int result, void *userdata);
void vpi_submit_pw(const char *s, vpi_pw_callback callback, void *userdata);
#endif
void vpi_stop_pipe();
void vpi_update_pipe();
#endif // VANILLA_PI_PIPE_MGMT_H
#endif // VANILLA_PI_PIPE_MGMT_H
+11 -3
View File
@@ -52,6 +52,7 @@ void vui_reset(vui_context_t *ctx)
ctx->button_active = -1;
ctx->layers = 1;
ctx->layer_opacity[0] = 1;
ctx->layer_enabled[0] = 1;
ctx->selected_button = -1;
ctx->cancel_button = -1;
ctx->active_textedit = -1;
@@ -365,7 +366,7 @@ void vui_process_mousedown(vui_context_t *ctx, int x, int y)
for (int i = 0; i < ctx->button_count; i++) {
vui_button_t *btn = &ctx->buttons[i];
if (btn->visible && btn->enabled && point_inside_button(btn, x, y)) {
if (btn->visible && btn->enabled && point_inside_button(btn, x, y) && ctx->layer_enabled[btn->layer]) {
press_button(ctx, i);
button_pressed = 1;
break;
@@ -377,7 +378,7 @@ void vui_process_mousedown(vui_context_t *ctx, int x, int y)
for (int i = 0; i < ctx->textedit_count; i++) {
vui_textedit_t *edit = &ctx->textedits[i];
if (edit->visible && edit->enabled && point_inside_textedit(edit, x, y)) {
if (edit->visible && edit->enabled && point_inside_textedit(edit, x, y) && ctx->layer_enabled[edit->layer]) {
new_active_textedit = i;
break;
}
@@ -545,6 +546,7 @@ int vui_layer_create(vui_context_t *ctx)
// Set layer defaults
ctx->layer_opacity[cur_layer] = 1.0f;
ctx->layer_enabled[cur_layer] = 1;
vui_color_t *layerbg = &ctx->layer_color[cur_layer];
layerbg->r = 0;
@@ -572,6 +574,11 @@ void vui_layer_set_opacity(vui_context_t *ctx, int layer, float opacity)
ctx->layer_opacity[layer] = opacity;
}
void vui_layer_set_enabled(vui_context_t *ctx, int layer, int enabled)
{
ctx->layer_enabled[layer] = enabled;
}
vui_color_t vui_color_create(float r, float g, float b, float a)
{
vui_color_t c;
@@ -626,7 +633,7 @@ void vui_label_update_visible(vui_context_t *ctx, int index, int visible)
lbl->visible = visible;
}
int vui_textedit_create(vui_context_t *ctx, int x, int y, int w, int h, const char *initial_text, vui_font_size_t size, int layer)
int vui_textedit_create(vui_context_t *ctx, int x, int y, int w, int h, const char *initial_text, vui_font_size_t size, int password, int layer)
{
if (ctx->textedit_count == MAX_BUTTON_COUNT) {
return -1;
@@ -640,6 +647,7 @@ int vui_textedit_create(vui_context_t *ctx, int x, int y, int w, int h, const ch
edit->y = y;
edit->w = w;
edit->h = h;
edit->password = password;
edit->layer = layer;
edit->size = size;
+2 -1
View File
@@ -84,6 +84,7 @@ void vui_set_background(vui_context_t *ctx, const char *background_image);
*/
int vui_layer_create(vui_context_t *ctx);
void vui_layer_set_opacity(vui_context_t *ctx, int layer, float opacity);
void vui_layer_set_enabled(vui_context_t *ctx, int layer, int enabled);
void vui_layer_set_bgcolor(vui_context_t *ctx, int layer, vui_color_t color);
int vui_layer_destroy(vui_context_t *ctx);
@@ -114,7 +115,7 @@ void vui_label_update_visible(vui_context_t *ctx, int label, int visible);
/**
* TextEdit-related functions
*/
int vui_textedit_create(vui_context_t *ctx, int x, int y, int w, int h, const char *initial_text, vui_font_size_t size, int layer);
int vui_textedit_create(vui_context_t *ctx, int x, int y, int w, int h, const char *initial_text, vui_font_size_t size, int password, int layer);
void vui_textedit_get_text(vui_context_t *ctx, int textedit, char *output, size_t output_size);
void vui_textedit_update_text(vui_context_t *ctx, int textedit, const char *text);
void vui_textedit_update_visible(vui_context_t *ctx, int textedit, int visible);
+3 -1
View File
@@ -6,7 +6,7 @@
#include "ui.h"
#define MAX_BUTTON_COUNT 16
#define MAX_BUTTON_TEXT 256
#define MAX_BUTTON_TEXT 512
typedef struct {
int x;
@@ -52,6 +52,7 @@ typedef struct {
int visible;
int enabled;
int cursor;
int password;
} vui_textedit_t;
typedef struct {
@@ -121,6 +122,7 @@ typedef struct vui_context_t {
int screen_height;
int layers;
float layer_opacity[MAX_BUTTON_COUNT];
int layer_enabled[MAX_BUTTON_COUNT];
vui_color_t layer_color[MAX_BUTTON_COUNT];
char background_image[MAX_BUTTON_TEXT];
int background_enabled;
+75 -26
View File
@@ -25,6 +25,8 @@
#include "ui_util.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
#define PW_CHAR_SIZE 20
#define PW_CHAR_PAD 2
typedef struct {
SDL_Texture *texture;
@@ -56,6 +58,7 @@ typedef struct {
SDL_Texture *toast_tex;
struct timeval toast_expiry;
AVFrame *frame;
SDL_Texture *pw_tex;
uint8_t *audio_buffer;
size_t audio_buffer_size;
@@ -422,6 +425,7 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
sdl_ctx->background = 0;
sdl_ctx->last_shown_toast = 0;
sdl_ctx->toast_tex = 0;
sdl_ctx->pw_tex = 0;
memset(sdl_ctx->layer_data, 0, sizeof(sdl_ctx->layer_data));
memset(sdl_ctx->label_data, 0, sizeof(sdl_ctx->label_data));
memset(sdl_ctx->button_cache, 0, sizeof(sdl_ctx->button_cache));
@@ -461,6 +465,11 @@ void vui_close_sdl(vui_context_t *ctx)
SDL_CloseAudioDevice(sdl_ctx->mic);
}
if (sdl_ctx->pw_tex) {
SDL_DestroyTexture(sdl_ctx->pw_tex);
sdl_ctx->pw_tex = 0;
}
SDL_DestroyTexture(sdl_ctx->toast_tex);
if (sdl_ctx->game_tex) SDL_DestroyTexture(sdl_ctx->game_tex);
SDL_DestroyTexture(sdl_ctx->background);
@@ -902,24 +911,51 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
TTF_Font *font = get_font(sdl_ctx, edit->size);
if (edit->text[0]) {
SDL_Surface *surface = TTF_RenderUTF8_Blended(font, edit->text, c);
SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_ctx->renderer, surface);
SDL_Rect src;
src.x = src.y = 0;
src.w = surface->w;
src.h = surface->h;
if (edit->password) {
if (!sdl_ctx->pw_tex) {
sdl_ctx->pw_tex = vui_sdl_load_texture(sdl_ctx->renderer, "circle_big.svg");
}
if (src.w > rect.w) {
src.w = rect.w;
} else {
rect.w = src.w;
}
uint8_t f = (edit->enabled ? 1 : 0.5f) * 0xFF;
SDL_SetTextureAlphaMod(sdl_ctx->pw_tex, f);
SDL_SetTextureColorMod(sdl_ctx->pw_tex, f, f, f);
SDL_RenderCopy(renderer, texture, &src, &rect);
const char *cc = edit->text;
SDL_Rect pwr;
pwr.x = rect.x;
pwr.y = rect.y + rect.h/2 - PW_CHAR_SIZE/2;
pwr.w = PW_CHAR_SIZE;
pwr.h = PW_CHAR_SIZE;
while (*cc != 0) {
SDL_RenderCopy(renderer, sdl_ctx->pw_tex, 0, &pwr);
cc++;
pwr.x += PW_CHAR_SIZE + PW_CHAR_PAD;
}
} else {
SDL_Surface *surface = TTF_RenderUTF8_Blended(font, edit->text, c);
SDL_Texture *texture = SDL_CreateTextureFromSurface(sdl_ctx->renderer, surface);
SDL_Rect src;
src.x = src.y = 0;
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
src.w = surface->w;
src.h = surface->h;
if (src.w > rect.w) {
src.w = rect.w;
} else {
rect.w = src.w;
}
uint8_t f = (edit->enabled ? 1 : 0.5f) * 0xFF;
SDL_SetTextureAlphaMod(texture, f);
SDL_SetTextureColorMod(texture, f, f, f);
SDL_RenderCopy(renderer, texture, &src, &rect);
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
}
}
if (ctx->active_textedit == i) {
@@ -927,23 +963,27 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
gettimeofday(&now, 0);
time_t diff = (now.tv_sec - ctx->active_textedit_time.tv_sec) * 1000000 + (now.tv_usec - ctx->active_textedit_time.tv_usec);
if ((diff % 1000000) < 500000) {
char tmp_ate[MAX_BUTTON_TEXT];
char *copy_end = edit->text;
for (int i = 0; i < edit->cursor; i++) {
copy_end = vui_utf8_advance(copy_end);
}
size_t len = copy_end - edit->text;
strncpy(tmp_ate, edit->text, len);
tmp_ate[len] = 0;
int tw;
TTF_SizeUTF8(font, tmp_ate, &tw, 0);
int cursor_x = rect.x + tw;
int cursor_x;
if (edit->password) {
cursor_x = rect.x + (PW_CHAR_SIZE + PW_CHAR_PAD) * len - PW_CHAR_PAD/2;
} else {
char tmp_ate[MAX_BUTTON_TEXT];
strncpy(tmp_ate, edit->text, len);
tmp_ate[len] = 0;
SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
SDL_RenderDrawLine(renderer, cursor_x, rect.y, cursor_x, rect.y + rect.h);
int tw;
TTF_SizeUTF8(font, tmp_ate, &tw, 0);
cursor_x = rect.x + tw;
}
SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, c.a);
SDL_RenderDrawLine(renderer, cursor_x, rect.y, cursor_x, rect.y + rect.h);
}
}
}
@@ -1442,9 +1482,18 @@ int vui_update_sdl(vui_context_t *vui)
vui_draw_sdl(vui, renderer);
// Flatten layers
for (int i = vui->layers - 1; i > 0; i--) {
SDL_Texture *bg = sdl_ctx->layer_data[i-1];
SDL_Texture *fg = sdl_ctx->layer_data[i];
int el[MAX_BUTTON_COUNT];
int el_count = 0;
for (int i = 0; i < vui->layers; i++) {
if (vui->layer_enabled[i]) {
el[el_count] = i;
el_count++;
}
}
for (int i = el_count - 1; i > 0; i--) {
SDL_Texture *bg = sdl_ctx->layer_data[el[i-1]];
SDL_Texture *fg = sdl_ctx->layer_data[el[i]];
SDL_SetRenderTarget(renderer, bg);
SDL_SetTextureColorMod(fg, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF);
+29
View File
@@ -279,6 +279,35 @@ exit_pipe:
close(skt);
}
int install_polkit_internal(thread_data_t *data, int install)
{
clear_interrupt();
SERVER_ADDRESS = data->server_address;
int ret = VANILLA_ERR_GENERIC;
int pipe_cc_skt = -1;
if (SERVER_ADDRESS != VANILLA_ADDRESS_DIRECT) {
vanilla_pipe_command_t cmd;
cmd.control_code = install ? VANILLA_PIPE_CC_INSTALL_POLKIT : VANILLA_PIPE_CC_UNINSTALL_POLKIT;
// Connect to backend pipe
ret = connect_to_backend(&pipe_cc_skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
// No interrupt is required here because VANILLA_PIPE_CC_INSTALL_POLKIT
// does not start a thread in the pipe
}
if (pipe_cc_skt != -1) {
// Disconnect from pipe if necessary
send_unbind_cc(pipe_cc_skt);
close(pipe_cc_skt);
}
return ret;
}
void connect_as_gamepad_internal(thread_data_t *data)
{
clear_interrupt();
+2 -1
View File
@@ -45,6 +45,7 @@ typedef struct thread_data_t
void sync_internal(thread_data_t *data);
void connect_as_gamepad_internal(thread_data_t *data);
int install_polkit_internal(thread_data_t *data, int install);
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port);
int push_event(event_loop_t *loop, int type, const void *data, size_t size);
int get_event(event_loop_t *loop, vanilla_event_t *event, int wait);
@@ -54,4 +55,4 @@ void free_event_buffer_arena();
void *get_event_buffer();
void release_event_buffer(void *buffer);
#endif // VANILLA_GAMEPAD_H
#endif // VANILLA_GAMEPAD_H
+14
View File
@@ -203,6 +203,20 @@ int vanilla_sync(uint16_t code, uint32_t server_address)
return vanilla_start_internal(server_address, (vanilla_bssid_t){.bssid = {0}}, (vanilla_psk_t){.psk = {0}}, sync_internal, (void *) (uintptr_t) code);
}
int vanilla_install_polkit(uint32_t server_address)
{
thread_data_t data;
data.server_address = server_address;
return install_polkit_internal(&data, 1);
}
int vanilla_uninstall_polkit(uint32_t server_address)
{
thread_data_t data;
data.server_address = server_address;
return install_polkit_internal(&data, 0);
}
int vanilla_poll_event(vanilla_event_t *event)
{
return get_event(&event_loop, event, 0);
+3
View File
@@ -125,6 +125,9 @@ typedef struct {
int vanilla_start(uint32_t server_address, vanilla_bssid_t bssid, vanilla_psk_t psk);
int vanilla_sync(uint16_t code, uint32_t server_address);
int vanilla_install_polkit(uint32_t server_address);
int vanilla_uninstall_polkit(uint32_t server_address);
int vanilla_poll_event(vanilla_event_t *event);
int vanilla_wait_event(vanilla_event_t *event);
int vanilla_free_event(vanilla_event_t *event);
+6 -1
View File
@@ -16,10 +16,15 @@
#define VANILLA_PIPE_CC_SYNC_SUCCESS 0x87
#define VANILLA_PIPE_CC_CONNECTED 0x88
#define VANILLA_PIPE_CC_DISCONNECTED 0x89
#define VANILLA_PIPE_CC_INSTALL_POLKIT 0x8A
#define VANILLA_PIPE_CC_UNINSTALL_POLKIT 0x8B
#define VANILLA_PIPE_CC_QUIT 0x90
#define VANILLA_PIPE_LOCAL_SOCKET "/tmp/vanilla-pipe_%i.sock"
#define POLKIT_ACTION_DST "/usr/share/polkit-1/actions/com.mattkc.vanilla.policy"
#define POLKIT_RULE_DST "/usr/share/polkit-1/rules.d/com.mattkc.vanilla.rules"
#pragma pack(push, 1)
typedef struct {
uint16_t code;
@@ -39,4 +44,4 @@ typedef struct {
} vanilla_pipe_command_t;
#pragma pack(pop)
#endif // VANILLA_PIPE_DEF_H
#endif // VANILLA_PIPE_DEF_H
-15
View File
@@ -227,18 +227,3 @@ target_link_libraries(vanilla-pipe PRIVATE
${OPENSSL_LIBRARIES}
wpa_supplicant_drc
)
# Handle polkit files
if (VANILLA_INSTALL_POLKIT_RULE)
set(PIPE_BIN "${CMAKE_INSTALL_PREFIX}/bin/vanilla-pipe")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/polkit/com.mattkc.vanilla.policy.in"
"${CMAKE_CURRENT_BINARY_DIR}/polkit/com.mattkc.vanilla.policy"
@ONLY
)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/polkit/com.mattkc.vanilla.policy" DESTINATION share/polkit-1/actions)
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/polkit/com.mattkc.vanilla.rules" DESTINATION share/polkit-1/rules.d)
else()
# Install script that can do it later
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/polkit/vanilla-polkit-install.sh" DESTINATION bin)
endif()
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<vendor>MattKC</vendor>
<vendor_url>https://mattkc.com</vendor_url>
<action id="com.mattkc.vanilla">
<description>Run Vanilla Pipe as root</description>
<message>Authentication is required to run Vanilla Pipe as root</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@PIPE_BIN@</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>
@@ -1,12 +0,0 @@
/**
* Allow all users to run vanilla-pipe as root without having to enter a password
*
* This provides convenience, especially on platforms more suited for touch
* controls, however it could be dangerous to allow a program unrestricted
* administrator access, so it should be used with caution.
*/
polkit.addRule(function(action, subject) {
if (action.id == "com.mattkc.vanilla") {
return polkit.Result.YES;
}
});
-109
View File
@@ -1,109 +0,0 @@
#!/bin/bash
set -e
if [ "$EUID" -ne 0 ]
then
echo "$0 must be run as root"
exit 1
fi
echo "--------------------------------------------------------------------------------"
echo "vanilla-pipe Polkit Install Script"
echo "--------------------------------------------------------------------------------"
echo "This script will install a Polkit action and rule that will allow vanilla-pipe"
echo "to run as root without entering a password."
echo ""
echo "This makes usage of Vanilla much more convenient, especially on touch-based"
echo "devices such as the Nintendo Switch and Steam Deck, however THERE ARE INHERENT"
echo "RISKS TO ALLOWING A PROGRAM TO RUN AS ROOT WITHOUT USER INTERVENTION. WE MAKE"
echo "NO SAFETY GUARANTEES IF YOU CHOOSE TO CONTINUE WITH THIS PROCESS."
echo ""
read -p "If you understand the risks and wish to continue, type \"I agree\": " agree
if [ "${agree,,}" != "i agree" ]
then
echo "Exiting"
exit 1
fi
echo "Continuing..."
while [ 1 ]
do
read -p "Enter the absolute path of vanilla-pipe (e.g. /usr/bin/vanilla-pipe): " path
if [ -z "${path}" ]
then
read -p "Do you wish to exit? [yn] " exit_confirm
if [ "${exit_confirm,,}" = "y" ]
then
exit 1
else
continue
fi
fi
if [ ! -f "${path}" ]
then
echo "File \"${path}\" does not exist"
continue
fi
if [ ${path:0:1} != "/" ]
then
echo "Path \"${path}\" does not appear to be an absolute path"
continue
fi
read -p "vanilla-pipe will be located at \"$path\", is that correct? [yn] " confirm
if [ "${confirm,,}" = "y" ]
then
break
fi
done
POLKIT_ACTION_DST="/usr/share/polkit-1/actions/com.mattkc.vanilla.policy"
POLKIT_RULE_DST="/usr/share/polkit-1/rules.d/com.mattkc.vanilla.rules"
echo ""
POLICY_TEMPLATE='<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<vendor>MattKC</vendor>
<vendor_url>https://mattkc.com</vendor_url>
<action id="com.mattkc.vanilla">
<description>Run Vanilla Pipe as root</description>
<message>Authentication is required to run Vanilla Pipe as root</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@PIPE_BIN@</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>'
RULES_TEMPLATE='/**
* Allow all users to run vanilla-pipe as root without having to enter a password
*
* This provides convenience, especially on platforms more suited for touch
* controls, however it could be dangerous to allow a program unrestricted
* administrator access, so it should be used with caution.
*/
polkit.addRule(function(action, subject) {
if (action.id == "com.mattkc.vanilla") {
return polkit.Result.YES;
}
});'
echo "Installing Polkit action to ${POLKIT_ACTION_DST}"
echo "$POLICY_TEMPLATE" | sed "s|@PIPE_BIN@|${path}|g" > "${POLKIT_ACTION_DST}"
echo "Installing Polkit rule to ${POLKIT_RULE_DST}"
echo "$RULES_TEMPLATE" > "${POLKIT_RULE_DST}"
echo ""
echo "Installation done. If you wish to undo this, simply remove the two files"
echo "listed above."
+79
View File
@@ -1087,6 +1087,71 @@ void *vanilla_connect_to_console(void *data)
return wpa_setup_environment(args);
}
int uninstall_polkit()
{
unlink(POLKIT_ACTION_DST);
unlink(POLKIT_RULE_DST);
}
int install_polkit()
{
static const char *ACTION_TEMPLATE =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE policyconfig PUBLIC \"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN\" \"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd\">\n"
"<policyconfig>\n"
" <vendor>MattKC</vendor>\n"
" <vendor_url>https://mattkc.com</vendor_url>\n"
" <action id=\"com.mattkc.vanilla\">\n"
" <description>Run Vanilla Pipe as root</description>\n"
" <message>Authentication is required to run Vanilla Pipe as root</message>\n"
" <defaults>\n"
" <allow_any>auth_admin</allow_any>\n"
" <allow_inactive>auth_admin</allow_inactive>\n"
" <allow_active>auth_admin</allow_active>\n"
" </defaults>\n"
" <annotate key=\"org.freedesktop.policykit.exec.path\">%s</annotate>\n"
" <annotate key=\"org.freedesktop.policykit.exec.allow_gui\">true</annotate>\n"
" </action>\n"
"</policyconfig>\n";
static const char *RULE_TEMPLATE =
"/**\n"
" * Allow all users to run vanilla-pipe as root without having to enter a password\n"
" *\n"
" * This provides convenience, especially on platforms more suited for touch\n"
" * controls, however it could be dangerous to allow a program unrestricted\n"
" * administrator access, so it should be used with caution.\n"
" */\n"
"polkit.addRule(function(action, subject) {\n"
" if (action.id == \"com.mattkc.vanilla\") {\n"
" return polkit.Result.YES;\n"
" }\n"
"});\n";
// Get current filename
char exe[4096];
ssize_t link_len = readlink("/proc/self/exe", exe, sizeof(exe));
exe[link_len] = 0;
// If on the Steam Deck, ensure the system partition is writeable
run_process_and_read_stdout((const char *[]) {"steamos-readonly", "disable", NULL}, 0, 0);
int ret = VANILLA_ERR_GENERIC;
FILE *action = fopen(POLKIT_ACTION_DST, "w");
FILE *rule = fopen(POLKIT_RULE_DST, "w");
if (action && rule) {
fprintf(action, ACTION_TEMPLATE, exe);
fprintf(rule, RULE_TEMPLATE);
ret = VANILLA_SUCCESS;
}
if (action) fclose(action);
if (rule) fclose(rule);
return ret;
}
void pipe_listen(int local, const char *wireless_interface, const char *log_file)
{
// Store reference to log file
@@ -1167,6 +1232,20 @@ void pipe_listen(int local, const char *wireless_interface, const char *log_file
nlprint("FAILED TO SEND BUSY: %i", errno);
}
}
} else if (cmd.control_code == VANILLA_PIPE_CC_INSTALL_POLKIT || cmd.control_code == VANILLA_PIPE_CC_UNINSTALL_POLKIT) {
if (cmd.control_code == VANILLA_PIPE_CC_INSTALL_POLKIT) {
// Write Polkit rule and action
install_polkit();
} else {
// Delete Polkit rule and action
uninstall_polkit();
}
// Acknowledge
cmd.control_code = VANILLA_PIPE_CC_BIND_ACK;
if (sendto(skt, &cmd, sizeof(cmd.control_code), 0, (const struct sockaddr *) &addr, addr_size) == -1) {
nlprint("FAILED TO SEND ACK: %i", errno);
}
} else if (cmd.control_code == VANILLA_PIPE_CC_UNBIND) {
nlprint("RECEIVED UNBIND SIGNAL");
interrupt();