/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
#include <fnmatch.h>
#include <apptrace.h>
#include <libintl.h>
#include "abienv.h"
static char const *strdup_sym = "strdup";
static char const *malloc_sym = "malloc";
static char const *comma = ",";
static void
bugout(char const *call)
{
(void) fprintf(stderr,
dgettext(TEXT_DOMAIN, "apptrace: %s failed\n"),
call);
exit(EXIT_FAILURE);
}
void
build_env_list(Liblist **list, char const *env)
{
char *envstr;
char *tok;
if ((envstr = getenv(env)) == NULL)
return;
if ((envstr = strdup(envstr)) == NULL)
bugout(strdup_sym);
tok = strtok(envstr, comma);
while (tok != NULL) {
Liblist *lp;
if ((lp = malloc(sizeof (Liblist))) == NULL)
bugout(malloc_sym);
lp->l_libname = tok;
lp->l_next = *list;
*list = lp;
tok = strtok(NULL, comma);
}
}
void
build_env_list1(Liblist **list, Liblist **listend, const char *env)
{
char *envstr;
char *tok;
if ((envstr = getenv(env)) == NULL)
return;
/*
* It is possible that we have a single file name,
* in which case the subseqent loop will do nothing
*/
if (strchr(envstr, ',') == NULL) {
appendlist(list, listend, envstr, 1);
return;
}
if ((envstr = strdup(envstr)) == NULL)
bugout(strdup_sym);
tok = strtok(envstr, comma);
while (tok != NULL) {
appendlist(list, listend, tok, 1);
tok = strtok(NULL, comma);
}
free(envstr);
}
void
env_to_intlist(Intlist **list, char const *env)
{
char *envstr;
char *tok;
if ((envstr = getenv(env)) == NULL)
return;
if ((envstr = strdup(envstr)) == NULL)
bugout(strdup_sym);
for (tok = strtok(envstr, comma);
tok != NULL;
tok = strtok(NULL, comma)) {
Intlist *ip;
if ((ip = malloc(sizeof (Intlist))) == NULL)
bugout(malloc_sym);
if ((ip->i_name = strdup(tok)) == NULL)
bugout(strdup_sym);
ip->i_next = *list;
*list = ip;
}
free(envstr);
}
void
appendlist(Liblist **list, Liblist **listend, const char *name, int fatal)
{
Liblist *lp;
void *handle;
if (access(name, R_OK)) {
if (fatal) {
(void) fprintf(stderr,
dgettext(TEXT_DOMAIN,
"apptrace: %s: %s\n"),
name,
strerror(errno));
exit(EXIT_FAILURE);
}
return;
}
if ((handle = dlopen(name, RTLD_LAZY)) == NULL) {
if (fatal) {
(void) fprintf(stderr,
dgettext(TEXT_DOMAIN,
"apptrace: dlopen on %s failed: %s\n"),
name,
dlerror());
exit(EXIT_FAILURE);
}
return;
}
/* OK, so now add it to the end of the list */
if ((lp = malloc(sizeof (Liblist))) == NULL)
bugout(malloc_sym);
if ((lp->l_libname = strdup(name)) == NULL)
bugout(strdup_sym);
lp->l_handle = handle;
lp->l_next = NULL;
if (*listend)
(*listend)->l_next = lp;
if (*list == NULL)
*list = lp;
*listend = lp;
}
/*
* Called abibasename() to avoid clash with basename(3C)
* Incidentally, basename(3C) is destructive which is why
* we are not using it instead.
*/
char *
abibasename(const char *str)
{
char *p;
if ((p = strrchr(str, '/')) != NULL)
return (p + 1);
else
return ((char *)str);
}
Liblist *
check_list(Liblist *list, char const *str)
{
char *basename1, *basename2, *p1, *p2;
Liblist *ret = NULL;
if (list == NULL)
return (NULL);
if ((basename2 = strdup(abibasename(str))) == NULL)
bugout(strdup_sym);
if ((p2 = strchr(basename2, '.')) != NULL)
*p2 = '\0';
for (; list; list = list->l_next) {
/* Lose the dirname */
if ((basename1 = strdup(abibasename(list->l_libname))) == NULL)
bugout(strdup_sym);
/* Lose the suffix */
if ((p1 = strchr(basename1, '.')) != NULL)
*p1 = '\0';
if (fnmatch(basename1, basename2, 0) == 0) {
ret = list;
free(basename1);
break;
}
free(basename1);
}
free(basename2);
return (ret);
}
int
check_intlist(Intlist *list, char const *iface)
{
if (list == NULL)
return (0);
for (; list != NULL; list = list->i_next) {
if (fnmatch(list->i_name, iface, 0) == 0)
return (1);
}
return (0);
}
char *
checkenv(char const *env)
{
char *envstr;
if ((envstr = getenv(env)) == NULL)
return (NULL);
while (*envstr == ' ')
envstr++;
if (*envstr == '\0')
return (NULL);
return (envstr);
}
int
build_interceptor_path(char *buf, size_t l, char const *path)
{
char *p, *t, *f;
#if defined(_LP64)
char *m;
#endif
int ret;
/* Duplicate the path */
if ((p = strdup(path)) == NULL)
bugout(strdup_sym);
/* Find the last slash, if there ain't one bug out */
if ((t = strrchr(p, '/')) == NULL) {
ret = 0;
goto done;
}
/*
* Wack the slash to a null byte.
* Thus if we got:
* /A/B/C/D.so.1
* p now points to /A/B/C
* f is set to point to D.so.1
*/
*t = '\0';
f = ++t;
#if defined(_LP64)
/*
* As above except that in LP64 (for sparc) we'll get:
* /A/B/C/sparcv9/D.so.1
* thus p now points to:
* /A/B/C/sparcv9
* so we repeat the wack so that we get:
* /A/B/C
* and retain a pointer, m, to the machine dependent portion.
*/
if ((t = strrchr(p, '/')) == NULL) {
ret = 0;
goto done;
}
*t = '\0';
m = ++t;
/*
* Now we can build a path name.
* This path is only a guess that'll be checked later in appendlist().
* Some system libraries, like libc.so.1, reside in /lib while their
* corresponding abi_* counterparts reside in /usr/lib. The same is
* true for libraries like libc_psr.so.1 that reside in /platform
* rather than /usr/platform. To deal with this, we check whether
* the file in the direct path name we generate exists, and if not,
* we prepend "/usr" to it. This handles all existing cases.
*/
ret = snprintf(buf, l, "%s/abi/%s/abi_%s", p, m, f);
if (access(buf, R_OK) != 0 && strncmp(buf, "/usr/", 5) != 0)
ret = snprintf(buf, l, "/usr%s/abi/%s/abi_%s", p, m, f);
#else
ret = snprintf(buf, l, "%s/abi/abi_%s", p, f);
if (access(buf, R_OK) != 0 && strncmp(buf, "/usr/", 5) != 0)
ret = snprintf(buf, l, "/usr%s/abi/abi_%s", p, f);
#endif
done:
free(p);
return (ret);
}