blob: de435f7edd980a52e8feec8e72fd4ef1583723a1 [file] [log] [blame]
/*
* Copyright (C) 2007-2016 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 <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
#include "config_read.h"
#include "logger.h"
static int pmsgAvailable(log_id_t logId);
static int pmsgVersion(struct android_log_logger *logger,
struct android_log_transport_context *transp);
static int pmsgRead(struct android_log_logger_list *logger_list,
struct android_log_transport_context *transp,
struct log_msg *log_msg);
static void pmsgClose(struct android_log_logger_list *logger_list,
struct android_log_transport_context *transp);
static int pmsgClear(struct android_log_logger *logger,
struct android_log_transport_context *transp);
LIBLOG_HIDDEN struct android_log_transport_read pmsgLoggerRead = {
.node = { &pmsgLoggerRead.node, &pmsgLoggerRead.node },
.name = "pmsg",
.available = pmsgAvailable,
.version = pmsgVersion,
.read = pmsgRead,
.poll = NULL,
.close = pmsgClose,
.clear = pmsgClear,
.setSize = NULL,
.getSize = NULL,
.getReadableSize = NULL,
.getPrune = NULL,
.setPrune = NULL,
.getStats = NULL,
};
static int pmsgAvailable(log_id_t logId)
{
if (logId > LOG_ID_SECURITY) {
return -EINVAL;
}
if (access("/dev/pmsg0", W_OK) == 0) {
return 0;
}
return -EBADF;
}
/* Determine the credentials of the caller */
static bool uid_has_log_permission(uid_t uid)
{
return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
}
static uid_t get_best_effective_uid()
{
uid_t euid;
uid_t uid;
gid_t gid;
ssize_t i;
static uid_t last_uid = (uid_t) -1;
if (last_uid != (uid_t) -1) {
return last_uid;
}
uid = __android_log_uid();
if (uid_has_log_permission(uid)) {
return last_uid = uid;
}
euid = geteuid();
if (uid_has_log_permission(euid)) {
return last_uid = euid;
}
gid = getgid();
if (uid_has_log_permission(gid)) {
return last_uid = gid;
}
gid = getegid();
if (uid_has_log_permission(gid)) {
return last_uid = gid;
}
i = getgroups((size_t) 0, NULL);
if (i > 0) {
gid_t list[i];
getgroups(i, list);
while (--i >= 0) {
if (uid_has_log_permission(list[i])) {
return last_uid = list[i];
}
}
}
return last_uid = uid;
}
static int pmsgClear(struct android_log_logger *logger __unused,
struct android_log_transport_context *transp __unused)
{
if (uid_has_log_permission(get_best_effective_uid())) {
return unlink("/sys/fs/pstore/pmsg-ramoops-0");
}
errno = EPERM;
return -1;
}
/*
* returns the logger version
*/
static int pmsgVersion(struct android_log_logger *logger __unused,
struct android_log_transport_context *transp __unused)
{
return 4;
}
static int pmsgRead(struct android_log_logger_list *logger_list,
struct android_log_transport_context *transp,
struct log_msg *log_msg)
{
ssize_t ret;
off_t current, next;
uid_t uid;
struct android_log_logger *logger;
struct __attribute__((__packed__)) {
android_pmsg_log_header_t p;
android_log_header_t l;
} buf;
static uint8_t preread_count;
bool is_system;
memset(log_msg, 0, sizeof(*log_msg));
if (transp->context.fd <= 0) {
int fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
if (fd < 0) {
return -errno;
}
if (fd == 0) { /* Argggg */
fd = open("/sys/fs/pstore/pmsg-ramoops-0", O_RDONLY);
close(0);
if (fd < 0) {
return -errno;
}
}
transp->context.fd = fd;
preread_count = 0;
}
while(1) {
if (preread_count < sizeof(buf)) {
ret = TEMP_FAILURE_RETRY(read(transp->context.fd,
&buf.p.magic + preread_count,
sizeof(buf) - preread_count));
if (ret < 0) {
return -errno;
}
preread_count += ret;
}
if (preread_count != sizeof(buf)) {
return preread_count ? -EIO : -EAGAIN;
}
if ((buf.p.magic != LOGGER_MAGIC)
|| (buf.p.len <= sizeof(buf))
|| (buf.p.len > (sizeof(buf) + LOGGER_ENTRY_MAX_PAYLOAD))
|| (buf.l.id >= LOG_ID_MAX)
|| (buf.l.realtime.tv_nsec >= NS_PER_SEC)) {
do {
memmove(&buf.p.magic, &buf.p.magic + 1, --preread_count);
} while (preread_count && (buf.p.magic != LOGGER_MAGIC));
continue;
}
preread_count = 0;
if ((transp->logMask & (1 << buf.l.id)) &&
((!logger_list->start.tv_sec && !logger_list->start.tv_nsec) ||
((logger_list->start.tv_sec <= buf.l.realtime.tv_sec) &&
((logger_list->start.tv_sec != buf.l.realtime.tv_sec) ||
(logger_list->start.tv_nsec <=
buf.l.realtime.tv_nsec)))) &&
(!logger_list->pid || (logger_list->pid == buf.p.pid))) {
uid = get_best_effective_uid();
is_system = uid_has_log_permission(uid);
if (is_system || (uid == buf.p.uid)) {
ret = TEMP_FAILURE_RETRY(read(transp->context.fd,
is_system ?
log_msg->entry_v4.msg :
log_msg->entry_v3.msg,
buf.p.len - sizeof(buf)));
if (ret < 0) {
return -errno;
}
if (ret != (ssize_t)(buf.p.len - sizeof(buf))) {
return -EIO;
}
log_msg->entry_v4.len = buf.p.len - sizeof(buf);
log_msg->entry_v4.hdr_size = is_system ?
sizeof(log_msg->entry_v4) :
sizeof(log_msg->entry_v3);
log_msg->entry_v4.pid = buf.p.pid;
log_msg->entry_v4.tid = buf.l.tid;
log_msg->entry_v4.sec = buf.l.realtime.tv_sec;
log_msg->entry_v4.nsec = buf.l.realtime.tv_nsec;
log_msg->entry_v4.lid = buf.l.id;
if (is_system) {
log_msg->entry_v4.uid = buf.p.uid;
}
return ret;
}
}
current = TEMP_FAILURE_RETRY(lseek(transp->context.fd,
(off_t)0, SEEK_CUR));
if (current < 0) {
return -errno;
}
next = TEMP_FAILURE_RETRY(lseek(transp->context.fd,
(off_t)(buf.p.len - sizeof(buf)),
SEEK_CUR));
if (next < 0) {
return -errno;
}
if ((next - current) != (ssize_t)(buf.p.len - sizeof(buf))) {
return -EIO;
}
}
}
static void pmsgClose(struct android_log_logger_list *logger_list __unused,
struct android_log_transport_context *transp) {
if (transp->context.fd > 0) {
close (transp->context.fd);
}
transp->context.fd = 0;
}