/src/net-snmp/agent/kernel.c
Line | Count | Source |
1 | | /*********************************************************************** |
2 | | Net-SNMP - Simple Network Management Protocol agent library. |
3 | | ***********************************************************************/ |
4 | | /** @file kernel.c |
5 | | * Net-SNMP Kernel Data Access Library. |
6 | | * Provides access to kernel virtual memory for systems that |
7 | | * support it. |
8 | | * @author See README file for a list of contributors |
9 | | */ |
10 | | /* Copyrights: |
11 | | * Copyright holders are listed in README file. |
12 | | * Redistribution and use in source and binary forms, with or |
13 | | * without modification, are permitted. License terms are specified |
14 | | * in COPYING file distributed with the Net-SNMP package. |
15 | | */ |
16 | | /***********************************************************************/ |
17 | | |
18 | | #include <net-snmp/net-snmp-config.h> |
19 | | |
20 | | #include <sys/types.h> |
21 | | #ifdef HAVE_STDLIB_H |
22 | | #include <stdlib.h> |
23 | | #endif |
24 | | #ifdef HAVE_UNISTD_H |
25 | | #include <unistd.h> |
26 | | #endif |
27 | | #include <stdio.h> |
28 | | #include <errno.h> |
29 | | #ifdef HAVE_STRING_H |
30 | | #include <string.h> |
31 | | #endif |
32 | | #ifdef HAVE_FCNTL_H |
33 | | #include <fcntl.h> |
34 | | #endif |
35 | | #ifdef HAVE_NETINET_IN_H |
36 | | #include <netinet/in.h> |
37 | | #endif |
38 | | #ifdef HAVE_KVM_H |
39 | | #include <kvm.h> |
40 | | #endif |
41 | | |
42 | | #include <net-snmp/net-snmp-includes.h> |
43 | | |
44 | | #include "kernel.h" |
45 | | #include <net-snmp/agent/ds_agent.h> |
46 | | |
47 | | #if defined(HAVE_KVM_H) && !defined(NETSNMP_NO_KMEM_USAGE) && !defined(__FreeBSD__) |
48 | | kvm_t *kd; |
49 | | |
50 | | /** |
51 | | * Initialize the support for accessing kernel virtual memory. |
52 | | * |
53 | | * @return TRUE upon success; FALSE upon failure. |
54 | | */ |
55 | | int |
56 | | init_kmem(const char *file) |
57 | | { |
58 | | int res = TRUE; |
59 | | |
60 | | #ifdef HAVE_KVM_OPENFILES |
61 | | char err[4096]; |
62 | | |
63 | | kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, err); |
64 | | if (!kd) |
65 | | #ifdef KVM_NO_FILES |
66 | | kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, err); |
67 | | #else |
68 | | kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, err); |
69 | | #endif |
70 | | if (!kd && !netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
71 | | NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { |
72 | | snmp_log(LOG_CRIT, "init_kmem: kvm_openfiles failed: %s\n", err); |
73 | | res = FALSE; |
74 | | } |
75 | | #else |
76 | | kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); |
77 | | if (!kd && !netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
78 | | NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { |
79 | | snmp_log(LOG_CRIT, "init_kmem: kvm_open failed: %s\n", |
80 | | strerror(errno)); |
81 | | res = FALSE; |
82 | | } |
83 | | #endif /* HAVE_KVM_OPENFILES */ |
84 | | return res; |
85 | | } |
86 | | |
87 | | /** Reads kernel memory. |
88 | | * Seeks to the specified location in kmem, then |
89 | | * does a read of given amount ob bytes into target buffer. |
90 | | * |
91 | | * @param off The location to seek. |
92 | | * |
93 | | * @param target The target buffer to read into. |
94 | | * |
95 | | * @param siz Number of bytes to read. |
96 | | * |
97 | | * @return gives 1 on success and 0 on failure. |
98 | | */ |
99 | | int |
100 | | klookup(unsigned long off, void *target, size_t siz) |
101 | | { |
102 | | int result; |
103 | | |
104 | | if (kd == NULL) |
105 | | return 0; |
106 | | result = kvm_read(kd, off, target, siz); |
107 | | if (result != siz) { |
108 | | #ifdef HAVE_KVM_OPENFILES |
109 | | snmp_log(LOG_ERR, "kvm_read(*, %lx, %p, %x) = %d: %s\n", off, |
110 | | target, (unsigned) siz, result, kvm_geterr(kd)); |
111 | | #else |
112 | | snmp_log(LOG_ERR, "kvm_read(*, %lx, %p, %d) = %d: ", off, target, |
113 | | (unsigned) siz, result); |
114 | | snmp_log_perror("klookup"); |
115 | | #endif |
116 | | return 0; |
117 | | } |
118 | | return 1; |
119 | | } |
120 | | |
121 | | /** Closes the kernel memory support. |
122 | | */ |
123 | | void |
124 | | free_kmem(void) |
125 | | { |
126 | | if (kd != NULL) |
127 | | { |
128 | | kvm_close(kd); |
129 | | kd = NULL; |
130 | | } |
131 | | } |
132 | | |
133 | | #elif defined(HAVE_NLIST_H) && !defined(__linux__) && !defined(__FreeBSD__) && \ |
134 | | !defined(NETSNMP_NO_KMEM_USAGE) |
135 | | |
136 | | static off_t klseek(off_t); |
137 | | static int klread(char *, int); |
138 | | int swap = -1, mem = -1, kmem = -1; |
139 | | |
140 | | static void netsnmp_cloexec(int fd) |
141 | | { |
142 | | if (fd < 0) |
143 | | return; |
144 | | #ifdef HAVE_FD_CLOEXEC |
145 | | if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) |
146 | | snmp_log_perror("fcntl(FD_CLOEXEC)"); |
147 | | #endif |
148 | | } |
149 | | |
150 | | /** |
151 | | * Initialize the support for accessing kernel virtual memory. |
152 | | * |
153 | | * @return TRUE upon success; FALSE upon failure. |
154 | | */ |
155 | | int |
156 | | init_kmem(const char *file) |
157 | | { |
158 | | const int no_root_access = netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, |
159 | | NETSNMP_DS_AGENT_NO_ROOT_ACCESS); |
160 | | int res = TRUE; |
161 | | |
162 | | kmem = open(file, O_RDONLY); |
163 | | if (kmem < 0 && !no_root_access) { |
164 | | snmp_log_perror(file); |
165 | | res = FALSE; |
166 | | } |
167 | | netsnmp_cloexec(kmem); |
168 | | mem = open("/dev/mem", O_RDONLY); |
169 | | if (mem < 0 && !no_root_access) { |
170 | | snmp_log_perror("/dev/mem"); |
171 | | res = FALSE; |
172 | | } |
173 | | netsnmp_cloexec(mem); |
174 | | #ifdef DMEM_LOC |
175 | | swap = open(DMEM_LOC, O_RDONLY); |
176 | | if (swap < 0 && !no_root_access) { |
177 | | snmp_log_perror(DMEM_LOC); |
178 | | res = FALSE; |
179 | | } |
180 | | netsnmp_cloexec(swap); |
181 | | #endif |
182 | | return res; |
183 | | } |
184 | | |
185 | | /** @private |
186 | | * Seek into the kernel for a value. |
187 | | */ |
188 | | static off_t |
189 | | klseek(off_t base) |
190 | | { |
191 | | return (lseek(kmem, (off_t) base, SEEK_SET)); |
192 | | } |
193 | | |
194 | | /** @private |
195 | | * Read from the kernel. |
196 | | */ |
197 | | static int |
198 | | klread(char *buf, int buflen) |
199 | | { |
200 | | return (read(kmem, buf, buflen)); |
201 | | } |
202 | | |
203 | | /** Reads kernel memory. |
204 | | * Seeks to the specified location in kmem, then |
205 | | * does a read of given amount ob bytes into target buffer. |
206 | | * |
207 | | * @param off The location to seek. |
208 | | * |
209 | | * @param target The target buffer to read into. |
210 | | * |
211 | | * @param siz Number of bytes to read. |
212 | | * |
213 | | * @return gives 1 on success and 0 on failure. |
214 | | */ |
215 | | int |
216 | | klookup(unsigned long off, void *target, size_t siz) |
217 | | { |
218 | | long retsiz; |
219 | | |
220 | | if (kmem < 0) |
221 | | return 0; |
222 | | |
223 | | if ((retsiz = klseek((off_t) off)) != off) { |
224 | | snmp_log(LOG_ERR, "klookup(%lx, %p, %d): ", off, target, (int) siz); |
225 | | snmp_log_perror("klseek"); |
226 | | return (0); |
227 | | } |
228 | | if ((retsiz = klread(target, siz)) != siz) { |
229 | | if (snmp_get_do_debugging()) { |
230 | | /* |
231 | | * these happen too often on too many architectures to print them |
232 | | * unless we're in debugging mode. People get very full log files. |
233 | | */ |
234 | | snmp_log(LOG_ERR, "klookup(%lx, %p, %d): ", off, target, (int) siz); |
235 | | snmp_log_perror("klread"); |
236 | | } |
237 | | return (0); |
238 | | } |
239 | | DEBUGMSGTL(("verbose:kernel:klookup", "klookup(%lx, %p, %d) succeeded", |
240 | | off, target, (int) siz)); |
241 | | return (1); |
242 | | } |
243 | | |
244 | | /** Closes the kernel memory support. |
245 | | */ |
246 | | void |
247 | | free_kmem(void) |
248 | | { |
249 | | if (swap >= 0) { |
250 | | close(swap); |
251 | | swap = -1; |
252 | | } |
253 | | if (mem >= 0) { |
254 | | close(mem); |
255 | | mem = -1; |
256 | | } |
257 | | if (kmem >= 0) { |
258 | | close(kmem); |
259 | | kmem = -1; |
260 | | } |
261 | | } |
262 | | #elif defined(__FreeBSD__) |
263 | | kvm_t *kd; |
264 | | |
265 | | /** |
266 | | * Initialize the libkvm descriptor. On FreeBSD we can use most of libkvm |
267 | | * without requiring /dev/kmem access. Only kvm_nlist() and kvm_read() need |
268 | | * that, and we don't use them. |
269 | | * |
270 | | * @return TRUE upon success; FALSE upon failure. |
271 | | */ |
272 | | int |
273 | | init_kmem(const char *file) |
274 | | { |
275 | | char err[4096]; |
276 | | |
277 | | kd = kvm_openfiles(NULL, "/dev/null", NULL, O_RDONLY, err); |
278 | | if (!kd) { |
279 | | snmp_log(LOG_CRIT, "init_kmem: kvm_openfiles failed: %s\n", err); |
280 | | return FALSE; |
281 | | } |
282 | | return TRUE; |
283 | | } |
284 | | |
285 | | /** |
286 | | * A stub to return failure to any attempt to read kernel memory. Our |
287 | | * libkvm handle doesn't enable /dev/kmem access. MIB implementations should |
288 | | * use unprivileged to fetch information about the system. |
289 | | */ |
290 | | int |
291 | | klookup(unsigned long off, void *target, size_t siz) |
292 | | { |
293 | | return 0; |
294 | | } |
295 | | |
296 | | void |
297 | | free_kmem(void) |
298 | | { |
299 | | if (kd != NULL) { |
300 | | (void)kvm_close(kd); |
301 | | kd = NULL; |
302 | | } |
303 | | } |
304 | | #else |
305 | | int |
306 | | init_kmem(const char *file) |
307 | 0 | { |
308 | 0 | return 1; /* success */ |
309 | 0 | } |
310 | | |
311 | | void |
312 | | free_kmem(void) |
313 | 0 | { |
314 | 0 | } |
315 | | #endif |