]>
Commit | Line | Data |
---|---|---|
fd67aa11 | 1 | /* Copyright (C) 2021-2024 Free Software Foundation, Inc. |
bb368aad VM |
2 | Contributed by Oracle. |
3 | ||
4 | This file is part of GNU Binutils. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3, or (at your option) | |
9 | any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, 51 Franklin Street - Fifth Floor, Boston, | |
19 | MA 02110-1301, USA. */ | |
20 | ||
21 | /* | |
22 | * Lineage events for process fork, exec, etc. | |
23 | */ | |
24 | ||
25 | #include "config.h" | |
26 | #include <string.h> | |
27 | #include <elf.h> | |
28 | #include <regex.h> | |
29 | #include <sys/types.h> | |
30 | #include <sys/stat.h> | |
31 | #include <sys/mman.h> | |
fe39ffdc | 32 | #include <limits.h> |
de8e7059 | 33 | #include <spawn.h> |
bb368aad VM |
34 | |
35 | #include "descendants.h" | |
36 | ||
bb368aad VM |
37 | #define LT_MAXNAMELEN 1024 |
38 | #define LT_MAXPATHLEN 1024 | |
39 | ||
40 | int __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
41 | int dbg_current_mode = FOLLOW_NONE; /* for debug only */ | |
42 | unsigned line_key = COLLECTOR_TSD_INVALID_KEY; | |
43 | line_mode_t line_mode = LM_DORMANT; | |
44 | int user_follow_mode = FOLLOW_ON; | |
45 | int java_mode = 0; | |
46 | ||
47 | static char *user_follow_spec; | |
48 | static char new_lineage[LT_MAXNAMELEN]; | |
49 | static char curr_lineage[LT_MAXNAMELEN]; | |
50 | static char linetrace_exp_dir_name[LT_MAXPATHLEN + 1]; // experiment directory | |
51 | ||
52 | /* lineage tracking for descendants of this process */ | |
53 | ||
54 | static int fork_linenum = 0; | |
55 | static int line_initted = 0; | |
56 | static collector_mutex_t fork_lineage_lock = COLLECTOR_MUTEX_INITIALIZER; | |
57 | static collector_mutex_t clone_lineage_lock = COLLECTOR_MUTEX_INITIALIZER; | |
58 | ||
bb368aad VM |
59 | // For a given Linux, which lib functions have more than one GLIBC version? Do this: |
60 | // objdump -T `find /lib /lib64 -name "*.so*"` | grep GLIBC | grep text | grep \( | |
de8e7059 VM |
61 | static pid_t (*__real_fork) (void) = NULL; |
62 | static pid_t (*__real_vfork) (void) = NULL; | |
63 | static int (*__real_execve) (const char *file, char *const argv[], | |
64 | char *const envp[]) = NULL; | |
65 | static int (*__real_execvp) (const char *file, char *const argv[]) = NULL; | |
66 | static int (*__real_execv) (const char *file, char *const argv[]) = NULL; | |
67 | static int (*__real_execle) (const char *path, const char *arg, ...) = NULL; | |
68 | static int (*__real_execlp) (const char *file, const char *arg, ...) = NULL; | |
69 | static int (*__real_execl) (const char *file, const char *arg, ...) = NULL; | |
70 | static int (*__real_clone) (int (*fn) (void *), void *child_stack, | |
71 | int flags, void *arg, ...) = NULL; | |
72 | static int (*__real_grantpt) (int fd) = NULL; | |
73 | static char *(*__real_ptsname) (int fd) = NULL; | |
74 | static FILE *(*__real_popen) (const char *command, const char *type) = NULL; | |
bb368aad | 75 | static int clone_linenum = 0; |
66f76c54 VM |
76 | static FILE *(*__real_popen_2_17) (const char *command, const char *type) = NULL; |
77 | static FILE *(*__real_popen_2_2_5) (const char *command, const char *type) = NULL; | |
de8e7059 VM |
78 | static FILE *(*__real_popen_2_1) (const char *command, const char *type) = NULL; |
79 | static FILE *(*__real_popen_2_0) (const char *command, const char *type) = NULL; | |
66f76c54 VM |
80 | |
81 | static int (*__real_posix_spawn_2_17) (pid_t *pid, const char *path, | |
de8e7059 VM |
82 | const posix_spawn_file_actions_t *file_actions, |
83 | const posix_spawnattr_t *attrp, | |
84 | char *const argv[], char *const envp[]) = NULL; | |
de8e7059 VM |
85 | static int (*__real_posix_spawn_2_15) (pid_t *pid, const char *path, |
86 | const posix_spawn_file_actions_t *file_actions, | |
87 | const posix_spawnattr_t *attrp, | |
88 | char *const argv[], char *const envp[]) = NULL; | |
89 | static int (*__real_posix_spawn_2_2_5) (pid_t *pid, const char *path, | |
90 | const posix_spawn_file_actions_t *file_actions, | |
91 | const posix_spawnattr_t *attrp, | |
92 | char *const argv[], char *const envp[]) = NULL; | |
66f76c54 VM |
93 | static int (*__real_posix_spawn_2_2) (pid_t *pid, const char *path, |
94 | const posix_spawn_file_actions_t *file_actions, | |
95 | const posix_spawnattr_t *attrp, | |
96 | char *const argv[], char *const envp[]) = NULL; | |
97 | ||
98 | static int (*__real_posix_spawnp_2_17) (pid_t *pid, const char *file, | |
99 | const posix_spawn_file_actions_t *file_actions, | |
100 | const posix_spawnattr_t *attrp, | |
101 | char *const argv[], char *const envp[]) = NULL; | |
de8e7059 VM |
102 | static int (*__real_posix_spawnp_2_15) (pid_t *pid, const char *file, |
103 | const posix_spawn_file_actions_t *file_actions, | |
104 | const posix_spawnattr_t *attrp, | |
105 | char *const argv[], char *const envp[]) = NULL; | |
106 | static int (*__real_posix_spawnp_2_2_5) (pid_t *pid, const char *file, | |
107 | const posix_spawn_file_actions_t *file_actions, | |
108 | const posix_spawnattr_t *attrp, | |
109 | char *const argv[], char *const envp[]) = NULL; | |
66f76c54 VM |
110 | static int (*__real_posix_spawnp_2_2) (pid_t *pid, const char *file, |
111 | const posix_spawn_file_actions_t *file_actions, | |
112 | const posix_spawnattr_t *attrp, | |
113 | char *const argv[], char *const envp[]) = NULL; | |
de8e7059 VM |
114 | static int (*__real_system) (const char *command) = NULL; |
115 | static int (*__real_posix_spawn) (pid_t *pid, const char *path, | |
116 | const posix_spawn_file_actions_t *file_actions, | |
117 | const posix_spawnattr_t *attrp, | |
118 | char *const argv[], char *const envp[]) = NULL; | |
119 | static int (*__real_posix_spawnp) (pid_t *pid, const char *file, | |
120 | const posix_spawn_file_actions_t *file_actions, | |
121 | const posix_spawnattr_t *attrp, | |
122 | char *const argv[], char *const envp[]) = NULL; | |
123 | static int (*__real_setuid) (uid_t uid) = NULL; | |
124 | static int (*__real_seteuid) (uid_t euid) = NULL; | |
125 | static int (*__real_setreuid) (uid_t ruid, uid_t euid) = NULL; | |
126 | static int (*__real_setgid) (gid_t gid) = NULL; | |
127 | static int (*__real_setegid) (gid_t egid) = NULL; | |
128 | static int (*__real_setregid) (gid_t rgid, gid_t egid)= NULL; | |
bb368aad VM |
129 | static void linetrace_dormant (); |
130 | static int check_follow_fork (); | |
131 | static int check_follow_exec (const char *execfile); | |
132 | static int check_follow_combo (const char *execfile); | |
133 | static int path_collectable (const char *execfile); | |
134 | static char * build_experiment_path (char *instring, size_t instring_sz, const char *lineage_str); | |
135 | static int init_lineage_intf (); | |
136 | ||
137 | /* ------- "Previously dbx-visible" function prototypes ----------------- */ | |
138 | static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *execfile); | |
139 | static char *lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname); | |
140 | static void linetrace_ext_fork_prologue (const char *variant, char * new_lineage, int *following_fork); | |
141 | static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * new_lineage, int *following_fork); | |
142 | static char **linetrace_ext_exec_prologue (const char *variant, | |
143 | const char* path, char *const argv[], char *const envp[], int *following_exec); | |
144 | static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec); | |
145 | static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo); | |
146 | static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo); | |
147 | ||
148 | #ifdef DEBUG | |
149 | static int | |
150 | get_combo_flag () | |
151 | { | |
152 | int * guard = NULL; | |
153 | int combo_flag = ((line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0); | |
154 | return combo_flag; | |
155 | } | |
156 | #endif /* DEBUG */ | |
157 | ||
158 | /* must be called for potentially live experiment */ | |
159 | int | |
160 | __collector_ext_line_init (int *precord_this_experiment, | |
161 | const char * progspec, const char * progname) | |
162 | { | |
163 | *precord_this_experiment = 1; | |
164 | TprintfT (DBG_LT0, "__collector_ext_line_init(%s)\n", progspec); | |
165 | if (NULL_PTR (fork)) | |
166 | if (init_lineage_intf ()) | |
167 | { | |
168 | TprintfT (DBG_LT0, "__collector_ext_line_init() ERROR: initialization failed.\n"); | |
169 | return COL_ERROR_LINEINIT; | |
170 | } | |
171 | /* check the follow spec */ | |
172 | user_follow_spec = CALL_UTIL (getenv)(SP_COLLECTOR_FOLLOW_SPEC); | |
173 | if (user_follow_spec != NULL) | |
174 | { | |
175 | TprintfT (DBG_LT0, "collector: %s=%s\n", SP_COLLECTOR_FOLLOW_SPEC, user_follow_spec); | |
176 | if (!linetrace_follow_experiment (user_follow_spec, curr_lineage, progname)) | |
177 | { | |
178 | *precord_this_experiment = 0; | |
179 | TprintfT (DBG_LT0, "collector: -F =<regex> does not match, will NOT be followed\n"); | |
180 | } | |
181 | else | |
182 | TprintfT (DBG_LT0, "collector: -F =<regex> matches, will be followed\n"); | |
183 | user_follow_mode = FOLLOW_ALL; | |
184 | } | |
185 | __collector_env_save_preloads (); | |
186 | TprintfT (DBG_LT0, "__collector_ext_line_init(), progname=%s, followspec=%s, followthis=%d\n", | |
187 | progname, user_follow_spec ? user_follow_spec : "NULL", | |
188 | *precord_this_experiment); | |
189 | line_mode = LM_TRACK_LINEAGE; /* even if we don't follow, we report the interposition */ | |
190 | line_initted = 1; | |
191 | return COL_ERROR_NONE; | |
192 | } | |
193 | ||
194 | /* | |
195 | * int __collector_ext_line_install(args) | |
196 | * Check args to determine which line events to follow. | |
197 | * Create tsd key for combo flag. | |
198 | */ | |
199 | int | |
200 | __collector_ext_line_install (char *args, const char * expname) | |
201 | { | |
202 | if (!line_initted) | |
203 | { | |
204 | TprintfT (DBG_LT0, "__collector_ext_line_install(%s) ERROR: init hasn't be called yet\n", args); | |
205 | return COL_ERROR_EXPOPEN; | |
206 | } | |
207 | TprintfT (DBG_LT0, "__collector_ext_line_install(%s, %s)\n", args, expname); | |
208 | line_key = __collector_tsd_create_key (sizeof (int), NULL, NULL); | |
209 | ||
210 | /* determine experiment name */ | |
211 | __collector_strlcpy (linetrace_exp_dir_name, expname, sizeof (linetrace_exp_dir_name)); | |
212 | lineage_from_expname (curr_lineage, sizeof (curr_lineage), linetrace_exp_dir_name); | |
213 | user_follow_mode = CALL_UTIL (atoi)(args); | |
214 | TprintfT (DBG_LT0, "__collector_ext_line_install() user_follow_mode=0x%X, linetrace_exp_dir_name=%s\n", | |
215 | user_follow_mode, linetrace_exp_dir_name); | |
216 | ||
217 | // determine java mode | |
218 | char * java_follow_env = CALL_UTIL (getenv)(JAVA_TOOL_OPTIONS); | |
219 | if (java_follow_env != NULL && CALL_UTIL (strstr)(java_follow_env, COLLECTOR_JVMTI_OPTION)) | |
220 | java_mode = 1; | |
221 | ||
222 | // backup collector specific env | |
223 | if (sp_env_backup == NULL) | |
224 | { | |
225 | sp_env_backup = __collector_env_backup (); | |
226 | TprintfT (DBG_LT0, "__collector_ext_line_install creating sp_env_backup -- 0x%p\n", sp_env_backup); | |
227 | } | |
228 | else | |
229 | TprintfT (DBG_LT0, "__collector_ext_line_install existing sp_env_backup -- 0x%p\n", sp_env_backup); | |
230 | if (user_follow_mode == FOLLOW_NONE) | |
231 | __collector_env_unset (NULL); | |
232 | ||
233 | char logmsg[256]; | |
234 | logmsg[0] = '\0'; | |
235 | if (user_follow_mode != FOLLOW_NONE) | |
236 | CALL_UTIL (strlcat)(logmsg, "fork|exec|combo", sizeof (logmsg)); | |
237 | size_t slen = __collector_strlen (logmsg); | |
238 | if (slen > 0) | |
239 | logmsg[slen] = '\0'; | |
240 | else | |
241 | CALL_UTIL (strlcat)(logmsg, "none", sizeof (logmsg)); | |
242 | ||
243 | /* report which line events are followed */ | |
244 | (void) __collector_log_write ("<setting %s=\"%s\"/>\n", SP_JCMD_LINETRACE, logmsg); | |
245 | TprintfT (DBG_LT0, "__collector_ext_line_install(%s): %s \n", expname, logmsg); | |
246 | return COL_ERROR_NONE; | |
247 | } | |
248 | ||
249 | char * | |
250 | lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname) | |
251 | { | |
252 | TprintfT (DBG_LT0, "lineage_from_expname(%s, %s)\n", lineage_str, expname); | |
253 | char *p = NULL; | |
254 | if (lstr_sz < 1 || !lineage_str || !expname) | |
255 | { | |
256 | TprintfT (DBG_LT0, "lineage_from_expname(): ERROR, null string\n"); | |
257 | return NULL; | |
258 | } | |
259 | /* determine lineage from experiment name */ | |
260 | p = __collector_strrchr (expname, '/'); | |
261 | if ((p == NULL) || (*++p != '_')) | |
262 | { | |
263 | lineage_str[0] = 0; | |
264 | TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\".\" (founder)\n", expname); | |
265 | } | |
266 | else | |
267 | { | |
268 | size_t tmp = __collector_strlcpy (lineage_str, p, lstr_sz); | |
269 | if (tmp >= lstr_sz) | |
270 | TprintfT (DBG_LT0, "lineage_from_expname(): ERROR: expt=%s lineage=\"%s\" truncated %ld characters\n", | |
271 | expname, lineage_str, (long) (tmp - lstr_sz)); | |
272 | lineage_str[lstr_sz - 1] = 0; | |
273 | p = __collector_strchr (lineage_str, '.'); | |
274 | if (p != NULL) | |
275 | *p = '\0'; | |
276 | TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\"%s\"\n", expname, lineage_str); | |
277 | } | |
278 | return lineage_str; | |
279 | } | |
280 | ||
281 | /* | |
282 | * void __collector_line_cleanup (void) | |
283 | * Disable logging. Clear backup ENV. | |
284 | */ | |
285 | void | |
286 | __collector_line_cleanup (void) | |
287 | { | |
288 | if (line_mode == LM_CLOSED) | |
289 | { | |
290 | TprintfT (DBG_LT0, "__collector_line_cleanup(): WARNING, line is already closed\n"); | |
291 | return; | |
292 | } | |
293 | else if (line_mode == LM_DORMANT) | |
294 | TprintfT (DBG_LT0, "__collector_line_cleanup(): ERROR, line is DORMANT\n"); | |
295 | else | |
296 | TprintfT (DBG_LT0, "__collector_line_cleanup()\n"); | |
297 | line_mode = LM_CLOSED; | |
298 | user_follow_mode = FOLLOW_NONE; | |
299 | dbg_current_mode = FOLLOW_NONE; /* for debug only */ | |
300 | line_key = COLLECTOR_TSD_INVALID_KEY; | |
301 | java_mode = 0; | |
302 | if (sp_env_backup != NULL) | |
303 | { | |
304 | __collector_env_backup_free (); | |
305 | sp_env_backup = NULL; | |
306 | } | |
307 | return; | |
308 | } | |
309 | ||
310 | /* | |
311 | * void __collector_ext_line_close (void) | |
312 | * Disable logging. Cleans ENV vars. Clear backup ENV. | |
313 | */ | |
314 | void | |
315 | __collector_ext_line_close (void) | |
316 | { | |
317 | TprintfT (DBG_LT0, "__collector_ext_line_close()\n"); | |
318 | __collector_line_cleanup (); | |
319 | __collector_env_unset (NULL); | |
320 | return; | |
321 | } | |
322 | ||
323 | /* | |
324 | * void linetrace_dormant(void) | |
325 | * Disable logging. Preserve ENV vars. | |
326 | */ | |
327 | static void | |
328 | linetrace_dormant (void) | |
329 | { | |
330 | if (line_mode == LM_DORMANT) | |
331 | { | |
332 | TprintfT (DBG_LT0, "linetrace_dormant() -- already dormant\n"); | |
333 | return; | |
334 | } | |
335 | else if (line_mode == LM_CLOSED) | |
336 | { | |
337 | TprintfT (DBG_LT0, "linetrace_dormant(): ERROR, line is already CLOSED\n"); | |
338 | return; | |
339 | } | |
340 | else | |
341 | TprintfT (DBG_LT0, "linetrace_dormant()\n"); | |
342 | line_mode = LM_DORMANT; | |
343 | return; | |
344 | } | |
345 | ||
346 | static int | |
347 | check_follow_fork () | |
348 | { | |
349 | int follow = (user_follow_mode != FOLLOW_NONE); | |
350 | TprintfT (DBG_LT0, "check_follow_fork()=%d\n", follow); | |
351 | return follow; | |
352 | } | |
353 | ||
354 | static int | |
355 | check_follow_exec (const char *execfile) | |
356 | { | |
357 | int follow = (user_follow_mode != FOLLOW_NONE); | |
358 | if (follow) | |
359 | { | |
360 | /* revise based on collectability of execfile */ | |
361 | follow = path_collectable (execfile); | |
362 | } | |
363 | TprintfT (DBG_LT0, "check_follow_exec(%s)=%d\n", execfile, follow); | |
364 | return follow; | |
365 | } | |
366 | ||
367 | static int | |
368 | check_follow_combo (const char *execfile) | |
369 | { | |
370 | int follow = (user_follow_mode != FOLLOW_NONE); | |
371 | TprintfT (DBG_LT0, "check_follow_combo(%s)=%d\n", execfile, follow); | |
372 | return follow; | |
373 | } | |
374 | ||
375 | static int | |
376 | check_fd_dynamic (int fd) | |
377 | { | |
378 | TprintfT (DBG_LT0, "check_fd_dynamic(%d)\n", fd); | |
379 | off_t off = CALL_UTIL (lseek)(fd, (off_t) 0, SEEK_END); | |
380 | size_t sz = (size_t) 8192; /* one page should suffice */ | |
381 | if (sz > off) | |
382 | sz = off; | |
fe39ffdc | 383 | char *p = CALL_UTIL (mmap64_)((char *) 0, sz, PROT_READ, MAP_PRIVATE, fd, (off64_t) 0); |
bb368aad VM |
384 | if (p == MAP_FAILED) |
385 | { | |
386 | TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: mmap failed for `%d'\n", fd); | |
387 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
388 | COL_WARN_NOFOLLOW, "mmap-failed"); | |
389 | return 0; | |
390 | } | |
391 | char elfclass = p[EI_CLASS]; | |
392 | if ((p[EI_MAG0] != ELFMAG0) || | |
393 | (p[EI_MAG1] != ELFMAG1) || | |
394 | (p[EI_MAG2] != ELFMAG2) || | |
395 | (p[EI_MAG3] != ELFMAG3) || | |
396 | (elfclass != ELFCLASS32 && elfclass != ELFCLASS64) | |
397 | ) | |
398 | { | |
399 | TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' is not executable ELF!\n", fd); | |
400 | CALL_UTIL (munmap)(p, sz); | |
401 | return 1; | |
402 | } | |
403 | Elf32_Ehdr *ehdr32 = (Elf32_Ehdr*) p; | |
404 | Elf64_Ehdr *ehdr64 = (Elf64_Ehdr*) p; | |
405 | Elf64_Off e_phoff; | |
406 | Elf64_Half e_phnum; | |
407 | Elf64_Half e_phentsize; | |
408 | if (elfclass == ELFCLASS32) | |
409 | { | |
410 | e_phoff = ehdr32->e_phoff; | |
411 | e_phnum = ehdr32->e_phnum; | |
412 | e_phentsize = ehdr32->e_phentsize; | |
413 | } | |
414 | else | |
415 | { | |
416 | e_phoff = ehdr64->e_phoff; | |
417 | e_phnum = ehdr64->e_phnum; | |
418 | e_phentsize = ehdr64->e_phentsize; | |
419 | } | |
420 | if ((sizeof (Elf32_Ehdr) > sz) || | |
421 | (sizeof (Elf64_Ehdr) > sz) || | |
422 | (e_phoff + e_phentsize * (e_phnum - 1) > sz)) | |
423 | { | |
424 | TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' ELF file did not fit in page!\n", fd); | |
425 | #if 0 | |
426 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
427 | COL_WARN_RISKYFOLLOW, "ELF header size"); | |
428 | #endif | |
429 | CALL_UTIL (munmap)(p, sz); | |
430 | return 1; | |
431 | } | |
432 | TprintfT (DBG_LT2, "check_fd_dynamic(): elfclass=%d, e_phoff=%lu e_phnum=%lu e_phentsize=%lu\n", | |
433 | (int) elfclass, (unsigned long) e_phoff, (unsigned long) e_phnum, | |
434 | (unsigned long) e_phentsize); | |
435 | int dynamic = 0; | |
436 | Elf64_Half i; | |
437 | for (i = 0; i < e_phnum; i++) | |
438 | { | |
439 | if (elfclass == ELFCLASS32) | |
440 | { | |
441 | if (PT_DYNAMIC == | |
442 | ((Elf32_Phdr*) (p + e_phoff + e_phentsize * i))->p_type) | |
443 | { | |
444 | dynamic = 1; | |
445 | break; | |
446 | } | |
447 | } | |
448 | else | |
449 | { | |
450 | if (PT_DYNAMIC == | |
451 | ((Elf64_Phdr*) (p + e_phoff + e_phentsize * i))->p_type) | |
452 | { | |
453 | dynamic = 1; | |
454 | break; | |
455 | } | |
456 | } | |
457 | } | |
458 | if (!dynamic) | |
459 | { | |
460 | TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: Command `%d' is not a dynamic executable!\n", fd); | |
461 | #if 0 | |
462 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
463 | COL_WARN_NOFOLLOW, "!dynamic"); | |
464 | #endif | |
465 | } | |
466 | else | |
467 | TprintfT (DBG_LT2, "check_fd_dynamic(): Command `%d' is a dynamic executable!\n", fd); | |
468 | CALL_UTIL (munmap)(p, sz); | |
469 | return dynamic; | |
470 | } | |
471 | ||
472 | static int | |
473 | check_dynamic (const char *execfile) | |
474 | { | |
475 | TprintfT (DBG_LT2, "check_dynamic(%s)\n", execfile); | |
476 | int fd = CALL_UTIL (open)(execfile, O_RDONLY); | |
477 | if (fd == -1) | |
478 | { | |
479 | TprintfT (DBG_LT0, "check_dynamic(): ERROR/WARNING: Command `%s' could not be opened!\n", execfile); | |
480 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
481 | COL_WARN_RISKYFOLLOW, "open"); | |
482 | return 1; /* follow, though exec will presumably fail */ | |
483 | } | |
484 | int ret = check_fd_dynamic (fd); | |
485 | CALL_UTIL (close)(fd); | |
486 | return ret; | |
487 | } | |
488 | ||
489 | static int | |
490 | path_collectable (const char *execfile) | |
491 | { | |
492 | TprintfT (DBG_LT0, "path_collectable(%s)\n", execfile); | |
493 | /* Check that execfile exists and is a collectable executable */ | |
494 | /* logging warning when collection is likely to be unsuccessful */ | |
495 | /* (if check isn't accurate, generally best not to include it) */ | |
496 | ||
497 | if (execfile && !__collector_strchr (execfile, '/')) | |
498 | { /* got an unqualified name */ | |
499 | /* XXXX locate execfile on PATH to be able to check it */ | |
500 | TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't check unqualified executable `%s'\n", execfile); | |
501 | #if 0 | |
502 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
503 | COL_WARN_RISKYFOLLOW, "path"); | |
504 | #endif | |
505 | return 1; /* follow unqualified execfile unchecked */ | |
506 | } | |
507 | struct stat sbuf; | |
508 | if (stat (execfile, &sbuf)) | |
509 | { /* can't stat it */ | |
510 | TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't stat `%s'\n", execfile); | |
511 | #if 0 | |
512 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
513 | COL_WARN_RISKYFOLLOW, "stat"); | |
514 | #endif | |
515 | return 1; /* follow, though exec will probably fail */ | |
516 | } | |
517 | TprintfT (DBG_LT2, "path_collectable(%s) mode=0%o uid=%d gid=%d\n", | |
518 | execfile, sbuf.st_mode, sbuf.st_uid, sbuf.st_gid); | |
519 | if (((sbuf.st_mode & S_IXUSR) == 0) || ((sbuf.st_mode & S_IFMT) == S_IFDIR)) | |
520 | { | |
521 | TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is NOT an executable file!\n", execfile); | |
522 | #if 0 | |
523 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
524 | COL_WARN_RISKYFOLLOW, "mode"); | |
525 | #endif | |
526 | return 1; /* follow, though exec will presumably fail */ | |
527 | } | |
528 | /* XXXX setxid(root) is OK iff libcollector is registered as secure */ | |
529 | /* XXXX setxid(non-root) is OK iff umask is accomodating */ | |
530 | if (((sbuf.st_mode & S_ISUID) != 0) || ((sbuf.st_mode & S_ISGID) != 0)) | |
531 | { | |
532 | TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is SetXID!\n", execfile); | |
533 | #if 0 | |
534 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
535 | COL_WARN_RISKYFOLLOW, "setxid"); | |
536 | #endif | |
537 | return 1; /* follow, though collection may be unreliable */ | |
538 | } | |
539 | if (!check_dynamic (execfile)) | |
540 | { | |
541 | TprintfT (DBG_LT0, "path_collectable(%s) WARNING/ERROR: not dynamic, not collectng!\n", execfile); | |
542 | return 0; /* don't follow, collection will fail unpredictably */ | |
543 | } | |
544 | TprintfT (DBG_LT2, "path_collectable(%s) OK!\n", execfile); | |
545 | return 1; /* OK to follow */ | |
546 | } | |
547 | ||
548 | static char * | |
549 | build_experiment_path (char * instring, size_t instring_sz, const char *lineage_str) | |
550 | { | |
551 | TprintfT (DBG_LT0, "build_experiment_path(,%ld, %s)\n", | |
552 | (long) instring_sz, lineage_str); | |
553 | const char *p = CALL_UTIL (strstr)(linetrace_exp_dir_name, DESCENDANT_EXPT_KEY); | |
554 | int basedir_sz; | |
555 | if (p) | |
556 | basedir_sz = p - linetrace_exp_dir_name + 4; /* +3 because of DESCENDANT_EXPT_KEY */ | |
557 | else | |
558 | basedir_sz = __collector_strlen (linetrace_exp_dir_name) + 1; | |
559 | int additional_sz = __collector_strlen (lineage_str) + 4; | |
560 | if (basedir_sz + additional_sz > instring_sz) | |
561 | { | |
562 | TprintfT (DBG_LT0, "build_experiment_path(%s,%s): ERROR: path too long: %d > %ld\n", | |
563 | linetrace_exp_dir_name, lineage_str, | |
564 | basedir_sz + additional_sz, (long) instring_sz); | |
565 | *instring = 0; | |
566 | return NULL; | |
567 | } | |
568 | __collector_strlcpy (instring, linetrace_exp_dir_name, basedir_sz); | |
569 | size_t slen = __collector_strlen (instring); | |
570 | CALL_UTIL (snprintf)(instring + slen, instring_sz - slen, "/%s.er", lineage_str); | |
571 | assert (__collector_strlen (instring) + 1 == basedir_sz + additional_sz); | |
572 | return instring; | |
573 | } | |
574 | ||
575 | static void | |
576 | check_reuid_change (uid_t ruid, uid_t euid) | |
577 | { | |
578 | uid_t curr_ruid = getuid (); | |
579 | uid_t curr_euid = geteuid (); | |
580 | mode_t curr_umask = umask (0); | |
581 | umask (curr_umask); /* restore original umask */ | |
582 | int W_oth = !(curr_umask & S_IWOTH); | |
583 | TprintfT (DBG_LT0, "check_reuid_change(%d,%d): umask=%03o\n", ruid, euid, curr_umask); | |
584 | TprintfT (DBG_LT0, "check_reuid_change(): umask W usr=%d grp=%d oth=%d\n", | |
585 | (int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth); | |
586 | if (ruid != -1) | |
587 | { | |
588 | TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_ruid, ruid); | |
589 | if ((curr_euid == 0) && (ruid != 0) && !W_oth) | |
590 | { | |
591 | /* changing to non-root ID, with umask blocking writes by other */ | |
592 | TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after ruid change (%d->%d)\n", | |
593 | curr_ruid, ruid); | |
594 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o ruid %d->%d</event>\n", | |
595 | SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_ruid, ruid); | |
596 | } | |
597 | } | |
598 | if (euid != -1) | |
599 | { | |
600 | TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_euid, euid); | |
601 | if ((curr_euid == 0) && (euid != 0) && !W_oth) | |
602 | { | |
603 | /* changing to non-root ID, with umask blocking writes by other */ | |
604 | TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after euid change (%d->%d)\n", | |
605 | curr_euid, euid); | |
606 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o euid %d->%d</event>\n", | |
607 | SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_euid, euid); | |
608 | } | |
609 | } | |
610 | } | |
611 | ||
612 | static void | |
613 | check_regid_change (gid_t rgid, gid_t egid) | |
614 | { | |
615 | gid_t curr_rgid = getgid (); | |
616 | gid_t curr_egid = getegid (); | |
617 | uid_t curr_euid = geteuid (); | |
618 | mode_t curr_umask = umask (0); | |
619 | umask (curr_umask); /* restore original umask */ | |
620 | int W_oth = !(curr_umask & S_IWOTH); | |
621 | TprintfT (DBG_LT0, "check_regid_change(%d,%d): umask=%03o euid=%d\n", | |
622 | rgid, egid, curr_umask, curr_euid); | |
623 | TprintfT (DBG_LT0, "umask W usr=%d grp=%d oth=%d\n", | |
624 | (int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth); | |
625 | if (rgid != -1) | |
626 | { | |
627 | TprintfT (DBG_LT0, "check_regid_change(%d->%d)\n", curr_rgid, rgid); | |
628 | if ((curr_euid == 0) && (rgid != 0) && !W_oth) | |
629 | { | |
630 | /* changing to non-root ID, with umask blocking writes by other */ | |
631 | TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after rgid change (%d->%d)\n", | |
632 | curr_rgid, rgid); | |
633 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o rgid %d->%d</event>\n", | |
634 | SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_rgid, rgid); | |
635 | } | |
636 | } | |
637 | if (egid != -1) | |
638 | { | |
639 | TprintfT (DBG_LT0, "check_regid_change(): check_egid_change(%d->%d)\n", curr_egid, egid); | |
640 | if ((curr_euid == 0) && (egid != 0) && !W_oth) | |
641 | { | |
642 | /* changing to non-root ID, with umask blocking writes by other */ | |
643 | TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after egid change (%d->%d)\n", | |
644 | curr_egid, egid); | |
645 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o egid %d->%d</event>\n", | |
646 | SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_egid, egid); | |
647 | } | |
648 | } | |
649 | } | |
650 | ||
651 | static int | |
652 | init_lineage_intf () | |
653 | { | |
654 | void *dlflag; | |
655 | TprintfT (DBG_LT2, "init_lineage_intf()\n"); | |
656 | ||
657 | static int nesting_check = 0; | |
658 | if (nesting_check >= 2) | |
659 | { | |
660 | /* segv before stack blows up */ | |
661 | nesting_check /= (nesting_check - 2); | |
662 | } | |
663 | nesting_check++; | |
664 | ||
665 | __real_fork = dlsym (RTLD_NEXT, "fork"); | |
666 | if (__real_fork == NULL) | |
667 | { | |
668 | __real_fork = dlsym (RTLD_DEFAULT, "fork"); | |
669 | if (__real_fork == NULL) | |
670 | return 1; | |
671 | dlflag = RTLD_DEFAULT; | |
672 | } | |
673 | else | |
674 | dlflag = RTLD_NEXT; | |
675 | TprintfT (DBG_LT2, "init_lineage_intf() using RTLD_%s\n", | |
676 | dlflag == RTLD_DEFAULT ? "DEFAULT" : "NEXT"); | |
bb368aad | 677 | __real_vfork = dlsym (dlflag, "vfork"); |
bb368aad | 678 | __real_execve = dlsym (dlflag, "execve"); |
bb368aad | 679 | __real_execvp = dlsym (dlflag, "execvp"); |
bb368aad | 680 | __real_execv = dlsym (dlflag, "execv"); |
bb368aad | 681 | __real_execle = dlsym (dlflag, "execle"); |
bb368aad | 682 | __real_execlp = dlsym (dlflag, "execlp"); |
bb368aad | 683 | __real_execl = dlsym (dlflag, "execl"); |
bb368aad | 684 | __real_clone = dlsym (dlflag, "clone"); |
66f76c54 VM |
685 | |
686 | __real_popen_2_17 = dlvsym (dlflag, "popen", "GLIBC_2.17"); | |
687 | __real_popen_2_2_5 = dlvsym (dlflag, "popen", "GLIBC_2.2.5"); | |
688 | __real_popen_2_1 = dlvsym (dlflag, "popen", "GLIBC_2.1"); | |
bb368aad | 689 | __real_popen_2_0 = dlvsym (dlflag, "popen", "GLIBC_2.0"); |
66f76c54 VM |
690 | if (__real_popen_2_17) |
691 | __real_popen = __real_popen_2_17; | |
692 | else if (__real_popen_2_2_5) | |
693 | __real_popen = __real_popen_2_2_5; | |
694 | else if (__real_popen_2_1) | |
695 | __real_popen = __real_popen_2_1; | |
696 | else if (__real_popen_2_0) | |
697 | __real_popen = __real_popen_2_0; | |
698 | else | |
699 | __real_popen = dlsym (dlflag, "popen"); | |
700 | ||
701 | __real_posix_spawn_2_17 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.17"); | |
702 | __real_posix_spawn_2_15 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.15"); | |
bb368aad | 703 | __real_posix_spawn_2_2_5 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2.5"); |
66f76c54 | 704 | __real_posix_spawn_2_2 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2"); |
35fab451 VM |
705 | if (__real_posix_spawn_2_17) |
706 | __real_posix_spawn = __real_posix_spawn_2_17; | |
707 | else if (__real_posix_spawn_2_15) | |
708 | __real_posix_spawn = __real_posix_spawn_2_15; | |
709 | else if (__real_posix_spawn_2_2_5) | |
710 | __real_posix_spawn = __real_posix_spawn_2_2_5; | |
711 | else if (__real_posix_spawn_2_2) | |
712 | __real_posix_spawn = __real_posix_spawn_2_2; | |
713 | else | |
714 | __real_posix_spawn = dlsym (dlflag, "posix_spawn"); | |
66f76c54 VM |
715 | |
716 | __real_posix_spawnp_2_17 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.17"); | |
717 | __real_posix_spawnp_2_15 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.15"); | |
bb368aad | 718 | __real_posix_spawnp_2_2_5 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2.5"); |
66f76c54 | 719 | __real_posix_spawnp_2_2 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2"); |
35fab451 VM |
720 | if (__real_posix_spawnp_2_17) |
721 | __real_posix_spawnp = __real_posix_spawnp_2_17; | |
722 | else if (__real_posix_spawnp_2_15) | |
723 | __real_posix_spawnp = __real_posix_spawnp_2_15; | |
724 | else if (__real_posix_spawnp_2_2_5) | |
725 | __real_posix_spawnp = __real_posix_spawnp_2_2_5; | |
726 | else if (__real_posix_spawnp_2_2) | |
727 | __real_posix_spawnp = __real_posix_spawnp_2_2; | |
728 | else | |
729 | __real_posix_spawnp = dlsym (dlflag, "posix_spawnp"); | |
66f76c54 | 730 | |
bb368aad | 731 | __real_grantpt = dlsym (dlflag, "grantpt"); |
bb368aad | 732 | __real_ptsname = dlsym (dlflag, "ptsname"); |
bb368aad | 733 | __real_system = dlsym (dlflag, "system"); |
bb368aad | 734 | __real_setuid = dlsym (dlflag, "setuid"); |
bb368aad | 735 | __real_seteuid = dlsym (dlflag, "seteuid"); |
bb368aad | 736 | __real_setreuid = dlsym (dlflag, "setreuid"); |
bb368aad | 737 | __real_setgid = dlsym (dlflag, "setgid"); |
bb368aad | 738 | __real_setegid = dlsym (dlflag, "setegid"); |
bb368aad | 739 | __real_setregid = dlsym (dlflag, "setregid"); |
66f76c54 VM |
740 | |
741 | #define PR_FUNC(f) TprintfT (DBG_LT2, "linetrace.c: " #f ": @%p\n", f) | |
742 | PR_FUNC (__real_fork); | |
743 | PR_FUNC (__real_vfork); | |
744 | PR_FUNC (__real_execve); | |
745 | PR_FUNC (__real_execvp); | |
746 | PR_FUNC (__real_execv); | |
747 | PR_FUNC (__real_execle); | |
748 | PR_FUNC (__real_execlp); | |
749 | PR_FUNC (__real_execl); | |
750 | PR_FUNC (__real_clone); | |
751 | PR_FUNC (__real_grantpt); | |
752 | PR_FUNC (__real_ptsname); | |
753 | PR_FUNC (__real_popen); | |
754 | PR_FUNC (__real_popen_2_17); | |
755 | PR_FUNC (__real_popen_2_2_5); | |
756 | PR_FUNC (__real_popen_2_1); | |
757 | PR_FUNC (__real_popen_2_0); | |
758 | PR_FUNC (__real_posix_spawn_2_17); | |
759 | PR_FUNC (__real_posix_spawn_2_15); | |
760 | PR_FUNC (__real_posix_spawn_2_2_5); | |
761 | PR_FUNC (__real_posix_spawn_2_2); | |
762 | PR_FUNC (__real_posix_spawnp_2_17); | |
763 | PR_FUNC (__real_posix_spawnp_2_15); | |
764 | PR_FUNC (__real_posix_spawnp_2_2_5); | |
765 | PR_FUNC (__real_posix_spawnp_2_2); | |
766 | PR_FUNC (__real_system); | |
767 | PR_FUNC (__real_posix_spawn); | |
768 | PR_FUNC (__real_posix_spawnp); | |
769 | PR_FUNC (__real_setuid); | |
770 | PR_FUNC (__real_seteuid); | |
771 | PR_FUNC (__real_setreuid); | |
772 | PR_FUNC (__real_setgid); | |
773 | PR_FUNC (__real_setegid); | |
774 | PR_FUNC (__real_setregid); | |
775 | ||
bb368aad VM |
776 | return 0; |
777 | } | |
778 | ||
779 | /*------------------------------------------------------------------------ */ | |
780 | /* Note: The following _prologue and _epilogue functions used to be dbx-visible. | |
781 | ||
782 | They are used to appropriately manage lineage-changing events, by | |
783 | quiescing and re-enabling/re-setting experiment collection before and after, | |
784 | and logging the lineage-change in the process/experiment undertaking it. | |
785 | As shown by the interposition functions for fork, exec, etc., which follow, | |
786 | the _prologue should be called immediately prior (such as a breakpoint | |
787 | action defined at function entry) and the _epilogue called immediately | |
788 | after (such as a breakpoint action defined at function return). | |
789 | */ | |
790 | ||
791 | /* | |
792 | Notes on MT from Solaris 10 man pthread_atfork: | |
793 | ||
794 | All multithreaded applications that call fork() in a POSIX | |
795 | threads program and do more than simply call exec(2) in the | |
796 | child of the fork need to ensure that the child is protected | |
797 | from deadlock. | |
798 | ||
799 | Since the "fork-one" model results in duplicating only the | |
800 | thread that called fork(), it is possible that at the time | |
801 | of the call another thread in the parent owns a lock. This | |
802 | thread is not duplicated in the child, so no thread will | |
803 | unlock this lock in the child. Deadlock occurs if the sin- | |
804 | gle thread in the child needs this lock. | |
805 | ||
806 | The problem is more serious with locks in libraries. Since | |
807 | a library writer does not know if the application using the | |
808 | library calls fork(), the library must protect itself from | |
809 | such a deadlock scenario. If the application that links | |
810 | with this library calls fork() and does not call exec() in | |
811 | the child, and if it needs a library lock that may be held | |
812 | by some other thread in the parent that is inside the | |
813 | library at the time of the fork, the application deadlocks | |
814 | inside the library. | |
815 | */ | |
816 | ||
817 | static void | |
818 | linetrace_ext_fork_prologue (const char *variant, char * n_lineage, int *following_fork) | |
819 | { | |
820 | TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n", | |
821 | variant, n_lineage, *following_fork); | |
822 | __collector_env_print ("fork_prologue start"); | |
823 | if (dbg_current_mode != FOLLOW_NONE) | |
824 | TprintfT (DBG_LT0, "linetrace_ext_fork_prologue(%s) ERROR: dbg_current_mode=%d, changing to FOLLOW_FORK!\n", | |
825 | variant, dbg_current_mode); | |
826 | dbg_current_mode = FOLLOW_ON; | |
827 | if (__collector_strncmp ((char *) variant, "clone", sizeof ("clone") - 1) == 0) | |
828 | { | |
829 | __collector_mutex_lock (&clone_lineage_lock); | |
830 | CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_C%d", curr_lineage, ++clone_linenum); | |
831 | __collector_mutex_unlock (&clone_lineage_lock); | |
832 | } | |
833 | else | |
834 | { | |
835 | __collector_mutex_lock (&fork_lineage_lock); | |
836 | CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_f%d", curr_lineage, ++fork_linenum); | |
837 | __collector_mutex_unlock (&fork_lineage_lock); | |
838 | } | |
839 | *following_fork = check_follow_fork (); | |
840 | ||
841 | /* write message before suspending, or it won't be written */ | |
842 | hrtime_t ts = GETRELTIME (); | |
843 | TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n", | |
844 | variant, n_lineage, *following_fork); | |
845 | __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\"/>\n", | |
846 | SP_JCMD_DESC_START, | |
847 | (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), | |
848 | variant, n_lineage, *following_fork); | |
849 | __collector_ext_dispatcher_thread_timer_suspend (); | |
850 | __collector_ext_hwc_lwp_suspend (); | |
851 | __collector_env_print ("fork_prologue end"); | |
852 | } | |
853 | ||
854 | static void | |
855 | linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * n_lineage, int *following_fork) | |
856 | { | |
857 | if (dbg_current_mode == FOLLOW_NONE) | |
858 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) ERROR: dbg_current_mode=%d!\n", | |
859 | variant, dbg_current_mode); | |
860 | /* compute descendant experiment name */ | |
861 | char new_exp_name[LT_MAXPATHLEN]; | |
862 | /* save exp_name to global var */ | |
863 | if (!build_experiment_path (new_exp_name, sizeof (new_exp_name), n_lineage)) | |
864 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s): ERROR SP_COLLECTOR_EXPNAME not set\n", n_lineage); | |
865 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s):%d() returned %d %s; child experiment name = %s\n", | |
866 | variant, *following_fork, ret, (ret ? "parent" : "child"), new_exp_name); | |
867 | if (ret == 0) | |
868 | { | |
869 | /* *************************************child */ | |
870 | __collector_env_print ("fork_epilogue child at start"); | |
871 | /* start a new line */ | |
872 | fork_linenum = 0; | |
873 | __collector_mutex_init (&fork_lineage_lock); | |
874 | clone_linenum = 0; | |
875 | __collector_mutex_init (&clone_lineage_lock); | |
876 | __collector_env_update (NULL); | |
877 | __collector_env_print ("fork_epilogue child after env_update"); | |
878 | __collector_clean_state (); | |
879 | __collector_env_print ("fork_epilogue child after clean_slate"); | |
880 | __collector_line_cleanup (); | |
881 | __collector_env_print ("fork_epilogue child after line_cleanup"); | |
882 | if (*following_fork) | |
883 | { | |
884 | /* stop recording this experiment, but preserve env vars */ | |
885 | linetrace_dormant (); | |
886 | __collector_env_print ("fork_epilogue child after linetrace_dormant"); | |
887 | ||
888 | //static char exp_name_env[LT_MAXPATHLEN]; | |
889 | char * exp_name_env = CALL_UTIL (calloc)(LT_MAXPATHLEN, 1); | |
890 | CALL_UTIL (snprintf)(exp_name_env, LT_MAXPATHLEN, "%s=%s", SP_COLLECTOR_EXPNAME, new_exp_name); | |
891 | CALL_UTIL (putenv)(exp_name_env); | |
892 | ||
893 | const char *params = CALL_UTIL (getenv)(SP_COLLECTOR_PARAMS); | |
894 | int ret; | |
309b9a1a | 895 | if (*new_exp_name == 0) |
bb368aad VM |
896 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n", |
897 | SP_COLLECTOR_EXPNAME); | |
898 | else if (params == NULL) | |
899 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n", | |
900 | SP_COLLECTOR_PARAMS); | |
901 | else if ((ret = __collector_open_experiment (new_exp_name, params, SP_ORIGIN_FORK))) | |
902 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: '%s' open failed, ret=%d\n", | |
903 | new_exp_name, ret); | |
904 | else | |
905 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: opened(%s)\n", new_exp_name); | |
906 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) returning to *child*\n", variant); | |
907 | } | |
908 | else | |
909 | { | |
910 | /* disable current and further linetrace experiment resumption */ | |
911 | TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) child calling line_close\n", variant); | |
912 | __collector_ext_line_close (); | |
913 | } | |
914 | __collector_env_print ("fork_epilogue child at end"); | |
915 | /* *************************************end child */ | |
916 | } | |
917 | else | |
918 | { | |
919 | /* *************************************parent */ | |
920 | __collector_env_print ("fork_epilogue parent at start"); | |
921 | __collector_ext_dispatcher_thread_timer_resume (); | |
922 | __collector_ext_hwc_lwp_resume (); | |
923 | hrtime_t ts = GETRELTIME (); | |
924 | char msg[256 + LT_MAXPATHLEN]; | |
925 | if (ret >= 0) | |
926 | CALL_UTIL (snprintf)(msg, sizeof (msg), "pid=%d", ret); | |
927 | else | |
928 | { | |
929 | /* delete stillborn experiment? */ | |
930 | char errmsg[256]; | |
931 | strerror_r (errno, errmsg, sizeof (errmsg)); | |
932 | CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg); | |
933 | } | |
934 | __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", | |
935 | SP_JCMD_DESC_STARTED, | |
936 | (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), | |
937 | variant, n_lineage, *following_fork, msg); | |
938 | /* environment remains set for collection */ | |
939 | __collector_env_print ("fork_epilogue parent at end"); | |
940 | /* *************************************end parent */ | |
941 | } | |
942 | dbg_current_mode = FOLLOW_NONE; | |
943 | *following_fork = 0; | |
944 | } | |
945 | ||
946 | static char** | |
947 | linetrace_ext_exec_prologue_end (const char *variant, const char* cmd_string, | |
948 | char *const envp[], int following_exec) | |
949 | { | |
950 | char **coll_env; | |
951 | TprintfT (DBG_LT0, "linetrace_ext_exec_prologue_end; variant=%s; cmd_string=%s; follow=%d\n", | |
952 | variant, cmd_string, following_exec); | |
953 | /* write message before suspending, or it won't be written */ | |
954 | hrtime_t ts = GETRELTIME (); | |
955 | __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", | |
956 | SP_JCMD_EXEC_START, | |
957 | (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), | |
958 | variant, new_lineage, following_exec, cmd_string); | |
959 | if (following_exec) | |
960 | { | |
961 | coll_env = __collector_env_allocate (envp, 0); | |
962 | __collector_env_update (coll_env); | |
963 | extern char **environ; /* the process' actual environment */ | |
964 | if (environ == envp) /* user selected process environment */ | |
965 | environ = coll_env; | |
966 | } | |
967 | else | |
968 | coll_env = (char**) envp; | |
969 | __collector_env_printall ("linetrace_ext_exec_prologue_end", coll_env); | |
970 | if (!CALL_UTIL (strstr)(variant, "posix_spawn")) | |
971 | { | |
972 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; | |
973 | __collector_suspend_experiment ("suspend_for_exec"); | |
974 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
975 | } | |
976 | if (CALL_UTIL (strstr)(variant, "posix_spawn")) | |
977 | { | |
978 | __collector_ext_dispatcher_thread_timer_suspend (); | |
979 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; | |
980 | __collector_ext_hwc_lwp_suspend (); | |
981 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
982 | } | |
983 | return (coll_env); | |
984 | } | |
985 | ||
986 | static char** | |
987 | linetrace_ext_exec_prologue (const char *variant, | |
988 | const char* path, char *const argv[], | |
989 | char *const envp[], int *following_exec) | |
990 | { | |
991 | char cmd_string[_POSIX_ARG_MAX] = {'\0'}; | |
992 | ||
993 | if (dbg_current_mode != FOLLOW_NONE) | |
994 | TprintfT (DBG_LT0, "linetrace_ext_exec_prologue() ERROR: dbg_current_mode=%d, changing to FOLLOW_EXEC!\n", dbg_current_mode); | |
995 | dbg_current_mode = FOLLOW_ON; | |
996 | *following_exec = check_follow_exec (path); | |
997 | if (path != NULL) | |
998 | { | |
999 | /* escape any newline, " or \ characters in the exec command */ | |
1000 | TprintfT (DBG_LT3, "linetrace_ext_exec_prologue(): arg0=%s\n", path); | |
1001 | /* leave space in log message for terminator (and header) */ | |
1002 | __collector_strlcpy (cmd_string, path, sizeof (cmd_string)); | |
1003 | size_t len; | |
1004 | unsigned argn = 0; | |
1005 | if (argv[0]) | |
1006 | { | |
1007 | char *p; | |
1008 | while (((p = argv[++argn]) != 0) && | |
1009 | (len = __collector_strlen (cmd_string)) < sizeof (cmd_string) - 2) | |
1010 | { | |
1011 | cmd_string[len++] = ' '; | |
1012 | __collector_strlcpy (cmd_string + len, p, sizeof (cmd_string) - len); | |
1013 | } | |
1014 | } | |
1015 | } | |
1016 | TprintfT (DBG_LT0, "linetrace_ext_exec_prologue(%s), lineage=%s, follow=%d, prog=%s, path=%s \n", | |
1017 | variant, new_lineage, *following_exec, cmd_string, path); | |
1018 | return linetrace_ext_exec_prologue_end (variant, cmd_string, envp, *following_exec); | |
1019 | } | |
1020 | ||
1021 | static void | |
1022 | linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec) | |
1023 | { | |
1024 | /* For exec, this routine is only entered if the exec failed */ | |
1025 | /* However, posix_spawn() is expected to return */ | |
1026 | if (dbg_current_mode == FOLLOW_NONE) | |
1027 | TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: dbg_current_mode=%d!\n", dbg_current_mode); | |
1028 | TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue(%s):%d returned: %d, errno=%d\n", | |
1029 | variant, *following_exec, ret, errno); | |
1030 | if (!CALL_UTIL (strstr)(variant, "posix_spawn")) | |
1031 | { | |
1032 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; | |
1033 | __collector_resume_experiment (); | |
1034 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
1035 | } | |
1036 | if (CALL_UTIL (strstr)(variant, "posix_spawn")) | |
1037 | { | |
1038 | __collector_ext_dispatcher_thread_timer_resume (); | |
1039 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; | |
1040 | __collector_ext_hwc_lwp_resume (); | |
1041 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
1042 | } | |
1043 | hrtime_t ts = GETRELTIME (); | |
1044 | char msg[256]; | |
1045 | if (ret) | |
1046 | { | |
1047 | char errmsg[256]; | |
1048 | strerror_r (errno, errmsg, sizeof (errmsg)); | |
1049 | CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg); | |
1050 | } | |
1051 | else | |
1052 | CALL_UTIL (snprintf)(msg, sizeof (msg), "rc=%d", ret); | |
1053 | if (!CALL_UTIL (strstr)(variant, "posix_spawn")) | |
1054 | __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", | |
1055 | SP_JCMD_EXEC_ERROR, | |
1056 | (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), | |
1057 | variant, new_lineage, *following_exec, msg); | |
1058 | if (envp == NULL) | |
1059 | TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: envp NULL after %s!\n", variant); | |
1060 | dbg_current_mode = FOLLOW_NONE; | |
1061 | *following_exec = 0; | |
1062 | } | |
1063 | ||
1064 | static void | |
1065 | linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo) | |
1066 | { | |
1067 | char cmd_string[_POSIX_ARG_MAX] = {'\0'}; | |
1068 | char execfile[_POSIX_ARG_MAX] = {'\0'}; | |
1069 | ||
1070 | if (dbg_current_mode != FOLLOW_NONE) | |
1071 | TprintfT (DBG_LT0, "linetrace_ext_combo_prologue() ERROR: dbg_current_mode=%d! changing to FOLLOW_ON\n", | |
1072 | dbg_current_mode); | |
1073 | dbg_current_mode = FOLLOW_ON; | |
1074 | if (cmd != NULL) | |
1075 | { | |
1076 | /* extract executable name from combo command */ | |
1077 | unsigned len = strcspn (cmd, " "); | |
1078 | __collector_strlcpy (execfile, cmd, len + 1); | |
1079 | ||
1080 | /* escape any newline, " or \ characters in the combo command */ | |
1081 | /* leave space in log message for terminator (and header) */ | |
1082 | __collector_strlcpy (cmd_string, cmd, sizeof (cmd_string)); | |
1083 | } | |
1084 | ||
1085 | *following_combo = check_follow_combo (execfile); | |
1086 | TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(%s) follow=%d, prog=%s\n\n", | |
1087 | variant, *following_combo, cmd_string); | |
1088 | ||
1089 | /* Construct the lineage string for the new image */ | |
1090 | new_lineage[0] = 0; | |
1091 | __collector_strcat (new_lineage, "XXX"); | |
1092 | ||
1093 | /* write message before suspending, or it won't be written */ | |
1094 | hrtime_t ts = GETRELTIME (); | |
1095 | __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n", | |
1096 | SP_JCMD_DESC_START, | |
1097 | (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), | |
1098 | variant, new_lineage, *following_combo, cmd_string); | |
1099 | if (*following_combo) | |
1100 | { | |
1101 | __collector_env_update (NULL); | |
1102 | TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(): Following %s(\"%s\")\n", variant, execfile); | |
1103 | } | |
1104 | __collector_ext_dispatcher_thread_timer_suspend (); | |
1105 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; | |
1106 | __collector_ext_hwc_lwp_suspend (); | |
1107 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
1108 | } | |
1109 | ||
1110 | static void | |
1111 | linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo) | |
1112 | { | |
1113 | if (dbg_current_mode == FOLLOW_NONE) | |
1114 | TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue() ERROR: dbg_current_mode=FOLLOW_NONE\n"); | |
1115 | TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue(%s):%d() returned %d\n", | |
1116 | variant, *following_combo, ret); | |
1117 | __collector_ext_dispatcher_thread_timer_resume (); | |
1118 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 1; | |
1119 | __collector_ext_hwc_lwp_resume (); | |
1120 | __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0; | |
1121 | hrtime_t ts = GETRELTIME (); | |
1122 | __collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" follow=\"%d\" msg=\"rc=%d\"/>\n", | |
1123 | SP_JCMD_DESC_STARTED, | |
1124 | (unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC), | |
1125 | variant, *following_combo, ret); | |
1126 | ||
1127 | dbg_current_mode = FOLLOW_NONE; | |
1128 | *following_combo = 0; | |
1129 | } | |
1130 | ||
1131 | /*------------------------------------------------------------- fork */ | |
1132 | pid_t fork () __attribute__ ((weak, alias ("__collector_fork"))); | |
1133 | pid_t _fork () __attribute__ ((weak, alias ("__collector_fork"))); | |
1134 | ||
1135 | pid_t | |
1136 | __collector_fork (void) | |
1137 | { | |
1138 | pid_t ret; | |
1139 | if (NULL_PTR (fork)) | |
1140 | { | |
1141 | TprintfT (DBG_LT0, "__collector_fork() calling init_lineage_intf()\n"); | |
1142 | init_lineage_intf (); | |
1143 | } | |
1144 | __collector_env_print ("__collector_fork start"); | |
1145 | int * guard = NULL; | |
1146 | int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; | |
1147 | TprintfT (DBG_LT0, "__collector_fork() interposition: line_mode=%d combo=%d\n", | |
1148 | line_mode, combo_flag); | |
1149 | if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) | |
1150 | { | |
1151 | TprintfT (DBG_LT0, "__collector_fork() not following, returning CALL_REAL(fork)()\n"); | |
1152 | return CALL_REAL (fork)(); | |
1153 | } | |
1154 | int following_fork = 0; | |
1155 | linetrace_ext_fork_prologue ("fork", new_lineage, &following_fork); | |
1156 | ||
1157 | /* since libpthread/fork ends up calling fork1, it's a combo */ | |
1158 | PUSH_REENTRANCE (guard); | |
1159 | ret = CALL_REAL (fork)(); | |
1160 | POP_REENTRANCE (guard); | |
1161 | linetrace_ext_fork_epilogue ("fork", ret, new_lineage, &following_fork); | |
1162 | return ret; | |
1163 | } | |
1164 | ||
1165 | /*------------------------------------------------------------- vfork */ | |
1166 | /* vfork interposition in the usual sense is not possible, since vfork(2) | |
1167 | relies on specifics of the stack frames in the parent and child which | |
1168 | only work when the child's exec (or _exit) are in the same stack frame | |
1169 | as the vfork: this isn't the case when there's interposition on exec. | |
1170 | As a workaround, the interposing vfork calls fork1 instead of the real | |
1171 | vfork. Note that fork1 is somewhat less efficient than vfork, and requires | |
1172 | additional memory, which may result in a change of application behaviour | |
1173 | when libcollector is loaded (even when collection is not active), | |
1174 | affecting not only direct use of vfork by the subject application, | |
1175 | but also indirect use through system, popen, and the other combos. | |
1176 | */ | |
1177 | pid_t vfork () __attribute__ ((weak, alias ("__collector_vfork"))); | |
1178 | pid_t _vfork () __attribute__ ((weak, alias ("__collector_vfork"))); | |
1179 | ||
1180 | pid_t | |
1181 | __collector_vfork (void) | |
1182 | { | |
1183 | if (NULL_PTR (vfork)) | |
1184 | init_lineage_intf (); | |
1185 | ||
1186 | int * guard = NULL; | |
1187 | int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; | |
1188 | ||
1189 | TprintfT (DBG_LT0, "__collector_vfork() interposing: line_mode=%d combo=%d\n", | |
1190 | line_mode, combo_flag); | |
1191 | if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) | |
1192 | return CALL_REAL (fork)(); | |
1193 | ||
1194 | /* this warning is also appropriate for combos which use vfork, | |
1195 | however, let's assume this is achieved elsewhere */ | |
1196 | (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN, | |
1197 | COL_WARN_VFORK, "fork"); | |
1198 | ||
1199 | char new_lineage[LT_MAXNAMELEN]; | |
1200 | new_lineage[0] = 0; | |
1201 | int following_fork = 0; | |
1202 | linetrace_ext_fork_prologue ("vfork", new_lineage, &following_fork); | |
1203 | ||
1204 | pid_t ret = CALL_REAL (fork)(); | |
1205 | linetrace_ext_fork_epilogue ("vfork", ret, new_lineage, &following_fork); | |
1206 | return ret; | |
1207 | } | |
1208 | ||
1209 | /*------------------------------------------------------------- execve */ | |
1210 | int execve () __attribute__ ((weak, alias ("__collector_execve"))); | |
1211 | ||
1212 | int | |
1213 | __collector_execve (const char* path, char *const argv[], char *const envp[]) | |
1214 | { | |
1215 | static char **coll_env = NULL; /* environment for collection */ | |
1216 | if (NULL_PTR (execve)) | |
1217 | init_lineage_intf (); | |
1218 | int * guard = NULL; | |
1219 | int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; | |
1220 | TprintfT (DBG_LT0, | |
1221 | "__collector_execve(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n", | |
1222 | path ? path : "NULL", | |
1223 | argv ? (argv[0] ? argv[0] : "NULL") : "NULL", | |
1224 | envp ? (envp[0] ? envp[0] : "NULL") : "NULL", | |
1225 | line_mode, combo_flag); | |
1226 | if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ | |
1227 | __collector_env_unset ((char**) envp); | |
1228 | if (line_mode != LM_TRACK_LINEAGE || combo_flag) | |
1229 | return CALL_REAL (execve)(path, argv, envp); | |
1230 | ||
1231 | int following_exec = 0; | |
1232 | coll_env = linetrace_ext_exec_prologue ("execve", path, argv, envp, &following_exec); | |
1233 | TprintfT (DBG_LT2, "__collector_execve(): coll_env=0x%p\n", coll_env); | |
1234 | __collector_env_printall ("__collector_execve", coll_env); | |
1235 | int ret = CALL_REAL (execve)(path, argv, coll_env); | |
1236 | linetrace_ext_exec_epilogue ("execve", envp, ret, &following_exec); | |
1237 | return ret; | |
1238 | } | |
1239 | ||
1240 | int execvp () __attribute__ ((weak, alias ("__collector_execvp"))); | |
1241 | ||
1242 | int | |
1243 | __collector_execvp (const char* file, char *const argv[]) | |
1244 | { | |
1245 | extern char **environ; /* the process' actual environment */ | |
1246 | char ** envp = environ; | |
1247 | if (NULL_PTR (execvp)) | |
1248 | init_lineage_intf (); | |
1249 | int * guard = NULL; | |
1250 | int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; | |
1251 | TprintfT (DBG_LT0, | |
1252 | "__collector_execvp(file=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n", | |
1253 | file ? file : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", | |
1254 | line_mode, combo_flag); | |
1255 | if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ | |
1256 | __collector_env_unset ((char**) envp); | |
1257 | if ((line_mode != LM_TRACK_LINEAGE) || combo_flag) | |
1258 | return CALL_REAL (execvp)(file, argv); | |
1259 | ||
1260 | int following_exec = 0; | |
1261 | #ifdef DEBUG | |
1262 | char **coll_env = /* environment for collection */ | |
1263 | #endif /* DEBUG */ | |
1264 | linetrace_ext_exec_prologue ("execvp", file, argv, envp, &following_exec); | |
1265 | TprintfT (DBG_LT0, "__collector_execvp(): coll_env=0x%p\n", coll_env); | |
1266 | ||
1267 | int ret = CALL_REAL (execvp)(file, argv); | |
1268 | linetrace_ext_exec_epilogue ("execvp", envp, ret, &following_exec); | |
1269 | return ret; | |
1270 | } | |
1271 | ||
1272 | int execv () __attribute__ ((weak, alias ("__collector_execv"))); | |
1273 | ||
1274 | int | |
1275 | __collector_execv (const char* path, char *const argv[]) | |
1276 | { | |
1277 | int ret; | |
1278 | extern char **environ; /* the process' actual environment */ | |
1279 | char ** envp = environ; | |
1280 | TprintfT (DBG_LT0, "__collector_execv(path=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n", | |
1281 | path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", | |
1282 | line_mode, get_combo_flag ()); | |
1283 | ||
1284 | ret = __collector_execve (path, argv, envp); | |
1285 | return ret; | |
1286 | } | |
1287 | ||
1288 | int execle (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execle"))); | |
1289 | ||
1290 | int | |
1291 | __collector_execle (const char* path, const char *arg0, ...) | |
1292 | { | |
1293 | TprintfT (DBG_LT0, | |
1294 | "__collector_execle(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", | |
1295 | path ? path : "NULL", arg0 ? arg0 : "NULL", | |
1296 | line_mode, get_combo_flag ()); | |
1297 | ||
1298 | char **argp; | |
1299 | va_list args; | |
1300 | char **argvec; | |
1301 | register char **environmentp; | |
1302 | int nargs = 0; | |
1303 | char *nextarg; | |
1304 | ||
1305 | va_start (args, arg0); | |
1306 | while (va_arg (args, char *) != (char *) 0) | |
1307 | nargs++; | |
1308 | ||
1309 | /* | |
1310 | * save the environment pointer, which is at the end of the | |
1311 | * variable argument list | |
1312 | */ | |
1313 | environmentp = va_arg (args, char **); | |
1314 | va_end (args); | |
1315 | ||
1316 | /* | |
1317 | * load the arguments in the variable argument list | |
1318 | * into the argument vector, and add the terminating null pointer | |
1319 | */ | |
1320 | va_start (args, arg0); | |
1321 | /* workaround for bugid 1242839 */ | |
1322 | argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); | |
1323 | argp = argvec; | |
1324 | *argp++ = (char *) arg0; | |
1325 | while ((nextarg = va_arg (args, char *)) != (char *) 0) | |
1326 | *argp++ = nextarg; | |
1327 | va_end (args); | |
1328 | *argp = (char *) 0; | |
1329 | return __collector_execve (path, argvec, environmentp); | |
1330 | } | |
1331 | ||
1332 | int execlp (const char* file, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execlp"))); | |
1333 | ||
1334 | int | |
1335 | __collector_execlp (const char* file, const char *arg0, ...) | |
1336 | { | |
1337 | TprintfT (DBG_LT0, | |
1338 | "__collector_execlp(file=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", | |
1339 | file ? file : "NULL", arg0 ? arg0 : "NULL", | |
1340 | line_mode, get_combo_flag ()); | |
1341 | char **argp; | |
1342 | va_list args; | |
1343 | char **argvec; | |
1344 | int nargs = 0; | |
1345 | char *nextarg; | |
1346 | ||
1347 | va_start (args, arg0); | |
1348 | while (va_arg (args, char *) != (char *) 0) | |
1349 | nargs++; | |
1350 | va_end (args); | |
1351 | ||
1352 | /* | |
1353 | * load the arguments in the variable argument list | |
1354 | * into the argument vector and add the terminating null pointer | |
1355 | */ | |
1356 | va_start (args, arg0); | |
1357 | ||
1358 | /* workaround for bugid 1242839 */ | |
1359 | argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); | |
1360 | argp = argvec; | |
1361 | *argp++ = (char *) arg0; | |
1362 | while ((nextarg = va_arg (args, char *)) != (char *) 0) | |
1363 | *argp++ = nextarg; | |
1364 | va_end (args); | |
1365 | *argp = (char *) 0; | |
1366 | return __collector_execvp (file, argvec); | |
1367 | } | |
1368 | ||
1369 | int execl (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execl"))); | |
1370 | ||
1371 | int | |
1372 | __collector_execl (const char* path, const char *arg0, ...) | |
1373 | { | |
1374 | TprintfT (DBG_LT0, | |
1375 | "__collector_execl(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n", | |
1376 | path ? path : "NULL", arg0 ? arg0 : "NULL", | |
1377 | line_mode, get_combo_flag ()); | |
1378 | char **argp; | |
1379 | va_list args; | |
1380 | char **argvec; | |
1381 | extern char **environ; | |
1382 | int nargs = 0; | |
1383 | char *nextarg; | |
1384 | va_start (args, arg0); | |
1385 | while (va_arg (args, char *) != (char *) 0) | |
1386 | nargs++; | |
1387 | va_end (args); | |
1388 | ||
1389 | /* | |
1390 | * load the arguments in the variable argument list | |
1391 | * into the argument vector and add the terminating null pointer | |
1392 | */ | |
1393 | va_start (args, arg0); | |
1394 | ||
1395 | /* workaround for bugid 1242839 */ | |
1396 | argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *))); | |
1397 | argp = argvec; | |
1398 | *argp++ = (char *) arg0; | |
1399 | while ((nextarg = va_arg (args, char *)) != (char *) 0) | |
1400 | *argp++ = nextarg; | |
1401 | va_end (args); | |
1402 | *argp = (char *) 0; | |
1403 | return __collector_execve (path, argvec, environ); | |
1404 | } | |
1405 | ||
1406 | #include <spawn.h> | |
1407 | ||
1408 | /*-------------------------------------------------------- posix_spawn */ | |
bb368aad VM |
1409 | // map interposed symbol versions |
1410 | static int | |
66f76c54 VM |
1411 | gprofng_posix_spawn (int(real_posix_spawn) (), |
1412 | pid_t *pidp, const char *path, | |
1413 | const posix_spawn_file_actions_t *file_actions, | |
1414 | const posix_spawnattr_t *attrp, | |
1415 | char *const argv[], char *const envp[]) | |
bb368aad VM |
1416 | { |
1417 | int ret; | |
1418 | static char **coll_env = NULL; /* environment for collection */ | |
66f76c54 | 1419 | if (real_posix_spawn == NULL) |
bb368aad | 1420 | { |
66f76c54 | 1421 | TprintfT (DBG_LT0, "gprofng_posix_spawn (path=%s) interposin ERROR, posix_spawn() not found by dlsym\n", |
bb368aad VM |
1422 | path ? path : "NULL"); |
1423 | return -1; /* probably should set errno */ | |
1424 | } | |
66f76c54 VM |
1425 | int *guard = NULL; |
1426 | int combo_flag = (line_mode == LM_TRACK_LINEAGE && | |
1427 | CHCK_REENTRANCE (guard)) ? 1 : 0; | |
1428 | TprintfT (DBG_LT0, "gprofng_posix_spawn @%p (%s, argv[0]=%s, env[0]=%s)" | |
1429 | "line_mode=%d combo=%d\n", (real_posix_spawn), path ? path : "NULL", | |
1430 | (argv && argv[0]) ? argv[0] : "NULL", | |
1431 | (envp && envp[0]) ? envp[0] : "NULL", line_mode, combo_flag); | |
bb368aad VM |
1432 | if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */ |
1433 | __collector_env_unset ((char**) envp); | |
1434 | ||
66f76c54 VM |
1435 | if (line_mode != LM_TRACK_LINEAGE || combo_flag) |
1436 | return (real_posix_spawn) (pidp, path, file_actions, attrp, argv, envp); | |
bb368aad | 1437 | int following_exec = 0; |
66f76c54 VM |
1438 | coll_env = linetrace_ext_exec_prologue ("posix_spawn", path, argv, envp, |
1439 | &following_exec); | |
1440 | __collector_env_printall ("gprofng_posix_spawn", coll_env); | |
bb368aad | 1441 | PUSH_REENTRANCE (guard); |
66f76c54 | 1442 | ret = real_posix_spawn (pidp, path, file_actions, attrp, argv, coll_env); |
bb368aad VM |
1443 | POP_REENTRANCE (guard); |
1444 | linetrace_ext_exec_epilogue ("posix_spawn", envp, ret, &following_exec); | |
1445 | return ret; | |
1446 | } | |
1447 | ||
35fab451 | 1448 | #define DCL_POSIX_SPAWN(dcl_f) \ |
66f76c54 VM |
1449 | int dcl_f (pid_t *pidp, const char *path, \ |
1450 | const posix_spawn_file_actions_t *file_actions, \ | |
1451 | const posix_spawnattr_t *attrp, \ | |
1452 | char *const argv[], char *const envp[]) \ | |
1453 | { \ | |
35fab451 | 1454 | if (__real_posix_spawn == NULL) \ |
66f76c54 | 1455 | init_lineage_intf (); \ |
35fab451 VM |
1456 | return gprofng_posix_spawn (__real_posix_spawn, pidp, path, file_actions, \ |
1457 | attrp, argv, envp); \ | |
66f76c54 VM |
1458 | } |
1459 | ||
1460 | ||
1461 | DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_17, posix_spawn@GLIBC_2.17) | |
1462 | DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_15, posix_spawn@GLIBC_2.15) | |
1463 | DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_2_5, posix_spawn@GLIBC_2.2.5) | |
1464 | DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_2, posix_spawn@GLIBC_2.2) | |
35fab451 | 1465 | DCL_POSIX_SPAWN (posix_spawn) |
bb368aad | 1466 | |
66f76c54 | 1467 | /*-------------------------------------------------------- posix_spawnp */ |
bb368aad | 1468 | static int |
66f76c54 VM |
1469 | gprofng_posix_spawnp (int (real_posix_spawnp) (), |
1470 | pid_t *pidp, const char *path, | |
1471 | const posix_spawn_file_actions_t *file_actions, | |
1472 | const posix_spawnattr_t *attrp, | |
1473 | char *const argv[], char *const envp[]) | |
bb368aad | 1474 | { |
bb368aad | 1475 | static char **coll_env = NULL; /* environment for collection */ |
66f76c54 VM |
1476 | |
1477 | if (real_posix_spawnp == NULL) | |
bb368aad | 1478 | { |
66f76c54 | 1479 | TprintfT (DBG_LT0, "gprofng_posix_spawnp (path=%s) interposin ERROR\n", |
bb368aad VM |
1480 | path ? path : "NULL"); |
1481 | return -1; /* probably should set errno */ | |
1482 | } | |
66f76c54 | 1483 | int *guard = NULL; |
bb368aad | 1484 | int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; |
66f76c54 VM |
1485 | TprintfT (DBG_LT0, "gprofng_posix_spawnp @%p (path=%s, argv[0]=%s, env[0]=%s) line_mode=%d combo=%d\n", |
1486 | real_posix_spawnp, path ? path : "NULL", | |
1487 | argv && argv[0] ? argv[0] : "NULL", | |
1488 | envp && envp[0] ? envp[0] : "NULL", line_mode, combo_flag); | |
bb368aad | 1489 | |
66f76c54 | 1490 | if (line_mode == LM_CLOSED) /* ensure collection environment is sanitized */ |
bb368aad VM |
1491 | __collector_env_unset ((char**) envp); |
1492 | if (line_mode != LM_TRACK_LINEAGE || combo_flag) | |
66f76c54 | 1493 | return (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, envp); |
bb368aad VM |
1494 | int following_exec = 0; |
1495 | coll_env = linetrace_ext_exec_prologue ("posix_spawnp", path, argv, envp, &following_exec); | |
1496 | TprintfT (DBG_LT0, "__collector_posix_spawnp(): coll_env=0x%p\n", coll_env); | |
1497 | __collector_env_printall ("__collector_posix_spawnp", coll_env); | |
1498 | PUSH_REENTRANCE (guard); | |
66f76c54 | 1499 | int ret = real_posix_spawnp (pidp, path, file_actions, attrp, argv, coll_env); |
bb368aad VM |
1500 | POP_REENTRANCE (guard); |
1501 | linetrace_ext_exec_epilogue ("posix_spawnp", envp, ret, &following_exec); | |
1502 | return ret; | |
1503 | } | |
1504 | ||
35fab451 | 1505 | #define DCL_POSIX_SPAWNP(dcl_f) \ |
66f76c54 VM |
1506 | int dcl_f (pid_t *pidp, const char *path, \ |
1507 | const posix_spawn_file_actions_t *file_actions, \ | |
1508 | const posix_spawnattr_t *attrp, \ | |
1509 | char *const argv[], char *const envp[]) \ | |
1510 | { \ | |
35fab451 | 1511 | if (__real_posix_spawnp == NULL) \ |
66f76c54 | 1512 | init_lineage_intf (); \ |
35fab451 | 1513 | return gprofng_posix_spawnp (__real_posix_spawnp, pidp, path, \ |
66f76c54 VM |
1514 | file_actions, attrp, argv, envp); \ |
1515 | } | |
1516 | ||
1517 | DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_17, posix_spawnp@GLIBC_2.17) | |
1518 | DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_15, posix_spawnp@GLIBC_2.15) | |
1519 | DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_2_5, posix_spawnp@GLIBC_2.2.5) | |
1520 | DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_2, posix_spawnp@GLIBC_2.2) | |
35fab451 | 1521 | DCL_POSIX_SPAWNP (posix_spawnp) |
66f76c54 | 1522 | |
bb368aad VM |
1523 | /*------------------------------------------------------------- system */ |
1524 | int system () __attribute__ ((weak, alias ("__collector_system"))); | |
1525 | ||
1526 | int | |
1527 | __collector_system (const char *cmd) | |
1528 | { | |
1529 | if (NULL_PTR (system)) | |
1530 | init_lineage_intf (); | |
1531 | TprintfT (DBG_LT0, | |
1532 | "__collector_system(cmd=%s) interposing: line_mode=%d combo=%d\n", | |
1533 | cmd ? cmd : "NULL", line_mode, get_combo_flag ()); | |
1534 | int *guard = NULL; | |
1535 | if (line_mode == LM_TRACK_LINEAGE) | |
1536 | INIT_REENTRANCE (guard); | |
1537 | if (guard == NULL) | |
1538 | return CALL_REAL (system)(cmd); | |
1539 | int following_combo = 0; | |
1540 | linetrace_ext_combo_prologue ("system", cmd, &following_combo); | |
1541 | PUSH_REENTRANCE (guard); | |
1542 | int ret = CALL_REAL (system)(cmd); | |
1543 | POP_REENTRANCE (guard); | |
1544 | linetrace_ext_combo_epilogue ("system", ret, &following_combo); | |
1545 | return ret; | |
1546 | } | |
1547 | ||
1548 | /*------------------------------------------------------------- popen */ | |
1549 | // map interposed symbol versions | |
35fab451 | 1550 | #define DCL_POPEN(dcl_f) \ |
66f76c54 VM |
1551 | FILE *dcl_f (const char *cmd, const char *mode) \ |
1552 | { \ | |
35fab451 | 1553 | if (__real_popen == NULL) \ |
66f76c54 VM |
1554 | init_lineage_intf (); \ |
1555 | TprintfT (DBG_LT0, #dcl_f " (%s) interposing: line_mode=%d combo=%d\n", \ | |
1556 | cmd ? cmd : "NULL", line_mode, get_combo_flag ()); \ | |
1557 | int *guard = NULL; \ | |
1558 | if (line_mode == LM_TRACK_LINEAGE) \ | |
1559 | INIT_REENTRANCE (guard); \ | |
1560 | if (guard == NULL) \ | |
35fab451 | 1561 | return __real_popen (cmd, mode); \ |
66f76c54 VM |
1562 | int following_combo = 0; \ |
1563 | linetrace_ext_combo_prologue ("popen", cmd, &following_combo); \ | |
1564 | PUSH_REENTRANCE (guard); \ | |
35fab451 | 1565 | FILE *ret = __real_popen (cmd, mode); \ |
66f76c54 VM |
1566 | POP_REENTRANCE (guard); \ |
1567 | linetrace_ext_combo_epilogue ("popen", ret == NULL ? -1 : 0, \ | |
1568 | &following_combo); \ | |
1569 | return ret; \ | |
1570 | } | |
1571 | ||
1572 | DCL_FUNC_VER (DCL_POPEN, popen_2_17, popen@GLIBC_2.17) | |
1573 | DCL_FUNC_VER (DCL_POPEN, popen_2_2_5, popen@GLIBC_2.2.5) | |
1574 | DCL_FUNC_VER (DCL_POPEN, popen_2_1, popen@GLIBC_2.1) | |
1575 | DCL_FUNC_VER (DCL_POPEN, popen_2_0, popen@GLIBC_2.0) | |
35fab451 | 1576 | DCL_POPEN (popen) |
bb368aad VM |
1577 | |
1578 | /*------------------------------------------------------------- grantpt */ | |
1579 | int grantpt () __attribute__ ((weak, alias ("__collector_grantpt"))); | |
1580 | ||
1581 | int | |
1582 | __collector_grantpt (const int fildes) | |
1583 | { | |
1584 | if (NULL_PTR (grantpt)) | |
1585 | init_lineage_intf (); | |
1586 | TprintfT (DBG_LT0, | |
1587 | "__collector_grantpt(%d) interposing: line_mode=%d combo=%d\n", | |
1588 | fildes, line_mode, get_combo_flag ()); | |
1589 | int *guard = NULL; | |
1590 | if (line_mode == LM_TRACK_LINEAGE) | |
1591 | INIT_REENTRANCE (guard); | |
1592 | if (guard == NULL) | |
1593 | return CALL_REAL (grantpt)(fildes); | |
1594 | int following_combo = 0; | |
1595 | linetrace_ext_combo_prologue ("grantpt", "/usr/lib/pt_chmod", &following_combo); | |
1596 | PUSH_REENTRANCE (guard); | |
1597 | int ret = CALL_REAL (grantpt)(fildes); | |
1598 | POP_REENTRANCE (guard); | |
1599 | linetrace_ext_combo_epilogue ("grantpt", ret, &following_combo); | |
1600 | return ret; | |
1601 | } | |
1602 | ||
1603 | /*------------------------------------------------------------- ptsname */ | |
1604 | char *ptsname () __attribute__ ((weak, alias ("__collector_ptsname"))); | |
1605 | ||
1606 | char * | |
1607 | __collector_ptsname (const int fildes) | |
1608 | { | |
1609 | if (NULL_PTR (ptsname)) | |
1610 | init_lineage_intf (); | |
1611 | TprintfT (DBG_LT0, | |
1612 | "__collector_ptsname(%d) interposing: line_mode=%d combo=%d\n", | |
1613 | fildes, line_mode, get_combo_flag ()); | |
1614 | int *guard = NULL; | |
1615 | if (line_mode == LM_TRACK_LINEAGE) | |
1616 | INIT_REENTRANCE (guard); | |
1617 | if (guard == NULL) | |
de8e7059 | 1618 | return CALL_REAL (ptsname)(fildes); |
bb368aad VM |
1619 | int following_combo = 0; |
1620 | linetrace_ext_combo_prologue ("ptsname", "/usr/lib/pt_chmod", &following_combo); | |
1621 | PUSH_REENTRANCE (guard); | |
de8e7059 | 1622 | char *ret = CALL_REAL (ptsname)(fildes); |
bb368aad VM |
1623 | POP_REENTRANCE (guard); |
1624 | linetrace_ext_combo_epilogue ("ptsname", (ret == NULL) ? (-1) : 1, &following_combo); | |
1625 | return ret; | |
1626 | } | |
1627 | ||
1628 | /*------------------------------------------------------------- clone */ | |
1629 | /* clone can be fork-like or pthread_create-like, depending on whether | |
1630 | * the flag CLONE_VM is set. If CLONE_VM is not set, then we interpose | |
1631 | * clone in the way similar to interposing fork; if CLONE_VM is set, | |
1632 | * then we interpose clone in the way similar to interposing pthread_create. | |
1633 | * One special case is not handled: when CLONE_VM is set but CLONE_THREAD | |
1634 | * is not, if the parent process exits earlier than the child process, | |
1635 | * experiment will close, losing data from child process. | |
1636 | */ | |
1637 | typedef struct __collector_clone_arg | |
1638 | { | |
1639 | int (*fn)(void *); | |
1640 | void * arg; | |
1641 | char * new_lineage; | |
1642 | int following_fork; | |
1643 | } __collector_clone_arg_t; | |
1644 | ||
1645 | static int | |
1646 | __collector_clone_fn (void *fn_arg) | |
1647 | { | |
1648 | int (*fn)(void *) = ((__collector_clone_arg_t*) fn_arg)->fn; | |
1649 | void * arg = ((__collector_clone_arg_t*) fn_arg)->arg; | |
1650 | char * new_lineage = ((__collector_clone_arg_t*) fn_arg)->new_lineage; | |
1651 | int following_fork = ((__collector_clone_arg_t*) fn_arg)->following_fork; | |
1652 | __collector_freeCSize (__collector_heap, fn_arg, sizeof (__collector_clone_arg_t)); | |
1653 | linetrace_ext_fork_epilogue ("clone", 0, new_lineage, &following_fork); | |
1654 | return fn (arg); | |
1655 | } | |
1656 | ||
1657 | int clone (int (*fn)(void *), void *, int, void *, ...) __attribute__ ((weak, alias ("__collector_clone"))); | |
1658 | ||
1659 | int | |
1660 | __collector_clone (int (*fn)(void *), void *child_stack, int flags, void *arg, | |
1661 | ... /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */) | |
1662 | { | |
1663 | int ret; | |
1664 | va_list va; | |
1665 | if (flags & CLONE_VM) | |
1666 | { | |
1667 | va_start (va, arg); | |
1668 | ret = __collector_ext_clone_pthread (fn, child_stack, flags, arg, va); | |
1669 | va_end (va); | |
1670 | } | |
1671 | else | |
1672 | { | |
1673 | if (NULL_PTR (clone)) | |
1674 | init_lineage_intf (); | |
1675 | int *guard = NULL; | |
1676 | int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0; | |
1677 | TprintfT (DBG_LT0, "__collector_clone() interposition: line_mode=%d combo=%d\n", | |
1678 | line_mode, combo_flag); | |
1679 | char new_lineage[LT_MAXNAMELEN]; | |
1680 | int following_fork = 0; | |
1681 | __collector_clone_arg_t *funcinfo = __collector_allocCSize (__collector_heap, sizeof (__collector_clone_arg_t), 1); | |
1682 | (*funcinfo).fn = fn; | |
1683 | (*funcinfo).arg = arg; | |
1684 | (*funcinfo).new_lineage = new_lineage; | |
1685 | (*funcinfo).following_fork = 0; | |
1686 | pid_t * ptid = NULL; | |
1687 | struct user_desc * tls = NULL; | |
1688 | pid_t * ctid = NULL; | |
1689 | int num_args = 0; | |
1690 | va_start (va, arg); | |
1691 | if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) | |
1692 | { | |
1693 | ptid = va_arg (va, pid_t *); | |
1694 | tls = va_arg (va, struct user_desc*); | |
1695 | ctid = va_arg (va, pid_t *); | |
1696 | num_args = 3; | |
1697 | } | |
1698 | else if (flags & CLONE_SETTLS) | |
1699 | { | |
1700 | ptid = va_arg (va, pid_t *); | |
1701 | tls = va_arg (va, struct user_desc*); | |
1702 | num_args = 2; | |
1703 | } | |
1704 | else if (flags & CLONE_PARENT_SETTID) | |
1705 | { | |
1706 | ptid = va_arg (va, pid_t *); | |
1707 | num_args = 1; | |
1708 | } | |
1709 | if ((line_mode != LM_TRACK_LINEAGE) || combo_flag || funcinfo == NULL) | |
1710 | { | |
1711 | switch (num_args) | |
1712 | { | |
1713 | case 3: | |
1714 | ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); | |
1715 | break; | |
1716 | case 2: | |
1717 | ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); | |
1718 | break; | |
1719 | case 1: | |
1720 | ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); | |
1721 | break; | |
1722 | default: | |
1723 | ret = CALL_REAL (clone)(fn, child_stack, flags, arg); | |
1724 | break; | |
1725 | } | |
1726 | ||
1727 | va_end (va); | |
1728 | return ret; | |
1729 | } | |
1730 | linetrace_ext_fork_prologue ("clone", new_lineage, &following_fork); | |
1731 | (*funcinfo).following_fork = following_fork; | |
1732 | switch (num_args) | |
1733 | { | |
1734 | case 3: | |
1735 | ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls, ctid); | |
1736 | break; | |
1737 | case 2: | |
1738 | ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls); | |
1739 | break; | |
1740 | case 1: | |
1741 | ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid); | |
1742 | break; | |
1743 | default: | |
1744 | ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo); | |
1745 | break; | |
1746 | } | |
1747 | va_end (va); | |
1748 | if (ret < 0) | |
1749 | __collector_freeCSize (__collector_heap, funcinfo, sizeof (__collector_clone_arg_t)); | |
1750 | TprintfT (DBG_LT0, "__collector_clone() interposing: pid=%d\n", ret); | |
1751 | linetrace_ext_fork_epilogue ("clone", ret, new_lineage, &following_fork); | |
1752 | } | |
1753 | return ret; | |
1754 | } | |
1755 | ||
1756 | /*-------------------------------------------------------------------- setuid */ | |
1757 | int setuid () __attribute__ ((weak, alias ("__collector_setuid"))); | |
1758 | int _setuid () __attribute__ ((weak, alias ("__collector_setuid"))); | |
1759 | ||
1760 | int | |
1761 | __collector_setuid (uid_t ruid) | |
1762 | { | |
1763 | if (NULL_PTR (setuid)) | |
1764 | init_lineage_intf (); | |
1765 | TprintfT (DBG_LT0, "__collector_setuid(0x%x) interposing\n", ruid); | |
1766 | check_reuid_change (ruid, -1); | |
1767 | int ret = CALL_REAL (setuid)(ruid); | |
1768 | TprintfT (DBG_LT0, "__collector_setuid(0x%x) returning %d\n", ruid, ret); | |
1769 | return ret; | |
1770 | } | |
1771 | ||
1772 | /*------------------------------------------------------------------- seteuid */ | |
1773 | int seteuid () __attribute__ ((weak, alias ("__collector_seteuid"))); | |
1774 | int _seteuid () __attribute__ ((weak, alias ("__collector_seteuid"))); | |
1775 | ||
1776 | int | |
1777 | __collector_seteuid (uid_t euid) | |
1778 | { | |
1779 | if (NULL_PTR (seteuid)) | |
1780 | init_lineage_intf (); | |
1781 | TprintfT (DBG_LT0, "__collector_seteuid(0x%x) interposing\n", euid); | |
1782 | check_reuid_change (-1, euid); | |
1783 | int ret = CALL_REAL (seteuid)(euid); | |
1784 | TprintfT (DBG_LT0, "__collector_seteuid(0x%x) returning %d\n", euid, ret); | |
1785 | return ret; | |
1786 | } | |
1787 | ||
1788 | /*------------------------------------------------------------------ setreuid */ | |
1789 | int setreuid () __attribute__ ((weak, alias ("__collector_setreuid"))); | |
1790 | int _setreuid () __attribute__ ((weak, alias ("__collector_setreuid"))); | |
1791 | ||
1792 | int | |
1793 | __collector_setreuid (uid_t ruid, uid_t euid) | |
1794 | { | |
1795 | if (NULL_PTR (setreuid)) | |
1796 | init_lineage_intf (); | |
1797 | TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) interposing\n", ruid, euid); | |
1798 | check_reuid_change (ruid, euid); | |
1799 | int ret = CALL_REAL (setreuid)(ruid, euid); | |
1800 | TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) returning %d\n", ruid, euid, ret); | |
1801 | return ret; | |
1802 | } | |
1803 | ||
1804 | /*-------------------------------------------------------------------- setgid */ | |
1805 | int setgid () __attribute__ ((weak, alias ("__collector_setgid"))); | |
1806 | int _setgid () __attribute__ ((weak, alias ("__collector_setgid"))); | |
1807 | ||
1808 | int | |
1809 | __collector_setgid (gid_t rgid) | |
1810 | { | |
1811 | if (NULL_PTR (setgid)) | |
1812 | init_lineage_intf (); | |
1813 | TprintfT (DBG_LT0, "__collector_setgid(0x%x) interposing\n", rgid); | |
1814 | check_regid_change (rgid, -1); | |
1815 | int ret = CALL_REAL (setgid)(rgid); | |
1816 | TprintfT (DBG_LT0, "__collector_setgid(0x%x) returning %d\n", rgid, ret); | |
1817 | return ret; | |
1818 | } | |
1819 | ||
1820 | /*------------------------------------------------------------------- setegid */ | |
1821 | int setegid () __attribute__ ((weak, alias ("__collector_setegid"))); | |
1822 | int _setegid () __attribute__ ((weak, alias ("__collector_setegid"))); | |
1823 | ||
1824 | int | |
1825 | __collector_setegid (gid_t egid) | |
1826 | { | |
1827 | if (NULL_PTR (setegid)) | |
1828 | init_lineage_intf (); | |
1829 | TprintfT (DBG_LT0, "__collector_setegid(0x%x) interposing\n", egid); | |
1830 | check_regid_change (-1, egid); | |
1831 | int ret = CALL_REAL (setegid)(egid); | |
1832 | TprintfT (DBG_LT0, "__collector_setegid(0x%x) returning %d\n", egid, ret); | |
1833 | return ret; | |
1834 | } | |
1835 | ||
1836 | /*------------------------------------------------------------------ setregid */ | |
1837 | int setregid () __attribute__ ((weak, alias ("__collector_setregid"))); | |
1838 | int _setregid () __attribute__ ((weak, alias ("__collector_setregid"))); | |
1839 | ||
1840 | int | |
1841 | __collector_setregid (gid_t rgid, gid_t egid) | |
1842 | { | |
1843 | if (NULL_PTR (setregid)) | |
1844 | init_lineage_intf (); | |
1845 | TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) interposing\n", rgid, egid); | |
1846 | check_regid_change (rgid, egid); | |
1847 | int ret = CALL_REAL (setregid)(rgid, egid); | |
1848 | TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) returning %d\n", rgid, egid, ret); | |
1849 | return ret; | |
1850 | } | |
1851 | ||
1852 | /*------------------------------------------------------- selective following */ | |
1853 | ||
1854 | static int | |
1855 | linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *progname) | |
1856 | { | |
1857 | regex_t regex_desc; | |
1858 | if (!follow_spec) | |
1859 | { | |
1860 | TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES NULL follow_spec\n"); | |
1861 | return 1; | |
1862 | } | |
1863 | int ercode = regcomp (®ex_desc, follow_spec, REG_EXTENDED | REG_NOSUB | REG_NEWLINE); | |
1864 | if (ercode) | |
1865 | { | |
1866 | // syntax error in parsing string | |
1867 | #ifdef DEBUG | |
1868 | char errbuf[256]; | |
1869 | regerror (ercode, ®ex_desc, errbuf, sizeof (errbuf)); | |
1870 | TprintfT (DBG_LT0, "linetrace_follow_experiment: regerror()=%s\n", errbuf); | |
1871 | #endif | |
1872 | return 1; | |
1873 | } | |
1874 | TprintfT (DBG_LT0, "linetrace_follow_experiment(): compiled spec='%s'\n", follow_spec); | |
1875 | if (lineage_str) | |
1876 | { | |
1877 | if (!regexec (®ex_desc, lineage_str, 0, NULL, 0)) | |
1878 | { | |
1879 | TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES lineage (follow_spec=%s,lineage=%s)\n", | |
1880 | follow_spec, lineage_str); | |
1881 | return 1; | |
1882 | } | |
1883 | } | |
1884 | if (progname) | |
1885 | { | |
1886 | if (!regexec (®ex_desc, progname, 0, NULL, 0)) | |
1887 | { | |
1888 | TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES progname (follow_spec=%s,progname=%s)\n", | |
1889 | follow_spec, progname); | |
1890 | return 1; | |
1891 | } | |
1892 | } | |
1893 | TprintfT (DBG_LT0, "linetrace_follow_experiment(): DOES NOT MATCH (follow_spec=%s,lineage=%s,progname=%s)\n", | |
1894 | follow_spec, lineage_str ? lineage_str : "NULL", | |
1895 | progname ? progname : "NULL"); | |
1896 | return 0; | |
1897 | } |