| File: | obj-scan-build/procfs/../../procfs/rootdir.c |
| Location: | line 312, 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 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. */ | |||
| 50 | static error_t | |||
| 51 | get_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. */ | |||
| 76 | static error_t | |||
| 77 | get_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 | ||||
| 126 | out: | |||
| 127 | if (pst) _proc_stat_free (pst); | |||
| 128 | _proc_stat_free (ps); | |||
| 129 | return err; | |||
| 130 | } | |||
| 131 | ||||
| 132 | static error_t | |||
| 133 | get_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)) | |||
| 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 | ||||
| 151 | static error_t | |||
| 152 | rootdir_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 | ||||
| 168 | static error_t | |||
| 169 | rootdir_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 | ||||
| 201 | static error_t | |||
| 202 | rootdir_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 | ||||
| 243 | static error_t | |||
| 244 | rootdir_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 | ||||
| 265 | static error_t | |||
| 266 | rootdir_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) | |||
| ||||
| 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) | |||
| 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) | |||
| 286 | return err; | |||
| 287 | ||||
| 288 | err = get_swapinfo (&swap); | |||
| 289 | if (err) | |||
| 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); | |||
| ||||
| 313 | ||||
| 314 | return 0; | |||
| 315 | } | |||
| 316 | ||||
| 317 | static error_t | |||
| 318 | rootdir_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 | ||||
| 362 | static error_t | |||
| 363 | rootdir_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 | ||||
| 392 | out: | |||
| 393 | _proc_stat_free (ps); | |||
| 394 | return err; | |||
| 395 | } | |||
| 396 | ||||
| 397 | static int | |||
| 398 | rootdir_fakeself_exists (void *dir_hook, const void *entry_hook) | |||
| 399 | { | |||
| 400 | return opt_fake_self >= 0; | |||
| 401 | } | |||
| 402 | ||||
| 403 | static error_t | |||
| 404 | rootdir_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 | ||||
| 413 | static struct node *rootdir_mounts_node; | |||
| 414 | static pthread_spinlock_t rootdir_mounts_node_lock = | |||
| 415 | PTHREAD_SPINLOCK_INITIALIZER((__pthread_spinlock_t) 0); | |||
| 416 | ||||
| 417 | static struct node * | |||
| 418 | rootdir_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 | ||||
| 454 | static error_t | |||
| 455 | rootdir_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 | ||||
| 468 | static int | |||
| 469 | rootdir_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 | ||||
| 477 | static error_t | |||
| 478 | rootdir_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 | ||||
| 541 | static struct node * | |||
| 542 | rootdir_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 | ||||
| 551 | static struct node * | |||
| 552 | rootdir_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 | ||||
| 560 | static 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 | ||||
| 650 | struct 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 |