feat(resource): rlimit/rusage/priority
This commit is contained in:
@@ -0,0 +1,203 @@
|
|||||||
|
#ifndef VLIBC_SYS_RESOURCE_H
|
||||||
|
#define VLIBC_SYS_RESOURCE_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* vlibc — <sys/resource.h>.
|
||||||
|
*
|
||||||
|
* Per-process resource limits and usage accounting. Nothing in this header
|
||||||
|
* is POSIX.1-2008 base — getrlimit/setrlimit/getrusage/getpriority/
|
||||||
|
* setpriority and nice are all XSI [CX] — so the entire surface is gated at
|
||||||
|
* level 2:
|
||||||
|
*
|
||||||
|
* Level 2 (muslmimic): getrlimit, setrlimit, getrusage, getpriority,
|
||||||
|
* setpriority, nice, getrlimit64, setrlimit64,
|
||||||
|
* struct rlimit, struct rusage, rlim_t, RLIMIT_*,
|
||||||
|
* RLIM_INFINITY, PRIO_*, RUSAGE_*.
|
||||||
|
*
|
||||||
|
* struct rusage matches the kernel layout on x86_64 verbatim: two struct
|
||||||
|
* timevals (16 bytes each — the kernel record uses the same two-long
|
||||||
|
* __kernel_old_timeval) followed by fourteen longs, and the kernel
|
||||||
|
* getrusage copies it out without translation. struct rlimit is likewise
|
||||||
|
* the kernel's struct rlimit64 (two 64-bit words), which is why
|
||||||
|
* getrlimit/setrlimit pass the record straight to SYS_prlimit64 and why
|
||||||
|
* getrlimit64/setrlimit64 are name-only aliases on this architecture.
|
||||||
|
* Both shapes are pinned by the static asserts below.
|
||||||
|
*
|
||||||
|
* Every function here performs kernel I/O with side effects and reports
|
||||||
|
* failures through errno, so no declaration carries an intent attribute
|
||||||
|
* (const/pure would be unsound).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vlibc/features.h>
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
#include <stddef.h> /* offsetof */
|
||||||
|
|
||||||
|
#include <sys/time.h> /* struct timeval (shared VLIBC_TIMEVAL_DEFINED guard) */
|
||||||
|
|
||||||
|
#include <sys/types.h> /* id_t */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resource limit values: the 64-bit width of the kernel's rlimit64 record
|
||||||
|
* (unsigned long long on x86_64, where the classic long is also 64-bit but
|
||||||
|
* the LFS spelling is authoritative).
|
||||||
|
*/
|
||||||
|
typedef unsigned long long rlim_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A resource limit pair: rlim_cur is the soft limit (enforced), rlim_max
|
||||||
|
* the hard limit (ceiling a soft limit may be raised to without
|
||||||
|
* privilege). RLIM_INFINITY in either field means "unlimited".
|
||||||
|
*/
|
||||||
|
struct rlimit
|
||||||
|
{
|
||||||
|
rlim_t rlim_cur; /* soft limit (current) */
|
||||||
|
rlim_t rlim_max; /* hard limit (ceiling) */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-process resource usage. The field order is the kernel's own
|
||||||
|
* (getrusage copies the record out untouched): the two CPU-time timevals
|
||||||
|
* first, then the fourteen accounting longs. ru_maxrss is in kilobytes.
|
||||||
|
* The fields a kernel does not maintain stay zero.
|
||||||
|
*/
|
||||||
|
struct rusage
|
||||||
|
{
|
||||||
|
struct timeval ru_utime; /* user CPU time used */
|
||||||
|
struct timeval ru_stime; /* system CPU time used */
|
||||||
|
long ru_maxrss; /* maximum resident set size (KiB) */
|
||||||
|
long ru_ixrss; /* integral shared memory size */
|
||||||
|
long ru_idrss; /* integral unshared data size */
|
||||||
|
long ru_isrss; /* integral unshared stack size */
|
||||||
|
long ru_minflt; /* page reclaims (soft page faults) */
|
||||||
|
long ru_majflt; /* page faults (hard page faults) */
|
||||||
|
long ru_nswap; /* swaps */
|
||||||
|
long ru_inblock; /* block input operations */
|
||||||
|
long ru_oublock; /* block output operations */
|
||||||
|
long ru_msgsnd; /* IPC messages sent */
|
||||||
|
long ru_msgrcv; /* IPC messages received */
|
||||||
|
long ru_nsignals; /* signals received */
|
||||||
|
long ru_nvcsw; /* voluntary context switches */
|
||||||
|
long ru_nivcsw; /* involuntary context switches */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* x86_64 kernel ABI: 2 x struct timeval (16 B) + 14 longs (8 B) = 144 B. */
|
||||||
|
_Static_assert(sizeof(struct rusage) == 144, "struct rusage must match the x86_64 kernel layout");
|
||||||
|
_Static_assert(offsetof(struct rusage, ru_maxrss) == 32, "ru_maxrss must follow the two timevals");
|
||||||
|
_Static_assert(sizeof(struct rlimit) == 2 * sizeof(rlim_t),
|
||||||
|
"struct rlimit is a pair of rlim_t words");
|
||||||
|
|
||||||
|
/* ---- Resource limit identifiers (kernel asm-generic values) ---- */
|
||||||
|
|
||||||
|
#define RLIMIT_CPU 0 /* CPU time in seconds */
|
||||||
|
#define RLIMIT_FSIZE 1 /* maximum file size */
|
||||||
|
#define RLIMIT_DATA 2 /* data segment size */
|
||||||
|
#define RLIMIT_STACK 3 /* stack size */
|
||||||
|
#define RLIMIT_CORE 4 /* core file size */
|
||||||
|
#define RLIMIT_RSS 5 /* resident set size */
|
||||||
|
#define RLIMIT_NPROC 6 /* number of processes */
|
||||||
|
#define RLIMIT_NOFILE 7 /* number of open files */
|
||||||
|
#define RLIMIT_MEMLOCK 8 /* locked-in-memory address space */
|
||||||
|
#define RLIMIT_AS 9 /* address space size */
|
||||||
|
#define RLIMIT_LOCKS 10 /* number of file locks held */
|
||||||
|
#define RLIMIT_SIGPENDING 11 /* number of pending signals */
|
||||||
|
#define RLIMIT_MSGQUEUE 12 /* bytes in POSIX message queues */
|
||||||
|
#define RLIMIT_NICE 13 /* ceiling for the nice value */
|
||||||
|
#define RLIMIT_RTPRIO 14 /* maximum realtime priority */
|
||||||
|
#define RLIMIT_RTTIME 15 /* realtime CPU time (us) */
|
||||||
|
#define RLIMIT_NLIMITS 16 /* number of resource kinds */
|
||||||
|
|
||||||
|
/* "No limit" value for either field of struct rlimit. */
|
||||||
|
#define RLIM_INFINITY (~0UL)
|
||||||
|
|
||||||
|
/* Saved-limit markers (legacy; equal to RLIM_INFINITY on Linux). */
|
||||||
|
#define RLIM_SAVED_CUR RLIM_INFINITY
|
||||||
|
#define RLIM_SAVED_MAX RLIM_INFINITY
|
||||||
|
|
||||||
|
/* ---- getpriority/setpriority target selectors ---- */
|
||||||
|
|
||||||
|
#define PRIO_PROCESS 0 /* a single process */
|
||||||
|
#define PRIO_PGRP 1 /* a process group */
|
||||||
|
#define PRIO_USER 2 /* every process of a user */
|
||||||
|
|
||||||
|
/* The valid nice-value range (PRIO_MIN is the highest scheduling priority). */
|
||||||
|
#define PRIO_MIN (-20)
|
||||||
|
#define PRIO_MAX 19
|
||||||
|
|
||||||
|
/* ---- getrusage `who` selectors ---- */
|
||||||
|
|
||||||
|
#define RUSAGE_SELF 0 /* the calling process */
|
||||||
|
#define RUSAGE_CHILDREN (-1) /* terminated and waited-for children */
|
||||||
|
#define RUSAGE_THREAD 1 /* the calling thread */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the current soft and hard limits of resource through rlim. The
|
||||||
|
* call never fails for a valid resource. Return 0, or -1 with errno set
|
||||||
|
* when resource is out of range or rlim points outside the address space.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getrlimit(int resource, struct rlimit *rlim);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the soft and hard limits of resource from rlim. Raising the hard
|
||||||
|
* limit (or the soft limit above the hard limit) requires privilege.
|
||||||
|
* Return 0, or -1 with errno set for an out-of-range resource, a soft
|
||||||
|
* limit above the hard limit, or a denied raise.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
setrlimit(int resource, const struct rlimit *rlim);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill usage with the resource usage of who (RUSAGE_SELF, RUSAGE_CHILDREN
|
||||||
|
* or RUSAGE_THREAD). Return 0, or -1 with errno set for an unknown who or
|
||||||
|
* when usage points outside the address space.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getrusage(int who, struct rusage *usage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the nice value of the target described by (which, who), in the
|
||||||
|
* range PRIO_MIN..PRIO_MAX, or -1 with errno set on error. Because -1 is
|
||||||
|
* also a valid priority, callers conventionally clear errno before the
|
||||||
|
* call and treat a -1 return with a nonzero errno as an error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getpriority(int which, id_t who);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the nice value of every process named by (which, who) to prio,
|
||||||
|
* clamping out-of-range values to PRIO_MIN..PRIO_MAX. Lowering the value
|
||||||
|
* (raising priority) requires privilege. Return 0, or -1 with errno set.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
setpriority(int which, id_t who, int prio);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add inc to the calling process's nice value and return the new value,
|
||||||
|
* or -1 with errno set on error (see getpriority for the -1 convention).
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
nice(int inc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Large-file aliases. rlim_t is already 64-bit on x86_64, so these behave
|
||||||
|
* exactly as getrlimit/setrlimit and exist as separate exported symbols for
|
||||||
|
* LFS callers.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getrlimit64(int resource, struct rlimit *rlim);
|
||||||
|
int
|
||||||
|
setrlimit64(int resource, const struct rlimit *rlim);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
|
|
||||||
|
#endif /* VLIBC_SYS_RESOURCE_H */
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "../internal/syscall.h"
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getpriority (todo 31).
|
||||||
|
*
|
||||||
|
* The kernel never returns the raw nice value from SYS_getpriority: to
|
||||||
|
* keep successful results from colliding with its negative -errno error
|
||||||
|
* encoding, it reports 20 - nice (1..40 for nice -20..19). Recover the
|
||||||
|
* nice value with the inverse translation. Errors arrive as negative
|
||||||
|
* -errno values and go through syscall_ret, which stores errno and
|
||||||
|
* returns -1.
|
||||||
|
*
|
||||||
|
* A returned -1 is therefore either an error (errno set) or the
|
||||||
|
* legitimate priority of a process running at nice -1 (errno untouched) —
|
||||||
|
* the caller distinguishes them by clearing errno before the call, per the
|
||||||
|
* POSIX convention documented in <sys/resource.h>.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getpriority(int which, id_t who)
|
||||||
|
{
|
||||||
|
long r;
|
||||||
|
|
||||||
|
r = __syscall2(SYS_getpriority, (long)which, (long)who);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
return syscall_ret(r);
|
||||||
|
}
|
||||||
|
return (int)(20 - r);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "../internal/syscall.h"
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getrlimit/getrlimit64 (todo 31).
|
||||||
|
*
|
||||||
|
* x86_64 has no getrlimit syscall; the prlimit64 ABI serves the read with
|
||||||
|
* (pid = 0, resource, new_limit = NULL, old_limit = &rlim). struct rlimit
|
||||||
|
* is two 64-bit words here — the same layout as the kernel's struct
|
||||||
|
* rlimit64 — so the record passes through without conversion.
|
||||||
|
*
|
||||||
|
* getrlimit64 is a name-only distinction on this architecture (rlim_t is
|
||||||
|
* already 64 bits); it exists as a separate exported symbol so LFS callers
|
||||||
|
* that reference it link cleanly.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getrlimit(int resource, struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
return syscall_ret(__syscall4(SYS_prlimit64, 0, (long)resource, 0, (long)rlim));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
getrlimit64(int resource, struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
return getrlimit(resource, rlim);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "../internal/syscall.h"
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getrusage (todo 31).
|
||||||
|
*
|
||||||
|
* Thin SYS_getrusage wrapper: (who, &usage). struct rusage is the kernel's
|
||||||
|
* own record layout on x86_64 (two timevals + fourteen longs, verified in
|
||||||
|
* <sys/resource.h>), so the kernel's copy-to-user writes the public struct
|
||||||
|
* without translation. The kernel rejects an unknown who with EINVAL.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
getrusage(int who, struct rusage *usage)
|
||||||
|
{
|
||||||
|
return syscall_ret(__syscall2(SYS_getrusage, (long)who, (long)usage));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nice (todo 31).
|
||||||
|
*
|
||||||
|
* Built on getpriority/setpriority over PRIO_PROCESS for the calling
|
||||||
|
* process (the plan's design): read the current nice value, add inc, write
|
||||||
|
* the sum back. The kernel clamps the result into PRIO_MIN..PRIO_MAX and
|
||||||
|
* rejects a raise (a decrease of the nice value) without privilege.
|
||||||
|
*
|
||||||
|
* The new nice value is returned, as POSIX requires; that value may be -1
|
||||||
|
* only when the process already runs at nice -1, which getpriority reports
|
||||||
|
* with errno untouched, so the -1 error sentinel is distinguishable the
|
||||||
|
* same way getpriority documents it.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
nice(int inc)
|
||||||
|
{
|
||||||
|
int prio;
|
||||||
|
|
||||||
|
prio = getpriority(PRIO_PROCESS, 0);
|
||||||
|
if (prio == -1)
|
||||||
|
{
|
||||||
|
/* -1 is a valid priority too; only a nonzero errno marks the read
|
||||||
|
* as a genuine failure (getpriority leaves errno alone on
|
||||||
|
* success). Callers may clear errno first to disambiguate. */
|
||||||
|
if (errno != 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (setpriority(PRIO_PROCESS, 0, prio + inc) != 0)
|
||||||
|
{
|
||||||
|
return -1; /* errno already set */
|
||||||
|
}
|
||||||
|
return getpriority(PRIO_PROCESS, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "../internal/syscall.h"
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* setpriority (todo 31).
|
||||||
|
*
|
||||||
|
* Thin SYS_setpriority wrapper: (which, who, prio). The kernel clamps
|
||||||
|
* out-of-range nice values to PRIO_MIN..PRIO_MAX instead of rejecting
|
||||||
|
* them, so the only failure paths are an invalid which (EINVAL), an
|
||||||
|
* unknown target (ESRCH) and a denied priority raise (EPERM/EACCES); the
|
||||||
|
* latter applies when the call would lower the target's nice value below
|
||||||
|
* its current one without privilege.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
setpriority(int which, id_t who, int prio)
|
||||||
|
{
|
||||||
|
return syscall_ret(__syscall3(SYS_setpriority, (long)which, (long)who, (long)prio));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "../internal/syscall.h"
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* setrlimit/setrlimit64 (todo 31).
|
||||||
|
*
|
||||||
|
* The write side of the prlimit64 ABI: (pid = 0, resource,
|
||||||
|
* new_limit = &rlim, old_limit = NULL). struct rlimit is the kernel's
|
||||||
|
* struct rlimit64 layout on x86_64, so the record passes through without
|
||||||
|
* conversion; the kernel rejects a soft limit above the hard limit with
|
||||||
|
* EINVAL and a hard-limit raise without CAP_SYS_RESOURCE with EPERM.
|
||||||
|
*
|
||||||
|
* setrlimit64 is a name-only distinction on this architecture (rlim_t is
|
||||||
|
* already 64 bits); it exists as a separate exported symbol for LFS
|
||||||
|
* callers.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
setrlimit(int resource, const struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
return syscall_ret(__syscall4(SYS_prlimit64, 0, (long)resource, (long)rlim, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
setrlimit64(int resource, const struct rlimit *rlim)
|
||||||
|
{
|
||||||
|
return setrlimit(resource, rlim);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
/*
|
||||||
|
* vlibc — sys/resource.h test (todo 31).
|
||||||
|
*
|
||||||
|
* Exercises the whole <sys/resource.h> surface end to end:
|
||||||
|
*
|
||||||
|
* 1. getrlimit(RLIMIT_NOFILE): returns 0 with a finite soft limit in
|
||||||
|
* (0, max] — the kernel's own NOFILE ceiling, so cur is never
|
||||||
|
* RLIM_INFINITY. getrlimit(RLIMIT_STACK) is the QA happy shape and
|
||||||
|
* must also report a positive soft limit.
|
||||||
|
* 2. setrlimit round-trip: re-installing the just-read RLIMIT_NOFILE
|
||||||
|
* pair must succeed without privilege (identical values never raise
|
||||||
|
* the hard limit), and a following getrlimit must reflect it.
|
||||||
|
* 3. getrlimit64/setrlimit64 parity: the LFS aliases return the same
|
||||||
|
* values and accept the same writes as the base functions.
|
||||||
|
* 4. getrusage(RUSAGE_SELF) and RUSAGE_THREAD: ru_utime is a sane
|
||||||
|
* timeval and ru_maxrss is non-negative.
|
||||||
|
* 5. getpriority(PRIO_PROCESS, 0) lands in PRIO_MIN..PRIO_MAX, and
|
||||||
|
* setpriority to the same value round-trips (equal values are never
|
||||||
|
* a denied raise).
|
||||||
|
* 6. nice(0) returns the current nice value: no change, no error.
|
||||||
|
* 7. Constant sanity: RLIM_INFINITY is ~0UL, RLIMIT_NLIMITS is 16 and
|
||||||
|
* PRIO_MIN/PRIO_MAX bound the range [-20, 19].
|
||||||
|
*
|
||||||
|
* The -f mode runs the failure shapes — setrlimit/getrusage/getpriority/
|
||||||
|
* setpriority with a bogus resource kind or `which` — asserting return
|
||||||
|
* values only (errno is never read: vlibc's errno slot collides with the
|
||||||
|
* host TCB). It leaves via a raw SYS_exit_group so no host cleanup runs
|
||||||
|
* after the library's errno writes (house pattern, tests/test_systime.c).
|
||||||
|
*
|
||||||
|
* No host libc headers are included (the -Iinclude path would shadow
|
||||||
|
* GCC's internal headers); diagnostics go through raw SYS_write. The whole
|
||||||
|
* body mirrors the header's gate: <sys/resource.h> is entirely level 2, so
|
||||||
|
* at level 1 this TU compiles to a no-op runner.
|
||||||
|
*
|
||||||
|
* Standalone build (list the src/resource/ files individually, or the
|
||||||
|
* shell glob would trip -Wcomment in this header block):
|
||||||
|
* gcc -Iinclude -DVLIBC_LEVEL=2 -std=c23 -Wall -Wextra -pedantic -O2 \
|
||||||
|
* -o /tmp/t31 tests/test_resource.c \
|
||||||
|
* src/resource/getrlimit.c src/resource/setrlimit.c \
|
||||||
|
* src/resource/getrusage.c src/resource/getpriority.c \
|
||||||
|
* src/resource/setpriority.c src/resource/nice.c \
|
||||||
|
* src/internal/errno.c src/internal/syscall_ret.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "../src/internal/syscall.h"
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if VLIBC_LEVEL_GE(2)
|
||||||
|
|
||||||
|
static int failures;
|
||||||
|
|
||||||
|
static void
|
||||||
|
check(int ok, const char *msg)
|
||||||
|
{
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
say(1, "ok ");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
say(1, "FAIL ");
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
say(1, msg);
|
||||||
|
say(1, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. getrlimit reads sane, finite NOFILE and STACK soft limits. */
|
||||||
|
static void
|
||||||
|
rlimit_read_scenario(void)
|
||||||
|
{
|
||||||
|
struct rlimit rl;
|
||||||
|
|
||||||
|
check(getrlimit(RLIMIT_NOFILE, &rl) == 0, "getrlimit(RLIMIT_NOFILE) returns 0");
|
||||||
|
check(rl.rlim_cur > 0, "getrlimit NOFILE rlim_cur is positive");
|
||||||
|
check(rl.rlim_max > 0 && rl.rlim_max >= rl.rlim_cur,
|
||||||
|
"getrlimit NOFILE rlim_max is positive and at least rlim_cur");
|
||||||
|
|
||||||
|
check(getrlimit(RLIMIT_STACK, &rl) == 0, "getrlimit(RLIMIT_STACK) returns 0");
|
||||||
|
check(rl.rlim_cur > 0, "getrlimit STACK rlim_cur is positive");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Re-installing the same limit pair succeeds and round-trips. */
|
||||||
|
static void
|
||||||
|
setrlimit_roundtrip_scenario(void)
|
||||||
|
{
|
||||||
|
struct rlimit before;
|
||||||
|
struct rlimit after;
|
||||||
|
|
||||||
|
if (getrlimit(RLIMIT_NOFILE, &before) != 0)
|
||||||
|
{
|
||||||
|
check(0, "getrlimit(RLIMIT_NOFILE) for the round-trip returns 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
check(setrlimit(RLIMIT_NOFILE, &before) == 0, "setrlimit of identical NOFILE pair returns 0");
|
||||||
|
check(getrlimit(RLIMIT_NOFILE, &after) == 0, "getrlimit after setrlimit returns 0");
|
||||||
|
check(after.rlim_cur == before.rlim_cur && after.rlim_max == before.rlim_max,
|
||||||
|
"getrlimit reflects the re-installed NOFILE pair");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3. The LFS aliases read and write exactly like the base functions. */
|
||||||
|
static void
|
||||||
|
rlimit64_alias_scenario(void)
|
||||||
|
{
|
||||||
|
struct rlimit a;
|
||||||
|
struct rlimit b;
|
||||||
|
|
||||||
|
check(getrlimit64(RLIMIT_NOFILE, &a) == 0, "getrlimit64(RLIMIT_NOFILE) returns 0");
|
||||||
|
check(getrlimit(RLIMIT_NOFILE, &b) == 0, "getrlimit(RLIMIT_NOFILE) returns 0");
|
||||||
|
check(a.rlim_cur == b.rlim_cur && a.rlim_max == b.rlim_max,
|
||||||
|
"getrlimit64 agrees with getrlimit");
|
||||||
|
check(setrlimit64(RLIMIT_NOFILE, &a) == 0, "setrlimit64 of identical pair returns 0");
|
||||||
|
check(getrlimit(RLIMIT_NOFILE, &b) == 0, "getrlimit after setrlimit64 returns 0");
|
||||||
|
check(b.rlim_cur == a.rlim_cur && b.rlim_max == a.rlim_max,
|
||||||
|
"setrlimit64 write is visible through getrlimit");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. getrusage reports sane accounting for the process and the thread. */
|
||||||
|
static void
|
||||||
|
getrusage_scenario(void)
|
||||||
|
{
|
||||||
|
struct rusage ru;
|
||||||
|
|
||||||
|
check(getrusage(RUSAGE_SELF, &ru) == 0, "getrusage(RUSAGE_SELF) returns 0");
|
||||||
|
check(ru.ru_utime.tv_sec >= 0, "getrusage ru_utime.tv_sec is non-negative");
|
||||||
|
check(ru.ru_utime.tv_usec >= 0 && ru.ru_utime.tv_usec < 1000000,
|
||||||
|
"getrusage ru_utime.tv_usec is in [0, 1e6)");
|
||||||
|
check(ru.ru_maxrss >= 0, "getrusage ru_maxrss is non-negative");
|
||||||
|
|
||||||
|
check(getrusage(RUSAGE_THREAD, &ru) == 0, "getrusage(RUSAGE_THREAD) returns 0");
|
||||||
|
check(ru.ru_utime.tv_sec >= 0, "getrusage THREAD ru_utime.tv_sec is non-negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 5. getpriority/setpriority round-trip on the current process. */
|
||||||
|
static void
|
||||||
|
priority_roundtrip_scenario(void)
|
||||||
|
{
|
||||||
|
int p;
|
||||||
|
|
||||||
|
p = getpriority(PRIO_PROCESS, 0);
|
||||||
|
check(p >= PRIO_MIN && p <= PRIO_MAX,
|
||||||
|
"getpriority(PRIO_PROCESS, 0) is in [PRIO_MIN, PRIO_MAX]");
|
||||||
|
check(setpriority(PRIO_PROCESS, 0, p) == 0, "setpriority to the current value returns 0");
|
||||||
|
check(getpriority(PRIO_PROCESS, 0) == p, "getpriority round-trips through setpriority");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 6. nice(0) reports the current nice value without changing it. */
|
||||||
|
static void
|
||||||
|
nice_scenario(void)
|
||||||
|
{
|
||||||
|
int cur;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
cur = getpriority(PRIO_PROCESS, 0);
|
||||||
|
check(cur >= PRIO_MIN && cur <= PRIO_MAX, "nice baseline is in [PRIO_MIN, PRIO_MAX]");
|
||||||
|
n = nice(0);
|
||||||
|
check(n == cur, "nice(0) returns the current nice value");
|
||||||
|
check(getpriority(PRIO_PROCESS, 0) == cur, "nice(0) left the nice value unchanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 7. Constant sanity. */
|
||||||
|
static void
|
||||||
|
constant_scenario(void)
|
||||||
|
{
|
||||||
|
check(RLIM_INFINITY == ~0UL, "RLIM_INFINITY is ~0UL");
|
||||||
|
check(RLIMIT_NLIMITS == 16, "RLIMIT_NLIMITS is 16");
|
||||||
|
check(PRIO_MIN == -20 && PRIO_MAX == 19, "PRIO_MIN..PRIO_MAX is -20..19");
|
||||||
|
check(RUSAGE_SELF == 0 && RUSAGE_CHILDREN == -1 && RUSAGE_THREAD == 1,
|
||||||
|
"RUSAGE_SELF/CHILDREN/THREAD are 0/-1/1");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The failure shapes; return values only, never errno. Exit via a 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, tests/test_systime.c). */
|
||||||
|
static void
|
||||||
|
failure_scenarios(void)
|
||||||
|
{
|
||||||
|
struct rlimit rl;
|
||||||
|
struct rusage ru;
|
||||||
|
|
||||||
|
rl.rlim_cur = 0;
|
||||||
|
rl.rlim_max = 0;
|
||||||
|
|
||||||
|
check(setrlimit(9999, &rl) == -1, "-f setrlimit(9999) returns -1");
|
||||||
|
check(getrlimit(9999, &rl) == -1, "-f getrlimit(9999) returns -1");
|
||||||
|
check(getrusage(999, &ru) == -1, "-f getrusage(999) returns -1");
|
||||||
|
check(getpriority(999, 0) == -1, "-f getpriority(999, 0) returns -1");
|
||||||
|
check(setpriority(999, 0, 0) == -1, "-f setpriority(999, 0, 0) returns -1");
|
||||||
|
|
||||||
|
if (failures == 0)
|
||||||
|
{
|
||||||
|
say(1, "all resource failure scenarios passed\n");
|
||||||
|
}
|
||||||
|
__syscall1(SYS_exit_group, failures == 0 ? 0 : 1);
|
||||||
|
/* not reached */
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f' && argv[1][2] == '\0')
|
||||||
|
{
|
||||||
|
failure_scenarios();
|
||||||
|
}
|
||||||
|
|
||||||
|
rlimit_read_scenario();
|
||||||
|
setrlimit_roundtrip_scenario();
|
||||||
|
rlimit64_alias_scenario();
|
||||||
|
getrusage_scenario();
|
||||||
|
priority_roundtrip_scenario();
|
||||||
|
nice_scenario();
|
||||||
|
constant_scenario();
|
||||||
|
|
||||||
|
if (failures == 0)
|
||||||
|
{
|
||||||
|
say(1, "all resource tests passed\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
say(1, "FAILURES\n");
|
||||||
|
}
|
||||||
|
return failures == 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !VLIBC_LEVEL_GE(2) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Level 1: every <sys/resource.h> symbol is gated at level 2, so there is
|
||||||
|
* nothing to run. Keep the TU compilable at any configured profile.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
say(1, "SKIP: <sys/resource.h> is level 2, not available here\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* VLIBC_LEVEL_GE(2) */
|
||||||
Reference in New Issue
Block a user