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