feat(misc): uname/gethostname/sysconf/pathconf
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
#ifndef VLIBC_SYS_UTSNAME_H
|
||||
#define VLIBC_SYS_UTSNAME_H
|
||||
|
||||
/*
|
||||
* vlibc — <sys/utsname.h>.
|
||||
*
|
||||
* System identification: uname() and struct utsname. uname is XSI [CX] in
|
||||
* POSIX.1-2008 (glibc gates it behind _XOPEN_SOURCE, not _POSIX_C_SOURCE),
|
||||
* so the whole surface sits at level 2 — nothing here is POSIX.1-2008 base.
|
||||
*
|
||||
* Level 2 (muslmimic): uname, struct utsname.
|
||||
*
|
||||
* struct utsname mirrors the x86_64 kernel's struct new_utsname exactly: six
|
||||
* NUL-terminated 65-byte character arrays (the kernel's _UTSNAME_LENGTH, 64
|
||||
* usable characters plus the terminator). The kernel's uname syscall copies
|
||||
* all 390 bytes, so the layout is an ABI fact, not a style choice.
|
||||
* domainname is a Linux extension (the kernel's uts namespace domain name)
|
||||
* and is not part of the POSIX struct; it is included for glibc
|
||||
* compatibility — POSIX only requires the first five fields.
|
||||
*
|
||||
* The declaration carries no intent attribute: uname performs I/O with side
|
||||
* effects and reports failures through errno, so const/pure would be
|
||||
* unsound (see the rationale in <unistd.h>).
|
||||
*/
|
||||
|
||||
#include <vlibc/features.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* The system identification strings. Each field is NUL-terminated; the
|
||||
* 65-byte width matches the kernel buffer (64 characters plus the NUL).
|
||||
* sysname is the operating system name ("Linux"), nodename the host name,
|
||||
* release the kernel release, version the kernel build, machine the
|
||||
* hardware identifier ("x86_64"), and domainname the NIS/UTS domain name.
|
||||
*/
|
||||
struct utsname
|
||||
{
|
||||
char sysname[65]; /* operating system name */
|
||||
char nodename[65]; /* name within the communications network */
|
||||
char release[65]; /* operating system release level */
|
||||
char version[65]; /* operating system version */
|
||||
char machine[65]; /* hardware type */
|
||||
char domainname[65]; /* NIS/UTS domain name (Linux extension) */
|
||||
};
|
||||
|
||||
/*
|
||||
* Fill the buffer with the current system identification strings; return
|
||||
* 0, or -1 with errno set when the buffer points outside the address
|
||||
* space. The strings are copies from the kernel's UTS namespace.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf);
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* VLIBC_SYS_UTSNAME_H */
|
||||
@@ -24,6 +24,17 @@
|
||||
* Level 2 (muslmimic): vfork, setpgrp (obsolescent), setgroups, getpgid,
|
||||
* getsid, tcgetpgrp, tcsetpgrp (XSI).
|
||||
*
|
||||
* System limits and options (todo 32) are declared after the process
|
||||
* block:
|
||||
*
|
||||
* Level 1 (onlyposix): sysconf, pathconf, fpathconf, the _SC_* and _PC_*
|
||||
* configuration keys.
|
||||
* Level 2 (muslmimic): gethostname, sethostname, gethostid (XSI/BSD
|
||||
* system identification — NOT POSIX.1-2008 base),
|
||||
* and the _CS_* keys for todo 38's confstr().
|
||||
*
|
||||
* uname() and struct utsname live in <sys/utsname.h> (todo 32).
|
||||
*
|
||||
* The open-flag constants (O_RDONLY, O_CREAT, O_CLOEXEC, ...) belong to
|
||||
* <fcntl.h> and are deliberately not defined here; the oflag arguments below
|
||||
* are plain int and take their values from that header. The optional mode
|
||||
@@ -297,6 +308,172 @@ getpgrp(void);
|
||||
pid_t
|
||||
setsid(void);
|
||||
|
||||
/* Level 1 (POSIX base): system limits and options. */
|
||||
|
||||
/*
|
||||
* The configuration keys for sysconf(), pathconf() and fpathconf(), and
|
||||
* (level 2) the _CS_* keys for confstr() (todo 38).
|
||||
*
|
||||
* POSIX defines the _SC_* and _PC_* names and the SEMANTICS of the values
|
||||
* the configuration functions return for them, but NOT their numeric
|
||||
* values: those are private to each C library (glibc numbers them
|
||||
* arbitrarily in <bits/confname.h>, musl in a private table). The values
|
||||
* below are vlibc's OWN stable ABI, assigned in declaration order and never
|
||||
* reused — program source must pass the symbolic constants, never literal
|
||||
* integers. They are deliberately NOT glibc's numbers. Any value outside
|
||||
* these enums is unknown to the configuration functions and yields -1
|
||||
* without errno (the unsupported-key convention, see below).
|
||||
*/
|
||||
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _SC_/_PC_ names */
|
||||
enum
|
||||
{
|
||||
/* Process and file limits (sysconf). */
|
||||
_SC_ARG_MAX = 0, /* max bytes of arguments + environment for exec */
|
||||
_SC_CHILD_MAX, /* max simultaneous processes per real user id */
|
||||
_SC_CLK_TCK, /* clock ticks per second, the times() unit (100) */
|
||||
_SC_NGROUPS_MAX, /* max supplementary group ids */
|
||||
_SC_OPEN_MAX, /* max open file descriptors per process */
|
||||
_SC_PAGESIZE, /* memory page size in bytes (from AT_PAGESZ) */
|
||||
_SC_PAGE_SIZE, /* POSIX synonym of _SC_PAGESIZE */
|
||||
_SC_NPROCESSORS_CONF, /* number of processors configured */
|
||||
_SC_NPROCESSORS_ONLN, /* number of processors online */
|
||||
_SC_PHYS_PAGES, /* number of physical memory pages */
|
||||
_SC_AVPHYS_PAGES, /* number of currently available memory pages */
|
||||
_SC_SYMLOOP_MAX, /* max symlink traversals while resolving a path */
|
||||
_SC_STREAM_MAX, /* max open standard I/O streams (FOPEN_MAX) */
|
||||
_SC_TZNAME_MAX, /* max bytes in a timezone name */
|
||||
_SC_VERSION, /* POSIX.1 version, 200809L */
|
||||
_SC_2_VERSION, /* POSIX.2 version, 200809L */
|
||||
_SC_JOB_CONTROL, /* 1 if job control is supported */
|
||||
_SC_SAVED_IDS, /* 1 if saved set-user/group-id is supported */
|
||||
_SC_HOST_NAME_MAX, /* max bytes in a host name (excluding NUL) */
|
||||
_SC_LOGIN_NAME_MAX, /* max bytes in a login name */
|
||||
_SC_GETPW_R_SIZE_MAX, /* recommended buffer size for getpw*_r */
|
||||
_SC_IOV_MAX, /* max iovec entries in readv/writev */
|
||||
_SC_LINE_MAX, /* max bytes in an input line (POSIX.2) */
|
||||
_SC_RE_DUP_MAX, /* max duplicate regexp interval counts */
|
||||
_SC_COLL_WEIGHTS_MAX, /* max weights for collating elements */
|
||||
_SC_EXPR_NEST_MAX, /* max expression nesting in expr(1) */
|
||||
|
||||
/* POSIX option indicators (sysconf). Values follow the _POSIX_*
|
||||
* convention: a version value means supported, -1 unsupported. */
|
||||
_SC_THREADS, /* _POSIX_THREADS */
|
||||
_SC_SEMAPHORES, /* _POSIX_SEMAPHORES */
|
||||
_SC_SHARED_MEMORY_OBJECTS, /* _POSIX_SHARED_MEMORY_OBJECTS */
|
||||
_SC_MESSAGE_PASSING, /* _POSIX_MESSAGE_PASSING */
|
||||
_SC_MONOTONIC_CLOCK, /* _POSIX_MONOTONIC_CLOCK */
|
||||
_SC_TIMERS, /* _POSIX_TIMERS */
|
||||
_SC_SYNCHRONIZED_IO, /* _POSIX_SYNCHRONIZED_IO */
|
||||
_SC_THREAD_SAFE_FUNCTIONS, /* _POSIX_THREAD_SAFE_FUNCTIONS */
|
||||
_SC_THREAD_ATTR_STACKADDR, /* _POSIX_THREAD_ATTR_STACKADDR */
|
||||
_SC_THREAD_ATTR_STACKSIZE, /* _POSIX_THREAD_ATTR_STACKSIZE */
|
||||
_SC_THREAD_PRIORITY_SCHEDULING, /* _POSIX_THREAD_PRIORITY_SCHEDULING */
|
||||
_SC_THREAD_PRIO_INHERIT, /* _POSIX_THREAD_PRIO_INHERIT */
|
||||
_SC_THREAD_PRIO_PROTECT, /* _POSIX_THREAD_PRIO_PROTECT */
|
||||
_SC_THREAD_PROCESS_SHARED, /* _POSIX_THREAD_PROCESS_SHARED */
|
||||
_SC_REALTIME_SIGNALS, /* _POSIX_REALTIME_SIGNALS */
|
||||
|
||||
/* POSIX.1-2008 option indicators that use plain 1 for supported. */
|
||||
_SC_REGEXP, /* 1 if the RE functions are supported */
|
||||
_SC_SHELL, /* 1 if the shell is supported */
|
||||
_SC_XOPEN_VERSION, /* XSI version, 700 */
|
||||
_SC_XOPEN_UNIX, /* 1 if XSI interfaces are supported */
|
||||
_SC_XOPEN_CRYPT, /* 1 if the crypt function is available */
|
||||
_SC_XOPEN_ENH_I18N, /* 1 if enhanced internationalization is present */
|
||||
_SC_XOPEN_SHM, /* 1 if XSI shared memory is present */
|
||||
_SC_2_C_BIND, /* POSIX.2 C-language binding */
|
||||
_SC_2_C_DEV, /* POSIX.2 C-language development utilities */
|
||||
_SC_2_FORT_DEV, /* POSIX.2 FORTRAN development utilities */
|
||||
_SC_2_FORT_RUN, /* POSIX.2 FORTRAN runtime utilities */
|
||||
_SC_2_LOCALEDEF, /* POSIX.2 locale creation utilities */
|
||||
_SC_2_SW_DEV, /* POSIX.2 software development utilities */
|
||||
_SC_2_UPE /* POSIX.2 user-portability utilities */
|
||||
};
|
||||
|
||||
/*
|
||||
* The _PC_* keys for pathconf() and fpathconf(). Each names a limit or an
|
||||
* option of the FILESYSTEM holding the queried path or descriptor; see the
|
||||
* pathconf() documentation below for the supported keys and their values.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
_PC_LINK_MAX = 0, /* max link count of a file */
|
||||
_PC_MAX_CANON, /* max bytes in a terminal canonical input line */
|
||||
_PC_MAX_INPUT, /* max bytes available on a terminal input queue */
|
||||
_PC_NAME_MAX, /* max bytes in a file name (excluding NUL) */
|
||||
_PC_PATH_MAX, /* max bytes in a pathname (excluding NUL) */
|
||||
_PC_PIPE_BUF, /* max bytes atomically writable to a pipe */
|
||||
_PC_CHOWN_RESTRICTED, /* 1 if chown is restricted to the owner */
|
||||
_PC_NO_TRUNC, /* 1 if overlong file names are an error */
|
||||
_PC_VDISABLE, /* the terminal disable character (0 = none) */
|
||||
_PC_ASYNC_IO, /* 200809L if asynchronous I/O is supported */
|
||||
_PC_SYNC_IO, /* 200809L if synchronized I/O is supported */
|
||||
_PC_PRIO_IO, /* 200809L if prioritized I/O is supported */
|
||||
_PC_SOCK_MAXBUF, /* max bytes storable in a socket buffer */
|
||||
_PC_FILESIZEBITS, /* bits in a file size field */
|
||||
_PC_REC_INCR_XFER_SIZE, /* recommended increment for transfer sizes */
|
||||
_PC_REC_MAX_XFER_SIZE, /* recommended maximum transfer size */
|
||||
_PC_REC_MIN_XFER_SIZE, /* recommended minimum transfer size */
|
||||
_PC_REC_XFER_ALIGN, /* recommended transfer alignment */
|
||||
_PC_ALLOC_SIZE_MIN, /* minimum bytes of a file allocation */
|
||||
_PC_SYMLINK_MAX, /* max bytes in a symbolic link target */
|
||||
_PC_2_SYMLINKS /* 1 if symlinks support the ".." resolution rule */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Query a system configuration value for key name (one of the _SC_* above)
|
||||
* and return it as a long. A negative value means the option is not
|
||||
* supported or the key is unknown; for unsupported options and for any key
|
||||
* outside the _SC_* enum, -1 is returned WITHOUT setting errno (callers
|
||||
* must not rely on errno after -1).
|
||||
*
|
||||
* The supported keys and their vlibc values: _SC_ARG_MAX (2097152),
|
||||
* _SC_CHILD_MAX (65535), _SC_CLK_TCK (100, the fixed x86_64 clock-tick
|
||||
* rate — see the note in src/misc/sysconf.c, todo 35's times() consumes
|
||||
* that same constant), _SC_NGROUPS_MAX (65536), _SC_OPEN_MAX (the current
|
||||
* RLIMIT_NOFILE soft limit, 1024 if it cannot be read), _SC_PAGESIZE and
|
||||
* _SC_PAGE_SIZE (the memory page size from the AT_PAGESZ auxiliary vector
|
||||
* entry, 4096 on x86_64), _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN
|
||||
* (the processor count), _SC_PHYS_PAGES and _SC_AVPHYS_PAGES (physical and
|
||||
* available memory page counts), _SC_SYMLOOP_MAX (40), _SC_STREAM_MAX
|
||||
* (16), _SC_TZNAME_MAX (6), _SC_VERSION and _SC_2_VERSION (200809L),
|
||||
* _SC_JOB_CONTROL, _SC_SAVED_IDS, _SC_REGEXP, _SC_SHELL (1 each),
|
||||
* _SC_HOST_NAME_MAX (64), _SC_LOGIN_NAME_MAX (256), _SC_GETPW_R_SIZE_MAX
|
||||
* (1024), _SC_IOV_MAX (1024), _SC_LINE_MAX (2048), _SC_RE_DUP_MAX (32767),
|
||||
* _SC_COLL_WEIGHTS_MAX (255), _SC_EXPR_NEST_MAX (32), _SC_XOPEN_VERSION
|
||||
* (700), _SC_XOPEN_UNIX, _SC_XOPEN_CRYPT, _SC_XOPEN_ENH_I18N, _SC_XOPEN_SHM
|
||||
* (1 each), and the thread/option version keys (_SC_THREADS ... _SC_2_UPE,
|
||||
* 200809L each).
|
||||
*/
|
||||
long
|
||||
sysconf(int name);
|
||||
|
||||
/*
|
||||
* Query a configurable limit or option of the filesystem holding path (the
|
||||
* _PC_* keys above) and return it as a long; -1 with errno set when path
|
||||
* cannot be examined. Fixed values: _PC_LINK_MAX (32000), _PC_MAX_CANON
|
||||
* and _PC_MAX_INPUT (255), _PC_PATH_MAX (4096), _PC_PIPE_BUF (4096),
|
||||
* _PC_CHOWN_RESTRICTED, _PC_NO_TRUNC and _PC_2_SYMLINKS (1), _PC_VDISABLE
|
||||
* (0), _PC_FILESIZEBITS (64), and _PC_REC_MIN_XFER_SIZE,
|
||||
* _PC_REC_XFER_ALIGN and _PC_ALLOC_SIZE_MIN (the filesystem block size).
|
||||
* _PC_NAME_MAX is the filesystem's f_namelen (255 on ext4/xfs). For keys
|
||||
* whose option the filesystem does not provide (_PC_ASYNC_IO, _PC_SYNC_IO,
|
||||
* _PC_PRIO_IO, _PC_SOCK_MAXBUF, _PC_REC_INCR_XFER_SIZE,
|
||||
* _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX) and for any key outside the
|
||||
* _PC_* enum, -1 is returned WITHOUT setting errno.
|
||||
*/
|
||||
long
|
||||
pathconf(const char *path, int name);
|
||||
|
||||
/*
|
||||
* Like pathconf(), but the filesystem is the one holding the open
|
||||
* descriptor fildes.
|
||||
*/
|
||||
long
|
||||
fpathconf(int fildes, int name);
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* Level 2 (muslmimic): Linux extensions + XSI + obsolescent. */
|
||||
|
||||
@@ -370,6 +547,58 @@ tcgetpgrp(int fildes);
|
||||
|
||||
int
|
||||
tcsetpgrp(int fildes, pid_t pgid);
|
||||
|
||||
/* Level 2 (muslmimic): system identification (XSI / BSD, not POSIX base). */
|
||||
|
||||
/*
|
||||
* The _CS_* keys for confstr() (todo 38, which owns the function and its
|
||||
* string results). Values are vlibc-local ABI like the _SC_ and _PC_ keys
|
||||
* above; they are defined here now so todo 38 only implements confstr()
|
||||
* itself.
|
||||
*/
|
||||
/* NOLINTBEGIN(bugprone-reserved-identifier) -- POSIX-mandated _CS_ names */
|
||||
enum
|
||||
{
|
||||
_CS_PATH = 0, /* value of PATH (always a usable default) */
|
||||
_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS, /* 1 if the V6 width-restricted envs exist */
|
||||
_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS, /* 1 if the V7 width-restricted envs exist */
|
||||
_CS_LFS_CFLAGS, /* cpp/cflags for large-file support */
|
||||
_CS_LFS_LDFLAGS, /* ld flags for large-file support */
|
||||
_CS_LFS_LIBS, /* libraries for large-file support */
|
||||
_CS_LFS_LINTFLAGS, /* lint flags for large-file support */
|
||||
_CS_LFS64_CFLAGS, /* cpp/cflags for large-file64 support */
|
||||
_CS_LFS64_LDFLAGS, /* ld flags for large-file64 support */
|
||||
_CS_LFS64_LIBS, /* libraries for large-file64 support */
|
||||
_CS_LFS64_LINTFLAGS, /* lint flags for large-file64 support */
|
||||
_CS_GNU_LIBC_VERSION, /* "glibc X.Y" (vlibc extension) */
|
||||
_CS_GNU_LIBPTHREAD_VERSION /* "NPTL X.Y" (vlibc extension) */
|
||||
};
|
||||
/* NOLINTEND(bugprone-reserved-identifier) */
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated host name of the current machine into name,
|
||||
* sized len bytes. Return 0 on success; if the name (including its NUL
|
||||
* terminator) does not fit in len bytes, nothing is written beyond the
|
||||
* fit and -1 is returned with errno set to ENAMETOOLONG. XSI.
|
||||
*/
|
||||
int
|
||||
gethostname(char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Set the kernel's host name from name (len bytes, no NUL required).
|
||||
* Requires privilege. Return 0, or -1 with errno set. BSD, not POSIX.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len);
|
||||
|
||||
/*
|
||||
* Return the 32-bit host identifier of the current machine. vlibc returns
|
||||
* 0 (musl's behavior): the historic value was derived from the host's
|
||||
* primary IP address via gethostbyname, which the modern library does not
|
||||
* perform. [OB] — obsolescent in POSIX.
|
||||
*/
|
||||
long
|
||||
gethostid(void);
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* pathconf() / fpathconf(): configurable limits and options of the
|
||||
* filesystem holding a path or an open descriptor.
|
||||
*
|
||||
* Both functions first validate the object with statfs/fstatfs (so a
|
||||
* missing path or a closed descriptor yields -1 with errno from the
|
||||
* syscall layer) and then answer from the resulting filesystem data plus
|
||||
* the fixed value table below. Only _PC_NAME_MAX and the three
|
||||
* block-size-derived keys are truly per-filesystem; the rest are vlibc's
|
||||
* fixed Linux values (see the pathconf() documentation in
|
||||
* include/unistd.h). Unknown keys and keys whose option this filesystem
|
||||
* does not provide return -1 WITHOUT setting errno.
|
||||
*
|
||||
* The name argument (one of the _PC_* keys) is forwarded to the value
|
||||
* table unchanged; POSIX does not define per-file-type answers for the
|
||||
* keys vlibc supports, so the object type is not inspected beyond the
|
||||
* statfs validation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Linux struct statfs (kernel ABI, x86_64; linux/statfs.h). Layout and
|
||||
* sizes are LP64 facts: f_type..f_ffree are longs, f_fsid is an int pair,
|
||||
* then f_namelen/f_frsize/f_flags/f_spare[4] complete the 120 bytes the
|
||||
* kernel copies out. Only f_bsize and f_namelen are read here.
|
||||
*/
|
||||
struct vlibc_statfs
|
||||
{
|
||||
long f_type;
|
||||
long f_bsize;
|
||||
long f_blocks;
|
||||
long f_bfree;
|
||||
long f_bavail;
|
||||
long f_files;
|
||||
long f_ffree;
|
||||
int f_fsid[2];
|
||||
long f_namelen;
|
||||
long f_frsize;
|
||||
long f_flags;
|
||||
long f_spare[4];
|
||||
};
|
||||
|
||||
/* Answer key name from the filesystem data in fs; -1 for unknown keys and
|
||||
* for keys whose option this filesystem does not provide, without setting
|
||||
* errno in either case. */
|
||||
static long
|
||||
pathconf_value(const struct vlibc_statfs *fs, int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _PC_LINK_MAX:
|
||||
return 32000;
|
||||
case _PC_MAX_CANON:
|
||||
case _PC_MAX_INPUT:
|
||||
return 255;
|
||||
case _PC_NAME_MAX:
|
||||
return fs->f_namelen;
|
||||
case _PC_PATH_MAX:
|
||||
case _PC_PIPE_BUF:
|
||||
return 4096;
|
||||
case _PC_CHOWN_RESTRICTED:
|
||||
case _PC_NO_TRUNC:
|
||||
case _PC_2_SYMLINKS:
|
||||
return 1;
|
||||
case _PC_VDISABLE:
|
||||
return 0;
|
||||
case _PC_FILESIZEBITS:
|
||||
return 64;
|
||||
case _PC_REC_MIN_XFER_SIZE:
|
||||
case _PC_REC_XFER_ALIGN:
|
||||
case _PC_ALLOC_SIZE_MIN:
|
||||
return fs->f_bsize;
|
||||
default:
|
||||
/* _PC_ASYNC_IO, _PC_SYNC_IO, _PC_PRIO_IO, _PC_SOCK_MAXBUF,
|
||||
* _PC_REC_INCR_XFER_SIZE, _PC_REC_MAX_XFER_SIZE, _PC_SYMLINK_MAX
|
||||
* and unknown keys. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
long
|
||||
pathconf(const char *path, int name)
|
||||
{
|
||||
struct vlibc_statfs fs = {0};
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_statfs, (long)path, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pathconf_value(&fs, name);
|
||||
}
|
||||
|
||||
long
|
||||
fpathconf(int fildes, int name) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
struct vlibc_statfs fs = {0};
|
||||
|
||||
if (syscall_ret(__syscall2(SYS_fstatfs, fildes, (long)&fs)) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return pathconf_value(&fs, name);
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* sysconf(): system limits and options.
|
||||
*
|
||||
* Every supported key returns its value directly (see the sysconf()
|
||||
* documentation in include/unistd.h for the full table). Unknown keys and
|
||||
* genuinely unsupported options return -1 WITHOUT setting errno — a call
|
||||
* must be able to probe arbitrary _SC_* values without clobbering a
|
||||
* caller's errno (the QA contract of todo 32).
|
||||
*
|
||||
* The fixed x86_64 clock-tick rate, defined ONCE here: USER_HZ is 100 on
|
||||
* every x86_64 Linux kernel. todo 35's times() reports in exactly these
|
||||
* ticks and consumes this constant rather than re-defining it.
|
||||
*/
|
||||
#define VLIBC_CLK_TCK 100
|
||||
|
||||
/* Kernel-UAPI values local to this file (include/fcntl.h owns the public
|
||||
* AT_FDCWD / O_RDONLY spellings; asm-generic/auxvec.h owns AT_PAGESZ). */
|
||||
#define VLIBC_SYSCONF_AT_FDCWD (-100)
|
||||
#define VLIBC_SYSCONF_AT_NULL 0
|
||||
#define VLIBC_SYSCONF_AT_PAGESZ 6
|
||||
#define VLIBC_SYSCONF_O_RDONLY 0
|
||||
#define VLIBC_SYSCONF_RLIMIT_NOFILE 7
|
||||
|
||||
/*
|
||||
* Linux struct sysinfo (kernel ABI, x86_64; linux/sysinfo.h). The totalram/
|
||||
* freeram/... fields are memory sizes counted in mem_unit-byte units
|
||||
* (current kernels report mem_unit == 1 with sizes in bytes). procs is the
|
||||
* number of live PROCESSES, not a CPU count — CPU counts come from
|
||||
* sched_getaffinity, not from here.
|
||||
*/
|
||||
struct vlibc_sysinfo
|
||||
{
|
||||
long uptime;
|
||||
unsigned long loads[3];
|
||||
unsigned long totalram;
|
||||
unsigned long freeram;
|
||||
unsigned long sharedram;
|
||||
unsigned long bufferram;
|
||||
unsigned long totalswap;
|
||||
unsigned long freeswap;
|
||||
unsigned short procs;
|
||||
unsigned short pad;
|
||||
unsigned long totalhigh;
|
||||
unsigned long freehigh;
|
||||
unsigned int mem_unit;
|
||||
};
|
||||
|
||||
/* RLIMIT_NOFILE as returned by prlimit64: current and maximum limits. */
|
||||
struct vlibc_rlimit
|
||||
{
|
||||
unsigned long cur;
|
||||
unsigned long max;
|
||||
};
|
||||
|
||||
/*
|
||||
* The memory page size, read from the AT_PAGESZ entry of the auxiliary
|
||||
* vector. The vlibc startup code (todo 3) does not save the auxv, so it is
|
||||
* re-read from /proc/self/auxv on first use and cached; a static cache is
|
||||
* safe because no threads exist yet (todo 45 must revisit). On any failure
|
||||
* — auxv unreadable or AT_PAGESZ absent — the x86_64 true page size 4096
|
||||
* is used as the documented fallback (this is what AT_PAGESZ reports on
|
||||
* every x86_64 kernel anyway; the value is never hardcoded as the primary
|
||||
* source).
|
||||
*/
|
||||
static long
|
||||
query_pagesize(void)
|
||||
{
|
||||
static long cached;
|
||||
static int done;
|
||||
|
||||
if (!done)
|
||||
{
|
||||
unsigned char buf[1024] = {0};
|
||||
long fd;
|
||||
long n;
|
||||
|
||||
done = 1;
|
||||
cached = 4096;
|
||||
fd = __syscall3(SYS_openat, VLIBC_SYSCONF_AT_FDCWD, (long)"/proc/self/auxv",
|
||||
VLIBC_SYSCONF_O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
n = __syscall3(SYS_read, fd, (long)buf, (long)sizeof buf);
|
||||
__syscall1(SYS_close, fd);
|
||||
for (long off = 0; n >= 16 && off <= n - 16; off += 16)
|
||||
{
|
||||
unsigned long type;
|
||||
unsigned long value;
|
||||
|
||||
/* The auxv is a sequence of (type, value) unsigned long pairs. */
|
||||
// NOLINTBEGIN(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
__builtin_memcpy(&type, buf + off, sizeof type);
|
||||
__builtin_memcpy(&value, buf + off + sizeof type, sizeof value);
|
||||
// NOLINTEND(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
|
||||
if (type == VLIBC_SYSCONF_AT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (type == VLIBC_SYSCONF_AT_PAGESZ)
|
||||
{
|
||||
cached = (long)value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cached;
|
||||
}
|
||||
|
||||
/*
|
||||
* The open-descriptor limit: the RLIMIT_NOFILE soft limit via prlimit64.
|
||||
* prlimit64 cannot usefully fail on the calling process, but the raw
|
||||
* result is checked anyway so no errno write happens on the fallback path
|
||||
* (the probe must stay errno-clean: it may run under a seccomp filter).
|
||||
*/
|
||||
static long
|
||||
query_open_max(void)
|
||||
{
|
||||
struct vlibc_rlimit rlim = {0};
|
||||
long r;
|
||||
|
||||
r = __syscall4(SYS_prlimit64, 0, VLIBC_SYSCONF_RLIMIT_NOFILE, 0, (long)&rlim);
|
||||
if (r < 0 || rlim.cur == 0 || rlim.cur == ~0UL)
|
||||
{
|
||||
return 1024;
|
||||
}
|
||||
return (long)rlim.cur;
|
||||
}
|
||||
|
||||
/*
|
||||
* The online processor count, from the calling process's CPU affinity
|
||||
* mask via sched_getaffinity(pid 0): the kernel returns the task's allowed
|
||||
* CPUs, which is the count of CPUs this process can actually run on. The
|
||||
* configured count is reported as the same value (a cpuset could hide
|
||||
* CPUs, but on a system without hotplug the configured and online counts
|
||||
* agree; glibc/musl make the same simplification for the affinity path).
|
||||
*/
|
||||
static long
|
||||
query_nprocs(void)
|
||||
{
|
||||
unsigned long mask[16] = {0}; /* 1024 bits, enough for any CPU count */
|
||||
long count = 0;
|
||||
long r;
|
||||
|
||||
r = __syscall3(SYS_sched_getaffinity, 0, (long)sizeof mask, (long)mask);
|
||||
if (r < 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (r > (long)sizeof mask)
|
||||
{
|
||||
r = (long)sizeof mask;
|
||||
}
|
||||
for (long i = 0; i < r / (long)sizeof(unsigned long); i++)
|
||||
{
|
||||
count += (long)__builtin_popcountl(mask[i]);
|
||||
}
|
||||
return count > 0 ? count : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Physical (avail == 0) or available (avail != 0) memory in pages, from
|
||||
* SYS_sysinfo. Sizes arrive in mem_unit-byte units, so the byte count is
|
||||
* totalram * mem_unit (overflow is not a concern: the product stays far
|
||||
* below 2^64 for any real machine). A sysinfo failure is reported as -1,
|
||||
* which the caller treats like an unsupported key.
|
||||
*/
|
||||
static long
|
||||
query_mem_pages(int avail)
|
||||
{
|
||||
struct vlibc_sysinfo si = {0};
|
||||
unsigned long unit;
|
||||
unsigned long bytes;
|
||||
long r;
|
||||
|
||||
r = __syscall1(SYS_sysinfo, (long)&si);
|
||||
if (r < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
unit = si.mem_unit != 0 ? si.mem_unit : 1;
|
||||
bytes = (avail != 0 ? si.freeram : si.totalram) * unit;
|
||||
return (long)(bytes / (unsigned long)query_pagesize());
|
||||
}
|
||||
|
||||
long
|
||||
sysconf(int name)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case _SC_ARG_MAX:
|
||||
return 2097152;
|
||||
case _SC_CHILD_MAX:
|
||||
return 65535;
|
||||
case _SC_CLK_TCK:
|
||||
return VLIBC_CLK_TCK;
|
||||
case _SC_NGROUPS_MAX:
|
||||
return 65536;
|
||||
case _SC_OPEN_MAX:
|
||||
return query_open_max();
|
||||
case _SC_PAGESIZE:
|
||||
case _SC_PAGE_SIZE:
|
||||
return query_pagesize();
|
||||
case _SC_NPROCESSORS_CONF:
|
||||
case _SC_NPROCESSORS_ONLN:
|
||||
return query_nprocs();
|
||||
case _SC_PHYS_PAGES:
|
||||
return query_mem_pages(0);
|
||||
case _SC_AVPHYS_PAGES:
|
||||
return query_mem_pages(1);
|
||||
case _SC_SYMLOOP_MAX:
|
||||
return 40;
|
||||
case _SC_STREAM_MAX:
|
||||
return 16;
|
||||
case _SC_TZNAME_MAX:
|
||||
return 6;
|
||||
case _SC_HOST_NAME_MAX:
|
||||
return 64;
|
||||
case _SC_LOGIN_NAME_MAX:
|
||||
return 256;
|
||||
case _SC_GETPW_R_SIZE_MAX:
|
||||
case _SC_IOV_MAX:
|
||||
return 1024;
|
||||
case _SC_LINE_MAX:
|
||||
return 2048;
|
||||
case _SC_RE_DUP_MAX:
|
||||
return 32767;
|
||||
case _SC_COLL_WEIGHTS_MAX:
|
||||
return 255;
|
||||
case _SC_EXPR_NEST_MAX:
|
||||
return 32;
|
||||
case _SC_XOPEN_VERSION:
|
||||
return 700;
|
||||
case _SC_JOB_CONTROL:
|
||||
case _SC_SAVED_IDS:
|
||||
case _SC_REGEXP:
|
||||
case _SC_SHELL:
|
||||
case _SC_XOPEN_UNIX:
|
||||
case _SC_XOPEN_CRYPT:
|
||||
case _SC_XOPEN_ENH_I18N:
|
||||
case _SC_XOPEN_SHM:
|
||||
return 1;
|
||||
case _SC_VERSION:
|
||||
case _SC_2_VERSION:
|
||||
case _SC_THREADS:
|
||||
case _SC_SEMAPHORES:
|
||||
case _SC_SHARED_MEMORY_OBJECTS:
|
||||
case _SC_MESSAGE_PASSING:
|
||||
case _SC_MONOTONIC_CLOCK:
|
||||
case _SC_TIMERS:
|
||||
case _SC_SYNCHRONIZED_IO:
|
||||
case _SC_THREAD_SAFE_FUNCTIONS:
|
||||
case _SC_THREAD_ATTR_STACKADDR:
|
||||
case _SC_THREAD_ATTR_STACKSIZE:
|
||||
case _SC_THREAD_PRIORITY_SCHEDULING:
|
||||
case _SC_THREAD_PRIO_INHERIT:
|
||||
case _SC_THREAD_PRIO_PROTECT:
|
||||
case _SC_THREAD_PROCESS_SHARED:
|
||||
case _SC_REALTIME_SIGNALS:
|
||||
case _SC_2_C_BIND:
|
||||
case _SC_2_C_DEV:
|
||||
case _SC_2_FORT_DEV:
|
||||
case _SC_2_FORT_RUN:
|
||||
case _SC_2_LOCALEDEF:
|
||||
case _SC_2_SW_DEV:
|
||||
case _SC_2_UPE:
|
||||
return 200809L;
|
||||
default:
|
||||
/* Unknown key or unsupported option: -1, errno untouched. */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "../internal/syscall.h"
|
||||
|
||||
/*
|
||||
* uname/gethostname/sethostname/gethostid: system identification.
|
||||
*
|
||||
* Everything here is XSI or BSD — none of it is POSIX.1-2008 base — so the
|
||||
* whole file is gated at level 2 (whole-file-L2 rule; the wiring pass puts
|
||||
* it in VLIBC_LEVEL2_SRCS, and at level 1 this TU compiles empty).
|
||||
*
|
||||
* gethostname and sethostname are declared in <unistd.h>, gethostid too;
|
||||
* uname and struct utsname live in <sys/utsname.h>. struct utsname is the
|
||||
* kernel's struct new_utsname layout (six 65-byte arrays), so the kernel
|
||||
* writes it in place through SYS_uname.
|
||||
*/
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
|
||||
/*
|
||||
* Fill the buffer with the kernel's UTS namespace strings (the "name"
|
||||
* information of the running kernel and host). syscall_ret reports 0 on
|
||||
* success and -1 with EFAULT when the buffer is not writable.
|
||||
*/
|
||||
int
|
||||
uname(struct utsname *buf)
|
||||
{
|
||||
return syscall_ret(__syscall1(SYS_uname, (long)buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the NUL-terminated nodename (the host name) into name. The
|
||||
* nodename is read from the kernel through uname() into a stack buffer, so
|
||||
* nothing is allocated or leaked. When the name does not fit in len bytes
|
||||
* — including its terminator — -1 is returned with ENAMETOOLONG and name
|
||||
* is left untouched (glibc semantics; the POSIX text leaves the truncation
|
||||
* case implementation-defined).
|
||||
*
|
||||
* The attribute keeps GCC from recognizing the length loop as a strlen
|
||||
* call or the copy loop as memcpy (house idiom, see src/string): a
|
||||
* libc-owned symbol must not be pulled into an unplanned dependency.
|
||||
*/
|
||||
__attribute__((optimize("no-tree-loop-distribute-patterns"))) int
|
||||
gethostname(char *name, size_t len)
|
||||
{
|
||||
struct utsname u = {0}; /* kernel-written; zeroed for the analyzer */
|
||||
size_t n = 0;
|
||||
|
||||
if (uname(&u) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (u.nodename[n] != '\0' && n < sizeof(u.nodename))
|
||||
{
|
||||
n++;
|
||||
}
|
||||
if (n + 1 > len)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
for (size_t i = 0; i <= n; i++)
|
||||
{
|
||||
name[i] = u.nodename[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the kernel's host name (the uts nodename) to the len bytes at name.
|
||||
* Requires CAP_SYS_ADMIN in the initial user namespace. syscall_ret
|
||||
* reports 0 on success and -1 with EPERM/EINVAL/EFAULT otherwise.
|
||||
*/
|
||||
int
|
||||
sethostname(const char *name, size_t len)
|
||||
{
|
||||
return syscall_ret(__syscall2(SYS_sethostname, (long)name, (long)len));
|
||||
}
|
||||
|
||||
/*
|
||||
* The 32-bit host identifier. vlibc returns 0, matching musl: the classic
|
||||
* value was a hash of the host's primary address obtained through
|
||||
* gethostbyname, a lookup this library deliberately does not perform, and
|
||||
* POSIX marks the interface obsolescent [OB].
|
||||
*/
|
||||
long
|
||||
gethostid(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* vlibc — uname/gethostname/sysconf/pathconf test (todo 32).
|
||||
*
|
||||
* Exercises the system-configuration surface end to end:
|
||||
*
|
||||
* 1. sysconf(_SC_PAGESIZE) equals the AT_PAGESZ entry read from
|
||||
* /proc/self/auxv (proving the page size is derived, not hardcoded)
|
||||
* and is a power of two; _SC_PAGE_SIZE is its POSIX synonym.
|
||||
* 2. sysconf(_SC_CLK_TCK) == 100 — the fixed x86_64 USER_HZ constant,
|
||||
* the documented contract with todo 35's times().
|
||||
* 3. sysconf(_SC_ARG_MAX)/_SC_OPEN_MAX/_SC_NPROCESSORS_ONLN/
|
||||
* _SC_PHYS_PAGES and friends report sane positive values.
|
||||
* 4. sysconf of an unknown key (999999) returns -1 WITHOUT setting
|
||||
* errno — proven by a host-TCB slot-1 snapshot around the call.
|
||||
* 5. pathconf("/", _PC_PATH_MAX) > 0 and fpathconf on a raw-opened fd
|
||||
* of "/" agrees with pathconf("/") on _PC_NAME_MAX.
|
||||
* 6. Level 2: uname fills every field; machine contains "86_64";
|
||||
* gethostname returns the same name as uname's nodename,
|
||||
* NUL-terminated; gethostid() == 0.
|
||||
*
|
||||
* The -f mode runs the failure scenarios alone: sysconf(unknown) == -1,
|
||||
* pathconf/fpathconf on a missing path and a closed descriptor == -1, and
|
||||
* gethostname into a 1-byte buffer == -1. Only return values are asserted;
|
||||
* the failing paths make the LIBRARY write errno (syscall_ret /
|
||||
* ENAMETOOLONG), which under a host-linked binary targets glibc's private
|
||||
* dtv slot at %fs:0+8, so -f exits via raw SYS_exit_group (house pattern,
|
||||
* tests/test_stat.c).
|
||||
*
|
||||
* All diagnostics go through raw SYS_write (no host stdio): under
|
||||
* -Iinclude the vlibc public headers shadow GCC's internal ones, so a host
|
||||
* header would not compile. Not part of the library proper; compiled
|
||||
* manually for this todo (the make check wiring is owned by a later todo).
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "../src/internal/syscall.h"
|
||||
|
||||
/* Kernel-UAPI values local to this test (fcntl.h owns the public names). */
|
||||
#define T32_AT_FDCWD (-100)
|
||||
#define T32_AT_NULL 0
|
||||
#define T32_AT_PAGESZ 6
|
||||
#define T32_O_RDONLY 0
|
||||
#define T32_UNKNOWN_KEY 999999
|
||||
|
||||
static int failures;
|
||||
|
||||
/* Write a NUL-terminated string to fd via the raw syscall layer. The
|
||||
* optimize attribute keeps GCC from lowering the length loop into a
|
||||
* strlen call, which would leave a vlibc-owned symbol undefined in this
|
||||
* host-linked standalone binary (house idiom, see src/string). */
|
||||
static __attribute__((optimize("no-tree-loop-distribute-patterns"))) void
|
||||
say(int fd, const char *s)
|
||||
{
|
||||
long n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
{
|
||||
n++;
|
||||
}
|
||||
__syscall3(SYS_write, fd, (long)s, n);
|
||||
}
|
||||
|
||||
/* Write v in decimal to fd. */
|
||||
static void
|
||||
say_dec(int fd, unsigned long v) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
char buf[24];
|
||||
int i = (int)sizeof(buf);
|
||||
|
||||
buf[--i] = '\0';
|
||||
if (v == 0)
|
||||
{
|
||||
buf[--i] = '0';
|
||||
}
|
||||
while (v > 0 && i > 0)
|
||||
{
|
||||
buf[--i] = (char)('0' + v % 10);
|
||||
v /= 10;
|
||||
}
|
||||
say(fd, &buf[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
check(int ok, const char *msg)
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
say(1, "PASS: ");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAIL: ");
|
||||
failures++;
|
||||
}
|
||||
say(1, msg);
|
||||
say(1, "\n");
|
||||
}
|
||||
|
||||
/* Host-TCB slot-1 access: the library's errno writes on a failing path
|
||||
* land at %fs:0+8, glibc's dtv pointer. Snapshotting it around a call
|
||||
* that must NOT write errno proves the no-errno contract (task 13
|
||||
* technique); only vlibc/raw-syscall code runs between the reads. */
|
||||
static unsigned long
|
||||
tcb_slot1(void)
|
||||
{
|
||||
return *(unsigned long *)((char *)__builtin_thread_pointer() + 8);
|
||||
}
|
||||
|
||||
/* The AT_PAGESZ entry of the auxiliary vector, read directly from
|
||||
* /proc/self/auxv (the same derivation src/misc/sysconf.c uses), so the
|
||||
* test proves sysconf(_SC_PAGESIZE) tracks the kernel value. */
|
||||
static long
|
||||
auxv_pagesize(void)
|
||||
{
|
||||
static unsigned long abuf[64] = {0}; /* 1024 bytes, larger than any auxv */
|
||||
long r;
|
||||
long fd;
|
||||
|
||||
fd = __syscall3(SYS_openat, T32_AT_FDCWD, (long)"/proc/self/auxv", T32_O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
r = __syscall3(SYS_read, fd, (long)abuf, (long)sizeof abuf);
|
||||
__syscall1(SYS_close, fd);
|
||||
for (long i = 0; i + 1 < r / (long)sizeof(unsigned long); i += 2)
|
||||
{
|
||||
if (abuf[i] == T32_AT_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (abuf[i] == T32_AT_PAGESZ)
|
||||
{
|
||||
return (long)abuf[i + 1];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 1-4. sysconf limits, options, derivation, and the unknown-key contract. */
|
||||
static void
|
||||
sysconf_scenario(void)
|
||||
{
|
||||
unsigned long saved;
|
||||
long ps;
|
||||
long av;
|
||||
|
||||
ps = sysconf(_SC_PAGESIZE);
|
||||
av = auxv_pagesize();
|
||||
check(av > 0, "auxv AT_PAGESZ is readable");
|
||||
check(ps == av, "sysconf(_SC_PAGESIZE) equals the AT_PAGESZ auxv value");
|
||||
check(ps > 0 && (ps & (ps - 1)) == 0, "sysconf(_SC_PAGESIZE) is a power of two");
|
||||
check(sysconf(_SC_PAGE_SIZE) == ps, "sysconf(_SC_PAGE_SIZE) is the _SC_PAGESIZE synonym");
|
||||
check(sysconf(_SC_CLK_TCK) == 100, "sysconf(_SC_CLK_TCK) is the fixed x86_64 value 100");
|
||||
check(sysconf(_SC_ARG_MAX) > 0, "sysconf(_SC_ARG_MAX) > 0");
|
||||
check(sysconf(_SC_CHILD_MAX) > 0, "sysconf(_SC_CHILD_MAX) > 0");
|
||||
check(sysconf(_SC_NGROUPS_MAX) > 0, "sysconf(_SC_NGROUPS_MAX) > 0");
|
||||
check(sysconf(_SC_OPEN_MAX) > 0, "sysconf(_SC_OPEN_MAX) > 0");
|
||||
check(sysconf(_SC_NPROCESSORS_ONLN) >= 1, "sysconf(_SC_NPROCESSORS_ONLN) >= 1");
|
||||
check(sysconf(_SC_NPROCESSORS_CONF) >= 1, "sysconf(_SC_NPROCESSORS_CONF) >= 1");
|
||||
check(sysconf(_SC_PHYS_PAGES) > 0, "sysconf(_SC_PHYS_PAGES) > 0");
|
||||
check(sysconf(_SC_AVPHYS_PAGES) > 0, "sysconf(_SC_AVPHYS_PAGES) > 0");
|
||||
check(sysconf(_SC_VERSION) == 200809L, "sysconf(_SC_VERSION) is 200809L");
|
||||
check(sysconf(_SC_HOST_NAME_MAX) == 64, "sysconf(_SC_HOST_NAME_MAX) is 64");
|
||||
check(sysconf(_SC_LOGIN_NAME_MAX) > 0, "sysconf(_SC_LOGIN_NAME_MAX) > 0");
|
||||
check(sysconf(_SC_SYMLOOP_MAX) > 0, "sysconf(_SC_SYMLOOP_MAX) > 0");
|
||||
check(sysconf(_SC_STREAM_MAX) > 0, "sysconf(_SC_STREAM_MAX) > 0");
|
||||
check(sysconf(_SC_IOV_MAX) > 0, "sysconf(_SC_IOV_MAX) > 0");
|
||||
check(sysconf(_SC_XOPEN_VERSION) == 700, "sysconf(_SC_XOPEN_VERSION) is 700");
|
||||
check(sysconf(_SC_THREADS) > 0, "sysconf(_SC_THREADS) > 0");
|
||||
check(sysconf(_SC_MONOTONIC_CLOCK) > 0, "sysconf(_SC_MONOTONIC_CLOCK) > 0");
|
||||
saved = tcb_slot1();
|
||||
check(sysconf(T32_UNKNOWN_KEY) == -1, "sysconf(unknown key) returns -1");
|
||||
check(tcb_slot1() == saved, "sysconf(unknown key) does not set errno");
|
||||
}
|
||||
|
||||
/* 5. pathconf/fpathconf consistency on the root filesystem. */
|
||||
static void
|
||||
pathconf_scenario(void)
|
||||
{
|
||||
long name_max;
|
||||
long fd;
|
||||
|
||||
check(pathconf("/", _PC_PATH_MAX) > 0, "pathconf(/, _PC_PATH_MAX) > 0");
|
||||
check(pathconf("/", _PC_NAME_MAX) > 0, "pathconf(/, _PC_NAME_MAX) > 0");
|
||||
check(pathconf("/", _PC_2_SYMLINKS) == 1, "pathconf(/, _PC_2_SYMLINKS) == 1");
|
||||
check(pathconf("/", _PC_CHOWN_RESTRICTED) == 1, "pathconf(/, _PC_CHOWN_RESTRICTED) == 1");
|
||||
name_max = pathconf("/", _PC_NAME_MAX);
|
||||
fd = __syscall3(SYS_openat, T32_AT_FDCWD, (long)"/", T32_O_RDONLY);
|
||||
check(fd >= 0, "raw open of / returns a descriptor");
|
||||
if (fd >= 0)
|
||||
{
|
||||
check(fpathconf((int)fd, _PC_NAME_MAX) == name_max,
|
||||
"fpathconf(/, _PC_NAME_MAX) agrees with pathconf(/ )");
|
||||
__syscall1(SYS_close, fd);
|
||||
}
|
||||
}
|
||||
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
/* 6. uname/gethostname/gethostid. */
|
||||
static void
|
||||
uname_scenario(void)
|
||||
{
|
||||
struct utsname u; /* fully written by the kernel on a successful uname() */
|
||||
char host[65];
|
||||
char *p;
|
||||
|
||||
check(sizeof(struct utsname) == 390, "sizeof(struct utsname) is 390");
|
||||
check(uname(&u) == 0, "uname returns 0");
|
||||
check(u.sysname[0] != '\0', "uname sysname is non-empty");
|
||||
check(u.nodename[0] != '\0', "uname nodename is non-empty");
|
||||
check(u.release[0] != '\0', "uname release is non-empty");
|
||||
check(u.version[0] != '\0', "uname version is non-empty");
|
||||
check(u.machine[0] != '\0', "uname machine is non-empty");
|
||||
p = strstr(u.machine, "86_64");
|
||||
check(p != (char *)0, "uname machine contains 86_64");
|
||||
check(gethostname(host, sizeof host) == 0, "gethostname returns 0");
|
||||
check(host[0] != '\0', "gethostname result is non-empty");
|
||||
check(strcmp(u.nodename, host) == 0, "gethostname equals the uname nodename");
|
||||
check(gethostid() == 0, "gethostid() returns 0");
|
||||
}
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
/* The failure scenarios. Exit via raw SYS_exit_group: the library's errno
|
||||
* writes on these paths corrupt glibc's private dtv slot at %fs:0+8, so
|
||||
* host cleanup must never run (house pattern). */
|
||||
static void
|
||||
failure_scenarios(void)
|
||||
{
|
||||
check(sysconf(T32_UNKNOWN_KEY) == -1, "-f sysconf(unknown key) returns -1");
|
||||
check(pathconf("/nonexistent-vlibc-zzz", _PC_PATH_MAX) == -1,
|
||||
"pathconf on a missing path returns -1");
|
||||
// NOLINTBEGIN(clang-analyzer-unix.StdCLibraryFunctions) -- deliberate EBADF probe
|
||||
check(fpathconf(-1, _PC_NAME_MAX) == -1, "fpathconf(-1) returns -1");
|
||||
// NOLINTEND(clang-analyzer-unix.StdCLibraryFunctions)
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
{
|
||||
char one[1];
|
||||
|
||||
check(gethostname(one, 1) == -1, "gethostname into a 1-byte buffer returns -1");
|
||||
}
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
if (failures == 0)
|
||||
{
|
||||
say(1, "all uname failure scenarios passed\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAILURES: ");
|
||||
say_dec(1, (unsigned long)failures);
|
||||
say(1, "\n");
|
||||
}
|
||||
__syscall1(SYS_exit_group, failures == 0 ? 0 : 1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' && argv[1][2] == '\0')
|
||||
{
|
||||
failure_scenarios();
|
||||
}
|
||||
|
||||
sysconf_scenario();
|
||||
pathconf_scenario();
|
||||
#if VLIBC_LEVEL_GE(2)
|
||||
uname_scenario();
|
||||
#endif /* VLIBC_LEVEL_GE(2) */
|
||||
|
||||
if (failures == 0)
|
||||
{
|
||||
say(1, "all uname tests passed\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
say(1, "FAILURES: ");
|
||||
say_dec(1, (unsigned long)failures);
|
||||
say(1, "\n");
|
||||
}
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user