| 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 |