xref: /openbsd-src/gnu/llvm/clang/tools/scan-build-py/lib/libear/ear.c (revision 12c855180aad702bbcca06e0398d774beeafb155)
1a9ac8606Spatrick /* -*- coding: utf-8 -*-
2a9ac8606Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3a9ac8606Spatrick // See https://llvm.org/LICENSE.txt for license information.
4a9ac8606Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5a9ac8606Spatrick */
6a9ac8606Spatrick 
7a9ac8606Spatrick /**
8a9ac8606Spatrick  * This file implements a shared library. This library can be pre-loaded by
9a9ac8606Spatrick  * the dynamic linker of the Operating System (OS). It implements a few function
10a9ac8606Spatrick  * related to process creation. By pre-load this library the executed process
11a9ac8606Spatrick  * uses these functions instead of those from the standard library.
12a9ac8606Spatrick  *
13a9ac8606Spatrick  * The idea here is to inject a logic before call the real methods. The logic is
14a9ac8606Spatrick  * to dump the call into a file. To call the real method this library is doing
15a9ac8606Spatrick  * the job of the dynamic linker.
16a9ac8606Spatrick  *
17a9ac8606Spatrick  * The only input for the log writing is about the destination directory.
18a9ac8606Spatrick  * This is passed as environment variable.
19a9ac8606Spatrick  */
20a9ac8606Spatrick 
21a9ac8606Spatrick // NOLINTNEXTLINE
22a9ac8606Spatrick #include "config.h"
23a9ac8606Spatrick 
24a9ac8606Spatrick #include <dlfcn.h>
25a9ac8606Spatrick #include <pthread.h>
26a9ac8606Spatrick #include <stdarg.h>
27a9ac8606Spatrick #include <stddef.h>
28a9ac8606Spatrick #include <stdio.h>
29a9ac8606Spatrick #include <stdlib.h>
30a9ac8606Spatrick #include <string.h>
31a9ac8606Spatrick #include <unistd.h>
32a9ac8606Spatrick 
33a9ac8606Spatrick #if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
34a9ac8606Spatrick #include <spawn.h>
35a9ac8606Spatrick #endif
36a9ac8606Spatrick 
37a9ac8606Spatrick #if defined HAVE_NSGETENVIRON
38a9ac8606Spatrick #include <crt_externs.h>
39a9ac8606Spatrick #else
40a9ac8606Spatrick extern char **environ;
41a9ac8606Spatrick #endif
42a9ac8606Spatrick 
43a9ac8606Spatrick #define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
44a9ac8606Spatrick #ifdef APPLE
45a9ac8606Spatrick #define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE"
46a9ac8606Spatrick #define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
47a9ac8606Spatrick #define ENV_SIZE 3
48a9ac8606Spatrick #else
49a9ac8606Spatrick #define ENV_PRELOAD "LD_PRELOAD"
50a9ac8606Spatrick #define ENV_SIZE 2
51a9ac8606Spatrick #endif
52a9ac8606Spatrick 
53a9ac8606Spatrick #define DLSYM(TYPE_, VAR_, SYMBOL_)                                            \
54a9ac8606Spatrick   union {                                                                      \
55a9ac8606Spatrick     void *from;                                                                \
56a9ac8606Spatrick     TYPE_ to;                                                                  \
57a9ac8606Spatrick   } cast;                                                                      \
58a9ac8606Spatrick   if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) {                          \
59a9ac8606Spatrick     perror("bear: dlsym");                                                     \
60a9ac8606Spatrick     exit(EXIT_FAILURE);                                                        \
61a9ac8606Spatrick   }                                                                            \
62a9ac8606Spatrick   TYPE_ const VAR_ = cast.to;
63a9ac8606Spatrick 
64a9ac8606Spatrick typedef char const *bear_env_t[ENV_SIZE];
65a9ac8606Spatrick 
66a9ac8606Spatrick static int bear_capture_env_t(bear_env_t *env);
67a9ac8606Spatrick static int bear_reset_env_t(bear_env_t *env);
68a9ac8606Spatrick static void bear_release_env_t(bear_env_t *env);
69a9ac8606Spatrick static char const **bear_update_environment(char *const envp[],
70a9ac8606Spatrick                                             bear_env_t *env);
71a9ac8606Spatrick static char const **bear_update_environ(char const **in, char const *key,
72a9ac8606Spatrick                                         char const *value);
73a9ac8606Spatrick static char **bear_get_environment();
74a9ac8606Spatrick static void bear_report_call(char const *fun, char const *const argv[]);
75a9ac8606Spatrick static char const **bear_strings_build(char const *arg, va_list *ap);
76a9ac8606Spatrick static char const **bear_strings_copy(char const **const in);
77a9ac8606Spatrick static char const **bear_strings_append(char const **in, char const *e);
78a9ac8606Spatrick static size_t bear_strings_length(char const *const *in);
79a9ac8606Spatrick static void bear_strings_release(char const **);
80a9ac8606Spatrick 
81a9ac8606Spatrick static bear_env_t env_names = {ENV_OUTPUT, ENV_PRELOAD
82a9ac8606Spatrick #ifdef ENV_FLAT
83a9ac8606Spatrick                                ,
84a9ac8606Spatrick                                ENV_FLAT
85a9ac8606Spatrick #endif
86a9ac8606Spatrick };
87a9ac8606Spatrick 
88a9ac8606Spatrick static bear_env_t initial_env = {0, 0
89a9ac8606Spatrick #ifdef ENV_FLAT
90a9ac8606Spatrick                                  ,
91a9ac8606Spatrick                                  0
92a9ac8606Spatrick #endif
93a9ac8606Spatrick };
94a9ac8606Spatrick 
95a9ac8606Spatrick static int initialized = 0;
96a9ac8606Spatrick static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
97a9ac8606Spatrick 
98a9ac8606Spatrick static void on_load(void) __attribute__((constructor));
99a9ac8606Spatrick static void on_unload(void) __attribute__((destructor));
100a9ac8606Spatrick 
101a9ac8606Spatrick #ifdef HAVE_EXECVE
102a9ac8606Spatrick static int call_execve(const char *path, char *const argv[],
103a9ac8606Spatrick                        char *const envp[]);
104a9ac8606Spatrick #endif
105a9ac8606Spatrick #ifdef HAVE_EXECVP
106a9ac8606Spatrick static int call_execvp(const char *file, char *const argv[]);
107a9ac8606Spatrick #endif
108a9ac8606Spatrick #ifdef HAVE_EXECVPE
109a9ac8606Spatrick static int call_execvpe(const char *file, char *const argv[],
110a9ac8606Spatrick                         char *const envp[]);
111a9ac8606Spatrick #endif
112a9ac8606Spatrick #ifdef HAVE_EXECVP2
113a9ac8606Spatrick static int call_execvP(const char *file, const char *search_path,
114a9ac8606Spatrick                        char *const argv[]);
115a9ac8606Spatrick #endif
116a9ac8606Spatrick #ifdef HAVE_EXECT
117a9ac8606Spatrick static int call_exect(const char *path, char *const argv[], char *const envp[]);
118a9ac8606Spatrick #endif
119a9ac8606Spatrick #ifdef HAVE_POSIX_SPAWN
120a9ac8606Spatrick static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
121a9ac8606Spatrick                             const posix_spawn_file_actions_t *file_actions,
122a9ac8606Spatrick                             const posix_spawnattr_t *restrict attrp,
123a9ac8606Spatrick                             char *const argv[restrict],
124a9ac8606Spatrick                             char *const envp[restrict]);
125a9ac8606Spatrick #endif
126a9ac8606Spatrick #ifdef HAVE_POSIX_SPAWNP
127a9ac8606Spatrick static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
128a9ac8606Spatrick                              const posix_spawn_file_actions_t *file_actions,
129a9ac8606Spatrick                              const posix_spawnattr_t *restrict attrp,
130a9ac8606Spatrick                              char *const argv[restrict],
131a9ac8606Spatrick                              char *const envp[restrict]);
132a9ac8606Spatrick #endif
133a9ac8606Spatrick 
134a9ac8606Spatrick /* Initialization method to Captures the relevant environment variables.
135a9ac8606Spatrick  */
136a9ac8606Spatrick 
on_load(void)137a9ac8606Spatrick static void on_load(void) {
138a9ac8606Spatrick   pthread_mutex_lock(&mutex);
139a9ac8606Spatrick   if (!initialized)
140a9ac8606Spatrick     initialized = bear_capture_env_t(&initial_env);
141a9ac8606Spatrick   pthread_mutex_unlock(&mutex);
142a9ac8606Spatrick }
143a9ac8606Spatrick 
on_unload(void)144a9ac8606Spatrick static void on_unload(void) {
145a9ac8606Spatrick   pthread_mutex_lock(&mutex);
146a9ac8606Spatrick   bear_release_env_t(&initial_env);
147a9ac8606Spatrick   initialized = 0;
148a9ac8606Spatrick   pthread_mutex_unlock(&mutex);
149a9ac8606Spatrick }
150a9ac8606Spatrick 
151a9ac8606Spatrick /* These are the methods we are try to hijack.
152a9ac8606Spatrick  */
153a9ac8606Spatrick 
154a9ac8606Spatrick #ifdef HAVE_EXECVE
execve(const char * path,char * const argv[],char * const envp[])155a9ac8606Spatrick int execve(const char *path, char *const argv[], char *const envp[]) {
156a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
157a9ac8606Spatrick   return call_execve(path, argv, envp);
158a9ac8606Spatrick }
159a9ac8606Spatrick #endif
160a9ac8606Spatrick 
161a9ac8606Spatrick #ifdef HAVE_EXECV
162a9ac8606Spatrick #ifndef HAVE_EXECVE
163a9ac8606Spatrick #error can not implement execv without execve
164a9ac8606Spatrick #endif
execv(const char * path,char * const argv[])165a9ac8606Spatrick int execv(const char *path, char *const argv[]) {
166a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
167a9ac8606Spatrick   char *const *envp = bear_get_environment();
168a9ac8606Spatrick   return call_execve(path, argv, envp);
169a9ac8606Spatrick }
170a9ac8606Spatrick #endif
171a9ac8606Spatrick 
172a9ac8606Spatrick #ifdef HAVE_EXECVPE
execvpe(const char * file,char * const argv[],char * const envp[])173a9ac8606Spatrick int execvpe(const char *file, char *const argv[], char *const envp[]) {
174a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
175a9ac8606Spatrick   return call_execvpe(file, argv, envp);
176a9ac8606Spatrick }
177a9ac8606Spatrick #endif
178a9ac8606Spatrick 
179a9ac8606Spatrick #ifdef HAVE_EXECVP
execvp(const char * file,char * const argv[])180a9ac8606Spatrick int execvp(const char *file, char *const argv[]) {
181a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
182a9ac8606Spatrick   return call_execvp(file, argv);
183a9ac8606Spatrick }
184a9ac8606Spatrick #endif
185a9ac8606Spatrick 
186a9ac8606Spatrick #ifdef HAVE_EXECVP2
execvP(const char * file,const char * search_path,char * const argv[])187a9ac8606Spatrick int execvP(const char *file, const char *search_path, char *const argv[]) {
188a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
189a9ac8606Spatrick   return call_execvP(file, search_path, argv);
190a9ac8606Spatrick }
191a9ac8606Spatrick #endif
192a9ac8606Spatrick 
193a9ac8606Spatrick #ifdef HAVE_EXECT
exect(const char * path,char * const argv[],char * const envp[])194a9ac8606Spatrick int exect(const char *path, char *const argv[], char *const envp[]) {
195a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
196a9ac8606Spatrick   return call_exect(path, argv, envp);
197a9ac8606Spatrick }
198a9ac8606Spatrick #endif
199a9ac8606Spatrick 
200a9ac8606Spatrick #ifdef HAVE_EXECL
201a9ac8606Spatrick #ifndef HAVE_EXECVE
202a9ac8606Spatrick #error can not implement execl without execve
203a9ac8606Spatrick #endif
execl(const char * path,const char * arg,...)204a9ac8606Spatrick int execl(const char *path, const char *arg, ...) {
205a9ac8606Spatrick   va_list args;
206a9ac8606Spatrick   va_start(args, arg);
207a9ac8606Spatrick   char const **argv = bear_strings_build(arg, &args);
208a9ac8606Spatrick   va_end(args);
209a9ac8606Spatrick 
210a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
211a9ac8606Spatrick   char *const *envp = bear_get_environment();
212a9ac8606Spatrick   int const result = call_execve(path, (char *const *)argv, envp);
213a9ac8606Spatrick 
214a9ac8606Spatrick   bear_strings_release(argv);
215a9ac8606Spatrick   return result;
216a9ac8606Spatrick }
217a9ac8606Spatrick #endif
218a9ac8606Spatrick 
219a9ac8606Spatrick #ifdef HAVE_EXECLP
220a9ac8606Spatrick #ifndef HAVE_EXECVP
221a9ac8606Spatrick #error can not implement execlp without execvp
222a9ac8606Spatrick #endif
execlp(const char * file,const char * arg,...)223a9ac8606Spatrick int execlp(const char *file, const char *arg, ...) {
224a9ac8606Spatrick   va_list args;
225a9ac8606Spatrick   va_start(args, arg);
226a9ac8606Spatrick   char const **argv = bear_strings_build(arg, &args);
227a9ac8606Spatrick   va_end(args);
228a9ac8606Spatrick 
229a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
230a9ac8606Spatrick   int const result = call_execvp(file, (char *const *)argv);
231a9ac8606Spatrick 
232a9ac8606Spatrick   bear_strings_release(argv);
233a9ac8606Spatrick   return result;
234a9ac8606Spatrick }
235a9ac8606Spatrick #endif
236a9ac8606Spatrick 
237a9ac8606Spatrick #ifdef HAVE_EXECLE
238a9ac8606Spatrick #ifndef HAVE_EXECVE
239a9ac8606Spatrick #error can not implement execle without execve
240a9ac8606Spatrick #endif
241a9ac8606Spatrick // int execle(const char *path, const char *arg, ..., char * const envp[]);
execle(const char * path,const char * arg,...)242a9ac8606Spatrick int execle(const char *path, const char *arg, ...) {
243a9ac8606Spatrick   va_list args;
244a9ac8606Spatrick   va_start(args, arg);
245a9ac8606Spatrick   char const **argv = bear_strings_build(arg, &args);
246a9ac8606Spatrick   char const **envp = va_arg(args, char const **);
247a9ac8606Spatrick   va_end(args);
248a9ac8606Spatrick 
249a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
250a9ac8606Spatrick   int const result =
251a9ac8606Spatrick       call_execve(path, (char *const *)argv, (char *const *)envp);
252a9ac8606Spatrick 
253a9ac8606Spatrick   bear_strings_release(argv);
254a9ac8606Spatrick   return result;
255a9ac8606Spatrick }
256a9ac8606Spatrick #endif
257a9ac8606Spatrick 
258a9ac8606Spatrick #ifdef HAVE_POSIX_SPAWN
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])259a9ac8606Spatrick int posix_spawn(pid_t *restrict pid, const char *restrict path,
260a9ac8606Spatrick                 const posix_spawn_file_actions_t *file_actions,
261a9ac8606Spatrick                 const posix_spawnattr_t *restrict attrp,
262a9ac8606Spatrick                 char *const argv[restrict], char *const envp[restrict]) {
263a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
264a9ac8606Spatrick   return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
265a9ac8606Spatrick }
266a9ac8606Spatrick #endif
267a9ac8606Spatrick 
268a9ac8606Spatrick #ifdef HAVE_POSIX_SPAWNP
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])269a9ac8606Spatrick int posix_spawnp(pid_t *restrict pid, const char *restrict file,
270a9ac8606Spatrick                  const posix_spawn_file_actions_t *file_actions,
271a9ac8606Spatrick                  const posix_spawnattr_t *restrict attrp,
272a9ac8606Spatrick                  char *const argv[restrict], char *const envp[restrict]) {
273a9ac8606Spatrick   bear_report_call(__func__, (char const *const *)argv);
274a9ac8606Spatrick   return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
275a9ac8606Spatrick }
276a9ac8606Spatrick #endif
277a9ac8606Spatrick 
278a9ac8606Spatrick /* These are the methods which forward the call to the standard implementation.
279a9ac8606Spatrick  */
280a9ac8606Spatrick 
281a9ac8606Spatrick #ifdef HAVE_EXECVE
call_execve(const char * path,char * const argv[],char * const envp[])282a9ac8606Spatrick static int call_execve(const char *path, char *const argv[],
283a9ac8606Spatrick                        char *const envp[]) {
284a9ac8606Spatrick   typedef int (*func)(const char *, char *const *, char *const *);
285a9ac8606Spatrick 
286a9ac8606Spatrick   DLSYM(func, fp, "execve");
287a9ac8606Spatrick 
288a9ac8606Spatrick   char const **const menvp = bear_update_environment(envp, &initial_env);
289a9ac8606Spatrick   int const result = (*fp)(path, argv, (char *const *)menvp);
290a9ac8606Spatrick   bear_strings_release(menvp);
291a9ac8606Spatrick   return result;
292a9ac8606Spatrick }
293a9ac8606Spatrick #endif
294a9ac8606Spatrick 
295a9ac8606Spatrick #ifdef HAVE_EXECVPE
call_execvpe(const char * file,char * const argv[],char * const envp[])296a9ac8606Spatrick static int call_execvpe(const char *file, char *const argv[],
297a9ac8606Spatrick                         char *const envp[]) {
298a9ac8606Spatrick   typedef int (*func)(const char *, char *const *, char *const *);
299a9ac8606Spatrick 
300a9ac8606Spatrick   DLSYM(func, fp, "execvpe");
301a9ac8606Spatrick 
302a9ac8606Spatrick   char const **const menvp = bear_update_environment(envp, &initial_env);
303a9ac8606Spatrick   int const result = (*fp)(file, argv, (char *const *)menvp);
304a9ac8606Spatrick   bear_strings_release(menvp);
305a9ac8606Spatrick   return result;
306a9ac8606Spatrick }
307a9ac8606Spatrick #endif
308a9ac8606Spatrick 
309a9ac8606Spatrick #ifdef HAVE_EXECVP
call_execvp(const char * file,char * const argv[])310a9ac8606Spatrick static int call_execvp(const char *file, char *const argv[]) {
311a9ac8606Spatrick   typedef int (*func)(const char *file, char *const argv[]);
312a9ac8606Spatrick 
313a9ac8606Spatrick   DLSYM(func, fp, "execvp");
314a9ac8606Spatrick 
315a9ac8606Spatrick   bear_env_t current_env;
316a9ac8606Spatrick   bear_capture_env_t(&current_env);
317a9ac8606Spatrick   bear_reset_env_t(&initial_env);
318a9ac8606Spatrick   int const result = (*fp)(file, argv);
319a9ac8606Spatrick   bear_reset_env_t(&current_env);
320a9ac8606Spatrick   bear_release_env_t(&current_env);
321a9ac8606Spatrick 
322a9ac8606Spatrick   return result;
323a9ac8606Spatrick }
324a9ac8606Spatrick #endif
325a9ac8606Spatrick 
326a9ac8606Spatrick #ifdef HAVE_EXECVP2
call_execvP(const char * file,const char * search_path,char * const argv[])327a9ac8606Spatrick static int call_execvP(const char *file, const char *search_path,
328a9ac8606Spatrick                        char *const argv[]) {
329a9ac8606Spatrick   typedef int (*func)(const char *, const char *, char *const *);
330a9ac8606Spatrick 
331a9ac8606Spatrick   DLSYM(func, fp, "execvP");
332a9ac8606Spatrick 
333a9ac8606Spatrick   bear_env_t current_env;
334a9ac8606Spatrick   bear_capture_env_t(&current_env);
335a9ac8606Spatrick   bear_reset_env_t(&initial_env);
336a9ac8606Spatrick   int const result = (*fp)(file, search_path, argv);
337a9ac8606Spatrick   bear_reset_env_t(&current_env);
338a9ac8606Spatrick   bear_release_env_t(&current_env);
339a9ac8606Spatrick 
340a9ac8606Spatrick   return result;
341a9ac8606Spatrick }
342a9ac8606Spatrick #endif
343a9ac8606Spatrick 
344a9ac8606Spatrick #ifdef HAVE_EXECT
call_exect(const char * path,char * const argv[],char * const envp[])345a9ac8606Spatrick static int call_exect(const char *path, char *const argv[],
346a9ac8606Spatrick                       char *const envp[]) {
347a9ac8606Spatrick   typedef int (*func)(const char *, char *const *, char *const *);
348a9ac8606Spatrick 
349a9ac8606Spatrick   DLSYM(func, fp, "exect");
350a9ac8606Spatrick 
351a9ac8606Spatrick   char const **const menvp = bear_update_environment(envp, &initial_env);
352a9ac8606Spatrick   int const result = (*fp)(path, argv, (char *const *)menvp);
353a9ac8606Spatrick   bear_strings_release(menvp);
354a9ac8606Spatrick   return result;
355a9ac8606Spatrick }
356a9ac8606Spatrick #endif
357a9ac8606Spatrick 
358a9ac8606Spatrick #ifdef HAVE_POSIX_SPAWN
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])359a9ac8606Spatrick static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
360a9ac8606Spatrick                             const posix_spawn_file_actions_t *file_actions,
361a9ac8606Spatrick                             const posix_spawnattr_t *restrict attrp,
362a9ac8606Spatrick                             char *const argv[restrict],
363a9ac8606Spatrick                             char *const envp[restrict]) {
364a9ac8606Spatrick   typedef int (*func)(pid_t *restrict, const char *restrict,
365a9ac8606Spatrick                       const posix_spawn_file_actions_t *,
366a9ac8606Spatrick                       const posix_spawnattr_t *restrict, char *const *restrict,
367a9ac8606Spatrick                       char *const *restrict);
368a9ac8606Spatrick 
369a9ac8606Spatrick   DLSYM(func, fp, "posix_spawn");
370a9ac8606Spatrick 
371a9ac8606Spatrick   char const **const menvp = bear_update_environment(envp, &initial_env);
372a9ac8606Spatrick   int const result =
373a9ac8606Spatrick       (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
374a9ac8606Spatrick   bear_strings_release(menvp);
375a9ac8606Spatrick   return result;
376a9ac8606Spatrick }
377a9ac8606Spatrick #endif
378a9ac8606Spatrick 
379a9ac8606Spatrick #ifdef HAVE_POSIX_SPAWNP
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])380a9ac8606Spatrick static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
381a9ac8606Spatrick                              const posix_spawn_file_actions_t *file_actions,
382a9ac8606Spatrick                              const posix_spawnattr_t *restrict attrp,
383a9ac8606Spatrick                              char *const argv[restrict],
384a9ac8606Spatrick                              char *const envp[restrict]) {
385a9ac8606Spatrick   typedef int (*func)(pid_t *restrict, const char *restrict,
386a9ac8606Spatrick                       const posix_spawn_file_actions_t *,
387a9ac8606Spatrick                       const posix_spawnattr_t *restrict, char *const *restrict,
388a9ac8606Spatrick                       char *const *restrict);
389a9ac8606Spatrick 
390a9ac8606Spatrick   DLSYM(func, fp, "posix_spawnp");
391a9ac8606Spatrick 
392a9ac8606Spatrick   char const **const menvp = bear_update_environment(envp, &initial_env);
393a9ac8606Spatrick   int const result =
394a9ac8606Spatrick       (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
395a9ac8606Spatrick   bear_strings_release(menvp);
396a9ac8606Spatrick   return result;
397a9ac8606Spatrick }
398a9ac8606Spatrick #endif
399a9ac8606Spatrick 
400a9ac8606Spatrick /* this method is to write log about the process creation. */
401a9ac8606Spatrick 
bear_report_call(char const * fun,char const * const argv[])402a9ac8606Spatrick static void bear_report_call(char const *fun, char const *const argv[]) {
403a9ac8606Spatrick   static int const GS = 0x1d;
404a9ac8606Spatrick   static int const RS = 0x1e;
405a9ac8606Spatrick   static int const US = 0x1f;
406a9ac8606Spatrick 
407a9ac8606Spatrick   if (!initialized)
408a9ac8606Spatrick     return;
409a9ac8606Spatrick 
410a9ac8606Spatrick   pthread_mutex_lock(&mutex);
411a9ac8606Spatrick   const char *cwd = getcwd(NULL, 0);
412a9ac8606Spatrick   if (0 == cwd) {
413a9ac8606Spatrick     perror("bear: getcwd");
414*12c85518Srobert     pthread_mutex_unlock(&mutex);
415a9ac8606Spatrick     exit(EXIT_FAILURE);
416a9ac8606Spatrick   }
417a9ac8606Spatrick   char const *const out_dir = initial_env[0];
418a9ac8606Spatrick   size_t const path_max_length = strlen(out_dir) + 32;
419a9ac8606Spatrick   char filename[path_max_length];
420a9ac8606Spatrick   if (-1 ==
421a9ac8606Spatrick       snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
422a9ac8606Spatrick     perror("bear: snprintf");
423*12c85518Srobert     pthread_mutex_unlock(&mutex);
424a9ac8606Spatrick     exit(EXIT_FAILURE);
425a9ac8606Spatrick   }
426a9ac8606Spatrick   FILE *fd = fopen(filename, "a+");
427a9ac8606Spatrick   if (0 == fd) {
428a9ac8606Spatrick     perror("bear: fopen");
429*12c85518Srobert     pthread_mutex_unlock(&mutex);
430a9ac8606Spatrick     exit(EXIT_FAILURE);
431a9ac8606Spatrick   }
432a9ac8606Spatrick   fprintf(fd, "%d%c", getpid(), RS);
433a9ac8606Spatrick   fprintf(fd, "%d%c", getppid(), RS);
434a9ac8606Spatrick   fprintf(fd, "%s%c", fun, RS);
435a9ac8606Spatrick   fprintf(fd, "%s%c", cwd, RS);
436a9ac8606Spatrick   size_t const argc = bear_strings_length(argv);
437a9ac8606Spatrick   for (size_t it = 0; it < argc; ++it) {
438a9ac8606Spatrick     fprintf(fd, "%s%c", argv[it], US);
439a9ac8606Spatrick   }
440a9ac8606Spatrick   fprintf(fd, "%c", GS);
441a9ac8606Spatrick   if (fclose(fd)) {
442a9ac8606Spatrick     perror("bear: fclose");
443*12c85518Srobert     pthread_mutex_unlock(&mutex);
444a9ac8606Spatrick     exit(EXIT_FAILURE);
445a9ac8606Spatrick   }
446a9ac8606Spatrick   free((void *)cwd);
447a9ac8606Spatrick   pthread_mutex_unlock(&mutex);
448a9ac8606Spatrick }
449a9ac8606Spatrick 
450*12c85518Srobert /* update environment assure that children processes will copy the desired
451a9ac8606Spatrick  * behaviour */
452a9ac8606Spatrick 
bear_capture_env_t(bear_env_t * env)453a9ac8606Spatrick static int bear_capture_env_t(bear_env_t *env) {
454a9ac8606Spatrick   int status = 1;
455a9ac8606Spatrick   for (size_t it = 0; it < ENV_SIZE; ++it) {
456a9ac8606Spatrick     char const *const env_value = getenv(env_names[it]);
457a9ac8606Spatrick     char const *const env_copy = (env_value) ? strdup(env_value) : env_value;
458a9ac8606Spatrick     (*env)[it] = env_copy;
459a9ac8606Spatrick     status &= (env_copy) ? 1 : 0;
460a9ac8606Spatrick   }
461a9ac8606Spatrick   return status;
462a9ac8606Spatrick }
463a9ac8606Spatrick 
bear_reset_env_t(bear_env_t * env)464a9ac8606Spatrick static int bear_reset_env_t(bear_env_t *env) {
465a9ac8606Spatrick   int status = 1;
466a9ac8606Spatrick   for (size_t it = 0; it < ENV_SIZE; ++it) {
467a9ac8606Spatrick     if ((*env)[it]) {
468a9ac8606Spatrick       setenv(env_names[it], (*env)[it], 1);
469a9ac8606Spatrick     } else {
470a9ac8606Spatrick       unsetenv(env_names[it]);
471a9ac8606Spatrick     }
472a9ac8606Spatrick   }
473a9ac8606Spatrick   return status;
474a9ac8606Spatrick }
475a9ac8606Spatrick 
bear_release_env_t(bear_env_t * env)476a9ac8606Spatrick static void bear_release_env_t(bear_env_t *env) {
477a9ac8606Spatrick   for (size_t it = 0; it < ENV_SIZE; ++it) {
478a9ac8606Spatrick     free((void *)(*env)[it]);
479a9ac8606Spatrick     (*env)[it] = 0;
480a9ac8606Spatrick   }
481a9ac8606Spatrick }
482a9ac8606Spatrick 
bear_update_environment(char * const envp[],bear_env_t * env)483a9ac8606Spatrick static char const **bear_update_environment(char *const envp[],
484a9ac8606Spatrick                                             bear_env_t *env) {
485a9ac8606Spatrick   char const **result = bear_strings_copy((char const **)envp);
486a9ac8606Spatrick   for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
487a9ac8606Spatrick     result = bear_update_environ(result, env_names[it], (*env)[it]);
488a9ac8606Spatrick   return result;
489a9ac8606Spatrick }
490a9ac8606Spatrick 
bear_update_environ(char const * envs[],char const * key,char const * const value)491a9ac8606Spatrick static char const **bear_update_environ(char const *envs[], char const *key,
492a9ac8606Spatrick                                         char const *const value) {
493a9ac8606Spatrick   // find the key if it's there
494a9ac8606Spatrick   size_t const key_length = strlen(key);
495a9ac8606Spatrick   char const **it = envs;
496a9ac8606Spatrick   for (; (it) && (*it); ++it) {
497a9ac8606Spatrick     if ((0 == strncmp(*it, key, key_length)) && (strlen(*it) > key_length) &&
498a9ac8606Spatrick         ('=' == (*it)[key_length]))
499a9ac8606Spatrick       break;
500a9ac8606Spatrick   }
501a9ac8606Spatrick   // allocate a environment entry
502a9ac8606Spatrick   size_t const value_length = strlen(value);
503a9ac8606Spatrick   size_t const env_length = key_length + value_length + 2;
504a9ac8606Spatrick   char *env = malloc(env_length);
505a9ac8606Spatrick   if (0 == env) {
506a9ac8606Spatrick     perror("bear: malloc [in env_update]");
507a9ac8606Spatrick     exit(EXIT_FAILURE);
508a9ac8606Spatrick   }
509a9ac8606Spatrick   if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
510a9ac8606Spatrick     perror("bear: snprintf");
511a9ac8606Spatrick     exit(EXIT_FAILURE);
512a9ac8606Spatrick   }
513a9ac8606Spatrick   // replace or append the environment entry
514a9ac8606Spatrick   if (it && *it) {
515a9ac8606Spatrick     free((void *)*it);
516a9ac8606Spatrick     *it = env;
517a9ac8606Spatrick     return envs;
518a9ac8606Spatrick   }
519a9ac8606Spatrick   return bear_strings_append(envs, env);
520a9ac8606Spatrick }
521a9ac8606Spatrick 
bear_get_environment()522a9ac8606Spatrick static char **bear_get_environment() {
523a9ac8606Spatrick #if defined HAVE_NSGETENVIRON
524a9ac8606Spatrick   return *_NSGetEnviron();
525a9ac8606Spatrick #else
526a9ac8606Spatrick   return environ;
527a9ac8606Spatrick #endif
528a9ac8606Spatrick }
529a9ac8606Spatrick 
530a9ac8606Spatrick /* util methods to deal with string arrays. environment and process arguments
531a9ac8606Spatrick  * are both represented as string arrays. */
532a9ac8606Spatrick 
bear_strings_build(char const * const arg,va_list * args)533a9ac8606Spatrick static char const **bear_strings_build(char const *const arg, va_list *args) {
534a9ac8606Spatrick   char const **result = 0;
535a9ac8606Spatrick   size_t size = 0;
536a9ac8606Spatrick   for (char const *it = arg; it; it = va_arg(*args, char const *)) {
537a9ac8606Spatrick     result = realloc(result, (size + 1) * sizeof(char const *));
538a9ac8606Spatrick     if (0 == result) {
539a9ac8606Spatrick       perror("bear: realloc");
540a9ac8606Spatrick       exit(EXIT_FAILURE);
541a9ac8606Spatrick     }
542a9ac8606Spatrick     char const *copy = strdup(it);
543a9ac8606Spatrick     if (0 == copy) {
544a9ac8606Spatrick       perror("bear: strdup");
545a9ac8606Spatrick       exit(EXIT_FAILURE);
546a9ac8606Spatrick     }
547a9ac8606Spatrick     result[size++] = copy;
548a9ac8606Spatrick   }
549a9ac8606Spatrick   result = realloc(result, (size + 1) * sizeof(char const *));
550a9ac8606Spatrick   if (0 == result) {
551a9ac8606Spatrick     perror("bear: realloc");
552a9ac8606Spatrick     exit(EXIT_FAILURE);
553a9ac8606Spatrick   }
554a9ac8606Spatrick   result[size++] = 0;
555a9ac8606Spatrick 
556a9ac8606Spatrick   return result;
557a9ac8606Spatrick }
558a9ac8606Spatrick 
bear_strings_copy(char const ** const in)559a9ac8606Spatrick static char const **bear_strings_copy(char const **const in) {
560a9ac8606Spatrick   size_t const size = bear_strings_length(in);
561a9ac8606Spatrick 
562a9ac8606Spatrick   char const **const result = malloc((size + 1) * sizeof(char const *));
563a9ac8606Spatrick   if (0 == result) {
564a9ac8606Spatrick     perror("bear: malloc");
565a9ac8606Spatrick     exit(EXIT_FAILURE);
566a9ac8606Spatrick   }
567a9ac8606Spatrick 
568a9ac8606Spatrick   char const **out_it = result;
569a9ac8606Spatrick   for (char const *const *in_it = in; (in_it) && (*in_it); ++in_it, ++out_it) {
570a9ac8606Spatrick     *out_it = strdup(*in_it);
571a9ac8606Spatrick     if (0 == *out_it) {
572a9ac8606Spatrick       perror("bear: strdup");
573a9ac8606Spatrick       exit(EXIT_FAILURE);
574a9ac8606Spatrick     }
575a9ac8606Spatrick   }
576a9ac8606Spatrick   *out_it = 0;
577a9ac8606Spatrick   return result;
578a9ac8606Spatrick }
579a9ac8606Spatrick 
bear_strings_append(char const ** const in,char const * const e)580a9ac8606Spatrick static char const **bear_strings_append(char const **const in,
581a9ac8606Spatrick                                         char const *const e) {
582a9ac8606Spatrick   size_t size = bear_strings_length(in);
583a9ac8606Spatrick   char const **result = realloc(in, (size + 2) * sizeof(char const *));
584a9ac8606Spatrick   if (0 == result) {
585a9ac8606Spatrick     perror("bear: realloc");
586a9ac8606Spatrick     exit(EXIT_FAILURE);
587a9ac8606Spatrick   }
588a9ac8606Spatrick   result[size++] = e;
589a9ac8606Spatrick   result[size++] = 0;
590a9ac8606Spatrick   return result;
591a9ac8606Spatrick }
592a9ac8606Spatrick 
bear_strings_length(char const * const * const in)593a9ac8606Spatrick static size_t bear_strings_length(char const *const *const in) {
594a9ac8606Spatrick   size_t result = 0;
595a9ac8606Spatrick   for (char const *const *it = in; (it) && (*it); ++it)
596a9ac8606Spatrick     ++result;
597a9ac8606Spatrick   return result;
598a9ac8606Spatrick }
599a9ac8606Spatrick 
bear_strings_release(char const ** in)600a9ac8606Spatrick static void bear_strings_release(char const **in) {
601a9ac8606Spatrick   for (char const *const *it = in; (it) && (*it); ++it) {
602a9ac8606Spatrick     free((void *)*it);
603a9ac8606Spatrick   }
604a9ac8606Spatrick   free((void *)in);
605a9ac8606Spatrick }
606