1 /* Copyright (C) 2007 Joe Marcus Clarke
2    This file is part of LibGTop 2.
3 
4    LibGTop is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License,
7    or (at your option) any later version.
8 
9    LibGTop is distributed in the hope that it will be useful, but WITHOUT
10    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12    for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with LibGTop; see the file COPYING. If not, write to the
16    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18 */
19 
20 #include <config.h>
21 #include <glibtop/procwd.h>
22 #include <glibtop/error.h>
23 
24 #include <glibtop_private.h>
25 
26 #include <sys/types.h>
27 #include <sys/sysctl.h>
28 #include <sys/param.h>
29 #include <sys/user.h>
30 #ifdef HAVE_KINFO_GETFILE
31 #include <libutil.h>
32 #endif
33 #include <string.h>
34 
35 static const unsigned long _glibtop_sysdeps_proc_wd =
36 (1 << GLIBTOP_PROC_WD_EXE) |
37 (1 << GLIBTOP_PROC_WD_ROOT) |
38 (1 << GLIBTOP_PROC_WD_NUMBER);
39 
40 void
_glibtop_init_proc_wd_s(glibtop * server)41 _glibtop_init_proc_wd_s(glibtop *server)
42 {
43 	server->sysdeps.proc_wd = _glibtop_sysdeps_proc_wd;
44 }
45 
46 #if (__FreeBSD_version >= 800000 && __FreeBSD_version < 800019) || __FreeBSD_version < 700104
47 static GPtrArray *
parse_output(const char * output,glibtop_proc_wd * buf)48 parse_output(const char *output, glibtop_proc_wd *buf)
49 {
50 	GPtrArray *dirs;
51 	char     **lines;
52 	gboolean   nextwd = FALSE;
53 	gboolean   nextrtd = FALSE;
54 	gboolean   havertd = FALSE;
55 	guint      i;
56 	guint      len;
57 
58 	dirs = g_ptr_array_sized_new(1);
59 
60 	lines = g_strsplit(output, "\n", 0);
61 	len = g_strv_length(lines);
62 
63 	for (i = 0; i < len && lines[i]; i++) {
64 		if (strlen(lines[i]) < 2)
65 			continue;
66 
67 		if (!strcmp(lines[i], "fcwd")) {
68 			nextwd = TRUE;
69 			continue;
70 		}
71 
72 		if (!strcmp(lines[i], "frtd")) {
73 			nextrtd = TRUE;
74 			continue;
75 		}
76 
77 		if (!g_str_has_prefix(lines[i], "n"))
78 			continue;
79 
80 		if (nextwd) {
81 			g_ptr_array_add(dirs, g_strdup(lines[i] + 1));
82 			nextwd = FALSE;
83 		}
84 
85 		if (nextrtd && !havertd) {
86 			g_strlcpy(buf->root, lines[i] + 1,
87 				  sizeof(buf->root));
88 			buf->flags |= (1 << GLIBTOP_PROC_WD_ROOT);
89 			nextrtd = FALSE;
90 			havertd = TRUE;
91 		}
92 	}
93 
94 	g_strfreev(lines);
95 
96 	return dirs;
97 }
98 #endif
99 
100 char**
glibtop_get_proc_wd_s(glibtop * server,glibtop_proc_wd * buf,pid_t pid)101 glibtop_get_proc_wd_s(glibtop *server, glibtop_proc_wd *buf, pid_t pid)
102 {
103 	int exe_mib[4];
104 	size_t len;
105 #if __FreeBSD_version > 800018 || (__FreeBSD_version < 800000 && __FreeBSD_version >= 700104)
106 	struct kinfo_file *freep, *kif;
107 	GPtrArray *dirs;
108 #ifndef HAVE_KINFO_GETFILE
109 	int name[4];
110 #else
111 	int cnt;
112 #endif
113 	int i;
114 #else
115 	char *output;
116 #endif
117 
118 	memset (buf, 0, sizeof (glibtop_proc_wd));
119 	len = 0;
120 
121 	exe_mib[0] = CTL_KERN;
122 	exe_mib[1] = KERN_PROC;
123 	exe_mib[2] = KERN_PROC_PATHNAME;
124 	exe_mib[3] = pid;
125 
126 	if (sysctl(exe_mib, 4, NULL, &len, NULL, 0) == 0) {
127 		if (len > sizeof(buf->exe))
128 			len = sizeof(buf->exe);
129 		if (sysctl(exe_mib, 4, buf->exe, &len, NULL, 0) == 0)
130 			buf->flags |= (1 << GLIBTOP_PROC_WD_EXE);
131 	}
132 
133 #if __FreeBSD_version > 800018 || (__FreeBSD_version < 800000 && __FreeBSD_version >= 700104)
134 #ifndef HAVE_KINFO_GETFILE
135 	len = 0;
136 	name[0] = CTL_KERN;
137 	name[1] = KERN_PROC;
138 	name[2] = KERN_PROC_FILEDESC;
139 	name[3] = pid;
140 
141 	if (sysctl(name, 4, NULL, &len, NULL, 0) < 0)
142 		return NULL;
143 	freep = kif = g_malloc(len);
144 	if (sysctl(name, 4, kif, &len, NULL, 0) < 0) {
145 		g_free(freep);
146 		return NULL;
147 	}
148 #else
149 	freep = kinfo_getfile(pid, &cnt);
150 #endif
151 
152 	dirs = g_ptr_array_sized_new(1);
153 
154 #ifndef HAVE_KINFO_GETFILE
155 	for (i = 0; i < len / sizeof(*kif); i++, kif++) {
156 		if (kif->kf_structsize != sizeof(*kif))
157 			continue;
158 #else
159 	for (i = 0; i < cnt; i++) {
160 		kif = &freep[i];
161 #endif
162 
163 		switch (kif->kf_fd) {
164 			case KF_FD_TYPE_ROOT:
165 				g_strlcpy(buf->root, kif->kf_path,
166 					   sizeof(buf->root));
167 				buf->flags |= (1 << GLIBTOP_PROC_WD_ROOT);
168 				break;
169 			case KF_FD_TYPE_CWD:
170 				g_ptr_array_add(dirs, g_strdup (kif->kf_path));
171 				break;
172 		}
173 	}
174 	g_free(freep);
175 
176 	buf->number = dirs->len;
177 	buf->flags |= (1 << GLIBTOP_PROC_WD_NUMBER);
178 
179 	g_ptr_array_add(dirs, NULL);
180 
181 	return (char **)g_ptr_array_free(dirs, FALSE);
182 #else
183 	output = execute_lsof(pid);
184 	if (output != NULL) {
185 		GPtrArray *dirs;
186 
187 		dirs = parse_output(output, buf);
188 		g_free(output);
189 
190 		buf->number = dirs->len;
191 		buf->flags |= (1 << GLIBTOP_PROC_WD_NUMBER);
192 
193 		g_ptr_array_add(dirs, NULL);
194 
195 		return (char **)g_ptr_array_free(dirs, FALSE);
196 	}
197 #endif
198 
199 	return NULL;
200 }
201