diff --git a/Libraries/LibC/Makefile b/Libraries/LibC/Makefile index fb115390e3..6c301e96ec 100644 --- a/Libraries/LibC/Makefile +++ b/Libraries/LibC/Makefile @@ -53,7 +53,8 @@ LIBC_OBJS = \ sched.o \ dlfcn.o \ libgen.o \ - wchar.o + wchar.o \ + syslog.o ASM_OBJS = setjmp.ao crti.ao crtn.ao diff --git a/Libraries/LibC/syslog.cpp b/Libraries/LibC/syslog.cpp new file mode 100644 index 0000000000..b9c816e70b --- /dev/null +++ b/Libraries/LibC/syslog.cpp @@ -0,0 +1,132 @@ +// Has to be defined before including due to legacy Unices +#define SYSLOG_NAMES 1 + +#include +#include +#include +#include +#include + +// This implementation doesn't talk to a syslog server. Any options related to +// that are no-ops. + +extern "C" { + +// For implementation simplicity, we actually only use the re-entrant version +// of each function, and the version that isn't just redirects with a static +// struct to share. +static struct syslog_data global_log_data = { + .ident = nullptr, + .logopt = 0, + .facility = LOG_USER, + .maskpri = LOG_UPTO(LOG_DEBUG) +}; + +// Used when ident is null, since syslog traditionally prints the program's +// own name; the process name will always be the same unless we exec. +static char program_name_buffer[256]; +static bool program_name_set = false; + +// Convenience function for initialization and checking what string to use +// for the program name. +static const char* get_syslog_ident(struct syslog_data* data) +{ + if (!program_name_set && data->ident == nullptr) + program_name_set = get_process_name(program_name_buffer, sizeof(program_name_buffer)) >= 0; + + if (data->ident != nullptr) + return data->ident; + else if (program_name_set) + return program_name_buffer; + + ASSERT_NOT_REACHED(); +} + +void openlog_r(const char* ident, int logopt, int facility, struct syslog_data* data) +{ + data->ident = ident; + data->logopt = logopt; + data->facility = facility; + // default value + data->maskpri = LOG_UPTO(LOG_DEBUG); + // would be where we connect to a daemon +} + +void openlog(const char* ident, int logopt, int facility) +{ + openlog_r(ident, logopt, facility, &global_log_data); +} + +void closelog_r(struct syslog_data* data) +{ + // would be where we disconnect from a daemon + // restore defaults + data->ident = nullptr; + data->logopt = 0; + data->facility = LOG_USER; + data->maskpri = LOG_UPTO(LOG_DEBUG); +} + +void closelog(void) +{ + closelog_r(&global_log_data); +} + +int setlogmask_r(int maskpri, struct syslog_data* data) +{ + // Remember, this takes the input of LOG_MASK/LOG_UPTO + int old_maskpri = data->maskpri; + data->maskpri = maskpri; + return old_maskpri; +} + +int setlogmask(int maskpri) +{ + return setlogmask_r(maskpri, &global_log_data); +} + +void syslog_r(int priority, struct syslog_data* data, const char* message, ...) +{ + va_list ap; + va_start(ap, message); + vsyslog_r(priority, data, message, ap); + va_end(ap); +} + +void syslog(int priority, const char* message, ...) +{ + va_list ap; + va_start(ap, message); + vsyslog_r(priority, &global_log_data, message, ap); + va_end(ap); +} + +void vsyslog_r(int priority, struct syslog_data* data, const char* message, va_list args) +{ + StringBuilder combined; + + int real_priority = LOG_PRI(priority); + // Lots of parens, but it just extracts the priority from combo and masks. + if (!(data->maskpri & LOG_MASK(real_priority))) + return; + + // Some metadata would be consumed by a syslog daemon, if we had one. + if (data->logopt & LOG_PID) + combined.appendf("%s[%d]: ", get_syslog_ident(data), getpid()); + else + combined.appendf("%s: ", get_syslog_ident(data)); + + combined.appendvf(message, args); + String combined_string = combined.build(); + + if (data->logopt & LOG_CONS) + dbgputstr(combined_string.characters(), combined_string.length()); + if (data->logopt & LOG_PERROR) + fputs(combined_string.characters(), stderr); +} + +void vsyslog(int priority, const char* message, va_list args) +{ + vsyslog_r(priority, &global_log_data, message, args); +} +} diff --git a/Libraries/LibC/syslog.h b/Libraries/LibC/syslog.h new file mode 100644 index 0000000000..a5a9d73862 --- /dev/null +++ b/Libraries/LibC/syslog.h @@ -0,0 +1,151 @@ +#pragma once + +#include + +__BEGIN_DECLS + +struct syslog_data { + const char* ident; + int logopt; + int facility; + int maskpri; +}; + +/* The severity of the message. This is ordered. */ +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +/* Macros for masking out the priority of a combined priority */ +#define LOG_PRIMASK (7) +#define LOG_PRI(priority) ((priority) & LOG_PRIMASK) + +/* + * Many of these facilities don't really make sense anymore, but we keep them + * for compatability purposes. + */ +#define LOG_KERN ( 0 << 3) +#define LOG_USER ( 1 << 3) +#define LOG_MAIL ( 2 << 3) +#define LOG_DAEMON ( 3 << 3) +#define LOG_AUTH ( 4 << 3) +#define LOG_SYSLOG ( 5 << 3) +#define LOG_LPR ( 6 << 3) +#define LOG_NEWS ( 7 << 3) +#define LOG_UUCP ( 8 << 3) +#define LOG_CRON ( 9 << 3) +#define LOG_AUTHPRIV (10 << 3) +#define LOG_FTP (11 << 3) +/* glibc and OpenBSD reserve 12..15 for future system usage, we will too */ +#define LOG_LOCAL0 (16 << 3) +#define LOG_LOCAL1 (17 << 3) +#define LOG_LOCAL2 (18 << 3) +#define LOG_LOCAL3 (19 << 3) +#define LOG_LOCAL4 (20 << 3) +#define LOG_LOCAL5 (21 << 3) +#define LOG_LOCAL6 (22 << 3) +#define LOG_LOCAL7 (23 << 3) + +#define LOG_NFACILITIES 24 + +/* Macros to get the facility from a combined priority. */ +#define LOG_FACMASK (~7) +#define LOG_FAC(priority) (((priority) & LOG_FACMASK) >> 3) + +/* For masking logs, we use these macros with just the priority. */ +#define LOG_MASK(priority) (1 << (priority)) +#define LOG_UPTO(priority) (LOG_MASK(priority) + (LOG_MASK(priority) - 1)) + +/* Macro to make a combined priority. */ +#define LOG_MAKEPRI(facility, priority) ((facility) | (priority)) + +/* Include a PID with the message. */ +#define LOG_PID (1 << 0) +/* Log on the console. */ +#define LOG_CONS (1 << 1) +/* Open the syslogd connection at the first call. (not implemented, default) */ +#define LOG_ODELAY (1 << 2) +/* Open the syslogd connection immediately. (not implemented) */ +#define LOG_NDELAY (1 << 3) +/* Log to stderr as well. */ +#define LOG_PERROR (1 << 4) + +/* This is useful to have, but has to be stored weirdly for compatibility. */ +#ifdef SYSLOG_NAMES +/* Used for marking the fallback; some applications check for these defines. */ +# define INTERNAL_NOPRI 0x10 +# define INTERNAL_MARK LOG_MAKEPRI(LOG_NFACILITIES << 3, 0) + +typedef struct _code { + /* + * Most Unices define this as char*, but in C++, we have to define it as a + * const char* if we want to use string constants. + */ + const char* c_name; + int c_val; +} CODE; + +/* + * The names we use are the same as what glibc and OpenBSD use. We omit + * deprecated values in the hope that no one uses them. Sorted, as well. + */ + +CODE prioritynames[] = { + { "alert", LOG_ALERT }, + { "crit", LOG_CRIT }, + { "debug", LOG_DEBUG }, + { "emerg", LOG_EMERG }, + { "err", LOG_ERR }, + { "info", LOG_INFO }, + /* Fallback */ + { "none", INTERNAL_NOPRI }, + { "notice", LOG_NOTICE }, + { "warning", LOG_WARNING }, + { NULL, -1 }, +}; + +CODE facilitynames[] = { + { "auth", LOG_AUTH }, + { "authpriv", LOG_AUTHPRIV }, + { "cron", LOG_CRON }, + { "daemon", LOG_DAEMON }, + { "ftp", LOG_FTP }, + { "kern", LOG_KERN }, + { "local0", LOG_LOCAL0 }, + { "local1", LOG_LOCAL1 }, + { "local2", LOG_LOCAL2 }, + { "local3", LOG_LOCAL3 }, + { "local4", LOG_LOCAL4 }, + { "local5", LOG_LOCAL5 }, + { "local6", LOG_LOCAL6 }, + { "local7", LOG_LOCAL7 }, + { "lpr", LOG_LPR }, + { "mail", LOG_MAIL }, + /* Fallback */ + { "mark", INTERNAL_MARK }, + { "news", LOG_NEWS }, + { "syslog", LOG_SYSLOG }, + { "user", LOG_USER }, + { "uucp", LOG_UUCP }, + { NULL, -1 }, +}; +#endif + +/* The re-entrant versions are an OpenBSD extension we also implement. */ +void syslog(int, const char*, ...); +void syslog_r(int, struct syslog_data*, const char*, ...); +void vsyslog(int, const char* message, va_list); +void vsyslog_r(int, struct syslog_data* data, const char* message, va_list); +void openlog(const char*, int, int); +void openlog_r(const char*, int, int, struct syslog_data*); +void closelog(void); +void closelog_r(struct syslog_data*); +int setlogmask(int); +int setlogmask_r(int, struct syslog_data*); + +__END_DECLS