mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 19:43:32 +00:00
348 lines
12 KiB
C
348 lines
12 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
│ any purpose with or without fee is hereby granted, provided that the │
|
|
│ above copyright notice and this permission notice appear in all copies. │
|
|
│ │
|
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/bits/safemacros.internal.h"
|
|
#include "libc/calls/calls.h"
|
|
#include "libc/calls/weirdtypes.h"
|
|
#include "libc/dce.h"
|
|
#include "libc/errno.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/log/internal.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/nt/events.h"
|
|
#include "libc/nt/runtime.h"
|
|
#include "libc/runtime/valist.h"
|
|
#include "libc/sock/sock.h"
|
|
#include "libc/sock/syslog.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/af.h"
|
|
#include "libc/sysv/consts/clock.h"
|
|
#include "libc/sysv/consts/log.h"
|
|
#include "libc/sysv/consts/o.h"
|
|
#include "libc/sysv/consts/sock.h"
|
|
#include "libc/time/struct/tm.h"
|
|
#include "libc/time/time.h"
|
|
|
|
/* Note: log_facility should be initialized with LOG_USER by default,
|
|
* but since LOG_USER is not a constant value, we cannot initialize it
|
|
* here. Set it to -1 to force the public functions to call __initlog()
|
|
* for the first time.
|
|
*/
|
|
static int log_facility = -1;
|
|
static char16_t log_ident[32];
|
|
static int log_opt;
|
|
static int log_mask;
|
|
static uint16_t log_id; /* Used for Windows EvtID */
|
|
static int64_t log_fd = -1;
|
|
|
|
static const char *const kLogPaths[] = {
|
|
"/dev/log",
|
|
// "/var/run/log", // TODO: Help with XNU and FreeBSD.
|
|
};
|
|
|
|
static struct sockaddr_un log_addr = {AF_UNIX, "/dev/log"};
|
|
|
|
static int64_t Time(int64_t *tp) {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
if (tp) *tp = ts.tv_sec;
|
|
return ts.tv_sec;
|
|
}
|
|
|
|
static void __initlog() {
|
|
log_ident[0] = '\0';
|
|
log_opt = LOG_ODELAY;
|
|
log_facility = LOG_USER;
|
|
log_mask = LOG_PRI(0xff); // Initially use max verbosity
|
|
log_fd = -1;
|
|
}
|
|
|
|
forceinline int is_lost_conn(int e) {
|
|
return (e == ECONNREFUSED) || (e == ECONNRESET) || (e == ENOTCONN) ||
|
|
(e == EPIPE);
|
|
}
|
|
|
|
static void __openlog() {
|
|
int i;
|
|
if (IsWindows()) {
|
|
log_fd = RegisterEventSource(NULL, log_ident);
|
|
} else {
|
|
log_fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
|
if (log_fd >= 0) {
|
|
for (i = 0; i < ARRAYLEN(kLogPaths); ++i) {
|
|
strcpy(log_addr.sun_path, kLogPaths[i]);
|
|
if (!connect(log_fd, (void *)&log_addr, sizeof(log_addr))) {
|
|
return;
|
|
}
|
|
}
|
|
printf("ERR: connect(openlog) failed: %s (errno=%d)\n", strerror(errno),
|
|
errno);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a log message which will be distributed by syslogd
|
|
*
|
|
* Note: no errors are reported if an error occurred while logging
|
|
* the message.
|
|
*
|
|
* @param priority is a bitmask containing the facility value and
|
|
* the level value. If no facility value is ORed into priority,
|
|
* then the default value set by openlog() is used.
|
|
* it set to NULL, the program name is used.
|
|
* Level is one of LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
|
|
* LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG
|
|
* @param message the format of the message to be processed by vprintf()
|
|
* @param ap the va_list of arguments to be applied to the message
|
|
* @asyncsignalsafe
|
|
*/
|
|
void vsyslog(int priority, const char *message, va_list ap) {
|
|
char timebuf[16]; /* Store formatted time */
|
|
time_t now;
|
|
struct tm tm;
|
|
char buf[1024];
|
|
int errno_save = errno; /* Save current errno */
|
|
int pid;
|
|
int l, l2;
|
|
int hlen; /* If LOG_CONS is specified, use to store the point in
|
|
* the header message after the timestamp */
|
|
|
|
if (log_fd < 0) __openlog();
|
|
|
|
if (!(priority & LOG_FACMASK)) priority |= log_facility;
|
|
|
|
/* Build the time string */
|
|
now = Time(NULL);
|
|
gmtime_r(&now, &tm);
|
|
strftime(timebuf, sizeof(timebuf), "%b %e %T", &tm);
|
|
|
|
pid = (log_opt & LOG_PID) ? getpid() : 0;
|
|
/* This is a clever trick to optionally include "[<pid>]"
|
|
* only if pid != 0. When pid==0, the while "[%.0d]" is skipped:
|
|
* %s%.0d%s -> String, pidValue, String
|
|
* Each of those %s:
|
|
* - if pid == 0 -> !pid is true (1), so "[" + 1 points to the
|
|
* NULL terminator after the "[".
|
|
* - if pid != 0 -> !pid is false (0), so the string printed is
|
|
* the "[".
|
|
*/
|
|
l = snprintf(buf, sizeof(buf), "<%d>%s ", priority, timebuf);
|
|
hlen = l;
|
|
l += snprintf(buf + l, sizeof(buf) - l, "%hs%s%.0d%s: ", log_ident,
|
|
"[" + !pid, pid, "]" + !pid);
|
|
errno = errno_save;
|
|
|
|
/* Append user message */
|
|
l2 = vsnprintf(buf + l, sizeof(buf) - l, message, ap);
|
|
if (l2 >= 0) {
|
|
if (l2 >= sizeof(buf) - l) {
|
|
l = sizeof(buf) - 1;
|
|
} else {
|
|
l += l2;
|
|
}
|
|
if (buf[l - 1] != '\n') {
|
|
buf[l++] = '\n';
|
|
}
|
|
if (!IsWindows()) {
|
|
/* Unix:
|
|
* - First try to send it to syslogd
|
|
* - If fails and LOG_CONS is provided, writes to /dev/console
|
|
*/
|
|
|
|
#if 0
|
|
if (send(log_fd, buf, l, 0) < 0 && (!is_lost_conn(errno)
|
|
|| connect(log_fd, (void *)&log_addr, sizeof(log_addr)) < 0
|
|
|| send(log_fd, buf, l, 0) < 0)
|
|
&& (log_opt & LOG_CONS)) {
|
|
int fd = open("/dev/console", O_WRONLY|O_NOCTTY);
|
|
if (fd >= 0) {
|
|
dprintf(fd, "%.*s", l-hlen, buf+hlen);
|
|
close(fd);
|
|
}
|
|
}
|
|
#else
|
|
int rc = send(log_fd, buf, l, 0);
|
|
if (rc < 0) {
|
|
printf("ERR: send(1) failed: %s (errno=%d)\n", strerror(errno), errno);
|
|
if (!is_lost_conn(errno)) {
|
|
rc = connect(log_fd, (void *)&log_addr, sizeof(log_addr));
|
|
if (rc < 0) {
|
|
printf("ERR: connect(syslog) failed: %s (errno=%d)\n",
|
|
strerror(errno), errno);
|
|
} else {
|
|
rc = send(log_fd, buf, l, 0);
|
|
if (rc < 0) {
|
|
printf("ERR: send(2) failed: %s (errno=%d)\n", strerror(errno),
|
|
errno);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
uint16_t evtType;
|
|
uint32_t evtID;
|
|
const char *bufArr[] = {
|
|
&buf[hlen]}; /* Only print the message without time*/
|
|
/* Windows only have 3 usable event types
|
|
* Event ID are not supported
|
|
* For more information on message types and event IDs, see:
|
|
* https://docs.microsoft.com/en-us/windows/win32/eventlog/event-identifiers
|
|
*/
|
|
priority &= LOG_PRIMASK; // Remove facility from the priority field
|
|
if (priority == LOG_EMERG || priority == LOG_ALERT ||
|
|
priority == LOG_CRIT) {
|
|
evtType = EVENTLOG_ERROR_TYPE;
|
|
evtID = 0xe << 28 | (LOG_USER) << 16;
|
|
} else if (priority == LOG_WARNING) {
|
|
evtType = EVENTLOG_WARNING_TYPE;
|
|
evtID = 0xa << 28 | (LOG_USER) << 16;
|
|
} else {
|
|
/* LOG_NOTICE, LOG_INFO, LOG_DEBUG */
|
|
evtType = EVENTLOG_INFORMATION_TYPE;
|
|
evtID = 0x6 << 28 | (LOG_USER) << 16;
|
|
}
|
|
ReportEventA(log_fd, evtType, /* Derived from priority */
|
|
0 /* Category unsupported */, evtID, /* Unsupported */
|
|
NULL, /* User SID */
|
|
1, /* Number of strings */
|
|
0, /* Raw data size */
|
|
bufArr, /* String(s) */
|
|
NULL /* Arguments */);
|
|
++log_id;
|
|
}
|
|
|
|
if (log_opt & LOG_PERROR) dprintf(2, "%.*s", l - hlen, buf + hlen);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets log priority mask
|
|
*
|
|
* Modifies the log priority mask that determines which calls to
|
|
* syslog() may be logged.
|
|
* Log priority values are LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
|
|
* LOG_WARNING, LOG_NOTICE, LOG_INFO, and LOG_DEBUG.
|
|
*
|
|
* @param mask the new priority mask to use by syslog()
|
|
* @return the previous log priority mask
|
|
* @asyncsignalsafe
|
|
*/
|
|
int setlogmask(int maskpri) {
|
|
int ret;
|
|
if (log_facility == -1) {
|
|
__initlog();
|
|
}
|
|
ret = log_mask;
|
|
if (maskpri) log_mask = LOG_PRI(maskpri);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Opens a connection to the system logger
|
|
*
|
|
* Calling this function before calling syslog() is optional and
|
|
* only allow customizing the identity, options and facility of
|
|
* the messages logged.
|
|
*
|
|
* @param ident a string that prepends every logged message. If
|
|
* it set to NULL, the program name is used.
|
|
* @param opt specifies flags which control the operation of openlog().
|
|
* Only the following flags are supported:
|
|
* LOG_CONS = Write directly to the system console if there is
|
|
* an error while sending to the system logger.
|
|
* LOG_NDELAY = Open the connection with the system logger
|
|
* immediately instead of waiting for the first
|
|
* message to be logged.
|
|
* LOG_ODELAY = The converse of LOG_NDELAY.
|
|
* LOG_PERROR = Also log the message to stderr.
|
|
* LOG_PID = Include the caller's PID with each message
|
|
* @param facility specifies the default facitlity value that defines
|
|
* what kind of program is logging the message.
|
|
* Possible values are: LOG_AUTH, LOG_AUTHPRIV,
|
|
* LOG_CRON, LOG_DAEMON, LOG_FTP, LOG_LOCAL0 through LOG_LOCAL7,
|
|
* LOG_LPR, LOG_MAIL, LOG_NEWS, LOG_SYSLOG, LOG_USER (default),
|
|
* LOG_UUCP.
|
|
* @asyncsignalsafe
|
|
*/
|
|
void openlog(const char *ident, int opt, int facility) {
|
|
size_t n;
|
|
|
|
if (log_facility == -1) {
|
|
__initlog();
|
|
}
|
|
if (!ident) {
|
|
ident = firstnonnull(program_invocation_short_name, "unknown");
|
|
}
|
|
tprecode8to16(log_ident, ARRAYLEN(log_ident), ident);
|
|
log_opt = opt;
|
|
log_facility = facility;
|
|
log_id = 0;
|
|
|
|
if ((opt & LOG_NDELAY) && log_fd < 0) __openlog();
|
|
}
|
|
|
|
/**
|
|
* Generates a log message which will be distributed by syslogd
|
|
*
|
|
* Note: no errors are reported if an error occurred while logging
|
|
* the message.
|
|
*
|
|
* @param priority is a bitmask containing the facility value and
|
|
* the level value. If no facility value is ORed into priority,
|
|
* then the default value set by openlog() is used.
|
|
* it set to NULL, the program name is used.
|
|
* Level is one of LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
|
|
* LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG
|
|
* @param message the message formatted using the same rules as printf()
|
|
* @asyncsignalsafe
|
|
*/
|
|
void syslog(int priority, const char *message, ...) {
|
|
va_list ap;
|
|
if (log_facility == -1) {
|
|
__initlog();
|
|
}
|
|
if (LOG_PRI(priority) <= log_mask) {
|
|
va_start(ap, message);
|
|
vsyslog(priority, message, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the file descriptor being used to write to the system logger
|
|
*
|
|
* Use of closelog is optional
|
|
* @asyncsignalsafe
|
|
*/
|
|
void closelog(void) {
|
|
if (log_facility == -1) {
|
|
__initlog();
|
|
}
|
|
if (log_fd == -1) {
|
|
return;
|
|
}
|
|
if (IsWindows()) {
|
|
DeregisterEventSource(log_fd);
|
|
} else {
|
|
close(log_fd);
|
|
}
|
|
log_fd = -1;
|
|
}
|