/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
*/
/*
* Copyright (c) 2013 Joyent, Inc. All Rights reserved.
*/
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include <string.h>
#include <sys/mkdev.h>
#include "libproc.h"
#include "Pcontrol.h"
/*
* Pfdinfo.c - obtain open file information.
*/
/*
* Allocate an fd_info structure and stick it on the list.
* (Unless one already exists.) The list is sorted in
* reverse order. We will traverse it in that order later.
* This makes the usual ordered insert *fast*.
*/
fd_info_t *
Pfd2info(struct ps_prochandle *P, int fd)
{
fd_info_t *fip = list_next(&P->fd_head);
fd_info_t *next;
int i;
if (fip == NULL) {
list_link(&P->fd_head, NULL);
fip = list_next(&P->fd_head);
}
for (i = 0; i < P->num_fd; i++, fip = list_next(fip)) {
if (fip->fd_info.pr_fd == fd) {
return (fip);
}
if (fip->fd_info.pr_fd < fd) {
break;
}
}
next = fip;
if ((fip = calloc(1, sizeof (*fip))) == NULL)
return (NULL);
fip->fd_info.pr_fd = fd;
list_link(fip, next ? next : (void *)&(P->fd_head));
P->num_fd++;
return (fip);
}
/*
* Attempt to load the open file information from a live process.
*/
static void
load_fdinfo(struct ps_prochandle *P)
{
/*
* In the unlikely case there are *no* file descriptors open,
* we will keep rescanning the proc directory, which will be empty.
* This is an edge case it isn't worth adding additional state to
* to eliminate.
*/
if (P->num_fd > 0) {
return;
}
if (P->state != PS_DEAD && P->state != PS_IDLE) {
char dir_name[PATH_MAX];
char path[PATH_MAX];
struct dirent *ent;
DIR *dirp;
int fd;
/*
* Try to get the path information first.
*/
(void) snprintf(dir_name, sizeof (dir_name),
"%s/%d/path", procfs_path, (int)P->pid);
dirp = opendir(dir_name);
if (dirp == NULL) {
return;
}
ent = NULL;
while ((ent = readdir(dirp)) != NULL) {
fd_info_t *fip;
prfdinfo_t *info;
int len;
struct stat64 stat;
if (!isdigit(ent->d_name[0]))
continue;
fd = atoi(ent->d_name);
fip = Pfd2info(P, fd);
info = &fip->fd_info;
info->pr_fd = fd;
if (pr_fstat64(P, fd, &stat) == 0) {
info->pr_mode = stat.st_mode;
info->pr_uid = stat.st_uid;
info->pr_gid = stat.st_gid;
info->pr_major = major(stat.st_dev);
info->pr_minor = minor(stat.st_dev);
info->pr_rmajor = major(stat.st_rdev);
info->pr_rminor = minor(stat.st_rdev);
info->pr_size = stat.st_size;
info->pr_ino = stat.st_ino;
}
info->pr_fileflags = pr_fcntl(P, fd, F_GETXFL, 0);
info->pr_fdflags = pr_fcntl(P, fd, F_GETFD, 0);
info->pr_offset = pr_llseek(P, fd, 0, SEEK_CUR);
/* attempt to determine the path to it */
switch (info->pr_mode & S_IFMT) {
case S_IFDOOR:
case S_IFSOCK:
/* not applicable */
len = -1;
break;
default:
(void) snprintf(path, sizeof (path),
"%s/%d/path/%d", procfs_path, (int)P->pid,
fd);
len = readlink(path, info->pr_path,
sizeof (info->pr_path) - 1);
break;
}
if (len < 0) {
info->pr_path[0] = 0;
} else {
info->pr_path[len] = 0;
}
}
(void) closedir(dirp);
}
}
int
Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd)
{
fd_info_t *fip;
int rv;
/* Make sure we have live data, if appropriate */
load_fdinfo(P);
/* NB: We walk the list backwards. */
for (fip = list_prev(&P->fd_head);
fip != (void *)&P->fd_head && fip != NULL;
fip = list_prev(fip)) {
if ((rv = func(cd, &fip->fd_info)) != 0)
return (rv);
}
return (0);
}