mirror of
https://github.com/yuzu-emu/AppImageKit-checkrt.git
synced 2024-12-25 01:55:42 +00:00
316 lines
11 KiB
C
316 lines
11 KiB
C
/*
|
|
* Copyright (C) 2016 Sven Brauch <mail@svenbrauch.de>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/**
|
|
This library is intended to be used together with the AppImage distribution mechanism.
|
|
Place the library somewhere in your AppImage and point LD_PRELOAD to it
|
|
before launching your application.
|
|
|
|
Whenever your application invokes a child process through execv() or execve(),
|
|
this wrapper will intercept the call and see if the child process lies
|
|
outside of the bundled appdir. If it does, the wrapper will attempt to undo
|
|
any changes done to environment variables before launching the process,
|
|
since you probably did not intend to launch it with e.g. the LD_LIBRARY_PATH
|
|
you previously set for your application.
|
|
|
|
To perform this operation, you have to set the following environment variables:
|
|
$APPDIR -- path of the AppDir you are launching your application from. If this
|
|
is not present, the wrapper will do nothing.
|
|
|
|
For each environment variable you want restored, where {VAR} is the name of the environment
|
|
variable (e.g. "PATH"):
|
|
$APPIMAGE_ORIGINAL_{VAR} -- original value of the environment variable
|
|
$APPIMAGE_STARTUP_{VAR} -- value of the variable when you were starting up
|
|
your application
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <unistd.h>
|
|
#include <dlfcn.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
|
|
typedef ssize_t (*execve_func_t)(const char* filename, char* const argv[], char* const envp[]);
|
|
static execve_func_t old_execve = NULL;
|
|
|
|
typedef ssize_t (*execvp_func_t)(const char* filename, char* const argv[]);
|
|
//static execvp_func_t old_execvp = NULL;
|
|
|
|
// TODO implement me: execl, execlp, execle; but it's annoying work and nothing seems to use them
|
|
// typedef int (*execl_func_t)(const char *path, const char *arg);
|
|
// static execl_func_t old_execl = NULL;
|
|
//
|
|
// typedef int (*execlp_func_t)(const char *file, const char *arg);
|
|
// static execlp_func_t old_execlp = NULL;
|
|
//
|
|
// typedef int (*execle_func_t)(const char *path, const char *arg, char * const envp[]);
|
|
// static execle_func_t old_execle = NULL;
|
|
|
|
typedef int (*execv_func_t)(const char *path, char *const argv[]);
|
|
//static execv_func_t old_execv = NULL;
|
|
|
|
typedef int (*execvpe_func_t)(const char *file, char *const argv[], char *const envp[]);
|
|
static execvpe_func_t old_execvpe = NULL;
|
|
|
|
char* APPIMAGE_ORIG_PREFIX = "APPIMAGE_ORIGINAL_";
|
|
char* APPIMAGE_STARTUP_PREFIX = "APPIMAGE_STARTUP_";
|
|
char* APPDIR = "APPDIR";
|
|
|
|
typedef struct {
|
|
char** names;
|
|
char** values;
|
|
} environment;
|
|
|
|
environment environment_alloc(size_t envc) {
|
|
environment env;
|
|
env.names = calloc(envc+1, sizeof(char*));
|
|
env.values = calloc(envc+1, sizeof(char*));
|
|
return env;
|
|
}
|
|
|
|
int arr_len(char* const x[]) {
|
|
int len = 0;
|
|
while ( x[len] != 0 ) {
|
|
len++;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
void stringlist_free(char* const envp[]) {
|
|
if ( envp ) {
|
|
for ( int i = 0; i < arr_len(envp); i++ ) {
|
|
free(envp[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
char** stringlist_alloc(int size) {
|
|
char** ret = calloc(size, sizeof(char*));
|
|
return ret;
|
|
}
|
|
|
|
int environment_len(const environment env) {
|
|
return arr_len(env.names);
|
|
}
|
|
|
|
void environment_free(environment env) {
|
|
stringlist_free(env.names);
|
|
stringlist_free(env.values);
|
|
}
|
|
|
|
void environment_append_item(environment env, char* name, int name_size, char* val, int val_size) {
|
|
int count = environment_len(env);
|
|
env.names[count] = calloc(name_size+1, sizeof(char));
|
|
env.values[count] = calloc(val_size+1, sizeof(char));
|
|
strncpy(env.names[count], name, name_size);
|
|
strncpy(env.values[count], val, val_size);
|
|
}
|
|
|
|
int environment_find_name(environment env, char* name, int name_size) {
|
|
int count = environment_len(env);
|
|
for ( int i = 0; i < count; i++ ) {
|
|
if ( !strncmp(env.names[i], name, name_size) ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
char** environment_to_stringlist(environment env) {
|
|
int len = environment_len(env);
|
|
char** ret = stringlist_alloc(len+1);
|
|
for ( int i = 0; i < len; i++ ) {
|
|
char* name = env.names[i];
|
|
char* value = env.values[i];
|
|
int result_len = strlen(name) + strlen(value) + 1;
|
|
ret[i] = calloc(result_len+1, sizeof(char));
|
|
strcat(ret[i], name);
|
|
strcat(ret[i], "=");
|
|
strcat(ret[i], value);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
char** adjusted_environment(const char* filename, char* const envp[]) {
|
|
if ( !envp ) {
|
|
return NULL;
|
|
}
|
|
|
|
int envc = arr_len(envp);
|
|
|
|
char* appdir = NULL;
|
|
|
|
environment orig = environment_alloc(envc);
|
|
environment startup = environment_alloc(envc);
|
|
int orig_prefix_len = strlen(APPIMAGE_ORIG_PREFIX);
|
|
int startup_prefix_len = strlen(APPIMAGE_STARTUP_PREFIX);
|
|
for ( int i = 0; i < envc; i++ ) {
|
|
char* line = envp[i];
|
|
int name_size = strchr(line, '=')-line;
|
|
int val_size = strlen(line)-name_size-1;
|
|
|
|
if ( !strncmp(line, APPIMAGE_ORIG_PREFIX, orig_prefix_len) ) {
|
|
environment_append_item(orig, line+orig_prefix_len, name_size-orig_prefix_len,
|
|
line+name_size+1, val_size);
|
|
}
|
|
if ( !strncmp(line, APPIMAGE_STARTUP_PREFIX, startup_prefix_len) ) {
|
|
environment_append_item(startup, line+startup_prefix_len, name_size-startup_prefix_len,
|
|
line+name_size+1, val_size);
|
|
}
|
|
if ( !strncmp(line, APPDIR, strlen(APPDIR)) ) {
|
|
appdir = calloc(val_size+1, sizeof(char));
|
|
strncpy(appdir, line+name_size+1, val_size);
|
|
}
|
|
}
|
|
|
|
environment new_env = environment_alloc(envc);
|
|
if ( appdir && strncmp(filename, appdir, strlen(appdir)) ) {
|
|
// we have a value for $APPDIR and are leaving it -- perform replacement
|
|
for ( int i = 0; i < envc; i++ ) {
|
|
char* line = envp[i];
|
|
if ( !strncmp(line, APPIMAGE_ORIG_PREFIX, strlen(APPIMAGE_ORIG_PREFIX)) ||
|
|
!strncmp(line, APPIMAGE_STARTUP_PREFIX, strlen(APPIMAGE_STARTUP_PREFIX)) )
|
|
{
|
|
// we are not interested in the backup vars here, don't copy them over
|
|
continue;
|
|
}
|
|
|
|
int name_size = strchr(line, '=')-line;
|
|
int val_size = strlen(line)-name_size-1;
|
|
char* value = line+name_size+1;
|
|
int value_len = strlen(value);
|
|
|
|
int at_startup = environment_find_name(startup, line, name_size);
|
|
int at_original = environment_find_name(orig, line, name_size);
|
|
if ( at_startup == -1 || at_original == -1 ) {
|
|
// no information, just keep it
|
|
environment_append_item(new_env, line, name_size, value, value_len);
|
|
continue;
|
|
}
|
|
|
|
char* at_start = startup.values[at_startup];
|
|
int at_start_len = strlen(at_start);
|
|
char* at_orig = orig.values[at_original];
|
|
int at_orig_len = strlen(at_orig);
|
|
|
|
// TODO HACK: do not copy over empty vars
|
|
if ( strlen(at_orig) == 0 ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !strncmp(line+name_size+1, startup.values[at_startup], val_size) ) {
|
|
// nothing changed since startup, restore old value
|
|
environment_append_item(new_env, line, name_size, at_orig, at_orig_len);
|
|
continue;
|
|
}
|
|
|
|
int chars_added = value_len > at_start_len;
|
|
char* use_value = NULL;
|
|
if ( chars_added > 0 ) {
|
|
// something was added to the current value
|
|
// take _original_ value of the env var and append/prepend the same thing
|
|
use_value = calloc(strlen(at_orig) + chars_added + 1, sizeof(char));
|
|
if ( !strncmp(value, at_start, at_start_len) ) {
|
|
// append case
|
|
strcat(use_value, value);
|
|
strcat(use_value, at_orig + strlen(value));
|
|
}
|
|
else if ( !strncmp(value+(value_len-at_start_len), at_start, at_start_len) ) {
|
|
// prepend case
|
|
strcat(use_value, at_orig + strlen(value));
|
|
strcat(use_value, value);
|
|
}
|
|
else {
|
|
// none of the above methods matched
|
|
// assume the value changed completely and simply keep what the application set
|
|
free(use_value);
|
|
use_value = NULL;
|
|
}
|
|
}
|
|
if ( !use_value ) {
|
|
environment_append_item(new_env, line, name_size, value, value_len);
|
|
}
|
|
else {
|
|
environment_append_item(new_env, line, name_size, use_value, strlen(use_value));
|
|
free(use_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
char** ret = NULL;
|
|
if ( environment_len(new_env) > 0 ) {
|
|
ret = environment_to_stringlist(new_env);
|
|
}
|
|
else {
|
|
// nothing changed
|
|
ret = stringlist_alloc(envc+1);
|
|
for ( int i = 0; i < envc; i++ ) {
|
|
int len = strlen(envp[i]);
|
|
ret[i] = calloc(len+1, sizeof(char));
|
|
strncpy(ret[i], envp[i], len);
|
|
}
|
|
}
|
|
environment_free(orig);
|
|
environment_free(startup);
|
|
environment_free(new_env);
|
|
free(appdir);
|
|
return ret;
|
|
}
|
|
|
|
int execve(const char* filename, char* const argv[], char* const envp[]) {
|
|
char** new_envp = adjusted_environment(filename, envp);
|
|
old_execve = dlsym(RTLD_NEXT, "execve");
|
|
int ret = old_execve(filename, argv, new_envp);
|
|
stringlist_free(new_envp);
|
|
//printf(">>> custom execve()!\n");
|
|
return ret;
|
|
}
|
|
|
|
int execv(const char* filename, char* const argv[]) {
|
|
char** new_envp = adjusted_environment(filename, environ);
|
|
old_execve = dlsym(RTLD_NEXT, "execve");
|
|
int ret = old_execve(filename, argv, new_envp);
|
|
stringlist_free(new_envp);
|
|
//printf(">>> custom execv()!\n");
|
|
return ret;
|
|
}
|
|
|
|
int execvpe(const char* filename, char* const argv[], char* const envp[]) {
|
|
// TODO: might not be full path
|
|
char** new_envp = adjusted_environment(filename, envp);
|
|
old_execvpe = dlsym(RTLD_NEXT, "execvpe");
|
|
int ret = old_execvpe(filename, argv, new_envp);
|
|
stringlist_free(new_envp);
|
|
//printf(">>> custom execvpe()!\n");
|
|
return ret;
|
|
}
|
|
|
|
int execvp(const char* filename, char* const argv[]) {
|
|
// TODO: might not be full path
|
|
char** new_envp = adjusted_environment(filename, environ);
|
|
old_execvpe = dlsym(RTLD_NEXT, "execvpe");
|
|
int ret = old_execvpe(filename, argv, new_envp);
|
|
stringlist_free(new_envp);
|
|
//printf(">>> custom execvp()!\n");
|
|
return ret;
|
|
}
|