1999 lines
73 KiB
C
1999 lines
73 KiB
C
|
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
||
|
Contributed by Oracle.
|
||
|
|
||
|
This file is part of GNU Binutils.
|
||
|
|
||
|
This program is free software; you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation; either version 3, or (at your option)
|
||
|
any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; if not, write to the Free Software
|
||
|
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
||
|
MA 02110-1301, USA. */
|
||
|
|
||
|
/*
|
||
|
* Lineage events for process fork, exec, etc.
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
#include <string.h>
|
||
|
#include <elf.h>
|
||
|
#include <regex.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/mman.h>
|
||
|
|
||
|
#include "descendants.h"
|
||
|
|
||
|
/* TprintfT(<level>,...) definitions. Adjust per module as needed */
|
||
|
#define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings
|
||
|
#define DBG_LTT 0 // for interposition on GLIBC functions
|
||
|
#define DBG_LT1 1 // for configuration details, warnings
|
||
|
#define DBG_LT2 2
|
||
|
#define DBG_LT3 3
|
||
|
|
||
|
#define LT_MAXNAMELEN 1024
|
||
|
#define LT_MAXPATHLEN 1024
|
||
|
|
||
|
int __collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
int dbg_current_mode = FOLLOW_NONE; /* for debug only */
|
||
|
unsigned line_key = COLLECTOR_TSD_INVALID_KEY;
|
||
|
line_mode_t line_mode = LM_DORMANT;
|
||
|
int user_follow_mode = FOLLOW_ON;
|
||
|
int java_mode = 0;
|
||
|
|
||
|
static char *user_follow_spec;
|
||
|
static char new_lineage[LT_MAXNAMELEN];
|
||
|
static char curr_lineage[LT_MAXNAMELEN];
|
||
|
static char linetrace_exp_dir_name[LT_MAXPATHLEN + 1]; // experiment directory
|
||
|
|
||
|
/* lineage tracking for descendants of this process */
|
||
|
|
||
|
static int fork_linenum = 0;
|
||
|
static int line_initted = 0;
|
||
|
static collector_mutex_t fork_lineage_lock = COLLECTOR_MUTEX_INITIALIZER;
|
||
|
static collector_mutex_t clone_lineage_lock = COLLECTOR_MUTEX_INITIALIZER;
|
||
|
|
||
|
/* interposition */
|
||
|
#define CALL_REAL(x) (*(int(*)())__real_##x)
|
||
|
#define CALL_REALC(x) (*(char*(*)())__real_##x)
|
||
|
#define CALL_REALF(x) (*(FILE*(*)())__real_##x)
|
||
|
#define NULL_PTR(x) ( __real_##x == NULL )
|
||
|
|
||
|
// For a given Linux, which lib functions have more than one GLIBC version? Do this:
|
||
|
// objdump -T `find /lib /lib64 -name "*.so*"` | grep GLIBC | grep text | grep \(
|
||
|
static void *__real_fork = NULL;
|
||
|
static void *__real_vfork = NULL;
|
||
|
static void *__real_execve = NULL;
|
||
|
static void *__real_execvp = NULL;
|
||
|
static void *__real_execv = NULL;
|
||
|
static void *__real_execle = NULL;
|
||
|
static void *__real_execlp = NULL;
|
||
|
static void *__real_execl = NULL;
|
||
|
static void *__real_clone = NULL;
|
||
|
static void *__real_grantpt = NULL;
|
||
|
static void *__real_ptsname = NULL;
|
||
|
static void *__real_popen = NULL;
|
||
|
static int clone_linenum = 0;
|
||
|
#if ARCH(Intel)
|
||
|
#if WSIZE(32)
|
||
|
static void *__real_popen_2_1 = NULL;
|
||
|
static void *__real_popen_2_0 = NULL;
|
||
|
static void *__real_posix_spawn_2_15 = NULL;
|
||
|
static void *__real_posix_spawnp_2_15 = NULL;
|
||
|
static void *__real_posix_spawn_2_2 = NULL;
|
||
|
static void *__real_posix_spawnp_2_2 = NULL;
|
||
|
#elif WSIZE(64)
|
||
|
static void *__real_posix_spawn_2_15 = NULL;
|
||
|
static void *__real_posix_spawnp_2_15 = NULL;
|
||
|
static void *__real_posix_spawn_2_2_5 = NULL;
|
||
|
static void *__real_posix_spawnp_2_2_5 = NULL;
|
||
|
#endif /* WSIZE() */
|
||
|
#endif /* ARCH(Intel) */
|
||
|
static void *__real_system = NULL;
|
||
|
static void *__real_posix_spawn = NULL;
|
||
|
static void *__real_posix_spawnp = NULL;
|
||
|
static void *__real_setuid = NULL;
|
||
|
static void *__real_seteuid = NULL;
|
||
|
static void *__real_setreuid = NULL;
|
||
|
static void *__real_setgid = NULL;
|
||
|
static void *__real_setegid = NULL;
|
||
|
static void *__real_setregid = NULL;
|
||
|
static void linetrace_dormant ();
|
||
|
static int check_follow_fork ();
|
||
|
static int check_follow_exec (const char *execfile);
|
||
|
static int check_follow_combo (const char *execfile);
|
||
|
static int path_collectable (const char *execfile);
|
||
|
static char * build_experiment_path (char *instring, size_t instring_sz, const char *lineage_str);
|
||
|
static int init_lineage_intf ();
|
||
|
|
||
|
/* ------- "Previously dbx-visible" function prototypes ----------------- */
|
||
|
static int linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *execfile);
|
||
|
static char *lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname);
|
||
|
static void linetrace_ext_fork_prologue (const char *variant, char * new_lineage, int *following_fork);
|
||
|
static void linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * new_lineage, int *following_fork);
|
||
|
static char **linetrace_ext_exec_prologue (const char *variant,
|
||
|
const char* path, char *const argv[], char *const envp[], int *following_exec);
|
||
|
static void linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec);
|
||
|
static void linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo);
|
||
|
static void linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
static int
|
||
|
get_combo_flag ()
|
||
|
{
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = ((line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0);
|
||
|
return combo_flag;
|
||
|
}
|
||
|
#endif /* DEBUG */
|
||
|
|
||
|
/* must be called for potentially live experiment */
|
||
|
int
|
||
|
__collector_ext_line_init (int *precord_this_experiment,
|
||
|
const char * progspec, const char * progname)
|
||
|
{
|
||
|
*precord_this_experiment = 1;
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_init(%s)\n", progspec);
|
||
|
if (NULL_PTR (fork))
|
||
|
if (init_lineage_intf ())
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_init() ERROR: initialization failed.\n");
|
||
|
return COL_ERROR_LINEINIT;
|
||
|
}
|
||
|
/* check the follow spec */
|
||
|
user_follow_spec = CALL_UTIL (getenv)(SP_COLLECTOR_FOLLOW_SPEC);
|
||
|
if (user_follow_spec != NULL)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "collector: %s=%s\n", SP_COLLECTOR_FOLLOW_SPEC, user_follow_spec);
|
||
|
if (!linetrace_follow_experiment (user_follow_spec, curr_lineage, progname))
|
||
|
{
|
||
|
*precord_this_experiment = 0;
|
||
|
TprintfT (DBG_LT0, "collector: -F =<regex> does not match, will NOT be followed\n");
|
||
|
}
|
||
|
else
|
||
|
TprintfT (DBG_LT0, "collector: -F =<regex> matches, will be followed\n");
|
||
|
user_follow_mode = FOLLOW_ALL;
|
||
|
}
|
||
|
__collector_env_save_preloads ();
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_init(), progname=%s, followspec=%s, followthis=%d\n",
|
||
|
progname, user_follow_spec ? user_follow_spec : "NULL",
|
||
|
*precord_this_experiment);
|
||
|
line_mode = LM_TRACK_LINEAGE; /* even if we don't follow, we report the interposition */
|
||
|
line_initted = 1;
|
||
|
return COL_ERROR_NONE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* int __collector_ext_line_install(args)
|
||
|
* Check args to determine which line events to follow.
|
||
|
* Create tsd key for combo flag.
|
||
|
*/
|
||
|
int
|
||
|
__collector_ext_line_install (char *args, const char * expname)
|
||
|
{
|
||
|
if (!line_initted)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_install(%s) ERROR: init hasn't be called yet\n", args);
|
||
|
return COL_ERROR_EXPOPEN;
|
||
|
}
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_install(%s, %s)\n", args, expname);
|
||
|
line_key = __collector_tsd_create_key (sizeof (int), NULL, NULL);
|
||
|
|
||
|
/* determine experiment name */
|
||
|
__collector_strlcpy (linetrace_exp_dir_name, expname, sizeof (linetrace_exp_dir_name));
|
||
|
lineage_from_expname (curr_lineage, sizeof (curr_lineage), linetrace_exp_dir_name);
|
||
|
user_follow_mode = CALL_UTIL (atoi)(args);
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_install() user_follow_mode=0x%X, linetrace_exp_dir_name=%s\n",
|
||
|
user_follow_mode, linetrace_exp_dir_name);
|
||
|
|
||
|
// determine java mode
|
||
|
char * java_follow_env = CALL_UTIL (getenv)(JAVA_TOOL_OPTIONS);
|
||
|
if (java_follow_env != NULL && CALL_UTIL (strstr)(java_follow_env, COLLECTOR_JVMTI_OPTION))
|
||
|
java_mode = 1;
|
||
|
|
||
|
// backup collector specific env
|
||
|
if (sp_env_backup == NULL)
|
||
|
{
|
||
|
sp_env_backup = __collector_env_backup ();
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_install creating sp_env_backup -- 0x%p\n", sp_env_backup);
|
||
|
}
|
||
|
else
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_install existing sp_env_backup -- 0x%p\n", sp_env_backup);
|
||
|
if (user_follow_mode == FOLLOW_NONE)
|
||
|
__collector_env_unset (NULL);
|
||
|
|
||
|
char logmsg[256];
|
||
|
logmsg[0] = '\0';
|
||
|
if (user_follow_mode != FOLLOW_NONE)
|
||
|
CALL_UTIL (strlcat)(logmsg, "fork|exec|combo", sizeof (logmsg));
|
||
|
size_t slen = __collector_strlen (logmsg);
|
||
|
if (slen > 0)
|
||
|
logmsg[slen] = '\0';
|
||
|
else
|
||
|
CALL_UTIL (strlcat)(logmsg, "none", sizeof (logmsg));
|
||
|
|
||
|
/* report which line events are followed */
|
||
|
(void) __collector_log_write ("<setting %s=\"%s\"/>\n", SP_JCMD_LINETRACE, logmsg);
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_install(%s): %s \n", expname, logmsg);
|
||
|
return COL_ERROR_NONE;
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
lineage_from_expname (char *lineage_str, size_t lstr_sz, const char *expname)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "lineage_from_expname(%s, %s)\n", lineage_str, expname);
|
||
|
char *p = NULL;
|
||
|
if (lstr_sz < 1 || !lineage_str || !expname)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "lineage_from_expname(): ERROR, null string\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
/* determine lineage from experiment name */
|
||
|
p = __collector_strrchr (expname, '/');
|
||
|
if ((p == NULL) || (*++p != '_'))
|
||
|
{
|
||
|
lineage_str[0] = 0;
|
||
|
TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\".\" (founder)\n", expname);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
size_t tmp = __collector_strlcpy (lineage_str, p, lstr_sz);
|
||
|
if (tmp >= lstr_sz)
|
||
|
TprintfT (DBG_LT0, "lineage_from_expname(): ERROR: expt=%s lineage=\"%s\" truncated %ld characters\n",
|
||
|
expname, lineage_str, (long) (tmp - lstr_sz));
|
||
|
lineage_str[lstr_sz - 1] = 0;
|
||
|
p = __collector_strchr (lineage_str, '.');
|
||
|
if (p != NULL)
|
||
|
*p = '\0';
|
||
|
TprintfT (DBG_LT2, "lineage_from_expname(): expt=%s lineage=\"%s\"\n", expname, lineage_str);
|
||
|
}
|
||
|
return lineage_str;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* void __collector_line_cleanup (void)
|
||
|
* Disable logging. Clear backup ENV.
|
||
|
*/
|
||
|
void
|
||
|
__collector_line_cleanup (void)
|
||
|
{
|
||
|
if (line_mode == LM_CLOSED)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_line_cleanup(): WARNING, line is already closed\n");
|
||
|
return;
|
||
|
}
|
||
|
else if (line_mode == LM_DORMANT)
|
||
|
TprintfT (DBG_LT0, "__collector_line_cleanup(): ERROR, line is DORMANT\n");
|
||
|
else
|
||
|
TprintfT (DBG_LT0, "__collector_line_cleanup()\n");
|
||
|
line_mode = LM_CLOSED;
|
||
|
user_follow_mode = FOLLOW_NONE;
|
||
|
dbg_current_mode = FOLLOW_NONE; /* for debug only */
|
||
|
line_key = COLLECTOR_TSD_INVALID_KEY;
|
||
|
java_mode = 0;
|
||
|
if (sp_env_backup != NULL)
|
||
|
{
|
||
|
__collector_env_backup_free ();
|
||
|
sp_env_backup = NULL;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* void __collector_ext_line_close (void)
|
||
|
* Disable logging. Cleans ENV vars. Clear backup ENV.
|
||
|
*/
|
||
|
void
|
||
|
__collector_ext_line_close (void)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_ext_line_close()\n");
|
||
|
__collector_line_cleanup ();
|
||
|
__collector_env_unset (NULL);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* void linetrace_dormant(void)
|
||
|
* Disable logging. Preserve ENV vars.
|
||
|
*/
|
||
|
static void
|
||
|
linetrace_dormant (void)
|
||
|
{
|
||
|
if (line_mode == LM_DORMANT)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "linetrace_dormant() -- already dormant\n");
|
||
|
return;
|
||
|
}
|
||
|
else if (line_mode == LM_CLOSED)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "linetrace_dormant(): ERROR, line is already CLOSED\n");
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
TprintfT (DBG_LT0, "linetrace_dormant()\n");
|
||
|
line_mode = LM_DORMANT;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
check_follow_fork ()
|
||
|
{
|
||
|
int follow = (user_follow_mode != FOLLOW_NONE);
|
||
|
TprintfT (DBG_LT0, "check_follow_fork()=%d\n", follow);
|
||
|
return follow;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
check_follow_exec (const char *execfile)
|
||
|
{
|
||
|
int follow = (user_follow_mode != FOLLOW_NONE);
|
||
|
if (follow)
|
||
|
{
|
||
|
/* revise based on collectability of execfile */
|
||
|
follow = path_collectable (execfile);
|
||
|
}
|
||
|
TprintfT (DBG_LT0, "check_follow_exec(%s)=%d\n", execfile, follow);
|
||
|
return follow;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
check_follow_combo (const char *execfile)
|
||
|
{
|
||
|
int follow = (user_follow_mode != FOLLOW_NONE);
|
||
|
TprintfT (DBG_LT0, "check_follow_combo(%s)=%d\n", execfile, follow);
|
||
|
return follow;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
check_fd_dynamic (int fd)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_fd_dynamic(%d)\n", fd);
|
||
|
off_t off = CALL_UTIL (lseek)(fd, (off_t) 0, SEEK_END);
|
||
|
size_t sz = (size_t) 8192; /* one page should suffice */
|
||
|
if (sz > off)
|
||
|
sz = off;
|
||
|
char *p = CALL_UTIL (mmap64)((char *) 0, sz, PROT_READ, MAP_PRIVATE, fd, (off64_t) 0);
|
||
|
if (p == MAP_FAILED)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: mmap failed for `%d'\n", fd);
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_NOFOLLOW, "mmap-failed");
|
||
|
return 0;
|
||
|
}
|
||
|
char elfclass = p[EI_CLASS];
|
||
|
if ((p[EI_MAG0] != ELFMAG0) ||
|
||
|
(p[EI_MAG1] != ELFMAG1) ||
|
||
|
(p[EI_MAG2] != ELFMAG2) ||
|
||
|
(p[EI_MAG3] != ELFMAG3) ||
|
||
|
(elfclass != ELFCLASS32 && elfclass != ELFCLASS64)
|
||
|
)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' is not executable ELF!\n", fd);
|
||
|
CALL_UTIL (munmap)(p, sz);
|
||
|
return 1;
|
||
|
}
|
||
|
Elf32_Ehdr *ehdr32 = (Elf32_Ehdr*) p;
|
||
|
Elf64_Ehdr *ehdr64 = (Elf64_Ehdr*) p;
|
||
|
Elf64_Off e_phoff;
|
||
|
Elf64_Half e_phnum;
|
||
|
Elf64_Half e_phentsize;
|
||
|
if (elfclass == ELFCLASS32)
|
||
|
{
|
||
|
e_phoff = ehdr32->e_phoff;
|
||
|
e_phnum = ehdr32->e_phnum;
|
||
|
e_phentsize = ehdr32->e_phentsize;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
e_phoff = ehdr64->e_phoff;
|
||
|
e_phnum = ehdr64->e_phnum;
|
||
|
e_phentsize = ehdr64->e_phentsize;
|
||
|
}
|
||
|
if ((sizeof (Elf32_Ehdr) > sz) ||
|
||
|
(sizeof (Elf64_Ehdr) > sz) ||
|
||
|
(e_phoff + e_phentsize * (e_phnum - 1) > sz))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_fd_dynamic(): WARNING: Command `%d' ELF file did not fit in page!\n", fd);
|
||
|
#if 0
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_RISKYFOLLOW, "ELF header size");
|
||
|
#endif
|
||
|
CALL_UTIL (munmap)(p, sz);
|
||
|
return 1;
|
||
|
}
|
||
|
TprintfT (DBG_LT2, "check_fd_dynamic(): elfclass=%d, e_phoff=%lu e_phnum=%lu e_phentsize=%lu\n",
|
||
|
(int) elfclass, (unsigned long) e_phoff, (unsigned long) e_phnum,
|
||
|
(unsigned long) e_phentsize);
|
||
|
int dynamic = 0;
|
||
|
Elf64_Half i;
|
||
|
for (i = 0; i < e_phnum; i++)
|
||
|
{
|
||
|
if (elfclass == ELFCLASS32)
|
||
|
{
|
||
|
if (PT_DYNAMIC ==
|
||
|
((Elf32_Phdr*) (p + e_phoff + e_phentsize * i))->p_type)
|
||
|
{
|
||
|
dynamic = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (PT_DYNAMIC ==
|
||
|
((Elf64_Phdr*) (p + e_phoff + e_phentsize * i))->p_type)
|
||
|
{
|
||
|
dynamic = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!dynamic)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_fd_dynamic(): ERROR/WARNING: Command `%d' is not a dynamic executable!\n", fd);
|
||
|
#if 0
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_NOFOLLOW, "!dynamic");
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
TprintfT (DBG_LT2, "check_fd_dynamic(): Command `%d' is a dynamic executable!\n", fd);
|
||
|
CALL_UTIL (munmap)(p, sz);
|
||
|
return dynamic;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
check_dynamic (const char *execfile)
|
||
|
{
|
||
|
TprintfT (DBG_LT2, "check_dynamic(%s)\n", execfile);
|
||
|
int fd = CALL_UTIL (open)(execfile, O_RDONLY);
|
||
|
if (fd == -1)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_dynamic(): ERROR/WARNING: Command `%s' could not be opened!\n", execfile);
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_RISKYFOLLOW, "open");
|
||
|
return 1; /* follow, though exec will presumably fail */
|
||
|
}
|
||
|
int ret = check_fd_dynamic (fd);
|
||
|
CALL_UTIL (close)(fd);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
path_collectable (const char *execfile)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "path_collectable(%s)\n", execfile);
|
||
|
/* Check that execfile exists and is a collectable executable */
|
||
|
/* logging warning when collection is likely to be unsuccessful */
|
||
|
/* (if check isn't accurate, generally best not to include it) */
|
||
|
|
||
|
if (execfile && !__collector_strchr (execfile, '/'))
|
||
|
{ /* got an unqualified name */
|
||
|
/* XXXX locate execfile on PATH to be able to check it */
|
||
|
TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't check unqualified executable `%s'\n", execfile);
|
||
|
#if 0
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_RISKYFOLLOW, "path");
|
||
|
#endif
|
||
|
return 1; /* follow unqualified execfile unchecked */
|
||
|
}
|
||
|
struct stat sbuf;
|
||
|
if (stat (execfile, &sbuf))
|
||
|
{ /* can't stat it */
|
||
|
TprintfT (DBG_LT0, "path_collectable(): WARNING: Can't stat `%s'\n", execfile);
|
||
|
#if 0
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_RISKYFOLLOW, "stat");
|
||
|
#endif
|
||
|
return 1; /* follow, though exec will probably fail */
|
||
|
}
|
||
|
TprintfT (DBG_LT2, "path_collectable(%s) mode=0%o uid=%d gid=%d\n",
|
||
|
execfile, sbuf.st_mode, sbuf.st_uid, sbuf.st_gid);
|
||
|
if (((sbuf.st_mode & S_IXUSR) == 0) || ((sbuf.st_mode & S_IFMT) == S_IFDIR))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is NOT an executable file!\n", execfile);
|
||
|
#if 0
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_RISKYFOLLOW, "mode");
|
||
|
#endif
|
||
|
return 1; /* follow, though exec will presumably fail */
|
||
|
}
|
||
|
/* XXXX setxid(root) is OK iff libcollector is registered as secure */
|
||
|
/* XXXX setxid(non-root) is OK iff umask is accomodating */
|
||
|
if (((sbuf.st_mode & S_ISUID) != 0) || ((sbuf.st_mode & S_ISGID) != 0))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "path_collectable(): WARNING: Command `%s' is SetXID!\n", execfile);
|
||
|
#if 0
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_RISKYFOLLOW, "setxid");
|
||
|
#endif
|
||
|
return 1; /* follow, though collection may be unreliable */
|
||
|
}
|
||
|
if (!check_dynamic (execfile))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "path_collectable(%s) WARNING/ERROR: not dynamic, not collectng!\n", execfile);
|
||
|
return 0; /* don't follow, collection will fail unpredictably */
|
||
|
}
|
||
|
TprintfT (DBG_LT2, "path_collectable(%s) OK!\n", execfile);
|
||
|
return 1; /* OK to follow */
|
||
|
}
|
||
|
|
||
|
static char *
|
||
|
build_experiment_path (char * instring, size_t instring_sz, const char *lineage_str)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "build_experiment_path(,%ld, %s)\n",
|
||
|
(long) instring_sz, lineage_str);
|
||
|
const char *p = CALL_UTIL (strstr)(linetrace_exp_dir_name, DESCENDANT_EXPT_KEY);
|
||
|
int basedir_sz;
|
||
|
if (p)
|
||
|
basedir_sz = p - linetrace_exp_dir_name + 4; /* +3 because of DESCENDANT_EXPT_KEY */
|
||
|
else
|
||
|
basedir_sz = __collector_strlen (linetrace_exp_dir_name) + 1;
|
||
|
int additional_sz = __collector_strlen (lineage_str) + 4;
|
||
|
if (basedir_sz + additional_sz > instring_sz)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "build_experiment_path(%s,%s): ERROR: path too long: %d > %ld\n",
|
||
|
linetrace_exp_dir_name, lineage_str,
|
||
|
basedir_sz + additional_sz, (long) instring_sz);
|
||
|
*instring = 0;
|
||
|
return NULL;
|
||
|
}
|
||
|
__collector_strlcpy (instring, linetrace_exp_dir_name, basedir_sz);
|
||
|
size_t slen = __collector_strlen (instring);
|
||
|
CALL_UTIL (snprintf)(instring + slen, instring_sz - slen, "/%s.er", lineage_str);
|
||
|
assert (__collector_strlen (instring) + 1 == basedir_sz + additional_sz);
|
||
|
return instring;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
check_reuid_change (uid_t ruid, uid_t euid)
|
||
|
{
|
||
|
uid_t curr_ruid = getuid ();
|
||
|
uid_t curr_euid = geteuid ();
|
||
|
mode_t curr_umask = umask (0);
|
||
|
umask (curr_umask); /* restore original umask */
|
||
|
int W_oth = !(curr_umask & S_IWOTH);
|
||
|
TprintfT (DBG_LT0, "check_reuid_change(%d,%d): umask=%03o\n", ruid, euid, curr_umask);
|
||
|
TprintfT (DBG_LT0, "check_reuid_change(): umask W usr=%d grp=%d oth=%d\n",
|
||
|
(int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth);
|
||
|
if (ruid != -1)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_ruid, ruid);
|
||
|
if ((curr_euid == 0) && (ruid != 0) && !W_oth)
|
||
|
{
|
||
|
/* changing to non-root ID, with umask blocking writes by other */
|
||
|
TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after ruid change (%d->%d)\n",
|
||
|
curr_ruid, ruid);
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o ruid %d->%d</event>\n",
|
||
|
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_ruid, ruid);
|
||
|
}
|
||
|
}
|
||
|
if (euid != -1)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_reuid_change(%d->%d)\n", curr_euid, euid);
|
||
|
if ((curr_euid == 0) && (euid != 0) && !W_oth)
|
||
|
{
|
||
|
/* changing to non-root ID, with umask blocking writes by other */
|
||
|
TprintfT (DBG_LT0, "check_reuid_change(): ERROR/WARNING: umask blocks write other after euid change (%d->%d)\n",
|
||
|
curr_euid, euid);
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o euid %d->%d</event>\n",
|
||
|
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_euid, euid);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
check_regid_change (gid_t rgid, gid_t egid)
|
||
|
{
|
||
|
gid_t curr_rgid = getgid ();
|
||
|
gid_t curr_egid = getegid ();
|
||
|
uid_t curr_euid = geteuid ();
|
||
|
mode_t curr_umask = umask (0);
|
||
|
umask (curr_umask); /* restore original umask */
|
||
|
int W_oth = !(curr_umask & S_IWOTH);
|
||
|
TprintfT (DBG_LT0, "check_regid_change(%d,%d): umask=%03o euid=%d\n",
|
||
|
rgid, egid, curr_umask, curr_euid);
|
||
|
TprintfT (DBG_LT0, "umask W usr=%d grp=%d oth=%d\n",
|
||
|
(int) (!(curr_umask & S_IWUSR)), (int) (!(curr_umask & S_IWGRP)), W_oth);
|
||
|
if (rgid != -1)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_regid_change(%d->%d)\n", curr_rgid, rgid);
|
||
|
if ((curr_euid == 0) && (rgid != 0) && !W_oth)
|
||
|
{
|
||
|
/* changing to non-root ID, with umask blocking writes by other */
|
||
|
TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after rgid change (%d->%d)\n",
|
||
|
curr_rgid, rgid);
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o rgid %d->%d</event>\n",
|
||
|
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_rgid, rgid);
|
||
|
}
|
||
|
}
|
||
|
if (egid != -1)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "check_regid_change(): check_egid_change(%d->%d)\n", curr_egid, egid);
|
||
|
if ((curr_euid == 0) && (egid != 0) && !W_oth)
|
||
|
{
|
||
|
/* changing to non-root ID, with umask blocking writes by other */
|
||
|
TprintfT (DBG_LT0, "check_regid_change(): WARNING/ERROR: umask blocks write other after egid change (%d->%d)\n",
|
||
|
curr_egid, egid);
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">umask %03o egid %d->%d</event>\n",
|
||
|
SP_JCMD_CWARN, COL_WARN_IDCHNG, curr_umask, curr_egid, egid);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
init_lineage_intf ()
|
||
|
{
|
||
|
void *dlflag;
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf()\n");
|
||
|
|
||
|
static int nesting_check = 0;
|
||
|
if (nesting_check >= 2)
|
||
|
{
|
||
|
/* segv before stack blows up */
|
||
|
nesting_check /= (nesting_check - 2);
|
||
|
}
|
||
|
nesting_check++;
|
||
|
|
||
|
__real_fork = dlsym (RTLD_NEXT, "fork");
|
||
|
if (__real_fork == NULL)
|
||
|
{
|
||
|
__real_fork = dlsym (RTLD_DEFAULT, "fork");
|
||
|
if (__real_fork == NULL)
|
||
|
return 1;
|
||
|
dlflag = RTLD_DEFAULT;
|
||
|
}
|
||
|
else
|
||
|
dlflag = RTLD_NEXT;
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() using RTLD_%s\n",
|
||
|
dlflag == RTLD_DEFAULT ? "DEFAULT" : "NEXT");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_fork\n", __real_fork);
|
||
|
__real_vfork = dlsym (dlflag, "vfork");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_vfork\n", __real_vfork);
|
||
|
__real_execve = dlsym (dlflag, "execve");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execve\n", __real_execve);
|
||
|
__real_execvp = dlsym (dlflag, "execvp");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execvp\n", __real_execvp);
|
||
|
__real_execv = dlsym (dlflag, "execv");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execv\n", __real_execv);
|
||
|
__real_execle = dlsym (dlflag, "execle");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execle\n", __real_execle);
|
||
|
__real_execlp = dlsym (dlflag, "execlp");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execlp\n", __real_execlp);
|
||
|
__real_execl = dlsym (dlflag, "execl");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_execl\n", __real_execl);
|
||
|
__real_clone = dlsym (dlflag, "clone");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_clone\n", __real_clone);
|
||
|
__real_posix_spawn = dlsym (dlflag, "posix_spawn");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawn\n",
|
||
|
__real_posix_spawn);
|
||
|
__real_posix_spawnp = dlsym (dlflag, "posix_spawnp");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_posix_spawnp\n",
|
||
|
__real_posix_spawnp);
|
||
|
__real_popen = dlvsym (dlflag, "popen", SYS_POPEN_VERSION);
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf()[%s] @0x%p __real_popen\n",
|
||
|
SYS_POPEN_VERSION, __real_popen);
|
||
|
#if ARCH(Intel)
|
||
|
__real_posix_spawn_2_15 = dlvsym (dlflag, "posix_spawn", SYS_POSIX_SPAWN_VERSION);
|
||
|
__real_posix_spawnp_2_15 = dlvsym (dlflag, "posix_spawnp", SYS_POSIX_SPAWN_VERSION);
|
||
|
#if WSIZE(32)
|
||
|
__real_popen_2_1 = __real_popen;
|
||
|
__real_popen_2_0 = dlvsym (dlflag, "popen", "GLIBC_2.0");
|
||
|
__real_posix_spawn_2_2 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2");
|
||
|
__real_posix_spawnp_2_2 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2");
|
||
|
#elif WSIZE(64)
|
||
|
__real_posix_spawn_2_2_5 = dlvsym (dlflag, "posix_spawn", "GLIBC_2.2.5");
|
||
|
__real_posix_spawnp_2_2_5 = dlvsym (dlflag, "posix_spawnp", "GLIBC_2.2.5");
|
||
|
#endif /* WSIZE() */
|
||
|
#endif /* ARCH(Intel) */
|
||
|
__real_grantpt = dlsym (dlflag, "grantpt");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_grantpt\n", __real_grantpt);
|
||
|
__real_ptsname = dlsym (dlflag, "ptsname");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_ptsname\n", __real_ptsname);
|
||
|
__real_system = dlsym (dlflag, "system");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_system\n", __real_system);
|
||
|
__real_setuid = dlsym (dlflag, "setuid");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setuid\n", __real_setuid);
|
||
|
__real_seteuid = dlsym (dlflag, "seteuid");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_seteuid\n", __real_seteuid);
|
||
|
__real_setreuid = dlsym (dlflag, "setreuid");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setreuid\n", __real_setreuid);
|
||
|
__real_setgid = dlsym (dlflag, "setgid");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setgid\n", __real_setgid);
|
||
|
__real_setegid = dlsym (dlflag, "setegid");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setegid\n", __real_setegid);
|
||
|
__real_setregid = dlsym (dlflag, "setregid");
|
||
|
TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_setregid\n", __real_setregid);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------------------ */
|
||
|
/* Note: The following _prologue and _epilogue functions used to be dbx-visible.
|
||
|
|
||
|
They are used to appropriately manage lineage-changing events, by
|
||
|
quiescing and re-enabling/re-setting experiment collection before and after,
|
||
|
and logging the lineage-change in the process/experiment undertaking it.
|
||
|
As shown by the interposition functions for fork, exec, etc., which follow,
|
||
|
the _prologue should be called immediately prior (such as a breakpoint
|
||
|
action defined at function entry) and the _epilogue called immediately
|
||
|
after (such as a breakpoint action defined at function return).
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
Notes on MT from Solaris 10 man pthread_atfork:
|
||
|
|
||
|
All multithreaded applications that call fork() in a POSIX
|
||
|
threads program and do more than simply call exec(2) in the
|
||
|
child of the fork need to ensure that the child is protected
|
||
|
from deadlock.
|
||
|
|
||
|
Since the "fork-one" model results in duplicating only the
|
||
|
thread that called fork(), it is possible that at the time
|
||
|
of the call another thread in the parent owns a lock. This
|
||
|
thread is not duplicated in the child, so no thread will
|
||
|
unlock this lock in the child. Deadlock occurs if the sin-
|
||
|
gle thread in the child needs this lock.
|
||
|
|
||
|
The problem is more serious with locks in libraries. Since
|
||
|
a library writer does not know if the application using the
|
||
|
library calls fork(), the library must protect itself from
|
||
|
such a deadlock scenario. If the application that links
|
||
|
with this library calls fork() and does not call exec() in
|
||
|
the child, and if it needs a library lock that may be held
|
||
|
by some other thread in the parent that is inside the
|
||
|
library at the time of the fork, the application deadlocks
|
||
|
inside the library.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
linetrace_ext_fork_prologue (const char *variant, char * n_lineage, int *following_fork)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n",
|
||
|
variant, n_lineage, *following_fork);
|
||
|
__collector_env_print ("fork_prologue start");
|
||
|
if (dbg_current_mode != FOLLOW_NONE)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_prologue(%s) ERROR: dbg_current_mode=%d, changing to FOLLOW_FORK!\n",
|
||
|
variant, dbg_current_mode);
|
||
|
dbg_current_mode = FOLLOW_ON;
|
||
|
if (__collector_strncmp ((char *) variant, "clone", sizeof ("clone") - 1) == 0)
|
||
|
{
|
||
|
__collector_mutex_lock (&clone_lineage_lock);
|
||
|
CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_C%d", curr_lineage, ++clone_linenum);
|
||
|
__collector_mutex_unlock (&clone_lineage_lock);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
__collector_mutex_lock (&fork_lineage_lock);
|
||
|
CALL_UTIL (snprintf)(n_lineage, LT_MAXNAMELEN, "%s_f%d", curr_lineage, ++fork_linenum);
|
||
|
__collector_mutex_unlock (&fork_lineage_lock);
|
||
|
}
|
||
|
*following_fork = check_follow_fork ();
|
||
|
|
||
|
/* write message before suspending, or it won't be written */
|
||
|
hrtime_t ts = GETRELTIME ();
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_prologue; variant=%s; new_lineage=%s; follow=%d\n",
|
||
|
variant, n_lineage, *following_fork);
|
||
|
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\"/>\n",
|
||
|
SP_JCMD_DESC_START,
|
||
|
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
|
||
|
variant, n_lineage, *following_fork);
|
||
|
__collector_ext_dispatcher_thread_timer_suspend ();
|
||
|
__collector_ext_hwc_lwp_suspend ();
|
||
|
__collector_env_print ("fork_prologue end");
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
linetrace_ext_fork_epilogue (const char *variant, const pid_t ret, char * n_lineage, int *following_fork)
|
||
|
{
|
||
|
if (dbg_current_mode == FOLLOW_NONE)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) ERROR: dbg_current_mode=%d!\n",
|
||
|
variant, dbg_current_mode);
|
||
|
/* compute descendant experiment name */
|
||
|
char new_exp_name[LT_MAXPATHLEN];
|
||
|
/* save exp_name to global var */
|
||
|
if (!build_experiment_path (new_exp_name, sizeof (new_exp_name), n_lineage))
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s): ERROR SP_COLLECTOR_EXPNAME not set\n", n_lineage);
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s):%d() returned %d %s; child experiment name = %s\n",
|
||
|
variant, *following_fork, ret, (ret ? "parent" : "child"), new_exp_name);
|
||
|
if (ret == 0)
|
||
|
{
|
||
|
/* *************************************child */
|
||
|
__collector_env_print ("fork_epilogue child at start");
|
||
|
/* start a new line */
|
||
|
fork_linenum = 0;
|
||
|
__collector_mutex_init (&fork_lineage_lock);
|
||
|
clone_linenum = 0;
|
||
|
__collector_mutex_init (&clone_lineage_lock);
|
||
|
__collector_env_update (NULL);
|
||
|
__collector_env_print ("fork_epilogue child after env_update");
|
||
|
__collector_clean_state ();
|
||
|
__collector_env_print ("fork_epilogue child after clean_slate");
|
||
|
__collector_line_cleanup ();
|
||
|
__collector_env_print ("fork_epilogue child after line_cleanup");
|
||
|
if (*following_fork)
|
||
|
{
|
||
|
/* stop recording this experiment, but preserve env vars */
|
||
|
linetrace_dormant ();
|
||
|
__collector_env_print ("fork_epilogue child after linetrace_dormant");
|
||
|
|
||
|
//static char exp_name_env[LT_MAXPATHLEN];
|
||
|
char * exp_name_env = CALL_UTIL (calloc)(LT_MAXPATHLEN, 1);
|
||
|
CALL_UTIL (snprintf)(exp_name_env, LT_MAXPATHLEN, "%s=%s", SP_COLLECTOR_EXPNAME, new_exp_name);
|
||
|
CALL_UTIL (putenv)(exp_name_env);
|
||
|
|
||
|
const char *params = CALL_UTIL (getenv)(SP_COLLECTOR_PARAMS);
|
||
|
int ret;
|
||
|
if (new_exp_name == NULL)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n",
|
||
|
SP_COLLECTOR_EXPNAME);
|
||
|
else if (params == NULL)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: getenv(%s) undefined -- new expt aborted!\n",
|
||
|
SP_COLLECTOR_PARAMS);
|
||
|
else if ((ret = __collector_open_experiment (new_exp_name, params, SP_ORIGIN_FORK)))
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: ERROR: '%s' open failed, ret=%d\n",
|
||
|
new_exp_name, ret);
|
||
|
else
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue: opened(%s)\n", new_exp_name);
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) returning to *child*\n", variant);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* disable current and further linetrace experiment resumption */
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_fork_epilogue(%s) child calling line_close\n", variant);
|
||
|
__collector_ext_line_close ();
|
||
|
}
|
||
|
__collector_env_print ("fork_epilogue child at end");
|
||
|
/* *************************************end child */
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* *************************************parent */
|
||
|
__collector_env_print ("fork_epilogue parent at start");
|
||
|
__collector_ext_dispatcher_thread_timer_resume ();
|
||
|
__collector_ext_hwc_lwp_resume ();
|
||
|
hrtime_t ts = GETRELTIME ();
|
||
|
char msg[256 + LT_MAXPATHLEN];
|
||
|
if (ret >= 0)
|
||
|
CALL_UTIL (snprintf)(msg, sizeof (msg), "pid=%d", ret);
|
||
|
else
|
||
|
{
|
||
|
/* delete stillborn experiment? */
|
||
|
char errmsg[256];
|
||
|
strerror_r (errno, errmsg, sizeof (errmsg));
|
||
|
CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg);
|
||
|
}
|
||
|
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
|
||
|
SP_JCMD_DESC_STARTED,
|
||
|
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
|
||
|
variant, n_lineage, *following_fork, msg);
|
||
|
/* environment remains set for collection */
|
||
|
__collector_env_print ("fork_epilogue parent at end");
|
||
|
/* *************************************end parent */
|
||
|
}
|
||
|
dbg_current_mode = FOLLOW_NONE;
|
||
|
*following_fork = 0;
|
||
|
}
|
||
|
|
||
|
static char**
|
||
|
linetrace_ext_exec_prologue_end (const char *variant, const char* cmd_string,
|
||
|
char *const envp[], int following_exec)
|
||
|
{
|
||
|
char **coll_env;
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_exec_prologue_end; variant=%s; cmd_string=%s; follow=%d\n",
|
||
|
variant, cmd_string, following_exec);
|
||
|
/* write message before suspending, or it won't be written */
|
||
|
hrtime_t ts = GETRELTIME ();
|
||
|
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
|
||
|
SP_JCMD_EXEC_START,
|
||
|
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
|
||
|
variant, new_lineage, following_exec, cmd_string);
|
||
|
if (following_exec)
|
||
|
{
|
||
|
coll_env = __collector_env_allocate (envp, 0);
|
||
|
__collector_env_update (coll_env);
|
||
|
extern char **environ; /* the process' actual environment */
|
||
|
if (environ == envp) /* user selected process environment */
|
||
|
environ = coll_env;
|
||
|
}
|
||
|
else
|
||
|
coll_env = (char**) envp;
|
||
|
__collector_env_printall ("linetrace_ext_exec_prologue_end", coll_env);
|
||
|
if (!CALL_UTIL (strstr)(variant, "posix_spawn"))
|
||
|
{
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
|
||
|
__collector_suspend_experiment ("suspend_for_exec");
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
}
|
||
|
if (CALL_UTIL (strstr)(variant, "posix_spawn"))
|
||
|
{
|
||
|
__collector_ext_dispatcher_thread_timer_suspend ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
|
||
|
__collector_ext_hwc_lwp_suspend ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
}
|
||
|
return (coll_env);
|
||
|
}
|
||
|
|
||
|
static char**
|
||
|
linetrace_ext_exec_prologue (const char *variant,
|
||
|
const char* path, char *const argv[],
|
||
|
char *const envp[], int *following_exec)
|
||
|
{
|
||
|
char cmd_string[_POSIX_ARG_MAX] = {'\0'};
|
||
|
|
||
|
if (dbg_current_mode != FOLLOW_NONE)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_exec_prologue() ERROR: dbg_current_mode=%d, changing to FOLLOW_EXEC!\n", dbg_current_mode);
|
||
|
dbg_current_mode = FOLLOW_ON;
|
||
|
*following_exec = check_follow_exec (path);
|
||
|
if (path != NULL)
|
||
|
{
|
||
|
/* escape any newline, " or \ characters in the exec command */
|
||
|
TprintfT (DBG_LT3, "linetrace_ext_exec_prologue(): arg0=%s\n", path);
|
||
|
/* leave space in log message for terminator (and header) */
|
||
|
__collector_strlcpy (cmd_string, path, sizeof (cmd_string));
|
||
|
size_t len;
|
||
|
unsigned argn = 0;
|
||
|
if (argv[0])
|
||
|
{
|
||
|
char *p;
|
||
|
while (((p = argv[++argn]) != 0) &&
|
||
|
(len = __collector_strlen (cmd_string)) < sizeof (cmd_string) - 2)
|
||
|
{
|
||
|
cmd_string[len++] = ' ';
|
||
|
__collector_strlcpy (cmd_string + len, p, sizeof (cmd_string) - len);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_exec_prologue(%s), lineage=%s, follow=%d, prog=%s, path=%s \n",
|
||
|
variant, new_lineage, *following_exec, cmd_string, path);
|
||
|
return linetrace_ext_exec_prologue_end (variant, cmd_string, envp, *following_exec);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
linetrace_ext_exec_epilogue (const char *variant, char *const envp[], const int ret, int *following_exec)
|
||
|
{
|
||
|
/* For exec, this routine is only entered if the exec failed */
|
||
|
/* However, posix_spawn() is expected to return */
|
||
|
if (dbg_current_mode == FOLLOW_NONE)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: dbg_current_mode=%d!\n", dbg_current_mode);
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue(%s):%d returned: %d, errno=%d\n",
|
||
|
variant, *following_exec, ret, errno);
|
||
|
if (!CALL_UTIL (strstr)(variant, "posix_spawn"))
|
||
|
{
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
|
||
|
__collector_resume_experiment ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
}
|
||
|
if (CALL_UTIL (strstr)(variant, "posix_spawn"))
|
||
|
{
|
||
|
__collector_ext_dispatcher_thread_timer_resume ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
|
||
|
__collector_ext_hwc_lwp_resume ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
}
|
||
|
hrtime_t ts = GETRELTIME ();
|
||
|
char msg[256];
|
||
|
if (ret)
|
||
|
{
|
||
|
char errmsg[256];
|
||
|
strerror_r (errno, errmsg, sizeof (errmsg));
|
||
|
CALL_UTIL (snprintf)(msg, sizeof (msg), "err %s", errmsg);
|
||
|
}
|
||
|
else
|
||
|
CALL_UTIL (snprintf)(msg, sizeof (msg), "rc=%d", ret);
|
||
|
if (!CALL_UTIL (strstr)(variant, "posix_spawn"))
|
||
|
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
|
||
|
SP_JCMD_EXEC_ERROR,
|
||
|
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
|
||
|
variant, new_lineage, *following_exec, msg);
|
||
|
if (envp == NULL)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_exec_epilogue() ERROR: envp NULL after %s!\n", variant);
|
||
|
dbg_current_mode = FOLLOW_NONE;
|
||
|
*following_exec = 0;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
linetrace_ext_combo_prologue (const char *variant, const char *cmd, int *following_combo)
|
||
|
{
|
||
|
char cmd_string[_POSIX_ARG_MAX] = {'\0'};
|
||
|
char execfile[_POSIX_ARG_MAX] = {'\0'};
|
||
|
|
||
|
if (dbg_current_mode != FOLLOW_NONE)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_combo_prologue() ERROR: dbg_current_mode=%d! changing to FOLLOW_ON\n",
|
||
|
dbg_current_mode);
|
||
|
dbg_current_mode = FOLLOW_ON;
|
||
|
if (cmd != NULL)
|
||
|
{
|
||
|
/* extract executable name from combo command */
|
||
|
unsigned len = strcspn (cmd, " ");
|
||
|
__collector_strlcpy (execfile, cmd, len + 1);
|
||
|
|
||
|
/* escape any newline, " or \ characters in the combo command */
|
||
|
/* leave space in log message for terminator (and header) */
|
||
|
__collector_strlcpy (cmd_string, cmd, sizeof (cmd_string));
|
||
|
}
|
||
|
|
||
|
*following_combo = check_follow_combo (execfile);
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(%s) follow=%d, prog=%s\n\n",
|
||
|
variant, *following_combo, cmd_string);
|
||
|
|
||
|
/* Construct the lineage string for the new image */
|
||
|
new_lineage[0] = 0;
|
||
|
__collector_strcat (new_lineage, "XXX");
|
||
|
|
||
|
/* write message before suspending, or it won't be written */
|
||
|
hrtime_t ts = GETRELTIME ();
|
||
|
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" lineage=\"%s\" follow=\"%d\" msg=\"%s\"/>\n",
|
||
|
SP_JCMD_DESC_START,
|
||
|
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
|
||
|
variant, new_lineage, *following_combo, cmd_string);
|
||
|
if (*following_combo)
|
||
|
{
|
||
|
__collector_env_update (NULL);
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_combo_prologue(): Following %s(\"%s\")\n", variant, execfile);
|
||
|
}
|
||
|
__collector_ext_dispatcher_thread_timer_suspend ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
|
||
|
__collector_ext_hwc_lwp_suspend ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
linetrace_ext_combo_epilogue (const char *variant, const int ret, int *following_combo)
|
||
|
{
|
||
|
if (dbg_current_mode == FOLLOW_NONE)
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue() ERROR: dbg_current_mode=FOLLOW_NONE\n");
|
||
|
TprintfT (DBG_LT0, "linetrace_ext_combo_epilogue(%s):%d() returned %d\n",
|
||
|
variant, *following_combo, ret);
|
||
|
__collector_ext_dispatcher_thread_timer_resume ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 1;
|
||
|
__collector_ext_hwc_lwp_resume ();
|
||
|
__collector_linetrace_shutdown_hwcs_6830763_XXXX = 0;
|
||
|
hrtime_t ts = GETRELTIME ();
|
||
|
__collector_log_write ("<event kind=\"%s\" tstamp=\"%u.%09u\" variant=\"%s\" follow=\"%d\" msg=\"rc=%d\"/>\n",
|
||
|
SP_JCMD_DESC_STARTED,
|
||
|
(unsigned) (ts / NANOSEC), (unsigned) (ts % NANOSEC),
|
||
|
variant, *following_combo, ret);
|
||
|
|
||
|
dbg_current_mode = FOLLOW_NONE;
|
||
|
*following_combo = 0;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- fork */
|
||
|
pid_t fork () __attribute__ ((weak, alias ("__collector_fork")));
|
||
|
pid_t _fork () __attribute__ ((weak, alias ("__collector_fork")));
|
||
|
|
||
|
pid_t
|
||
|
__collector_fork (void)
|
||
|
{
|
||
|
pid_t ret;
|
||
|
if (NULL_PTR (fork))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_fork() calling init_lineage_intf()\n");
|
||
|
init_lineage_intf ();
|
||
|
}
|
||
|
__collector_env_print ("__collector_fork start");
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
TprintfT (DBG_LT0, "__collector_fork() interposition: line_mode=%d combo=%d\n",
|
||
|
line_mode, combo_flag);
|
||
|
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_fork() not following, returning CALL_REAL(fork)()\n");
|
||
|
return CALL_REAL (fork)();
|
||
|
}
|
||
|
int following_fork = 0;
|
||
|
linetrace_ext_fork_prologue ("fork", new_lineage, &following_fork);
|
||
|
|
||
|
/* since libpthread/fork ends up calling fork1, it's a combo */
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
ret = CALL_REAL (fork)();
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_fork_epilogue ("fork", ret, new_lineage, &following_fork);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- vfork */
|
||
|
/* vfork interposition in the usual sense is not possible, since vfork(2)
|
||
|
relies on specifics of the stack frames in the parent and child which
|
||
|
only work when the child's exec (or _exit) are in the same stack frame
|
||
|
as the vfork: this isn't the case when there's interposition on exec.
|
||
|
As a workaround, the interposing vfork calls fork1 instead of the real
|
||
|
vfork. Note that fork1 is somewhat less efficient than vfork, and requires
|
||
|
additional memory, which may result in a change of application behaviour
|
||
|
when libcollector is loaded (even when collection is not active),
|
||
|
affecting not only direct use of vfork by the subject application,
|
||
|
but also indirect use through system, popen, and the other combos.
|
||
|
*/
|
||
|
pid_t vfork () __attribute__ ((weak, alias ("__collector_vfork")));
|
||
|
pid_t _vfork () __attribute__ ((weak, alias ("__collector_vfork")));
|
||
|
|
||
|
pid_t
|
||
|
__collector_vfork (void)
|
||
|
{
|
||
|
if (NULL_PTR (vfork))
|
||
|
init_lineage_intf ();
|
||
|
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
|
||
|
TprintfT (DBG_LT0, "__collector_vfork() interposing: line_mode=%d combo=%d\n",
|
||
|
line_mode, combo_flag);
|
||
|
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
|
||
|
return CALL_REAL (fork)();
|
||
|
|
||
|
/* this warning is also appropriate for combos which use vfork,
|
||
|
however, let's assume this is achieved elsewhere */
|
||
|
(void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", SP_JCMD_CWARN,
|
||
|
COL_WARN_VFORK, "fork");
|
||
|
|
||
|
char new_lineage[LT_MAXNAMELEN];
|
||
|
new_lineage[0] = 0;
|
||
|
int following_fork = 0;
|
||
|
linetrace_ext_fork_prologue ("vfork", new_lineage, &following_fork);
|
||
|
|
||
|
pid_t ret = CALL_REAL (fork)();
|
||
|
linetrace_ext_fork_epilogue ("vfork", ret, new_lineage, &following_fork);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- execve */
|
||
|
int execve () __attribute__ ((weak, alias ("__collector_execve")));
|
||
|
|
||
|
int
|
||
|
__collector_execve (const char* path, char *const argv[], char *const envp[])
|
||
|
{
|
||
|
static char **coll_env = NULL; /* environment for collection */
|
||
|
if (NULL_PTR (execve))
|
||
|
init_lineage_intf ();
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_execve(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
path ? path : "NULL",
|
||
|
argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
|
||
|
envp ? (envp[0] ? envp[0] : "NULL") : "NULL",
|
||
|
line_mode, combo_flag);
|
||
|
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
|
||
|
__collector_env_unset ((char**) envp);
|
||
|
if (line_mode != LM_TRACK_LINEAGE || combo_flag)
|
||
|
return CALL_REAL (execve)(path, argv, envp);
|
||
|
|
||
|
int following_exec = 0;
|
||
|
coll_env = linetrace_ext_exec_prologue ("execve", path, argv, envp, &following_exec);
|
||
|
TprintfT (DBG_LT2, "__collector_execve(): coll_env=0x%p\n", coll_env);
|
||
|
__collector_env_printall ("__collector_execve", coll_env);
|
||
|
int ret = CALL_REAL (execve)(path, argv, coll_env);
|
||
|
linetrace_ext_exec_epilogue ("execve", envp, ret, &following_exec);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int execvp () __attribute__ ((weak, alias ("__collector_execvp")));
|
||
|
|
||
|
int
|
||
|
__collector_execvp (const char* file, char *const argv[])
|
||
|
{
|
||
|
extern char **environ; /* the process' actual environment */
|
||
|
char ** envp = environ;
|
||
|
if (NULL_PTR (execvp))
|
||
|
init_lineage_intf ();
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_execvp(file=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
file ? file : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
|
||
|
line_mode, combo_flag);
|
||
|
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
|
||
|
__collector_env_unset ((char**) envp);
|
||
|
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
|
||
|
return CALL_REAL (execvp)(file, argv);
|
||
|
|
||
|
int following_exec = 0;
|
||
|
#ifdef DEBUG
|
||
|
char **coll_env = /* environment for collection */
|
||
|
#endif /* DEBUG */
|
||
|
linetrace_ext_exec_prologue ("execvp", file, argv, envp, &following_exec);
|
||
|
TprintfT (DBG_LT0, "__collector_execvp(): coll_env=0x%p\n", coll_env);
|
||
|
|
||
|
int ret = CALL_REAL (execvp)(file, argv);
|
||
|
linetrace_ext_exec_epilogue ("execvp", envp, ret, &following_exec);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int execv () __attribute__ ((weak, alias ("__collector_execv")));
|
||
|
|
||
|
int
|
||
|
__collector_execv (const char* path, char *const argv[])
|
||
|
{
|
||
|
int ret;
|
||
|
extern char **environ; /* the process' actual environment */
|
||
|
char ** envp = environ;
|
||
|
TprintfT (DBG_LT0, "__collector_execv(path=%s, argv[0]=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
|
||
|
line_mode, get_combo_flag ());
|
||
|
|
||
|
ret = __collector_execve (path, argv, envp);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int execle (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execle")));
|
||
|
|
||
|
int
|
||
|
__collector_execle (const char* path, const char *arg0, ...)
|
||
|
{
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_execle(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
path ? path : "NULL", arg0 ? arg0 : "NULL",
|
||
|
line_mode, get_combo_flag ());
|
||
|
|
||
|
char **argp;
|
||
|
va_list args;
|
||
|
char **argvec;
|
||
|
register char **environmentp;
|
||
|
int nargs = 0;
|
||
|
char *nextarg;
|
||
|
|
||
|
va_start (args, arg0);
|
||
|
while (va_arg (args, char *) != (char *) 0)
|
||
|
nargs++;
|
||
|
|
||
|
/*
|
||
|
* save the environment pointer, which is at the end of the
|
||
|
* variable argument list
|
||
|
*/
|
||
|
environmentp = va_arg (args, char **);
|
||
|
va_end (args);
|
||
|
|
||
|
/*
|
||
|
* load the arguments in the variable argument list
|
||
|
* into the argument vector, and add the terminating null pointer
|
||
|
*/
|
||
|
va_start (args, arg0);
|
||
|
/* workaround for bugid 1242839 */
|
||
|
argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *)));
|
||
|
argp = argvec;
|
||
|
*argp++ = (char *) arg0;
|
||
|
while ((nextarg = va_arg (args, char *)) != (char *) 0)
|
||
|
*argp++ = nextarg;
|
||
|
va_end (args);
|
||
|
*argp = (char *) 0;
|
||
|
return __collector_execve (path, argvec, environmentp);
|
||
|
}
|
||
|
|
||
|
int execlp (const char* file, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execlp")));
|
||
|
|
||
|
int
|
||
|
__collector_execlp (const char* file, const char *arg0, ...)
|
||
|
{
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_execlp(file=%s, arg0=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
file ? file : "NULL", arg0 ? arg0 : "NULL",
|
||
|
line_mode, get_combo_flag ());
|
||
|
char **argp;
|
||
|
va_list args;
|
||
|
char **argvec;
|
||
|
int nargs = 0;
|
||
|
char *nextarg;
|
||
|
|
||
|
va_start (args, arg0);
|
||
|
while (va_arg (args, char *) != (char *) 0)
|
||
|
nargs++;
|
||
|
va_end (args);
|
||
|
|
||
|
/*
|
||
|
* load the arguments in the variable argument list
|
||
|
* into the argument vector and add the terminating null pointer
|
||
|
*/
|
||
|
va_start (args, arg0);
|
||
|
|
||
|
/* workaround for bugid 1242839 */
|
||
|
argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *)));
|
||
|
argp = argvec;
|
||
|
*argp++ = (char *) arg0;
|
||
|
while ((nextarg = va_arg (args, char *)) != (char *) 0)
|
||
|
*argp++ = nextarg;
|
||
|
va_end (args);
|
||
|
*argp = (char *) 0;
|
||
|
return __collector_execvp (file, argvec);
|
||
|
}
|
||
|
|
||
|
int execl (const char* path, const char *arg0, ...) __attribute__ ((weak, alias ("__collector_execl")));
|
||
|
|
||
|
int
|
||
|
__collector_execl (const char* path, const char *arg0, ...)
|
||
|
{
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_execl(path=%s, arg0=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
path ? path : "NULL", arg0 ? arg0 : "NULL",
|
||
|
line_mode, get_combo_flag ());
|
||
|
char **argp;
|
||
|
va_list args;
|
||
|
char **argvec;
|
||
|
extern char **environ;
|
||
|
int nargs = 0;
|
||
|
char *nextarg;
|
||
|
va_start (args, arg0);
|
||
|
while (va_arg (args, char *) != (char *) 0)
|
||
|
nargs++;
|
||
|
va_end (args);
|
||
|
|
||
|
/*
|
||
|
* load the arguments in the variable argument list
|
||
|
* into the argument vector and add the terminating null pointer
|
||
|
*/
|
||
|
va_start (args, arg0);
|
||
|
|
||
|
/* workaround for bugid 1242839 */
|
||
|
argvec = (char **) alloca ((size_t) ((nargs + 2) * sizeof (char *)));
|
||
|
argp = argvec;
|
||
|
*argp++ = (char *) arg0;
|
||
|
while ((nextarg = va_arg (args, char *)) != (char *) 0)
|
||
|
*argp++ = nextarg;
|
||
|
va_end (args);
|
||
|
*argp = (char *) 0;
|
||
|
return __collector_execve (path, argvec, environ);
|
||
|
}
|
||
|
|
||
|
#include <spawn.h>
|
||
|
|
||
|
/*-------------------------------------------------------- posix_spawn */
|
||
|
#if ARCH(Intel)
|
||
|
// map interposed symbol versions
|
||
|
static int
|
||
|
__collector_posix_spawn_symver (int(real_posix_spawn) (),
|
||
|
pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[]);
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector_posix_spawn_2_15, posix_spawn@@GLIBC_2.15)
|
||
|
int
|
||
|
__collector_posix_spawn_2_15 (pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
|
||
|
CALL_REAL (posix_spawn_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
|
||
|
if (NULL_PTR (posix_spawn))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_15), pidp,
|
||
|
path, file_actions, attrp, argv, envp);
|
||
|
}
|
||
|
|
||
|
#if WSIZE(32)
|
||
|
SYMVER_ATTRIBUTE (__collector_posix_spawn_2_2, posix_spawn@GLIBC_2.2)
|
||
|
int
|
||
|
__collector_posix_spawn_2_2 (pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
|
||
|
CALL_REAL (posix_spawn_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
|
||
|
if (NULL_PTR (posix_spawn))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2), pidp,
|
||
|
path, file_actions, attrp, argv, envp);
|
||
|
}
|
||
|
|
||
|
#else /* ^WSIZE(32) */
|
||
|
SYMVER_ATTRIBUTE (__collector_posix_spawn_2_2_5, posix_spawn@GLIBC_2.2.5)
|
||
|
int
|
||
|
__collector_posix_spawn_2_2_5 (pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawn_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
|
||
|
CALL_REAL (posix_spawn_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
|
||
|
if (NULL_PTR (posix_spawn))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_posix_spawn_symver (CALL_REAL (posix_spawn_2_2_5), pidp,
|
||
|
path, file_actions, attrp, argv, envp);
|
||
|
}
|
||
|
#endif /* ^WSIZE(32) */
|
||
|
|
||
|
static int
|
||
|
__collector_posix_spawn_symver (int(real_posix_spawn) (),
|
||
|
#else /* ^ARCH(Intel) */
|
||
|
int
|
||
|
__collector_posix_spawn (
|
||
|
#endif /* ARCH() */
|
||
|
pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
int ret;
|
||
|
static char **coll_env = NULL; /* environment for collection */
|
||
|
if (NULL_PTR (posix_spawn))
|
||
|
init_lineage_intf ();
|
||
|
if (NULL_PTR (posix_spawn))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s) interposing: ERROR, posix_spawn() not found by dlsym\n",
|
||
|
path ? path : "NULL");
|
||
|
return -1; /* probably should set errno */
|
||
|
}
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
TprintfT (DBG_LT0, "__collector_posix_spawn(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag);
|
||
|
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
|
||
|
__collector_env_unset ((char**) envp);
|
||
|
|
||
|
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag)
|
||
|
{
|
||
|
#if ARCH(Intel)
|
||
|
return (real_posix_spawn) (pidp, path, file_actions, attrp, argv, envp);
|
||
|
#else
|
||
|
return CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, envp);
|
||
|
#endif
|
||
|
}
|
||
|
int following_exec = 0;
|
||
|
coll_env = linetrace_ext_exec_prologue ("posix_spawn", path, argv, envp, &following_exec);
|
||
|
TprintfT (DBG_LT0, "__collector_posix_spawn(): coll_env=0x%p\n", coll_env);
|
||
|
__collector_env_printall ("__collector_posix_spawn", coll_env);
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
#if ARCH(Intel)
|
||
|
ret = (real_posix_spawn) (pidp, path, file_actions, attrp, argv, coll_env);
|
||
|
#else
|
||
|
ret = CALL_REAL (posix_spawn)(pidp, path, file_actions, attrp, argv, coll_env);
|
||
|
#endif
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_exec_epilogue ("posix_spawn", envp, ret, &following_exec);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*-------------------------------------------------------- posix_spawnp */
|
||
|
#if ARCH(Intel)
|
||
|
// map interposed symbol versions
|
||
|
|
||
|
static int
|
||
|
__collector_posix_spawnp_symver (int(real_posix_spawnp) (), pid_t *pidp,
|
||
|
const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[]);
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_15, posix_spawnp@@GLIBC_2.15)
|
||
|
int // Common interposition
|
||
|
__collector_posix_spawnp_2_15 (pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_15@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
|
||
|
CALL_REAL (posix_spawnp_2_15), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
|
||
|
if (NULL_PTR (posix_spawnp))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_15), pidp,
|
||
|
path, file_actions, attrp, argv, envp);
|
||
|
}
|
||
|
|
||
|
#if WSIZE(32)
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_2, posix_spawnp@GLIBC_2.2)
|
||
|
int
|
||
|
__collector_posix_spawnp_2_2 (pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
|
||
|
CALL_REAL (posix_spawnp_2_2), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
|
||
|
if (NULL_PTR (posix_spawnp))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2), pidp,
|
||
|
path, file_actions, attrp, argv, envp);
|
||
|
}
|
||
|
|
||
|
#else /* ^WSIZE(32) */
|
||
|
SYMVER_ATTRIBUTE (__collector_posix_spawnp_2_2_5, posix_spawnp@GLIBC_2.2.5)
|
||
|
int
|
||
|
__collector_posix_spawnp_2_2_5 (pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[])
|
||
|
{
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_posix_spawnp_2_2_5@%p(path=%s, argv[0]=%s, env[0]=%s)\n",
|
||
|
CALL_REAL (posix_spawnp_2_2_5), path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL", envp ? (envp[0] ? envp[0] : "NULL") : "NULL");
|
||
|
if (NULL_PTR (posix_spawnp))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_posix_spawnp_symver (CALL_REAL (posix_spawnp_2_2_5), pidp,
|
||
|
path, file_actions, attrp, argv, envp);
|
||
|
}
|
||
|
|
||
|
#endif /* ^WSIZE(32) */
|
||
|
|
||
|
static int
|
||
|
__collector_posix_spawnp_symver (int(real_posix_spawnp) (),
|
||
|
#else /* ^ARCH(Intel) */
|
||
|
int
|
||
|
__collector_posix_spawnp (
|
||
|
#endif /* ARCH() */
|
||
|
pid_t *pidp, const char *path,
|
||
|
const posix_spawn_file_actions_t *file_actions,
|
||
|
const posix_spawnattr_t *attrp,
|
||
|
char *const argv[], char *const envp[]){
|
||
|
int ret;
|
||
|
static char **coll_env = NULL; /* environment for collection */
|
||
|
if (NULL_PTR (posix_spawnp))
|
||
|
init_lineage_intf ();
|
||
|
if (NULL_PTR (posix_spawnp))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s) interposing: ERROR, posix_spawnp() not found by dlsym\n",
|
||
|
path ? path : "NULL");
|
||
|
return -1; /* probably should set errno */
|
||
|
}
|
||
|
int * guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
TprintfT (DBG_LT0, "__collector_posix_spawnp(path=%s, argv[0]=%s, env[0]=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
path ? path : "NULL", argv ? (argv[0] ? argv[0] : "NULL") : "NULL",
|
||
|
envp ? (envp[0] ? envp[0] : "NULL") : "NULL", line_mode, combo_flag);
|
||
|
|
||
|
if (line_mode == LM_CLOSED) /* ensure collection environment is sanitised */
|
||
|
__collector_env_unset ((char**) envp);
|
||
|
if (line_mode != LM_TRACK_LINEAGE || combo_flag)
|
||
|
{
|
||
|
#if ARCH(Intel)
|
||
|
return (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, envp);
|
||
|
#else
|
||
|
return CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, envp);
|
||
|
#endif
|
||
|
}
|
||
|
int following_exec = 0;
|
||
|
coll_env = linetrace_ext_exec_prologue ("posix_spawnp", path, argv, envp, &following_exec);
|
||
|
TprintfT (DBG_LT0, "__collector_posix_spawnp(): coll_env=0x%p\n", coll_env);
|
||
|
__collector_env_printall ("__collector_posix_spawnp", coll_env);
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
#if ARCH(Intel)
|
||
|
ret = (real_posix_spawnp) (pidp, path, file_actions, attrp, argv, coll_env);
|
||
|
#else
|
||
|
ret = CALL_REAL (posix_spawnp)(pidp, path, file_actions, attrp, argv, coll_env);
|
||
|
#endif
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_exec_epilogue ("posix_spawnp", envp, ret, &following_exec);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- system */
|
||
|
int system () __attribute__ ((weak, alias ("__collector_system")));
|
||
|
|
||
|
int
|
||
|
__collector_system (const char *cmd)
|
||
|
{
|
||
|
if (NULL_PTR (system))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_system(cmd=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
cmd ? cmd : "NULL", line_mode, get_combo_flag ());
|
||
|
int *guard = NULL;
|
||
|
if (line_mode == LM_TRACK_LINEAGE)
|
||
|
INIT_REENTRANCE (guard);
|
||
|
if (guard == NULL)
|
||
|
return CALL_REAL (system)(cmd);
|
||
|
int following_combo = 0;
|
||
|
linetrace_ext_combo_prologue ("system", cmd, &following_combo);
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
int ret = CALL_REAL (system)(cmd);
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_combo_epilogue ("system", ret, &following_combo);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- popen */
|
||
|
// map interposed symbol versions
|
||
|
#if ARCH(Intel) && WSIZE(32)
|
||
|
static FILE *
|
||
|
__collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode);
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector_popen_2_1, popen@@GLIBC_2.1)
|
||
|
FILE *
|
||
|
__collector_popen_2_1 (const char *cmd, const char *mode)
|
||
|
{
|
||
|
if (NULL_PTR (popen))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_1@%p\n", CALL_REAL (popen_2_1));
|
||
|
return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode);
|
||
|
}
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector_popen_2_0, popen@GLIBC_2.0)
|
||
|
FILE *
|
||
|
__collector_popen_2_0 (const char *cmd, const char *mode)
|
||
|
{
|
||
|
if (NULL_PTR (popen))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector_popen_2_0@%p\n", CALL_REAL (popen_2_0));
|
||
|
return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode);
|
||
|
}
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector__popen_2_1, _popen@@GLIBC_2.1)
|
||
|
FILE *
|
||
|
__collector__popen_2_1 (const char *cmd, const char *mode)
|
||
|
{
|
||
|
if (NULL_PTR (popen))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LTT, "linetrace: GLIBC: __collector__popen_2_1@%p\n", CALL_REAL (popen_2_1));
|
||
|
return __collector_popen_symver (CALL_REALF (popen_2_1), cmd, mode);
|
||
|
}
|
||
|
|
||
|
SYMVER_ATTRIBUTE (__collector__popen_2_0, _popen@GLIBC_2.0)
|
||
|
FILE *
|
||
|
__collector__popen_2_0 (const char *cmd, const char *mode)
|
||
|
{
|
||
|
if (NULL_PTR (popen))
|
||
|
init_lineage_intf ();
|
||
|
return __collector_popen_symver (CALL_REALF (popen_2_0), cmd, mode);
|
||
|
}
|
||
|
#else // WSIZE(64)
|
||
|
FILE * popen () __attribute__ ((weak, alias ("__collector_popen")));
|
||
|
#endif
|
||
|
|
||
|
#if ARCH(Intel) && WSIZE(32)
|
||
|
static FILE *
|
||
|
__collector_popen_symver (FILE*(real_popen) (), const char *cmd, const char *mode)
|
||
|
#else
|
||
|
|
||
|
FILE *
|
||
|
__collector_popen (const char *cmd, const char *mode)
|
||
|
#endif
|
||
|
{
|
||
|
FILE *ret;
|
||
|
if (NULL_PTR (popen))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_popen(cmd=%s) interposing: line_mode=%d combo=%d\n",
|
||
|
cmd ? cmd : "NULL", line_mode, get_combo_flag ());
|
||
|
int *guard = NULL;
|
||
|
if (line_mode == LM_TRACK_LINEAGE)
|
||
|
INIT_REENTRANCE (guard);
|
||
|
if (guard == NULL)
|
||
|
{
|
||
|
#if ARCH(Intel) && WSIZE(32)
|
||
|
return (real_popen) (cmd, mode);
|
||
|
#else
|
||
|
return CALL_REALF (popen)(cmd, mode);
|
||
|
#endif
|
||
|
}
|
||
|
int following_combo = 0;
|
||
|
linetrace_ext_combo_prologue ("popen", cmd, &following_combo);
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
#if ARCH(Intel) && WSIZE(32)
|
||
|
ret = (real_popen) (cmd, mode);
|
||
|
#else
|
||
|
ret = CALL_REALF (popen)(cmd, mode);
|
||
|
#endif
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_combo_epilogue ("popen", (ret == NULL) ? (-1) : 0, &following_combo);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- grantpt */
|
||
|
int grantpt () __attribute__ ((weak, alias ("__collector_grantpt")));
|
||
|
|
||
|
int
|
||
|
__collector_grantpt (const int fildes)
|
||
|
{
|
||
|
if (NULL_PTR (grantpt))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_grantpt(%d) interposing: line_mode=%d combo=%d\n",
|
||
|
fildes, line_mode, get_combo_flag ());
|
||
|
int *guard = NULL;
|
||
|
if (line_mode == LM_TRACK_LINEAGE)
|
||
|
INIT_REENTRANCE (guard);
|
||
|
if (guard == NULL)
|
||
|
return CALL_REAL (grantpt)(fildes);
|
||
|
int following_combo = 0;
|
||
|
linetrace_ext_combo_prologue ("grantpt", "/usr/lib/pt_chmod", &following_combo);
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
int ret = CALL_REAL (grantpt)(fildes);
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_combo_epilogue ("grantpt", ret, &following_combo);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- ptsname */
|
||
|
char *ptsname () __attribute__ ((weak, alias ("__collector_ptsname")));
|
||
|
|
||
|
char *
|
||
|
__collector_ptsname (const int fildes)
|
||
|
{
|
||
|
if (NULL_PTR (ptsname))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0,
|
||
|
"__collector_ptsname(%d) interposing: line_mode=%d combo=%d\n",
|
||
|
fildes, line_mode, get_combo_flag ());
|
||
|
int *guard = NULL;
|
||
|
if (line_mode == LM_TRACK_LINEAGE)
|
||
|
INIT_REENTRANCE (guard);
|
||
|
if (guard == NULL)
|
||
|
return CALL_REALC (ptsname)(fildes);
|
||
|
int following_combo = 0;
|
||
|
linetrace_ext_combo_prologue ("ptsname", "/usr/lib/pt_chmod", &following_combo);
|
||
|
PUSH_REENTRANCE (guard);
|
||
|
char *ret = CALL_REALC (ptsname)(fildes);
|
||
|
POP_REENTRANCE (guard);
|
||
|
linetrace_ext_combo_epilogue ("ptsname", (ret == NULL) ? (-1) : 1, &following_combo);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------- clone */
|
||
|
/* clone can be fork-like or pthread_create-like, depending on whether
|
||
|
* the flag CLONE_VM is set. If CLONE_VM is not set, then we interpose
|
||
|
* clone in the way similar to interposing fork; if CLONE_VM is set,
|
||
|
* then we interpose clone in the way similar to interposing pthread_create.
|
||
|
* One special case is not handled: when CLONE_VM is set but CLONE_THREAD
|
||
|
* is not, if the parent process exits earlier than the child process,
|
||
|
* experiment will close, losing data from child process.
|
||
|
*/
|
||
|
typedef struct __collector_clone_arg
|
||
|
{
|
||
|
int (*fn)(void *);
|
||
|
void * arg;
|
||
|
char * new_lineage;
|
||
|
int following_fork;
|
||
|
} __collector_clone_arg_t;
|
||
|
|
||
|
static int
|
||
|
__collector_clone_fn (void *fn_arg)
|
||
|
{
|
||
|
int (*fn)(void *) = ((__collector_clone_arg_t*) fn_arg)->fn;
|
||
|
void * arg = ((__collector_clone_arg_t*) fn_arg)->arg;
|
||
|
char * new_lineage = ((__collector_clone_arg_t*) fn_arg)->new_lineage;
|
||
|
int following_fork = ((__collector_clone_arg_t*) fn_arg)->following_fork;
|
||
|
__collector_freeCSize (__collector_heap, fn_arg, sizeof (__collector_clone_arg_t));
|
||
|
linetrace_ext_fork_epilogue ("clone", 0, new_lineage, &following_fork);
|
||
|
return fn (arg);
|
||
|
}
|
||
|
|
||
|
int clone (int (*fn)(void *), void *, int, void *, ...) __attribute__ ((weak, alias ("__collector_clone")));
|
||
|
|
||
|
int
|
||
|
__collector_clone (int (*fn)(void *), void *child_stack, int flags, void *arg,
|
||
|
... /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */)
|
||
|
{
|
||
|
int ret;
|
||
|
va_list va;
|
||
|
if (flags & CLONE_VM)
|
||
|
{
|
||
|
va_start (va, arg);
|
||
|
ret = __collector_ext_clone_pthread (fn, child_stack, flags, arg, va);
|
||
|
va_end (va);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (NULL_PTR (clone))
|
||
|
init_lineage_intf ();
|
||
|
int *guard = NULL;
|
||
|
int combo_flag = (line_mode == LM_TRACK_LINEAGE) ? ((CHCK_REENTRANCE (guard)) ? 1 : 0) : 0;
|
||
|
TprintfT (DBG_LT0, "__collector_clone() interposition: line_mode=%d combo=%d\n",
|
||
|
line_mode, combo_flag);
|
||
|
char new_lineage[LT_MAXNAMELEN];
|
||
|
int following_fork = 0;
|
||
|
__collector_clone_arg_t *funcinfo = __collector_allocCSize (__collector_heap, sizeof (__collector_clone_arg_t), 1);
|
||
|
(*funcinfo).fn = fn;
|
||
|
(*funcinfo).arg = arg;
|
||
|
(*funcinfo).new_lineage = new_lineage;
|
||
|
(*funcinfo).following_fork = 0;
|
||
|
pid_t * ptid = NULL;
|
||
|
struct user_desc * tls = NULL;
|
||
|
pid_t * ctid = NULL;
|
||
|
int num_args = 0;
|
||
|
va_start (va, arg);
|
||
|
if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID))
|
||
|
{
|
||
|
ptid = va_arg (va, pid_t *);
|
||
|
tls = va_arg (va, struct user_desc*);
|
||
|
ctid = va_arg (va, pid_t *);
|
||
|
num_args = 3;
|
||
|
}
|
||
|
else if (flags & CLONE_SETTLS)
|
||
|
{
|
||
|
ptid = va_arg (va, pid_t *);
|
||
|
tls = va_arg (va, struct user_desc*);
|
||
|
num_args = 2;
|
||
|
}
|
||
|
else if (flags & CLONE_PARENT_SETTID)
|
||
|
{
|
||
|
ptid = va_arg (va, pid_t *);
|
||
|
num_args = 1;
|
||
|
}
|
||
|
if ((line_mode != LM_TRACK_LINEAGE) || combo_flag || funcinfo == NULL)
|
||
|
{
|
||
|
switch (num_args)
|
||
|
{
|
||
|
case 3:
|
||
|
ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid);
|
||
|
break;
|
||
|
case 2:
|
||
|
ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls);
|
||
|
break;
|
||
|
case 1:
|
||
|
ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid);
|
||
|
break;
|
||
|
default:
|
||
|
ret = CALL_REAL (clone)(fn, child_stack, flags, arg);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
va_end (va);
|
||
|
return ret;
|
||
|
}
|
||
|
linetrace_ext_fork_prologue ("clone", new_lineage, &following_fork);
|
||
|
(*funcinfo).following_fork = following_fork;
|
||
|
switch (num_args)
|
||
|
{
|
||
|
case 3:
|
||
|
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls, ctid);
|
||
|
break;
|
||
|
case 2:
|
||
|
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid, tls);
|
||
|
break;
|
||
|
case 1:
|
||
|
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo, ptid);
|
||
|
break;
|
||
|
default:
|
||
|
ret = CALL_REAL (clone)(__collector_clone_fn, child_stack, flags, funcinfo);
|
||
|
break;
|
||
|
}
|
||
|
va_end (va);
|
||
|
if (ret < 0)
|
||
|
__collector_freeCSize (__collector_heap, funcinfo, sizeof (__collector_clone_arg_t));
|
||
|
TprintfT (DBG_LT0, "__collector_clone() interposing: pid=%d\n", ret);
|
||
|
linetrace_ext_fork_epilogue ("clone", ret, new_lineage, &following_fork);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*-------------------------------------------------------------------- setuid */
|
||
|
int setuid () __attribute__ ((weak, alias ("__collector_setuid")));
|
||
|
int _setuid () __attribute__ ((weak, alias ("__collector_setuid")));
|
||
|
|
||
|
int
|
||
|
__collector_setuid (uid_t ruid)
|
||
|
{
|
||
|
if (NULL_PTR (setuid))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0, "__collector_setuid(0x%x) interposing\n", ruid);
|
||
|
check_reuid_change (ruid, -1);
|
||
|
int ret = CALL_REAL (setuid)(ruid);
|
||
|
TprintfT (DBG_LT0, "__collector_setuid(0x%x) returning %d\n", ruid, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------------- seteuid */
|
||
|
int seteuid () __attribute__ ((weak, alias ("__collector_seteuid")));
|
||
|
int _seteuid () __attribute__ ((weak, alias ("__collector_seteuid")));
|
||
|
|
||
|
int
|
||
|
__collector_seteuid (uid_t euid)
|
||
|
{
|
||
|
if (NULL_PTR (seteuid))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0, "__collector_seteuid(0x%x) interposing\n", euid);
|
||
|
check_reuid_change (-1, euid);
|
||
|
int ret = CALL_REAL (seteuid)(euid);
|
||
|
TprintfT (DBG_LT0, "__collector_seteuid(0x%x) returning %d\n", euid, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------------ setreuid */
|
||
|
int setreuid () __attribute__ ((weak, alias ("__collector_setreuid")));
|
||
|
int _setreuid () __attribute__ ((weak, alias ("__collector_setreuid")));
|
||
|
|
||
|
int
|
||
|
__collector_setreuid (uid_t ruid, uid_t euid)
|
||
|
{
|
||
|
if (NULL_PTR (setreuid))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) interposing\n", ruid, euid);
|
||
|
check_reuid_change (ruid, euid);
|
||
|
int ret = CALL_REAL (setreuid)(ruid, euid);
|
||
|
TprintfT (DBG_LT0, "__collector_setreuid(0x%x,0x%x) returning %d\n", ruid, euid, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*-------------------------------------------------------------------- setgid */
|
||
|
int setgid () __attribute__ ((weak, alias ("__collector_setgid")));
|
||
|
int _setgid () __attribute__ ((weak, alias ("__collector_setgid")));
|
||
|
|
||
|
int
|
||
|
__collector_setgid (gid_t rgid)
|
||
|
{
|
||
|
if (NULL_PTR (setgid))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0, "__collector_setgid(0x%x) interposing\n", rgid);
|
||
|
check_regid_change (rgid, -1);
|
||
|
int ret = CALL_REAL (setgid)(rgid);
|
||
|
TprintfT (DBG_LT0, "__collector_setgid(0x%x) returning %d\n", rgid, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------------- setegid */
|
||
|
int setegid () __attribute__ ((weak, alias ("__collector_setegid")));
|
||
|
int _setegid () __attribute__ ((weak, alias ("__collector_setegid")));
|
||
|
|
||
|
int
|
||
|
__collector_setegid (gid_t egid)
|
||
|
{
|
||
|
if (NULL_PTR (setegid))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0, "__collector_setegid(0x%x) interposing\n", egid);
|
||
|
check_regid_change (-1, egid);
|
||
|
int ret = CALL_REAL (setegid)(egid);
|
||
|
TprintfT (DBG_LT0, "__collector_setegid(0x%x) returning %d\n", egid, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------------------ setregid */
|
||
|
int setregid () __attribute__ ((weak, alias ("__collector_setregid")));
|
||
|
int _setregid () __attribute__ ((weak, alias ("__collector_setregid")));
|
||
|
|
||
|
int
|
||
|
__collector_setregid (gid_t rgid, gid_t egid)
|
||
|
{
|
||
|
if (NULL_PTR (setregid))
|
||
|
init_lineage_intf ();
|
||
|
TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) interposing\n", rgid, egid);
|
||
|
check_regid_change (rgid, egid);
|
||
|
int ret = CALL_REAL (setregid)(rgid, egid);
|
||
|
TprintfT (DBG_LT0, "__collector_setregid(0x%x,0x%x) returning %d\n", rgid, egid, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------------------- selective following */
|
||
|
|
||
|
static int
|
||
|
linetrace_follow_experiment (const char *follow_spec, const char *lineage_str, const char *progname)
|
||
|
{
|
||
|
regex_t regex_desc;
|
||
|
if (!follow_spec)
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES NULL follow_spec\n");
|
||
|
return 1;
|
||
|
}
|
||
|
int ercode = regcomp (®ex_desc, follow_spec, REG_EXTENDED | REG_NOSUB | REG_NEWLINE);
|
||
|
if (ercode)
|
||
|
{
|
||
|
// syntax error in parsing string
|
||
|
#ifdef DEBUG
|
||
|
char errbuf[256];
|
||
|
regerror (ercode, ®ex_desc, errbuf, sizeof (errbuf));
|
||
|
TprintfT (DBG_LT0, "linetrace_follow_experiment: regerror()=%s\n", errbuf);
|
||
|
#endif
|
||
|
return 1;
|
||
|
}
|
||
|
TprintfT (DBG_LT0, "linetrace_follow_experiment(): compiled spec='%s'\n", follow_spec);
|
||
|
if (lineage_str)
|
||
|
{
|
||
|
if (!regexec (®ex_desc, lineage_str, 0, NULL, 0))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES lineage (follow_spec=%s,lineage=%s)\n",
|
||
|
follow_spec, lineage_str);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
if (progname)
|
||
|
{
|
||
|
if (!regexec (®ex_desc, progname, 0, NULL, 0))
|
||
|
{
|
||
|
TprintfT (DBG_LT0, "linetrace_follow_experiment(): MATCHES progname (follow_spec=%s,progname=%s)\n",
|
||
|
follow_spec, progname);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
TprintfT (DBG_LT0, "linetrace_follow_experiment(): DOES NOT MATCH (follow_spec=%s,lineage=%s,progname=%s)\n",
|
||
|
follow_spec, lineage_str ? lineage_str : "NULL",
|
||
|
progname ? progname : "NULL");
|
||
|
return 0;
|
||
|
}
|