diff --git a/include/sys/resource.h b/include/sys/resource.h new file mode 100644 index 0000000..5180202 --- /dev/null +++ b/include/sys/resource.h @@ -0,0 +1,203 @@ +#ifndef VLIBC_SYS_RESOURCE_H +#define VLIBC_SYS_RESOURCE_H + +/* + * vlibc — . + * + * 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 + +#if VLIBC_LEVEL_GE(2) + +#include /* offsetof */ + +#include /* struct timeval (shared VLIBC_TIMEVAL_DEFINED guard) */ + +#include /* 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 */ diff --git a/src/resource/getpriority.c b/src/resource/getpriority.c new file mode 100644 index 0000000..9fc032d --- /dev/null +++ b/src/resource/getpriority.c @@ -0,0 +1,39 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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 . + */ +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) */ diff --git a/src/resource/getrlimit.c b/src/resource/getrlimit.c new file mode 100644 index 0000000..0b5a7f2 --- /dev/null +++ b/src/resource/getrlimit.c @@ -0,0 +1,35 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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) */ diff --git a/src/resource/getrusage.c b/src/resource/getrusage.c new file mode 100644 index 0000000..a546383 --- /dev/null +++ b/src/resource/getrusage.c @@ -0,0 +1,25 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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 + * ), 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) */ diff --git a/src/resource/nice.c b/src/resource/nice.c new file mode 100644 index 0000000..0eef7ba --- /dev/null +++ b/src/resource/nice.c @@ -0,0 +1,47 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#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) */ diff --git a/src/resource/setpriority.c b/src/resource/setpriority.c new file mode 100644 index 0000000..d9a98c9 --- /dev/null +++ b/src/resource/setpriority.c @@ -0,0 +1,27 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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) */ diff --git a/src/resource/setrlimit.c b/src/resource/setrlimit.c new file mode 100644 index 0000000..34cdb43 --- /dev/null +++ b/src/resource/setrlimit.c @@ -0,0 +1,36 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#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) */ diff --git a/tests/test_resource.c b/tests/test_resource.c new file mode 100644 index 0000000..a412e48 --- /dev/null +++ b/tests/test_resource.c @@ -0,0 +1,256 @@ +/* + * vlibc — sys/resource.h test (todo 31). + * + * Exercises the whole 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: 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 + +#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 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: is level 2, not available here\n"); + return 0; +} + +#endif /* VLIBC_LEVEL_GE(2) */