]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gprofng/libcollector/linetrace.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gprofng / libcollector / linetrace.c
CommitLineData
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
40int __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
41int dbg_current_mode = FOLLOW_NONE; /* for debug only */
42unsigned line_key = COLLECTOR_TSD_INVALID_KEY;
43line_mode_t line_mode = LM_DORMANT;
44int user_follow_mode = FOLLOW_ON;
45int java_mode = 0;
46
47static char *user_follow_spec;
48static char new_lineage[LT_MAXNAMELEN];
49static char curr_lineage[LT_MAXNAMELEN];
50static char linetrace_exp_dir_name[LT_MAXPATHLEN + 1]; // experiment directory
51
52/* lineage tracking for descendants of this process */
53
54static int fork_linenum = 0;
55static int line_initted = 0;
56static collector_mutex_t fork_lineage_lock = COLLECTOR_MUTEX_INITIALIZER;
57static 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
61static pid_t (*__real_fork) (void) = NULL;
62static pid_t (*__real_vfork) (void) = NULL;
63static int (*__real_execve) (const char *file, char *const argv[],
64 char *const envp[]) = NULL;
65static int (*__real_execvp) (const char *file, char *const argv[]) = NULL;
66static int (*__real_execv) (const char *file, char *const argv[]) = NULL;
67static int (*__real_execle) (const char *path, const char *arg, ...) = NULL;
68static int (*__real_execlp) (const char *file, const char *arg, ...) = NULL;
69static int (*__real_execl) (const char *file, const char *arg, ...) = NULL;
70static int (*__real_clone) (int (*fn) (void *), void *child_stack,
71 int flags, void *arg, ...) = NULL;
72static int (*__real_grantpt) (int fd) = NULL;
73static char *(*__real_ptsname) (int fd) = NULL;
74static FILE *(*__real_popen) (const char *command, const char *type) = NULL;
bb368aad 75static int clone_linenum = 0;
66f76c54
VM
76static FILE *(*__real_popen_2_17) (const char *command, const char *type) = NULL;
77static FILE *(*__real_popen_2_2_5) (const char *command, const char *type) = NULL;
de8e7059
VM
78static FILE *(*__real_popen_2_1) (const char *command, const char *type) = NULL;
79static FILE *(*__real_popen_2_0) (const char *command, const char *type) = NULL;
66f76c54
VM
80
81static 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
85static 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;
89static 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
93static 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
98static 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
102static 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;
106static 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
110static 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
114static int (*__real_system) (const char *command) = NULL;
115static 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;
119static 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;
123static int (*__real_setuid) (uid_t uid) = NULL;
124static int (*__real_seteuid) (uid_t euid) = NULL;
125static int (*__real_setreuid) (uid_t ruid, uid_t euid) = NULL;
126static int (*__real_setgid) (gid_t gid) = NULL;
127static int (*__real_setegid) (gid_t egid) = NULL;
128static int (*__real_setregid) (gid_t rgid, gid_t egid)= NULL;
bb368aad
VM
129static void linetrace_dormant ();
130static int check_follow_fork ();
131static int check_follow_exec (const char *execfile);
132static int check_follow_combo (const char *execfile);
133static int path_collectable (const char *execfile);
134static char * build_experiment_path (char *instring, size_t instring_sz, const char *lineage_str);
135static int init_lineage_intf ();
136
137/* ------- "Previously dbx-visible" function prototypes ----------------- */
138static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *execfile);
139static char *lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname);
140static void linetrace_ext_fork_prologue (const char *variant, char * new_lineage, int *following_fork);
141static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * new_lineage, int *following_fork);
142static char **linetrace_ext_exec_prologue (const char *variant,
143 const char* path, char *const argv[], char *const envp[], int *following_exec);
144static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec);
145static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo);
146static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo);
147
148#ifdef DEBUG
149static int
150get_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 */
159int
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 */
199int
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
249char *
250lineage_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 */
285void
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 */
314void
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 */
327static void
328linetrace_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
346static int
347check_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
354static int
355check_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
367static int
368check_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
375static int
376check_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
472static int
473check_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
489static int
490path_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
548static char *
549build_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
575static void
576check_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
612static void
613check_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
651static int
652init_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
817static void
818linetrace_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
854static void
855linetrace_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
946static char**
947linetrace_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
986static char**
987linetrace_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
1021static void
1022linetrace_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
1064static void
1065linetrace_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
1110static void
1111linetrace_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 */
1132pid_t fork () __attribute__ ((weak, alias ("__collector_fork")));
1133pid_t _fork () __attribute__ ((weak, alias ("__collector_fork")));
1134
1135pid_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 */
1177pid_t vfork () __attribute__ ((weak, alias ("__collector_vfork")));
1178pid_t _vfork () __attribute__ ((weak, alias ("__collector_vfork")));
1179
1180pid_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 */
1210int execve () __attribute__ ((weak, alias ("__collector_execve")));
1211
1212int
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
1240int execvp () __attribute__ ((weak, alias ("__collector_execvp")));
1241
1242int
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
1272int execv () __attribute__ ((weak, alias ("__collector_execv")));
1273
1274int
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
1288int execle (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execle")));
1289
1290int
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
1332int execlp (const char* file, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execlp")));
1333
1334int
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
1369int execl (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execl")));
1370
1371int
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
1410static int
66f76c54
VM
1411gprofng_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
1449int 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
1461DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_17, posix_spawn@GLIBC_2.17)
1462DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_15, posix_spawn@GLIBC_2.15)
1463DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_2_5, posix_spawn@GLIBC_2.2.5)
1464DCL_FUNC_VER (DCL_POSIX_SPAWN, posix_spawn_2_2, posix_spawn@GLIBC_2.2)
35fab451 1465DCL_POSIX_SPAWN (posix_spawn)
bb368aad 1466
66f76c54 1467/*-------------------------------------------------------- posix_spawnp */
bb368aad 1468static int
66f76c54
VM
1469gprofng_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
1506int 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
1517DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_17, posix_spawnp@GLIBC_2.17)
1518DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_15, posix_spawnp@GLIBC_2.15)
1519DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_2_5, posix_spawnp@GLIBC_2.2.5)
1520DCL_FUNC_VER (DCL_POSIX_SPAWNP, posix_spawnp_2_2, posix_spawnp@GLIBC_2.2)
35fab451 1521DCL_POSIX_SPAWNP (posix_spawnp)
66f76c54 1522
bb368aad
VM
1523/*------------------------------------------------------------- system */
1524int system () __attribute__ ((weak, alias ("__collector_system")));
1525
1526int
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
1572DCL_FUNC_VER (DCL_POPEN, popen_2_17, popen@GLIBC_2.17)
1573DCL_FUNC_VER (DCL_POPEN, popen_2_2_5, popen@GLIBC_2.2.5)
1574DCL_FUNC_VER (DCL_POPEN, popen_2_1, popen@GLIBC_2.1)
1575DCL_FUNC_VER (DCL_POPEN, popen_2_0, popen@GLIBC_2.0)
35fab451 1576DCL_POPEN (popen)
bb368aad
VM
1577
1578/*------------------------------------------------------------- grantpt */
1579int grantpt () __attribute__ ((weak, alias ("__collector_grantpt")));
1580
1581int
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 */
1604char *ptsname () __attribute__ ((weak, alias ("__collector_ptsname")));
1605
1606char *
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 */
1637typedef 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
1645static 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
1657int clone (int (*fn)(void *), void *, int, void *, ...) __attribute__ ((weak, alias ("__collector_clone")));
1658
1659int
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 */
1757int setuid () __attribute__ ((weak, alias ("__collector_setuid")));
1758int _setuid () __attribute__ ((weak, alias ("__collector_setuid")));
1759
1760int
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 */
1773int seteuid () __attribute__ ((weak, alias ("__collector_seteuid")));
1774int _seteuid () __attribute__ ((weak, alias ("__collector_seteuid")));
1775
1776int
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 */
1789int setreuid () __attribute__ ((weak, alias ("__collector_setreuid")));
1790int _setreuid () __attribute__ ((weak, alias ("__collector_setreuid")));
1791
1792int
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 */
1805int setgid () __attribute__ ((weak, alias ("__collector_setgid")));
1806int _setgid () __attribute__ ((weak, alias ("__collector_setgid")));
1807
1808int
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 */
1821int setegid () __attribute__ ((weak, alias ("__collector_setegid")));
1822int _setegid () __attribute__ ((weak, alias ("__collector_setegid")));
1823
1824int
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 */
1837int setregid () __attribute__ ((weak, alias ("__collector_setregid")));
1838int _setregid () __attribute__ ((weak, alias ("__collector_setregid")));
1839
1840int
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
1854static int
1855linetrace_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 (&regex_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, &regex_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 (&regex_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 (&regex_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}