Android Build Tools
This commit is contained in:
@ -0,0 +1,272 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
""" This module compiles the intercept library. """
|
||||
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import tempfile
|
||||
import shutil
|
||||
import contextlib
|
||||
import logging
|
||||
|
||||
__all__ = ["build_libear"]
|
||||
|
||||
|
||||
def build_libear(compiler, dst_dir):
|
||||
"""Returns the full path to the 'libear' library."""
|
||||
|
||||
try:
|
||||
src_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
toolset = make_toolset(src_dir)
|
||||
toolset.set_compiler(compiler)
|
||||
toolset.set_language_standard("c99")
|
||||
toolset.add_definitions(["-D_GNU_SOURCE"])
|
||||
|
||||
configure = do_configure(toolset)
|
||||
configure.check_function_exists("execve", "HAVE_EXECVE")
|
||||
configure.check_function_exists("execv", "HAVE_EXECV")
|
||||
configure.check_function_exists("execvpe", "HAVE_EXECVPE")
|
||||
configure.check_function_exists("execvp", "HAVE_EXECVP")
|
||||
configure.check_function_exists("execvP", "HAVE_EXECVP2")
|
||||
configure.check_function_exists("exect", "HAVE_EXECT")
|
||||
configure.check_function_exists("execl", "HAVE_EXECL")
|
||||
configure.check_function_exists("execlp", "HAVE_EXECLP")
|
||||
configure.check_function_exists("execle", "HAVE_EXECLE")
|
||||
configure.check_function_exists("posix_spawn", "HAVE_POSIX_SPAWN")
|
||||
configure.check_function_exists("posix_spawnp", "HAVE_POSIX_SPAWNP")
|
||||
configure.check_symbol_exists(
|
||||
"_NSGetEnviron", "crt_externs.h", "HAVE_NSGETENVIRON"
|
||||
)
|
||||
configure.write_by_template(
|
||||
os.path.join(src_dir, "config.h.in"), os.path.join(dst_dir, "config.h")
|
||||
)
|
||||
|
||||
target = create_shared_library("ear", toolset)
|
||||
target.add_include(dst_dir)
|
||||
target.add_sources("ear.c")
|
||||
target.link_against(toolset.dl_libraries())
|
||||
target.link_against(["pthread"])
|
||||
target.build_release(dst_dir)
|
||||
|
||||
return os.path.join(dst_dir, target.name)
|
||||
|
||||
except Exception:
|
||||
logging.info("Could not build interception library.", exc_info=True)
|
||||
return None
|
||||
|
||||
|
||||
def execute(cmd, *args, **kwargs):
|
||||
"""Make subprocess execution silent."""
|
||||
|
||||
import subprocess
|
||||
|
||||
kwargs.update({"stdout": subprocess.PIPE, "stderr": subprocess.STDOUT})
|
||||
return subprocess.check_call(cmd, *args, **kwargs)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def TemporaryDirectory(**kwargs):
|
||||
name = tempfile.mkdtemp(**kwargs)
|
||||
try:
|
||||
yield name
|
||||
finally:
|
||||
shutil.rmtree(name)
|
||||
|
||||
|
||||
class Toolset(object):
|
||||
"""Abstract class to represent different toolset."""
|
||||
|
||||
def __init__(self, src_dir):
|
||||
self.src_dir = src_dir
|
||||
self.compiler = None
|
||||
self.c_flags = []
|
||||
|
||||
def set_compiler(self, compiler):
|
||||
"""part of public interface"""
|
||||
self.compiler = compiler
|
||||
|
||||
def set_language_standard(self, standard):
|
||||
"""part of public interface"""
|
||||
self.c_flags.append("-std=" + standard)
|
||||
|
||||
def add_definitions(self, defines):
|
||||
"""part of public interface"""
|
||||
self.c_flags.extend(defines)
|
||||
|
||||
def dl_libraries(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def shared_library_name(self, name):
|
||||
raise NotImplementedError()
|
||||
|
||||
def shared_library_c_flags(self, release):
|
||||
extra = ["-DNDEBUG", "-O3"] if release else []
|
||||
return extra + ["-fPIC"] + self.c_flags
|
||||
|
||||
def shared_library_ld_flags(self, release, name):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class DarwinToolset(Toolset):
|
||||
def __init__(self, src_dir):
|
||||
Toolset.__init__(self, src_dir)
|
||||
|
||||
def dl_libraries(self):
|
||||
return []
|
||||
|
||||
def shared_library_name(self, name):
|
||||
return "lib" + name + ".dylib"
|
||||
|
||||
def shared_library_ld_flags(self, release, name):
|
||||
extra = ["-dead_strip"] if release else []
|
||||
return extra + ["-dynamiclib", "-install_name", "@rpath/" + name]
|
||||
|
||||
|
||||
class UnixToolset(Toolset):
|
||||
def __init__(self, src_dir):
|
||||
Toolset.__init__(self, src_dir)
|
||||
|
||||
def dl_libraries(self):
|
||||
return []
|
||||
|
||||
def shared_library_name(self, name):
|
||||
return "lib" + name + ".so"
|
||||
|
||||
def shared_library_ld_flags(self, release, name):
|
||||
extra = [] if release else []
|
||||
return extra + ["-shared", "-Wl,-soname," + name]
|
||||
|
||||
|
||||
class LinuxToolset(UnixToolset):
|
||||
def __init__(self, src_dir):
|
||||
UnixToolset.__init__(self, src_dir)
|
||||
|
||||
def dl_libraries(self):
|
||||
return ["dl"]
|
||||
|
||||
|
||||
def make_toolset(src_dir):
|
||||
platform = sys.platform
|
||||
if platform in {"win32", "cygwin"}:
|
||||
raise RuntimeError("not implemented on this platform")
|
||||
elif platform == "darwin":
|
||||
return DarwinToolset(src_dir)
|
||||
elif platform in {"linux", "linux2"}:
|
||||
return LinuxToolset(src_dir)
|
||||
else:
|
||||
return UnixToolset(src_dir)
|
||||
|
||||
|
||||
class Configure(object):
|
||||
def __init__(self, toolset):
|
||||
self.ctx = toolset
|
||||
self.results = {"APPLE": sys.platform == "darwin"}
|
||||
|
||||
def _try_to_compile_and_link(self, source):
|
||||
try:
|
||||
with TemporaryDirectory() as work_dir:
|
||||
src_file = "check.c"
|
||||
with open(os.path.join(work_dir, src_file), "w") as handle:
|
||||
handle.write(source)
|
||||
|
||||
execute([self.ctx.compiler, src_file] + self.ctx.c_flags, cwd=work_dir)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def check_function_exists(self, function, name):
|
||||
template = "int FUNCTION(); int main() { return FUNCTION(); }"
|
||||
source = template.replace("FUNCTION", function)
|
||||
|
||||
logging.debug("Checking function %s", function)
|
||||
found = self._try_to_compile_and_link(source)
|
||||
logging.debug(
|
||||
"Checking function %s -- %s", function, "found" if found else "not found"
|
||||
)
|
||||
self.results.update({name: found})
|
||||
|
||||
def check_symbol_exists(self, symbol, include, name):
|
||||
template = """#include <INCLUDE>
|
||||
int main() { return ((int*)(&SYMBOL))[0]; }"""
|
||||
source = template.replace("INCLUDE", include).replace("SYMBOL", symbol)
|
||||
|
||||
logging.debug("Checking symbol %s", symbol)
|
||||
found = self._try_to_compile_and_link(source)
|
||||
logging.debug(
|
||||
"Checking symbol %s -- %s", symbol, "found" if found else "not found"
|
||||
)
|
||||
self.results.update({name: found})
|
||||
|
||||
def write_by_template(self, template, output):
|
||||
def transform(line, definitions):
|
||||
|
||||
pattern = re.compile(r"^#cmakedefine\s+(\S+)")
|
||||
m = pattern.match(line)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
if key not in definitions or not definitions[key]:
|
||||
return "/* #undef {0} */{1}".format(key, os.linesep)
|
||||
else:
|
||||
return "#define {0}{1}".format(key, os.linesep)
|
||||
return line
|
||||
|
||||
with open(template, "r") as src_handle:
|
||||
logging.debug("Writing config to %s", output)
|
||||
with open(output, "w") as dst_handle:
|
||||
for line in src_handle:
|
||||
dst_handle.write(transform(line, self.results))
|
||||
|
||||
|
||||
def do_configure(toolset):
|
||||
return Configure(toolset)
|
||||
|
||||
|
||||
class SharedLibrary(object):
|
||||
def __init__(self, name, toolset):
|
||||
self.name = toolset.shared_library_name(name)
|
||||
self.ctx = toolset
|
||||
self.inc = []
|
||||
self.src = []
|
||||
self.lib = []
|
||||
|
||||
def add_include(self, directory):
|
||||
self.inc.extend(["-I", directory])
|
||||
|
||||
def add_sources(self, source):
|
||||
self.src.append(source)
|
||||
|
||||
def link_against(self, libraries):
|
||||
self.lib.extend(["-l" + lib for lib in libraries])
|
||||
|
||||
def build_release(self, directory):
|
||||
for src in self.src:
|
||||
logging.debug("Compiling %s", src)
|
||||
execute(
|
||||
[
|
||||
self.ctx.compiler,
|
||||
"-c",
|
||||
os.path.join(self.ctx.src_dir, src),
|
||||
"-o",
|
||||
src + ".o",
|
||||
]
|
||||
+ self.inc
|
||||
+ self.ctx.shared_library_c_flags(True),
|
||||
cwd=directory,
|
||||
)
|
||||
logging.debug("Linking %s", self.name)
|
||||
execute(
|
||||
[self.ctx.compiler]
|
||||
+ [src + ".o" for src in self.src]
|
||||
+ ["-o", self.name]
|
||||
+ self.lib
|
||||
+ self.ctx.shared_library_ld_flags(True, self.name),
|
||||
cwd=directory,
|
||||
)
|
||||
|
||||
|
||||
def create_shared_library(name, toolset):
|
||||
return SharedLibrary(name, toolset)
|
||||
@ -0,0 +1,22 @@
|
||||
/* -*- coding: utf-8 -*-
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#cmakedefine HAVE_EXECVE
|
||||
#cmakedefine HAVE_EXECV
|
||||
#cmakedefine HAVE_EXECVPE
|
||||
#cmakedefine HAVE_EXECVP
|
||||
#cmakedefine HAVE_EXECVP2
|
||||
#cmakedefine HAVE_EXECT
|
||||
#cmakedefine HAVE_EXECL
|
||||
#cmakedefine HAVE_EXECLP
|
||||
#cmakedefine HAVE_EXECLE
|
||||
#cmakedefine HAVE_POSIX_SPAWN
|
||||
#cmakedefine HAVE_POSIX_SPAWNP
|
||||
#cmakedefine HAVE_NSGETENVIRON
|
||||
|
||||
#cmakedefine APPLE
|
||||
@ -0,0 +1,605 @@
|
||||
/* -*- coding: utf-8 -*-
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file implements a shared library. This library can be pre-loaded by
|
||||
* the dynamic linker of the Operating System (OS). It implements a few function
|
||||
* related to process creation. By pre-load this library the executed process
|
||||
* uses these functions instead of those from the standard library.
|
||||
*
|
||||
* The idea here is to inject a logic before call the real methods. The logic is
|
||||
* to dump the call into a file. To call the real method this library is doing
|
||||
* the job of the dynamic linker.
|
||||
*
|
||||
* The only input for the log writing is about the destination directory.
|
||||
* This is passed as environment variable.
|
||||
*/
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
#include "config.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
|
||||
#include <spawn.h>
|
||||
#endif
|
||||
|
||||
#if defined HAVE_NSGETENVIRON
|
||||
#include <crt_externs.h>
|
||||
#else
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
|
||||
#ifdef APPLE
|
||||
#define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE"
|
||||
#define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
|
||||
#define ENV_SIZE 3
|
||||
#else
|
||||
#define ENV_PRELOAD "LD_PRELOAD"
|
||||
#define ENV_SIZE 2
|
||||
#endif
|
||||
|
||||
#define DLSYM(TYPE_, VAR_, SYMBOL_) \
|
||||
union { \
|
||||
void *from; \
|
||||
TYPE_ to; \
|
||||
} cast; \
|
||||
if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \
|
||||
perror("bear: dlsym"); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
TYPE_ const VAR_ = cast.to;
|
||||
|
||||
typedef char const *bear_env_t[ENV_SIZE];
|
||||
|
||||
static int bear_capture_env_t(bear_env_t *env);
|
||||
static int bear_reset_env_t(bear_env_t *env);
|
||||
static void bear_release_env_t(bear_env_t *env);
|
||||
static char const **bear_update_environment(char *const envp[],
|
||||
bear_env_t *env);
|
||||
static char const **bear_update_environ(char const **in, char const *key,
|
||||
char const *value);
|
||||
static char **bear_get_environment();
|
||||
static void bear_report_call(char const *fun, char const *const argv[]);
|
||||
static char const **bear_strings_build(char const *arg, va_list *ap);
|
||||
static char const **bear_strings_copy(char const **const in);
|
||||
static char const **bear_strings_append(char const **in, char const *e);
|
||||
static size_t bear_strings_length(char const *const *in);
|
||||
static void bear_strings_release(char const **);
|
||||
|
||||
static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD
|
||||
#ifdef ENV_FLAT
|
||||
,
|
||||
ENV_FLAT
|
||||
#endif
|
||||
};
|
||||
|
||||
static bear_env_t initial_env = {0, 0
|
||||
#ifdef ENV_FLAT
|
||||
,
|
||||
0
|
||||
#endif
|
||||
};
|
||||
|
||||
static int initialized = 0;
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static void on_load(void) __attribute__((constructor));
|
||||
static void on_unload(void) __attribute__((destructor));
|
||||
|
||||
#ifdef HAVE_EXECVE
|
||||
static int call_execve(const char *path, char *const argv[],
|
||||
char *const envp[]);
|
||||
#endif
|
||||
#ifdef HAVE_EXECVP
|
||||
static int call_execvp(const char *file, char *const argv[]);
|
||||
#endif
|
||||
#ifdef HAVE_EXECVPE
|
||||
static int call_execvpe(const char *file, char *const argv[],
|
||||
char *const envp[]);
|
||||
#endif
|
||||
#ifdef HAVE_EXECVP2
|
||||
static int call_execvP(const char *file, const char *search_path,
|
||||
char *const argv[]);
|
||||
#endif
|
||||
#ifdef HAVE_EXECT
|
||||
static int call_exect(const char *path, char *const argv[], char *const envp[]);
|
||||
#endif
|
||||
#ifdef HAVE_POSIX_SPAWN
|
||||
static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *restrict attrp,
|
||||
char *const argv[restrict],
|
||||
char *const envp[restrict]);
|
||||
#endif
|
||||
#ifdef HAVE_POSIX_SPAWNP
|
||||
static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *restrict attrp,
|
||||
char *const argv[restrict],
|
||||
char *const envp[restrict]);
|
||||
#endif
|
||||
|
||||
/* Initialization method to Captures the relevant environment variables.
|
||||
*/
|
||||
|
||||
static void on_load(void) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
if (!initialized)
|
||||
initialized = bear_capture_env_t(&initial_env);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
static void on_unload(void) {
|
||||
pthread_mutex_lock(&mutex);
|
||||
bear_release_env_t(&initial_env);
|
||||
initialized = 0;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
/* These are the methods we are try to hijack.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_EXECVE
|
||||
int execve(const char *path, char *const argv[], char *const envp[]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_execve(path, argv, envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECV
|
||||
#ifndef HAVE_EXECVE
|
||||
#error can not implement execv without execve
|
||||
#endif
|
||||
int execv(const char *path, char *const argv[]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
char *const *envp = bear_get_environment();
|
||||
return call_execve(path, argv, envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECVPE
|
||||
int execvpe(const char *file, char *const argv[], char *const envp[]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_execvpe(file, argv, envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECVP
|
||||
int execvp(const char *file, char *const argv[]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_execvp(file, argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECVP2
|
||||
int execvP(const char *file, const char *search_path, char *const argv[]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_execvP(file, search_path, argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECT
|
||||
int exect(const char *path, char *const argv[], char *const envp[]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_exect(path, argv, envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECL
|
||||
#ifndef HAVE_EXECVE
|
||||
#error can not implement execl without execve
|
||||
#endif
|
||||
int execl(const char *path, const char *arg, ...) {
|
||||
va_list args;
|
||||
va_start(args, arg);
|
||||
char const **argv = bear_strings_build(arg, &args);
|
||||
va_end(args);
|
||||
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
char *const *envp = bear_get_environment();
|
||||
int const result = call_execve(path, (char *const *)argv, envp);
|
||||
|
||||
bear_strings_release(argv);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECLP
|
||||
#ifndef HAVE_EXECVP
|
||||
#error can not implement execlp without execvp
|
||||
#endif
|
||||
int execlp(const char *file, const char *arg, ...) {
|
||||
va_list args;
|
||||
va_start(args, arg);
|
||||
char const **argv = bear_strings_build(arg, &args);
|
||||
va_end(args);
|
||||
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
int const result = call_execvp(file, (char *const *)argv);
|
||||
|
||||
bear_strings_release(argv);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECLE
|
||||
#ifndef HAVE_EXECVE
|
||||
#error can not implement execle without execve
|
||||
#endif
|
||||
// int execle(const char *path, const char *arg, ..., char * const envp[]);
|
||||
int execle(const char *path, const char *arg, ...) {
|
||||
va_list args;
|
||||
va_start(args, arg);
|
||||
char const **argv = bear_strings_build(arg, &args);
|
||||
char const **envp = va_arg(args, char const **);
|
||||
va_end(args);
|
||||
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
int const result =
|
||||
call_execve(path, (char *const *)argv, (char *const *)envp);
|
||||
|
||||
bear_strings_release(argv);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POSIX_SPAWN
|
||||
int posix_spawn(pid_t *restrict pid, const char *restrict path,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *restrict attrp,
|
||||
char *const argv[restrict], char *const envp[restrict]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POSIX_SPAWNP
|
||||
int posix_spawnp(pid_t *restrict pid, const char *restrict file,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *restrict attrp,
|
||||
char *const argv[restrict], char *const envp[restrict]) {
|
||||
bear_report_call(__func__, (char const *const *)argv);
|
||||
return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* These are the methods which forward the call to the standard implementation.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_EXECVE
|
||||
static int call_execve(const char *path, char *const argv[],
|
||||
char *const envp[]) {
|
||||
typedef int (*func)(const char *, char *const *, char *const *);
|
||||
|
||||
DLSYM(func, fp, "execve");
|
||||
|
||||
char const **const menvp = bear_update_environment(envp, &initial_env);
|
||||
int const result = (*fp)(path, argv, (char *const *)menvp);
|
||||
bear_strings_release(menvp);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECVPE
|
||||
static int call_execvpe(const char *file, char *const argv[],
|
||||
char *const envp[]) {
|
||||
typedef int (*func)(const char *, char *const *, char *const *);
|
||||
|
||||
DLSYM(func, fp, "execvpe");
|
||||
|
||||
char const **const menvp = bear_update_environment(envp, &initial_env);
|
||||
int const result = (*fp)(file, argv, (char *const *)menvp);
|
||||
bear_strings_release(menvp);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECVP
|
||||
static int call_execvp(const char *file, char *const argv[]) {
|
||||
typedef int (*func)(const char *file, char *const argv[]);
|
||||
|
||||
DLSYM(func, fp, "execvp");
|
||||
|
||||
bear_env_t current_env;
|
||||
bear_capture_env_t(¤t_env);
|
||||
bear_reset_env_t(&initial_env);
|
||||
int const result = (*fp)(file, argv);
|
||||
bear_reset_env_t(¤t_env);
|
||||
bear_release_env_t(¤t_env);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECVP2
|
||||
static int call_execvP(const char *file, const char *search_path,
|
||||
char *const argv[]) {
|
||||
typedef int (*func)(const char *, const char *, char *const *);
|
||||
|
||||
DLSYM(func, fp, "execvP");
|
||||
|
||||
bear_env_t current_env;
|
||||
bear_capture_env_t(¤t_env);
|
||||
bear_reset_env_t(&initial_env);
|
||||
int const result = (*fp)(file, search_path, argv);
|
||||
bear_reset_env_t(¤t_env);
|
||||
bear_release_env_t(¤t_env);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_EXECT
|
||||
static int call_exect(const char *path, char *const argv[],
|
||||
char *const envp[]) {
|
||||
typedef int (*func)(const char *, char *const *, char *const *);
|
||||
|
||||
DLSYM(func, fp, "exect");
|
||||
|
||||
char const **const menvp = bear_update_environment(envp, &initial_env);
|
||||
int const result = (*fp)(path, argv, (char *const *)menvp);
|
||||
bear_strings_release(menvp);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POSIX_SPAWN
|
||||
static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *restrict attrp,
|
||||
char *const argv[restrict],
|
||||
char *const envp[restrict]) {
|
||||
typedef int (*func)(pid_t *restrict, const char *restrict,
|
||||
const posix_spawn_file_actions_t *,
|
||||
const posix_spawnattr_t *restrict, char *const *restrict,
|
||||
char *const *restrict);
|
||||
|
||||
DLSYM(func, fp, "posix_spawn");
|
||||
|
||||
char const **const menvp = bear_update_environment(envp, &initial_env);
|
||||
int const result =
|
||||
(*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
|
||||
bear_strings_release(menvp);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POSIX_SPAWNP
|
||||
static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
|
||||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *restrict attrp,
|
||||
char *const argv[restrict],
|
||||
char *const envp[restrict]) {
|
||||
typedef int (*func)(pid_t *restrict, const char *restrict,
|
||||
const posix_spawn_file_actions_t *,
|
||||
const posix_spawnattr_t *restrict, char *const *restrict,
|
||||
char *const *restrict);
|
||||
|
||||
DLSYM(func, fp, "posix_spawnp");
|
||||
|
||||
char const **const menvp = bear_update_environment(envp, &initial_env);
|
||||
int const result =
|
||||
(*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
|
||||
bear_strings_release(menvp);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* this method is to write log about the process creation. */
|
||||
|
||||
static void bear_report_call(char const *fun, char const *const argv[]) {
|
||||
static int const GS = 0x1d;
|
||||
static int const RS = 0x1e;
|
||||
static int const US = 0x1f;
|
||||
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
const char *cwd = getcwd(NULL, 0);
|
||||
if (0 == cwd) {
|
||||
perror("bear: getcwd");
|
||||
pthread_mutex_unlock(&mutex);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
char const *const out_dir = initial_env[0];
|
||||
size_t const path_max_length = strlen(out_dir) + 32;
|
||||
char filename[path_max_length];
|
||||
if (-1 ==
|
||||
snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
|
||||
perror("bear: snprintf");
|
||||
pthread_mutex_unlock(&mutex);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
FILE *fd = fopen(filename, "a+");
|
||||
if (0 == fd) {
|
||||
perror("bear: fopen");
|
||||
pthread_mutex_unlock(&mutex);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(fd, "%d%c", getpid(), RS);
|
||||
fprintf(fd, "%d%c", getppid(), RS);
|
||||
fprintf(fd, "%s%c", fun, RS);
|
||||
fprintf(fd, "%s%c", cwd, RS);
|
||||
size_t const argc = bear_strings_length(argv);
|
||||
for (size_t it = 0; it < argc; ++it) {
|
||||
fprintf(fd, "%s%c", argv[it], US);
|
||||
}
|
||||
fprintf(fd, "%c", GS);
|
||||
if (fclose(fd)) {
|
||||
perror("bear: fclose");
|
||||
pthread_mutex_unlock(&mutex);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
free((void *)cwd);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
/* update environment assure that children processes will copy the desired
|
||||
* behaviour */
|
||||
|
||||
static int bear_capture_env_t(bear_env_t *env) {
|
||||
int status = 1;
|
||||
for (size_t it = 0; it < ENV_SIZE; ++it) {
|
||||
char const *const env_value = getenv(env_names[it]);
|
||||
char const *const env_copy = (env_value) ? strdup(env_value) : env_value;
|
||||
(*env)[it] = env_copy;
|
||||
status &= (env_copy) ? 1 : 0;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int bear_reset_env_t(bear_env_t *env) {
|
||||
int status = 1;
|
||||
for (size_t it = 0; it < ENV_SIZE; ++it) {
|
||||
if ((*env)[it]) {
|
||||
setenv(env_names[it], (*env)[it], 1);
|
||||
} else {
|
||||
unsetenv(env_names[it]);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static void bear_release_env_t(bear_env_t *env) {
|
||||
for (size_t it = 0; it < ENV_SIZE; ++it) {
|
||||
free((void *)(*env)[it]);
|
||||
(*env)[it] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static char const **bear_update_environment(char *const envp[],
|
||||
bear_env_t *env) {
|
||||
char const **result = bear_strings_copy((char const **)envp);
|
||||
for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
|
||||
result = bear_update_environ(result, env_names[it], (*env)[it]);
|
||||
return result;
|
||||
}
|
||||
|
||||
static char const **bear_update_environ(char const *envs[], char const *key,
|
||||
char const *const value) {
|
||||
// find the key if it's there
|
||||
size_t const key_length = strlen(key);
|
||||
char const **it = envs;
|
||||
for (; (it) && (*it); ++it) {
|
||||
if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) &&
|
||||
('=' == (*it)[key_length]))
|
||||
break;
|
||||
}
|
||||
// allocate a environment entry
|
||||
size_t const value_length = strlen(value);
|
||||
size_t const env_length = key_length + value_length + 2;
|
||||
char *env = malloc(env_length);
|
||||
if (0 == env) {
|
||||
perror("bear: malloc [in env_update]");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
|
||||
perror("bear: snprintf");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// replace or append the environment entry
|
||||
if (it && *it) {
|
||||
free((void *)*it);
|
||||
*it = env;
|
||||
return envs;
|
||||
}
|
||||
return bear_strings_append(envs, env);
|
||||
}
|
||||
|
||||
static char **bear_get_environment() {
|
||||
#if defined HAVE_NSGETENVIRON
|
||||
return *_NSGetEnviron();
|
||||
#else
|
||||
return environ;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* util methods to deal with string arrays. environment and process arguments
|
||||
* are both represented as string arrays. */
|
||||
|
||||
static char const **bear_strings_build(char const *const arg, va_list *args) {
|
||||
char const **result = 0;
|
||||
size_t size = 0;
|
||||
for (char const *it = arg; it; it = va_arg(*args, char const *)) {
|
||||
result = realloc(result, (size + 1) * sizeof(char const *));
|
||||
if (0 == result) {
|
||||
perror("bear: realloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
char const *copy = strdup(it);
|
||||
if (0 == copy) {
|
||||
perror("bear: strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
result[size++] = copy;
|
||||
}
|
||||
result = realloc(result, (size + 1) * sizeof(char const *));
|
||||
if (0 == result) {
|
||||
perror("bear: realloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
result[size++] = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static char const **bear_strings_copy(char const **const in) {
|
||||
size_t const size = bear_strings_length(in);
|
||||
|
||||
char const **const result = malloc((size + 1) * sizeof(char const *));
|
||||
if (0 == result) {
|
||||
perror("bear: malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char const **out_it = result;
|
||||
for (char const *const *in_it = in; (in_it) && (*in_it); ++in_it, ++out_it) {
|
||||
*out_it = strdup(*in_it);
|
||||
if (0 == *out_it) {
|
||||
perror("bear: strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
*out_it = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static char const **bear_strings_append(char const **const in,
|
||||
char const *const e) {
|
||||
size_t size = bear_strings_length(in);
|
||||
char const **result = realloc(in, (size + 2) * sizeof(char const *));
|
||||
if (0 == result) {
|
||||
perror("bear: realloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
result[size++] = e;
|
||||
result[size++] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
static size_t bear_strings_length(char const *const *const in) {
|
||||
size_t result = 0;
|
||||
for (char const *const *it = in; (it) && (*it); ++it)
|
||||
++result;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void bear_strings_release(char const **in) {
|
||||
for (char const *const *it = in; (it) && (*it); ++it) {
|
||||
free((void *)*it);
|
||||
}
|
||||
free((void *)in);
|
||||
}
|
||||
Reference in New Issue
Block a user