#include <assert.h>
#include <dlfcn.h>
#include <errno.h>
#include <libzonecfg.h>
#include <link.h>
#include <string.h>
#include <strings.h>
#include <sys/list.h>
#include <sys/types.h>
#include <sys/mkdev.h>
#include <sys/mman.h>
#include <sys/mnttab.h>
#include "Pcontrol.h"
struct path_node {
struct path_node *pn_next;
char *pn_path;
};
typedef struct path_node path_node_t;
static struct stat64 lofs_mstat;
static struct lofs_mnttab {
struct lofs_mnttab *l_next;
char *l_special;
char *l_mountp;
} *lofs_mnttab = NULL;
static mutex_t lofs_lock = DEFAULTMUTEX;
static void
rebuild_lofs_cache(void)
{
struct mnttab mt;
struct mnttab mt_find;
struct lofs_mnttab *lmt;
struct lofs_mnttab *next;
FILE *fp;
assert(MUTEX_HELD(&lofs_lock));
for (lmt = lofs_mnttab; lmt != NULL; lmt = next) {
next = lmt->l_next;
free(lmt->l_special);
free(lmt->l_mountp);
free(lmt);
}
lofs_mnttab = NULL;
if ((fp = fopen(MNTTAB, "r")) == NULL)
return;
bzero(&mt_find, sizeof (mt_find));
mt_find.mnt_fstype = "lofs";
while (getmntany(fp, &mt, &mt_find) == 0 &&
(strcmp(mt.mnt_fstype, "lofs") == 0) &&
(strcmp(mt.mnt_special, mt.mnt_mountp) != 0)) {
if ((lmt = malloc(sizeof (struct lofs_mnttab))) == NULL)
break;
lmt->l_special = strdup(mt.mnt_special);
lmt->l_mountp = strdup(mt.mnt_mountp);
lmt->l_next = lofs_mnttab;
lofs_mnttab = lmt;
}
(void) fclose(fp);
}
static const char *
lookup_lofs_mount_point(const char *mountp)
{
struct lofs_mnttab *lmt;
assert(MUTEX_HELD(&lofs_lock));
for (lmt = lofs_mnttab; lmt != NULL; lmt = lmt->l_next) {
if (strcmp(lmt->l_mountp, mountp) == 0)
return (lmt->l_special);
}
return (NULL);
}
static path_node_t *
pn_push(path_node_t **pnp, char *path)
{
path_node_t *pn;
if ((pn = calloc(sizeof (path_node_t), 1)) == NULL)
return (NULL);
if ((pn->pn_path = strdup(path)) == NULL) {
free(pn);
return (NULL);
}
pn->pn_next = *pnp;
return (*pnp = pn);
}
static void
pn_free(path_node_t **pnp)
{
path_node_t *pn;
while (*pnp != NULL) {
pn = *pnp;
*pnp = pn->pn_next;
free(pn->pn_path);
free(pn);
}
}
static void
pn_free2(path_node_t **pn1, path_node_t **pn2)
{
pn_free(pn1);
pn_free(pn2);
}
static char *
pn_pop(path_node_t **pnp, char *path)
{
path_node_t *pn;
if (*pnp == NULL)
return (NULL);
pn = *pnp;
*pnp = pn->pn_next;
pn->pn_next = NULL;
if (path == NULL) {
pn_free(&pn);
return (NULL);
}
(void) strlcpy(path, pn->pn_path, PATH_MAX);
pn_free(&pn);
return (path);
}
static int
i_zone_get_zonepath(char *zone_name, char *zonepath, size_t rp_sz)
{
typedef int (*zone_get_zonepath_t)(char *, char *, size_t);
static zone_get_zonepath_t zone_get_zonepath_fp = NULL;
if (zone_get_zonepath_fp == NULL) {
void *dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY);
void *sym = (void *)(-1);
if (dlhandle != NULL &&
(sym = dlsym(dlhandle, "zone_get_zonepath")) == NULL) {
sym = (void *)(-1);
(void) dlclose(dlhandle);
}
zone_get_zonepath_fp = (zone_get_zonepath_t)sym;
}
if (zone_get_zonepath_fp != (zone_get_zonepath_t)(-1))
return (zone_get_zonepath_fp(zone_name, zonepath, rp_sz));
return (Z_NO_ZONE);
}
char *
Pbrandname(struct ps_prochandle *P, char *buf, size_t buflen)
{
long addr;
if ((addr = Pgetauxval(P, AT_SUN_BRANDNAME)) == -1)
return (NULL);
if (Pread_string(P, buf, buflen, addr) == -1)
return (NULL);
return (buf);
}
char *
Pzonename(struct ps_prochandle *P, char *s, size_t n)
{
return (P->ops.pop_zonename(P, s, n, P->data));
}
char *
Pzoneroot(struct ps_prochandle *P, char *s, size_t n)
{
char zname[ZONENAME_MAX], zpath[PATH_MAX], tmp[PATH_MAX];
int rv;
if (P->zoneroot != NULL) {
(void) strlcpy(s, P->zoneroot, n);
return (s);
}
if ((Pzonename(P, zname, sizeof (zname)) == NULL) ||
(strcmp(zname, GLOBAL_ZONENAME) == 0)) {
if ((P->zoneroot = strdup("")) == NULL) {
errno = ENOMEM;
return (NULL);
}
Pdprintf("Pzoneroot defaulting to '%s'\n", GLOBAL_ZONENAME);
(void) strlcpy(s, P->zoneroot, n);
return (s);
}
if (i_zone_get_zonepath(zname, zpath, sizeof (zpath)) != Z_OK) {
if ((P->zoneroot = strdup("")) == NULL) {
errno = ENOMEM;
return (NULL);
}
Pdprintf(
"Pzoneroot zone not found '%s', defaulting to '%s'\n",
zname, GLOBAL_ZONENAME);
(void) strlcpy(s, P->zoneroot, n);
return (s);
}
(void) strlcat(zpath, "/root", sizeof (zpath));
if ((rv = resolvepath(zpath, tmp, sizeof (tmp) - 1)) < 0) {
if ((P->zoneroot = strdup("")) == NULL) {
errno = ENOMEM;
return (NULL);
}
Pdprintf(
"Pzoneroot can't access '%s:%s', defaulting to '%s'\n",
zname, zpath, GLOBAL_ZONENAME);
(void) strlcpy(s, P->zoneroot, n);
return (s);
}
tmp[rv] = '\0';
(void) strlcpy(zpath, tmp, sizeof (zpath));
if ((P->zoneroot = strdup(zpath)) == NULL) {
errno = ENOMEM;
return (NULL);
}
Pdprintf("Pzoneroot found zone root '%s:%s'\n", zname, zpath);
(void) strlcpy(s, P->zoneroot, n);
return (s);
}
char *
Plofspath(const char *path, char *s, size_t n)
{
char tmp[PATH_MAX + 1];
struct stat64 statb;
const char *special;
char *p, *p2;
int rv;
Pdprintf("Plofspath path '%s'\n", path);
if (path[0] != '/')
return (NULL);
(void) strlcpy(tmp, path, sizeof (tmp) - 1);
if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
tmp[rv] = '\0';
(void) mutex_lock(&lofs_lock);
if (stat64(MNTTAB, &statb) == 0 &&
(statb.st_mtim.tv_sec != lofs_mstat.st_mtim.tv_sec ||
statb.st_mtim.tv_nsec != lofs_mstat.st_mtim.tv_nsec ||
statb.st_ctim.tv_sec != lofs_mstat.st_ctim.tv_sec ||
statb.st_ctim.tv_nsec != lofs_mstat.st_ctim.tv_nsec)) {
lofs_mstat = statb;
rebuild_lofs_cache();
}
p = &tmp[strlen(tmp)];
p[1] = '\0';
for (;;) {
if ((special = lookup_lofs_mount_point(tmp)) != NULL) {
char tmp2[PATH_MAX + 1];
(void) strlcpy(tmp2, special, sizeof (tmp2) - 1);
(void) strlcat(tmp2, "/", sizeof (tmp2) - 1);
(void) strlcat(tmp2, &p[1], sizeof (tmp2) - 1);
if ((rv = resolvepath(tmp2, tmp2, sizeof (tmp2) - 1)) >=
0) {
tmp2[rv] = '\0';
(void) strlcpy(tmp, tmp2, sizeof (tmp) - 1);
p = &tmp[strlen(tmp)];
p[1] = '\0';
continue;
}
}
if ((p2 = strrchr(tmp, '/')) == NULL) {
char tmp2[PATH_MAX];
(void) mutex_unlock(&lofs_lock);
assert(p == tmp);
assert(p[0] == '\0');
p[0] = '/';
if (strcmp(tmp, path) == 0) {
return (NULL);
}
(void) strlcpy(tmp2, tmp, sizeof (tmp2));
if ((rv = resolvepath(tmp, tmp, sizeof (tmp) - 1)) >= 0)
tmp[rv] = '\0';
(void) Plofspath(tmp, tmp, PATH_MAX);
(void) strlcpy(s, tmp, n);
Pdprintf("Plofspath path result '%s'\n", s);
return (s);
}
if (p[1] != '\0')
p[0] = '/';
p2[0] = '\0';
p = p2;
}
}
char *
Pzonepath(struct ps_prochandle *P, const char *path, char *s, size_t n)
{
char zroot[PATH_MAX], zpath[PATH_MAX], tmp[PATH_MAX], link[PATH_MAX];
path_node_t *pn_stack = NULL, *pn_links = NULL, *pn;
struct stat64 sb;
char *p;
int i, rv;
Pdprintf("Pzonepath lookup '%s'\n", path);
if (Pzoneroot(P, zroot, sizeof (zroot)) == NULL)
return (NULL);
tmp[0] = '\0';
if (path[0] != '/')
(void) strlcat(tmp, "/", sizeof (tmp));
(void) strlcat(tmp, path, sizeof (tmp));
if (strcmp(tmp, zroot) == 0) {
(void) Plofspath(zroot, zroot, sizeof (zroot));
Pdprintf("Pzonepath found zone path (1) '%s'\n", zroot);
(void) strlcpy(s, zroot, n);
return (s);
}
i = strlen(zroot);
if ((strncmp(tmp, zroot, i) == 0) && (tmp[i] == '/'))
(void) memmove(tmp, tmp + i, strlen(tmp + i) + 1);
if (strlen(tmp) == 0) {
(void) Plofspath(zroot, zroot, sizeof (zroot));
Pdprintf("Pzonepath found zone path (2) '%s'\n", zroot);
(void) strlcpy(s, zroot, n);
return (s);
}
while ((p = strrchr(tmp, '/')) != NULL) {
*p = '\0';
if (pn_push(&pn_stack, &p[1]) != NULL)
continue;
pn_free(&pn_stack);
return (NULL);
}
*zpath = '\0';
while (pn_pop(&pn_stack, tmp) != NULL) {
if ((strlen(tmp) == 0) || (strcmp(tmp, ".") == 0))
continue;
if (strcmp(tmp, "..") == 0) {
if ((p = strrchr(zpath, '/')) != NULL)
*p = '\0';
continue;
}
(void) strlcat(zpath, "/", sizeof (zpath));
(void) strlcat(zpath, tmp, sizeof (zpath));
if ((strncmp(zpath, "/native", sizeof ("/native")) == 0) ||
(strncmp(zpath, "/.SUNWnative",
sizeof ("/.SUNWnative")) == 0)) {
pn_free(&pn_links);
*zpath = '\0';
while (pn_pop(&pn_stack, tmp) != NULL) {
(void) strlcat(zpath, "/", sizeof (zpath));
(void) strlcat(zpath, tmp, sizeof (zpath));
}
rv = resolvepath(zpath, tmp, sizeof (tmp) - 1);
if (rv < 0) {
Pdprintf("Pzonepath invalid native path '%s'\n",
zpath);
return (NULL);
}
tmp[rv] = '\0';
Pdprintf("Pzonepath found native path '%s'\n", tmp);
(void) Plofspath(tmp, tmp, sizeof (tmp));
(void) strlcpy(s, tmp, n);
return (s);
}
(void) strlcpy(tmp, zroot, sizeof (tmp));
(void) strlcat(tmp, zpath, sizeof (tmp));
if (lstat64(tmp, &sb) != 0) {
pn_free2(&pn_stack, &pn_links);
return (NULL);
}
if (!S_ISLNK(sb.st_mode)) {
continue;
}
for (pn = pn_links; pn != NULL; pn = pn->pn_next) {
if (strcmp(zpath, pn->pn_path) != 0)
continue;
Pdprintf("Pzonepath symlink loop '%s'\n", zpath);
pn_free2(&pn_stack, &pn_links);
return (NULL);
}
if (pn_push(&pn_links, zpath) == NULL) {
pn_free2(&pn_stack, &pn_links);
return (NULL);
}
bzero(link, sizeof (link));
if (readlink(tmp, link, sizeof (link)) == -1) {
pn_free2(&pn_stack, &pn_links);
return (NULL);
}
Pdprintf("Pzonepath following symlink '%s' -> '%s'\n",
zpath, link);
while ((p = strrchr(link, '/')) != NULL) {
*p = '\0';
if (pn_push(&pn_stack, &p[1]) != NULL)
continue;
pn_free2(&pn_stack, &pn_links);
return (NULL);
}
if (*link == '\0') {
*zpath = '\0';
continue;
}
if (pn_push(&pn_stack, link) == NULL) {
pn_free2(&pn_stack, &pn_links);
return (NULL);
}
p = strrchr(zpath, '/');
assert(p != NULL);
*p = '\0';
continue;
}
pn_free(&pn_links);
(void) strlcpy(tmp, zroot, sizeof (tmp));
(void) strlcat(tmp, zpath, sizeof (tmp));
(void) strlcpy(zpath, tmp, sizeof (zpath));
(void) Plofspath(zpath, zpath, sizeof (zpath));
Pdprintf("Pzonepath found zone path (3) '%s'\n", zpath);
(void) strlcpy(s, zpath, n);
return (s);
}
char *
Pfindobj(struct ps_prochandle *P, const char *path, char *s, size_t n)
{
int len;
Pdprintf("Pfindobj '%s'\n", path);
if (path[0] != '/')
return (NULL);
if (Pzonepath(P, path, s, n) != NULL)
return (s);
if (Plofspath(path, s, n) != NULL)
return (s);
if ((len = resolvepath(path, s, n)) > 0) {
s[len] = '\0';
return (s);
}
return (NULL);
}
char *
Pfindmap(struct ps_prochandle *P, map_info_t *mptr, char *s, size_t n)
{
file_info_t *fptr = mptr->map_file;
char buf[PATH_MAX];
int len;
if ((fptr != NULL) && (fptr->file_rname != NULL)) {
(void) strlcpy(s, fptr->file_rname, n);
return (s);
}
if ((P->map_exec == mptr) ||
(strcmp(mptr->map_pmap.pr_mapname, "a.out") == 0) ||
((fptr != NULL) && (fptr->file_lname != NULL) &&
(strcmp(fptr->file_lname, "a.out") == 0))) {
if (Pexecname(P, buf, sizeof (buf)) != NULL) {
(void) strlcpy(s, buf, n);
return (s);
}
}
if ((Pstate(P) != PS_DEAD) && (mptr->map_pmap.pr_mapname[0] != '\0')) {
char path[PATH_MAX];
len = snprintf(path, sizeof (path), "%s/%d/path/%s",
procfs_path, (int)P->pid, mptr->map_pmap.pr_mapname);
if (len < 0 || (size_t)len >= sizeof (path))
return (NULL);
if ((len = readlink(path, buf, sizeof (buf))) > 0) {
buf[len] = '\0';
(void) Plofspath(buf, buf, sizeof (buf));
(void) strlcpy(s, buf, n);
return (s);
}
}
fptr = mptr->map_file;
if ((fptr != NULL) && (fptr->file_lname != NULL)) {
(void) strlcpy(buf, fptr->file_lname, sizeof (buf));
if (Pfindobj(P, buf, buf, sizeof (buf)) == NULL)
return (NULL);
(void) strlcpy(s, buf, n);
return (s);
}
return (NULL);
}