diff options
Diffstat (limited to 'src/procenv.c')
-rw-r--r-- | src/procenv.c | 7909 |
1 files changed, 6839 insertions, 1070 deletions
diff --git a/src/procenv.c b/src/procenv.c index a1c17cc..966eef5 100644 --- a/src/procenv.c +++ b/src/procenv.c @@ -4,12 +4,12 @@ * * Date: 24 October 2012. * - * Author: James Hunt <james.hunt@ubuntu.com>. + * Author: James Hunt <jamesodhunt@ubuntu.com>. * * Licence: GPLv3. See below... *-------------------------------------------------------------------- * - * Copyright 2012-2013 James Hunt. + * Copyright © 2012-2015 James Hunt <jamesodhunt@ubuntu.com>. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,12 @@ #include <procenv.h> -extern char **environ; +/** + * doc: + * + * The output document in wide-character format. + **/ +pstring *doc = NULL; /** * output: @@ -38,6 +43,13 @@ extern char **environ; Output output = OUTPUT_STDOUT; /** + * output_format: + * + * Format output will be displayed in. + **/ +OutputFormat output_format = OUTPUT_FORMAT_TEXT; + +/** * output_file: * * Name or output file to send output to if not NULL. @@ -45,6 +57,22 @@ Output output = OUTPUT_STDOUT; const char *output_file = NULL; /** + * text_separator: + * + * Separator used for text output format to separate a name from a + * value. + **/ +const char *text_separator = PROCENV_DEFAULT_TEXT_SEPARATOR; + +/** + * crumb_separator: + * + * Separator used for text output format to separate a name from a + * value. + **/ +const char *crumb_separator = PROCENV_DEFAULT_CRUMB_SEPARATOR; + +/** * output_fd: * * File descriptor associated with output_file. @@ -70,11 +98,26 @@ int selected_option = 0; /** * indent: * - * Number of spaces to indent output. + * Current output indent value. **/ int indent = 0; /** + * indent_amount: + * + * Number of INDENT_CHARs to emit for an indent. + **/ +int indent_amount = DEFAULT_INDENT_AMOUNT; + +/** + * indent_char, wide_indent_char: + * + * Character to use for indenting and wide-char equivalent. + **/ +const char *indent_char = DEFAULT_INDENT_CHAR; +wchar_t wide_indent_char; + +/** * program_name: * * Name of program. @@ -94,13 +137,35 @@ char **exec_args = NULL; char **argvp = NULL; int argvc = 0; +/** + * Locale in effect at program startup. + **/ +char *saved_locale = NULL; + +/** + * last_element: Type of previous element handled. + **/ +ElementType last_element = ELEMENT_TYPE_NONE; + +/** + * current_element: Type of element currently being handled. + **/ +ElementType current_element = ELEMENT_TYPE_NONE; + +/** + * crumb_list: + * + * List used to store breadcrumbs when OUTPUT_FORMAT_CRUMB being used. + **/ +static PRList *crumb_list = NULL; + struct procenv_user user; struct procenv_misc misc; struct procenv_priority priority; struct utsname uts; -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) struct mntopt_map { uint64_t flag; char *name; @@ -112,7 +177,7 @@ struct mntopt_map { { MNT_GJOURNAL , "gjournal" }, { MNT_LOCAL , "local" }, { MNT_MULTILABEL , "multilabel" }, -#ifndef __FreeBSD_kernel__ +#ifndef PROCENV_GNU_BSD { MNT_NFS4ACLS , "nfsv4acls" }, #endif { MNT_NOATIME , "noatime" }, @@ -125,7 +190,7 @@ struct mntopt_map { { MNT_RDONLY , "read-only" }, { MNT_SOFTDEP , "soft-updates" }, { MNT_SUIDDIR , "suiddir" }, -#ifndef __FreeBSD_kernel__ +#if ! defined (PROCENV_GNU_BSD) && defined (MNT_SUJ) { MNT_SUJ , "journaled soft-updates" }, #endif { MNT_SYNCHRONOUS , "synchronous" }, @@ -145,6 +210,15 @@ struct procenv_map output_map[] = { { 0, NULL } }; +struct procenv_map output_format_map[] = { + { OUTPUT_FORMAT_TEXT, "text" }, + { OUTPUT_FORMAT_CRUMB, "crumb" }, + { OUTPUT_FORMAT_JSON, "json" }, + { OUTPUT_FORMAT_XML, "xml" }, + + { 0, NULL } +}; + struct baud_speed baud_speeds[] = { SPEED (B0), SPEED (B50), @@ -194,7 +268,7 @@ struct if_flag_map { mk_map_entry (IFF_SLAVE), #endif -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) mk_map_entry (IFF_SIMPLEX), #endif @@ -206,30 +280,149 @@ struct if_flag_map { mk_map_entry (IFF_DYNAMIC), mk_map_entry (IFF_LOWER_UP), mk_map_entry (IFF_DORMANT), +# ifdef IFF_ECHO mk_map_entry (IFF_ECHO), +# endif #endif { 0, NULL } }; +/* + * Note the gross hack to avoid need for flexible arrays. + */ +TranslateTable translate_table[] = { + { + OUTPUT_FORMAT_XML, + { + { L'\'', L"'" }, + { L'"', L""" }, + { L'&', L"&" }, + { L'<', L"<" }, + { L'>', L">" }, + + /* terminator */ + { L'\0', NULL } + } + }, + { + OUTPUT_FORMAT_JSON, + { + { L'"', L"\\\"" }, + { L'\\', L"\\\\" }, + + /* hack! */ + { L'\0', NULL }, + { L'\0', NULL }, + { L'\0', NULL }, + + /* terminator */ + { L'\0', NULL } + } + }, +}; + #if defined (PROCENV_LINUX) struct if_extended_flag_map { unsigned int flag; char *name; } if_extended_flag_map[] = { +#if defined (IFF_802_1Q_VLAN) mk_map_entry (IFF_802_1Q_VLAN), +#endif +#if defined (IFF_EBRIDGE) mk_map_entry (IFF_EBRIDGE), +#endif +#if defined (IFF_SLAVE_INACTIVE) mk_map_entry (IFF_SLAVE_INACTIVE), +#endif +#if defined (IFF_MASTER_8023AD) mk_map_entry (IFF_MASTER_8023AD), +#endif +#if defined (IFF_MASTER_ALB) mk_map_entry (IFF_MASTER_ALB), +#endif +#if defined (IFF_BONDING) mk_map_entry (IFF_BONDING), +#endif +#if defined (IFF_SLAVE_NEEDARP) mk_map_entry (IFF_SLAVE_NEEDARP), +#endif +#if defined (IFF_ISATAP) mk_map_entry (IFF_ISATAP), +#endif + { 0, NULL } +}; + +struct personality_map { + unsigned int personality; + char *name; +} personality_map[] = { + mk_map_entry (PER_LINUX), + mk_map_entry (PER_LINUX_32BIT), + mk_map_entry (PER_SVR4), + mk_map_entry (PER_SVR3), + mk_map_entry (PER_SCOSVR3), + mk_map_entry (PER_OSR5), + mk_map_entry (PER_WYSEV386), + mk_map_entry (PER_ISCR4), + mk_map_entry (PER_BSD), + mk_map_entry (PER_SUNOS), + mk_map_entry (PER_XENIX), +#if defined (PER_LINUX32) + mk_map_entry (PER_LINUX32), +#endif +#if defined (PER_LINUX32_3GB) + mk_map_entry (PER_LINUX32_3GB), +#endif + mk_map_entry (PER_IRIX32), + mk_map_entry (PER_IRIXN32), + mk_map_entry (PER_IRIX64), + mk_map_entry (PER_RISCOS), + mk_map_entry (PER_SOLARIS), + mk_map_entry (PER_UW7), + mk_map_entry (PER_OSF4), + mk_map_entry (PER_HPUX), { 0, NULL } }; + +struct personality_flag_map { + unsigned int flag; + char *name; +} personality_flag_map[] = { +#if defined (ADDR_COMPAT_LAYOUT) + mk_map_entry (ADDR_COMPAT_LAYOUT), +#endif +#if defined (ADDR_LIMIT_32BIT) + mk_map_entry (ADDR_LIMIT_32BIT), +#endif +#if defined (ADDR_LIMIT_3GB) + mk_map_entry (ADDR_LIMIT_3GB), +#endif +#if defined (ADDR_NO_RANDOMIZE) + mk_map_entry (ADDR_NO_RANDOMIZE), +#endif +#if defined (MMAP_PAGE_ZERO) + mk_map_entry (MMAP_PAGE_ZERO), +#endif +#if defined (READ_IMPLIES_EXEC) + mk_map_entry (READ_IMPLIES_EXEC), +#endif +#if defined (SHORT_INODE) + mk_map_entry (SHORT_INODE), #endif +#if defined (STICKY_TIMEOUTS) + mk_map_entry (STICKY_TIMEOUTS), +#endif +#if defined (WHOLE_SECONDS) + mk_map_entry (WHOLE_SECONDS), +#endif + + { 0, NULL } +}; +#endif /* Really, every single sysconf variable should be ifdef'ed since it * may not exist on a particular system, but that makes the code look @@ -240,160 +433,850 @@ struct if_extended_flag_map { * sigh... */ struct procenv_map sysconf_map[] = { - mk_posix_sysconf_map_entry (ARG_MAX), - mk_posix_sysconf_map_entry (BC_BASE_MAX), - mk_posix_sysconf_map_entry (BC_DIM_MAX), - mk_posix_sysconf_map_entry (BC_SCALE_MAX), - mk_posix_sysconf_map_entry (BC_STRING_MAX), - mk_posix_sysconf_map_entry (CHILD_MAX), - mk_sysconf_map_entry (_SC_CLK_TCK), - mk_posix_sysconf_map_entry (COLL_WEIGHTS_MAX), - mk_posix_sysconf_map_entry (EXPR_NEST_MAX), -#if defined (_SC_HOST_NAME_MAX) - mk_posix_sysconf_map_entry (HOST_NAME_MAX), -#endif - mk_posix_sysconf_map_entry (LINE_MAX), - mk_posix_sysconf_map_entry (LOGIN_NAME_MAX), - mk_posix_sysconf_map_entry (OPEN_MAX), - mk_posix_sysconf_map_entry (PAGESIZE), - mk_posix_sysconf_map_entry (RE_DUP_MAX), - mk_posix_sysconf_map_entry (STREAM_MAX), -#if defined (_SC_SYMLOOP_MAX) - mk_posix_sysconf_map_entry (SYMLOOP_MAX), + +#if defined (_SC_2_C_BIND) + mk_sysconf_map_entry (_SC_2_C_BIND), #endif - mk_posix_sysconf_map_entry (TTY_NAME_MAX), - mk_posix_sysconf_map_entry (TZNAME_MAX), - { _SC_VERSION, "_POSIX_VERSION(_SC_VERSION)" }, -#if defined (_SC_POSIX2_C_DEV) - mk_posix_sysconf_map_entry (POSIX2_C_DEV), -#endif - mk_posix_sysconf_map_entry (BC_BASE_MAX), - mk_posix_sysconf_map_entry (BC_DIM_MAX), - mk_posix_sysconf_map_entry (BC_SCALE_MAX), - mk_posix_sysconf_map_entry (BC_STRING_MAX), - mk_posix_sysconf_map_entry (COLL_WEIGHTS_MAX), - mk_posix_sysconf_map_entry (EXPR_NEST_MAX), - mk_posix_sysconf_map_entry (LINE_MAX), - mk_posix_sysconf_map_entry (RE_DUP_MAX), - { _SC_2_VERSION, "POSIX2_VERSION(_SC_2_VERSION)" }, - { _SC_2_C_DEV, "POSIX2_C_DEV(_SC_2_C_DEV)" }, - { _SC_2_FORT_DEV, "POSIX2_FORT_DEV(_SC_2_FORT_DEV)" }, - { _SC_2_FORT_RUN, "POSIX2_FORT_RUN(_SC_2_FORT_RUN)" }, - { _SC_2_LOCALEDEF, "_POSIX2_LOCALEDEF(_SC_2_LOCALEDEF)" }, - { _SC_2_SW_DEV, "POSIX2_SW_DEV(_SC_2_SW_DEV)" }, - mk_sysconf_map_entry (_SC_PHYS_PAGES), -#if defined (_SC_AVPHYS_PAGES) - mk_sysconf_map_entry (_SC_AVPHYS_PAGES), + +#if defined (_SC_2_C_DEV) + mk_sysconf_map_entry (_SC_2_C_DEV), +#endif + +#if defined (_SC_2_CHAR_TERM) + mk_sysconf_map_entry (_SC_2_CHAR_TERM), +#endif + +#if defined (_SC_2_C_VERSION) + mk_sysconf_map_entry (_SC_2_C_VERSION), +#endif + +#if defined (_SC_2_FORT_DEV) + mk_sysconf_map_entry (_SC_2_FORT_DEV), +#endif + +#if defined (_SC_2_FORT_RUN) + mk_sysconf_map_entry (_SC_2_FORT_RUN), +#endif + +#if defined (_SC_2_LOCALEDEF) + mk_sysconf_map_entry (_SC_2_LOCALEDEF), +#endif + +#if defined (_SC_2_PBS) + mk_sysconf_map_entry (_SC_2_PBS), +#endif + +#if defined (_SC_2_PBS_ACCOUNTING) + mk_sysconf_map_entry (_SC_2_PBS_ACCOUNTING), +#endif + +#if defined (_SC_2_PBS_LOCATE) + mk_sysconf_map_entry (_SC_2_PBS_LOCATE), +#endif + +#if defined (_SC_2_PBS_MESSAGE) + mk_sysconf_map_entry (_SC_2_PBS_MESSAGE), +#endif + +#if defined (_SC_2_PBS_TRACK) + mk_sysconf_map_entry (_SC_2_PBS_TRACK), +#endif + +#if defined (_SC_2_SW_DEV) + mk_sysconf_map_entry (_SC_2_SW_DEV), +#endif + +#if defined (_SC_2_UPE) + mk_sysconf_map_entry (_SC_2_UPE), +#endif + +#if defined (_SC_2_VERSION) + mk_sysconf_map_entry (_SC_2_VERSION), #endif - mk_sysconf_map_entry (_SC_NPROCESSORS_CONF), - mk_sysconf_map_entry (_SC_NPROCESSORS_ONLN), #if defined (_SC_ADVISORY_INFO) - mk_posixopt_sysconf_map_entry (ADVISORY_INFO), + mk_sysconf_map_entry (_SC_ADVISORY_INFO), +#endif + +#if defined (_SC_AIO_LISTIO_MAX) + mk_sysconf_map_entry (_SC_AIO_LISTIO_MAX), +#endif + +#if defined (_SC_AIO_MAX) + mk_sysconf_map_entry (_SC_AIO_MAX), #endif - mk_posixopt_sysconf_map_entry (ASYNCHRONOUS_IO), + +#if defined (_SC_AIO_PRIO_DELTA_MAX) + mk_sysconf_map_entry (_SC_AIO_PRIO_DELTA_MAX), +#endif + +#if defined (_SC_ARG_MAX) + mk_sysconf_map_entry (_SC_ARG_MAX), +#endif + +#if defined (_SC_ASYNCHRONOUS_IO) + mk_sysconf_map_entry (_SC_ASYNCHRONOUS_IO), +#endif + +#if defined (_SC_ATEXIT_MAX) + mk_sysconf_map_entry (_SC_ATEXIT_MAX), +#endif + +#if defined (_SC_AVPHYS_PAGES) + mk_sysconf_map_entry (_SC_AVPHYS_PAGES), +#endif + #if defined (_SC_BARRIERS) - mk_posixopt_sysconf_map_entry (BARRIERS), + mk_sysconf_map_entry (_SC_BARRIERS), +#endif + +#if defined (_SC_BASE) + mk_sysconf_map_entry (_SC_BASE), #endif -#if defined (_POSIX_CHOWN_RESTRICTED) - mk_sysconf_map_entry (_POSIX_CHOWN_RESTRICTED), + +#if defined (_SC_BC_BASE_MAX) + mk_sysconf_map_entry (_SC_BC_BASE_MAX), +#endif + +#if defined (_SC_BC_DIM_MAX) + mk_sysconf_map_entry (_SC_BC_DIM_MAX), #endif + +#if defined (_SC_BC_SCALE_MAX) + mk_sysconf_map_entry (_SC_BC_SCALE_MAX), +#endif + +#if defined (_SC_BC_STRING_MAX) + mk_sysconf_map_entry (_SC_BC_STRING_MAX), +#endif + +#if defined (_SC_CHAR_BIT) + mk_sysconf_map_entry (_SC_CHAR_BIT), +#endif + +#if defined (_SC_CHARCLASS_NAME_MAX) + mk_sysconf_map_entry (_SC_CHARCLASS_NAME_MAX), +#endif + +#if defined (_SC_CHAR_MAX) + mk_sysconf_map_entry (_SC_CHAR_MAX), +#endif + +#if defined (_SC_CHAR_MIN) + mk_sysconf_map_entry (_SC_CHAR_MIN), +#endif + +#if defined (_SC_CHILD_MAX) + mk_sysconf_map_entry (_SC_CHILD_MAX), +#endif + +#if defined (_SC_C_LANG_SUPPORT) + mk_sysconf_map_entry (_SC_C_LANG_SUPPORT), +#endif + +#if defined (_SC_C_LANG_SUPPORT_R) + mk_sysconf_map_entry (_SC_C_LANG_SUPPORT_R), +#endif + +#if defined (_SC_CLK_TCK) + mk_sysconf_map_entry (_SC_CLK_TCK), +#endif + #if defined (_SC_CLOCK_SELECTION) - mk_posixopt_sysconf_map_entry (CLOCK_SELECTION), + mk_sysconf_map_entry (_SC_CLOCK_SELECTION), #endif + +#if defined (_SC_COLL_WEIGHTS_MAX) + mk_sysconf_map_entry (_SC_COLL_WEIGHTS_MAX), +#endif + #if defined (_SC_CPUTIME) - mk_posixopt_sysconf_map_entry (CPUTIME), + mk_sysconf_map_entry (_SC_CPUTIME), +#endif + +#if defined (_SC_DELAYTIMER_MAX) + mk_sysconf_map_entry (_SC_DELAYTIMER_MAX), +#endif + +#if defined (_SC_DEVICE_IO) + mk_sysconf_map_entry (_SC_DEVICE_IO), +#endif + +#if defined (_SC_DEVICE_SPECIFIC) + mk_sysconf_map_entry (_SC_DEVICE_SPECIFIC), +#endif + +#if defined (_SC_DEVICE_SPECIFIC_R) + mk_sysconf_map_entry (_SC_DEVICE_SPECIFIC_R), +#endif + +#if defined (_SC_EQUIV_CLASS_MAX) + mk_sysconf_map_entry (_SC_EQUIV_CLASS_MAX), +#endif + +#if defined (_SC_EXPR_NEST_MAX) + mk_sysconf_map_entry (_SC_EXPR_NEST_MAX), #endif + +#if defined (_SC_FD_MGMT) + mk_sysconf_map_entry (_SC_FD_MGMT), +#endif + +#if defined (_SC_FIFO) + mk_sysconf_map_entry (_SC_FIFO), +#endif + +#if defined (_SC_FILE_ATTRIBUTES) + mk_sysconf_map_entry (_SC_FILE_ATTRIBUTES), +#endif + #if defined (_SC_FILE_LOCKING) - mk_posixopt_sysconf_map_entry (FILE_LOCKING), -#endif - mk_posixopt_sysconf_map_entry (FSYNC), - mk_posixopt_sysconf_map_entry (JOB_CONTROL), - mk_posixopt_sysconf_map_entry (MAPPED_FILES), - mk_posixopt_sysconf_map_entry (MEMLOCK), - mk_posixopt_sysconf_map_entry (MEMLOCK_RANGE), - mk_posixopt_sysconf_map_entry (MEMORY_PROTECTION), - mk_posixopt_sysconf_map_entry (MESSAGE_PASSING), + mk_sysconf_map_entry (_SC_FILE_LOCKING), +#endif + +#if defined (_SC_FILE_SYSTEM) + mk_sysconf_map_entry (_SC_FILE_SYSTEM), +#endif + +#if defined (_SC_FSYNC) + mk_sysconf_map_entry (_SC_FSYNC), +#endif + +#if defined (_SC_GETGR_R_SIZE_MAX) + mk_sysconf_map_entry (_SC_GETGR_R_SIZE_MAX), +#endif + +#if defined (_SC_GETPW_R_SIZE_MAX) + mk_sysconf_map_entry (_SC_GETPW_R_SIZE_MAX), +#endif + +#if defined (_SC_HOST_NAME_MAX) + mk_sysconf_map_entry (_SC_HOST_NAME_MAX), +#endif + +#if defined (_SC_INT_MAX) + mk_sysconf_map_entry (_SC_INT_MAX), +#endif + +#if defined (_SC_INT_MIN) + mk_sysconf_map_entry (_SC_INT_MIN), +#endif + +#if defined (_SC_IOV_MAX) + mk_sysconf_map_entry (_SC_IOV_MAX), +#endif + +#if defined (_SC_IPV6) + mk_sysconf_map_entry (_SC_IPV6), +#endif + +#if defined (_SC_JOB_CONTROL) + mk_sysconf_map_entry (_SC_JOB_CONTROL), +#endif + +#if defined (_SC_LEVEL1_DCACHE_ASSOC) + mk_sysconf_map_entry (_SC_LEVEL1_DCACHE_ASSOC), +#endif + +#if defined (_SC_LEVEL1_DCACHE_LINESIZE) + mk_sysconf_map_entry (_SC_LEVEL1_DCACHE_LINESIZE), +#endif + +#if defined (_SC_LEVEL1_DCACHE_SIZE) + mk_sysconf_map_entry (_SC_LEVEL1_DCACHE_SIZE), +#endif + +#if defined (_SC_LEVEL1_ICACHE_ASSOC) + mk_sysconf_map_entry (_SC_LEVEL1_ICACHE_ASSOC), +#endif + +#if defined (_SC_LEVEL1_ICACHE_LINESIZE) + mk_sysconf_map_entry (_SC_LEVEL1_ICACHE_LINESIZE), +#endif + +#if defined (_SC_LEVEL1_ICACHE_SIZE) + mk_sysconf_map_entry (_SC_LEVEL1_ICACHE_SIZE), +#endif + +#if defined (_SC_LEVEL2_CACHE_ASSOC) + mk_sysconf_map_entry (_SC_LEVEL2_CACHE_ASSOC), +#endif + +#if defined (_SC_LEVEL2_CACHE_LINESIZE) + mk_sysconf_map_entry (_SC_LEVEL2_CACHE_LINESIZE), +#endif + +#if defined (_SC_LEVEL2_CACHE_SIZE) + mk_sysconf_map_entry (_SC_LEVEL2_CACHE_SIZE), +#endif + +#if defined (_SC_LEVEL3_CACHE_ASSOC) + mk_sysconf_map_entry (_SC_LEVEL3_CACHE_ASSOC), +#endif + +#if defined (_SC_LEVEL3_CACHE_LINESIZE) + mk_sysconf_map_entry (_SC_LEVEL3_CACHE_LINESIZE), +#endif + +#if defined (_SC_LEVEL3_CACHE_SIZE) + mk_sysconf_map_entry (_SC_LEVEL3_CACHE_SIZE), +#endif + +#if defined (_SC_LEVEL4_CACHE_ASSOC) + mk_sysconf_map_entry (_SC_LEVEL4_CACHE_ASSOC), +#endif + +#if defined (_SC_LEVEL4_CACHE_LINESIZE) + mk_sysconf_map_entry (_SC_LEVEL4_CACHE_LINESIZE), +#endif + +#if defined (_SC_LEVEL4_CACHE_SIZE) + mk_sysconf_map_entry (_SC_LEVEL4_CACHE_SIZE), +#endif + +#if defined (_SC_LINE_MAX) + mk_sysconf_map_entry (_SC_LINE_MAX), +#endif + +#if defined (_SC_LOGIN_NAME_MAX) + mk_sysconf_map_entry (_SC_LOGIN_NAME_MAX), +#endif + +#if defined (_SC_LONG_BIT) + mk_sysconf_map_entry (_SC_LONG_BIT), +#endif + +#if defined (_SC_MAPPED_FILES) + mk_sysconf_map_entry (_SC_MAPPED_FILES), +#endif + +#if defined (_SC_MB_LEN_MAX) + mk_sysconf_map_entry (_SC_MB_LEN_MAX), +#endif + +#if defined (_SC_MEMLOCK) + mk_sysconf_map_entry (_SC_MEMLOCK), +#endif + +#if defined (_SC_MEMLOCK_RANGE) + mk_sysconf_map_entry (_SC_MEMLOCK_RANGE), +#endif + +#if defined (_SC_MEMORY_PROTECTION) + mk_sysconf_map_entry (_SC_MEMORY_PROTECTION), +#endif + +#if defined (_SC_MESSAGE_PASSING) + mk_sysconf_map_entry (_SC_MESSAGE_PASSING), +#endif + #if defined (_SC_MONOTONIC_CLOCK) - mk_posixopt_sysconf_map_entry (MONOTONIC_CLOCK), + mk_sysconf_map_entry (_SC_MONOTONIC_CLOCK), #endif + +#if defined (_SC_MQ_OPEN_MAX) + mk_sysconf_map_entry (_SC_MQ_OPEN_MAX), +#endif + +#if defined (_SC_MQ_PRIO_MAX) + mk_sysconf_map_entry (_SC_MQ_PRIO_MAX), +#endif + #ifdef _SC_MULTI_PROCESS - mk_posixopt_sysconf_map_entry (MULTI_PROCESS), + mk_sysconf_map_entry (_SC_MULTI_PROCESS), #endif - mk_posixopt_sysconf_map_entry (PRIORITIZED_IO), - mk_posixopt_sysconf_map_entry (PRIORITY_SCHEDULING), -#if defined (_POSIX_RAW_SOCKETS) - mk_sysconf_map_entry (_POSIX_RAW_SOCKETS), + +#if defined (_SC_NETWORKING) + mk_sysconf_map_entry (_SC_NETWORKING), +#endif + +#if defined (_SC_NGROUPS_MAX) + mk_sysconf_map_entry (_SC_NGROUPS_MAX), +#endif + +#if defined (_SC_NL_ARGMAX) + mk_sysconf_map_entry (_SC_NL_ARGMAX), +#endif + +#if defined (_SC_NL_LANGMAX) + mk_sysconf_map_entry (_SC_NL_LANGMAX), +#endif + +#if defined (_SC_NL_MSGMAX) + mk_sysconf_map_entry (_SC_NL_MSGMAX), +#endif + +#if defined (_SC_NL_NMAX) + mk_sysconf_map_entry (_SC_NL_NMAX), +#endif + +#if defined (_SC_NL_SETMAX) + mk_sysconf_map_entry (_SC_NL_SETMAX), +#endif + +#if defined (_SC_NL_TEXTMAX) + mk_sysconf_map_entry (_SC_NL_TEXTMAX), +#endif + +#if defined (_SC_NPROCESSORS_CONF) + mk_sysconf_map_entry (_SC_NPROCESSORS_CONF), +#endif + +#if defined (_SC_NPROCESSORS_ONLN) + mk_sysconf_map_entry (_SC_NPROCESSORS_ONLN), +#endif + +#if defined (_SC_NZERO) + mk_sysconf_map_entry (_SC_NZERO), +#endif + +#if defined (_SC_OPEN_MAX) + mk_sysconf_map_entry (_SC_OPEN_MAX), +#endif + +#if defined (_SC_PAGESIZE) + mk_sysconf_map_entry (_SC_PAGESIZE), +#endif + +#if defined (_SC_PAGE_SIZE) + mk_sysconf_map_entry (_SC_PAGE_SIZE), +#endif + +#if defined (_SC_PASS_MAX) + mk_sysconf_map_entry (_SC_PASS_MAX), +#endif + +#if defined (_SC_PHYS_PAGES) + mk_sysconf_map_entry (_SC_PHYS_PAGES), +#endif + +#if defined (_SC_PII) + mk_sysconf_map_entry (_SC_PII), +#endif + +#if defined (_SC_PII_INTERNET) + mk_sysconf_map_entry (_SC_PII_INTERNET), +#endif + +#if defined (_SC_PII_INTERNET_DGRAM) + mk_sysconf_map_entry (_SC_PII_INTERNET_DGRAM), +#endif + +#if defined (_SC_PII_INTERNET_STREAM) + mk_sysconf_map_entry (_SC_PII_INTERNET_STREAM), +#endif + +#if defined (_SC_PII_OSI) + mk_sysconf_map_entry (_SC_PII_OSI), +#endif + +#if defined (_SC_PII_OSI_CLTS) + mk_sysconf_map_entry (_SC_PII_OSI_CLTS), +#endif + +#if defined (_SC_PII_OSI_COTS) + mk_sysconf_map_entry (_SC_PII_OSI_COTS), +#endif + +#if defined (_SC_PII_OSI_M) + mk_sysconf_map_entry (_SC_PII_OSI_M), +#endif + +#if defined (_SC_PII_SOCKET) + mk_sysconf_map_entry (_SC_PII_SOCKET), +#endif + +#if defined (_SC_PII_XTI) + mk_sysconf_map_entry (_SC_PII_XTI), +#endif + +#if defined (_SC_PIPE) + mk_sysconf_map_entry (_SC_PIPE), +#endif + +#if defined (_SC_POLL) + mk_sysconf_map_entry (_SC_POLL), +#endif + +#if defined (_SC_SINGLE_PROCESS) + mk_sysconf_map_entry (_SC_SINGLE_PROCESS), +#endif + +#if defined (_SC_SYSTEM_DATABASE) + mk_sysconf_map_entry (_SC_SYSTEM_DATABASE), +#endif + +#if defined (_SC_SYSTEM_DATABASE_R) + mk_sysconf_map_entry (_SC_SYSTEM_DATABASE_R), +#endif + +#if defined (_SC_THREAD_KEYS_MAX) + mk_sysconf_map_entry (_SC_THREAD_KEYS_MAX), +#endif + +#if defined (_SC_THREAD_DESTRUCTOR_ITERATIONS) + mk_sysconf_map_entry (_SC_THREAD_DESTRUCTOR_ITERATIONS), +#endif + +#if defined (_SC_THREAD_THREADS_MAX) + mk_sysconf_map_entry (_SC_THREAD_THREADS_MAX), +#endif + +#if defined (_SC_STREAM_MAX) + mk_sysconf_map_entry (_SC_STREAM_MAX), +#endif + +#if defined (_SC_SYMLOOP_MAX) + mk_sysconf_map_entry (_SC_SYMLOOP_MAX), +#endif + +#if defined (_SC_TTY_NAME_MAX) + mk_sysconf_map_entry (_SC_TTY_NAME_MAX), +#endif + +#if defined (_SC_TZNAME_MAX) + mk_sysconf_map_entry (_SC_TZNAME_MAX), +#endif + +#if defined (_SC_TYPED_MEMORY_OBJECTS) + mk_sysconf_map_entry (_SC_TYPED_MEMORY_OBJECTS), +#endif + +#if defined (_SC_USER_GROUPS) + mk_sysconf_map_entry (_SC_USER_GROUPS), +#endif + +#if defined (_SC_USER_GROUPS_R) + mk_sysconf_map_entry (_SC_USER_GROUPS_R), +#endif + +#if defined (_SC_VERSION) + mk_sysconf_map_entry (_SC_VERSION), +#endif + +#if defined (_SC_POSIX2_C_DEV) + mk_sysconf_map_entry (_SC_POSIX2_C_DEV), +#endif + +#if defined (_SC_SIGQUEUE_MAX) + mk_sysconf_map_entry (_SC_SIGQUEUE_MAX), +#endif + +#if defined (_SC_T_IOV_MAX) + mk_sysconf_map_entry (_SC_T_IOV_MAX), +#endif + +#if defined (_SC_THREAD_STACK_MIN) + mk_sysconf_map_entry (_SC_THREAD_STACK_MIN), +#endif + +#if defined (_SC_SSIZE_MAX) + mk_sysconf_map_entry (_SC_SSIZE_MAX), +#endif + +#if defined (_SC_TIMER_MAX) + mk_sysconf_map_entry (_SC_TIMER_MAX), +#endif + +#if defined (_SC_UCHAR_MAX) + mk_sysconf_map_entry (_SC_UCHAR_MAX), +#endif + +#if defined (_SC_UINT_MAX) + mk_sysconf_map_entry (_SC_UINT_MAX), +#endif + +#if defined (_SC_UIO_MAXIOV) + mk_sysconf_map_entry (_SC_UIO_MAXIOV), +#endif + +#if defined (_SC_ULONG_MAX) + mk_sysconf_map_entry (_SC_ULONG_MAX), +#endif + +#if defined (_SC_USHRT_MAX) + mk_sysconf_map_entry (_SC_USHRT_MAX), +#endif + +#if defined (_SC_V6_LPBIG_OFFBIG) + mk_sysconf_map_entry (_SC_V6_LPBIG_OFFBIG), +#endif + +#if defined (_SC_WORD_BIT) + mk_sysconf_map_entry (_SC_WORD_BIT), +#endif + +#if defined (_SC_EXPR_NEST_MAX) + mk_sysconf_map_entry (_SC_EXPR_NEST_MAX), #endif + +#if defined (_SC_V6_ILP32_OFF32) + mk_sysconf_map_entry (_SC_V6_ILP32_OFF32), +#endif + +#if defined (_SC_V6_ILP32_OFFBIG) + mk_sysconf_map_entry (_SC_V6_ILP32_OFFBIG), +#endif + +#if defined (_SC_V6_LP64_OFF64) + mk_sysconf_map_entry (_SC_V6_LP64_OFF64), +#endif + +#if defined (_SC_V7_ILP32_OFF32) + mk_sysconf_map_entry (_SC_V7_ILP32_OFF32), +#endif + +#if defined (_SC_V7_ILP32_OFFBIG) + mk_sysconf_map_entry (_SC_V7_ILP32_OFFBIG), +#endif + +#if defined (_SC_V7_LPBIG_OFFBIG) + mk_sysconf_map_entry (_SC_V7_LPBIG_OFFBIG), +#endif + +#if defined (_SC_V7_LP64_OFF64) + mk_sysconf_map_entry (_SC_V7_LP64_OFF64), +#endif + +#if defined (_SC_PRIORITIZED_IO) + mk_sysconf_map_entry (_SC_PRIORITIZED_IO), +#endif + +#if defined (_SC_PRIORITY_SCHEDULING) + mk_sysconf_map_entry (_SC_PRIORITY_SCHEDULING), +#endif + +#if defined (_SC_RAW_SOCKETS) + mk_sysconf_map_entry (_SC_RAW_SOCKETS), +#endif + #if defined (_SC_READER_WRITER_LOCKS) - mk_posixopt_sysconf_map_entry (READER_WRITER_LOCKS), + mk_sysconf_map_entry (_SC_READER_WRITER_LOCKS), #endif - mk_posixopt_sysconf_map_entry (REALTIME_SIGNALS), + +#if defined (_SC_REALTIME_SIGNALS) + mk_sysconf_map_entry (_SC_REALTIME_SIGNALS), +#endif + +#if defined (_SC_RE_DUP_MAX) + mk_sysconf_map_entry (_SC_RE_DUP_MAX), +#endif + #if defined (_SC_REGEXP) - mk_posixopt_sysconf_map_entry (REGEXP), + mk_sysconf_map_entry (_SC_REGEXP), +#endif + +#if defined (_SC_REGEX_VERSION) + mk_sysconf_map_entry (_SC_REGEX_VERSION), +#endif + +#if defined (_SC_RTSIG_MAX) + mk_sysconf_map_entry (_SC_RTSIG_MAX), +#endif + +#if defined (_SC_SAVED_IDS) + mk_sysconf_map_entry (_SC_SAVED_IDS), +#endif + +#if defined (_SC_SCHAR_MAX) + mk_sysconf_map_entry (_SC_SCHAR_MAX), +#endif + +#if defined (_SC_SCHAR_MIN) + mk_sysconf_map_entry (_SC_SCHAR_MIN), #endif - mk_posixopt_sysconf_map_entry (SAVED_IDS), - mk_posixopt_sysconf_map_entry (SEMAPHORES), - mk_posixopt_sysconf_map_entry (SHARED_MEMORY_OBJECTS), + +#if defined (_SC_SELECT) + mk_sysconf_map_entry (_SC_SELECT), +#endif + +#if defined (_SC_SEMAPHORES) + mk_sysconf_map_entry (_SC_SEMAPHORES), +#endif + +#if defined (_SC_SEM_NSEMS_MAX) + mk_sysconf_map_entry (_SC_SEM_NSEMS_MAX), +#endif + +#if defined (_SC_SEM_VALUE_MAX) + mk_sysconf_map_entry (_SC_SEM_VALUE_MAX), +#endif + +#if defined (_SC_SHARED_MEMORY_OBJECTS) + mk_sysconf_map_entry (_SC_SHARED_MEMORY_OBJECTS), +#endif + #if defined (_SC_SHELL) - mk_posixopt_sysconf_map_entry (SHELL), + mk_sysconf_map_entry (_SC_SHELL), #endif + +#if defined (_SC_SHRT_MAX) + mk_sysconf_map_entry (_SC_SHRT_MAX), +#endif + +#if defined (_SC_SHRT_MIN) + mk_sysconf_map_entry (_SC_SHRT_MIN), +#endif + +#if defined (_SC_SIGNALS) + mk_sysconf_map_entry (_SC_SIGNALS), +#endif + #if defined (_SC_SPAWN) - mk_posixopt_sysconf_map_entry (SPAWN), + mk_sysconf_map_entry (_SC_SPAWN), #endif + #if defined (_SC_SPIN_LOCKS) - mk_posixopt_sysconf_map_entry (SPIN_LOCKS), + mk_sysconf_map_entry (_SC_SPIN_LOCKS), #endif + #if defined (_SC_SPORADIC_SERVER) - mk_posixopt_sysconf_map_entry (SPORADIC_SERVER), + mk_sysconf_map_entry (_SC_SPORADIC_SERVER), #endif - mk_posixopt_sysconf_map_entry (SYNCHRONIZED_IO), - mk_posixopt_sysconf_map_entry (THREAD_ATTR_STACKSIZE), + +#if defined (_SC_SYNCHRONIZED_IO) + mk_sysconf_map_entry (_SC_SYNCHRONIZED_IO), +#endif + +#if defined (_SC_THREAD_ATTR_STACKADDR) + mk_sysconf_map_entry (_SC_THREAD_ATTR_STACKADDR), +#endif + +#if defined (_SC_THREAD_ATTR_STACKSIZE) + mk_sysconf_map_entry (_SC_THREAD_ATTR_STACKSIZE), +#endif + #if defined (_SC_THREAD_CPUTIME) - mk_posixopt_sysconf_map_entry (THREAD_CPUTIME), + mk_sysconf_map_entry (_SC_THREAD_CPUTIME), +#endif + +#if defined (_SC_THREAD_PRIO_INHERIT) + mk_sysconf_map_entry (_SC_THREAD_PRIO_INHERIT), #endif - mk_posixopt_sysconf_map_entry (THREAD_PRIO_INHERIT), - mk_posixopt_sysconf_map_entry (THREAD_PRIO_PROTECT), - mk_posixopt_sysconf_map_entry (THREAD_PRIORITY_SCHEDULING), + +#if defined (_SC_THREAD_PRIO_PROTECT) + mk_sysconf_map_entry (_SC_THREAD_PRIO_PROTECT), +#endif + +#if defined (_SC_THREAD_PRIORITY_SCHEDULING) + mk_sysconf_map_entry (_SC_THREAD_PRIORITY_SCHEDULING), +#endif + #if defined (_SC_THREAD_PROCESS_SHARED) - mk_posixopt_sysconf_map_entry (THREAD_PROCESS_SHARED), + mk_sysconf_map_entry (_SC_THREAD_PROCESS_SHARED), #endif - mk_posixopt_sysconf_map_entry (THREAD_SAFE_FUNCTIONS), + +#if defined (_SC_THREAD_ROBUST_PRIO_INHERIT) + mk_sysconf_map_entry (_SC_THREAD_ROBUST_PRIO_INHERIT), +#endif + +#if defined (_SC_THREAD_ROBUST_PRIO_PROTECT) + mk_sysconf_map_entry (_SC_THREAD_ROBUST_PRIO_PROTECT), +#endif + +#if defined (_SC_THREAD_SAFE_FUNCTIONS) + mk_sysconf_map_entry (_SC_THREAD_SAFE_FUNCTIONS), +#endif + #if defined (_SC_THREAD_SPORADIC_SERVER) - mk_posixopt_sysconf_map_entry (THREAD_SPORADIC_SERVER), + mk_sysconf_map_entry (_SC_THREAD_SPORADIC_SERVER), +#endif + +#if defined (_SC_THREADS) + mk_sysconf_map_entry (_SC_THREADS), #endif - mk_posixopt_sysconf_map_entry (THREADS), + #if defined (_SC_TIMEOUTS) - mk_posixopt_sysconf_map_entry (TIMEOUTS), + mk_sysconf_map_entry (_SC_TIMEOUTS), +#endif + +#if defined (_SC_TIMERS) + mk_sysconf_map_entry (_SC_TIMERS), #endif - mk_posixopt_sysconf_map_entry (TIMERS), + #if defined (_SC_TRACE) - mk_posixopt_sysconf_map_entry (TRACE), + mk_sysconf_map_entry (_SC_TRACE), #endif + #if defined (_SC_TRACE_EVENT_FILTER) - mk_posixopt_sysconf_map_entry (TRACE_EVENT_FILTER), + mk_sysconf_map_entry (_SC_TRACE_EVENT_FILTER), #endif + #if defined (_SC_TRACE_INHERIT) - mk_posixopt_sysconf_map_entry (TRACE_INHERIT), + mk_sysconf_map_entry (_SC_TRACE_INHERIT), #endif + #if defined (_SC_TRACE_LOG) - mk_posixopt_sysconf_map_entry (TRACE_LOG), + mk_sysconf_map_entry (_SC_TRACE_LOG), +#endif + +#if defined (_SC_TYPED_MEMORY_OBJECT) + mk_sysconf_map_entry (_SC_TYPED_MEMORY_OBJECT), +#endif + +#if defined (_SC_XBS5_ILP32_OFF32) + mk_sysconf_map_entry (_SC_XBS5_ILP32_OFF32), +#endif + +#if defined (_SC_XBS5_ILP32_OFFBIG) + mk_sysconf_map_entry (_SC_XBS5_ILP32_OFFBIG), +#endif + +#if defined (_SC_XBS5_LP64_OFF64) + mk_sysconf_map_entry (_SC_XBS5_LP64_OFF64), +#endif + +#if defined (_SC_XBS5_LPBIG_OFFBIG) + mk_sysconf_map_entry (_SC_XBS5_LPBIG_OFFBIG), +#endif + +#if defined (_SC_XOPEN_CRYPT) + mk_sysconf_map_entry (_SC_XOPEN_CRYPT), +#endif + +#if defined (_SC_XOPEN_ENH_I18N) + mk_sysconf_map_entry (_SC_XOPEN_ENH_I18N), +#endif + +#if defined (_SC_XOPEN_LEGACY) + mk_sysconf_map_entry (_SC_XOPEN_LEGACY), +#endif + +#if defined (_SC_XOPEN_REALTIME) + mk_sysconf_map_entry (_SC_XOPEN_REALTIME), +#endif + +#if defined (_SC_XOPEN_REALTIME_THREADS) + mk_sysconf_map_entry (_SC_XOPEN_REALTIME_THREADS), +#endif + +#if defined (_SC_XOPEN_SHM) + mk_sysconf_map_entry (_SC_XOPEN_SHM), +#endif + +#if defined (_SC_XOPEN_UNIX) + mk_sysconf_map_entry (_SC_XOPEN_UNIX), +#endif + +#if defined (_SC_XOPEN_XPG2) + mk_sysconf_map_entry (_SC_XOPEN_XPG2), #endif -#ifdef _SC_TYPED_MEMORY_OBJECT - mk_posixopt_sysconf_map_entry (TYPED_MEMORY_OBJECT), + +#if defined (_SC_XOPEN_XPG3) + mk_sysconf_map_entry (_SC_XOPEN_XPG3), #endif -#if defined (_POSIX_VDISABLE) - mk_sysconf_map_entry (_POSIX_VDISABLE), + +#if defined (_SC_XOPEN_XPG4) + mk_sysconf_map_entry (_SC_XOPEN_XPG4), #endif - mk_sysconf_map_entry (_XOPEN_CRYPT), - mk_sysconf_map_entry (_XOPEN_LEGACY), -#if defined (_XOPEN_REALTIME) - mk_sysconf_map_entry (_XOPEN_REALTIME), + +#if defined (_SC_XOPEN_VERSION) + mk_sysconf_map_entry (_SC_XOPEN_VERSION), #endif -#if defined (_XOPEN_REALTIME_THREADS) - mk_sysconf_map_entry (_XOPEN_REALTIME_THREADS), + +#if defined (_SC_XOPEN_XCU_VERSION) + mk_sysconf_map_entry (_SC_XOPEN_XCU_VERSION), #endif - mk_sysconf_map_entry (_XOPEN_UNIX), { 0, NULL } }; @@ -514,7 +1397,7 @@ struct procenv_map scheduler_map[] = { mk_map_entry (SCHED_IDLE), #endif #endif - + { 0, NULL } }; @@ -524,6 +1407,15 @@ struct procenv_map thread_sched_policy_map[] = { mk_map_entry (SCHED_RR) }; +#if defined (PROCENV_LINUX) && defined (HAVE_NUMA_H) +struct procenv_map numa_mempolicy_map[] = { + mk_map_entry (MPOL_DEFAULT), + mk_map_entry (MPOL_PREFERRED), + mk_map_entry (MPOL_BIND), + mk_map_entry (MPOL_INTERLEAVE), +}; +#endif + void usage (void) { @@ -533,53 +1425,90 @@ usage (void) show (""); show ("Options:"); show (""); - show (" -a, --meta : Display meta details."); - show (" -A, --arguments : Display program arguments."); - show (" -b, --libs : Display library details."); - show (" -c, --cgroup[s] : Display cgroup details (Linux only)."); - show (" -d, --compiler : Display compiler details."); - show (" -e, --env[ironment] : Display environment variables."); - show (" --exec : Treat non-option arguments as program to execute."); - show (" -f, --fds : Display file descriptor details."); - show (" --file=<file> : Send output to <file> (implies --output=file)."); - show (" -g, --sizeof : Display sizes of data types in bytes."); - show (" -h, --help : This help text."); - show (" -i, --misc : Display miscellaneous details."); - show (" -j, --uname : Display uname details."); - show (" -k, --clock[s] : Display clock details."); - show (" -l, --limits : Display limits."); - show (" -L, --locale : Display locale details."); - show (" -m, --mount[s] : Display mount details."); - show (" -n, --confstr : Display confstr details."); - show (" -N, --network : Display network details."); - show (" -o, --oom : Display out-of-memory manager details (Linux only)"); - show (" --output=<type> : Send output to alternative location. <type> can be one of:"); + show (" -a, --meta : Display meta details."); + show (" -A, --arguments : Display program arguments."); + show (" -b, --libs : Display details of linked libraries."); + show (" -B, --libc : Display standard library details."); + show (" -c, --cgroups : Display cgroup details (Linux only)."); + show (" -C, --cpu : Display CPU and scheduler details."); + show (" --crumb-separator=<str> : Specify string '<str>' as alternate delimiter"); + show (" for crumb format output (default='%s').", + PROCENV_DEFAULT_CRUMB_SEPARATOR); + show (" -d, --compiler : Display compiler details."); + show (" -e, --environment : Display environment variables."); + show (" -E, --semaphores : Display semaphore details."); + show (" --exec : Treat non-option arguments as program to execute."); + show (" -f, --fds : Display file descriptor details."); + show (" -F, --namespaces : Display namespace details."); + show (" --file=<file> : Send output to <file> (implies --output=file)."); + show (" --format=<format> : Specify output format. <format> can be one of:"); + show (""); + show (" crumb : ASCII 'breadcrumbs'"); + show (" json : JSON output."); + show (" text : ASCII output (default)."); + show (" xml : XML output."); + show (""); + show (" -g, --sizeof : Display sizes of data types in bytes."); + show (" -h, --help : This help text."); + show (" -i, --misc : Display miscellaneous details."); + show (" --indent : Number of indent characters to use for each indent"); + show (" (default=%d).", DEFAULT_INDENT_AMOUNT); + show (" --indent-char=<c> : Use character '<c>' for indenting"); + show (" (default='%s').", DEFAULT_INDENT_CHAR); + show (" -j, --uname : Display uname details."); + show (" -k, --clocks : Display clock details."); + show (" -l, --limits : Display limits."); + show (" -L, --locale : Display locale details."); + show (" -m, --mounts : Display mount details."); + show (" -M, --message-queues : Display message queue details."); + show (" -n, --confstr : Display confstr details."); + show (" -N, --network : Display network details."); + show (" -o, --oom : Display out-of-memory manager details (Linux only)"); + show (" --output=<type> : Send output to alternative location."); + show (" <type> can be one of:"); show (""); - show (" file : Send output to a file."); - show (" stderr : Write to standard error."); - show (" stdout : Write to standard output (default)."); - show (" syslog : Write to the system log file."); - show (" terminal : Write to terminal."); + show (" file : Send output to a file."); + show (" stderr : Write to standard error."); + show (" stdout : Write to standard output (default)."); + show (" syslog : Write to the system log file."); + show (" terminal : Write to terminal."); show (""); - show (" -p, --proc[ess] : Display process details."); - show (" -P, --platform : Display platform details."); - show (" -q, --time : Display time details."); - show (" -r, --range[s] : Display range of data types."); - show (" -s, --signal[s] : Display signal details."); - show (" -t, --tty : Display terminal details."); - show (" -T, --threads : Display thread details."); - show (" -u, --stat : Display stat details."); - show (" -U, --rusage : Display rusage details."); - show (" -v, --version : Display version details."); - show (" -w, --capabilities : Display capaibility details (Linux only)."); - show (" -x, --pathconf : Display pathconf details."); - show (" -y, --sysconf : Display sysconf details."); - show (" -z, --timezone : Display timezone details."); + show (" -p, --process : Display process details."); + show (" -P, --platform : Display platform details."); + show (" -q, --time : Display time details."); + show (" -r, --ranges : Display range of data types."); + show (" --separator=<str> : Specify string '<str>' as alternate delimiter"); + show (" for text format output (default='%s').", + PROCENV_DEFAULT_TEXT_SEPARATOR); + show (" -s, --signals : Display signal details."); + show (" -S, --shared-memory : Display shared memory details."); + show (" -t, --tty : Display terminal details."); + show (" -T, --threads : Display thread details."); + show (" -u, --stat : Display stat details."); + show (" -U, --rusage : Display rusage details."); + show (" -v, --version : Display version details."); + show (" -w, --capabilities : Display capability details (Linux only)."); + show (" -x, --pathconf : Display pathconf details."); + show (" -y, --sysconf : Display sysconf details."); + show (" -Y, --memory : Display memory details."); + show (" -z, --timezone : Display timezone details."); show (""); show ("Notes:"); show (""); - show (" - If no option is specified, all details are displayed."); - show (" - Only one option may be specified."); + show (" - Options are considered in order, so '--output' should"); + show (" precede any other option."); + show (" - If no display option is specified, all details are displayed."); + show (" - Only one display option may be specified."); + show (" - All values for '--indent-char' are literal except '\\t' which can be"); + show (" used to specify a tab character. The same is true for '--separator'"); + show (" and '--crumb-separator' but only if it is the first character specified."); + show (" - Specifying a visible indent-char is only (vaguely) meaningful"); + show (" for text output."); + show (" - Any long option name may be shortened as long as it remains unique."); + show (" - The 'crumb' output format is designed for easy parsing: it displays"); + show (" the data in a flattened format with each value on a separate line"); + show (" preceded by all appropriate headings which are separated by the"); + show (" current separator."); show (""); } @@ -589,20 +1518,98 @@ show_pathconfs (ShowMountType what, { assert (dir); - if (what == SHOW_ALL) - showi (INDENT, "pathconf for path '%s':", dir); - else - show ("pathconf for path '%s':", dir); + if (what == SHOW_PATHCONF) { + header (dir); + } else { + header ("pathconf"); + } + + +#if defined (_PC_ALLOC_SIZE_MIN) + show_pathconf (what, dir, _PC_ALLOC_SIZE_MIN); +#endif +#if defined (_PC_ASYNC_IO) + show_pathconf (what, dir, _PC_ASYNC_IO); +#endif + +#if defined (_PC_CHOWN_RESTRICTED) + show_pathconf (what, dir, _PC_CHOWN_RESTRICTED); +#endif + +#if defined (_PC_FILESIZEBITS) + show_pathconf (what, dir, _PC_FILESIZEBITS); +#endif + +#if defined (_PC_LINK_MAX) show_pathconf (what, dir, _PC_LINK_MAX); +#endif + +#if defined (_PC_MAX_CANON) show_pathconf (what, dir, _PC_MAX_CANON); +#endif + +#if defined (_PC_MAX_INPUT) show_pathconf (what, dir, _PC_MAX_INPUT); +#endif + +#if defined (_PC_NAME_MAX) show_pathconf (what, dir, _PC_NAME_MAX); +#endif + +#if defined (_PC_NO_TRUNC) + show_pathconf (what, dir, _PC_NO_TRUNC); +#endif + +#if defined (_PC_PATH_MAX) show_pathconf (what, dir, _PC_PATH_MAX); +#endif + +#if defined (_PC_PIPE_BUF) show_pathconf (what, dir, _PC_PIPE_BUF); - show_pathconf (what, dir, _PC_CHOWN_RESTRICTED); - show_pathconf (what, dir, _PC_NO_TRUNC); +#endif + +#if defined (_PC_PRIO_IO) + show_pathconf (what, dir, _PC_PRIO_IO); +#endif + +#if defined (_PC_REC_INCR_XFER_SIZE) + show_pathconf (what, dir, _PC_REC_INCR_XFER_SIZE); +#endif + +#if defined (_PC_REC_MAX_XFER_SIZE) + show_pathconf (what, dir, _PC_REC_MAX_XFER_SIZE); +#endif + +#if defined (_PC_REC_MIN_XFER_SIZE) + show_pathconf (what, dir, _PC_REC_MIN_XFER_SIZE); +#endif + +#if defined (_PC_REC_XFER_ALIGN) + show_pathconf (what, dir, _PC_REC_XFER_ALIGN); +#endif + +#if defined (_PC_SOCK_MAXBUF) + show_pathconf (what, dir, _PC_SOCK_MAXBUF); +#endif + +#if defined (_PC_SYMLINK_MAX) + show_pathconf (what, dir, _PC_SYMLINK_MAX); +#endif + +#if defined (_PC_2_SYMLINKS) + show_pathconf (what, dir, _PC_2_SYMLINKS); +#endif + +#if defined (_PC_SYNC_IO) + show_pathconf (what, dir, _PC_SYNC_IO); +#endif + +#if defined (_PC_VDISABLE) show_pathconf (what, dir, _PC_VDISABLE); +#endif + + footer (); } const char * @@ -623,23 +1630,25 @@ get_speed (speed_t speed) * * @prefix: string prefix to write, * @indent: number of spaces to indent output to, - * @fmt: printf-style format with associated arguments. + * @fmt: printf-style format with associated arguments that comprises + * the value part. + * + * Write output to @string, indented by @indent spaces. A trailing newline + * will be added. * - * Write output to @string, indented by @indent spaces. * Note that error scenarios cannot call die() as by definition output * may not be possible. **/ void _show (const char *prefix, int indent, const char *fmt, ...) { - int ret; va_list ap; char *buffer = NULL; assert (fmt); if (indent) - appendf (&buffer, "%*s", indent, " "); + appendf (&buffer, "%*s", indent, indent_char); if (prefix && *prefix) appendf (&buffer, "%s: ", prefix); @@ -648,28 +1657,160 @@ _show (const char *prefix, int indent, const char *fmt, ...) appendva (&buffer, fmt, ap); va_end (ap); - appendf (&buffer, "\n"); + append (&buffer, "\n"); + + _show_output (buffer); + + free (buffer); +} + +/** + * entry: + * + * @name: name of thing to display, + * @fmt: printf-style format with associated arguments that comprises + * the value part. + * + * Add name/value pair represented by @name and value comprising + * printf-format string to the @doc global. The value added will be + * indented appropriately. + **/ +void +entry (const char *name, const char *fmt, ...) +{ + va_list ap; + pstring *encoded_name = NULL; + pstring *encoded_value = NULL; + + assert (name); + assert (fmt); + + common_assert (); + + change_element (ELEMENT_TYPE_ENTRY); + + encoded_name = char_to_pstring (name); + if (! encoded_name) + die ("failed to encode name"); + + /* annoyingly, we must encode here; we cannot simply call + * encode_string() once just prior to showing the output + * document since if the output format is XML, we'd end + * up encoding the XML tags themselves, not just the values + * within! + */ + if (encode_string (&encoded_name) < 0) + die ("failed to encode name"); + + /* expand format */ + va_start (ap, fmt); + wmappendva (&encoded_value, fmt, ap); + va_end (ap); + + if (encode_string (&encoded_value) < 0) + die ("failed to encode value"); + + switch (output_format) { + + case OUTPUT_FORMAT_CRUMB: + assert (crumb_list); + + /* Add the bread crumbs */ + PR_LIST_FOREACH (crumb_list, iter) { + char *crumb = (char *)iter->data; + wappendf (&doc, + L"%s%s", + crumb, + crumb_separator); + } + + wappendf (&doc, + L"%ls%s%ls\n", + encoded_name->buf, + text_separator, + encoded_value->buf); + break; + + case OUTPUT_FORMAT_TEXT: + wappendf (&doc, + L"%ls%s%ls", + encoded_name->buf, + text_separator, + encoded_value->buf); + break; + + case OUTPUT_FORMAT_JSON: + wappendf (&doc, + L"\"%ls\" : \"%ls\"", + encoded_name->buf, + encoded_value->buf); + break; + + case OUTPUT_FORMAT_XML: + wappendf (&doc, + L"<entry name=\"%ls\">%ls</entry>", + encoded_name->buf, + encoded_value->buf); + break; + + default: + assert_not_reached (); + break; + } + + pstring_free (encoded_name); + pstring_free (encoded_value); +} + +/** + * _show_output: + * + * @string: String to display. + * + * Write output @string to appropriate location based on Output + * destination. + **/ +void +_show_output_pstring (const pstring *pstr) +{ + char *str; + + assert (pstr); + + str = pstring_to_char (pstr); + + _show_output (str); + + free (str); +} + +void +_show_output (const char *str) +{ + int ret; + + assert (str); switch (output) { case OUTPUT_SYSLOG: - syslog (LOG_INFO, "%s", buffer); + syslog (LOG_INFO, "%s", str); ret = 0; break; case OUTPUT_STDOUT: - ret = fputs (buffer, stdout); + ret = fputs (str, stdout); break; case OUTPUT_STDERR: - ret = fputs (buffer, stderr); + ret = fputs (str, stderr); break; case OUTPUT_TERM: assert (user.tty_fd != -1); - ret = write (user.tty_fd, buffer, strlen (buffer)); + ret = write (user.tty_fd, str, strlen (str)); if (ret < 0) { fprintf (stderr, "ERROR: failed to write to terminal\n"); - goto error; + exit (EXIT_FAILURE); } break; @@ -682,14 +1823,14 @@ _show (const char *prefix, int indent, const char *fmt, ...) if (output_fd < 0) { fprintf (stderr, "ERROR: failed to open file '%s'\n", output_file); - goto error; + exit (EXIT_FAILURE); } } - ret = write (output_fd, buffer, strlen (buffer)); + ret = write (output_fd, str, strlen (str)); if (ret < 0) { fprintf (stderr, "ERROR: failed to write to file '%s'\n", output_file); - goto error; + exit (EXIT_FAILURE); } break; @@ -699,61 +1840,429 @@ _show (const char *prefix, int indent, const char *fmt, ...) break; } - if (ret < 0) - goto error; + if (ret < 0) { + fprintf (stderr, "ERROR: failed to output message\n"); + exit (EXIT_FAILURE); + } +} - free (buffer); +/** + * inc_indent: + * + * Increase indent. + **/ +void +inc_indent (void) +{ + assert (indent >= 0); - return; + indent += indent_amount; +} -error: - if (buffer) - free (buffer); +/** + * dec_indent: + * + * Decrease indent. + **/ +void +dec_indent (void) +{ + assert (indent >= 0); + + indent -= indent_amount; - fprintf (stderr, "ERROR: failed to construct message\n"); - exit (EXIT_FAILURE); + assert (indent >= 0); } /** - * header: - * @fmt: printf-style format with associated arguments. + * add_indent: * - * Display a header to stdout, unless user has requested - * a summary view. - */ + * Insert the current indent to the output document. + **/ void -header (const char *fmt, ...) +add_indent (pstring **doc) { - char buffer[PROCENV_BUFFER]; - va_list ap; - size_t bytes; - size_t ret; + common_assert (); - bytes = sizeof (buffer); + if (! indent) + return; - memset (buffer, '\0', sizeof (buffer)); + if (! strcmp (indent_char, DEFAULT_INDENT_CHAR)) { + wappendf (doc, L"%*s", indent, indent_char); + } else { + pstring *buffer = NULL; - /* Don't display header if user has specified what to display as - * only 1 group of information will be displayed (so a header is - * unnecessary). - */ - if (selected_option) - return; + // Expand the buffer to the appropriate + // length by filling it with spaces and a random + // character. + wappendf (&buffer, L"%*lc", indent, wide_indent_char); - /* append colon */ - va_start (ap, fmt); - ret = vsprintf (buffer, fmt, ap); - assert (ret); - bytes -= ret; - bytes--; + /* Now, replace the spaces and the random character with + * the chosen character. This convoluted approach is + * necessary as printf-type functions don't allow the + * padding character to be specified. + */ + wmemset (buffer->buf, wide_indent_char, wcslen (buffer->buf)); - strncat (buffer, ":", bytes); - va_end (ap); + pappend (doc, buffer); + pstring_free (buffer); + } +} + +/** + * master_header: + * + * @doc: document to write footer to. + * + * Main header which is displayed once. + **/ +void +master_header (pstring **doc) +{ + common_assert (); + + switch (output_format) { + + case OUTPUT_FORMAT_CRUMB: /* FALL */ + case OUTPUT_FORMAT_TEXT: + /* NOP */ + break; + + case OUTPUT_FORMAT_JSON: + object_open (FALSE); + break; - buffer[sizeof (buffer) - 1] = '\0'; + case OUTPUT_FORMAT_XML: + wappend (doc, L"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + wappendf (doc, L"<%s version=\"%s\" package_string=\"%s\" " + "mode=\"%s%s\" format_version=\"%d\">\n", + PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_STRING, + user.euid ? _(NON_STR) "-" : "", + PRIVILEGED_STR, + PROCENV_FORMAT_VERSION); - _show ("", 0, buffer); + inc_indent (); + + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * master_footer: + * + * @doc: document to write footer to. + * + * Main footer which is displayed once. + **/ +void +master_footer (pstring **doc) +{ + common_assert (); + + switch (output_format) { + + case OUTPUT_FORMAT_CRUMB: /* FALL */ + case OUTPUT_FORMAT_TEXT: + /* Tweak */ + wappend (doc, L"\n"); + break; + + case OUTPUT_FORMAT_JSON: + object_close (FALSE); + + /* Tweak */ + wappend (doc, L"\n"); + break; + + case OUTPUT_FORMAT_XML: + /* Tweak */ + wappend (doc, L"\n"); + dec_indent (); + wappendf (doc, L"</%s>\n", PACKAGE_NAME); + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * object_open: + * + * @retain: if TRUE, do not disrupt the current element such that the + * opening of the object will be invisible to the state machine, but + * will still produce the required output. + * + * Note: @retain is only meaningful for OUTPUT_FORMAT_JSON. + * + * Handle opening an object. + **/ +void +object_open (int retain) +{ + common_assert (); + + if (output_format == OUTPUT_FORMAT_JSON) { + if (retain) { + format_element (); + } else { + change_element (ELEMENT_TYPE_OBJECT_OPEN); + } + } else { + /* Objects are only required for handling JSON. In + * fact, they cause problems for other output formats + * that do not have visible "objects" in that they cause + * the state table to lose track of the previous element + * since it is actually the previous-previous element + * (as the pointless object is the previous element). + * + * As such, ignore them. + */ + } + + switch (output_format) { + + case OUTPUT_FORMAT_CRUMB: /* FALL */ + case OUTPUT_FORMAT_TEXT: + /* NOP */ + break; + + case OUTPUT_FORMAT_JSON: + wappend (&doc, L"{"); + break; + + case OUTPUT_FORMAT_XML: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * object_close: + * + * @retain: if TRUE, do not disrupt the current element such that the + * object closure will be invisible to the state machine, but will still + * produce @retain: if TRUE, do not disrupt the current element. + * + * Note: @retain is only meaningful for OUTPUT_FORMAT_JSON. + * + * Handle closing an object. + **/ +void +object_close (int retain) +{ + common_assert (); + + if (output_format == OUTPUT_FORMAT_JSON) { + if (retain) { + format_element (); + } else { + change_element (ELEMENT_TYPE_OBJECT_CLOSE); + } + } else { + /* Objects are only required for handling JSON. In + * fact, they cause problems for other output formats + * that do not have visible "objects" in that they cause + * the state table to lose track of the previous element + * since it is actually the previous-previous element + * (as the pointless object is the previous element). + * + * As such, ignore them. + */ + } + + switch (output_format) { + + case OUTPUT_FORMAT_CRUMB: /* FALL */ + case OUTPUT_FORMAT_TEXT: + /* NOP */ + break; + + case OUTPUT_FORMAT_JSON: + wappend (&doc, L"}"); + break; + + case OUTPUT_FORMAT_XML: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * section_open: + * + * @name: name of section. + * + * Start a new section which will contain >0 entry() calls. + **/ +void +section_open (const char *name) +{ + assert (name); + common_assert (); + + change_element (ELEMENT_TYPE_SECTION_OPEN); + + switch (output_format) { + + case OUTPUT_FORMAT_TEXT: + wappendf (&doc, L"%s:", name); + break; + + case OUTPUT_FORMAT_CRUMB: + add_breadcrumb (name); + break; + + case OUTPUT_FORMAT_JSON: + wappendf (&doc, L"\"%s\" : {", name); + break; + + case OUTPUT_FORMAT_XML: + wappendf (&doc, L"<section name=\"%s\">", name); + break; + + default: + assert_not_reached (); + break; + } +} + +void +section_close (void) +{ + common_assert (); + + change_element (ELEMENT_TYPE_SECTION_CLOSE); + + switch (output_format) { + + case OUTPUT_FORMAT_TEXT: + /* NOP */ + break; + + case OUTPUT_FORMAT_CRUMB: + remove_breadcrumb (); + break; + + case OUTPUT_FORMAT_JSON: + wappend (&doc, L"}"); + break; + + case OUTPUT_FORMAT_XML: + wappend (&doc, L"</section>"); + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * container_open: + * + * @name: name of container. + * + * Start a new container which will contain >0 entry() calls. + * + * This is primarily to handle JSON arrays. + **/ +void +container_open (const char *name) +{ + assert (name); + common_assert (); + + change_element (ELEMENT_TYPE_CONTAINER_OPEN); + + switch (output_format) { + + case OUTPUT_FORMAT_TEXT: + wappendf (&doc, L"%s:", name); + break; + + case OUTPUT_FORMAT_CRUMB: + add_breadcrumb (name); + break; + + case OUTPUT_FORMAT_JSON: + wappendf (&doc, L"\"%s\" : [", name); + break; + + case OUTPUT_FORMAT_XML: + wappendf (&doc, L"<container name=\"%s\">", name); + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * container_close: + * + * Finish with a container. + **/ +void +container_close (void) +{ + common_assert (); + + change_element (ELEMENT_TYPE_CONTAINER_CLOSE); + + switch (output_format) { + + case OUTPUT_FORMAT_TEXT: + /* NOP */ + break; + + case OUTPUT_FORMAT_CRUMB: + remove_breadcrumb (); + break; + + case OUTPUT_FORMAT_JSON: + wappend (&doc, L"]"); + break; + + case OUTPUT_FORMAT_XML: + wappend (&doc, L"</container>"); + break; + + default: + assert_not_reached (); + break; + } +} + +void +header (const char *name) +{ + assert (name); + common_assert (); + + section_open (name); +} + +void +footer (void) +{ + common_assert (); + section_close (); } /** @@ -804,7 +2313,7 @@ fd_valid (int fd) * * In additional to the classical semantics, by careful use of clone(2), * it is possible for a child to inherit its parents dispositions - * (using clones CLONE_SIGHAND+CLONE_VM flags). This is possible since + * (using clone's CLONE_SIGHAND+CLONE_VM flags). This is possible since * the child then shares the parents signal handlers, which inherantly * therefore provide access to the dispositions). **/ @@ -818,7 +2327,7 @@ show_signals (void) sigset_t old_sigset; struct sigaction act; - header ("signals"); + container_open ("signals"); /* Query blocked signals. * @@ -851,15 +2360,27 @@ show_signals (void) blocked = 1; signal_name = get_signal_name (i); + if (! signal_name) + continue; + signal_desc = strsignal (i); - show ("%s ('%s', %d): blocked=%s, ignored=%s", - signal_name ? signal_name : UNKNOWN_STR, - signal_desc ? signal_desc : UNKNOWN_STR, - i, - blocked ? YES_STR : NO_STR, - ignored ? YES_STR : NO_STR); + object_open (FALSE); + + section_open (signal_name); + + entry ("number", "%d", i); + entry ("description", "'%s'", + signal_desc ? signal_desc : UNKNOWN_STR); + entry ("blocked", "%s", blocked ? YES_STR : NO_STR); + entry ("ignored", "%s", ignored ? YES_STR : NO_STR); + + section_close (); + + object_close (FALSE); } + + container_close (); } void @@ -873,20 +2394,32 @@ show_rlimits (void) show_limit (RLIMIT_DATA); show_limit (RLIMIT_FSIZE); - /* why can't we use this? */ -#if 0 - show_limit (RLIMIT_RTTIME); -#endif - #if defined (PROCENV_LINUX) + + if (LINUX_KERNEL_MMR (2, 6, 25)) { +#if defined (RLIMIT_RTTIME) + show_limit (RLIMIT_RTTIME); +#endif + } show_limit (RLIMIT_LOCKS); #endif show_limit (RLIMIT_MEMLOCK); #if defined (PROCENV_LINUX) - show_limit (RLIMIT_MSGQUEUE); - show_limit (RLIMIT_NICE); + + if (LINUX_KERNEL_MMR (2, 6, 8)) { +#if defined (RLIMIT_MSGQUEUE) + show_limit (RLIMIT_MSGQUEUE); +#endif + } + + if (LINUX_KERNEL_MMR (2, 6, 12)) { +#if defined RLIMIT_NICE + show_limit (RLIMIT_NICE); +#endif + } + #endif show_limit (RLIMIT_NOFILE); @@ -897,16 +2430,19 @@ show_rlimits (void) show_limit (RLIMIT_RTPRIO); #endif - /* FIXME */ -#if 0 - show_limit (RLIMIT_RTTIME); -#endif - #if defined (PROCENV_LINUX) + + if (LINUX_KERNEL_MMR (2, 6, 8)) { +#if defined (RLIMIT_SIGPENDING) show_limit (RLIMIT_SIGPENDING); #endif + } + +#endif show_limit (RLIMIT_STACK); + + footer (); } void @@ -914,11 +2450,11 @@ show_rusage (void) { struct rusage usage; - header ("rusage"); - if (getrusage (RUSAGE_SELF, &usage) < 0) die ("unable to query rusage"); + header ("rusage"); + show_usage (usage, ru_maxrss); show_usage (usage, ru_ixrss); show_usage (usage, ru_idrss); @@ -933,10 +2469,12 @@ show_rusage (void) show_usage (usage, ru_nsignals); show_usage (usage, ru_nvcsw); show_usage (usage, ru_nivcsw); + + footer (); } void -dump_sysconf (void) +show_sysconf (void) { struct procenv_map *p; long value; @@ -945,11 +2483,17 @@ dump_sysconf (void) for (p = sysconf_map; p && p->name; p++) { value = get_sysconf (p->num); - show ("%s=%ld", p->name, value); + if (value == -1) + entry (p->name, "%s", NA_STR); + else + entry (p->name, "%ld", value); } + + footer (); } #ifndef PROCENV_ANDROID + void show_confstrs (void) { @@ -958,11 +2502,271 @@ show_confstrs (void) #if defined (_CS_GNU_LIBC_VERSION) show_confstr (_CS_GNU_LIBC_VERSION); #endif + #if defined (_CS_GNU_LIBPTHREAD_VERSION) show_confstr (_CS_GNU_LIBPTHREAD_VERSION); #endif + +#if defined (_CS_LFS64_CFLAGS) + show_confstr (_CS_LFS64_CFLAGS); +#endif + +#if defined (_CS_LFS64_LDFLAGS) + show_confstr (_CS_LFS64_LDFLAGS); +#endif + +#if defined (_CS_LFS64_LIBS) + show_confstr (_CS_LFS64_LIBS); +#endif + +#if defined (_CS_LFS64_LINTFLAGS) + show_confstr (_CS_LFS64_LINTFLAGS); +#endif + +#if defined (_CS_LFS_CFLAGS) + show_confstr (_CS_LFS_CFLAGS); +#endif + +#if defined (_CS_LFS_LDFLAGS) + show_confstr (_CS_LFS_LDFLAGS); +#endif + +#if defined (_CS_LFS_LIBS) + show_confstr (_CS_LFS_LIBS); +#endif + +#if defined (_CS_LFS_LINTFLAGS) + show_confstr (_CS_LFS_LINTFLAGS); +#endif + +#if defined (_CS_PATH) show_confstr (_CS_PATH); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFF32) + show_confstr (_CS_POSIX_V6_ILP32_OFF32); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFF32_CFLAGS) + show_confstr (_CS_POSIX_V6_ILP32_OFF32_CFLAGS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFF32_LDFLAGS) + show_confstr (_CS_POSIX_V6_ILP32_OFF32_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFF32_LIBS) + show_confstr (_CS_POSIX_V6_ILP32_OFF32_LIBS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFF32_LINTFLAGS) + show_confstr (_CS_POSIX_V6_ILP32_OFF32_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS) + show_confstr (_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS) + show_confstr (_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFFBIG_LIBS) + show_confstr (_CS_POSIX_V6_ILP32_OFFBIG_LIBS); +#endif + +#if defined (_CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS) + show_confstr (_CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V6_LP64_OFF64_CFLAGS) + show_confstr (_CS_POSIX_V6_LP64_OFF64_CFLAGS); +#endif + +#if defined (_CS_POSIX_V6_LP64_OFF64_LDFLAGS) + show_confstr (_CS_POSIX_V6_LP64_OFF64_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V6_LP64_OFF64_LIBS) + show_confstr (_CS_POSIX_V6_LP64_OFF64_LIBS); +#endif + +#if defined (_CS_POSIX_V6_LP64_OFF64_LINTFLAGS) + show_confstr (_CS_POSIX_V6_LP64_OFF64_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS) + show_confstr (_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS); +#endif + +#if defined (_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS) + show_confstr (_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V6_LPBIG_OFFBIG_LIBS) + show_confstr (_CS_POSIX_V6_LPBIG_OFFBIG_LIBS); +#endif + +#if defined (_CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS) + show_confstr (_CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFF32_CFLAGS) + show_confstr (_CS_POSIX_V7_ILP32_OFF32_CFLAGS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFF32_LDFLAGS) + show_confstr (_CS_POSIX_V7_ILP32_OFF32_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFF32_LIBS) + show_confstr (_CS_POSIX_V7_ILP32_OFF32_LIBS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFF32_LINTFLAGS) + show_confstr (_CS_POSIX_V7_ILP32_OFF32_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFFBIG_CFLAGS) + show_confstr (_CS_POSIX_V7_ILP32_OFFBIG_CFLAGS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS) + show_confstr (_CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFFBIG_LIBS) + show_confstr (_CS_POSIX_V7_ILP32_OFFBIG_LIBS); +#endif + +#if defined (_CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS) + show_confstr (_CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V7_LP64_OFF64_CFLAGS) + show_confstr (_CS_POSIX_V7_LP64_OFF64_CFLAGS); +#endif + +#if defined (_CS_POSIX_V7_LP64_OFF64_LDFLAGS) + show_confstr (_CS_POSIX_V7_LP64_OFF64_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V7_LP64_OFF64_LIBS) + show_confstr (_CS_POSIX_V7_LP64_OFF64_LIBS); +#endif + +#if defined (_CS_POSIX_V7_LP64_OFF64_LINTFLAGS) + show_confstr (_CS_POSIX_V7_LP64_OFF64_LINTFLAGS); +#endif + +#if defined (_CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS) + show_confstr (_CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS); +#endif + +#if defined (_CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS) + show_confstr (_CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS); +#endif + +#if defined (_CS_POSIX_V7_LPBIG_OFFBIG_LIBS) + show_confstr (_CS_POSIX_V7_LPBIG_OFFBIG_LIBS); +#endif + +#if defined (_CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS) + show_confstr (_CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS); +#endif + +#if defined (_CS_V5_WIDTH_RESTRICTED_ENVS) + show_confstr (_CS_V5_WIDTH_RESTRICTED_ENVS); +#endif + +#if defined (_CS_V5_WIDTH_RESTRICTED_ENVS) + _show_confstr (_CS_V5_WIDTH_RESTRICTED_ENVS, "_XBS5_WIDTH_RESTRICTED_ENVS"); +#endif + +#if defined (_CS_V5_WIDTH_RESTRICTED_ENVS) + _show_confstr (_CS_V5_WIDTH_RESTRICTED_ENVS, "XBS5_WIDTH_RESTRICTED_ENVS"); +#endif + +#if defined (_CS_V6_WIDTH_RESTRICTED_ENVS) + show_confstr (_CS_V6_WIDTH_RESTRICTED_ENVS); +#endif + +#if defined (_CS_V6_WIDTH_RESTRICTED_ENVS) + _show_confstr (_CS_V6_WIDTH_RESTRICTED_ENVS, "POSIX_V6_WIDTH_RESTRICTED_ENVS,_POSIX_V6_WIDTH_RESTRICTED_ENVS"); +#endif + +#if defined (_CS_V7_WIDTH_RESTRICTED_ENVS) + show_confstr (_CS_V7_WIDTH_RESTRICTED_ENVS); +#endif + +#if defined (_CS_XBS5_ILP32_OFF32_CFLAGS) + show_confstr (_CS_XBS5_ILP32_OFF32_CFLAGS); +#endif + +#if defined (_CS_XBS5_ILP32_OFF32_LDFLAGS) + show_confstr (_CS_XBS5_ILP32_OFF32_LDFLAGS); +#endif + +#if defined (_CS_XBS5_ILP32_OFF32_LIBS) + show_confstr (_CS_XBS5_ILP32_OFF32_LIBS); +#endif + +#if defined (_CS_XBS5_ILP32_OFF32_LINTFLAGS) + show_confstr (_CS_XBS5_ILP32_OFF32_LINTFLAGS); +#endif + +#if defined (_CS_XBS5_ILP32_OFFBIG_CFLAGS) + show_confstr (_CS_XBS5_ILP32_OFFBIG_CFLAGS); +#endif + +#if defined (_CS_XBS5_ILP32_OFFBIG_LDFLAGS) + show_confstr (_CS_XBS5_ILP32_OFFBIG_LDFLAGS); +#endif + +#if defined (_CS_XBS5_ILP32_OFFBIG_LIBS) + show_confstr (_CS_XBS5_ILP32_OFFBIG_LIBS); +#endif + +#if defined (_CS_XBS5_ILP32_OFFBIG_LINTFLAGS) + show_confstr (_CS_XBS5_ILP32_OFFBIG_LINTFLAGS); +#endif + +#if defined (_CS_XBS5_LP64_OFF64_CFLAGS) + show_confstr (_CS_XBS5_LP64_OFF64_CFLAGS); +#endif + +#if defined (_CS_XBS5_LP64_OFF64_LDFLAGS) + show_confstr (_CS_XBS5_LP64_OFF64_LDFLAGS); +#endif + +#if defined (_CS_XBS5_LP64_OFF64_LIBS) + show_confstr (_CS_XBS5_LP64_OFF64_LIBS); +#endif + +#if defined (_CS_XBS5_LP64_OFF64_LINTFLAGS) + show_confstr (_CS_XBS5_LP64_OFF64_LINTFLAGS); +#endif + +#if defined (_CS_XBS5_LPBIG_OFFBIG_CFLAGS) + show_confstr (_CS_XBS5_LPBIG_OFFBIG_CFLAGS); +#endif + +#if defined (_CS_XBS5_LPBIG_OFFBIG_LDFLAGS) + show_confstr (_CS_XBS5_LPBIG_OFFBIG_LDFLAGS); +#endif + +#if defined (_CS_XBS5_LPBIG_OFFBIG_LIBS) + show_confstr (_CS_XBS5_LPBIG_OFFBIG_LIBS); +#endif + +#if defined (_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS) + show_confstr (_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS); +#endif + + + footer (); } + #endif void @@ -973,10 +2777,10 @@ get_misc (void) assert (getcwd (misc.cwd, sizeof (misc.cwd))); #if defined (PROCENV_LINUX) - get_root (misc.root, sizeof (misc.root)); + get_canonical (ROOT_PATH, misc.root, sizeof (misc.root)); #endif -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) - get_bsd_misc (); +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) + get_misc_bsd (); #endif } @@ -1007,220 +2811,525 @@ is_console (int fd) #endif void -dump_user (void) +show_proc (void) { header ("process"); - show ("process id (pid): %d", user.pid); + entry ("process id (pid)", "%d", user.pid); - show ("parent process id (ppid): %d", user.ppid); - show ("session id (sid): %d (leader=%s)", + entry ("parent process id (ppid)", "%d", user.ppid); + entry ("session id (sid)", "%d (leader=%s)", user.sid, is_session_leader () ? YES_STR : NO_STR); - show ("name: '%s'", user.proc_name); + entry ("name", "'%s'", user.proc_name); show_proc_branch (); - show ("process group id: %d (leader=%s)", + entry ("process group id", "%d (leader=%s)", user.pgroup, is_process_group_leader () ? YES_STR : NO_STR); - show ("foreground process group: %d", user.fg_pgroup); + entry ("foreground process group", "%d", user.fg_pgroup); - show ("terminal: '%s'", user.ctrl_terminal); + entry ("terminal", "'%s'", user.ctrl_terminal); - show ("has controlling terminal: %s", + entry ("has controlling terminal", "%s", has_ctty () ? YES_STR : NO_STR); #if defined (PROCENV_HURD) - show ("on console: %s", UNKNOWN_STR); + entry ("on console", "%s", UNKNOWN_STR); #else - show ("on console: %s", + entry ("on console", "%s", is_console (user.tty_fd) ? YES_STR : NO_STR); #endif - show ("real user id (uid): %d ('%s')", + section_open ("user"); + + entry ("real user id (uid)", "%d ('%s')", user.uid, get_user_name (user.uid)); - show ("effective user id (euid): %d ('%s')", + entry ("effective user id (euid)", "%d ('%s')", user.euid, get_user_name (user.euid)); - show ("saved set-user-id (suid): %d ('%s')", + entry ("saved set-user-id (suid)", "%d ('%s')", user.suid, get_user_name (user.suid)); - show ("real group id (gid): %d ('%s')", + section_close (); + + section_open ("group"); + + entry ("real group id (gid)", "%d ('%s')", user.gid, get_group_name (user.gid)); - show ("effective group id (egid): %d ('%s')", + entry ("effective group id (egid)", "%d ('%s')", user.egid, get_group_name (user.egid)); - show ("saved set-group-id (sgid): %d ('%s')", + entry ("saved set-group-id (sgid)", "%d ('%s')", user.sgid, get_group_name (user.sgid)); - show ("login name: '%s'", user.login ? user.login : ""); + section_close (); -#if defined (PROCENV_ANDROID) + entry ("login name", "'%s'", user.login ? user.login : ""); + + section_open ("passwd"); + + entry ("name", "'%s'", user.passwd.pw_name); + +#if ! defined (PROCENV_ANDROID) /* No gecos on Android. In fact it doesn't actually use the * passwd database, but meh. */ - show ("passwd: name='%s', dir='%s', shell='%s'", - user.passwd.pw_name, - user.passwd.pw_dir, - user.passwd.pw_shell); -#else - show ("passwd: name='%s', gecos='%s', dir='%s', shell='%s'", - user.passwd.pw_name, - user.passwd.pw_gecos, - user.passwd.pw_dir, - user.passwd.pw_shell); + entry ("gecos", "'%s'", user.passwd.pw_gecos); #endif + + entry ("dir", "'%s'", user.passwd.pw_dir); + entry ("shell", "'%s'", user.passwd.pw_shell); + + section_close (); + show_all_groups (); + + footer (); } void -dump_priorities (void) +show_priorities (void) { - show ("scheduler priority: process=%d, process group=%d, user=%d", - priority.process, - priority.pgrp, - priority.user); +#if defined (PROCENV_LINUX) + int sched; + + sched = sched_getscheduler (0); +#endif + + section_open ("scheduler"); + +#if defined (PROCENV_LINUX) + entry ("type", "%s", + sched < 0 ? UNKNOWN_STR : + get_scheduler_name (sched)); +#endif + + section_open ("priority"); + + entry ("process", "%d", priority.process); + entry ("process group", "%d", priority.pgrp); + entry ("user", "%d", priority.user); + + section_close (); + + section_close (); } void -dump_misc (void) +show_misc (void) { +#if defined (PROCENV_LINUX) + int domain = 0x0; + + /* magic value - see personality(2) */ + unsigned int persona = 0xFFFFFFFF; + + unsigned int mask = PER_MASK; + + const char *personality_name = NULL; + char *personality_flags = NULL; +#endif + header ("misc"); - show ("umask: %4.4o", misc.umask_value); - show ("current directory (cwd): '%s'", misc.cwd); + entry ("umask", "%4.4o", misc.umask_value); + entry ("current directory (cwd)", "'%s'", misc.cwd); #if defined (PROCENV_LINUX) - show ("root: %s%s%s", + entry ("root", "%s%s%s", strcmp (misc.root, UNKNOWN_STR) ? "'" : "", misc.root, strcmp (misc.root, UNKNOWN_STR) ? "'" : ""); #endif - show ("chroot: %s", in_chroot () ? YES_STR : NO_STR); + entry ("chroot", "%s", in_chroot () ? YES_STR : NO_STR); + entry ("container", "%s", container_type ()); + #if defined (PROCENV_LINUX) - show_linux_prctl (); - show_linux_security_module (); - show_linux_security_module_context (); -#endif - show ("container: %s", container_type ()); + show_prctl_linux (); - show_cpu (); - dump_priorities (); - show ("memory page size: %d bytes", getpagesize ()); + section_open ("linux security module"); + show_security_module_linux (); + show_security_module_context_linux (); + section_close (); +#endif #if defined (PROCENV_LINUX) #ifdef LINUX_VERSION_CODE - show ("kernel headers version: %u.%u.%u", + entry ("kernel headers version", "%u.%u.%u", (LINUX_VERSION_CODE >> 16), ((LINUX_VERSION_CODE >> 8) & 0xFF), (LINUX_VERSION_CODE & 0xFF)); #endif + + domain = personality (persona); + + personality_name = get_personality_name (domain & mask); + + section_open ("personality"); + + entry ("type", "%s", personality_name + ? personality_name + : UNKNOWN_STR); + + personality_flags = get_personality_flags (domain & (-1 ^ mask)); + entry ("flags", "%s", + personality_flags + ? personality_flags + : ""); + + section_close (); + + if (personality_flags) + free (personality_flags); #endif + + footer (); } void -dump_platform (void) +show_platform (void) { long kernel_bits; long executable_bits; header ("platform"); - show ("operating system: %s", get_os ()); - show ("architecture: %s", get_arch ()); + entry ("operating system", "%s", get_os ()); + entry ("architecture", "%s", get_arch ()); kernel_bits = get_kernel_bits (); executable_bits = sizeof (void *) * CHAR_BIT * sizeof (char); if (kernel_bits == -1) - show ("kernel bits: %s", UNKNOWN_STR); + entry ("kernel bits", "%s", UNKNOWN_STR); else - show ("kernel bits: %lu", kernel_bits); + entry ("kernel bits", "%lu", kernel_bits); - show ("executable bits: %lu", executable_bits); + entry ("executable bits", "%lu", executable_bits); - show ("code endian: %s", + entry ("code endian", "%s", is_big_endian () ? BIG_STR : LITTLE_STR); + show_data_model (); + + footer (); } void show_cpu (void) { + header ("cpu"); + #if defined (PROCENV_LINUX) - show_linux_cpu (); - show_linux_scheduler (); + show_cpu_linux (); #endif #if defined (PROCENV_BSD) - show_bsd_cpu (); + show_cpu_bsd (); #endif + show_cpu_affinities (); + + show_priorities (); + + footer (); } void -dump_fds (void) +show_memory (void) { - int fd; - int max; + header ("memory"); - header ("fds"); - max = sysconf (_SC_OPEN_MAX); + entry ("page size", "%d bytes", getpagesize ()); - for (fd = 0; fd < max; fd++) { - if (fd_valid (fd)) { - int is_tty = isatty (fd); - char *name = NULL; +#if defined (PROCENV_LINUX) + show_numa_memory (); +#endif /* PROCENV_LINUX */ - if (is_tty) { -#if ! defined (PROCENV_ANDROID) - name = ttyname (fd); + footer (); +} + +/* Display cpu affinities in the same compressed but reasonably + * human-readable fashion as /proc/self/status:Cpus_allowed_list under Linux. + */ +void +show_cpu_affinities (void) +{ + int ret = 0; + long max; + size_t size; +#if defined (PROCENV_BSD) || ! defined (CPU_ALLOC) + PROCENV_CPU_SET_TYPE cpu_set_real; #endif - show ("fd %d: terminal=%s ('%s')", fd, - is_tty ? YES_STR : NO_STR, - name ? name : NA_STR); - } else { - show ("fd %d: terminal=%s", fd, NO_STR); + PROCENV_CPU_SET_TYPE *cpu_set; + long cpu; + char *cpu_list = NULL; + + /* TRUE if any enabled cpus have been displayed yet */ + int displayed = FALSE; + + /* Number of cpu's in *current* range */ + size_t count = 0; + + /* Only valid to read these when count is >0 */ + size_t last = 0; + size_t first = 0; + + max = get_sysconf (_SC_NPROCESSORS_ONLN); + if (max < 0) + die ("failed to query cpu count"); + +#if defined (PROCENV_LINUX) || defined (PROCENV_GNU_BSD) || defined (PROCENV_HURD) + +#if defined (CPU_ALLOC) + + cpu_set = CPU_ALLOC (max); + assert (cpu_set); + + size = CPU_ALLOC_SIZE (max); + CPU_ZERO_S (size, cpu_set); + +#else /* ! CPU_ALLOC */ + + /* RHEL 5 */ + + cpu_set = &cpu_set_real; + + size = sizeof (PROCENV_CPU_SET_TYPE); + +#endif /* CPU_ALLOC */ + +#elif defined (PROCENV_BSD) || ! defined (CPU_ALLOC) + cpu_set = &cpu_set_real; + + size = sizeof (PROCENV_CPU_SET_TYPE); + +#endif /* PROCENV_LINUX || PROCENV_GNU_BSD || PROCENV_HURD */ + + /* We could use sched_getaffinity(2) rather than + * sched_getaffinity() on Linux (2.5.8+) but + * pthread_getaffinity_np() provides the same information... + * Except it is missing on kFreeBSD systems (!) so we have to + * use sched_getaffinity() there. :( + */ +#if (defined (PROCENV_GNU_BSD) || defined (PROCENV_HURD)) || (defined (PROCENV_LINUX) && ! defined (CPU_ALLOC)) + ret = sched_getaffinity (0, size, cpu_set); +#else + +#if defined (PROCENV_LINUX) && defined (CPU_ALLOC) + /* On a hyperthreaded system, "size" as above may not actually + be big enough, and we get EINVAL. (hwloc has a similar + workaround.) */ + + { + int mult = 0; + while ((ret = pthread_getaffinity_np (pthread_self (), size, cpu_set)) + == EINVAL) { + CPU_FREE (cpu_set); + /* Bail out at an arbitrary value. */ + if (mult > 128) break; + mult += 2; + cpu_set = CPU_ALLOC (mult * max); + size = CPU_ALLOC_SIZE (mult * max); + CPU_ZERO_S (size, cpu_set); + } + } +#endif /* PROCENV_LINUX && CPU_ALLOC */ + +#endif + + if (ret) + goto out; + + for (cpu = 0; cpu < max; cpu++) { + + if (CPU_ISSET (cpu, cpu_set)) { + + /* Record first entry in the range */ + if (! count) + first = cpu; + + last = cpu; + count++; + } else { + if (count) { + if (first == last) { + appendf (&cpu_list, "%s%d", + displayed ? "," : "", + first); + } else { + appendf (&cpu_list, "%s%d-%d", + displayed ? "," : "", + first, last); + } + displayed = TRUE; } + + /* Reset */ + count = 0; + } + } + + if (count) { + if (first == last) { + appendf (&cpu_list, "%s%d", + displayed ? "," : "", + first); + } else { + appendf (&cpu_list, "%s%d-%d", + displayed ? "," : "", + first, last); } } +out: + entry ("affinity list", "%s", cpu_list ? cpu_list : "-1"); + +#if defined (PROCENV_LINUX) || defined (PROCENV_GNU_BSD) || defined (PROCENV_HURD) +#if defined (CPU_ALLOC) + CPU_FREE (cpu_set); +#endif +#endif + + free (cpu_list); +} + + +void +show_fds (void) +{ #if defined (PROCENV_LINUX) - dump_linux_proc_fds (); + show_fds_linux (); +#else + show_fds_generic (); +#endif + +} + +void +show_namespaces (void) +{ +#if defined (PROCENV_LINUX) + show_namespaces_linux (); +#else + show_namespaces_stub (); +#endif + +} + +void +show_fds_generic (void) +{ + int fd; + int max; + + container_open ("file descriptors"); + + max = get_sysconf (_SC_OPEN_MAX); + + for (fd = 0; fd < max; fd++) { + int is_tty = isatty (fd); + char *name = NULL; + char *num = NULL; + + if (! fd_valid (fd)) + continue; + +#if ! defined (PROCENV_ANDROID) + name = ttyname (fd); +#endif + appendf (&num, "%d", fd); + + object_open (FALSE); + + section_open (num); + + entry ("terminal", "%s", is_tty ? YES_STR : NO_STR); + entry ("valid", "%s", fd_valid (fd) ? YES_STR : NO_STR); + entry ("device", "%s", name ? name : NA_STR); + + section_open ("capabilities"); +#if defined (PROCENV_BSD) && defined (HAVE_SYS_CAPABILITY_H) + show_capabilities_bsd (fd); #endif + section_close (); + + section_close (); + + object_close (FALSE); + free (num); + } + + container_close (); } void show_env (void) { char **env = environ; + char **copy; + char *name; + char *value; size_t i; + size_t max; header ("environment"); /* Calculate size of environment array */ - for (i=0; env[i]; i++) + for (max=0; env[max]; max++) ; - /* sort it */ + copy = calloc (1+max, sizeof (char *)); + if (! copy) + die ("failed to alloate storage"); + + /* Copy the environ array since modification is not allowed */ + for (i=0; i < max; i++) { + copy[i] = calloc (1, 1+strlen (env[i])); + if (! copy[i]) + die ("failed to alloate storage"); + strcpy (copy[i], env[i]); + } + + /* Terminate */ + copy[max] = NULL; + + /* Sort */ + env = copy; qsort (env, i, sizeof (env[0]), qsort_compar); - env = environ; + /* Display */ + env = copy; while (env && *env) { - show ("%s", *env); + name = *env; + value = strchr (name, '='); + assert (value); + *value = '\0'; + value++; + + entry (name, "%s", value); env++; } + + /* Tidy up */ + for (i = 0; i < max; i++) + free (copy[i]); + + free (copy); + + footer (); } int @@ -1308,82 +3417,462 @@ get_user_info (void) assert (p == (void *)&user.passwd); } -/* append @new to @orig */ +/* append @src to @dest */ void -append (char **str, const char *new) +append (char **dest, const char *src) { - size_t len; - size_t total; + size_t len; - assert (str); - assert (new); + assert (dest); + assert (src); - if (! *str) - *str = strdup (""); + len = strlen (src); - assert (*str); + appendn (dest, src, len); +} - /* +1 for terminating nul */ - total = strlen (*str) + 1; +/* Version of append() that operates on a wide string @dest and @src */ +void +wappend (pstring **dest, const wchar_t *src) +{ + size_t len; - len = strlen (new); + assert (dest); + assert (src); - /* +1 for comma-delimiter */ - total += len + 1; + len = wcslen (src); - *str = realloc (*str, total); - assert (*str); - strcat (*str, new); + wappendn (dest, src, len); } -/* append fmt and args to @str */ +/* Version of append() that operates on a wide string @dest + * and multi-byte @src. + */ void -appendf (char **str, const char *fmt, ...) +wmappend (pstring **dest, const char *src) { - va_list ap; - char *new = NULL; + wchar_t *wide_src = NULL; - assert (str); - assert (fmt); + assert (dest); + assert (src); - if (! *str) - *str = strdup (""); + wide_src = char_to_wchar (src); + if (! wide_src) + die ("failed to allocate space for wide string"); - assert (*str); + wappend (dest, wide_src); - va_start (ap, fmt); + free (wide_src); +} - if (vasprintf (&new, fmt, ap) < 0) { - perror ("vasprintf"); - exit (EXIT_FAILURE); - } +/** + * appendn: + * + * @dest: [output] string to append to, + * @src: string to append to @dest, + * @len: length of @new. + * + * Append first @len bytes of @new to @str, + * ensuring result is nul-terminated. + **/ +void +appendn (char **dest, const char *src, size_t len) +{ + size_t total; - va_end (ap); + assert (dest); + assert (src); + + if (! len) + return; - append (str, new); - free (new); + if (! *dest) + *dest = strdup (""); + if (! *dest) + die ("failed to allocate space for string"); + + /* +1 for terminating nul */ + total = strlen (*dest) + 1; + + total += len; + + *dest = realloc (*dest, total); + assert (*dest); + + if (! *dest) { + /* string is empty, so initialise the memory to avoid + * surprises with strncat() being unable to find the + * terminator! + */ + memset (*dest, '\0', total); + } + + strncat (*dest, src, len); + + assert ((*dest)[total-1] == '\0'); } +/* Version of appendn() that operates on a wide string @dest and @src */ void -appendva (char **str, const char *fmt, va_list ap) +wappendn (pstring **dest, const wchar_t *src, size_t len) { - char *new = NULL; + wchar_t *p; + size_t total; + size_t bytes; + + assert (dest); + assert (src); - assert (str); - assert (fmt); + if (! len) + return; - if (! *str) - *str = strdup (""); + if (! *dest) + *dest = pstring_new (); + if (! *dest) + die ("failed to allocate space for pstring"); - assert (*str); + total = (*dest)->len + len; - if (vasprintf (&new, fmt, ap) < 0) { - perror ("vasprintf"); - exit (EXIT_FAILURE); - } + /* +1 for terminating nul */ + bytes = (1 + total) * sizeof (wchar_t); + + p = realloc ((*dest)->buf, bytes); + + /* FIXME: turn into die() [all occurences!] */ + assert (p); + + (*dest)->buf = p; + + if (! (*dest)->len) { + /* pstring is empty, so initialise the memory to avoid + * surprises with wcsncat() being unable to find the + * terminator! + */ + memset ((*dest)->buf, 0, bytes); + } + + /* Used to check for overrun */ + (*dest)->buf[total] = L'\0'; + + wcsncat ((*dest)->buf + (*dest)->len, src, len); + + /* update */ + (*dest)->len = total; + (*dest)->size = bytes; + + /* check for overrun */ + assert ((*dest)->buf[total] == L'\0'); +} + + +/* Version of appendn() that operates on a wide string @dest and + * multi-byte @src. + */ +void +wmappendn (pstring **dest, const char *src, size_t len) +{ + wchar_t *wide_src = NULL; + + assert (dest); + assert (src); + + if (! len) + return; + + wide_src = char_to_wchar (src); + if (! wide_src) + die ("failed to allocate space for wide string"); + + wappendn (dest, wide_src, wcslen (wide_src)); + + free (wide_src); +} + +/* append @fmt and args to @dest */ +void +appendf (char **dest, const char *fmt, ...) +{ + va_list ap; + + assert (dest); + assert (fmt); + + va_start (ap, fmt); + + appendva (dest, fmt, ap); + + va_end (ap); +} + +/* Version of appendf() that operates on a wide string @dest + * and @fmt. + */ +void +wappendf (pstring **dest, const wchar_t *fmt, ...) +{ + va_list ap; + + assert (dest); + assert (fmt); + + va_start (ap, fmt); + + wappendva (dest, fmt, ap); + + va_end (ap); +} + +/* Version of appendf() that operates on a wide string @dest + * and multi-byte @fmt. + */ +void +wmappendf (pstring **dest, const char *fmt, ...) +{ + wchar_t *wide_fmt = NULL; + va_list ap; + + assert (dest); + assert (fmt); + + wide_fmt = char_to_wchar (fmt); + if (! wide_fmt) + die ("failed to allocate memory for wide format"); + + va_start (ap, fmt); + + wappendva (dest, wide_fmt, ap); + + va_end (ap); + + free (wide_fmt); +} + +/* append @fmt and args to @dest */ +void +appendva (char **dest, const char *fmt, va_list ap) +{ + int ret; + char *new = NULL; + char *p; + size_t bytes; + + /* Start with a guess for how big we think the buffer needs to + * be. + */ + size_t len = DEFAULT_ALLOC_GUESS_SIZE; + + assert (dest); + assert (fmt); + + bytes = (1 + len) * sizeof (char); + + /* we could use vasprintf(3), but that's GNU-specific and hence + * not available everywhere we need it. + */ + new = malloc (bytes); + if (! new) + die ("failed to allocate space for string"); + + memset (new, '\0', bytes); + + /* keep on increasing size of buffer until the translation + * succeeds. + */ + while (TRUE) { + va_list ap_copy; + + va_copy (ap_copy, ap); + ret = vsnprintf (new, len, fmt, ap_copy); + va_end (ap_copy); + + if (ret < 0) + die ("failed to format string"); + + if ((size_t)ret < len) { + /* now we have sufficient space */ + break; + } + + /* Bump to allow one char to be written */ + len++; + + /* recalculate number of bytes */ + bytes = (1 + len) * sizeof (char); + + p = realloc (new, bytes); + if (! p) + die ("failed to allocate space for string"); + + new = p; + } + + if (*dest) { + append (dest, new); + free (new); + } else { + *dest = new; + } +} + +/* Version of appendva() that operates on a wide string @dest + * and @fmt. + */ +void +wappendva (pstring **dest, const wchar_t *fmt, va_list ap) +{ + int ret; + wchar_t *new = NULL; + wchar_t *p; + size_t bytes; + va_list ap_copy; + + /* Start with a guess for how big we think the buffer needs to + * be. + */ + size_t len = DEFAULT_ALLOC_GUESS_SIZE; + + assert (dest); + assert (fmt); + + bytes = (1 + len) * sizeof (wchar_t); + + new = malloc (bytes); + if (! new) + die ("failed to allocate space for wide string"); + + memset (new, '\0', bytes); + + /* keep on increasing size of buffer until the translation + * succeeds. + */ + while (TRUE) { + va_copy (ap_copy, ap); + ret = vswprintf (new, len, fmt, ap_copy); + va_end (ap_copy); + + if ((size_t)ret < len) { + /* now we have sufficient space, so update for + * actual number of bytes used (including the + * terminator!) + * + * Note that, conveniently, if the string is + * zero-characters long (ie ""), ret will be -1 + * which we correct to 0. + */ + len = ret + 1; + + break; + } + + /* Bump to allow one more wide-char to be written */ + len++; + + /* recalculate number of bytes */ + bytes = (1 + len) * sizeof (wchar_t); + + p = realloc (new, bytes); + if (! p) + die ("failed to allocate space for string"); + + new = p; + + memset (new, '\0', bytes); + } + + if (*dest) { + wappend (dest, new); + free (new); + } else { + wchar_t *n; + + /* recalculate number of bytes */ + bytes = (1 + len) * sizeof (wchar_t); + + /* compress */ + n = realloc (new, bytes); + + if (! n) + die ("failed to reallocate space"); + + new = n; + + (*dest) = pstring_new (); + assert (*dest); + (*dest)->buf = new; + (*dest)->len = len; + (*dest)->size = bytes; + } +} + +/* Version of appendva() that operates on a wide string @dest + * and multi-byte @fmt. + */ +void +wmappendva (pstring **dest, const char *fmt, va_list ap) +{ + wchar_t *wide_fmt = NULL; + va_list ap_copy; + + assert (dest); + assert (fmt); + + wide_fmt = char_to_wchar (fmt); + if (! wide_fmt) + die ("failed to allocate memory for wide format"); + + va_copy (ap_copy, ap); + wappendva (dest, wide_fmt, ap_copy); + va_end (ap_copy); - append (str, new); - free (new); + free (wide_fmt); +} + +/* + * Append @src onto the end of @dest. + */ +void +pappend (pstring **dest, const pstring *src) +{ + size_t total; + size_t bytes; + wchar_t *p; + + assert (dest); + assert (src); + + if (! src->len) + return; + + if (! *dest) + *dest = pstring_new (); + if (! *dest) + die ("failed to allocate space for pstring"); + + total = (*dest)->len + src->len; + + /* adjust since we only store _one_ of the string terminators + * from @dest and @src. + */ + total--; + + /* +1 for terminating nul */ + bytes = (1 + total) * sizeof (wchar_t); + + p = realloc ((*dest)->buf, bytes); + + /* FIXME: turn into die() [all occurences!] */ + assert (p); + + (*dest)->buf = p; + + wcsncat ((*dest)->buf + (*dest)->len, src->buf, src->len); + + /* update */ + (*dest)->len = total; + (*dest)->size = bytes; + + /* Used to check for overrun */ + (*dest)->buf[total] = L'\0'; } void @@ -1391,7 +3880,7 @@ show_all_groups (void) { int i; int ret; - char *str; + char *str = NULL; /* Initial number of groups we'll try to read. If this isn't * enough, we increase it to make rooom for all available @@ -1400,25 +3889,32 @@ show_all_groups (void) int size = 32; gid_t *groups = NULL; + gid_t *g; char **group_names = NULL; + size_t bytes; + + bytes = size * sizeof (gid_t); - groups = malloc (size * sizeof (gid_t)); + groups = malloc (bytes); if (! groups) goto error; + memset (groups, '\0', bytes); + while (TRUE) { ret = getgroups (size, groups); if (ret >= 0) break; size++; - groups = realloc (groups, (size * sizeof (gid_t))); - if (! groups) + g = realloc (groups, (size * sizeof (gid_t))); + if (! g) { + free (groups); goto error; - } + } - str = strdup ("groups:"); - assert (str); + groups = g; + } size = ret; @@ -1429,15 +3925,14 @@ show_all_groups (void) group = get_group_name (user.passwd.pw_gid); if (! group) { - show (str); - free (str); + entry ("groups", "%s", UNKNOWN_STR); return; } appendf (&str, " '%s' (%d)", group, user.passwd.pw_gid); - show (str); + entry ("groups", "%s", str); free (str); return; } @@ -1450,10 +3945,8 @@ show_all_groups (void) appendf (&str, " "); for (i = 0; i < size; i++) { - ret = asprintf (&group_names[i], "'%s' (%d)", + appendf (&group_names[i], "'%s' (%d)", get_group_name (groups[i]), groups[i]); - if (ret < 0) - die ("unable to create group entry"); } qsort (group_names, size, sizeof (char *), qsort_compar); @@ -1468,7 +3961,7 @@ show_all_groups (void) free (group_names); free (groups); - show (str); + entry ("groups", "%s", str); free (str); return; @@ -1478,15 +3971,63 @@ error: } void -set_indent (void) +save_locale (void) { - indent = selected_option ? 0 : INDENT; + char *value; + + assert (! saved_locale); + + /* save the existing value, but crucially also load the correct + * locale from the environment to enable UTF-8 functionality. + */ + value = setlocale (LC_ALL, ""); + if (! value) { + /* Can't determine locale, so ignore */ + return; + } + + saved_locale = strdup (value); + if (! saved_locale) + die ("failed to allocate space for locale"); + + if (atexit (restore_locale)) + die ("failed to register exit handler"); +} + +void +restore_locale (void) +{ + if (! saved_locale) { + /* Nothing to do */ + return; + } + + (void)setlocale (LC_ALL, saved_locale); + + free (saved_locale); +} + +void +handle_indent_char (void) +{ + size_t len; + + const char *new = indent_char; + + len = mbsrtowcs (NULL, &new, 0, NULL); + if (len != 1) + die ("invalid indent character"); + + if (mbsrtowcs (&wide_indent_char, &new, len, NULL) != len) + die ("failed to convert indent character"); } void init (void) { - set_indent (); + save_locale (); + + handle_indent_char (); /* required to allow for more graceful handling of prctl(2) * options that were introduced in kernel version 'x.y'. @@ -1496,12 +4037,14 @@ init (void) get_user_info (); get_misc (); get_priorities (); - } void cleanup (void) { + common_assert (); + assert (doc); + close (user.tty_fd); if (output_fd != -1) @@ -1509,6 +4052,13 @@ cleanup (void) if (output == OUTPUT_SYSLOG) closelog (); + + if (output_format == OUTPUT_FORMAT_CRUMB && crumb_list) { + clear_breadcrumbs (); + free (crumb_list); + } + + pstring_free (doc); } /** @@ -1528,27 +4078,61 @@ is_big_endian (void) } void -dump_meta (void) +show_meta (void) { - header (PACKAGE_NAME); + const char *build_type; + +#if defined (PROCENV_REPRODUCIBLE_BUILD) + build_type = BUILD_TYPE_REPRODUCIBLE_STR; +#else + build_type = BUILD_TYPE_STD_STR; +#endif + + header ("meta"); + + entry ("version", "%s", PACKAGE_VERSION); - show ("version: %s", PACKAGE_STRING); - show ("mode: %s%s", + entry ("package", "%s", PACKAGE_STRING); + entry ("mode", "%s%s", user.euid ? _(NON_STR) "-" : "", PRIVILEGED_STR); + + entry ("build-type", "%s", build_type); + + entry ("format-type", "%s", get_output_format_name ()); + entry ("format-version", "%d", PROCENV_FORMAT_VERSION); + + footer (); } void show_arguments (void) { - int i; + int i; header ("arguments"); - show ("count: %u", argvc); + entry ("count", "%u", argvc); + + container_open ("list"); + + for (i = 0; i < argvc; i++) { + char *buffer = NULL; - for (i = 0; i < argvc; i++) - show ("argv[%d]='%s'", i, argvp[i]); + appendf (&buffer, "argv[%d]", i); + + object_open (FALSE); + + entry (buffer, "%s", argvp[i]); + + object_close (FALSE); + + free (buffer); + } + + container_close (); + + footer (); } void @@ -1556,10 +4140,11 @@ show_stat (void) { struct stat st; char real_path[PATH_MAX]; - char formatted_time[32]; - char modestr[10+1]; + char formatted_atime[32]; + char formatted_ctime[32]; + char formatted_mtime[32]; + char *modestr; mode_t perms; - int i = 0; char *tmp = NULL; assert (program_name); @@ -1577,32 +4162,24 @@ show_stat (void) die ("unable to stat path: '%s'", real_path); header ("stat"); - show ("argv[0]: '%s'", program_name); - show ("real path: '%s'", real_path); - show ("dev: major=%u, minor=%u", - major (st.st_dev), minor (st.st_dev)); - show ("inode: %lu", (unsigned long int)st.st_ino); - memset (modestr, '\0', sizeof (modestr)); + entry ("argv[0]", "'%s'", program_name); + entry ("real path", "'%s'", real_path); - modestr[i++] = (S_ISLNK (st.st_mode & S_IFMT)) ? 'l' : '-'; + section_open ("device"); - perms = (st.st_mode & S_IRWXU); - modestr[i++] = (perms & S_IRUSR) ? 'r' : '-'; - modestr[i++] = (perms & S_IWUSR) ? 'w' : '-'; - modestr[i++] = (perms & S_IXUSR) ? 'x' : '-'; + entry ("major", "%u", major (st.st_dev)); + entry ("minor", "%u", minor (st.st_dev)); - perms = (st.st_mode & S_IRWXG); - modestr[i++] = (perms & S_IRGRP) ? 'r' : '-'; - modestr[i++] = (perms & S_IWGRP) ? 'w' : '-'; - modestr[i++] = (perms & S_IXGRP) ? 'x' : '-'; + section_close (); - perms = (st.st_mode & S_IRWXO); - modestr[i++] = (perms & S_IROTH) ? 'r' : '-'; - modestr[i++] = (perms & S_IWOTH) ? 'w' : '-'; - modestr[i++] = (perms & S_IXOTH) ? 'x' : '-'; + entry ("inode", "%lu", (unsigned long int)st.st_ino); + modestr = format_perms (st.st_mode); + if (! modestr) + die ("failed to allocate space for permissions string"); perms = (st.st_mode &= ~S_IFMT); + if (perms & S_ISUID) modestr[3] = 's'; if (perms & S_ISGID) @@ -1610,28 +4187,45 @@ show_stat (void) if (perms & S_ISVTX) modestr[9] = 't'; - show ("permissions: %4.4o (%s)", perms, modestr); + section_open ("permissions"); + + entry ("octal", "%4.4o", perms); + entry ("symbolic", "%s", modestr); + free (modestr); + + section_close (); - show ("hard links: %u", st.st_nlink); - show ("user id (uid): %d ('%s')", st.st_uid, get_user_name (st.st_uid)); - show ("group id (gid): %d ('%s')", st.st_gid, get_group_name (st.st_uid)); - show ("size: %lu bytes (%lu 512-byte blocks)", st.st_size, st.st_blocks); + entry ("hard links", "%u", st.st_nlink); + entry ("user id (uid)", "%d ('%s')", st.st_uid, get_user_name (st.st_uid)); + entry ("group id (gid)", "%d ('%s')", st.st_gid, get_group_name (st.st_uid)); + entry ("size", "%lu bytes (%lu 512-byte blocks)", st.st_size, st.st_blocks); - if (! ctime_r ((time_t *)&st.st_atime, formatted_time)) + /*****************************************/ + section_open ("times"); + + if (! ctime_r ((time_t *)&st.st_atime, formatted_atime)) die ("failed to format atime"); - formatted_time[ strlen (formatted_time)-1] = '\0'; - show ("atime: %lu (%s)", st.st_atime, formatted_time); + formatted_atime[ strlen (formatted_atime)-1] = '\0'; + entry ("atime (access)", "%lu (%s)", st.st_atime, formatted_atime); - if (! ctime_r ((time_t *)&st.st_mtime, formatted_time)) + if (! ctime_r ((time_t *)&st.st_mtime, formatted_mtime)) die ("failed to format mtime"); - formatted_time[ strlen (formatted_time)-1] = '\0'; - show ("mtime: %lu (%s)", st.st_mtime, formatted_time); + formatted_mtime[ strlen (formatted_mtime)-1] = '\0'; + + entry ("mtime (modification)", "%lu (%s)", st.st_mtime, formatted_mtime); - if (! ctime_r ((time_t *)&st.st_ctime, formatted_time)) + if (! ctime_r ((time_t *)&st.st_ctime, formatted_ctime)) die ("failed to format ctime"); - formatted_time[ strlen (formatted_time)-1] = '\0'; - show ("ctime: %lu (%s)", st.st_ctime, formatted_time); + formatted_ctime[ strlen (formatted_ctime)-1] = '\0'; + + entry ("ctime (status change)", "%lu (%s)", st.st_ctime, formatted_ctime); + + section_close (); + + /*****************************************/ + + footer (); } long @@ -1641,9 +4235,9 @@ get_kernel_bits (void) long value; errno = 0; - value = sysconf (_SC_LONG_BIT); + value = get_sysconf (_SC_LONG_BIT); if (value == -1 && errno != 0) - die ("failed to determine kernel bits"); + return -1; return value; #endif return -1; @@ -1653,49 +4247,55 @@ get_kernel_bits (void) void dump (void) { - dump_meta (); + master_header (&doc); + + show_meta (); show_arguments (); -#if defined (PROCENV_LINUX) show_capabilities (); - show_linux_cgroups (); -#endif + show_cgroups (); show_clocks (); show_compiler (); #ifndef PROCENV_ANDROID show_confstrs (); #endif + show_cpu (); show_env (); - dump_fds (); + show_fds (); + show_libc (); #ifndef PROCENV_ANDROID show_libs (); #endif show_rlimits (); show_locale (); - dump_misc (); + show_memory (); + show_msg_queues (); + show_misc (); show_mounts (SHOW_ALL); + show_namespaces (); show_network (); -#if defined (PROCENV_LINUX) show_oom (); -#endif - dump_platform (); - dump_user (); + show_platform (); + show_proc (); show_ranges (); /* We should really call this last, to make figures as reliable * as possible. */ show_rusage (); - + show_semaphores (); + show_shared_mem (); show_signals (); show_sizeof (); show_stat (); - dump_sysconf (); + show_sysconf (); show_threads (); show_time (); show_timezone (); show_tty_attrs (); - dump_uname (); + show_uname (); + + master_footer (&doc); } void get_network_address (const struct sockaddr *address, int family, char *name) @@ -1731,57 +4331,52 @@ get_network_address (const struct sockaddr *address, int family, char *name) assert (name[NI_MAXHOST-1] == '\0'); } -char * +void decode_if_flags (unsigned int flags) { - char *str = NULL; struct if_flag_map *p; - int first = TRUE; for (p = if_flag_map; p && p->name; p++) { if (flags & p->flag) { - appendf (&str, "%s%s", - first ? "" : ",", - p->name); - first = FALSE; + object_open (FALSE); + entry (p->name, "0x%x", p->flag); + object_close (FALSE); } } - - return str; } const char * get_ipv6_scope_name (uint32_t scope) { switch (scope) { - case 0x0: - case 0xf: - return "reserved"; - break; + case 0x0: + case 0xf: + return "reserved"; + break; - case 0x1: - return "interface-local"; - break; + case 0x1: + return "interface-local"; + break; - case 0x2: - return "link-local"; - break; + case 0x2: + return "link-local"; + break; - case 0x4: - return "admin-local"; - break; + case 0x4: + return "admin-local"; + break; - case 0x5: - return "site-local"; - break; + case 0x5: + return "site-local"; + break; - case 0x8: - return "organization-local"; - break; + case 0x8: + return "organization-local"; + break; - case 0xe: - return "global"; - break; + case 0xe: + return "global"; + break; } return UNKNOWN_STR; @@ -1792,7 +4387,7 @@ get_mtu (const struct ifaddrs *ifaddr) { int sock; struct ifreq ifr; - int request = SIOCGIFMTU; + unsigned long request = SIOCGIFMTU; assert (ifaddr); @@ -1808,8 +4403,8 @@ get_mtu (const struct ifaddrs *ifaddr) strncpy (ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ-1); if (ioctl (sock, request, &ifr) < 0) - goto out; -out: + ifr.ifr_mtu = 0; + close (sock); return ifr.ifr_mtu; @@ -1826,7 +4421,7 @@ get_mac_address (const struct ifaddrs *ifaddr) char *mac_address = NULL; int i; int valid = FALSE; -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) struct sockaddr_dl *link_layer; #else struct ifreq ifr; @@ -1834,12 +4429,12 @@ get_mac_address (const struct ifaddrs *ifaddr) #endif #if defined (PROCENV_LINUX) || defined (PROCENV_HURD) - int request = SIOCGIFHWADDR; + unsigned long request = SIOCGIFHWADDR; #endif assert (ifaddr); -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) link_layer = (struct sockaddr_dl *)ifaddr->ifa_addr; #else @@ -1858,7 +4453,7 @@ get_mac_address (const struct ifaddrs *ifaddr) goto out; #endif -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) data = LLADDR (link_layer); #else data = (char *)ifr.ifr_hwaddr.sa_data; @@ -1895,7 +4490,7 @@ get_mac_address (const struct ifaddrs *ifaddr) out: -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) /* NOP */ #else close (sock); @@ -1906,7 +4501,7 @@ out: #if defined (PROCENV_LINUX) || defined (PROCENV_HURD) void -show_linux_mounts (ShowMountType what) +show_mounts_linux (ShowMountType what) { FILE *mtab; struct mntent *mnt; @@ -1914,25 +4509,30 @@ show_linux_mounts (ShowMountType what) unsigned int major = 0; unsigned int minor = 0; int have_stats; +#if defined (PROCENV_LINUX) + char canonical[PATH_MAX]; +#endif + + common_assert (); mtab = fopen (MOUNTS, "r"); - if (! mtab) { - show ("%s", UNKNOWN_STR); + if (! mtab) return; - } while ((mnt = getmntent (mtab))) { - char *str = NULL; have_stats = TRUE; if (what == SHOW_ALL || what == SHOW_MOUNTS) { unsigned multiplier = 0; - fsblkcnt_t blocks; - fsblkcnt_t bfree; - fsblkcnt_t bavail; - fsblkcnt_t used_blocks; - fsblkcnt_t used_files; + fsblkcnt_t blocks = 0; + fsblkcnt_t bfree = 0; + fsblkcnt_t bavail = 0; + fsblkcnt_t used_blocks = 0; + fsblkcnt_t used_files = 0; +#if defined (PROCENV_LINUX) + int ret; +#endif if (statvfs (mnt->mnt_dir, &fs) < 0) { have_stats = FALSE; @@ -1946,61 +4546,96 @@ show_linux_mounts (ShowMountType what) used_files = fs.f_files - fs.f_ffree; } - get_major_minor (mnt->mnt_dir, + (void)get_major_minor (mnt->mnt_dir, &major, &minor); - appendf (&str, - "fsname='%s', dir='%s', type='%s', " - "opts='%s', " - "dev=(major:%u, minor:%u), " - "dump_freq=%d, fsck_passno=%d, ", - mnt->mnt_fsname, - mnt->mnt_dir, - mnt->mnt_type, - mnt->mnt_opts, - major, minor, - mnt->mnt_freq, mnt->mnt_passno); + + assert (mnt->mnt_dir); + section_open (mnt->mnt_dir); + + entry ("filesystem", "'%s'", mnt->mnt_fsname); + +#if defined (PROCENV_LINUX) + ret = get_canonical (mnt->mnt_fsname, canonical, sizeof (canonical)); + entry ("canonical", "%s%s%s", + ret ? "'" : "", + canonical, + ret ? "'" : ""); +#endif + + entry ("type", "'%s'", mnt->mnt_type); + entry ("options", "'%s'", mnt->mnt_opts); + + show_pathconfs (what, mnt->mnt_dir); + + section_open ("device"); + entry ("major", "%u", major); + entry ("minor", "%u", minor); + section_close (); + + entry ("dump frequency", "%d", mnt->mnt_freq); + entry ("fsck pass number", "%d", mnt->mnt_passno); if (have_stats) { - appendf (&str, - "fsid=%.*x, " - "optimal_block_size=%lu, " - "%d-byte blocks (total=%lu, used=%lu, free=%lu, available=%lu), " - "files/inodes (total=%lu, used=%lu, free=%lu)", - sizeof (fs.f_fsid), - fs.f_fsid, - fs.f_bsize, - DF_BLOCK_SIZE, - blocks, - used_blocks, - bfree, - bavail, - fs.f_files, - used_files, - fs.f_ffree); + union fsid_u { + unsigned long int fsid; + unsigned int val[2]; + } fsid_val; + + fsid_val.fsid = fs.f_fsid; + + entry ("fsid", "%.*x%.*x", + 2 * sizeof (fsid_val.val[0]), + fsid_val.val[0], + 2 * sizeof (fsid_val.val[1]), + fsid_val.val[1]); + + entry ("optimal block size", "%lu", fs.f_bsize); + + section_open ("blocks"); + + entry ("size", "%lu bytes", DF_BLOCK_SIZE); + entry ("total", "%lu", blocks); + entry ("used", "%lu", used_blocks); + entry ("free", "%lu", bfree); + entry ("available", "%lu", bavail); + + section_close (); + + section_open ("files/inodes"); + + entry ("total", "%lu", fs.f_files); + entry ("used", "%lu", used_files); + entry ("free", "%lu", fs.f_ffree); + + section_close (); } else { - appendf (&str, - "fsid=%s, " - "optimal_block_size=%s, " - "%d-byte blocks (total=%s, used=%s, free=%s, available=%s), " - "files/inodes (total=%s, free=%s)", - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR, - UNKNOWN_STR); - } + entry ("fsid", "%s", UNKNOWN_STR); + entry ("optimal block size", "%s", UNKNOWN_STR); - show (str); - free (str); - } + section_open ("blocks"); - if (what == SHOW_ALL || what == SHOW_PATHCONF) + entry ("size", "%lu bytes", DF_BLOCK_SIZE); + entry ("total", "%s", UNKNOWN_STR); + entry ("used", "%s", UNKNOWN_STR); + entry ("free", "%s", UNKNOWN_STR); + entry ("available", "%s", UNKNOWN_STR); + + section_close (); + + section_open ("files/inodes"); + + entry ("total", "%s", UNKNOWN_STR); + entry ("used", "%s", UNKNOWN_STR); + entry ("free", "%s", UNKNOWN_STR); + + section_close (); + } + + section_close (); + } else { show_pathconfs (what, mnt->mnt_dir); + } } fclose (mtab); @@ -2008,13 +4643,11 @@ show_linux_mounts (ShowMountType what) #endif #if defined (PROCENV_LINUX) -char * +void decode_extended_if_flags (const char *interface, unsigned short *flags) { int sock; struct ifreq ifr; - int first = TRUE; - char *str = NULL; struct if_extended_flag_map *p; assert (interface); @@ -2026,7 +4659,7 @@ decode_extended_if_flags (const char *interface, unsigned short *flags) sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP); if (sock < 0) - return NULL; + return; memset (&ifr, 0, sizeof (struct ifreq)); strncpy (ifr.ifr_name, interface, IFNAMSIZ-1); @@ -2038,15 +4671,13 @@ decode_extended_if_flags (const char *interface, unsigned short *flags) for (p = if_extended_flag_map; p && p->name; p++) { if (*flags & p->flag) { - appendf (&str, "%s%s", - first ? "" : ",", - p->name); - first = FALSE; + object_open (FALSE); + entry (p->name, "0x%x", p->flag); + object_close (FALSE); } } out: close (sock); - return str; } @@ -2114,20 +4745,200 @@ linux_kernel_version (int major, int minor, int revision) return FALSE; } +void +show_numa_memory (void) +{ +#if defined (HAVE_NUMA_H) + int policy; + const char *policy_name; + char *allowed_list = NULL; + int ret; + unsigned long node; + unsigned long allowed_size; + +#if defined (HAVE_NUMA_H) +#if LIBNUMA_API_VERSION == 2 + struct bitmask *allowed; +#else + nodemask_t allowed; #endif +#endif + + /* part of the libnuma public API - stop the library calling + * exit(3) on error. + */ + numa_exit_on_error = 0; + + /* TRUE if any numa nodes have been displayed yet */ + int displayed = FALSE; + + /* Number of numa nodes in *current* range */ + size_t count = 0; + + /* Only valid to read these when count is >0 */ + size_t last = 0; + size_t first = 0; +#endif /* HAVE_NUMA_H */ + + header ("numa"); + +#if defined (HAVE_NUMA_H) + if (numa_available () < 0) + /* NUMA not supported on this system */ + goto out; + +#if LIBNUMA_API_VERSION == 2 + entry ("api version", "%d", 2); +#else + entry ("api version", "%d", 1); +#endif + + ret = get_mempolicy (&policy, NULL, 0, 0, 0); + + if (ret < 0) { + entry ("policy", "%s", UNKNOWN_STR); + goto out; + } + + policy_name = get_numa_policy (policy); + + entry ("policy", "%s", policy_name ? policy_name : UNKNOWN_STR); + +#if LIBNUMA_API_VERSION == 2 + entry ("maximum nodes", "%d", numa_num_possible_nodes ()); + entry ("configured nodes", "%d", numa_num_configured_nodes ()); + + allowed = numa_get_mems_allowed (); + if (! allowed) + die ("failed to query NUMA allowed list"); + + allowed_size = allowed->size; + +#else + entry ("maximum nodes", "%s", UNKNOWN_STR); + entry ("configured nodes", "%d", numa_max_node ()); + + allowed = numa_get_run_node_mask (); + allowed_size = NUMA_NUM_NODES; +#endif + + for (node = 0; node < allowed_size; node++) { + if (PROCENV_NUMA_BITMASK_ISSET (allowed, node)) { + /* Record first entry in the range */ + if (! count) + first = node; + + last = node; + count++; + } else { + if (count) { + if (first == last) { + appendf (&allowed_list, "%s%d", + displayed ? "," : "", + first); + } else { + appendf (&allowed_list, "%s%d-%d", + displayed ? "," : "", + first, last); + } + displayed = TRUE; + } + + /* Reset */ + count = 0; + } + } + + if (count) { + if (first == last) { + appendf (&allowed_list, "%s%d", + displayed ? "," : "", + first); + } else { + appendf (&allowed_list, "%s%d-%d", + displayed ? "," : "", + first, last); + } + } + + entry ("allowed list", "%s", allowed_list); + +#if LIBNUMA_API_VERSION == 2 + numa_free_nodemask (allowed); +#endif + + free (allowed_list); + +out: +#endif /* HAVE_NUMA_H */ + footer (); +} + +#if defined (HAVE_NUMA_H) +const char * +get_numa_policy (int policy) +{ + struct procenv_map *p; + + for (p = numa_mempolicy_map; p && p->name; p++) { + if (p->num == policy) + return p->name; + } + + return NULL; +} +#endif /* HAVE_NUMA_H */ + +const char * +get_personality_name (unsigned int domain) +{ + struct personality_map *m; + + for (m = personality_map; m && m->name; m++) { + if (m->personality == (domain & PER_MASK)) + return m->name; + } + + return NULL; +} + +char * +get_personality_flags (unsigned int flags) +{ + struct personality_flag_map *m; + char *list = NULL; + int first = TRUE; + + for (m = personality_flag_map; m && m->name; m++) { + if (flags & m->flag) { + appendf (&list, "%s%s", + first ? "" : ", ", + m->name); + first = FALSE; + } + } + + return list; +} + +#endif /* PROCENV_LINUX */ void show_mounts (ShowMountType what) { - header ("mounts"); + common_assert (); + + header (what == SHOW_PATHCONF ? "pathconf" : "mounts"); #if defined (PROCENV_LINUX) || defined (PROCENV_HURD) - show_linux_mounts (what); + show_mounts_linux (what); #endif -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) - show_bsd_mounts (what); +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) + show_mounts_bsd (what); #endif + + footer (); } const char * @@ -2135,24 +4946,24 @@ get_net_family_name (sa_family_t family) { switch (family) { #if defined (PROCENV_LINUX) - case AF_PACKET: - return "AF_PACKET"; - break; + case AF_PACKET: + return "AF_PACKET"; + break; #endif -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) - case AF_LINK: - return "AF_LINK"; - break; +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) + case AF_LINK: + return "AF_LINK"; + break; #endif - case AF_INET: - return "AF_INET"; - break; + case AF_INET: + return "AF_INET"; + break; - case AF_INET6: - return "AF_INET6"; - break; + case AF_INET6: + return "AF_INET6"; + break; } return UNKNOWN_STR; @@ -2161,70 +4972,85 @@ get_net_family_name (sa_family_t family) void show_network_if (const struct ifaddrs *ifa, const char *mac_address) { - char *str = NULL; - char *flags = NULL; - char *extended_flags = NULL; unsigned short ext_flags = 0; sa_family_t family; char address[NI_MAXHOST]; int mtu = 0; + common_assert (); assert (ifa); family = ifa->ifa_addr->sa_family; - flags = decode_if_flags (ifa->ifa_flags); + assert (ifa->ifa_name); + section_open (ifa->ifa_name); + + entry ("family", "%s (0x%x)", get_net_family_name (family), family); + + /*******************************/ + + section_open ("flags"); + + entry ("value", "0x%x", ifa->ifa_flags); + + container_open ("fields"); + + decode_if_flags (ifa->ifa_flags); + + container_close (); + + section_close (); + + /*******************************/ + + section_open ("extended flags"); + + entry ("value", "0x%x", ext_flags); + + container_open ("fields"); #if defined (PROCENV_LINUX) - extended_flags = decode_extended_if_flags (ifa->ifa_name, &ext_flags); -#endif - appendf (&str, "interface %s: family=%s (0x%x), " - "flags=0x%x (%s), extended_flags=0x%x (%s)", - ifa->ifa_name, - get_net_family_name (family), - family, - ifa->ifa_flags, - flags ? flags : UNKNOWN_STR, - ext_flags, - extended_flags ? extended_flags : NA_STR); - if (flags) - free (flags); - - if (extended_flags) - free (extended_flags); + decode_extended_if_flags (ifa->ifa_name, &ext_flags); +#endif + + container_close (); + + section_close (); + + /*******************************/ mtu = get_mtu (ifa); #if defined (PROCENV_HURD) /* No AF_LINK/AF_PACKET on Hurd atm */ - appendf (&str, ", mac=%s", UNKNOWN_STR); + entry ("mac", "%s", UNKNOWN_STR); #else - appendf (&str, ", mac=%s", mac_address ? mac_address : NA_STR); + entry ("mac", "%s", mac_address ? mac_address : NA_STR); #endif if (mtu > 0) { - appendf (&str, ", mtu=%d", mtu); + entry ("mtu", "%d", mtu); } else { - appendf (&str, ", mtu=%s", UNKNOWN_STR); + entry ("mtu", "%s", UNKNOWN_STR); } get_network_address (ifa->ifa_addr, family, address); - appendf (&str, ", address=%s", address); + entry ("address", "%s", address); if (ifa->ifa_netmask) get_network_address (ifa->ifa_netmask, family, address); - appendf (&str, ", netmask=%s", ifa->ifa_netmask ? address : NA_STR); + entry ("netmask", "%s", ifa->ifa_netmask ? address : NA_STR); #if !defined (PROCENV_HURD) if (family != PROCENV_LINK_LEVEL_FAMILY) { if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) { get_network_address (ifa->ifa_broadaddr, family, address); - appendf (&str, ", broadcast=%s", ifa->ifa_broadaddr ? address : NA_STR); + entry ("broadcast", "%s", ifa->ifa_broadaddr ? address : NA_STR); } } else { #endif - appendf (&str, ", broadcast=%s", NA_STR); + entry ("broadcast", "%s", NA_STR); #if !defined (PROCENV_HURD) } #endif @@ -2233,11 +5059,10 @@ show_network_if (const struct ifaddrs *ifa, const char *mac_address) get_network_address (ifa->ifa_dstaddr, family, address); - appendf (&str, ", point-to-point=%s", address); + entry ("point-to-point", "%s", address); } - show (str); - free (str); + section_close (); } /* @@ -2275,6 +5100,18 @@ show_network_if (const struct ifaddrs *ifa, const char *mac_address) * suggests this _seems_ to be the case, but is not documented as being * guaranteed. */ +#ifdef PROCENV_ANDROID + +void +show_network (void) +{ + /* Bionic isn't actually that bionic at all :( */ + header ("network"); + footer (); +} + +#else + void show_network (void) { @@ -2285,13 +5122,13 @@ show_network (void) struct network_map *node = NULL; struct network_map *tmp = NULL; + common_assert (); + header ("network"); /* Query all network interfaces */ - if (getifaddrs (&if_addrs) < 0) { - show ("%s", UNKNOWN_STR); + if (getifaddrs (&if_addrs) < 0) return; - } /* Construct an initial node for the cache */ head = calloc (1, sizeof (struct network_map)); @@ -2397,12 +5234,15 @@ show_network (void) free (head); freeifaddrs (if_addrs); + + footer (); } +#endif -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) char * -get_bsd_mount_opts (uint64_t flags) +get_mount_opts_bsd (uint64_t flags) { struct mntopt_map *opt; char *str = NULL; @@ -2452,7 +5292,7 @@ get_bsd_mount_opts (uint64_t flags) } void -show_bsd_mounts (ShowMountType what) +show_mounts_bsd (ShowMountType what) { int count; struct statfs *mounts; @@ -2466,6 +5306,8 @@ show_bsd_mounts (ShowMountType what) statfs_int_type bavail; statfs_int_type used; + common_assert (); + /* Note that returned memory cannot be freed (by us) */ count = getmntinfo (&mounts, MNT_WAIT); @@ -2473,18 +5315,17 @@ show_bsd_mounts (ShowMountType what) die ("unable to query mount info"); mnt = mounts; - + for (i = 0; i < count; i++) { char *opts = NULL; - opts = get_bsd_mount_opts (mnt->f_flags); + opts = get_mount_opts_bsd (mnt->f_flags); if (! opts) die ("cannot determine FS flags for mountpoint '%s'", mnt->f_mntonname); if (what == SHOW_ALL || what == SHOW_MOUNTS) { - - get_major_minor (mnt->f_mntonname, + (void)get_major_minor (mnt->f_mntonname, &major, &minor); @@ -2494,34 +5335,49 @@ show_bsd_mounts (ShowMountType what) bavail = mnt->f_bavail * multiplier; used = blocks - bfree; - show ("fsname='%s', dir='%s', type='%s', " - "opts='%s', " - "dev=(major:%u, minor:%u), " - "fsid=%.*x%.*x, " - "optimal_block_size=%" statfs_int_fmt ", " - "%d-byte blocks (total=%" statfs_int_fmt ", " - "used=%" statfs_int_fmt ", free=%" statfs_int_fmt ", available=%" statfs_int_fmt "), " - "files/inodes (total=%" statfs_int_fmt ", free=%" statfs_int_fmt ")", - mnt->f_mntfromname, - mnt->f_mntonname, - mnt->f_fstypename, - opts, - major, minor, + assert (mnt->f_mntfromname); + section_open (mnt->f_mntfromname); + + entry ("dir", "'%s'", mnt->f_mntonname); + entry ("type", "%s", mnt->f_fstypename); + entry ("options", "'%s'", opts); + section_open ("device"); + entry ("major", "%u", major); + entry ("minor", "%u", minor); + section_close (); + + entry ("fsid", "%.*x%.*x", /* Always zero on BSD? */ - sizeof (mnt->f_fsid.val[0]), + 2 * sizeof (mnt->f_fsid.val[0]), mnt->f_fsid.val[0], - sizeof (mnt->f_fsid.val[1]), - mnt->f_fsid.val[1], - - mnt->f_bsize, - DF_BLOCK_SIZE, - blocks, - used, - bfree, - bavail, - mnt->f_files, - mnt->f_ffree); + 2 * sizeof (mnt->f_fsid.val[1]), + mnt->f_fsid.val[1]); + + entry ("optimal block size", "%" statfs_int_fmt, + mnt->f_bsize); + + section_open ("blocks"); + + entry ("size", "%lu bytes", DF_BLOCK_SIZE); + + entry ("total", "%" statfs_int_fmt, blocks); + entry ("used", "%"statfs_int_fmt, used); + entry ("free", "%" statfs_int_fmt, bfree); + entry ("available", "%" statfs_int_fmt, bavail); + + section_close (); + + section_open ("files/inodes"); + + entry ("total", "%" statfs_int_fmt, mnt->f_files); + entry ("used", "%" statfs_int_fmt, + mnt->f_files - mnt->f_ffree); + entry ("free", "%" statfs_int_fmt, mnt->f_ffree); + + section_close (); + + section_close (); } if (what == SHOW_ALL || what == SHOW_PATHCONF) @@ -2534,6 +5390,144 @@ show_bsd_mounts (ShowMountType what) #endif +#if defined (PROCENV_BSD) && defined (HAVE_SYS_CAPABILITY_H) +void +show_capabilities_bsd (int fd) +{ + int ret; + u_int mode; + cap_rights_t rights; + + ret = cap_getmode (&mode); + if (ret < 0) { + /* No Capsicum support */ + goto out; + } + + ret = cap_rights_get (fd, &rights); + if (ret < 0) { + /* Cannot query capabilities */ + goto out; + } + + show_capsicum_cap (rights, CAP_ACCEPT); + show_capsicum_cap (rights, CAP_ACL_CHECK); + show_capsicum_cap (rights, CAP_ACL_DELETE); + show_capsicum_cap (rights, CAP_ACL_GET); + show_capsicum_cap (rights, CAP_ACL_SET); + show_capsicum_cap (rights, CAP_BIND); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_BINDAT); + show_capsicum_cap (rights, CAP_CHFLAGSAT); +#endif + show_capsicum_cap (rights, CAP_CONNECT); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_CONNECTAT); +#endif + show_capsicum_cap (rights, CAP_CREATE); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_EVENT); +#endif + show_capsicum_cap (rights, CAP_EXTATTR_DELETE); + show_capsicum_cap (rights, CAP_EXTATTR_GET); + show_capsicum_cap (rights, CAP_EXTATTR_LIST); + show_capsicum_cap (rights, CAP_EXTATTR_SET); + show_capsicum_cap (rights, CAP_FCHDIR); + show_capsicum_cap (rights, CAP_FCHFLAGS); + show_capsicum_cap (rights, CAP_FCHMOD); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_FCHMODAT); +#endif + show_capsicum_cap (rights, CAP_FCHOWN); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_FCHOWNAT); +#endif + show_capsicum_cap (rights, CAP_FCNTL); + show_capsicum_cap (rights, CAP_FEXECVE); + show_capsicum_cap (rights, CAP_FLOCK); + show_capsicum_cap (rights, CAP_FPATHCONF); + show_capsicum_cap (rights, CAP_FSCK); + show_capsicum_cap (rights, CAP_FSTAT); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_FSTATAT); +#endif + show_capsicum_cap (rights, CAP_FSTATFS); + show_capsicum_cap (rights, CAP_FSYNC); + show_capsicum_cap (rights, CAP_FTRUNCATE); + show_capsicum_cap (rights, CAP_FUTIMES); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_FUTIMESAT); +#endif + show_capsicum_cap (rights, CAP_GETPEERNAME); + show_capsicum_cap (rights, CAP_GETSOCKNAME); + show_capsicum_cap (rights, CAP_GETSOCKOPT); + show_capsicum_cap (rights, CAP_IOCTL); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_KQUEUE); + show_capsicum_cap (rights, CAP_KQUEUE_CHANGE); + show_capsicum_cap (rights, CAP_KQUEUE_EVENT); + show_capsicum_cap (rights, CAP_LINKAT); +#endif + show_capsicum_cap (rights, CAP_LISTEN); + show_capsicum_cap (rights, CAP_LOOKUP); + show_capsicum_cap (rights, CAP_MAC_GET); + show_capsicum_cap (rights, CAP_MAC_SET); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_MKDIRAT); + show_capsicum_cap (rights, CAP_MKFIFOAT); + show_capsicum_cap (rights, CAP_MKNODAT); + show_capsicum_cap (rights, CAP_MMAP); + show_capsicum_cap (rights, CAP_MMAP_R); + show_capsicum_cap (rights, CAP_MMAP_RW); + show_capsicum_cap (rights, CAP_MMAP_RWX); + show_capsicum_cap (rights, CAP_MMAP_RX); + show_capsicum_cap (rights, CAP_MMAP_W); + show_capsicum_cap (rights, CAP_MMAP_WX); + show_capsicum_cap (rights, CAP_MMAP_X); +#endif + show_capsicum_cap (rights, CAP_PDGETPID); + show_capsicum_cap (rights, CAP_PDKILL); + show_capsicum_cap (rights, CAP_PDWAIT); + show_capsicum_cap (rights, CAP_PEELOFF); + show_capsicum_cap (rights, CAP_POLL_EVENT); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_PREAD); + show_capsicum_cap (rights, CAP_PWRITE); +#endif + show_capsicum_cap (rights, CAP_READ); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_RECV); + show_capsicum_cap (rights, CAP_RENAMEAT); +#endif + show_capsicum_cap (rights, CAP_SEEK); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_SEEK_TELL); +#endif + show_capsicum_cap (rights, CAP_SEM_GETVALUE); + show_capsicum_cap (rights, CAP_SEM_POST); + show_capsicum_cap (rights, CAP_SEM_WAIT); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_SEND); +#endif + show_capsicum_cap (rights, CAP_SETSOCKOPT); + show_capsicum_cap (rights, CAP_SHUTDOWN); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_SOCK_CLIENT); + show_capsicum_cap (rights, CAP_SOCK_SERVER); + show_capsicum_cap (rights, CAP_SYMLINKAT); +#endif + show_capsicum_cap (rights, CAP_TTYHOOK); +#if __FreeBSD__ > 9 + show_capsicum_cap (rights, CAP_UNLINKAT); +#endif + show_capsicum_cap (rights, CAP_WRITE); + +out: + /* clang requires this */ + return; +} +#endif /* (PROCENV_BSD) && defined (HAVE_SYS_CAPABILITY_H) */ + void get_priorities (void) { @@ -2659,6 +5653,9 @@ in_chroot (void) error: die ("cannot stat '%s'", dir); + + /* compiler appeasement */ + return FALSE; } /* detect if setsid(2) has been called */ @@ -2678,19 +5675,25 @@ is_process_group_leader (void) void show_proc_branch (void) { -#if defined (PROCENV_LINUX) - show_linux_proc_branch (); + common_assert (); + +#if defined (PROCENV_LINUX) || defined (PROCENV_GNU_BSD) + show_proc_branch_linux (); +#endif + +#if defined (PROCENV_HURD) + /* FIXME: how can this be queried in Hurd?? */ #endif #if defined (PROCENV_BSD) - show_bsd_proc_branch (); + show_proc_branch_bsd (); #endif } #if defined (PROCENV_BSD) /* Who would have thought handling PIDs was so tricky? */ void -show_bsd_proc_branch (void) +show_proc_branch_bsd (void) { int count = 0; int i; @@ -2700,11 +5703,10 @@ show_bsd_proc_branch (void) struct kinfo_proc *p; pid_t self, current; int done = FALSE; - char *str; + char *str = NULL; pid_t ultimate_parent = 0; - str = strdup ("ancestry: "); - assert (str); + common_assert (); self = current = getpid (); @@ -2807,19 +5809,23 @@ show_bsd_proc_branch (void) if (kvm_close (kvm) < 0) die ("failed to close kvm"); - show (str); + entry ("ancestry", "%s", str); free (str); } #endif #if defined (PROCENV_LINUX) void -show_linux_prctl (void) +show_prctl_linux (void) { int rc; int arg2; char name[17] = { 0 }; + common_assert (); + + section_open ("prctl"); + #ifdef PR_GET_ENDIAN if (LINUX_KERNEL_MMR (2, 6, 18)) { const char *value; @@ -2843,7 +5849,7 @@ show_linux_prctl (void) break; } } - show ("process endian: %s", value); + entry ("process endian", "%s", value); } #endif @@ -2869,7 +5875,7 @@ show_linux_prctl (void) break; } } - show ("dumpable: %s", value); + entry ("dumpable", "%s", value); } #endif @@ -2896,7 +5902,7 @@ show_linux_prctl (void) break; } } - show ("floating point emulation: %s", value); + entry ("floating point emulation", "%s", value); } #endif @@ -2932,7 +5938,7 @@ show_linux_prctl (void) break; } } - show ("floating point exceptions: %s", value); + entry ("floating point exceptions", "%s", value); } #endif @@ -2940,9 +5946,9 @@ show_linux_prctl (void) if (LINUX_KERNEL_MMR (2, 6, 11)) { rc = prctl (PR_GET_NAME, name, 0, 0, 0); if (rc < 0) - show ("process name: %s", UNKNOWN_STR); + entry ("process name", "%s", UNKNOWN_STR); else - show ("process name: %s", name); + entry ("process name", "%s", name); } #endif @@ -2951,11 +5957,11 @@ show_linux_prctl (void) if (LINUX_KERNEL_MMR (2, 3, 15)) { rc = prctl (PR_GET_PDEATHSIG, &arg2, 0, 0, 0); if (rc < 0) - show ("parent death signal: %s", UNKNOWN_STR); + entry ("parent death signal", "%s", UNKNOWN_STR); else if (rc == 0) - show ("parent death signal: disabled"); + entry ("parent death signal", "disabled"); else - show ("parent death signal: %d", arg2); + entry ("parent death signal", "%d", arg2); } #endif @@ -2982,7 +5988,7 @@ show_linux_prctl (void) break; } } - show ("secure computing: %s", value); + entry ("secure computing", "%s", value); } #endif @@ -3008,7 +6014,7 @@ show_linux_prctl (void) break; } } - show ("process timing: %s", value); + entry ("process timing", "%s", value); } #endif @@ -3032,7 +6038,7 @@ show_linux_prctl (void) break; } } - show ("timestamp counter read: %s", value); + entry ("timestamp counter read", "%s", value); } #endif @@ -3056,7 +6062,7 @@ show_linux_prctl (void) break; } } - show ("unaligned access: %s", value); + entry ("unaligned access", "%s", value); } #endif @@ -3083,7 +6089,7 @@ show_linux_prctl (void) break; } } - show ("machine-check exception: %s", value); + entry ("machine-check exception", "%s", value); } #endif @@ -3107,7 +6113,7 @@ show_linux_prctl (void) break; } } - show ("no new privileges: %s", value); + entry ("no new privileges", "%s", value); } #endif @@ -3115,9 +6121,9 @@ show_linux_prctl (void) if (LINUX_KERNEL_MMR (2, 6, 28)) { rc = prctl (PR_GET_TIMERSLACK, 0, 0, 0, 0); if (rc < 0) - show ("timer slack: %s", UNKNOWN_STR); + entry ("timer slack", "%s", UNKNOWN_STR); else - show ("timer slack: %dns", rc); + entry ("timer slack", "%dns", rc); } #endif @@ -3125,23 +6131,28 @@ show_linux_prctl (void) if (LINUX_KERNEL_MM (3, 4)) { rc = prctl (PR_GET_CHILD_SUBREAPER, &arg2, 0, 0, 0); if (rc < 0) - show ("child subreaper: %s", UNKNOWN_STR); + entry ("child subreaper", "%s", UNKNOWN_STR); else - show ("child subreaper: %s", arg2 ? YES_STR : NO_STR); + entry ("child subreaper", "%s", arg2 ? YES_STR : NO_STR); } #endif #ifdef PR_GET_TID_ADDRESS rc = prctl (PR_GET_TID_ADDRESS, &arg2, 0, 0, 0); if (rc < 0) - show ("clear child tid address: %s", UNKNOWN_STR); + entry ("clear child tid address", "%s", UNKNOWN_STR); else - show ("clear child tid address: %p", arg2); + entry ("clear child tid address", "%p", arg2); #endif + + section_close (); } +#endif + +#if defined (PROCENV_LINUX) || defined (PROCENV_GNU_BSD) void -show_linux_proc_branch (void) +show_proc_branch_linux (void) { char buffer[1024]; char path[PATH_MAX]; @@ -3151,10 +6162,9 @@ show_linux_proc_branch (void) size_t len; char *p; FILE *f; - char *str; + char *str = NULL; - str = strdup ("ancestry: "); - assert (str); + common_assert (); sprintf (pid, "%d", (int)getpid ()); @@ -3201,7 +6211,7 @@ show_linux_proc_branch (void) } out: - show (str); + entry ("ancestry", "%s", str); free (str); } #endif @@ -3221,6 +6231,8 @@ show_tty_attrs (void) int fds[4] = { -1, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }; size_t i; + common_assert (); + fds[0] = user.tty_fd; /* For non-Linux platforms, this will force the lock @@ -3240,18 +6252,21 @@ show_tty_attrs (void) } } - show ("%s", NA_STR); + /* cannot query attributes */ + footer (); return; work: - user.tty_fd = fds[i]; #ifdef PROCENV_LINUX get_tty_locked_status (&lock_status); #endif - show ("c_iflag=0x%x", tty.c_iflag); + /*****************************************/ + section_open ("c_iflag (input)"); + + entry ("value", "0x%x", tty.c_iflag); show_const_tty (tty, c_iflag, IGNBRK, lock_status); show_const_tty (tty, c_iflag, BRKINT, lock_status); @@ -3273,7 +6288,12 @@ work: show_const_tty (tty, c_iflag, IUTF8, lock_status); #endif - show ("c_oflag=0x%x", tty.c_oflag); + section_close (); + + /*****************************************/ + section_open ("c_oflag (output)"); + + entry ("c_oflag", "0x%x", tty.c_oflag); show_const_tty (tty, c_oflag, OPOST, lock_status); #if defined (PROCENV_LINUX) @@ -3296,13 +6316,12 @@ work: show_const_tty (tty, c_oflag, FFDLY, lock_status); #endif - show ("c_cflag=0x%x", tty.c_cflag); + section_close (); - show (" [c_cflag:input baud speed=%s]", - get_speed (cfgetispeed (&tty))); + /*****************************************/ + section_open ("c_cflag (control)"); - show (" [c_cflag:output baud speed=%s]", - get_speed (cfgetospeed (&tty))); + entry ("value", "0x%x", tty.c_cflag); #if defined (PROCENV_LINUX) show_const_tty (tty, c_cflag, CBAUDEX, lock_status); @@ -3324,7 +6343,12 @@ work: #endif show_const_tty (tty, c_cflag, CRTSCTS, lock_status); - show ("c_lflag=0x%x", tty.c_lflag); + section_close (); + + /*****************************************/ + section_open ("c_lflag (local)"); + + entry ("value", "0x%x", tty.c_lflag); show_const_tty (tty, c_lflag, ISIG, lock_status); #if defined (PROCENV_LINUX) @@ -3344,7 +6368,10 @@ work: show_const_tty (tty, c_lflag, PENDIN, lock_status); show_const_tty (tty, c_lflag, IEXTEN, lock_status); - show ("c_cc:"); + section_close (); + + /*****************************************/ + section_open ("c_cc (special)"); show_cc_tty (tty, VINTR, lock_status); show_cc_tty (tty, VQUIT, lock_status); @@ -3366,13 +6393,35 @@ work: show_cc_tty (tty, VLNEXT, lock_status); show_cc_tty (tty, VEOL2, lock_status); + section_close (); + if (ioctl (user.tty_fd, TIOCGWINSZ, &size) < 0) die ("failed to determine terminal dimensions"); - show ("winsize:ws_row=%u", size.ws_row); - show ("winsize:ws_col=%u", size.ws_col); - show ("winsize:ws_xpixel=%u", size.ws_xpixel); - show ("winsize:ws_ypixel=%u", size.ws_ypixel); + /*****************************************/ + section_open ("speed"); + + entry ("input (baud)", "%s", + get_speed (cfgetispeed (&tty))); + + entry ("output (baud)", "%s", + get_speed (cfgetospeed (&tty))); + + section_close (); + + /*****************************************/ + section_open ("winsize"); + + entry ("ws_row", "%u", size.ws_row); + entry ("ws_col", "%u", size.ws_col); + entry ("ws_xpixel", "%u", size.ws_xpixel); + entry ("ws_ypixel", "%u", size.ws_ypixel); + + section_close (); + + /*****************************************/ + + footer (); } void @@ -3383,13 +6432,15 @@ show_locale (void) char *v; char *saved = NULL; + common_assert (); + header ("locale"); v = getenv ("LANG"); - show ("LANG=\"%s\"", v ? v : ""); + entry ("LANG", "%s", v ? v : ""); v = getenv ("LANGUAGE"); - show ("LANGUAGE=\"%s\"", v ? v : ""); + entry ("LANGUAGE", "%s", v ? v : ""); value = setlocale (LC_ALL, ""); if (value) { @@ -3400,16 +6451,18 @@ show_locale (void) for (p = locale_map; p && p->name; p++) { value = setlocale (p->num, NULL); - show ("%s=\"%s\"", p->name, value ? value : UNKNOWN_STR); + entry (p->name, "%s", value ? value : UNKNOWN_STR); } v = getenv ("LC_ALL"); - show ("LC_ALL=\"%s\"", v ? v : ""); + entry ("LC_ALL", "%s", v ? v : ""); if (saved) { (void)setlocale (LC_ALL, saved); free (saved); } + + footer (); } const char * @@ -3460,7 +6513,7 @@ get_os (void) return "iSeries (OS/400)"; #endif -#if defined (__FreeBSD_kernel__) && defined (__GNUC__) +#if defined (PROCENV_GNU_BSD) && defined (__GNUC__) return "GNU/kFreeBSD"; #endif @@ -3507,7 +6560,9 @@ get_os (void) return "z/OS (MVS)"; #endif +#ifndef __COVERITY__ return UNKNOWN_STR; +#endif } /** @@ -3521,9 +6576,6 @@ get_arch (void) { #ifdef __arm__ -#ifdef __aarch64__ - return "ARM64"; -#endif #ifdef __ARM_PCS_VFP return "ARMhf"; #endif @@ -3533,6 +6585,15 @@ get_arch (void) return "ARM"; #endif + /* not arm apparently! :) */ +#ifdef __aarch64__ + return "ARM64/AARCH64"; +#endif + +#if defined (__OR1K__) || defined (__or1k__) + return "OpenRISC"; +#endif + #ifdef __hppa__ return "HP/PA RISC"; #endif @@ -3545,15 +6606,30 @@ get_arch (void) return "IA64"; #endif +#ifdef __MIPSEL__ + return "MIPSEL"; +#endif + #ifdef __mips__ return "MIPS"; #endif +#if defined (__powerpc64__) || defined (__ppc64__) + +#if defined (_LITTLE_ENDIAN) + return "PPC64LE"; +#endif + return "PPC64/PowerPC64"; +#endif + #ifdef __powerpc__ +#if defined (__SPE__) && __SIZEOF_POINTER__ == 4 + return "PPCspe"; +#endif return "PowerPC"; #endif -#ifdef __sparc64__ +#if defined (__sparc64__) || defined (__sparc_v9__) return "Sparc64"; #endif @@ -3573,6 +6649,10 @@ get_arch (void) return "x32"; #endif +#ifdef __sh__ + return "SuperH"; +#endif + #if defined (__s390__) || defined (__zarch__) || defined (__SYSC_ZARCH__) || defined (__THW_370__) return "SystemZ"; #endif @@ -3588,8 +6668,37 @@ get_arch (void) int libs_callback (struct dl_phdr_info *info, size_t size, void *data) { - if (info->dlpi_name && *info->dlpi_name) - show ("%s", info->dlpi_name); + const char *name; + const char *path; + + assert (info); + + if (! info->dlpi_name || ! *info->dlpi_name) + return 0; + + path = info->dlpi_name; + assert (path); + + name = strrchr (path, '/'); + + if (name) { + /* Jump over slash */ + name++; + } else { + /* BSD libraries don't show the path */ + name = path; + } + + object_open (FALSE); + + section_open (name); + + entry ("path", "%s", path); + entry ("address", "%p", (void *)info->dlpi_addr); + + section_close (); + + object_close (FALSE); return 0; } @@ -3597,9 +6706,13 @@ libs_callback (struct dl_phdr_info *info, size_t size, void *data) void show_libs (void) { - header ("libs"); + common_assert (); + + container_open ("libraries"); dl_iterate_phdr (libs_callback, NULL); + + container_close (); } #endif @@ -3617,7 +6730,7 @@ show_clocks (void) show_clock_res (CLOCK_MONOTONIC); -#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +#if defined (__FreeBSD__) || defined (PROCENV_GNU_BSD) show_clock_res (CLOCK_MONOTONIC_PRECISE); show_clock_res (CLOCK_MONOTONIC_FAST); show_clock_res (CLOCK_UPTIME); @@ -3637,84 +6750,115 @@ show_clocks (void) show_clock_res (CLOCK_PROCESS_CPUTIME_ID); show_clock_res (CLOCK_THREAD_CPUTIME_ID); #endif + + footer (); } void show_timezone (void) { #if defined (PROCENV_LINUX) - tzset (); - - header ("timezone"); - - show ("tzname[0]='%s'", tzname[0]); - show ("tzname[1]='%s'", tzname[1]); - show ("timezone=%ld", timezone); - show ("daylight=%d", daylight); + show_timezone_linux (); +#else + show_timezone_stub (); #endif } -#define show_size(thing) \ - show ("sizeof (" #thing "): %lu byte%s", \ - (unsigned long int)sizeof (thing), \ - sizeof (thing) == 1 ? "" : "s") - void show_sizeof (void) { header ("sizeof"); - show ("bits/byte (CHAR_BIT): %d", CHAR_BIT); - - /* fundamental types and non-aggregate typedefs */ - - show_size (char); - show_size (short int); - show_size (int); - - show_size (long int); - - show_size (long long int); - - show_size (float); - - show_size (double); - - show_size (long double); - - show_size (size_t); - show_size (ssize_t); - show_size (ptrdiff_t); - show_size (void *); - show_size (wchar_t); - - show_size (intmax_t); - show_size (uintmax_t); - show_size (imaxdiv_t); - show_size (intptr_t); - show_size (uintptr_t); - - show_size (time_t); - show_size (clock_t); - - show_size (sig_atomic_t); - show_size (off_t); - show_size (fpos_t); - show_size (mode_t); - - show_size (pid_t); - show_size (uid_t); - show_size (gid_t); - - show_size (rlim_t); - show_size (fenv_t); - show_size (fexcept_t); - - show_size (wint_t); - show_size (div_t); - show_size (ldiv_t); - show_size (lldiv_t); - show_size (mbstate_t); + entry ("bits/byte (CHAR_BIT)", "%d", CHAR_BIT); + + show_sizeof_type (blkcnt_t); + show_sizeof_type (blksize_t); + show_sizeof_type (char); + show_sizeof_type (clockid_t); + show_sizeof_type (clock_t); + show_sizeof_type (dev_t); + show_sizeof_type (div_t); + show_sizeof_type (double); + show_sizeof_type (fenv_t); + show_sizeof_type (fexcept_t); + show_sizeof_type (float); + show_sizeof_type (fpos_t); + show_sizeof_type (fsblkcnt_t); + show_sizeof_type (fsfilcnt_t); + show_sizeof_type (gid_t); + show_sizeof_type (id_t); + show_sizeof_type (imaxdiv_t); + show_sizeof_type (ino_t); + show_sizeof_type (int); + show_sizeof_type (int16_t); + show_sizeof_type (int32_t); + show_sizeof_type (int64_t); + show_sizeof_type (int8_t); + show_sizeof_type (int_fast16_t); + show_sizeof_type (int_fast32_t); + show_sizeof_type (int_fast64_t); + show_sizeof_type (int_fast8_t); + show_sizeof_type (int_least16_t); + show_sizeof_type (int_least32_t); + show_sizeof_type (int_least64_t); + show_sizeof_type (int_least8_t); + show_sizeof_type (intmax_t); + show_sizeof_type (intptr_t); + show_sizeof_type (key_t); + show_sizeof_type (ldiv_t); + show_sizeof_type (lldiv_t); + show_sizeof_type (long double); + show_sizeof_type (long int); + show_sizeof_type (long long int); + show_sizeof_type (mbstate_t); + show_sizeof_type (mode_t); + show_sizeof_type (mode_t); + show_sizeof_type (nlink_t); + show_sizeof_type (off_t); + show_sizeof_type (pid_t); + show_sizeof_type (pthread_attr_t); + show_sizeof_type (pthread_barrierattr_t); + show_sizeof_type (pthread_barrier_t); + show_sizeof_type (pthread_condattr_t); + show_sizeof_type (pthread_cond_t); + show_sizeof_type (pthread_key_t); + show_sizeof_type (pthread_mutexattr_t); + show_sizeof_type (pthread_mutex_t); + show_sizeof_type (pthread_once_t); + show_sizeof_type (pthread_rwlockattr_t); + show_sizeof_type (pthread_rwlock_t); + show_sizeof_type (pthread_spinlock_t); + show_sizeof_type (pthread_t); + show_sizeof_type (ptrdiff_t); + show_sizeof_type (rlim_t); + show_sizeof_type (short int); + show_sizeof_type (sig_atomic_t); + show_sizeof_type (size_t); + show_sizeof_type (ssize_t); + show_sizeof_type (suseconds_t); + show_sizeof_type (timer_t); + show_sizeof_type (time_t); + show_sizeof_type (uid_t); + show_sizeof_type (uint16_t); + show_sizeof_type (uint32_t); + show_sizeof_type (uint64_t); + show_sizeof_type (uint8_t); + show_sizeof_type (uint_fast16_t); + show_sizeof_type (uint_fast32_t); + show_sizeof_type (uint_fast64_t); + show_sizeof_type (uint_fast8_t); + show_sizeof_type (uint_least16_t); + show_sizeof_type (uint_least32_t); + show_sizeof_type (uint_least64_t); + show_sizeof_type (uint_least8_t); + show_sizeof_type (uintmax_t); + show_sizeof_type (uintptr_t); + show_sizeof_type (useconds_t); + show_sizeof_type (void *); + show_sizeof_type (wchar_t); + show_sizeof_type (wint_t); + + footer (); } void @@ -3723,71 +6867,133 @@ show_ranges (void) header ("ranges"); /******************************/ - show ("char:"); + section_open ("char"); + + show_size (char); + + section_open ("unsigned"); - showi (INDENT, "unsigned: %u to %u (%e to %e, 0x%.*x to 0x%.*x)", - 0, UCHAR_MAX, - (double)0, (double)UCHAR_MAX, + entry ("decimal", "%u to %u", 0, UCHAR_MAX); + entry ("symbolic", "%u to %s", 0, "UCHAR_MAX"); + + entry ("scientific", "%e to %e", (double)0, (double)UCHAR_MAX); + entry ("hex", "0x%.*x to 0x%.*x", type_hex_width (char), 0, type_hex_width (char), UCHAR_MAX); - showi (INDENT, "signed: %d to %d", CHAR_MIN, CHAR_MAX); + + section_close (); + + section_open ("signed"); + entry ("decimal", "%d to %d", SCHAR_MIN, SCHAR_MAX); + entry ("symbolic", "%s to %s", "SCHAR_MIN", "SCHAR_MAX"); + section_close (); + + section_close (); /******************************/ - show ("short int:"); + section_open ("short int"); - showi (INDENT, "unsigned: %u to %u (%e to %e, 0x%.*x to 0x%.*x)", - 0, USHRT_MAX, - (double)0, (double)USHRT_MAX, + show_size (short int); + section_open ("unsigned"); + entry ("decimal", "%u to %u", 0, USHRT_MAX); + entry ("symbolic", "%u to %s", 0, "USHRT_MAX"); + entry ("scientific", "%e to %e", (double)0, (double)USHRT_MAX); + entry ("hex", "0x%.*x to 0x%.*x", type_hex_width (short int), 0, type_hex_width (short int), USHRT_MAX); + section_close (); + + section_open ("signed"); + entry ("decimal", "%d to %d", SHRT_MIN, SHRT_MAX); + entry ("symbolic", "%s to %s", "SHRT_MIN", "SHRT_MAX"); + section_close (); - showi (INDENT, "signed: %d to %d", SHRT_MIN, SHRT_MAX); + section_close (); /******************************/ - show ("int:"); + section_open ("int"); - showi (INDENT, "unsigned: %u to %u (%e to %e, 0x%.*x to 0x%.*x)", - 0, UINT_MAX, - (double)0, (double)UINT_MAX, + show_size (int); + section_open ("unsigned"); + entry ("decimal", "%u to %u", 0, UINT_MAX); + entry ("symbolic", "%u to %s", 0, "UINT_MAX"); + entry ("scientific", "%e to %e", (double)0, (double)UINT_MAX); + entry ("hex", "0x%.*x to 0x%.*x", type_hex_width (int), 0, type_hex_width (int), UINT_MAX); - showi (INDENT, "signed: %d to %d", INT_MIN, INT_MAX); + section_close (); + + section_open ("signed"); + entry ("decimal", "%d to %d", INT_MIN, INT_MAX); + entry ("symbolic", "%s to %s", "INT_MIN", "INT_MAX"); + section_close (); + + section_close (); /******************************/ - show ("long int:"); + section_open ("long int"); - showi (INDENT, "unsigned: %lu to %lu (%e to %e, 0x%.*lx to 0x%.*lx)", - 0L, ULONG_MAX, - (double)0, (double)ULONG_MAX, + show_size (long int); + section_open ("unsigned"); + entry ("decimal", "%u to %u", 0, ULONG_MAX); + entry ("symbolic", "%u to %s", 0, "ULONG_MAX"); + entry ("scientific", "%e to %e", (double)0, (double)ULONG_MAX); + entry ("hex", "0x%.*x to 0x%.*x", type_hex_width (long int), 0L, type_hex_width (long int), ULONG_MAX); - showi (INDENT, "signed: %ld to %ld", LONG_MIN, LONG_MAX); + section_close (); + + section_open ("signed"); + entry ("decimal", "%ld to %ld", LONG_MIN, LONG_MAX); + entry ("symbolic", "%s to %s", "LONG_MIN", "LONG_MAX"); + section_close (); + + section_close (); /******************************/ - show ("long long int:"); + section_open ("long long int"); - showi (INDENT, "unsigned: %llu to %llu (%e to %e, 0x%.*llx to 0x%.*llx)", - 0LL, ULLONG_MAX, - (double)0LL, (double)ULLONG_MAX, + show_size (long long int); + section_open ("unsigned"); + entry ("decimal", "%llu to %llu", 0, ULLONG_MAX); + entry ("symbolic", "%u to %s", 0, "ULLONG_MAX"); + entry ("scientific", "%e to %e", (double)0, (double)ULLONG_MAX); + entry ("hex", "0x%.*llx to 0x%.*llx", type_hex_width (long long int), 0LL, type_hex_width (long long int), ULLONG_MAX); - showi (INDENT, "signed: %lld to %lld", LLONG_MIN, LLONG_MAX); - /******************************/ - show ("float:"); + section_close (); - showi (INDENT, "signed: %e to %e", FLT_MIN, FLT_MAX); + section_open ("signed"); + entry ("decimal", "%lld to %lld", LLONG_MIN, LLONG_MAX); + entry ("symbolic", "%s to %s", "LLONG_MIN", "LLONG_MAX"); + section_close (); - /******************************/ - show ("double:"); + section_close (); - showi (INDENT, "signed: %le to %le", DBL_MIN, DBL_MAX); + /******************************/ + section_open ("float"); + show_size (float); + entry ("signed", "%e to %e", FLT_MIN, FLT_MAX); + entry ("symbolic", "%s to %s", "FLT_MIN", "FLT_MAX"); + section_close (); /******************************/ - show ("long double:"); + section_open ("double"); + show_size (double); + entry ("signed", "%le to %le", DBL_MIN, DBL_MAX); + entry ("symbolic", "%s to %s", "DBL_MIN", "DBL_MAX"); + section_close (); - showi (INDENT, "signed: %Le to %Le", LDBL_MIN, LDBL_MAX); + /******************************/ + section_open ("long double"); + show_size (long double); + entry ("signed", "%Le to %Le", LDBL_MIN, LDBL_MAX); + entry ("symbolic", "%s to %s", "LDBL_MIN", "LDBL_MAX"); + section_close (); /******************************/ + + footer (); } void @@ -3808,123 +7014,227 @@ show_compiler (void) #endif header ("compiler"); - show ("name: %s", name ? name : UNKNOWN_STR); - show ("version: %s", version ? version : UNKNOWN_STR); - show ("compile date (__DATE__): %s", __DATE__); - show ("compile time (__TIME__): %s", __TIME__); - show ("translation unit (__FILE__): %s", __FILE__); - show ("base file (__BASE_FILE__): %s", __BASE_FILE__); - show ("timestamp (__TIMESTAMP__): %s", __TIMESTAMP__); + + entry ("name", "%s", name ? name : UNKNOWN_STR); + entry ("version", "%s", version ? version : UNKNOWN_STR); + +#if defined (PROCENV_REPRODUCIBLE_BUILD) + entry ("compile date (__DATE__)", "%s", SUPPRESSED_STR); + entry ("compile time (__TIME__)", "%s", SUPPRESSED_STR); + +#ifdef __TIMESTAMP__ + entry ("timestamp (__TIMESTAMP__)", "%s", SUPPRESSED_STR); +#endif + +#else + entry ("compile date (__DATE__)", "%s", __DATE__); + entry ("compile time (__TIME__)", "%s", __TIME__); + +#ifdef __TIMESTAMP__ + entry ("timestamp (__TIMESTAMP__)", "%s", __TIMESTAMP__); +#endif +#endif + + entry ("translation unit (__FILE__)", "%s", __FILE__); + entry ("base file (__BASE_FILE__)", "%s", __BASE_FILE__); + + section_open ("feature test macros"); + +#ifdef __STDC_VERSION__ + entry ("__STDC_VERSION__", "%lu", __STDC_VERSION__); +#else + entry ("__STDC_VERSION__", "%s", NOT_DEFINED_STR); +#endif #ifdef __STRICT_ANSI__ - show ("__STRICT_ANSI__: %s", DEFINED_STR); + entry ("__STRICT_ANSI__", "%s", DEFINED_STR); #else - show ("__STRICT_ANSI__: %s", NOT_DEFINED_STR); + entry ("__STRICT_ANSI__", "%s", NOT_DEFINED_STR); #endif #ifdef _POSIX_C_SOURCE - show ("_POSIX_C_SOURCE: %lu", _POSIX_C_SOURCE); + entry ("_POSIX_C_SOURCE", "%lu", _POSIX_C_SOURCE); #else - show ("_POSIX_C_SOURCE: %s", NOT_DEFINED_STR); + entry ("_POSIX_C_SOURCE", "%s", NOT_DEFINED_STR); +#endif + +#if defined (_POSIX_RAW_SOCKETS) + entry ("_POSIX_RAW_SOCKETS", "%s", DEFINED_STR), +#else + entry ("_POSIX_RAW_SOCKETS", "%s", NOT_DEFINED_STR), #endif #ifdef _POSIX_SOURCE - show ("_POSIX_SOURCE: %s", DEFINED_STR); + entry ("_POSIX_SOURCE", "%s", DEFINED_STR); #else - show ("_POSIX_SOURCE: %s", NOT_DEFINED_STR); + entry ("_POSIX_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _XOPEN_SOURCE - show ("_XOPEN_SOURCE: %lu", _XOPEN_SOURCE); + entry ("_XOPEN_SOURCE", "%lu", _XOPEN_SOURCE); #else - show ("_XOPEN_SOURCE: %s", NOT_DEFINED_STR); + entry ("_XOPEN_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _XOPEN_SOURCE_EXTENDED - show ("_XOPEN_SOURCE_EXTENDED: %s", DEFINED_STR); + entry ("_XOPEN_SOURCE_EXTENDED", "%s", DEFINED_STR); #else - show ("_XOPEN_SOURCE_EXTENDED: %s", NOT_DEFINED_STR); + entry ("_XOPEN_SOURCE_EXTENDED", "%s", NOT_DEFINED_STR); #endif #ifdef _ISOC95_SOURCE - show ("_ISOC95_SOURCE: %s", DEFINED_STR); + entry ("_ISOC95_SOURCE", "%s", DEFINED_STR); #else - show ("_ISOC95_SOURCE: %s", NOT_DEFINED_STR); + entry ("_ISOC95_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _ISOC99_SOURCE - show ("_ISOC99_SOURCE: %s", DEFINED_STR); + entry ("_ISOC99_SOURCE", "%s", DEFINED_STR); #else - show ("_ISOC99_SOURCE: %s", NOT_DEFINED_STR); + entry ("_ISOC99_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _ISOC11_SOURCE - show ("_ISOC11_SOURCE: %s", DEFINED_STR); + entry ("_ISOC11_SOURCE", "%s", DEFINED_STR); +#else + entry ("_ISOC11_SOURCE", "%s", NOT_DEFINED_STR); +#endif + +#ifdef _LARGEFILE_SOURCE + entry ("_LARGEFILE_SOURCE", "%s", DEFINED_STR); #else - show ("_ISOC11_SOURCE: %s", NOT_DEFINED_STR); + entry ("_LARGEFILE_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _LARGEFILE64_SOURCE - show ("_LARGEFILE64_SOURCE: %s", DEFINED_STR); + entry ("_LARGEFILE64_SOURCE", "%s", DEFINED_STR); #else - show ("_LARGEFILE64_SOURCE: %s", NOT_DEFINED_STR); + entry ("_LARGEFILE64_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _FILE_OFFSET_BITS - show ("_FILE_OFFSET_BITS: %lu", _FILE_OFFSET_BITS); + entry ("_FILE_OFFSET_BITS", "%lu", _FILE_OFFSET_BITS); #else - show ("_FILE_OFFSET_BITS: %s", NOT_DEFINED_STR); + entry ("_FILE_OFFSET_BITS", "%s", NOT_DEFINED_STR); #endif #ifdef _BSD_SOURCE - show ("_BSD_SOURCE: %s", DEFINED_STR); + entry ("_BSD_SOURCE", "%s", DEFINED_STR); #else - show ("_BSD_SOURCE: %s", NOT_DEFINED_STR); + entry ("_BSD_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _SVID_SOURCE - show ("_SVID_SOURCE: %s", DEFINED_STR); + entry ("_SVID_SOURCE", "%s", DEFINED_STR); #else - show ("_SVID_SOURCE: %s", NOT_DEFINED_STR); + entry ("_SVID_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _ATFILE_SOURCE - show ("_ATFILE_SOURCE: %s", DEFINED_STR); + entry ("_ATFILE_SOURCE", "%s", DEFINED_STR); #else - show ("_ATFILE_SOURCE: %s", NOT_DEFINED_STR); + entry ("_ATFILE_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _GNU_SOURCE - show ("_GNU_SOURCE: %s", DEFINED_STR); + entry ("_GNU_SOURCE", "%s", DEFINED_STR); #else - show ("_GNU_SOURCE: %s", NOT_DEFINED_STR); + entry ("_GNU_SOURCE", "%s", NOT_DEFINED_STR); #endif #ifdef _REENTRANT - show ("_REENTRANT: %s", DEFINED_STR); + entry ("_REENTRANT", "%s", DEFINED_STR); #else - show ("_REENTRANT: %s", NOT_DEFINED_STR); + entry ("_REENTRANT", "%s", NOT_DEFINED_STR); #endif #ifdef _THREAD_SAFE - show ("_THREAD_SAFE: %s", DEFINED_STR); + entry ("_THREAD_SAFE", "%s", DEFINED_STR); #else - show ("_THREAD_SAFE: %s", NOT_DEFINED_STR); + entry ("_THREAD_SAFE", "%s", NOT_DEFINED_STR); #endif #ifdef _FORTIFY_SOURCE - show ("_FORTIFY_SOURCE: %s", DEFINED_STR); + entry ("_FORTIFY_SOURCE", "%s", DEFINED_STR); +#else + entry ("_FORTIFY_SOURCE", "%s", NOT_DEFINED_STR); +#endif + +#ifdef _DEFAULT_SOURCE + entry ("_DEFAULT_SOURCE", "%s", DEFINED_STR); #else - show ("_FORTIFY_SOURCE: %s", NOT_DEFINED_STR); + entry ("_DEFAULT_SOURCE", "%s", NOT_DEFINED_STR); #endif + section_close (); + footer (); +} + +void +show_libc (void) +{ + char *name = NULL; + long version = -1; + long minor = -1; + +#if defined (__GLIBC__) + name = "GNU libc (glibc)"; + version = __GLIBC__; +#ifdef __GLIBC_MINOR__ + minor = __GLIBC_MINOR__; +#endif +#endif + +#if defined (__UCLIBC__) + name = "uClibc"; +#ifdef __UCLIBC_MAJOR__ + version = __UCLIBC_MAJOR__; +#endif +#ifdef __UCLIBC_MINOR__ + minor = __UCLIBC_MINOR__; +#endif +#endif + +#if defined (__KLIBC__) + name = "klibc"; + version = __KLIBC__; +#ifdef __KLIBC_MINOR__ + minor = __KLIBC_MINOR__; +#endif +#endif + +#if defined (__dietlibc__) + name = "dietlibc"; +#endif + +#if defined (__BIONIC__) + name = "bionic"; +#endif + + header ("libc"); + + entry ("name", "%s", name ? name : UNKNOWN_STR); + + if (version >= 0) { + entry ("version", "%lu", version); + } else { + entry ("version", "%s", UNKNOWN_STR); + } + + if (minor >= 0) { + entry ("minor", "%lu", minor); + } else { + entry ("minor", "%s", UNKNOWN_STR); + } + + footer (); } void show_time (void) { - char formatted_time[32]; + char formatted_time[CTIME_BUFFER]; struct timespec ts; struct tm *tm; @@ -3935,25 +7245,28 @@ show_time (void) if (! tm) die ("failed to determine localtime"); - header ("time"); - show ("raw: %u.%lu", - (unsigned int)ts.tv_sec, - ts.tv_nsec); - if (! asctime_r (tm, formatted_time)) die ("failed to determine formatted time"); /* overwrite trailing '\n' */ - formatted_time[ strlen (formatted_time)-1] = '\0'; + formatted_time[strlen (formatted_time)-1] = '\0'; - show ("local: %s", formatted_time); + header ("time"); + + entry ("raw", "%u.%lu", + (unsigned int)ts.tv_sec, + ts.tv_nsec); + + entry ("local", "%s", formatted_time); - show ("ISO: %4.4d-%2.2d-%2.2dT%2.2d:%2.2d", + entry ("ISO", "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d", 1900+tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min); + + footer (); } void @@ -3964,202 +7277,420 @@ get_uname (void) } void -dump_uname (void) +show_uname (void) { header ("uname"); - show ("sysname: %s", uts.sysname); - show ("nodename: %s", uts.nodename); - show ("release: %s", uts.release); - show ("version: %s", uts.version); - show ("machine: %s", uts.machine); + entry ("sysname", "%s", uts.sysname); + entry ("nodename", "%s", uts.nodename); + entry ("release", "%s", uts.release); + entry ("version", "%s", uts.version); + entry ("machine", "%s", uts.machine); #if defined (_GNU_SOURCE) && defined (PROCENV_LINUX) - show ("domainname: %s", uts.domainname); + entry ("domainname", "%s", uts.domainname[0] ? uts.domainname : UNKNOWN_STR); #endif + + footer (); } +void +show_cgroups (void) +{ #if defined (PROCENV_LINUX) + show_cgroups_linux (); +#else + show_cgroups_stub (); +#endif +} + +void +show_oom (void) +{ +#if defined (PROCENV_LINUX) + show_oom_linux (); +#else + show_oom_stub (); +#endif +} void show_capabilities (void) { - int ret; +#if defined (PROCENV_LINUX) + show_capabilities_linux (); +#else + show_capabilities_stub (); +#endif +} + +#if defined (PROCENV_LINUX) + +#if defined (HAVE_SYS_CAPABILITY_H) +int +get_capability_by_flag_type (cap_t cap_p, cap_flag_t type, cap_value_t cap) +{ + int ret; + cap_flag_value_t result; + + assert (cap_p); + + ret = cap_get_flag (cap_p, cap, type, &result); + + return ret < 0 ? ret : result; +} +#endif /* HAVE_SYS_CAPABILITY_H */ + +void +show_capabilities_linux (void) +{ +#if defined (HAVE_SYS_CAPABILITY_H) + int last_known; + cap_t caps; + + /* Most recently-added capability that procenv knew about at + * compile time. + */ + last_known = CAP_LAST_CAP; +#endif + + header ("capabilities"); + +#if defined (HAVE_SYS_CAPABILITY_H) + caps = cap_get_proc (); + + entry ("count (CAP_LAST_CAP+1)", "%d", CAP_LAST_CAP+1); + + if (! caps) + goto out; + + section_open ("known"); + + show_capability (caps, CAP_CHOWN); + show_capability (caps, CAP_DAC_OVERRIDE); + show_capability (caps, CAP_DAC_READ_SEARCH); + show_capability (caps, CAP_FOWNER); + show_capability (caps, CAP_FSETID); + show_capability (caps, CAP_KILL); + show_capability (caps, CAP_SETGID); + show_capability (caps, CAP_SETUID); + show_capability (caps, CAP_SETPCAP); + show_capability (caps, CAP_LINUX_IMMUTABLE); + show_capability (caps, CAP_NET_BIND_SERVICE); + show_capability (caps, CAP_NET_BROADCAST); + show_capability (caps, CAP_NET_ADMIN); + show_capability (caps, CAP_NET_RAW); + show_capability (caps, CAP_IPC_LOCK); + show_capability (caps, CAP_IPC_OWNER); + show_capability (caps, CAP_SYS_MODULE); + show_capability (caps, CAP_SYS_RAWIO); + show_capability (caps, CAP_SYS_CHROOT); + show_capability (caps, CAP_SYS_PTRACE); + show_capability (caps, CAP_SYS_PACCT); + show_capability (caps, CAP_SYS_ADMIN); + show_capability (caps, CAP_SYS_BOOT); + show_capability (caps, CAP_SYS_NICE); + show_capability (caps, CAP_SYS_RESOURCE); + show_capability (caps, CAP_SYS_TIME); + show_capability (caps, CAP_SYS_TTY_CONFIG); - header ("capabilities(linux)"); - - show_capability (CAP_CHOWN); - show_capability (CAP_DAC_OVERRIDE); - show_capability (CAP_DAC_READ_SEARCH); - show_capability (CAP_FOWNER); - show_capability (CAP_FSETID); - show_capability (CAP_KILL); - show_capability (CAP_SETGID); - show_capability (CAP_SETUID); - show_capability (CAP_SETPCAP); - show_capability (CAP_LINUX_IMMUTABLE); - show_capability (CAP_NET_BIND_SERVICE); - show_capability (CAP_NET_BROADCAST); - show_capability (CAP_NET_ADMIN); - show_capability (CAP_NET_RAW); - show_capability (CAP_IPC_LOCK); - show_capability (CAP_IPC_OWNER); - show_capability (CAP_SYS_MODULE); - show_capability (CAP_SYS_RAWIO); - show_capability (CAP_SYS_CHROOT); - show_capability (CAP_SYS_PTRACE); - show_capability (CAP_SYS_PACCT); - show_capability (CAP_SYS_ADMIN); - show_capability (CAP_SYS_BOOT); - show_capability (CAP_SYS_NICE); - show_capability (CAP_SYS_RESOURCE); - show_capability (CAP_SYS_TIME); - show_capability (CAP_SYS_TTY_CONFIG); if (LINUX_KERNEL_MM (2, 4)) { - show_capability (CAP_MKNOD); - show_capability (CAP_LEASE); + show_capability (caps, CAP_MKNOD); + show_capability (caps, CAP_LEASE); } + if (LINUX_KERNEL_MMR (2, 6, 11)) { - show_capability (CAP_AUDIT_WRITE); - show_capability (CAP_AUDIT_CONTROL); + show_capability (caps, CAP_AUDIT_WRITE); + show_capability (caps, CAP_AUDIT_CONTROL); } if (LINUX_KERNEL_MMR (2, 6, 24)) - show_capability (CAP_SETFCAP); + show_capability (caps, CAP_SETFCAP); if (LINUX_KERNEL_MMR (2, 6, 25)) { - show_capability (CAP_MAC_OVERRIDE); - show_capability (CAP_MAC_ADMIN); + show_capability (caps, CAP_MAC_OVERRIDE); + show_capability (caps, CAP_MAC_ADMIN); } #ifdef CAP_SYSLOG if (LINUX_KERNEL_MMR (2, 6, 37)) - show_capability (CAP_SYSLOG); + show_capability (caps, CAP_SYSLOG); #endif #ifdef CAP_WAKE_ALARM if (LINUX_KERNEL_MM (3, 0)) - show_capability (CAP_WAKE_ALARM); + show_capability (caps, CAP_WAKE_ALARM); +#endif + +#ifdef CAP_BLOCK_SUSPEND + if (LINUX_KERNEL_MM (3, 5)) + show_capability (caps, CAP_BLOCK_SUSPEND); +#endif + +#ifdef CAP_AUDIT_READ + if (LINUX_KERNEL_MM (3, 16)) { + show_capability (caps, CAP_AUDIT_READ); + } +#endif + + section_close (); + + /* It's possible that procenv is running on a system which has + * more capabilities that the system it was built on (for + * example, it might be running in a chroot with a newer kernel + * than the chroot environment). So display any unknown + * capabilities. We don't have their names, but it's useful to + * see that there are additional capabilities in available in + * the environment. + */ + section_open ("unknown"); + +#if defined (PR_CAPBSET_READ) + for (int i = 1+last_known; ; i++) { + int ret; + char *name = NULL; + + ret = cap_get_bound (i); + if (ret < 0) + break; + + /* Found an "unknown" */ + + appendf (&name, "CAP_LAST_CAP+%d", i); + + _show_capability (caps, i, name); + + free (name); + } #endif + cap_free (caps); + + section_close (); + #ifdef PR_GET_KEEPCAPS if (LINUX_KERNEL_MMR (2, 2, 18)) { + int ret; ret = prctl (PR_GET_KEEPCAPS, 0, 0, 0, 0); if (ret < 0) - show ("keep=%s", UNKNOWN_STR); + entry ("keep", "%s", UNKNOWN_STR); else - show ("keep=%s", ret ? YES_STR : NO_STR); + entry ("keep", "%s", ret ? YES_STR : NO_STR); } #endif + #if defined (PR_GET_SECUREBITS) && defined (HAVE_LINUX_SECUREBITS_H) if (LINUX_KERNEL_MMR (2, 6, 26)) { + int ret; + ret = prctl (PR_GET_SECUREBITS, 0, 0, 0, 0); if (ret < 0) - show ("securebits=%s", UNKNOWN_STR); + entry ("securebits", "%s", UNKNOWN_STR); else { struct securebits_t { unsigned int securebits; } flags; flags.securebits = (unsigned int)ret; - show ("securebits=0x%x", flags.securebits); + + section_open ("securebits"); + + entry ("value", "0x%x", flags.securebits); + + container_open ("fields"); show_const (flags, securebits, SECBIT_KEEP_CAPS); show_const (flags, securebits, SECBIT_NO_SETUID_FIXUP); show_const (flags, securebits, SECBIT_NOROOT); + + container_close (); + + section_close (); } } #endif + +out: +#endif /* HAVE_SYS_CAPABILITY_H */ + footer (); } +#if defined (HAVE_SYS_CAPABILITY_H) +#ifdef PROCENV_NEED_LOCAL_CAP_GET_BOUND + +int cap_get_bound (cap_value_t cap) +{ +#if defined (PR_CAPBSET_READ) + return prctl (PR_CAPBSET_READ, cap); +#else + return -1; +#endif +} + +#endif /* PROCENV_NEED_LOCAL_CAP_GET_BOUND */ +#endif /* HAVE_SYS_CAPABILITY_H */ + +void +show_timezone_linux (void) +{ + tzset (); + + header ("timezone"); + + entry ("tzname[0]", "'%s'", tzname[0]); + entry ("tzname[1]", "'%s'", tzname[1]); + entry ("timezone", "%ld", timezone); + entry ("daylight", "%d", daylight); + + footer (); +} + void -show_linux_security_module (void) +show_security_module_linux (void) { char *lsm = UNKNOWN_STR; + #if defined (HAVE_APPARMOR) if (aa_is_enabled ()) lsm = "AppArmor"; #endif -#if defined (HAVE_SELINUX) - if (is_selinux_enabled ()) - lsm = "SELinux"; + +#if defined (HAVE_SELINUX_SELINUX_H) + if (is_selinux_enabled () == 1) { + + if (is_selinux_mls_enabled () == 1) + lsm = "SELinux (MLS)"; + else + lsm = "SELinux"; + } #endif - show ("Linux Security Module: %s", lsm); + + entry ("name", "%s", lsm); } void -show_linux_security_module_context (void) +show_security_module_context_linux (void) { char *context = NULL; char *mode = NULL; #if defined (HAVE_APPARMOR) - if (aa_is_enabled ()) + if (aa_is_enabled ()) { + /* XXX: The mode string is *NOT* be freed since it forms + * part of the allocation returned in context. + * + * See aa_gettaskcon(2) for details. + */ if (aa_gettaskcon (user.pid, &context, &mode) < 0) die ("failed to query AppArmor context"); + } #endif -#if defined (HAVE_SELINUX) - if (is_selinux_enabled ()) + +#if defined (HAVE_SELINUX_SELINUX_H) + if (is_selinux_enabled ()) { if (getpidcon (user.pid, &context) < 0) die ("failed to query SELinux context"); + } #endif if (context) { - if (mode) - show ("LSM context: %s (%s)", context, mode); - else - show ("LSM context: %s", context); - } else - show ("LSM context: %s", UNKNOWN_STR); + if (mode) { + entry ("context", "%s (%s)", context, mode); + } else { + entry ("context", "%s", context); + } + } else { + entry ("context", "%s", UNKNOWN_STR); + } - free (context); - free (mode); + free (context); } void -show_linux_cgroups (void) +show_cgroups_linux (void) { - char *file = "/proc/self/cgroup"; - FILE *f; - char buffer[1024]; - size_t len; + const char *delim = ":"; + char *file = "/proc/self/cgroup"; + FILE *f; + char buffer[1024]; + size_t len; - header ("cgroup(linux)"); + header ("cgroups"); f = fopen (file, "r"); - if (! f) { - show ("%s", UNKNOWN_STR); - return; - } + + if (! f) + goto out; while (fgets (buffer, sizeof (buffer), f)) { + char *buf, *b; + char *hierarchy; + char *subsystems; + char *path; + len = strlen (buffer); + /* Remove NL */ buffer[len-1] = '\0'; - show ("%s", buffer); + + buf = b = strdup (buffer); + if (! buf) + die ("failed to alloate storage"); + + hierarchy = strsep (&b, delim); + if (! hierarchy) + goto next; + + subsystems = strsep (&b, delim); + if (! subsystems) + goto next; + + path = strsep (&b, delim); + if (! path) + goto next; + + /* FIXME: should sort by hierarchy */ + container_open (hierarchy); + + object_open (FALSE); + + /* FIXME: should split this on comma */ + entry ("subsystems", "%s", subsystems); + + entry ("path", "%s", path); + + object_close (FALSE); + + container_close (); + +next: + free (buf); } fclose (f); + +out: + + footer (); } void -dump_linux_proc_fds (void) +show_fds_linux (void) { - DIR *dir; - struct dirent *ent; - struct stat st; - char *prefix_path = "/proc/self/fd"; - char path[MAXPATHLEN]; - char link[MAXPATHLEN]; - ssize_t len; + DIR *dir; + struct dirent *ent; + char *prefix_path = "/proc/self/fd"; + struct stat st; + char path[MAXPATHLEN]; + char link[MAXPATHLEN]; + ssize_t len; - header ("fds (linux/proc)"); + container_open ("file descriptors"); dir = opendir (prefix_path); - if (! dir) { - show ("%s", UNKNOWN_STR); + if (! dir) return; - } while ((ent=readdir (dir)) != NULL) { - int fd; + int fd; + char *num = NULL; if (! strcmp (ent->d_name, ".") || ! strcmp (ent->d_name, "..")) continue; @@ -4172,31 +7703,126 @@ dump_linux_proc_fds (void) /* ignore errors */ continue; + appendf (&num, "%d", fd); + assert (len); link[len] = '\0'; if (link[0] == '/') { - if (stat (link, &st) < 0) + if (stat (link, &st) < 0) { + free (num); continue; + } /* Ignore the last (invalid) entry */ - if (S_ISDIR (st.st_mode)) + if (S_ISDIR (st.st_mode)) { + free (num); continue; - + } } - show ("'%s' -> '%s' (terminal=%s, valid=%s)", - path, link, - isatty (fd) ? YES_STR : NO_STR, - fd_valid (fd) ? YES_STR : NO_STR); + object_open (FALSE); + + section_open (num); + free (num); + + entry ("terminal", "%s", isatty (fd) ? YES_STR : NO_STR); + entry ("valid", "%s", fd_valid (fd) ? YES_STR : NO_STR); + entry ("device", "%s", link); + + section_close (); + + object_close (FALSE); } closedir (dir); + + container_close (); } void -show_oom (void) +show_namespaces_linux (void) +{ + DIR *dir; + struct dirent *ent; + char *prefix_path = "/proc/self/ns"; + char path[MAXPATHLEN]; + char link[MAXPATHLEN]; + ssize_t len; + PRList *list = NULL; + + container_open ("namespaces"); + + dir = opendir (prefix_path); + if (! dir) + goto end; + + list = pr_list_new (NULL); + assert (list); + + while ((ent=readdir (dir)) != NULL) { + PRList *entry; + + if (! strcmp (ent->d_name, ".") || ! strcmp (ent->d_name, "..")) + continue; + + sprintf (path, "%s/%s", prefix_path, ent->d_name); + + len = readlink (path, link, sizeof (link)-1); + if (len < 0) + /* ignore errors */ + continue; + + assert (len); + link[len] = '\0'; + + entry = pr_list_new (strdup (link)); + assert (entry); + + assert (pr_list_prepend_str_sorted (list, entry)); + } + + closedir (dir); + + PR_LIST_FOREACH_SAFE (list, iter) { + char *tmp; + char *name; + char *value; + + pr_list_remove (iter); + + tmp = iter->data; + + name = strsep (&tmp, ":"); + if (! name) + goto give_up; + + value = strsep (&tmp, "]"); + if (! value) + goto give_up; + + if (*value == '[' && value+1 && *(value+1)) { + value++; + } + + object_open (FALSE); + entry (name, "%s", value); + object_close (FALSE); + +give_up: + free ((char *)iter->data); + free(iter); + } + + free_if_set (list); + +end: + container_close (); +} + +void +show_oom_linux (void) { char *dir = "/proc/self"; char *files[] = { "oom_score", "oom_adj", "oom_score_adj", NULL }; @@ -4208,7 +7834,7 @@ show_oom (void) int ret; int seen = FALSE; - header ("oom(linux)"); + header ("oom"); for (file = files; file && *file; file++) { ret = sprintf (path, "%s/%s", dir, *file); @@ -4224,14 +7850,16 @@ show_oom (void) while (fgets (buffer, sizeof (buffer), f)) { len = strlen (buffer); buffer[len-1] = '\0'; - show ("%s=%s", *file, buffer); + entry (*file, "%s", buffer); } fclose (f); } if (! seen) - show ("%s", UNKNOWN_STR); + entry ("%s", UNKNOWN_STR); + + footer (); } char * @@ -4248,20 +7876,9 @@ get_scheduler_name (int sched) } void -show_linux_scheduler (void) +show_cpu_linux (void) { - int sched; - - sched = sched_getscheduler (0); - show ("scheduler: %s", - sched < 0 ? UNKNOWN_STR : - get_scheduler_name (sched)); -} - -void -show_linux_cpu (void) -{ - int cpu; + int cpu = -1; long max; max = get_sysconf (_SC_NPROCESSORS_ONLN); @@ -4271,36 +7888,102 @@ show_linux_cpu (void) if (cpu < 0) goto unknown_sched_cpu; +#else + cpu = procenv_getcpu (); + if (cpu < 0) + goto unknown_sched_cpu; +#endif + /* adjust to make 1-based */ cpu++; - show ("cpu: %u of %lu", cpu, max); + entry ("number", "%u of %ld", cpu, max); return; unknown_sched_cpu: -#endif - show ("cpu: %s of %lu", UNKNOWN_STR, max); + + entry ("number", "%s of %ld", UNKNOWN_STR, max); +} + +#if ! defined (HAVE_SCHED_GETCPU) + +/* Crutch function for RHEL 5 */ +int +procenv_getcpu (void) +{ + int cpu = -1; + FILE *f; + char **fields; + const char *field; + char buffer[1024]; + size_t len; + size_t count; + + f = fopen ("/proc/self/stat", "r"); + if (! f) + goto out; + + if (! fgets (buffer, sizeof (buffer), f)) + goto out; + + fclose (f); + + len = strlen (buffer); + buffer[len-1] = '\0'; + + count = split_fields (buffer, ' ', TRUE, &fields); + + if (! count) + return -1; + + if (count != 42) + goto cleanup; + + field = fields[41]; + assert (field); + + cpu = atoi (field); + +cleanup: + + for (len = 0; len < count; len++) + free (fields[len]); + free (fields); + +out: + return cpu; } +#endif /** - * get_root: + * get_canonical: + * + * @path: path to convert to canonical form, + * @canonical [out]: canonical version of @path, + * @len: Size of @canonical (should be atleast PATH_MAX). * - * @root [out]: path of root directory, - * @len: Size of @root (should be atleast PATH_MAX). + * FIXME: this should fully resolve not just sym links but replace all + * occurences of '../' by the appropriate direcotry! **/ -void -get_root (char *root, size_t len) +int +get_canonical (const char *path, char *canonical, size_t len) { - char self[] = "/proc/self/root"; ssize_t bytes; + int ret = TRUE; - assert (root); + assert (path); + assert (canonical); + assert (len); - bytes = readlink (self, root, len); - if (bytes < 0) - sprintf (root, UNKNOWN_STR); - else - root[bytes] = '\0'; + bytes = readlink (path, canonical, len); + if (bytes < 0) { + sprintf (canonical, UNKNOWN_STR); + ret = FALSE; + } else { + canonical[bytes <= len ? bytes : len] = '\0'; + } + + return ret; } void @@ -4309,8 +7992,66 @@ get_tty_locked_status (struct termios *lock_status) assert (lock_status); assert (user.tty_fd != -1); - if (ioctl (user.tty_fd, TIOCGLCKTRMIOS, lock_status) < 0) - die ("failed to query terminal lock status"); + if (ioctl (user.tty_fd, TIOCGLCKTRMIOS, lock_status) < 0) { + /* Set to unlocked */ + memset (lock_status, '\0', sizeof (struct termios)); + } +} +#else + +void +show_namespaces_stub (void) +{ + header ("namespaces"); + footer (); +} +void +show_cgroups_stub (void) +{ + header ("cgroups"); + footer (); +} + +void +show_oom_stub (void) +{ + header ("oom"); + footer (); +} + +void +show_timezone_stub (void) +{ + header ("timezone"); + footer (); +} + +void +show_capabilities_stub (void) +{ + header ("capabilities"); + footer (); +} + +void +show_shared_mem_stub (void) +{ + header ("shared memory"); + footer (); +} + +void +show_semaphores_stub (void) +{ + header ("semaphores"); + footer (); +} + +void +show_msg_queues_stub (void) +{ + header ("message queues"); + footer (); } #endif /* PROCENV_LINUX */ @@ -4329,9 +8070,9 @@ has_ctty (void) return TRUE; } -#if defined (PROCENV_BSD) || defined (__FreeBSD_kernel__) +#if defined (PROCENV_BSD) || defined (PROCENV_GNU_BSD) void -show_bsd_cpu (void) +show_cpu_bsd (void) { long max; kvm_t *kvm; @@ -4358,11 +8099,11 @@ show_bsd_cpu (void) if (kvm_close (kvm) < 0) die ("failed to close kvm"); - show ("cpu: %u of %lu", cpu, max); + entry ("number", "%u of %lu", cpu, max); } void -get_bsd_misc (void) +get_misc_bsd (void) { char errors[_POSIX2_LINE_MAX]; kvm_t *kvm; @@ -4399,6 +8140,43 @@ get_output_value (const char *name) } } die ("invalid output value: '%s'", name); + + /* compiler appeasement */ + return -1; +} + +int +get_output_format (const char *name) +{ + struct procenv_map *p; + + assert (name); + + for (p = output_format_map; p && p->name; p++) { + if (! strcmp (name, p->name)) { + return p->num; + } + } + die ("invalid output format value: '%s'", name); + + /* compiler appeasement */ + return -1; +} + +const char * +get_output_format_name (void) +{ + struct procenv_map *p; + + for (p = output_format_map; p && p->name; p++) { + if (output_format == p->num) + return p->name; + } + + bug ("invalid output format: %d", output_format); + + /* compiler appeasement */ + return NULL; } void @@ -4420,6 +8198,39 @@ check_envvars (void) output_file = e; output = OUTPUT_FILE; } + + e = getenv (PROCENV_FORMAT_ENV); + if (e && *e) { + output_format = get_output_format (e); + } + + e = getenv (PROCENV_INDENT_ENV); + if (e && *e) { + indent_amount = atoi (e); + } + + e = getenv (PROCENV_INDENT_CHAR_ENV); + if (e && *e) { + /* Special character handling */ + if (! strcmp (e, "\\t")) + indent_char = "\t"; + else + indent_char = e; + } + + e = getenv (PROCENV_SEPARATOR_ENV); + if (e && *e) { + text_separator = e; + } + + e = getenv (PROCENV_CRUMB_SEPARATOR_ENV); + if (e && *e) { + if (! strcmp (e, "\\t")) + crumb_separator = "\t"; + else + crumb_separator = e; + } + e = getenv (PROCENV_EXEC_ENV); if (e && *e) { char *tmp; @@ -4461,7 +8272,7 @@ check_envvars (void) } } -void +int get_major_minor (const char *path, unsigned int *_major, unsigned int *_minor) { struct stat st; @@ -4474,13 +8285,14 @@ get_major_minor (const char *path, unsigned int *_major, unsigned int *_minor) /* Don't fail as this query may be for a mount which the * user does not have permission to check. */ - warn ("unable to stat path '%s'", path); *_major = *_minor = 0; - return; + return FALSE; } *_major = major (st.st_dev); *_minor = minor (st.st_dev); + + return TRUE; } /** @@ -4580,7 +8392,7 @@ show_threads (void) (void) pthread_attr_init (&attr); (void) pthread_attr_getstacksize (&attr, &stack_size); - show ("thread stack size: %lu bytes", + entry ("stack size", "%lu bytes", (unsigned long int)stack_size); #if defined (PROCENV_ANDROID) @@ -4589,43 +8401,52 @@ show_threads (void) #else ret = pthread_attr_getscope (&attr, &scope); #endif - show ("thread scope: %s", + entry ("scope", "%s", ret != 0 ? UNKNOWN_STR : scope == PTHREAD_SCOPE_SYSTEM ? "PTHREAD_SCOPE_SYSTEM" : "PTHREAD_SCOPE_PROCESS"); ret = pthread_attr_getguardsize (&attr, &guard_size); if (ret == 0) { - show ("thread guard size: %lu bytes", + entry ("guard size", "%lu bytes", (unsigned long int)guard_size); } else { - show ("thread guard size: %s", UNKNOWN_STR); + entry ("guard size", "%s", UNKNOWN_STR); } ret = pthread_getschedparam (pthread_self (), &sched, ¶m); - show ("thread scheduler: %s", + + section_open ("scheduler"); + + entry ("type", "%s", ret != 0 ? UNKNOWN_STR : get_thread_scheduler_name (sched)); if (ret != 0) - show ("thread scheduler priority: %s", UNKNOWN_STR); + entry ("priority", "%s", UNKNOWN_STR); else - show ("thread scheduler priority: %d", param.sched_priority); + entry ("priority", "%d", param.sched_priority); -#ifndef PROCENV_ANDROID +#ifdef PROCENV_ANDROID + section_close (); +#else ret = pthread_attr_getinheritsched (&attr, &inherit_sched); - show ("thread inherit scheduler: %s", + entry ("inherit-scheduler attribute", "%s", ret != 0 ? UNKNOWN_STR : inherit_sched == PTHREAD_INHERIT_SCHED ? "PTHREAD_INHERIT_SCHED" : "PTHREAD_EXPLICIT_SCHED"); - show ("thread concurrency: %d", pthread_getconcurrency ()); + section_close (); + + entry ("concurrency", "%d", pthread_getconcurrency ()); #endif + + footer (); } -char * +const char * get_thread_scheduler_name (int sched) { struct procenv_map *p; @@ -4645,7 +8466,7 @@ void show_data_model (void) { int ilp[3]; - char data_model[8]; + char data_model[16]; size_t pointer_size; ilp[0] = sizeof (int); @@ -4670,7 +8491,7 @@ show_data_model (void) if (pointer_size > 8) die ("%d-byte pointers not supported", (int)pointer_size); - show ("data model: %s (%d/%d/%d)", + entry ("data model", "%s (%d/%d/%d)", data_model, ilp[0], ilp[1], ilp[2]); } @@ -4678,61 +8499,69 @@ show_data_model (void) int main (int argc, - char *argv[]) + char *argv[]) { - int option; - int long_index; - int done = FALSE; + int option; + int long_index; + int done = FALSE; struct option long_options[] = { - {"meta" , no_argument, NULL, 'a'}, - {"arguments" , no_argument, NULL, 'A'}, - {"libs" , no_argument, NULL, 'b'}, - {"cgroup" , no_argument, NULL, 'c'}, - {"cgroups" , no_argument, NULL, 'c'}, - {"compiler" , no_argument, NULL, 'd'}, - {"env" , no_argument, NULL, 'e'}, - {"environment" , no_argument, NULL, 'e'}, - {"fds" , no_argument, NULL, 'f'}, - {"sizeof" , no_argument, NULL, 'g'}, - {"help" , no_argument, NULL, 'h'}, - {"misc" , no_argument, NULL, 'i'}, - {"uname" , no_argument, NULL, 'j'}, - {"clock" , no_argument, NULL, 'k'}, - {"clocks" , no_argument, NULL, 'k'}, - {"limits" , no_argument, NULL, 'l'}, - {"locale" , no_argument, NULL, 'L'}, - {"mount" , no_argument, NULL, 'm'}, - {"mounts" , no_argument, NULL, 'm'}, - {"confstr" , no_argument, NULL, 'n'}, - {"network" , no_argument, NULL, 'N'}, - {"oom" , no_argument, NULL, 'o'}, - {"proc" , no_argument, NULL, 'p'}, - {"process" , no_argument, NULL, 'p'}, - {"platform" , no_argument, NULL, 'P'}, - {"time" , no_argument, NULL, 'q'}, - {"range" , no_argument, NULL, 'r'}, - {"ranges" , no_argument, NULL, 'r'}, - {"signal" , no_argument, NULL, 's'}, - {"signals" , no_argument, NULL, 's'}, - {"tty" , no_argument, NULL, 't'}, - {"threads" , no_argument, NULL, 'T'}, - {"stat" , no_argument, NULL, 'u'}, - {"rusage" , no_argument, NULL, 'U'}, - {"version" , no_argument, NULL, 'v'}, - {"capabilities" , no_argument, NULL, 'w'}, - {"pathconf" , no_argument, NULL, 'x'}, - {"sysconf" , no_argument, NULL, 'y'}, - {"timezone" , no_argument, NULL, 'z'}, - - {"output" , required_argument, NULL, 0}, - {"file" , required_argument, NULL, 0}, - {"exec" , no_argument , NULL, 0}, + {"meta" , no_argument, NULL, 'a'}, + {"arguments" , no_argument, NULL, 'A'}, + {"libs" , no_argument, NULL, 'b'}, + {"libc" , no_argument, NULL, 'B'}, + {"cgroups" , no_argument, NULL, 'c'}, + {"cpu" , no_argument, NULL, 'C'}, + {"compiler" , no_argument, NULL, 'd'}, + {"crumb-separator" , required_argument, NULL, 0}, + {"environment" , no_argument, NULL, 'e'}, + {"semaphores" , no_argument, NULL, 'E'}, + {"fds" , no_argument, NULL, 'f'}, + {"namespaces" , no_argument, NULL, 'F'}, + {"sizeof" , no_argument, NULL, 'g'}, + {"help" , no_argument, NULL, 'h'}, + {"misc" , no_argument, NULL, 'i'}, + {"uname" , no_argument, NULL, 'j'}, + {"clocks" , no_argument, NULL, 'k'}, + {"limits" , no_argument, NULL, 'l'}, + {"locale" , no_argument, NULL, 'L'}, + {"mounts" , no_argument, NULL, 'm'}, + {"message-queues" , no_argument, NULL, 'M'}, + {"confstr" , no_argument, NULL, 'n'}, + {"network" , no_argument, NULL, 'N'}, + {"oom" , no_argument, NULL, 'o'}, + {"process" , no_argument, NULL, 'p'}, + {"platform" , no_argument, NULL, 'P'}, + {"time" , no_argument, NULL, 'q'}, + {"ranges" , no_argument, NULL, 'r'}, + {"signals" , no_argument, NULL, 's'}, + {"shared-memory" , no_argument, NULL, 'S'}, + {"tty" , no_argument, NULL, 't'}, + {"threads" , no_argument, NULL, 'T'}, + {"stat" , no_argument, NULL, 'u'}, + {"rusage" , no_argument, NULL, 'U'}, + {"version" , no_argument, NULL, 'v'}, + {"capabilities" , no_argument, NULL, 'w'}, + {"pathconf" , no_argument, NULL, 'x'}, + {"sysconf" , no_argument, NULL, 'y'}, + {"memory" , no_argument, NULL, 'Y'}, + {"timezone" , no_argument, NULL, 'z'}, + {"exec" , no_argument , NULL, 0}, + {"file" , required_argument, NULL, 0}, + {"format" , required_argument, NULL, 0}, + {"indent" , required_argument, NULL, 0}, + {"indent-char" , required_argument, NULL, 0}, + {"output" , required_argument, NULL, 0}, + {"separator" , required_argument, NULL, 0}, /* terminator */ - {NULL, no_argument, NULL, 0} + {NULL , no_argument , NULL, 0} }; + doc = pstring_new (); + if (! doc) + die ("failed to allocate string"); + program_name = argv[0]; argvp = argv; argvc = argc; @@ -4746,20 +8575,29 @@ main (int argc, while (TRUE) { option = getopt_long (argc, argv, - "aAbcdefghijklLmnNopPqrstTuUvwxyz", + "aAbBcCdeEfFghijklLmMnNopPqrsStTuUvwxyYz", long_options, &long_index); if (option == -1) break; - done = TRUE; + /* If the user has specified a display option, only + * display that particular group (but crucially don't + * count non-display options). + */ + if (option) { + done = TRUE; + master_header (&doc); + } selected_option = option; - set_indent (); - switch (option) { case 0: + /* The exception is '--exec' */ + if (done && strcmp ("exec", long_options[long_index].name)) + die ("Must specify non-display options before display options"); + if (! strcmp ("output", long_options[long_index].name)) { output = get_output_value (optarg); } else if (! strcmp ("file", long_options[long_index].name)) { @@ -4767,18 +8605,46 @@ main (int argc, output_file = optarg; } else if (! strcmp ("exec", long_options[long_index].name)) { reexec = TRUE; - } + } else if (! strcmp ("format", long_options[long_index].name)) { + output_format = get_output_format (optarg); + } else if (! strcmp ("indent", long_options[long_index].name)) { + indent_amount = atoi (optarg); + if (indent_amount <= 0) + die ("cannot specify indent <= 0"); + } else if (! strcmp ("indent-char", long_options[long_index].name)) { + /* Special character handling */ + if (! strcmp (optarg, "\\t")) { + indent_char = "\t"; + } else { + indent_char = optarg; + } + if (! indent_char) + die ("cannot use nul indent character"); - done = FALSE; + /* call again */ + handle_indent_char (); + } else if (! strcmp ("separator", long_options[long_index].name)) { + if (! strcmp (optarg, "\\t")) { + text_separator = "\t"; + } else { + text_separator = optarg; + } + } else if (! strcmp ("crumb-separator", long_options[long_index].name)) { + if (! strcmp (optarg, "\\t")) { + crumb_separator = "\t"; + } else { + crumb_separator = optarg; + } + } /* reset */ selected_option = 0; - set_indent (); + indent = 0; break; case 'a': - dump_meta (); + show_meta (); break; case 'A': @@ -4791,10 +8657,16 @@ main (int argc, #endif break; + case 'B': + show_libc (); + break; + case 'c': -#if defined (PROCENV_LINUX) - show_linux_cgroups (); -#endif + show_cgroups (); + break; + + case 'C': + show_cpu (); break; case 'd': @@ -4805,8 +8677,16 @@ main (int argc, show_env (); break; + case 'E': + show_semaphores (); + break; + case 'f': - dump_fds (); + show_fds (); + break; + + case 'F': + show_namespaces (); break; case 'g': @@ -4821,11 +8701,11 @@ main (int argc, get_uname (); get_user_info (); get_misc (); - dump_misc (); + show_misc (); break; case 'j': - dump_uname (); + show_uname (); break; case 'k': @@ -4844,6 +8724,10 @@ main (int argc, show_mounts (SHOW_MOUNTS); break; + case 'M': + show_msg_queues (); + break; + case 'n': #ifndef PROCENV_ANDROID show_confstrs (); @@ -4855,18 +8739,16 @@ main (int argc, break; case 'o': -#if defined (PROCENV_LINUX) show_oom (); -#endif break; case 'p': get_misc (); - dump_user (); + show_proc (); break; case 'P': - dump_platform (); + show_platform (); break; case 'q': @@ -4881,6 +8763,10 @@ main (int argc, show_signals (); break; + case 'S': + show_shared_mem (); + break; + case 't': show_tty_attrs (); break; @@ -4898,17 +8784,11 @@ main (int argc, break; case 'v': - show ("%s %s: %s", - PACKAGE_NAME, - _("version"), - PACKAGE_VERSION); - show ("%s: %s\n", _("Written by"), PROGRAM_AUTHORS); + show_version (); break; case 'w': -#if defined (PROCENV_LINUX) show_capabilities (); -#endif break; case 'x': @@ -4916,7 +8796,11 @@ main (int argc, break; case 'y': - dump_sysconf (); + show_sysconf (); + break; + + case 'Y': + show_memory (); break; case 'z': @@ -4929,8 +8813,20 @@ main (int argc, } } - if (done) - exit (EXIT_SUCCESS); + if (done) { + common_assert (); + + master_footer (&doc); + + chomp (doc); + + + if (output_format != OUTPUT_FORMAT_XML && output_format != OUTPUT_FORMAT_JSON) { + compress (&doc, wide_indent_char); + } + + goto finish; + } if (output == OUTPUT_SYSLOG) openlog (PACKAGE_NAME, LOG_CONS | LOG_PID, LOG_USER); @@ -4948,6 +8844,12 @@ main (int argc, dump (); + chomp (doc); + + compress (&doc, wide_indent_char); + +finish: + _show_output_pstring (doc); cleanup (); /* Perform re-exec */ @@ -4964,7 +8866,7 @@ char * get_user_name (uid_t uid) { struct passwd *p; - + p = getpwuid (uid); return p ? p->pw_name : NULL; @@ -4974,8 +8876,1875 @@ char * get_group_name (gid_t gid) { struct group *g; - + g = getgrgid (gid); return g ? g->gr_name : NULL; } + +/** + * encode_string: + * + * @str: string to encode. + * + * Returns: 0 on success, -1 on failure. + * + * Convert the specified string to its encoded form. If no encoding is + * necessary, the string will not be modified. + * + * Notes: + * + * - By encoding, we mean replacing literals with their + * format-langage-specific encodings. For example for XML output, + * '<' is converted to '<'. + * + * - It is the callers responsibility to free @str iff this function + * is successful. any previous value of @str will be freed by + * encode_string(). + * + * BUGS: this is just horribly, horribly gross :( + **/ +int +encode_string (pstring **pstr) +{ + int ret = 0; + pstring *new = NULL; + wchar_t *p, *q; + size_t non_printables; + size_t len = 0; + size_t bytes; + + assert (pstr); + assert (*pstr); + + if ((*pstr)->len <= 1) { + /* Nothing to do */ + return 0; + } + + switch (output_format) { + + case OUTPUT_FORMAT_CRUMB: /* FALL */ + case OUTPUT_FORMAT_TEXT: + /* Nothing to do */ + ret = 0; + break; + + case OUTPUT_FORMAT_JSON: /* FALL THROUGH */ + case OUTPUT_FORMAT_XML: + new = translate (*pstr); + if (new) { + pstring_free (*pstr); + *pstr = new; + new = NULL; + } else { + ret = -1; + } + break; + + default: + assert_not_reached (); + break; + } + + if (ret < 0) + return ret; + + /* Now, search for evil non-printable characters and encode those + * appropriately. + */ + for (p = (*pstr)->buf, non_printables = 0; p && *p; p++) { + if (! iswprint (*p)) + non_printables++; + } + + if (non_printables && + (output_format == OUTPUT_FORMAT_XML + || output_format == OUTPUT_FORMAT_JSON)) { + + size_t new_size = 0; + + wchar_t *json_format = L"\\u%4.4x"; + + /* XXX: + * + * Although this format spec _may_ produce valid XML, + * the rules are arcane and some(?) control characters + * cannot be used within an XML document, hence the + * "may". + * + * Aside from simply discarding non-printable characters + * (thus distorting the output), we are left with + * attempting to produce some sort of encoded + * representation which may well choke a validating + * parser. + * + * Realistically, the problem is confined to handling + * control characters set in environment variables when + * attempting to output XML. This may occur if you run + * GNU Screen since it sets $TERMCAP which includes + * binary characters. + * + * FIXME: + * + * If you hit this issue, raise a bug so we can consider + * simply discarding all non-printables when attempting + * XML output. + */ + wchar_t *xml_format = L"&#x%2.2x;"; + + len = (*pstr)->len; + + /* Calculate expanded size of string by removing + * count of non-printable byte and adding back the + * number of bytes required to encode them in expanded + * form. + */ + switch (output_format) { + case OUTPUT_FORMAT_XML: + new_size = (len - non_printables) + (non_printables * wcslen (L"&#x..;")); + break; + + case OUTPUT_FORMAT_JSON: + new_size = (len - non_printables) + (non_printables * wcslen (L"\\u....")); + break; + default: + break; + } + + new = pstring_new (); + if (! new) + return -1; + + bytes = (1 + new_size) * sizeof (wchar_t); + + new->buf = malloc (bytes); + if (! new->buf) { + free (new); + return -1; + } + + new->size = bytes; + + memset (new->buf, '\0', bytes); + + for (p = (*pstr)->buf, q = new->buf; p && *p; p++) { + if (iswprint (*p)) { + *q = *p; + q++; + new->len++; + } else { + ret = swprintf (q, + new_size, + output_format == OUTPUT_FORMAT_JSON + ? json_format : xml_format, + *p); + q += ret; + } + } + + /* include terminator */ + new->len = wcslen (new->buf) + 1; + + pstring_free (*pstr); + *pstr = new; + } + + return ret; +} + +/* Performs simple substitution on the input */ +pstring * +translate (const pstring *pstr) +{ + pstring *result = NULL; + const wchar_t *start; + const wchar_t *p; + TranslateTable *table; + size_t i; + size_t len; + size_t extra; + size_t bytes; + size_t amount; + wchar_t from; + + assert (pstr); + assert (output_format != OUTPUT_FORMAT_TEXT); + assert (output_format != OUTPUT_FORMAT_CRUMB); + + /* Find the correct translation table for the chosen output format */ + for (i = 0; i < sizeof (translate_table) / sizeof (translate_table[0]); i++) { + table = &translate_table[i]; + if (table && table->output_format == output_format) + break; + } + + if (! table) + return NULL; + + len = pstr->len; + start = pstr->buf; + + /* First, calculate the amount of space needed for the expanded + * buffer. + */ + extra = 0; + while (start && *start) { + for (i = 0; i < TRANSLATE_MAP_ENTRIES; i++) { + from = table->map[i].from; + if (*start == from) { + /* Subtract one to take account of the + * pre-existing character we're going to + * replace. + */ + extra += (wcslen (table->map[i].to) - 1); + } + } + start++; + } + + if (! extra) { + /* No translation required. + * + * FIXME: this is inefficient - we should really have + * the function accept a 'pstring **' to avoid + * re-copying. + */ + return pstring_create (pstr->buf); + } + + len += extra; + + result = pstring_new (); + if (! result) + return NULL; + + /* Note that this includes the space for the terminator + * (since a pstring's len includes the terminator) + */ + bytes = len * sizeof (wchar_t); + + result->buf = malloc (bytes); + if (! result->buf) { + pstring_free (result); + return NULL; + } + + /* We're using wcsncat() so we'd better make sure there is a + * nul for it to find! + * + * Note: we could have used calloc to do this for us, but + * the code is clearer using the @bytes idiom. + */ + memset (result->buf, '\0', bytes); + + result->size = bytes; + + /* Sanity check for upcoming overrun check */ + assert (result->buf[len-1] == L'\0'); + + /* Now, iterate the string again, performing the actual + * replacements. + */ + p = start = pstr->buf; + + while (p && *p) { + for (i = 0; i < TRANSLATE_MAP_ENTRIES; i++) { + wchar_t *to; + size_t len; + + from = table->map[i].from; + + if (*p != from) + continue; + + to = table->map[i].to; + + amount = p - start; + + /* Copy from start to match */ + wcsncat (result->buf + result->len, start, amount); + + result->len += amount; + + /* Copy replacement text */ + len = wcslen (to); + wcsncat (result->buf + result->len, to, len); + result->len += len; + + /* Jump over the matching character */ + start = p + 1; + + break; + } + p++; + } + + /* Copy remaining non-matching chars */ + amount = p - start; + wcsncat (result->buf + result->len, start, amount); + result->len += amount; + + /* Account for terminator */ + result->len += 1; + + /* check for buffer overrun */ + assert (result->buf[len-1] == L'\0'); + + return result; +} +/** + * change_element: + * + * Handle changing to a new element type. Depending on the output + * format, this may require separators and newlines to be emitted to + * produce well-formatted output. + **/ +void +change_element (ElementType new) +{ + common_assert (); + + last_element = current_element; + + current_element = new; + + format_element (); +} + +void +format_element (void) +{ + switch (output_format) { + + case OUTPUT_FORMAT_TEXT: + format_text_element (); + break; + + case OUTPUT_FORMAT_CRUMB: + /* NOP */ + break; + + case OUTPUT_FORMAT_JSON: + format_json_element (); + break; + + case OUTPUT_FORMAT_XML: + format_xml_element (); + break; + + default: + assert_not_reached (); + break; + } +} + +void +format_text_element (void) +{ + common_assert (); + switch (last_element) { + + case ELEMENT_TYPE_ENTRY: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_SECTION_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_SECTION_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_CONTAINER_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_NONE: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_OPEN: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + break; + + default: + assert_not_reached (); + break; + } + } + break; + + default: + assert_not_reached (); + break; + } +} + +void +format_json_element (void) +{ + common_assert (); + + switch (last_element) { + + case ELEMENT_TYPE_ENTRY: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L",\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_SECTION_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + case ELEMENT_TYPE_OBJECT_OPEN: + /* NOP */ + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + assert_not_reached (); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L",\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_OPEN: + /* NOP */ + break; + + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_CONTAINER_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_OBJECT_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + case ELEMENT_TYPE_SECTION_CLOSE: + assert_not_reached (); + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_OBJECT_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L",\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_OBJECT_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_CONTAINER_CLOSE: + case ELEMENT_TYPE_SECTION_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_OPEN: + case ELEMENT_TYPE_SECTION_OPEN: + wappend (&doc, L",\n"); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_NONE: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_OBJECT_OPEN: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + default: + assert_not_reached (); + break; + } +} + +void +format_xml_element (void) +{ + common_assert (); + + switch (last_element) { + + case ELEMENT_TYPE_ENTRY: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_OBJECT_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_SECTION_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + assert_not_reached (); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_CONTAINER_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_SECTION_CLOSE: + assert_not_reached (); + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_OPEN: + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + wappend (&doc, L"\n"); + dec_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_OBJECT_OPEN: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_CONTAINER_OPEN: + wappend (&doc, L"\n"); + inc_indent (); + add_indent (&doc); + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + /* NOP */ + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_OBJECT_CLOSE: + { + switch (current_element) { + case ELEMENT_TYPE_OBJECT_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_CLOSE: + /* NOP */ + break; + + case ELEMENT_TYPE_CONTAINER_CLOSE: + case ELEMENT_TYPE_ENTRY: + wappend (&doc, L"\n"); + dec_indent (); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + case ELEMENT_TYPE_NONE: + { + switch (current_element) { + case ELEMENT_TYPE_ENTRY: /* FALL */ + case ELEMENT_TYPE_CONTAINER_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_OPEN: /* FALL */ + case ELEMENT_TYPE_SECTION_CLOSE: /* FALL */ + case ELEMENT_TYPE_CONTAINER_CLOSE: /* FALL */ + case ELEMENT_TYPE_OBJECT_OPEN: /* FALL */ + case ELEMENT_TYPE_OBJECT_CLOSE: + add_indent (&doc); + break; + + default: + assert_not_reached (); + break; + } + } + break; + + default: + assert_not_reached (); + break; + } +} + +/** + * compress: + * + * Remove lines composed entirely of whitespace from @str. + * + * This is required specifically for '--output=text' which in some + * scenarios generates lines comprising pure whitespace. This is + * unecessary and results from the fact that when an + * ELEMENT_TYPE_OBJECT_* is encountered, formatting is applied for the + * previously seen element, but sometimes such "objects" should be + * invisible. + **/ +void +compress (pstring **wstr, wchar_t remove_char) +{ + wchar_t *from; + wchar_t *to; + wchar_t *p; + wchar_t *start; + size_t count = 0; + size_t blanks = 0; + size_t new_len; + size_t bytes; + + assert (wstr); + + to = from = (*wstr)->buf; + + while (to && *to) { +again: + while (*to == L'\n' && *(to+1) == L'\n') { + /* skip over blank lines */ + to++; + blanks++; + } + + start = to; + + while (*to == remove_char) { + /* skip runs of contiguous characters */ + to++; + count++; + } + + if (to != start) { + /* Only start consuming NLs at the end of a + * contiguous run *iff* there was more than a + * single removed char. This is a heuristic to + * avoid removing valid entries for example env + * vars that are set to nul are shown as: + * + * 'var: ' + * + * Shudder. + */ + if (*to == L'\n' && to != start+1) { + while (*to == L'\n') { + /* consume the NL at the end of the contiguous run */ + to++; + } + + /* check to ensure that we haven't entered a new line + * containing another block of chars to remove. + */ + if (*to == remove_char) + goto again; + + blanks++; + + } else { + /* not a full line so backtrack */ + to = start; + count = 0; + } + } + + *from++ = *to++; + } + + /* terminate */ + *from = L'\0'; + + if (blanks || count) { + new_len = (*wstr)->len - (blanks + count); + + bytes = new_len * sizeof (wchar_t); + + p = realloc ((*wstr)->buf, bytes); + assert (p); + + (*wstr)->buf = p; + + (*wstr)->buf[new_len-1] = L'\0'; + + (*wstr)->len = new_len; + (*wstr)->size = bytes; + } +} +/** + * chomp: + * + * Remove trailing extraneous newlines and indent_chars from @str. + **/ +void +chomp (pstring *str) +{ + size_t len; + int removable = 0; + wchar_t *p; + + assert (str); + + /* Unable to add '\n' in this scenario */ + if (str->len < 2) + return; + + for (p = str->buf+str->len-1; *p == L'\n' || *p == wide_indent_char; + p--, removable++) + ; + + /* Chop string at the appropriate place after first adding a new + * newline. + */ + if (removable > 1) { + len = str->len - (removable-1); + str->buf[len-1] = L'\n'; + str->buf[len] = L'\0'; + str->len = len; + } +} + +void +show_version (void) +{ + common_assert (); + + header ("version"); + + entry (_("name"), "%s", PACKAGE_NAME); + entry (_("version"), "%s", PACKAGE_VERSION); + entry (_("author"), "%s", PROGRAM_AUTHORS); + + footer (); +} + +void +show_shared_mem (void) +{ +#if defined (PROCENV_LINUX) || defined (PROCENV_HURD) + show_shared_mem_linux (); +#else + show_shared_mem_stub (); +#endif +} + +void +show_semaphores (void) +{ +#if defined (PROCENV_LINUX) || defined (PROCENV_HURD) + show_semaphores_linux (); +#else + show_semaphores_stub (); +#endif +} + +void +show_msg_queues (void) +{ +#if defined (PROCENV_LINUX) || defined (PROCENV_HURD) + show_msg_queues_linux (); +#else + show_msg_queues_stub (); +#endif +} + + +#if defined (PROCENV_LINUX) || defined (PROCENV_HURD) +void +show_shared_mem_linux (void) +{ + int i; + int id; + int max; + struct shm_info info; + struct shmid_ds shmid_ds; + struct ipc_perm *perm; + char formatted_atime[CTIME_BUFFER]; + char formatted_ctime[CTIME_BUFFER]; + char formatted_dtime[CTIME_BUFFER]; + char *modestr = NULL; + int locked = -1; + int destroy = -1; + char *cpid = NULL; + char *lpid = NULL; + + header ("shared memory"); + + max = shmctl (0, SHM_INFO, (void *)&info); + if (max < 0) + goto out; + + /* Display summary details */ + + section_open ("info"); + + entry ("segments", "%u", info.used_ids); + entry ("pages", "%lu", info.shm_tot); + entry ("shm_rss", "%lu", info.shm_rss); + entry ("shm_swp", "%lu", info.shm_swp); + + /* Apparently unused */ + entry ("swap_attempts", "%lu", info.swap_attempts); + entry ("swap_successes", "%lu", info.swap_successes); + + section_close (); + + container_open ("segments"); + + object_open (FALSE); + + for (i = 0; i <= max; i++) { + char *id_str = NULL; + + id = shmctl (i, SHM_STAT, &shmid_ds); + if (id < 0) { + /* found an unused slot, so ignore it */ + continue; + } + + perm = &shmid_ds.shm_perm; + + modestr = format_perms (perm->mode); + if (! modestr) + die ("failed to allocate space for permissions string"); + +#ifdef PROCENV_LINUX + locked = (perm->mode & SHM_LOCKED); + destroy = (perm->mode & SHM_DEST); +#endif + + format_time (&shmid_ds.shm_atime, formatted_atime, sizeof (formatted_atime)); + format_time (&shmid_ds.shm_ctime, formatted_ctime, sizeof (formatted_ctime)); + format_time (&shmid_ds.shm_dtime, formatted_dtime, sizeof (formatted_dtime)); + + cpid = pid_to_name (shmid_ds.shm_cpid); + lpid = pid_to_name (shmid_ds.shm_lpid); + + appendf (&id_str, "%d", id); + + container_open (id_str); + free (id_str); + + object_open (FALSE); + + /* pad out to max pointer size represented in hex. + */ + entry ("key", "0x%.*x", POINTER_SIZE * 2, perm->__key); + entry ("sequence", "%u", perm->__seq); + + section_open ("permissions"); + entry ("octal", "%4.4o", perm->mode); + entry ("symbolic", "%s", modestr); + section_close (); + + section_open ("pids"); + entry ("create", "%d (%s)", shmid_ds.shm_cpid, cpid ? cpid : UNKNOWN_STR); + entry ("last", "%d (%s)", shmid_ds.shm_lpid, lpid ? lpid : UNKNOWN_STR); + section_close (); + + entry ("attachers", "%lu", shmid_ds.shm_nattch); + + section_open ("creator"); + entry ("euid", "%u ('%s')", perm->cuid, get_user_name (perm->cuid)); + entry ("egid", "%u ('%s')", perm->cgid, get_group_name (perm->cgid)); + section_close (); + + section_open ("owner"); + entry ("uid", "%u ('%s')", perm->uid, get_user_name (perm->uid)); + entry ("gid", "%u ('%s')", perm->gid, get_group_name (perm->gid)); + section_close (); + + entry ("segment size", "%lu", shmid_ds.shm_segsz); + + section_open ("times"); + entry ("last attach (atime)", "%lu (%s)", shmid_ds.shm_atime, formatted_atime); + entry ("last detach (dtime)", "%lu (%s)", shmid_ds.shm_dtime, formatted_dtime); + entry ("last change (ctime)", "%lu (%s)", shmid_ds.shm_ctime, formatted_ctime); + section_close (); + + entry ("locked", "%s", locked == 0 ? NO_STR + : locked > 0 ? YES_STR + : NA_STR); + entry ("destroy", "%s", destroy == 0 ? NO_STR + : destroy > 0 ? YES_STR + : NA_STR); + + object_close (FALSE); + + container_close (); + + free (modestr); + if (cpid) + free (cpid); + if (lpid) + free (lpid); + } + + object_close (FALSE); + + container_close (); + +out: + footer (); +} + +void +show_semaphores_linux (void) +{ + int i; + int id; + int max; + struct semid_ds semid_ds; + struct seminfo info; + struct ipc_perm *perm; + char formatted_otime[CTIME_BUFFER]; + char formatted_ctime[CTIME_BUFFER]; + char *modestr = NULL; + union semun arg; + + header ("semaphores"); + + arg.array = (unsigned short int *)(void *)&info; + max = semctl (0, 0, SEM_INFO, arg); + if (max < 0) + goto out; + + section_open ("info"); + + entry ("semmap", "%d", info.semmap); + entry ("semmni", "%d", info.semmni); + entry ("semmns", "%d", info.semmns); + entry ("semmnu", "%d", info.semmnu); + entry ("semmsl", "%d", info.semmsl); + entry ("semopm", "%d", info.semopm); + entry ("semume", "%d", info.semume); + entry ("semusz", "%d", info.semusz); + entry ("semvmx", "%d", info.semvmx); + entry ("semaem", "%d", info.semaem); + + section_close (); + + container_open ("set"); + + object_open (FALSE); + + for (i = 0; i <= max; i++) { + char *id_str = NULL; + + /* see semctl(2) */ + arg.buf = (struct semid_ds *)&semid_ds; + + id = semctl (i, 0, SEM_STAT, arg); + if (id < 0) { + /* found an unused slot, so ignore it */ + continue; + } + + perm = &semid_ds.sem_perm; + + modestr = format_perms (perm->mode); + if (! modestr) + die ("failed to allocate space for permissions string"); + + /* May not have been set */ + if (semid_ds.sem_otime) + format_time (&semid_ds.sem_otime, formatted_otime, sizeof (formatted_otime)); + else + sprintf (formatted_otime, "%s", NA_STR); + + format_time (&semid_ds.sem_ctime, formatted_ctime, sizeof (formatted_ctime)); + + appendf (&id_str, "%d", id); + + container_open (id_str); + free (id_str); + + object_open (FALSE); + + /* pad out to max pointer size represented in hex. + */ + entry ("key", "0x%.*x", POINTER_SIZE * 2, perm->__key); + entry ("sequence", "%u", perm->__seq); + + entry ("number in set", "%lu", semid_ds.sem_nsems); + + section_open ("permissions"); + entry ("octal", "%4.4o", perm->mode); + entry ("symbolic", "%s", modestr); + free (modestr); + section_close (); + + section_open ("creator"); + entry ("euid", "%u ('%s')", perm->cuid, get_user_name (perm->cuid)); + entry ("egid", "%u ('%s')", perm->cgid, get_group_name (perm->cgid)); + section_close (); + + section_open ("owner"); + entry ("uid", "%u ('%s')", perm->uid, get_user_name (perm->uid)); + entry ("gid", "%u ('%s')", perm->gid, get_group_name (perm->gid)); + section_close (); + + section_open ("times"); + entry ("last semop (otime)", "%lu (%s)", semid_ds.sem_otime, formatted_otime); + entry ("last change (ctime)", "%lu (%s)", semid_ds.sem_ctime, formatted_ctime); + section_close (); + + object_close (FALSE); + + container_close (); + } + + object_close (FALSE); + + container_close (); + +out: + footer (); +} + +void +show_msg_queues_linux (void) +{ + int i; + int id; + int max; + struct msginfo info; + struct msqid_ds msqid_ds; + struct ipc_perm *perm; + char formatted_stime[CTIME_BUFFER]; + char formatted_rtime[CTIME_BUFFER]; + char formatted_ctime[CTIME_BUFFER]; + char *modestr = NULL; + char *lspid = NULL; + char *lrpid = NULL; + + header ("message queues"); + + max = msgctl (0, MSG_INFO, (void *)&info); + if (max < 0) + goto out; + + section_open ("info"); + + entry ("msgpool", "%d", info.msgpool); + entry ("msgmap", "%d", info.msgmap); + entry ("msgmax", "%d", info.msgmax); + entry ("msgmnb", "%d", info.msgmnb); + entry ("msgmni", "%d", info.msgmni); + entry ("msgssz", "%d", info.msgssz); + entry ("msgtql", "%d", info.msgtql); + entry ("msgseg", "%d", info.msgseg); + + section_close (); + + container_open ("sets"); + + object_open (FALSE); + + for (i = 0; i <= max; i++) { + char *id_str = NULL; + + id = msgctl (i, MSG_STAT, &msqid_ds); + if (id < 0) { + /* found an unused slot, so ignore it */ + continue; + } + + perm = &msqid_ds.msg_perm; + + modestr = format_perms (perm->mode); + if (! modestr) + die ("failed to allocate space for permissions string"); + + /* May not have been set */ + if (msqid_ds.msg_stime) + format_time (&msqid_ds.msg_stime, formatted_stime, sizeof (formatted_stime)); + else + sprintf (formatted_stime, "%s", NA_STR); + + /* May not have been set */ + if (msqid_ds.msg_rtime) + format_time (&msqid_ds.msg_rtime, formatted_rtime, sizeof (formatted_rtime)); + else + sprintf (formatted_rtime, "%s", NA_STR); + + /* May not have been set */ + if (msqid_ds.msg_ctime) + format_time (&msqid_ds.msg_ctime, formatted_ctime, sizeof (formatted_ctime)); + else + sprintf (formatted_ctime, "%s", NA_STR); + + lspid = pid_to_name (msqid_ds.msg_lspid); + lrpid = pid_to_name (msqid_ds.msg_lrpid); + + appendf (&id_str, "%d", id); + + container_open (id_str); + free (id_str); + + object_open (FALSE); + + /* pad out to max pointer size represented in hex */ + entry ("key", "0x%.*x", POINTER_SIZE * 2, perm->__key); + entry ("sequence", "%u", perm->__seq); + + section_open ("permissions"); + entry ("octal", "%4.4o", perm->mode); + entry ("symbolic", "%s", modestr); + section_close (); + + section_open ("creator"); + entry ("euid", "%u ('%s')", perm->cuid, get_user_name (perm->cuid)); + entry ("egid", "%u ('%s')", perm->cgid, get_group_name (perm->cgid)); + section_close (); + + section_open ("owner"); + entry ("uid", "%u ('%s')", perm->uid, get_user_name (perm->uid)); + entry ("gid", "%u ('%s')", perm->gid, get_group_name (perm->gid)); + section_close (); + + section_open ("times"); + entry ("last send (stime)", "%lu (%s)", msqid_ds.msg_stime, formatted_stime); + entry ("last receive (rtime)", "%lu (%s)", msqid_ds.msg_rtime, formatted_rtime); + entry ("last change (ctime)", "%lu (%s)", msqid_ds.msg_ctime, formatted_ctime); + section_close (); + + entry ("queue_bytes", "%lu", msqid_ds.__msg_cbytes); + + entry ("msg_qnum", "%lu", msqid_ds.msg_qnum); + entry ("msg_qbytes", "%lu", msqid_ds.msg_qbytes); + + entry ("last msgsnd pid", "%d (%s)", msqid_ds.msg_lspid, + lspid ? lspid : UNKNOWN_STR); + + entry ("last msgrcv pid", "%d (%s)", msqid_ds.msg_lrpid, + lrpid ? lrpid : UNKNOWN_STR); + + object_close (FALSE); + + container_close (); + + free (modestr); + if (lspid) + free (lspid); + if (lrpid) + free (lrpid); + } + + object_close (FALSE); + + container_close (); + +out: + footer (); +} +#endif /* PROCENV_LINUX || PROCENV_HURD */ + +void +format_time (const time_t *t, char *buffer, size_t len) +{ + char *str = NULL; + size_t l; + + assert (t); + assert (buffer); + + str = ctime (t); + if (! str) + die ("failed to format time"); + + l = strlen (str); + + if (len < l) + bug ("buffer too small"); + + /* Ensure nul byte copied */ + strncpy (buffer, str, l+1); + + /* Overwrite NL */ + buffer[strlen (buffer)-1] = '\0'; +} + +char * +format_perms (mode_t mode) +{ + char *modestr = NULL; + mode_t perms; + int i = 0; + + /* + * "-rwxrwxrwx" = 10+1 bytes. + */ + modestr = calloc ((1+3+3+3)+1, sizeof (char)); + + if (! modestr) + return NULL; + + modestr[i++] = (S_ISLNK (mode & S_IFMT)) ? 'l' : '-'; + + perms = (mode & S_IRWXU); + modestr[i++] = (perms & S_IRUSR) ? 'r' : '-'; + modestr[i++] = (perms & S_IWUSR) ? 'w' : '-'; + modestr[i++] = (perms & S_IXUSR) ? 'x' : '-'; + + perms = (mode & S_IRWXG); + modestr[i++] = (perms & S_IRGRP) ? 'r' : '-'; + modestr[i++] = (perms & S_IWGRP) ? 'w' : '-'; + modestr[i++] = (perms & S_IXGRP) ? 'x' : '-'; + + perms = (mode & S_IRWXO); + modestr[i++] = (perms & S_IROTH) ? 'r' : '-'; + modestr[i++] = (perms & S_IWOTH) ? 'w' : '-'; + modestr[i++] = (perms & S_IXOTH) ? 'x' : '-'; + + perms = (mode &= ~S_IFMT); + if (perms & S_ISUID) + modestr[3] = 's'; + if (perms & S_ISGID) + modestr[6] = 's'; + if (perms & S_ISVTX) + modestr[9] = 't'; + + return modestr; +} + +char * +pid_to_name (pid_t pid) +{ + char path[PATH_MAX]; + char *name = NULL; + FILE *f = NULL; + + sprintf (path, "/proc/%d/cmdline", (int)pid); + + f = fopen (path, "r"); + if (! f) + goto out; + + /* Reuse buffer */ + if (! fgets (path, sizeof (path), f)) + goto out; + + /* Nul delimiting within /proc file will ensure we only get the + * program name. + */ + append (&name, path); + +out: + if (f) + fclose (f); + + return name; +} + + +void +add_breadcrumb (const char *name) +{ + assert (name); + + if (! crumb_list) + crumb_list = pr_list_new (NULL); + + assert (crumb_list); + + pr_list_prepend_str (crumb_list, name); +} + +void +remove_breadcrumb (void) +{ + PRList *entry; + + assert (crumb_list); + + entry = pr_list_remove (crumb_list->prev); + assert (entry); + + free ((char *)entry->data); + free (entry); +} + +void +clear_breadcrumbs (void) +{ + assert (crumb_list); + + while (crumb_list->prev != crumb_list) + remove_breadcrumb (); +} + +wchar_t * +char_to_wchar (const char *str) +{ + const char *p; + wchar_t *wstr = NULL; + size_t len; + size_t bytes; + + assert (str); + + len = mbsrtowcs (NULL, &str, 0, NULL); + if (len <= 0) + return NULL; + + /* include space for terminator */ + bytes = (1 + len) * sizeof (wchar_t); + + wstr = malloc (bytes); + if (! wstr) + return NULL; + + p = str; + + if (mbsrtowcs (wstr, &p, len, NULL) != len) + goto error; + + /* ensure it's terminated */ + wstr[len] = L'\0'; + + return wstr; + +error: + free (wstr); + return NULL; +} + +pstring * +pstring_new (void) +{ + pstring *pstr = NULL; + + pstr = calloc (1, sizeof (pstring)); + if (! pstr) + return NULL; + + pstr->len = 0; + pstr->size = 0; + pstr->buf = NULL; + + return pstr; +} + +pstring * +pstring_create (const wchar_t *str) +{ + pstring *pstr = NULL; + + assert (str); + + pstr = pstring_new (); + + if (! pstr) + return NULL; + + pstr->buf = wcsdup (str); + if (! pstr->buf) { + pstring_free (pstr); + return NULL; + } + + /* include the L'\0' terminator */ + pstr->len = 1 + wcslen (pstr->buf); + + pstr->size = pstr->len * sizeof (wchar_t); + + return pstr; +} + +void +pstring_free (pstring *str) +{ + assert (str); + + if (str->buf) + free (str->buf); + + free (str); +} + +pstring * +char_to_pstring (const char *str) +{ + pstring *pstr = NULL; + wchar_t *s; + + assert (str); + + s = char_to_wchar (str); + if (! s) + return NULL; + + pstr = pstring_create (s); + + free (s); + + return pstr; +} + +char * +pstring_to_char (const pstring *str) +{ + assert (str); + + return wchar_to_char (str->buf); +} + +char * +wchar_to_char (const wchar_t *wstr) +{ + char *str = NULL; + size_t len; + size_t bytes; + size_t ret; + + assert (wstr); + + len = wcslen (wstr); + + /* determine number of MBS (char) bytes requires to hold the + * wchar_t string. + */ + bytes = wcstombs (NULL, wstr, len); + if (! bytes) + return NULL; + + str = calloc (bytes + 1, sizeof (char)); + if (! str) + return NULL; + + /* actually perform the conversion */ + ret = wcstombs (str, wstr, bytes); + + if (! ret) + goto error; + + return str; + +error: + free (str); + return NULL; +} + +#if ! defined (HAVE_SCHED_GETCPU) + +/** + * @string: input, + * @delimiter: field delimiter, + * @compress: if TRUE, ignore repeated contiguous delimiter characters, + * @array: [output] array of fields, which this function will allocate. + * + * Notes: it is the callers responsibility to free @array + * if the returned value is >0. + * + * Returns: number of fields in @string. + **/ +size_t +split_fields (const char *string, char delimiter, int compress, char ***array) +{ + const char *p = NULL; + const char *start = NULL; + size_t count = 0; + char *elem; + char **new; + + assert (string); + assert (delimiter); + assert (array); + + *array = NULL; + + new = realloc ((*array), sizeof (char *) * (1+count)); + assert (new); + + new[0] = NULL; + *array = new; + + p = string; + + while (p && *p) { + /* skip leading prefix */ + while (compress && p && *p == delimiter) + p++; + + if (! *p) + break; + + /* found a field */ + count++; + + if (! compress) + p++; + + /* skip over the field */ + start = p; + while (p && *p && *p != delimiter) + p++; + + elem = strndup (start, p-start); + assert (elem); + + new = realloc ((*array), sizeof (char *) * (1+count)); + assert (new); + + new[count-1] = elem; + *array = new; + } + + return count; +} + +#endif |