1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. 14 */ 15 /* 16 * Copyright (c) 2013 Joyent, Inc. All Rights reserved. 17 */ 18 19 #include <limits.h> 20 #include <stdio.h> 21 #include <errno.h> 22 #include <unistd.h> 23 #include <dirent.h> 24 #include <ctype.h> 25 #include <string.h> 26 #include <sys/mkdev.h> 27 28 #include "libproc.h" 29 #include "Pcontrol.h" 30 31 /* 32 * Pfdinfo.c - obtain open file information. 33 */ 34 35 /* 36 * Allocate an fd_info structure and stick it on the list. 37 * (Unless one already exists.) The list is sorted in 38 * reverse order. We will traverse it in that order later. 39 * This makes the usual ordered insert *fast*. 40 */ 41 fd_info_t * 42 Pfd2info(struct ps_prochandle *P, int fd) 43 { 44 fd_info_t *fip = list_next(&P->fd_head); 45 fd_info_t *next; 46 int i; 47 48 if (fip == NULL) { 49 list_link(&P->fd_head, NULL); 50 fip = list_next(&P->fd_head); 51 } 52 53 for (i = 0; i < P->num_fd; i++, fip = list_next(fip)) { 54 if (fip->fd_info.pr_fd == fd) { 55 return (fip); 56 } 57 if (fip->fd_info.pr_fd < fd) { 58 break; 59 } 60 } 61 62 next = fip; 63 if ((fip = calloc(1, sizeof (*fip))) == NULL) 64 return (NULL); 65 66 fip->fd_info.pr_fd = fd; 67 list_link(fip, next ? next : (void *)&(P->fd_head)); 68 P->num_fd++; 69 return (fip); 70 } 71 72 /* 73 * Attempt to load the open file information from a live process. 74 */ 75 static void 76 load_fdinfo(struct ps_prochandle *P) 77 { 78 /* 79 * In the unlikely case there are *no* file descriptors open, 80 * we will keep rescanning the proc directory, which will be empty. 81 * This is an edge case it isn't worth adding additional state to 82 * to eliminate. 83 */ 84 if (P->num_fd > 0) { 85 return; 86 } 87 88 if (P->state != PS_DEAD && P->state != PS_IDLE) { 89 char dir_name[PATH_MAX]; 90 char path[PATH_MAX]; 91 struct dirent *ent; 92 DIR *dirp; 93 int fd; 94 95 /* 96 * Try to get the path information first. 97 */ 98 (void) snprintf(dir_name, sizeof (dir_name), 99 "%s/%d/path", procfs_path, (int)P->pid); 100 dirp = opendir(dir_name); 101 if (dirp == NULL) { 102 return; 103 } 104 ent = NULL; 105 while ((ent = readdir(dirp)) != NULL) { 106 fd_info_t *fip; 107 prfdinfo_t *info; 108 int len; 109 struct stat64 stat; 110 111 if (!isdigit(ent->d_name[0])) 112 continue; 113 114 fd = atoi(ent->d_name); 115 116 fip = Pfd2info(P, fd); 117 info = &fip->fd_info; 118 info->pr_fd = fd; 119 120 if (pr_fstat64(P, fd, &stat) == 0) { 121 info->pr_mode = stat.st_mode; 122 info->pr_uid = stat.st_uid; 123 info->pr_gid = stat.st_gid; 124 info->pr_major = major(stat.st_dev); 125 info->pr_minor = minor(stat.st_dev); 126 info->pr_rmajor = major(stat.st_rdev); 127 info->pr_rminor = minor(stat.st_rdev); 128 info->pr_size = stat.st_size; 129 info->pr_ino = stat.st_ino; 130 } 131 132 info->pr_fileflags = pr_fcntl(P, fd, F_GETXFL, 0); 133 info->pr_fdflags = pr_fcntl(P, fd, F_GETFD, 0); 134 info->pr_offset = pr_llseek(P, fd, 0, SEEK_CUR); 135 136 /* attempt to determine the path to it */ 137 switch (info->pr_mode & S_IFMT) { 138 case S_IFDOOR: 139 case S_IFSOCK: 140 /* not applicable */ 141 len = -1; 142 break; 143 default: 144 (void) snprintf(path, sizeof (path), 145 "%s/%d/path/%d", procfs_path, (int)P->pid, 146 fd); 147 len = readlink(path, info->pr_path, 148 sizeof (info->pr_path) - 1); 149 break; 150 } 151 152 if (len < 0) { 153 info->pr_path[0] = 0; 154 } else { 155 info->pr_path[len] = 0; 156 } 157 } 158 (void) closedir(dirp); 159 160 } 161 } 162 163 int 164 Pfdinfo_iter(struct ps_prochandle *P, proc_fdinfo_f *func, void *cd) 165 { 166 fd_info_t *fip; 167 int rv; 168 169 /* Make sure we have live data, if appropriate */ 170 load_fdinfo(P); 171 172 /* NB: We walk the list backwards. */ 173 174 for (fip = list_prev(&P->fd_head); 175 fip != (void *)&P->fd_head && fip != NULL; 176 fip = list_prev(fip)) { 177 if ((rv = func(cd, &fip->fd_info)) != 0) 178 return (rv); 179 } 180 return (0); 181 } 182