fix(libc): honor openlog identity and facility

This commit is contained in:
MarcZX100 2026-10-13 09:36:22 +00:00
parent 77b9f7270c
commit 653c5449f9
3 changed files with 59 additions and 4 deletions

View File

@ -4,9 +4,13 @@
#include <cstddef>
#include <cstdio>
#include <mutex>
#include <optional>
#include <string>
#ifdef _WIN32
#include "prx/libc/include/WindowsFormatting.hpp"
#include <process.h>
#else
#include <unistd.h>
#endif
extern "C" {
@ -20,7 +24,11 @@ constexpr int priorityMask = 0x07;
constexpr int facilityMask = 0x3f8;
constexpr int userFacility = 1 << 3;
constexpr int internalLogPriority = 3 | 0x02 | 0x20 | 0x01;
constexpr int logPid = 0x01;
int logMask = 0xff;
int logOptions = 0;
int logFacility = userFacility;
std::optional<std::string> logIdentity;
std::mutex& OutputMutex() {
static std::mutex mutex;
@ -28,7 +36,17 @@ std::mutex& OutputMutex() {
}
void Emit(int priority, const std::string& message) {
std::fprintf(stderr, "[syslog:%d] %s", priority, message.c_str());
std::fprintf(stderr, "[syslog:%d] ", priority);
if (logIdentity) std::fputs(logIdentity->c_str(), stderr);
if (logOptions & logPid) {
#ifdef _WIN32
std::fprintf(stderr, "[%d]", ::_getpid());
#else
std::fprintf(stderr, "[%d]", static_cast<int>(::getpid()));
#endif
}
if (logIdentity) std::fputs(": ", stderr);
std::fputs(message.c_str(), stderr);
if (message.empty() || message.back() != '\n') std::fputc('\n', stderr);
std::fflush(stderr);
}
@ -71,6 +89,18 @@ std::string Format(const char* format, VaList* args) {
}
extern "C" {
void APS5_VABI openlog_nid_postfix(const char* identity, int options, int facility) {
std::lock_guard lock(OutputMutex());
if (identity) logIdentity = identity;
logOptions = options;
if (facility != 0 && (facility & ~facilityMask) == 0) logFacility = facility;
}
void APS5_VABI closelog_nid_postfix() {
std::lock_guard lock(OutputMutex());
logIdentity.reset();
}
int APS5_VABI setlogmask_nid_postfix(int mask) {
std::lock_guard lock(OutputMutex());
const int previous = logMask;
@ -90,7 +120,7 @@ void APS5_VABI vsyslog_nid_postfix(int priority, const char* format, VaList* arg
priority &= priorityMask | facilityMask;
}
if (logMask & (1 << (priority & priorityMask))) {
if ((priority & facilityMask) == 0) priority |= userFacility;
if ((priority & facilityMask) == 0) priority |= logFacility;
Emit(priority, format ? Format(ExpandErrorText(format, saved).c_str(), args) : std::string());
}
*__error_nid_postfix() = saved;

View File

@ -2,15 +2,19 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <thread>
#ifdef _WIN32
#include <io.h>
#include <process.h>
#else
#include <unistd.h>
#endif
extern "C" {
void APS5_VABI syslog_nid_postfix(int, const char*, ...);
void APS5_VABI openlog_nid_postfix(const char*, int, int);
void APS5_VABI closelog_nid_postfix();
int APS5_VABI setlogmask_nid_postfix(int);
int* APS5_VABI __error_nid_postfix();
}
@ -60,6 +64,15 @@ int main() {
Require(setlogmask_nid_postfix(0) == 0xff);
syslog_nid_postfix(7, "mask shared across threads");
syslog_nid_postfix(7, "wide %ls %d %S", u"guest", 5, u"text");
openlog_nid_postfix("worker", 1, 16 << 3);
syslog_nid_postfix(6, "configured default facility");
openlog_nid_postfix(nullptr, 1, 0);
syslog_nid_postfix(6, "null identity keeps tag");
openlog_nid_postfix("daemon", 0, 24 << 3);
syslog_nid_postfix(16 | 3, "explicit facility");
openlog_nid_postfix("closing", 1, 0);
closelog_nid_postfix();
syslog_nid_postfix(6, "identity cleared, options and facility retained");
std::fflush(stderr);
#ifdef _WIN32
Require(::_dup2(saved, ::_fileno(stderr)) == 0 && ::_close(saved) == 0);
@ -86,6 +99,18 @@ int main() {
"[syslog:22] level 6\n"
"[syslog:15] mask shared across threads\n"
"[syslog:15] wide guest 5 text\n";
Require(length == std::strlen(expected) && std::strcmp(text, expected) == 0);
const std::string prefix = expected;
const std::string pid = std::to_string(
#ifdef _WIN32
::_getpid()
#else
::getpid()
#endif
);
const std::string configured = "[syslog:134] worker[" + pid + "]: configured default facility\n"
"[syslog:134] worker[" + pid + "]: null identity keeps tag\n"
"[syslog:19] daemon: explicit facility\n"
"[syslog:198] [" + pid + "] identity cleared, options and facility retained\n";
Require(length == prefix.size() + configured.size() && std::string(text, length) == prefix + configured);
Require(std::fclose(captured) == 0);
}

View File

@ -57,7 +57,7 @@
- [localtime, mktime](../../../core/libs/prx/libc/src/Time.cpp) (libc) on Windows convert through the CRT's `localtime_s` and `mktime`, which accept about 1970 to January 3001 UTC and apply the CRT's daylight-saving rule. A time outside that range is converted a whole number of 400-year Gregorian cycles away, where the calendar repeats, so it gets the rule the CRT applies to that year and not the zone's historical one. `gmtime` converts every time whose year fits an `int` on both hosts.
- [strftime](../../../core/libs/prx/libc/src/Time.cpp) (libc) expands FreeBSD's `%k`, `%l`, `%s`, `%v` and `%+` itself, ignoring a leading `E` or `O` as FreeBSD does. Unknown conversions, repeated modifiers and incomplete conversions emit literals as in FreeBSD 11; recognized conversions otherwise go to the host's `strftime`. On Windows the `%-`, `%_` and `%0` padding flags still produce nothing, including on the five expanded conversions (`%-k`, `%_k` and `%0k` go to the host), and a standalone `%z` or `%Z` uses the host's zone instead of the broken-down time's `tm_zone`. `%s` follows FreeBSD 9 and 11 and is the local-time `mktime` of the broken-down time (current FreeBSD uses `tm_gmtoff`), which is -1 on Windows outside the range above. `%+` is the C locale's `%a %b %e %H:%M:%S %Z %Y` with `%Z` taken from `tm_zone`, or from `tzname[tm_isdst != 0]` when it is null.
- [wcsftime](../../../core/libs/prx/libc/src/Time.cpp) (libc) follows FreeBSD 11.4's [wcsftime.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/locale/wcsftime.c) over this library's `wcsrtombs`, `strftime` and `mbsrtowcs` in the single-byte C locale of [none.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/locale/none.c), with the guest's 16-bit `wchar_t`, without console verification. It has the `strftime` gaps above. A format unit above 255 returns 0 with `EILSEQ`; an empty result, a result that does not fit and a null buffer return 0 with `errno` unchanged, discarding the `ERANGE` or `EINVAL` that the Windows CRT's `strftime` sets. A null format or `tm` throws, where FreeBSD dereferences it (a null `tm` is read there only when the format has a conversion that needs it)
- [syslog](../../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER`, and errno is preserved. `setlogmask` follows [FreeBSD 11](https://github.com/freebsd/freebsd-src/blob/8d2708de5d6112a492744d367cfdfb8fd219227a/lib/libc/gen/syslog.c): the process-wide mask defaults to 0xff, zero queries it, and nonzero values replace it; masked messages are skipped before formatting, including the unknown-priority diagnostic. The console's mask behaviour is unverified. `openlog` and `closelog` are not implemented, so their identity and options are not applied
- [syslog](../../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER` or the facility set by `openlog`, and errno is preserved. `openlog` applies its identity and `LOG_PID`; a null identity retains the previous one, and a valid nonzero facility replaces the default. `closelog` clears the identity but retains the options and facility, as in FreeBSD 11. `LOG_PERROR` has no extra effect because stderr is the emulated log sink; `LOG_NDELAY`/`LOG_ODELAY`, `LOG_NOWAIT` and `LOG_CONS` have no daemon, child or console backend to control. `setlogmask` follows [FreeBSD 11](https://github.com/freebsd/freebsd-src/blob/8d2708de5d6112a492744d367cfdfb8fd219227a/lib/libc/gen/syslog.c): the process-wide mask defaults to 0xff, zero queries it, and nonzero values replace it; masked messages are skipped before formatting, including the unknown-priority diagnostic. The console's mask behaviour is unverified
- [daemon](../../../core/libs/prx/libc/src/Process.cpp) (libc) throws: forking into the background is not supported. PPSA85300 imports it and fails to load without it. VLC 3.0 calls it only when its `daemon` option is set ([libvlc.c](https://github.com/videolan/vlc/blob/6de05adcbaf2e8b85fe86aad4169393098628119/src/libvlc.c#L190-L197)), and the title starts libVLC with `--ignore-config` and without `--daemon` ([player.cc](https://github.com/ZiZc3/VLC/blob/f3253020a64368153233ede29183014c0b6c8979/src/player.cc#L960-L981))
- [_WStoul](../../../core/libs/prx/libc/src/Strings.cpp), [_Iswctype](../../../core/libs/prx/libc/src/RuneLocale.cpp) and [swscanf_s](../../../core/libs/prx/libc/src/ScanningWide.cpp) (libc) - `_WStoul` is `wcstoul` and throws for a base other than 0 or 2 to 36. `_Iswctype` takes the class numbers of Dinkumware's [xwcc.h](https://github.com/superna9999/ti-cgt6x/blob/cgt6x_7_3_4/include/xwcc.h) (1 `alnum` to 11 `xdigit`, 12 `blank`) and throws for any other value, including the FreeBSD masks `wctype` returns; the numbering is not confirmed on the console's `libc.prx`. `swscanf_s` follows C11 7.29.2.2 and K.3.9.1.5, but reads the size argument as `unsigned int` like `sscanf_s` where K.3.9.1.5 specifies `rsize_t`; the console's width is unverified. A runtime-constraint violation (null buffer, format or receiving pointer) only returns `EOF`, and a too small array gets a null first element as in `sscanf_s`. A literal mismatch is a matching failure and a field that is only a prefix, such as `0x` or `1e`, does not convert, as C11 requires; FreeBSD's `vfwscanf` reports an input failure for the first and converts the second. Integers out of range are truncated from `strtoll`/`strtoull`. It throws for `%p`, `%S`, `%C`, unknown length modifiers, scanset ranges, `%n` with suppression or width, and narrow `%s`, `%c` or `%[` input that the C-locale `wcrtomb` cannot convert
- [flock](../../../core/libs/prx/libc/src/FileLock.cpp) (libkernel) on Windows shares one lock state between duplicated descriptors, following FreeBSD 11.4's [flock(2)](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/sys/flock.2). Independent opens have separate states; closing a descriptor or reopening a stream drops only its reference. Lock transitions on one open file description are serialized. The host's [LockFileEx](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex) locks also restrict ordinary reads and writes through other handles, unlike FreeBSD's advisory locks, and do not restrict mapped views. Console behavior, signal interruption and concurrent closure of a descriptor while its lock call is pending are unverified.