File: | obj-scan-build/procfs/../../procfs/rootdir.c |
Location: | line 313, column 7 |
Description: | The left operand of '/' is a garbage value |
1 | /* Hurd /proc filesystem, permanent files of the root directory. | |||
2 | Copyright (C) 2010,13,14 Free Software Foundation, Inc. | |||
3 | ||||
4 | This file is part of the GNU Hurd. | |||
5 | ||||
6 | The GNU Hurd is free software; you can redistribute it and/or | |||
7 | modify it under the terms of the GNU General Public License as | |||
8 | published by the Free Software Foundation; either version 2, or (at | |||
9 | your option) any later version. | |||
10 | ||||
11 | The GNU Hurd is distributed in the hope that it will be useful, but | |||
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
14 | General Public License for more details. | |||
15 | ||||
16 | You should have received a copy of the GNU General Public License | |||
17 | along with this program; if not, write to the Free Software | |||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |||
19 | ||||
20 | #include <mach/gnumach.h> | |||
21 | #include <mach/vm_param.h> | |||
22 | #include <mach/vm_statistics.h> | |||
23 | #include <mach/vm_cache_statistics.h> | |||
24 | #include <mach/default_pager.h> | |||
25 | #include <mach_debug/mach_debug_types.h> | |||
26 | #include <hurd/paths.h> | |||
27 | #include <stdio.h> | |||
28 | #include <unistd.h> | |||
29 | #include <fcntl.h> | |||
30 | #include <sys/time.h> | |||
31 | #include <sys/utsname.h> | |||
32 | #include <sys/stat.h> | |||
33 | #include <argz.h> | |||
34 | #include <ps.h> | |||
35 | #include <glob.h> | |||
36 | #include "procfs.h" | |||
37 | #include "procfs_dir.h" | |||
38 | #include "main.h" | |||
39 | ||||
40 | #include "mach_debug_U.h" | |||
41 | ||||
42 | /* This implements a directory node with the static files in /proc. | |||
43 | NB: the libps functions for host information return static storage; | |||
44 | using them would require locking and as a consequence it would be | |||
45 | more complicated, not simpler. */ | |||
46 | ||||
47 | ||||
48 | /* Helper functions */ | |||
49 | ||||
50 | /* We get the boot time by using that of the kernel process. */ | |||
51 | static error_t | |||
52 | get_boottime (struct ps_context *pc, struct timeval *tv) | |||
53 | { | |||
54 | struct proc_stat *ps; | |||
55 | error_t err; | |||
56 | ||||
57 | err = _proc_stat_create (opt_kernel_pid, pc, &ps); | |||
58 | if (err) | |||
59 | return err; | |||
60 | ||||
61 | err = proc_stat_set_flags (ps, PSTAT_TASK_BASIC0x00040); | |||
62 | if (err || !(proc_stat_flags (ps)((ps)->flags) & PSTAT_TASK_BASIC0x00040)) | |||
63 | err = EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
64 | ||||
65 | if (! err) | |||
66 | { | |||
67 | task_basic_info_t tbi = proc_stat_task_basic_info (ps)((ps)->task_basic_info); | |||
68 | tv->tv_sec = tbi->creation_time.seconds; | |||
69 | tv->tv_usec = tbi->creation_time.microseconds; | |||
70 | } | |||
71 | ||||
72 | _proc_stat_free (ps); | |||
73 | return err; | |||
74 | } | |||
75 | ||||
76 | /* We get the idle time by querying the kernel's idle thread. */ | |||
77 | static error_t | |||
78 | get_idletime (struct ps_context *pc, struct timeval *tv) | |||
79 | { | |||
80 | struct proc_stat *ps, *pst; | |||
81 | thread_basic_info_t tbi; | |||
82 | error_t err; | |||
83 | int i; | |||
84 | ||||
85 | err = _proc_stat_create (opt_kernel_pid, pc, &ps); | |||
86 | if (err) | |||
87 | return err; | |||
88 | ||||
89 | pst = NULL((void*)0), tbi = NULL((void*)0); | |||
90 | ||||
91 | err = proc_stat_set_flags (ps, PSTAT_NUM_THREADS0x00100); | |||
92 | if (err || !(proc_stat_flags (ps)((ps)->flags) & PSTAT_NUM_THREADS0x00100)) | |||
93 | { | |||
94 | err = EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
95 | goto out; | |||
96 | } | |||
97 | ||||
98 | /* Look for the idle thread */ | |||
99 | for (i=0; !tbi || !(tbi->flags & TH_FLAGS_IDLE0x2); i++) | |||
100 | { | |||
101 | if (pst) | |||
102 | _proc_stat_free (pst); | |||
103 | ||||
104 | pst = NULL((void*)0), tbi = NULL((void*)0); | |||
105 | if (i >= proc_stat_num_threads (ps)((ps)->num_threads)) | |||
106 | { | |||
107 | err = ESRCH((0x10 << 26) | ((3) & 0x3fff)); | |||
108 | goto out; | |||
109 | } | |||
110 | ||||
111 | err = proc_stat_thread_create (ps, i, &pst); | |||
112 | if (err) | |||
113 | continue; | |||
114 | ||||
115 | err = proc_stat_set_flags (pst, PSTAT_THREAD_BASIC0x00200); | |||
116 | if (err || ! (proc_stat_flags (pst)((pst)->flags) & PSTAT_THREAD_BASIC0x00200)) | |||
117 | continue; | |||
118 | ||||
119 | tbi = proc_stat_thread_basic_info (pst)((pst)->thread_basic_info); | |||
120 | } | |||
121 | ||||
122 | /* We found it! */ | |||
123 | tv->tv_sec = tbi->system_time.seconds; | |||
124 | tv->tv_usec = tbi->system_time.microseconds; | |||
125 | err = 0; | |||
126 | ||||
127 | out: | |||
128 | if (pst) _proc_stat_free (pst); | |||
129 | _proc_stat_free (ps); | |||
130 | return err; | |||
131 | } | |||
132 | ||||
133 | static error_t | |||
134 | get_swapinfo (default_pager_info_t *info) | |||
135 | { | |||
136 | mach_port_t defpager; | |||
137 | error_t err; | |||
138 | ||||
139 | defpager = file_name_lookup (_SERVERS_DEFPAGER"/servers/" "default-pager", O_READ0x0001, 0); | |||
140 | if (defpager == MACH_PORT_NULL((mach_port_t) 0)) | |||
141 | return errno(*__errno_location ()); | |||
142 | ||||
143 | err = default_pager_info (defpager, info); | |||
144 | mach_port_deallocate (mach_task_self ()((__mach_task_self_ + 0)), defpager); | |||
145 | ||||
146 | return err; | |||
147 | } | |||
148 | ||||
149 | ||||
150 | /* Content generators */ | |||
151 | ||||
152 | static error_t | |||
153 | rootdir_gc_version (void *hook, char **contents, ssize_t *contents_len) | |||
154 | { | |||
155 | struct utsname uts; | |||
156 | int r; | |||
157 | ||||
158 | r = uname (&uts); | |||
159 | if (r < 0) | |||
160 | return errno(*__errno_location ()); | |||
161 | ||||
162 | *contents_len = asprintf (contents, | |||
163 | "Linux version 2.6.1 (%s %s %s %s)\n", | |||
164 | uts.sysname, uts.release, uts.version, uts.machine); | |||
165 | ||||
166 | return 0; | |||
167 | } | |||
168 | ||||
169 | static error_t | |||
170 | rootdir_gc_uptime (void *hook, char **contents, ssize_t *contents_len) | |||
171 | { | |||
172 | struct timeval time, boottime, idletime; | |||
173 | double up_secs, idle_secs; | |||
174 | error_t err; | |||
175 | ||||
176 | err = gettimeofday (&time, NULL((void*)0)); | |||
177 | if (err < 0) | |||
178 | return errno(*__errno_location ()); | |||
179 | ||||
180 | err = get_boottime (hook, &boottime); | |||
181 | if (err) | |||
182 | return err; | |||
183 | ||||
184 | err = get_idletime (hook, &idletime); | |||
185 | if (err) | |||
186 | return err; | |||
187 | ||||
188 | timersub (&time, &boottime, &time)do { (&time)->tv_sec = (&time)->tv_sec - (& boottime)->tv_sec; (&time)->tv_usec = (&time)-> tv_usec - (&boottime)->tv_usec; if ((&time)->tv_usec < 0) { --(&time)->tv_sec; (&time)->tv_usec += 1000000; } } while (0); | |||
189 | up_secs = (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; | |||
190 | idle_secs = (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; | |||
191 | ||||
192 | /* The second field is the total idle time. As far as I know we don't | |||
193 | keep track of it. However, procps uses it to compute "USER_HZ", and | |||
194 | proc(5) specifies that it should be equal to USER_HZ times the idle value | |||
195 | in ticks from /proc/stat. So we assume a completely idle system both here | |||
196 | and there to make that work. */ | |||
197 | *contents_len = asprintf (contents, "%.2lf %.2lf\n", up_secs, idle_secs); | |||
198 | ||||
199 | return 0; | |||
200 | } | |||
201 | ||||
202 | static error_t | |||
203 | rootdir_gc_stat (void *hook, char **contents, ssize_t *contents_len) | |||
204 | { | |||
205 | struct timeval boottime, time, idletime; | |||
206 | struct vm_statistics vmstats; | |||
207 | unsigned long up_ticks, idle_ticks; | |||
208 | error_t err; | |||
209 | ||||
210 | err = gettimeofday (&time, NULL((void*)0)); | |||
211 | if (err < 0) | |||
212 | return errno(*__errno_location ()); | |||
213 | ||||
214 | err = get_boottime (hook, &boottime); | |||
215 | if (err) | |||
216 | return err; | |||
217 | ||||
218 | err = get_idletime (hook, &idletime); | |||
219 | if (err) | |||
220 | return err; | |||
221 | ||||
222 | err = vm_statistics (mach_task_self ()((__mach_task_self_ + 0)), &vmstats); | |||
223 | if (err) | |||
224 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
225 | ||||
226 | timersub (&time, &boottime, &time)do { (&time)->tv_sec = (&time)->tv_sec - (& boottime)->tv_sec; (&time)->tv_usec = (&time)-> tv_usec - (&boottime)->tv_usec; if ((&time)->tv_usec < 0) { --(&time)->tv_sec; (&time)->tv_usec += 1000000; } } while (0); | |||
227 | up_ticks = opt_clk_tck * (time.tv_sec * 1000000. + time.tv_usec) / 1000000.; | |||
228 | idle_ticks = opt_clk_tck * (idletime.tv_sec * 1000000. + idletime.tv_usec) / 1000000.; | |||
229 | ||||
230 | *contents_len = asprintf (contents, | |||
231 | "cpu %lu 0 0 %lu 0 0 0 0 0\n" | |||
232 | "cpu0 %lu 0 0 %lu 0 0 0 0 0\n" | |||
233 | "intr 0\n" | |||
234 | "page %d %d\n" | |||
235 | "btime %lu\n", | |||
236 | up_ticks - idle_ticks, idle_ticks, | |||
237 | up_ticks - idle_ticks, idle_ticks, | |||
238 | vmstats.pageins, vmstats.pageouts, | |||
239 | boottime.tv_sec); | |||
240 | ||||
241 | return 0; | |||
242 | } | |||
243 | ||||
244 | static error_t | |||
245 | rootdir_gc_loadavg (void *hook, char **contents, ssize_t *contents_len) | |||
246 | { | |||
247 | host_load_info_data_t hli; | |||
248 | mach_msg_type_number_t cnt; | |||
249 | error_t err; | |||
250 | ||||
251 | cnt = HOST_LOAD_INFO_COUNT(sizeof(host_load_info_data_t)/sizeof(integer_t)); | |||
252 | err = host_info (mach_host_self (), HOST_LOAD_INFO4, (host_info_t) &hli, &cnt); | |||
253 | if (err) | |||
254 | return err; | |||
255 | ||||
256 | assert (cnt == HOST_LOAD_INFO_COUNT)((cnt == (sizeof(host_load_info_data_t)/sizeof(integer_t))) ? (void) (0) : __assert_fail ("cnt == (sizeof(host_load_info_data_t)/sizeof(integer_t))" , "../../procfs/rootdir.c", 256, __PRETTY_FUNCTION__)); | |||
257 | *contents_len = asprintf (contents, | |||
258 | "%.2f %.2f %.2f 1/0 0\n", | |||
259 | hli.avenrun[0] / (double) LOAD_SCALE1000, | |||
260 | hli.avenrun[1] / (double) LOAD_SCALE1000, | |||
261 | hli.avenrun[2] / (double) LOAD_SCALE1000); | |||
262 | ||||
263 | return 0; | |||
264 | } | |||
265 | ||||
266 | static error_t | |||
267 | rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) | |||
268 | { | |||
269 | host_basic_info_data_t hbi; | |||
270 | mach_msg_type_number_t cnt; | |||
271 | struct vm_statistics vmstats; | |||
272 | struct vm_cache_statistics cache_stats; | |||
273 | default_pager_info_t swap; | |||
274 | error_t err; | |||
275 | ||||
276 | err = vm_statistics (mach_task_self ()((__mach_task_self_ + 0)), &vmstats); | |||
277 | if (err) | |||
| ||||
278 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
279 | ||||
280 | err = vm_cache_statistics (mach_task_self ()((__mach_task_self_ + 0)), &cache_stats); | |||
281 | if (err) | |||
282 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
283 | ||||
284 | cnt = HOST_BASIC_INFO_COUNT(sizeof(host_basic_info_data_t)/sizeof(integer_t)); | |||
285 | err = host_info (mach_host_self (), HOST_BASIC_INFO1, (host_info_t) &hbi, &cnt); | |||
286 | if (err) | |||
287 | return err; | |||
288 | ||||
289 | err = get_swapinfo (&swap); | |||
290 | if (err) | |||
291 | return err; | |||
292 | ||||
293 | assert (cnt == HOST_BASIC_INFO_COUNT)((cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))) ? (void) (0) : __assert_fail ("cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))" , "../../procfs/rootdir.c", 293, __PRETTY_FUNCTION__)); | |||
294 | *contents_len = asprintf (contents, | |||
295 | "MemTotal: %14lu kB\n" | |||
296 | "MemFree: %14lu kB\n" | |||
297 | "Buffers: %14lu kB\n" | |||
298 | "Cached: %14lu kB\n" | |||
299 | "Active: %14lu kB\n" | |||
300 | "Inactive: %14lu kB\n" | |||
301 | "Mlocked: %14lu kB\n" | |||
302 | "SwapTotal:%14lu kB\n" | |||
303 | "SwapFree: %14lu kB\n" | |||
304 | , | |||
305 | (long unsigned) hbi.memory_size / 1024, | |||
306 | (long unsigned) vmstats.free_count * PAGE_SIZE(1 << 12) / 1024, | |||
307 | 0UL, | |||
308 | (long unsigned) cache_stats.cache_count * PAGE_SIZE(1 << 12) / 1024, | |||
309 | (long unsigned) vmstats.active_count * PAGE_SIZE(1 << 12) / 1024, | |||
310 | (long unsigned) vmstats.inactive_count * PAGE_SIZE(1 << 12) / 1024, | |||
311 | (long unsigned) vmstats.wire_count * PAGE_SIZE(1 << 12) / 1024, | |||
312 | (long unsigned) swap.dpi_total_space / 1024, | |||
313 | (long unsigned) swap.dpi_free_space / 1024); | |||
| ||||
314 | ||||
315 | return 0; | |||
316 | } | |||
317 | ||||
318 | static error_t | |||
319 | rootdir_gc_vmstat (void *hook, char **contents, ssize_t *contents_len) | |||
320 | { | |||
321 | host_basic_info_data_t hbi; | |||
322 | mach_msg_type_number_t cnt; | |||
323 | struct vm_statistics vmstats; | |||
324 | error_t err; | |||
325 | ||||
326 | err = vm_statistics (mach_task_self ()((__mach_task_self_ + 0)), &vmstats); | |||
327 | if (err) | |||
328 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
329 | ||||
330 | cnt = HOST_BASIC_INFO_COUNT(sizeof(host_basic_info_data_t)/sizeof(integer_t)); | |||
331 | err = host_info (mach_host_self (), HOST_BASIC_INFO1, (host_info_t) &hbi, &cnt); | |||
332 | if (err) | |||
333 | return err; | |||
334 | ||||
335 | assert (cnt == HOST_BASIC_INFO_COUNT)((cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))) ? (void) (0) : __assert_fail ("cnt == (sizeof(host_basic_info_data_t)/sizeof(integer_t))" , "../../procfs/rootdir.c", 335, __PRETTY_FUNCTION__)); | |||
336 | *contents_len = asprintf (contents, | |||
337 | "nr_free_pages %lu\n" | |||
338 | "nr_inactive_anon %lu\n" | |||
339 | "nr_active_anon %lu\n" | |||
340 | "nr_inactive_file %lu\n" | |||
341 | "nr_active_file %lu\n" | |||
342 | "nr_unevictable %lu\n" | |||
343 | "nr_mlock %lu\n" | |||
344 | "pgpgin %lu\n" | |||
345 | "pgpgout %lu\n" | |||
346 | "pgfault %lu\n", | |||
347 | (long unsigned) vmstats.free_count, | |||
348 | /* FIXME: how can we distinguish the anon/file pages? Maybe we can | |||
349 | ask the default pager how many it manages? */ | |||
350 | (long unsigned) vmstats.inactive_count, | |||
351 | (long unsigned) vmstats.active_count, | |||
352 | (long unsigned) 0, | |||
353 | (long unsigned) 0, | |||
354 | (long unsigned) vmstats.wire_count, | |||
355 | (long unsigned) vmstats.wire_count, | |||
356 | (long unsigned) vmstats.pageins, | |||
357 | (long unsigned) vmstats.pageouts, | |||
358 | (long unsigned) vmstats.faults); | |||
359 | ||||
360 | return 0; | |||
361 | } | |||
362 | ||||
363 | static error_t | |||
364 | rootdir_gc_cmdline (void *hook, char **contents, ssize_t *contents_len) | |||
365 | { | |||
366 | struct ps_context *pc = hook; | |||
367 | struct proc_stat *ps; | |||
368 | error_t err; | |||
369 | ||||
370 | err = _proc_stat_create (opt_kernel_pid, pc, &ps); | |||
371 | if (err) | |||
372 | return EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
373 | ||||
374 | err = proc_stat_set_flags (ps, PSTAT_ARGS0x02000); | |||
375 | if (err || ! (proc_stat_flags (ps)((ps)->flags) & PSTAT_ARGS0x02000)) | |||
376 | { | |||
377 | err = EIO((0x10 << 26) | ((5) & 0x3fff)); | |||
378 | goto out; | |||
379 | } | |||
380 | ||||
381 | *contents_len = proc_stat_args_len (ps)((ps)->args_len); | |||
382 | *contents = malloc (*contents_len); | |||
383 | if (! *contents) | |||
384 | { | |||
385 | err = ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
386 | goto out; | |||
387 | } | |||
388 | ||||
389 | memcpy (*contents, proc_stat_args (ps)((ps)->args), *contents_len); | |||
390 | argz_stringify (*contents, *contents_len, ' '); | |||
391 | (*contents)[*contents_len - 1] = '\n'; | |||
392 | ||||
393 | out: | |||
394 | _proc_stat_free (ps); | |||
395 | return err; | |||
396 | } | |||
397 | ||||
398 | static int | |||
399 | rootdir_fakeself_exists (void *dir_hook, const void *entry_hook) | |||
400 | { | |||
401 | return opt_fake_self >= 0; | |||
402 | } | |||
403 | ||||
404 | static error_t | |||
405 | rootdir_gc_fakeself (void *hook, char **contents, ssize_t *contents_len) | |||
406 | { | |||
407 | *contents_len = asprintf (contents, "%d", opt_fake_self); | |||
408 | return 0; | |||
409 | } | |||
410 | ||||
411 | static struct node *rootdir_mounts_node; | |||
412 | ||||
413 | static error_t | |||
414 | rootdir_gc_slabinfo (void *hook, char **contents, ssize_t *contents_len) | |||
415 | { | |||
416 | error_t err; | |||
417 | FILE *m; | |||
418 | const char header[] = | |||
419 | "cache obj slab bufs objs bufs" | |||
420 | " total reclaimable\n" | |||
421 | "name flags size size /slab usage count" | |||
422 | " memory memory\n"; | |||
423 | cache_info_array_t cache_info; | |||
424 | size_t mem_usage, mem_reclaimable, mem_total, mem_total_reclaimable; | |||
425 | mach_msg_type_number_t cache_info_count; | |||
426 | int i; | |||
427 | ||||
428 | cache_info = NULL((void*)0); | |||
429 | cache_info_count = 0; | |||
430 | ||||
431 | err = host_slab_info (mach_host_self(), &cache_info, &cache_info_count); | |||
432 | if (err) | |||
433 | return err; | |||
434 | ||||
435 | m = open_memstream (contents, contents_len); | |||
436 | if (m == NULL((void*)0)) | |||
437 | { | |||
438 | err = ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
439 | goto out; | |||
440 | } | |||
441 | ||||
442 | fprintf (m, "%s", header); | |||
443 | ||||
444 | mem_total = 0; | |||
445 | mem_total_reclaimable = 0; | |||
446 | ||||
447 | for (i = 0; i < cache_info_count; i++) | |||
448 | { | |||
449 | mem_usage = (cache_info[i].nr_slabs * cache_info[i].slab_size) | |||
450 | >> 10; | |||
451 | mem_total += mem_usage; | |||
452 | mem_reclaimable = (cache_info[i].flags & CACHE_FLAGS_NO_RECLAIM0x04) | |||
453 | ? 0 : (cache_info[i].nr_free_slabs | |||
454 | * cache_info[i].slab_size) >> 10; | |||
455 | mem_total_reclaimable += mem_reclaimable; | |||
456 | fprintf (m, | |||
457 | "%-21s %04x %7zu %3zuk %4lu %6lu %6lu %7zuk %10zuk\n", | |||
458 | cache_info[i].name, cache_info[i].flags, | |||
459 | cache_info[i].obj_size, cache_info[i].slab_size >> 10, | |||
460 | cache_info[i].bufs_per_slab, cache_info[i].nr_objs, | |||
461 | cache_info[i].nr_bufs, mem_usage, mem_reclaimable); | |||
462 | } | |||
463 | ||||
464 | fprintf (m, "total: %zuk, reclaimable: %zuk\n", | |||
465 | mem_total, mem_total_reclaimable); | |||
466 | ||||
467 | fclose (m); | |||
468 | ||||
469 | out: | |||
470 | vm_deallocate (mach_task_self ()((__mach_task_self_ + 0)), | |||
471 | cache_info, cache_info_count * sizeof *cache_info); | |||
472 | return err; | |||
473 | } | |||
474 | ||||
475 | static error_t | |||
476 | rootdir_gc_filesystems (void *hook, char **contents, ssize_t *contents_len) | |||
477 | { | |||
478 | error_t err = 0; | |||
479 | size_t i; | |||
480 | int glob_ret; | |||
481 | glob_t matches; | |||
482 | FILE *m; | |||
483 | ||||
484 | m = open_memstream (contents, contents_len); | |||
485 | if (m == NULL((void*)0)) | |||
486 | return errno(*__errno_location ()); | |||
487 | ||||
488 | glob_ret = glob (_HURD"/hurd/" "*fs", 0, NULL((void*)0), &matches); | |||
489 | switch (glob_ret) | |||
490 | { | |||
491 | case 0: | |||
492 | for (i = 0; i < matches.gl_pathc; i++) | |||
493 | { | |||
494 | /* Get ith entry, shave off the prefix. */ | |||
495 | char *name = &matches.gl_pathv[i][sizeof _HURD"/hurd/" - 1]; | |||
496 | ||||
497 | /* Linux naming convention is a bit inconsistent. */ | |||
498 | if (strncmp (name, "ext", 3) == 0 | |||
499 | || strcmp (name, "procfs") == 0) | |||
500 | /* Drop the fs suffix. */ | |||
501 | name[strlen (name) - 2] = 0; | |||
502 | ||||
503 | fprintf (m, "\t%s\n", name); | |||
504 | } | |||
505 | ||||
506 | globfree (&matches); | |||
507 | break; | |||
508 | ||||
509 | case GLOB_NOMATCH3: | |||
510 | /* Poor fellow. */ | |||
511 | break; | |||
512 | ||||
513 | case GLOB_NOSPACE1: | |||
514 | err = ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
515 | break; | |||
516 | ||||
517 | default: | |||
518 | /* This should not happen. */ | |||
519 | err = EGRATUITOUS((0x10 << 26) | ((105) & 0x3fff)); | |||
520 | } | |||
521 | ||||
522 | fclose (m); | |||
523 | return err; | |||
524 | } | |||
525 | ||||
526 | /* Glue logic and entries table */ | |||
527 | ||||
528 | static struct node * | |||
529 | rootdir_file_make_node (void *dir_hook, const void *entry_hook) | |||
530 | { | |||
531 | /* The entry hook we use is actually a procfs_node_ops for the file to be | |||
532 | created. The hook associated to these newly created files (and passed | |||
533 | to the generators above as a consequence) is always the same global | |||
534 | ps_context, which we get from rootdir_make_node as the directory hook. */ | |||
535 | return procfs_make_node (entry_hook, dir_hook); | |||
536 | } | |||
537 | ||||
538 | static struct node * | |||
539 | rootdir_symlink_make_node (void *dir_hook, const void *entry_hook) | |||
540 | { | |||
541 | struct node *np = procfs_make_node (entry_hook, dir_hook); | |||
542 | if (np) | |||
543 | procfs_node_chtype (np, S_IFLNK0120000); | |||
544 | return np; | |||
545 | } | |||
546 | ||||
547 | /* Translator linkage. */ | |||
548 | ||||
549 | static pthread_spinlock_t rootdir_translated_node_lock = | |||
550 | PTHREAD_SPINLOCK_INITIALIZER((__pthread_spinlock_t) 0); | |||
551 | ||||
552 | struct procfs_translated_node_ops | |||
553 | { | |||
554 | struct procfs_node_ops node_ops; | |||
555 | ||||
556 | struct node **npp; | |||
557 | char *argz; | |||
558 | size_t argz_len; | |||
559 | }; | |||
560 | ||||
561 | static struct node * | |||
562 | rootdir_make_translated_node (void *dir_hook, const void *entry_hook) | |||
563 | { | |||
564 | const struct procfs_translated_node_ops *ops = entry_hook; | |||
565 | struct node *np, *prev; | |||
566 | ||||
567 | pthread_spin_lock (&rootdir_translated_node_lock); | |||
568 | np = *ops->npp; | |||
569 | pthread_spin_unlock (&rootdir_translated_node_lock); | |||
570 | ||||
571 | if (np != NULL((void*)0)) | |||
572 | { | |||
573 | netfs_nref (np); | |||
574 | return np; | |||
575 | } | |||
576 | ||||
577 | np = procfs_make_node (entry_hook, entry_hook); | |||
578 | if (np == NULL((void*)0)) | |||
579 | return NULL((void*)0); | |||
580 | ||||
581 | procfs_node_chtype (np, S_IFREG0100000 | S_IPTRANS000010000000); | |||
582 | procfs_node_chmod (np, 0444); | |||
583 | ||||
584 | pthread_spin_lock (&rootdir_translated_node_lock); | |||
585 | prev = *ops->npp; | |||
586 | if (*ops->npp == NULL((void*)0)) | |||
587 | *ops->npp = np; | |||
588 | pthread_spin_unlock (&rootdir_translated_node_lock); | |||
589 | ||||
590 | if (prev != NULL((void*)0)) | |||
591 | { | |||
592 | procfs_cleanup (np); | |||
593 | np = prev; | |||
594 | } | |||
595 | ||||
596 | return np; | |||
597 | } | |||
598 | ||||
599 | static error_t | |||
600 | rootdir_translated_node_get_translator (void *hook, char **argz, | |||
601 | size_t *argz_len) | |||
602 | { | |||
603 | const struct procfs_translated_node_ops *ops = hook; | |||
604 | ||||
605 | *argz = malloc (ops->argz_len); | |||
606 | if (! *argz) | |||
607 | return ENOMEM((0x10 << 26) | ((12) & 0x3fff)); | |||
608 | ||||
609 | memcpy (*argz, ops->argz, ops->argz_len); | |||
610 | *argz_len = ops->argz_len; | |||
611 | return 0; | |||
612 | } | |||
613 | ||||
614 | #define ROOTDIR_DEFINE_TRANSLATED_NODE(NPP, ARGZ)&(struct procfs_translated_node_ops) { .node_ops = { .get_translator = rootdir_translated_node_get_translator, }, .npp = NPP, .argz = (ARGZ), .argz_len = sizeof (ARGZ), } \ | |||
615 | &(struct procfs_translated_node_ops) { \ | |||
616 | .node_ops = { \ | |||
617 | .get_translator = rootdir_translated_node_get_translator, \ | |||
618 | }, \ | |||
619 | .npp = NPP, \ | |||
620 | .argz = (ARGZ), \ | |||
621 | .argz_len = sizeof (ARGZ), \ | |||
622 | } | |||
623 | ||||
624 | static const struct procfs_dir_entry rootdir_entries[] = { | |||
625 | { | |||
626 | .name = "self", | |||
627 | .hook = & (struct procfs_node_ops) { | |||
628 | .get_contents = rootdir_gc_fakeself, | |||
629 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
630 | }, | |||
631 | .ops = { | |||
632 | .make_node = rootdir_symlink_make_node, | |||
633 | .exists = rootdir_fakeself_exists, | |||
634 | } | |||
635 | }, | |||
636 | { | |||
637 | .name = "version", | |||
638 | .hook = & (struct procfs_node_ops) { | |||
639 | .get_contents = rootdir_gc_version, | |||
640 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
641 | }, | |||
642 | }, | |||
643 | { | |||
644 | .name = "uptime", | |||
645 | .hook = & (struct procfs_node_ops) { | |||
646 | .get_contents = rootdir_gc_uptime, | |||
647 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
648 | }, | |||
649 | }, | |||
650 | { | |||
651 | .name = "stat", | |||
652 | .hook = & (struct procfs_node_ops) { | |||
653 | .get_contents = rootdir_gc_stat, | |||
654 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
655 | }, | |||
656 | }, | |||
657 | { | |||
658 | .name = "loadavg", | |||
659 | .hook = & (struct procfs_node_ops) { | |||
660 | .get_contents = rootdir_gc_loadavg, | |||
661 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
662 | }, | |||
663 | }, | |||
664 | { | |||
665 | .name = "meminfo", | |||
666 | .hook = & (struct procfs_node_ops) { | |||
667 | .get_contents = rootdir_gc_meminfo, | |||
668 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
669 | }, | |||
670 | }, | |||
671 | { | |||
672 | .name = "vmstat", | |||
673 | .hook = & (struct procfs_node_ops) { | |||
674 | .get_contents = rootdir_gc_vmstat, | |||
675 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
676 | }, | |||
677 | }, | |||
678 | { | |||
679 | .name = "cmdline", | |||
680 | .hook = & (struct procfs_node_ops) { | |||
681 | .get_contents = rootdir_gc_cmdline, | |||
682 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
683 | }, | |||
684 | }, | |||
685 | { | |||
686 | .name = "mounts", | |||
687 | .hook = ROOTDIR_DEFINE_TRANSLATED_NODE (&rootdir_mounts_node,&(struct procfs_translated_node_ops) { .node_ops = { .get_translator = rootdir_translated_node_get_translator, }, .npp = &rootdir_mounts_node , .argz = ("/hurd/" "mtab" "\0/"), .argz_len = sizeof ("/hurd/" "mtab" "\0/"), } | |||
688 | _HURD_MTAB "\0/")&(struct procfs_translated_node_ops) { .node_ops = { .get_translator = rootdir_translated_node_get_translator, }, .npp = &rootdir_mounts_node , .argz = ("/hurd/" "mtab" "\0/"), .argz_len = sizeof ("/hurd/" "mtab" "\0/"), }, | |||
689 | .ops = { | |||
690 | .make_node = rootdir_make_translated_node, | |||
691 | } | |||
692 | }, | |||
693 | { | |||
694 | .name = "slabinfo", | |||
695 | .hook = & (struct procfs_node_ops) { | |||
696 | .get_contents = rootdir_gc_slabinfo, | |||
697 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
698 | }, | |||
699 | }, | |||
700 | { | |||
701 | .name = "filesystems", | |||
702 | .hook = & (struct procfs_node_ops) { | |||
703 | .get_contents = rootdir_gc_filesystems, | |||
704 | .cleanup_contents = procfs_cleanup_contents_with_free, | |||
705 | }, | |||
706 | }, | |||
707 | #ifdef PROFILE | |||
708 | /* In order to get a usable gmon.out file, we must apparently use exit(). */ | |||
709 | { | |||
710 | .name = "exit", | |||
711 | .ops = { | |||
712 | .make_node = exit, | |||
713 | }, | |||
714 | }, | |||
715 | #endif | |||
716 | {} | |||
717 | }; | |||
718 | ||||
719 | struct node | |||
720 | *rootdir_make_node (struct ps_context *pc) | |||
721 | { | |||
722 | static const struct procfs_dir_ops ops = { | |||
723 | .entries = rootdir_entries, | |||
724 | .entry_ops = { | |||
725 | .make_node = rootdir_file_make_node, | |||
726 | }, | |||
727 | }; | |||
728 | return procfs_dir_make_node (&ops, pc); | |||
729 | } | |||
730 |