Bug Summary

File:obj-scan-build/procfs/../../procfs/rootdir.c
Location:line 312, column 7
Description:The left operand of '/' is a garbage value

Annotated Source Code

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