diff --git a/HACKING.txt b/HACKING.txt index fb93b5a10..27e136827 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -81,13 +81,6 @@ libc/ private/ # These are private header files meant for use within bionic itself. - stdio/ - stdlib/ - unistd/ - # These are legacy files of unknown provenance. In the past, bionic was a - # mess of random versions of random files from all three of FreeBSD, NetBSD, - # and OpenBSD! We've been working to clean that up, but these directories - # are basically where all the stuff we haven't got to yet lives. dns/ # Contains the DNS resolver (originates from NetBSD code). @@ -114,6 +107,10 @@ libc/ # current upstream source in one of the upstream directories or by # switching the file to C++ and cleaning it up. + stdio/ + # These are legacy files of dubious provenance. We're working to clean + # this mess up, and this directory should disappear. + tools/ # Various tools used to maintain bionic. diff --git a/libc/Android.mk b/libc/Android.mk index 0adaff7d5..fd3da965a 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -67,7 +67,6 @@ libc_common_src_files := \ bionic/unlockpt.c \ stdio/snprintf.c\ stdio/sprintf.c \ - unistd/syslog.c \ # Fortify implementations of libc functions. libc_common_src_files += \ @@ -218,6 +217,7 @@ libc_bionic_src_files := \ bionic/stubs.cpp \ bionic/symlink.cpp \ bionic/sysconf.cpp \ + bionic/syslog.cpp \ bionic/sys_siglist.c \ bionic/sys_signame.c \ bionic/system_properties.cpp \ diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp index d0172edaa..d6fa1db78 100644 --- a/libc/bionic/libc_logging.cpp +++ b/libc/bionic/libc_logging.cpp @@ -378,6 +378,9 @@ static void out_vformat(Out& o, const char* format, va_list args) { } else if (c == '%') { buffer[0] = '%'; buffer[1] = '\0'; + } else if (c == 'm') { + // syslog-like %m for strerror(errno). + str = strerror(errno); } else { __assert(__FILE__, __LINE__, "conversion specifier unsupported"); } diff --git a/libc/bionic/syslog.cpp b/libc/bionic/syslog.cpp new file mode 100644 index 000000000..7e153eb16 --- /dev/null +++ b/libc/bionic/syslog.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include "private/libc_logging.h" + +static const char* syslog_log_tag = NULL; +static int syslog_priority_mask = 0xff; + +void closelog() { +} + +void openlog(const char* log_tag, int /*options*/, int /*facility*/) { + syslog_log_tag = log_tag; +} + +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 syslog(int priority, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vsyslog(priority, fmt, args); + va_end(args); +} + +void vsyslog(int priority, const char* fmt, va_list args) { + // 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 = getprogname(); + } + + // What's our Android log priority? + priority &= LOG_PRIMASK; + int android_log_priority; + if (priority < LOG_ERR) { + android_log_priority = ANDROID_LOG_ERROR; + } else if (priority == LOG_WARNING) { + android_log_priority = ANDROID_LOG_WARN; + } else if (priority <= LOG_INFO) { + android_log_priority = ANDROID_LOG_INFO; + } else { + android_log_priority = ANDROID_LOG_DEBUG; + } + + __libc_format_log_va_list(android_log_priority, log_tag, fmt, args); +} diff --git a/libc/include/syslog.h b/libc/include/syslog.h index a52e81154..f396feca7 100644 --- a/libc/include/syslog.h +++ b/libc/include/syslog.h @@ -74,20 +74,18 @@ __BEGIN_DECLS #define LOG_MASK(pri) (1 << (pri)) #define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) -#define LOG_PID 0x01 /* include pid with message */ -#define LOG_CONS 0x02 /* write to console on logger error */ -#define LOG_ODELAY 0x04 /* delay connection until syslog() */ -#define LOG_NDELAY 0x08 /* open connection immediately */ -#define LOG_NOWAIT 0x10 /* wait for child processes (unused on linux) */ -#define LOG_PERROR 0x20 /* additional logging to stderr */ +#define LOG_PID 0x01 +#define LOG_CONS 0x02 +#define LOG_ODELAY 0x04 +#define LOG_NDELAY 0x08 +#define LOG_NOWAIT 0x10 +#define LOG_PERROR 0x20 -#define _PATH_LOG "/dev/syslog" - -extern void closelog(void); -extern void openlog(const char *, int, int); -extern int setlogmask(int); -extern void syslog(int, const char *, ...) __printflike(2, 3); -extern void vsyslog(int, const char *, va_list) __printflike(2, 0); +extern void closelog(void); +extern void openlog(const char*, int, int); +extern int setlogmask(int); +extern void syslog(int, const char*, ...) __printflike(2, 3); +extern void vsyslog(int, const char*, va_list) __printflike(2, 0); __END_DECLS diff --git a/libc/unistd/syslog.c b/libc/unistd/syslog.c deleted file mode 100644 index 339df680a..000000000 --- a/libc/unistd/syslog.c +++ /dev/null @@ -1,392 +0,0 @@ -/* $OpenBSD: syslog.c,v 1.28 2005/08/08 08:05:34 espie Exp $ */ -/* - * Copyright (c) 1983, 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct syslog_data { - int log_file; - int connected; - int opened; - int log_stat; - const char* log_tag; - int log_fac; - int log_mask; -}; - -#define SYSLOG_DATA_INIT {-1, 0, 0, 0, (const char *)0, LOG_USER, 0xff} - -static struct syslog_data sdata = SYSLOG_DATA_INIT; - -extern const char *__progname; /* Program name, from crt0. */ - -static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */ -static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */ - -#if defined(__LP64__) -#define SYSLOG_R_VISIBILITY static -#else -#define SYSLOG_R_VISIBILITY extern -#endif - -SYSLOG_R_VISIBILITY void closelog_r(struct syslog_data*); -SYSLOG_R_VISIBILITY void openlog_r(const char*, int, int, struct syslog_data*); -SYSLOG_R_VISIBILITY int setlogmask_r(int, struct syslog_data*); -SYSLOG_R_VISIBILITY void syslog_r(int, struct syslog_data*, const char*, ...) __printflike(3, 4); -SYSLOG_R_VISIBILITY void vsyslog_r(int, struct syslog_data*, const char*, va_list) __printflike(3, 0); - -/* - * syslog, vsyslog -- - * print message on log file; output is intended for syslogd(8). - */ -void -syslog(int pri, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vsyslog(pri, fmt, ap); - va_end(ap); -} - -void -vsyslog(int pri, const char *fmt, va_list ap) -{ - vsyslog_r(pri, &sdata, fmt, ap); -} - -void -openlog(const char *ident, int logstat, int logfac) -{ - openlog_r(ident, logstat, logfac, &sdata); -} - -void -closelog(void) -{ - closelog_r(&sdata); -} - -/* setlogmask -- set the log mask level */ -int -setlogmask(int pmask) -{ - return setlogmask_r(pmask, &sdata); -} - -/* Reentrant version of syslog, i.e. syslog_r() */ - -void -syslog_r(int pri, struct syslog_data *data, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vsyslog_r(pri, data, fmt, ap); - va_end(ap); -} - -void -vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap) -{ - int cnt; - char ch, *p, *t; - time_t now; - int fd, saved_errno, error; -#define TBUF_LEN 2048 -#define FMT_LEN 1024 - char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; - int tbuf_left, fmt_left, prlen; - -#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID - /* Check for invalid bits. */ - if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { - if (data == &sdata) { - syslog(INTERNALLOG, - "syslog: unknown facility/priority: %x", pri); - } else { - syslog_r(INTERNALLOG, data, - "syslog_r: unknown facility/priority: %x", pri); - } - pri &= LOG_PRIMASK|LOG_FACMASK; - } - - /* Check priority against setlogmask values. */ - if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask)) - return; - - saved_errno = errno; - - /* Set default facility if none specified. */ - if ((pri & LOG_FACMASK) == 0) - pri |= data->log_fac; - - /* If we have been called through syslog(), no need for reentrancy. */ - if (data == &sdata) - (void)time(&now); - - p = tbuf; - tbuf_left = TBUF_LEN; - -#define DEC() \ - do { \ - if (prlen < 0) \ - prlen = 0; \ - if (prlen >= tbuf_left) \ - prlen = tbuf_left - 1; \ - p += prlen; \ - tbuf_left -= prlen; \ - } while (0) - - prlen = snprintf(p, tbuf_left, "<%d>", pri); - DEC(); - - /* - * syslogd will expand time automagically for reentrant case, and - * for normal case, just do like before - */ - if (data == &sdata) { - prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now)); - DEC(); - } - - if (data->log_stat & LOG_PERROR) - stdp = p; - if (data->log_tag == NULL) - data->log_tag = __progname; - if (data->log_tag != NULL) { - prlen = snprintf(p, tbuf_left, "%s", data->log_tag); - DEC(); - } - if (data->log_stat & LOG_PID) { - prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid()); - DEC(); - } - if (data->log_tag != NULL) { - if (tbuf_left > 1) { - *p++ = ':'; - tbuf_left--; - } - if (tbuf_left > 1) { - *p++ = ' '; - tbuf_left--; - } - } - - /* strerror() is not reentrant */ - - for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) { - if (ch == '%' && fmt[1] == 'm') { - ++fmt; - if (data == &sdata) { - prlen = snprintf(t, fmt_left, "%s", - strerror(saved_errno)); - } else { - prlen = snprintf(t, fmt_left, "Error %d", - saved_errno); - } - if (prlen < 0) - prlen = 0; - if (prlen >= fmt_left) - prlen = fmt_left - 1; - t += prlen; - fmt_left -= prlen; - } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) { - *t++ = '%'; - *t++ = '%'; - fmt++; - fmt_left -= 2; - } else { - if (fmt_left > 1) { - *t++ = ch; - fmt_left--; - } - } - } - *t = '\0'; - - prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); - DEC(); - cnt = p - tbuf; - - /* Output to stderr if requested. */ - if (data->log_stat & LOG_PERROR) { - struct iovec iov[2]; - - iov[0].iov_base = stdp; - iov[0].iov_len = cnt - (stdp - tbuf); - iov[1].iov_base = "\n"; - iov[1].iov_len = 1; - (void)writev(STDERR_FILENO, iov, 2); - } - - /* Get connected, output the message to the local logger. */ - if (!data->opened) - openlog_r(data->log_tag, data->log_stat, 0, data); - connectlog_r(data); - - /* - * If the send() failed, there are two likely scenarios: - * 1) syslogd was restarted - * 2) /dev/log is out of socket buffer space - * We attempt to reconnect to /dev/log to take care of - * case #1 and keep send()ing data to cover case #2 - * to give syslogd a chance to empty its socket buffer. - */ - if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) { - if (errno != ENOBUFS) { - disconnectlog_r(data); - connectlog_r(data); - } - do { - usleep(1); - if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0) - break; - } while (errno == ENOBUFS); - } - - /* - * Output the message to the console; try not to block - * as a blocking console should not stop other processes. - * Make sure the error reported is the one from the syslogd failure. - */ - if (error == -1 && (data->log_stat & LOG_CONS) && - (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) { - struct iovec iov[2]; - - p = strchr(tbuf, '>') + 1; - iov[0].iov_base = p; - iov[0].iov_len = cnt - (p - tbuf); - iov[1].iov_base = "\r\n"; - iov[1].iov_len = 2; - (void)writev(fd, iov, 2); - (void)close(fd); - } - - if (data != &sdata) - closelog_r(data); -} - -static void -disconnectlog_r(struct syslog_data *data) -{ - /* - * If the user closed the FD and opened another in the same slot, - * that's their problem. They should close it before calling on - * system services. - */ - if (data->log_file != -1) { - close(data->log_file); - data->log_file = -1; - } - data->connected = 0; /* retry connect */ -} - -static void -connectlog_r(struct syslog_data *data) -{ - union { - struct sockaddr syslogAddr; - struct sockaddr_un syslogAddrUn; - } u; - -#define SyslogAddr u.syslogAddrUn - - if (data->log_file == -1) { - if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) - return; - (void)fcntl(data->log_file, F_SETFD, 1); - } - if (data->log_file != -1 && !data->connected) { - memset(&SyslogAddr, '\0', sizeof(SyslogAddr)); -#if 0 - /* BIONIC: no sun_len field to fill on Linux */ - SyslogAddr.sun_len = sizeof(SyslogAddr); -#endif - SyslogAddr.sun_family = AF_UNIX; - strlcpy(SyslogAddr.sun_path, _PATH_LOG, - sizeof(SyslogAddr.sun_path)); - if (connect(data->log_file, &u.syslogAddr, - sizeof(SyslogAddr)) == -1) { - (void)close(data->log_file); - data->log_file = -1; - } else - data->connected = 1; - } -} - -void -openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data) -{ - if (ident != NULL) - data->log_tag = ident; - data->log_stat = logstat; - if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) - data->log_fac = logfac; - - if (data->log_stat & LOG_NDELAY) /* open immediately */ - connectlog_r(data); - - data->opened = 1; /* ident and facility has been set */ -} - -void -closelog_r(struct syslog_data *data) -{ - (void)close(data->log_file); - data->log_file = -1; - data->connected = 0; - data->log_tag = NULL; -} - -/* setlogmask -- set the log mask level */ -int -setlogmask_r(int pmask, struct syslog_data *data) -{ - int omask; - - omask = data->log_mask; - if (pmask != 0) - data->log_mask = pmask; - return (omask); -} diff --git a/tests/libc_logging_test.cpp b/tests/libc_logging_test.cpp index 950161e78..ef39d1c03 100644 --- a/tests/libc_logging_test.cpp +++ b/tests/libc_logging_test.cpp @@ -176,3 +176,14 @@ TEST(libc_logging, lld_LLONG_MIN) { GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif // __BIONIC__ } + +TEST(libc_logging, m) { +#if defined(__BIONIC__) + char buf[BUFSIZ]; + errno = EBADF; + __libc_format_buffer(buf, sizeof(buf), "<%m>"); + EXPECT_STREQ("", buf); +#else // __BIONIC__ + GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif // __BIONIC__ +}