0
0
mirror of https://github.com/termux-pacman/glibc-packages.git synced 2025-08-23 03:52:31 +00:00
Files
glibc-packages/gpkg/glibc/syslog.c
Max Ivan 0bd3559405 gpkg/glibc: global update (#317)
- updated to 2.41
- added the glibc32 subpackage
- updated fakesyscall handling scheme
- moved all disabled syscalls to fakesyscall list
- added new syscalls to fakesyscall list
- disabled removal of old locales in `locale-gen`
- implemented syslog function, which is configured to work with the android log system
- implemented analog variables `LD_*` to customize glibc environment without affecting bionic environment ('GLIBC_LD_*')
- improved source code for flexible glibc compilation
2025-05-31 17:50:16 +03:00

544 lines
14 KiB
C

/*
* Refactored syslog.c that can work with the Android
* log system (/dev/socket/logdw). It consists of functions
* that were taken from the following bionic scripts:
* - syslog.cpp (https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/bionic/syslog.cpp)
* - async_safe_log.cpp (https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/async_safe/async_safe_log.cpp)
*
* Addition: in the future it is planned to implement
* two types of syslog: one will work with android (bionic
* implementation), the other will work with termux+systemd
* environment (glibc implementation).
*/
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/syscall.h>
#include <sys/socket.h>
#include <time.h>
#include <linux/un.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <errno.h>
#include <ctype.h>
#include <assert.h>
#include <libio/libioP.h>
#define ANDROID_LOG_VERBOSE 2 // not used :/
#define ANDROID_LOG_DEBUG 3
#define ANDROID_LOG_INFO 4
#define ANDROID_LOG_WARN 5
#define ANDROID_LOG_ERROR 6
#define ANDROID_LOG_FATAL 7
#define LOG_ID_CRASH 4
#define LOG_ID_MAIN 0
static const char* syslog_log_tag = NULL;
static int syslog_priority_mask = 0xff;
static int syslog_options = 0;
extern char *__progname;
typedef struct log_time {
uint32_t tv_sec;
uint32_t tv_nsec;
} __attribute__((__packed__)) log_time;
struct BufferOutputStream {
size_t total;
char* pos_;
size_t avail_;
};
struct BufferOutputStream BufferOutput(char* buffer, size_t size) {
struct BufferOutputStream buff;
buff.total = 0;
buff.pos_ = buffer;
buff.avail_ = size;
if (buff.avail_ > 0)
buff.pos_[0] = '\0';
return buff;
}
void Send(struct BufferOutputStream* buff, const char* data, int len) {
if (len < 0)
len = strlen(data);
buff->total += len;
if (buff->avail_ <= 1)
// No space to put anything else.
return;
if ((size_t)len >= buff->avail_)
len = buff->avail_ - 1;
memcpy(buff->pos_, data, len);
buff->pos_ += len;
buff->pos_[0] = '\0';
buff->avail_ -= len;
}
static unsigned parse_decimal(const char* format, int* ppos) {
const char* p = format + *ppos;
unsigned result = 0;
for (;;) {
int ch = *p;
unsigned d = (unsigned)(ch - '0');
if (d >= 10U)
break;
result = result * 10 + d;
p++;
}
*ppos = p - format;
return result;
}
static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
char* p = buf;
char* end = buf + buf_size - 1;
// Generate digit string in reverse order.
while (value) {
unsigned d = value % base;
value /= base;
if (p != end) {
char ch;
if (d < 10)
ch = '0' + d;
else
ch = (caps ? 'A' : 'a') + (d - 10);
*p++ = ch;
}
}
// Special case for 0.
if (p == buf)
if (p != end)
*p++ = '0';
*p = '\0';
// Reverse digit string in-place.
size_t length = p - buf;
for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
char ch = buf[i];
buf[i] = buf[j];
buf[j] = ch;
}
}
static void format_integer(char* buf, size_t buf_size, uint64_t value, char conversion) {
// Decode the conversion specifier.
int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
int base = 10;
if (tolower(conversion) == 'x')
base = 16;
else if (conversion == 'o')
base = 8;
else if (tolower(conversion) == 'b')
base = 2;
bool caps = (conversion == 'X');
if (is_signed && (int64_t)value < 0) {
buf[0] = '-';
buf += 1;
buf_size -= 1;
value = (uint64_t)(-(int64_t)value);
}
format_unsigned(buf, buf_size, value, base, caps);
}
static void SendRepeat(struct BufferOutputStream* buff, char ch, int count) {
char pad[8];
memset(pad, ch, sizeof(pad));
const int pad_size = (int)sizeof(pad);
while (count > 0) {
int avail = count;
if (avail > pad_size)
avail = pad_size;
Send(buff, pad, avail);
count -= avail;
}
}
static void out_vformat(struct BufferOutputStream* buff, const char* format, va_list args) {
int nn = 0;
for (;;) {
int mm;
int padZero = 0;
int padLeft = 0;
char sign = '\0';
int width = -1;
int prec = -1;
bool alternate = false;
size_t bytelen = sizeof(int);
int slen;
char buffer[64]; // temporary buffer used to format numbers/format errno string
char c;
/* first, find all characters that are not 0 or '%' */
/* then send them to the output directly */
mm = nn;
do {
c = format[mm];
if (c == '\0' || c == '%') break;
mm++;
} while (1);
if (mm > nn) {
Send(buff, format + nn, mm - nn);
nn = mm;
}
/* is this it ? then exit */
if (c == '\0') break;
/* nope, we are at a '%' modifier */
nn++; // skip it
/* parse flags */
for (;;) {
c = format[nn++];
if (c == '\0') { /* single trailing '%' ? */
c = '%';
Send(buff, &c, 1);
return;
} else if (c == '0') {
padZero = 1;
continue;
} else if (c == '-') {
padLeft = 1;
continue;
} else if (c == ' ' || c == '+') {
sign = c;
continue;
} else if (c == '#') {
alternate = true;
continue;
}
break;
}
/* parse field width */
if ((c >= '0' && c <= '9')) {
nn--;
width = (int)parse_decimal(format, &nn);
c = format[nn++];
}
/* parse precision */
if (c == '.') {
prec = (int)parse_decimal(format, &nn);
c = format[nn++];
}
/* length modifier */
switch (c) {
case 'h':
bytelen = sizeof(short);
if (format[nn] == 'h') {
bytelen = sizeof(char);
nn += 1;
}
c = format[nn++];
break;
case 'l':
bytelen = sizeof(long);
if (format[nn] == 'l') {
bytelen = sizeof(long long);
nn += 1;
}
c = format[nn++];
break;
case 'z':
bytelen = sizeof(size_t);
c = format[nn++];
break;
case 't':
bytelen = sizeof(ptrdiff_t);
c = format[nn++];
break;
default:;
}
/* conversion specifier */
const char* str = buffer;
if (c == 's') {
/* string */
str = va_arg(args, const char*);
} else if (c == 'c') {
/* character */
/* NOTE: char is promoted to int when passed through the stack */
buffer[0] = (char)va_arg(args, int);
buffer[1] = '\0';
} else if (c == 'p') {
uint64_t value = (uintptr_t)va_arg(args, void*);
buffer[0] = '0';
buffer[1] = 'x';
format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
} else if (c == 'm') {
{
strerror_r(errno, buffer, sizeof(buffer));
}
} else if (tolower(c) == 'b' || c == 'd' || c == 'i' || c == 'o' || c == 'u' ||
tolower(c) == 'x') {
/* integers - first read value from stack */
uint64_t value;
int is_signed = (c == 'd' || c == 'i' || c == 'o');
/* NOTE: int8_t and int16_t are promoted to int when passed
* through the stack
*/
switch (bytelen) {
case 1:
value = (uint8_t)va_arg(args, int);
break;
case 2:
value = (uint16_t)va_arg(args, int);
break;
case 4:
value = va_arg(args, uint32_t);
break;
case 8:
value = va_arg(args, uint64_t);
break;
default:
return; /* should not happen */
}
/* sign extension, if needed */
if (is_signed) {
int shift = 64 - 8 * bytelen;
value = (uint64_t)(((int64_t)(value << shift)) >> shift);
}
if (alternate && value != 0 && (tolower(c) == 'x' || c == 'o' || tolower(c) == 'b')) {
if (tolower(c) == 'x' || tolower(c) == 'b') {
buffer[0] = '0';
buffer[1] = c;
format_integer(buffer + 2, sizeof(buffer) - 2, value, c);
} else {
buffer[0] = '0';
format_integer(buffer + 1, sizeof(buffer) - 1, value, c);
}
} else
/* format the number properly into our buffer */
format_integer(buffer, sizeof(buffer), value, c);
} else if (c == '%') {
buffer[0] = '%';
buffer[1] = '\0';
} else
__assert("conversion specifier unsupported", __FILE__, __LINE__);
if (str == NULL)
str = "(null)";
/* if we are here, 'str' points to the content that must be
* outputted. handle padding and alignment now */
slen = strlen(str);
if (sign != '\0' || prec != -1)
__assert("sign/precision unsupported", __FILE__, __LINE__);
if (slen < width && !padLeft) {
char padChar = padZero ? '0' : ' ';
SendRepeat(buff, padChar, width - slen);
}
Send(buff, str, slen);
if (slen < width && padLeft) {
char padChar = padZero ? '0' : ' ';
SendRepeat(buff, padChar, width - slen);
}
}
}
static int open_log_socket(void) {
// ToDo: Ideally we want this to fail if the gid of the current
// process is AID_LOGD, but will have to wait until we have
// registered this in private/android_filesystem_config.h. We have
// found that all logd crashes thus far have had no problem stuffing
// the UNIX domain socket and moving on so not critical *today*.
int log_fd = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
if (log_fd == -1)
return -1;
union {
struct sockaddr addr;
struct sockaddr_un addrUn;
} u;
memset(&u, 0, sizeof(u));
u.addrUn.sun_family = AF_UNIX;
strlcpy(u.addrUn.sun_path, _PATH_LOG, sizeof(u.addrUn.sun_path));
if (TEMP_FAILURE_RETRY(connect(log_fd, &u.addr, sizeof(u.addrUn))) != 0) {
close(log_fd);
return -1;
}
return log_fd;
}
static int write_stderr(const char* tag, const char* msg) {
struct iovec vec[4];
vec[0].iov_base = (char*)tag;
vec[0].iov_len = strlen(tag);
vec[1].iov_base = (char*)": ";
vec[1].iov_len = 2;
vec[2].iov_base = (char*)msg;
vec[2].iov_len = strlen(msg);
vec[3].iov_base = (char*)"\n";
vec[3].iov_len = 1;
return TEMP_FAILURE_RETRY(writev(STDERR_FILENO, vec, 4));
}
int async_safe_write_log(int priority, const char* tag, const char* msg) {
int main_log_fd = open_log_socket();
if (main_log_fd == -1)
// Try stderr instead.
return write_stderr(tag, msg);
struct iovec vec[6];
char log_id = (priority == ANDROID_LOG_FATAL) ? LOG_ID_CRASH : LOG_ID_MAIN;
vec[0].iov_base = &log_id;
vec[0].iov_len = sizeof(log_id);
uint16_t tid = INTERNAL_SYSCALL_CALL(gettid);
vec[1].iov_base = &tid;
vec[1].iov_len = sizeof(tid);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
struct log_time realtime_ts;
realtime_ts.tv_sec = ts.tv_sec;
realtime_ts.tv_nsec = ts.tv_nsec;
vec[2].iov_base = &realtime_ts;
vec[2].iov_len = sizeof(realtime_ts);
vec[3].iov_base = &priority;
vec[3].iov_len = 1;
vec[4].iov_base = (char*)tag;
vec[4].iov_len = strlen(tag) + 1;
vec[5].iov_base = (char*)msg;
vec[5].iov_len = strlen(msg) + 1;
int result = TEMP_FAILURE_RETRY(writev(main_log_fd, vec, sizeof(vec) / sizeof(vec[0])));
close(main_log_fd);
return result;
}
int async_safe_format_log_va_list(int priority, const char* tag, const char* format, va_list args) {
//ErrnoRestorer errno_restorer;
char buffer[1024];
struct BufferOutputStream os = BufferOutput(buffer, sizeof(buffer));
out_vformat(&os, format, args);
return async_safe_write_log(priority, tag, buffer);
}
int async_safe_format_log(int priority, const char* tag, const char* format, ...) {
va_list args;
va_start(args, format);
int result = async_safe_format_log_va_list(priority, tag, format, args);
va_end(args);
return result;
}
// ======================
// syslog functions
void closelog(void) {
syslog_log_tag = NULL;
syslog_options = 0;
}
void openlog(const char* log_tag, int options, int /*facility*/) {
syslog_log_tag = log_tag;
syslog_options = options;
}
int setlogmask(int new_mask) {
int old_mask = syslog_priority_mask;
// 0 is used to query the current mask.
if (new_mask != 0)
syslog_priority_mask = new_mask;
return old_mask;
}
void __vsyslog_internal(int priority, const char* fmt, va_list args, unsigned int mode_flags) {
// Check whether we're supposed to be logging messages of this priority.
if ((syslog_priority_mask & LOG_MASK(LOG_PRI(priority))) == 0)
return;
// What's our log tag?
const char* log_tag = syslog_log_tag;
if (log_tag == NULL)
log_tag = __progname;
// What's our Android log priority?
priority &= LOG_PRIMASK;
int android_log_priority;
if (priority <= LOG_CRIT) // LOG_ALERT LOG_EMERG
android_log_priority = ANDROID_LOG_FATAL;
else if (priority == LOG_ERR)
android_log_priority = ANDROID_LOG_ERROR;
else if (priority <= LOG_NOTICE) // LOG_WARNING
android_log_priority = ANDROID_LOG_WARN;
else if (priority == LOG_INFO)
android_log_priority = ANDROID_LOG_INFO;
else // LOG_DEBUG
android_log_priority = ANDROID_LOG_DEBUG;
// We can't let async_safe_format_log do the formatting because it doesn't
// support all the printf functionality.
char log_line[1024];
int n = __vsnprintf_internal(log_line, sizeof(log_line), fmt, args, mode_flags);
if (n < 0) return;
async_safe_format_log(android_log_priority, log_tag, "%s", log_line);
if ((syslog_options & LOG_PERROR) != 0) {
bool have_newline =
(n > 0 && n < (int)sizeof(log_line) && log_line[n - 1] == '\n');
dprintf(STDERR_FILENO, "%s: %s%s", log_tag, log_line, have_newline ? "" : "\n");
}
}
void __syslog(int pri, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
__vsyslog_internal(pri, fmt, ap, 0);
va_end(ap);
}
ldbl_hidden_def(__syslog, syslog)
ldbl_strong_alias(__syslog, syslog)
void __vsyslog(int pri, const char *fmt, va_list ap) {
__vsyslog_internal(pri, fmt, ap, 0);
}
ldbl_weak_alias(__vsyslog, vsyslog)
void ___syslog_chk(int pri, int flag, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
__vsyslog_internal(pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0);
va_end(ap);
}
ldbl_hidden_def(___syslog_chk, __syslog_chk)
ldbl_strong_alias(___syslog_chk, __syslog_chk)
void __vsyslog_chk(int pri, int flag, const char *fmt, va_list ap) {
__vsyslog_internal(pri, fmt, ap, (flag > 0) ? PRINTF_FORTIFY : 0);
}