mirror of
https://github.com/yuzu-emu/AppImageKit-checkrt.git
synced 2024-12-12 18:44:18 +01:00
Restore environment from parent for external processes
This commit is contained in:
parent
5f97d55211
commit
6059df77c3
28
Makefile
28
Makefile
@ -2,20 +2,37 @@ CFLAGS ?= -O2 -Wall -Wextra
|
||||
LDFLAGS += -s
|
||||
BIN = AppRun_patched
|
||||
LIB = exec.so
|
||||
EXEC_TEST = exec_test
|
||||
ENV_TEST = env_test
|
||||
|
||||
checkrt: $(BIN) $(LIB)
|
||||
|
||||
all: $(BIN) $(LIB)
|
||||
test: $(EXEC_TEST) $(ENV_TEST)
|
||||
|
||||
all: checkrt test
|
||||
|
||||
clean:
|
||||
-rm -f $(BIN) $(LIB) *.o AppRun.c AppRun_patched.c
|
||||
-rm -f $(BIN) $(LIB) $(EXEC_TEST) $(ENV_TEST) *.o AppRun.c AppRun_patched.c
|
||||
|
||||
$(BIN): AppRun_patched.o checkrt.o
|
||||
$(BIN): AppRun_patched.o checkrt.o env.o
|
||||
|
||||
$(LIB): exec.o
|
||||
$(LIB): exec.o env.o
|
||||
$(CC) -shared $(LDFLAGS) -o $@ $^ -ldl
|
||||
|
||||
AppRun_patched.o checkrt.o: CFLAGS += -include checkrt.h
|
||||
exec.o: CFLAGS += -fPIC -std=c99
|
||||
exec.o env.o: CFLAGS += -fPIC
|
||||
|
||||
$(EXEC_TEST): CFLAGS += -DEXEC_TEST
|
||||
$(EXEC_TEST): exec.c env.c
|
||||
$(CC) -o $@ $(CFLAGS) $^ -ldl
|
||||
|
||||
$(ENV_TEST): CFLAGS += -DENV_TEST
|
||||
$(ENV_TEST): env.c
|
||||
$(CC) -o $@ $(CFLAGS) $^
|
||||
|
||||
run_tests: $(EXEC_TEST) $(ENV_TEST)
|
||||
./$(ENV_TEST)
|
||||
./$(EXEC_TEST)
|
||||
|
||||
AppRun_patched.c: AppRun.c
|
||||
patch -p1 --output $@ < AppRun.c.patch
|
||||
@ -23,3 +40,4 @@ AppRun_patched.c: AppRun.c
|
||||
AppRun.c:
|
||||
wget -c "https://raw.githubusercontent.com/AppImage/AppImageKit/appimagetool/master/src/AppRun.c"
|
||||
|
||||
.PHONY: checkrt test run_tests all clean
|
||||
|
18
README.md
18
README.md
@ -16,4 +16,20 @@ You would have to know the library version of the host system and decide whether
|
||||
application is started. This is exactly what the patched AppRun binary does.
|
||||
It will search for `usr/optional/libstdc++/libstdc++.so.6` and `usr/optional/libgcc_s/libgcc_s.so.1` inside the AppImage or AppDir.
|
||||
If found it will compare their internal versions with the ones found on the system and prepend their paths to `LD_LIBRARY_PATH` if necessary.
|
||||
You should also put `exec.so` into `usr/optional`.
|
||||
|
||||
You should also put `exec.so` into `usr/optional`. This exec.so library is intended to restore the environment of the AppImage to its parent.
|
||||
This is done to avoid library clashing of bundled libraries with external processes. e.g when running the web browser
|
||||
|
||||
The intended usage is as follows:
|
||||
|
||||
1. This library is injected to the dynamic loader through LD_PRELOAD
|
||||
automatically in AppRun **only** if `usr/optional/exec.so` exists:
|
||||
e.g `LD_PRELOAD=$APPDIR/usr/optional/exec.so My.AppImage`
|
||||
|
||||
2. This library will intercept calls to new processes and will detect whether
|
||||
those calls are for binaries within the AppImage bundle or external ones.
|
||||
|
||||
3. In case it's an internal process, it will not change anything.
|
||||
In case it's an external process, it will restore the environment of
|
||||
the AppImage parent by reading `/proc/[pid]/environ`.
|
||||
This is the conservative approach taken.
|
||||
|
12
checkrt.c
12
checkrt.c
@ -103,12 +103,18 @@ void checkrt(char *usr_in_appdir)
|
||||
if (gcc_bundle_ver > gcc_sys_ver)
|
||||
bundle_gcc = 1;
|
||||
|
||||
if (bundle_cxx == 1 || bundle_gcc == 1) {
|
||||
char *exec_file = malloc(strlen(usr_in_appdir) + 1 + strlen(EXEC_SO) + 1);
|
||||
sprintf(exec_file, "%s/%s", usr_in_appdir, EXEC_SO);
|
||||
f = fopen(exec_file, "r");
|
||||
if (f) {
|
||||
char *old_ld_preload = getenv("LD_PRELOAD");
|
||||
optional_ld_preload = malloc(strlen(EXEC_SO) + (old_ld_preload ? 1+strlen(old_ld_preload) : 0) + 13 + len);
|
||||
sprintf(optional_ld_preload, "LD_PRELOAD=%s/" EXEC_SO "%s%s", usr_in_appdir,
|
||||
sprintf(optional_ld_preload, "LD_PRELOAD=%s%s%s", exec_file,
|
||||
old_ld_preload ? ":" : "", old_ld_preload ? old_ld_preload : "");
|
||||
DEBUG("optional_ld_preload: %s\n", optional_ld_preload);
|
||||
fclose(f);
|
||||
}
|
||||
free(exec_file);
|
||||
|
||||
if (bundle_cxx == 1 && bundle_gcc == 0) {
|
||||
optional_ld_library_path = malloc(strlen(CXXDIR) + 3 + len);
|
||||
@ -124,6 +130,6 @@ void checkrt(char *usr_in_appdir)
|
||||
sprintf(optional_ld_library_path, "%s", "");
|
||||
}
|
||||
|
||||
DEBUG("optional_ld_library_path: %s\noptional_ld_preload: %s\n", optional_ld_library_path, optional_ld_preload);
|
||||
DEBUG("optional_ld_library_path: %s\n", optional_ld_library_path);
|
||||
}
|
||||
|
||||
|
1
debug.h
1
debug.h
@ -2,6 +2,7 @@
|
||||
#define DEBUG_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define DEBUG(...) do { \
|
||||
if (getenv("APPIMAGE_CHECKRT_DEBUG")) \
|
||||
|
120
env.c
Executable file
120
env.c
Executable file
@ -0,0 +1,120 @@
|
||||
/* Copyright (c) 2018 Pablo Marcos Oltra <pablo.marcos.oltra@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "env.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
static char** env_allocate(size_t size) {
|
||||
return calloc(size + 1, sizeof(char*));
|
||||
}
|
||||
|
||||
void env_free(char* const *env) {
|
||||
size_t len = 0;
|
||||
while (env[len] != 0) {
|
||||
free(env[len]);
|
||||
len++;
|
||||
}
|
||||
free((char**)env);
|
||||
}
|
||||
|
||||
static size_t get_number_of_variables(FILE *file, char **buffer, size_t *len) {
|
||||
size_t number = 0;
|
||||
|
||||
if (getline(buffer, len, file) < 0)
|
||||
return -1;
|
||||
|
||||
char *ptr = *buffer;
|
||||
while (ptr < *buffer + *len) {
|
||||
size_t var_len = strlen(ptr);
|
||||
ptr += var_len + 1;
|
||||
if (var_len == 0)
|
||||
break;
|
||||
number++;
|
||||
}
|
||||
|
||||
return number != 0 ? (ssize_t)number : -1;
|
||||
}
|
||||
|
||||
static char* const* env_from_buffer(FILE *file) {
|
||||
char *buffer = NULL;
|
||||
size_t len = 0;
|
||||
size_t num_vars = get_number_of_variables(file, &buffer, &len);
|
||||
char** env = env_allocate(num_vars);
|
||||
|
||||
size_t n = 0;
|
||||
char *ptr = buffer;
|
||||
while (ptr < buffer + len && n < num_vars) {
|
||||
size_t var_len = strlen(ptr);
|
||||
if (var_len == 0)
|
||||
break;
|
||||
|
||||
env[n] = calloc(sizeof(char*), var_len + 1);
|
||||
strncpy(env[n], ptr, var_len + 1);
|
||||
DEBUG("\tenv var copied: %s\n", env[n]);
|
||||
ptr += var_len + 1;
|
||||
n++;
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
static char* const* read_env_from_process(pid_t pid) {
|
||||
char buffer[256] = {0};
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "/proc/%d/environ", pid);
|
||||
DEBUG("Reading env from parent process: %s\n", buffer);
|
||||
FILE *env_file = fopen(buffer, "r");
|
||||
if (!env_file) {
|
||||
DEBUG("Error reading file: %s (%s)\n", buffer, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* const* env = env_from_buffer(env_file);
|
||||
fclose(env_file);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
char* const* read_parent_env() {
|
||||
pid_t ppid = getppid();
|
||||
return read_env_from_process(ppid);
|
||||
}
|
||||
|
||||
#ifdef ENV_TEST
|
||||
int main() {
|
||||
putenv("APPIMAGE_CHECKRT_DEBUG=1");
|
||||
DEBUG("ENV TEST\n");
|
||||
char **env = NULL;
|
||||
read_parent_env(&env);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
9
env.h
Executable file
9
env.h
Executable file
@ -0,0 +1,9 @@
|
||||
#ifndef ENV_H
|
||||
#define END_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
char* const* read_parent_env();
|
||||
void env_free(char* const *env);
|
||||
|
||||
#endif
|
384
exec.c
384
exec.c
@ -1,317 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Sven Brauch <mail@svenbrauch.de>
|
||||
/* Copyright (c) 2018 Pablo Marcos Oltra <pablo.marcos.oltra@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* 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.
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* 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.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
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
|
||||
*/
|
||||
/*
|
||||
* This exec.so library is intended to restore the environment of the AppImage's
|
||||
* parent process. This is done to avoid library clashing of bundled libraries
|
||||
* with external processes. e.g when running the web browser
|
||||
*
|
||||
* The intended usage is as follows:
|
||||
*
|
||||
* 1. This library is injected to the dynamic loader through LD_PRELOAD
|
||||
* automatically in AppRun **only** if `usr/optional/exec.so` exists:
|
||||
* e.g `LD_PRELOAD=$APPDIR/usr/optional/exec.so My.AppImage`
|
||||
*
|
||||
* 2. This library will intercept calls to new processes and will detect whether
|
||||
* those calls are for binaries within the AppImage bundle or external ones.
|
||||
*
|
||||
* 3. In case it's an internal process, it will not change anything.
|
||||
* In case it's an external process, it will restore the environment of
|
||||
* the AppImage parent by reading `/proc/[pid]/environ`.
|
||||
* This is the conservative approach taken.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "env.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
typedef ssize_t (*execve_func_t)(const char* filename, char* const argv[], char* const envp[]);
|
||||
static execve_func_t old_execve = NULL;
|
||||
typedef int (*execve_func_t)(const char *filename, char *const argv[], char *const envp[]);
|
||||
|
||||
typedef ssize_t (*execvp_func_t)(const char* filename, char* const argv[]);
|
||||
//static execvp_func_t old_execvp = NULL;
|
||||
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||
|
||||
// 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;
|
||||
static const char* get_fullpath(const char *filename) {
|
||||
// Try to get the canonical path in case it's a relative path or symbolic
|
||||
// link. Otherwise, use which to get the fullpath of the binary
|
||||
char *fullpath = canonicalize_file_name(filename);
|
||||
DEBUG("filename %s, canonical path %s\n", filename, fullpath);
|
||||
if (fullpath)
|
||||
return fullpath;
|
||||
|
||||
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;
|
||||
return filename;
|
||||
}
|
||||
|
||||
int arr_len(char* const x[]) {
|
||||
int len = 0;
|
||||
while ( x[len] != 0 ) {
|
||||
len++;
|
||||
static int is_external_process(const char *filename) {
|
||||
const char *appdir = getenv("APPDIR");
|
||||
if (!appdir)
|
||||
return 0;
|
||||
DEBUG("APPDIR = %s\n", appdir);
|
||||
|
||||
return strncmp(filename, appdir, MIN(strlen(filename), strlen(appdir)));
|
||||
}
|
||||
|
||||
static int exec_common(execve_func_t function, const char *filename, char* const argv[], char* const envp[]) {
|
||||
const char *fullpath = get_fullpath(filename);
|
||||
DEBUG("filename %s, fullpath %s\n", filename, fullpath);
|
||||
char* const *env = envp;
|
||||
if (is_external_process(fullpath)) {
|
||||
DEBUG("External process detected. Restoring env vars from parent %d\n", getppid());
|
||||
env = read_parent_env();
|
||||
if (!env)
|
||||
env = envp;
|
||||
else
|
||||
DEBUG("Error restoring env vars from parent\n");
|
||||
}
|
||||
return len;
|
||||
}
|
||||
int ret = function(filename, argv, env);
|
||||
|
||||
void stringlist_free(char* const envp[]) {
|
||||
if ( envp ) {
|
||||
for ( int i = 0; i < arr_len(envp); i++ ) {
|
||||
free(envp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fullpath != filename)
|
||||
free((char*)fullpath);
|
||||
if (env != envp)
|
||||
env_free(env);
|
||||
|
||||
char** stringlist_alloc(int size) {
|
||||
char** ret = calloc(size, sizeof(char*));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int environment_len(const environment env) {
|
||||
return arr_len(env.names);
|
||||
int execve(const char *filename, char *const argv[], char *const envp[]) {
|
||||
DEBUG("execve call hijacked: %s\n", filename);
|
||||
execve_func_t execve_orig = dlsym(RTLD_NEXT, "execve");
|
||||
if (!execve_orig)
|
||||
DEBUG("Error getting execve original symbol: %s\n", strerror(errno));
|
||||
|
||||
return exec_common(execve_orig, filename, argv, envp);
|
||||
}
|
||||
|
||||
void environment_free(environment env) {
|
||||
stringlist_free(env.names);
|
||||
stringlist_free(env.values);
|
||||
int execv(const char *filename, char *const argv[]) {
|
||||
DEBUG("execv call hijacked: %s\n", filename);
|
||||
return execve(filename, argv, environ);
|
||||
}
|
||||
|
||||
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 execvpe(const char *filename, char *const argv[], char *const envp[]) {
|
||||
DEBUG("execvpe call hijacked: %s\n", filename);
|
||||
execve_func_t execve_orig = dlsym(RTLD_NEXT, "execvpe");
|
||||
if (!execve_orig)
|
||||
DEBUG("Error getting execvpe original symbol: %s\n", strerror(errno));
|
||||
|
||||
return exec_common(execve_orig, filename, argv, envp);
|
||||
}
|
||||
|
||||
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;
|
||||
int execvp(const char *filename, char *const argv[]) {
|
||||
DEBUG("execvp call hijacked: %s\n", filename);
|
||||
return execvpe(filename, argv, environ);
|
||||
}
|
||||
|
||||
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);
|
||||
DEBUG("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);
|
||||
DEBUG("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);
|
||||
DEBUG("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);
|
||||
DEBUG("custom execvp()!\n");
|
||||
return ret;
|
||||
#ifdef EXEC_TEST
|
||||
int main(int argc, char *argv[]) {
|
||||
putenv("APPIMAGE_CHECKRT_DEBUG=1");
|
||||
DEBUG("EXEC TEST\n");
|
||||
execv("./env_test", argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user