procstat_files.c revision 280250
1/*-
2 * Copyright (c) 2007-2011 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: stable/10/usr.bin/procstat/procstat_files.c 280250 2015-03-19 12:32:48Z rwatson $
27 */
28
29#include <sys/param.h>
30#include <sys/capsicum.h>
31#include <sys/socket.h>
32#include <sys/sysctl.h>
33#include <sys/un.h>
34#include <sys/user.h>
35
36#include <netinet/in.h>
37
38#include <arpa/inet.h>
39
40#include <err.h>
41#include <libprocstat.h>
42#include <inttypes.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46
47#include "procstat.h"
48
49static const char *
50protocol_to_string(int domain, int type, int protocol)
51{
52
53	switch (domain) {
54	case AF_INET:
55	case AF_INET6:
56		switch (protocol) {
57		case IPPROTO_TCP:
58			return ("TCP");
59		case IPPROTO_UDP:
60			return ("UDP");
61		case IPPROTO_ICMP:
62			return ("ICM");
63		case IPPROTO_RAW:
64			return ("RAW");
65		case IPPROTO_SCTP:
66			return ("SCT");
67		case IPPROTO_DIVERT:
68			return ("IPD");
69		default:
70			return ("IP?");
71		}
72
73	case AF_LOCAL:
74		switch (type) {
75		case SOCK_STREAM:
76			return ("UDS");
77		case SOCK_DGRAM:
78			return ("UDD");
79		default:
80			return ("UD?");
81		}
82	default:
83		return ("?");
84	}
85}
86
87static void
88addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen)
89{
90	char buffer2[INET6_ADDRSTRLEN];
91	struct sockaddr_in6 *sin6;
92	struct sockaddr_in *sin;
93	struct sockaddr_un *sun;
94
95	switch (ss->ss_family) {
96	case AF_LOCAL:
97		sun = (struct sockaddr_un *)ss;
98		if (strlen(sun->sun_path) == 0)
99			strlcpy(buffer, "-", buflen);
100		else
101			strlcpy(buffer, sun->sun_path, buflen);
102		break;
103
104	case AF_INET:
105		sin = (struct sockaddr_in *)ss;
106		snprintf(buffer, buflen, "%s:%d", inet_ntoa(sin->sin_addr),
107		    ntohs(sin->sin_port));
108		break;
109
110	case AF_INET6:
111		sin6 = (struct sockaddr_in6 *)ss;
112		if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2,
113		    sizeof(buffer2)) != NULL)
114			snprintf(buffer, buflen, "%s.%d", buffer2,
115			    ntohs(sin6->sin6_port));
116		else
117			strlcpy(buffer, "-", buflen);
118		break;
119
120	default:
121		strlcpy(buffer, "", buflen);
122		break;
123	}
124}
125
126static void
127print_address(struct sockaddr_storage *ss)
128{
129	char addr[PATH_MAX];
130
131	addr_to_string(ss, addr, sizeof(addr));
132	printf("%s", addr);
133}
134
135static struct cap_desc {
136	uint64_t	 cd_right;
137	const char	*cd_desc;
138} cap_desc[] = {
139	/* General file I/O. */
140	{ CAP_READ,		"rd" },
141	{ CAP_WRITE,		"wr" },
142	{ CAP_SEEK,		"se" },
143	{ CAP_MMAP,		"mm" },
144	{ CAP_CREATE,		"cr" },
145	{ CAP_FEXECVE,		"fe" },
146	{ CAP_FSYNC,		"fy" },
147	{ CAP_FTRUNCATE,	"ft" },
148
149	/* VFS methods. */
150	{ CAP_FCHDIR,		"cd" },
151	{ CAP_FCHFLAGS,		"cf" },
152	{ CAP_FCHMOD,		"cm" },
153	{ CAP_FCHOWN,		"cn" },
154	{ CAP_FCNTL,		"fc" },
155	{ CAP_FLOCK,		"fl" },
156	{ CAP_FPATHCONF,	"fp" },
157	{ CAP_FSCK,		"fk" },
158	{ CAP_FSTAT,		"fs" },
159	{ CAP_FSTATFS,		"sf" },
160	{ CAP_FUTIMES,		"fu" },
161	{ CAP_LINKAT,		"li" },
162	{ CAP_MKDIRAT,		"md" },
163	{ CAP_MKFIFOAT,		"mf" },
164	{ CAP_MKNODAT,		"mn" },
165	{ CAP_RENAMEAT,		"rn" },
166	{ CAP_SYMLINKAT,	"sl" },
167	{ CAP_UNLINKAT,		"un" },
168
169	/* Lookups - used to constrain *at() calls. */
170	{ CAP_LOOKUP,		"lo" },
171
172	/* Extended attributes. */
173	{ CAP_EXTATTR_GET,	"eg" },
174	{ CAP_EXTATTR_SET,	"es" },
175	{ CAP_EXTATTR_DELETE,	"ed" },
176	{ CAP_EXTATTR_LIST,	"el" },
177
178	/* Access Control Lists. */
179	{ CAP_ACL_GET,		"ag" },
180	{ CAP_ACL_SET,		"as" },
181	{ CAP_ACL_DELETE,	"ad" },
182	{ CAP_ACL_CHECK,	"ac" },
183
184	/* Socket operations. */
185	{ CAP_ACCEPT,		"at" },
186	{ CAP_BIND,		"bd" },
187	{ CAP_CONNECT,		"co" },
188	{ CAP_GETPEERNAME,	"pn" },
189	{ CAP_GETSOCKNAME,	"sn" },
190	{ CAP_GETSOCKOPT,	"gs" },
191	{ CAP_LISTEN,		"ln" },
192	{ CAP_PEELOFF,		"pf" },
193	{ CAP_SETSOCKOPT,	"ss" },
194	{ CAP_SHUTDOWN,		"sh" },
195
196	/* Mandatory Access Control. */
197	{ CAP_MAC_GET,		"mg" },
198	{ CAP_MAC_SET,		"ms" },
199
200	/* Methods on semaphores. */
201	{ CAP_SEM_GETVALUE,	"sg" },
202	{ CAP_SEM_POST,		"sp" },
203	{ CAP_SEM_WAIT,		"sw" },
204
205	/* Event monitoring and posting. */
206	{ CAP_EVENT,		"ev" },
207	{ CAP_KQUEUE_EVENT,	"ke" },
208	{ CAP_KQUEUE_CHANGE,	"kc" },
209
210	/* Strange and powerful rights that should not be given lightly. */
211	{ CAP_IOCTL,		"io" },
212	{ CAP_TTYHOOK,		"ty" },
213
214	/* Process management via process descriptors. */
215	{ CAP_PDGETPID,		"pg" },
216	{ CAP_PDWAIT,		"pw" },
217	{ CAP_PDKILL,		"pk" },
218
219	/*
220	 * Rights that allow to use bindat(2) and connectat(2) syscalls on a
221	 * directory descriptor.
222	 */
223	{ CAP_BINDAT,		"ba" },
224	{ CAP_CONNECTAT,	"ca" },
225
226	/* Aliases and defines that combine multiple rights. */
227	{ CAP_PREAD,		"prd" },
228	{ CAP_PWRITE,		"pwr" },
229
230	{ CAP_MMAP_R,		"mmr" },
231	{ CAP_MMAP_W,		"mmw" },
232	{ CAP_MMAP_X,		"mmx" },
233	{ CAP_MMAP_RW,		"mrw" },
234	{ CAP_MMAP_RX,		"mrx" },
235	{ CAP_MMAP_WX,		"mwx" },
236	{ CAP_MMAP_RWX,		"mma" },
237
238	{ CAP_RECV,		"re" },
239	{ CAP_SEND,		"sd" },
240
241	{ CAP_SOCK_CLIENT,	"scl" },
242	{ CAP_SOCK_SERVER,	"ssr" },
243};
244static const u_int	cap_desc_count = sizeof(cap_desc) /
245			    sizeof(cap_desc[0]);
246
247static u_int
248width_capability(cap_rights_t *rightsp)
249{
250	u_int count, i, width;
251
252	count = 0;
253	width = 0;
254	for (i = 0; i < cap_desc_count; i++) {
255		if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) {
256			width += strlen(cap_desc[i].cd_desc);
257			if (count)
258				width++;
259			count++;
260		}
261	}
262	return (width);
263}
264
265static void
266print_capability(cap_rights_t *rightsp, u_int capwidth)
267{
268	u_int count, i, width;
269
270	count = 0;
271	width = 0;
272	for (i = width_capability(rightsp); i < capwidth; i++) {
273		if (i != 0)
274			printf(" ");
275		else
276			printf("-");
277	}
278	for (i = 0; i < cap_desc_count; i++) {
279		if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) {
280			printf("%s%s", count ? "," : "", cap_desc[i].cd_desc);
281			width += strlen(cap_desc[i].cd_desc);
282			if (count)
283				width++;
284			count++;
285		}
286	}
287}
288
289void
290procstat_files(struct procstat *procstat, struct kinfo_proc *kipp)
291{
292	struct sockstat sock;
293	struct filestat_list *head;
294	struct filestat *fst;
295	const char *str;
296	struct vnstat vn;
297	u_int capwidth, width;
298	int error;
299
300	/*
301	 * To print the header in capability mode, we need to know the width
302	 * of the widest capability string.  Even if we get no processes
303	 * back, we will print the header, so we defer aborting due to a lack
304	 * of processes until after the header logic.
305	 */
306	capwidth = 0;
307	head = procstat_getfiles(procstat, kipp, 0);
308	if (head != NULL && Cflag) {
309		STAILQ_FOREACH(fst, head, next) {
310			width = width_capability(&fst->fs_cap_rights);
311			if (width > capwidth)
312				capwidth = width;
313		}
314		if (capwidth < strlen("CAPABILITIES"))
315			capwidth = strlen("CAPABILITIES");
316	}
317
318	if (!hflag) {
319		if (Cflag)
320			printf("%5s %-16s %4s %1s %-9s %-*s "
321			    "%-3s %-12s\n", "PID", "COMM", "FD", "T",
322			    "FLAGS", capwidth, "CAPABILITIES", "PRO",
323			    "NAME");
324		else
325			printf("%5s %-16s %4s %1s %1s %-9s "
326			    "%3s %7s %-3s %-12s\n", "PID", "COMM", "FD", "T",
327			    "V", "FLAGS", "REF", "OFFSET", "PRO", "NAME");
328	}
329
330	if (head == NULL)
331		return;
332	STAILQ_FOREACH(fst, head, next) {
333		printf("%5d ", kipp->ki_pid);
334		printf("%-16s ", kipp->ki_comm);
335		if (fst->fs_uflags & PS_FST_UFLAG_CTTY)
336			printf(" ctty ");
337		else if (fst->fs_uflags & PS_FST_UFLAG_CDIR)
338			printf("  cwd ");
339		else if (fst->fs_uflags & PS_FST_UFLAG_JAIL)
340			printf(" jail ");
341		else if (fst->fs_uflags & PS_FST_UFLAG_RDIR)
342			printf(" root ");
343		else if (fst->fs_uflags & PS_FST_UFLAG_TEXT)
344			printf(" text ");
345		else if (fst->fs_uflags & PS_FST_UFLAG_TRACE)
346			printf("trace ");
347		else
348			printf("%5d ", fst->fs_fd);
349
350		switch (fst->fs_type) {
351		case PS_FST_TYPE_VNODE:
352			str = "v";
353			break;
354
355		case PS_FST_TYPE_SOCKET:
356			str = "s";
357			break;
358
359		case PS_FST_TYPE_PIPE:
360			str = "p";
361			break;
362
363		case PS_FST_TYPE_FIFO:
364			str = "f";
365			break;
366
367		case PS_FST_TYPE_KQUEUE:
368			str = "k";
369			break;
370
371		case PS_FST_TYPE_CRYPTO:
372			str = "c";
373			break;
374
375		case PS_FST_TYPE_MQUEUE:
376			str = "m";
377			break;
378
379		case PS_FST_TYPE_SHM:
380			str = "h";
381			break;
382
383		case PS_FST_TYPE_PTS:
384			str = "t";
385			break;
386
387		case PS_FST_TYPE_SEM:
388			str = "e";
389			break;
390
391		case PS_FST_TYPE_NONE:
392		case PS_FST_TYPE_UNKNOWN:
393		default:
394			str = "?";
395			break;
396		}
397		printf("%1s ", str);
398		if (!Cflag) {
399			str = "-";
400			if (fst->fs_type == PS_FST_TYPE_VNODE) {
401				error = procstat_get_vnode_info(procstat, fst,
402				    &vn, NULL);
403				switch (vn.vn_type) {
404				case PS_FST_VTYPE_VREG:
405					str = "r";
406					break;
407
408				case PS_FST_VTYPE_VDIR:
409					str = "d";
410					break;
411
412				case PS_FST_VTYPE_VBLK:
413					str = "b";
414					break;
415
416				case PS_FST_VTYPE_VCHR:
417					str = "c";
418					break;
419
420				case PS_FST_VTYPE_VLNK:
421					str = "l";
422					break;
423
424				case PS_FST_VTYPE_VSOCK:
425					str = "s";
426					break;
427
428				case PS_FST_VTYPE_VFIFO:
429					str = "f";
430					break;
431
432				case PS_FST_VTYPE_VBAD:
433					str = "x";
434					break;
435
436				case PS_FST_VTYPE_VNON:
437				case PS_FST_VTYPE_UNKNOWN:
438				default:
439					str = "?";
440					break;
441				}
442			}
443			printf("%1s ", str);
444		}
445		printf("%s", fst->fs_fflags & PS_FST_FFLAG_READ ? "r" : "-");
446		printf("%s", fst->fs_fflags & PS_FST_FFLAG_WRITE ? "w" : "-");
447		printf("%s", fst->fs_fflags & PS_FST_FFLAG_APPEND ? "a" : "-");
448		printf("%s", fst->fs_fflags & PS_FST_FFLAG_ASYNC ? "s" : "-");
449		printf("%s", fst->fs_fflags & PS_FST_FFLAG_SYNC ? "f" : "-");
450		printf("%s", fst->fs_fflags & PS_FST_FFLAG_NONBLOCK ? "n" : "-");
451		printf("%s", fst->fs_fflags & PS_FST_FFLAG_DIRECT ? "d" : "-");
452		printf("%s", fst->fs_fflags & PS_FST_FFLAG_HASLOCK ? "l" : "-");
453		if (!Cflag) {
454			if (fst->fs_ref_count > -1)
455				printf("%3d ", fst->fs_ref_count);
456			else
457				printf("%3c ", '-');
458			if (fst->fs_offset > -1)
459				printf("%7jd ", (intmax_t)fst->fs_offset);
460			else
461				printf("%7c ", '-');
462		}
463		if (Cflag) {
464			print_capability(&fst->fs_cap_rights, capwidth);
465			printf(" ");
466		}
467		switch (fst->fs_type) {
468		case PS_FST_TYPE_SOCKET:
469			error = procstat_get_socket_info(procstat, fst, &sock, NULL);
470			if (error != 0)
471				break;
472			printf("%-3s ",
473			    protocol_to_string(sock.dom_family,
474			    sock.type, sock.proto));
475			/*
476			 * While generally we like to print two addresses,
477			 * local and peer, for sockets, it turns out to be
478			 * more useful to print the first non-nul address for
479			 * local sockets, as typically they aren't bound and
480			 *  connected, and the path strings can get long.
481			 */
482			if (sock.dom_family == AF_LOCAL) {
483				struct sockaddr_un *sun =
484				    (struct sockaddr_un *)&sock.sa_local;
485
486				if (sun->sun_path[0] != 0)
487					print_address(&sock.sa_local);
488				else
489					print_address(&sock.sa_peer);
490			} else {
491				print_address(&sock.sa_local);
492				printf(" ");
493				print_address(&sock.sa_peer);
494			}
495			break;
496
497		default:
498			printf("%-3s ", "-");
499			printf("%-18s", fst->fs_path != NULL ? fst->fs_path : "-");
500		}
501
502		printf("\n");
503	}
504	procstat_freefiles(procstat, head);
505}
506