|  | // Copyright 2017 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "sandbox/mac/sandbox_logging.h" | 
|  |  | 
|  | #include <asl.h> | 
|  | #include <errno.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <syslog.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <limits> | 
|  | #include <string> | 
|  |  | 
|  | #define ABORT()                                                                \ | 
|  | {                                                                            \ | 
|  | asm volatile(                                                              \ | 
|  | "int3; ud2; push %0;" ::"i"(static_cast<unsigned char>(__COUNTER__))); \ | 
|  | __builtin_unreachable();                                                   \ | 
|  | } | 
|  |  | 
|  | namespace sandbox { | 
|  |  | 
|  | namespace logging { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | enum class Level { FATAL, ERR, WARN, INFO }; | 
|  |  | 
|  | void SendAslLog(Level level, const char* message) { | 
|  | class ASLClient { | 
|  | public: | 
|  | explicit ASLClient() | 
|  | : client_(asl_open(nullptr, | 
|  | "com.apple.console", | 
|  | ASL_OPT_STDERR | ASL_OPT_NO_DELAY)) {} | 
|  | ~ASLClient() { asl_close(client_); } | 
|  |  | 
|  | aslclient get() const { return client_; } | 
|  |  | 
|  | ASLClient(const ASLClient&) = delete; | 
|  | ASLClient& operator=(const ASLClient&) = delete; | 
|  |  | 
|  | private: | 
|  | aslclient client_; | 
|  | } asl_client; | 
|  |  | 
|  | class ASLMessage { | 
|  | public: | 
|  | ASLMessage() : message_(asl_new(ASL_TYPE_MSG)) {} | 
|  | ~ASLMessage() { asl_free(message_); } | 
|  |  | 
|  | aslmsg get() const { return message_; } | 
|  |  | 
|  | ASLMessage(const ASLMessage&) = delete; | 
|  | ASLMessage& operator=(const ASLMessage&) = delete; | 
|  |  | 
|  | private: | 
|  | aslmsg message_; | 
|  | } asl_message; | 
|  |  | 
|  | // By default, messages are only readable by the admin group. Explicitly | 
|  | // make them readable by the user generating the messages. | 
|  | char euid_string[12]; | 
|  | snprintf(euid_string, sizeof(euid_string) / sizeof(euid_string[0]), "%d", | 
|  | geteuid()); | 
|  | asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string); | 
|  |  | 
|  | std::string asl_level_string; | 
|  | switch (level) { | 
|  | case Level::FATAL: | 
|  | asl_level_string = ASL_STRING_CRIT; | 
|  | break; | 
|  | case Level::ERR: | 
|  | asl_level_string = ASL_STRING_ERR; | 
|  | break; | 
|  | case Level::WARN: | 
|  | asl_level_string = ASL_STRING_WARNING; | 
|  | break; | 
|  | case Level::INFO: | 
|  | default: | 
|  | asl_level_string = ASL_STRING_INFO; | 
|  | break; | 
|  | } | 
|  |  | 
|  | asl_set(asl_message.get(), ASL_KEY_LEVEL, asl_level_string.c_str()); | 
|  | asl_set(asl_message.get(), ASL_KEY_MSG, message); | 
|  | asl_send(asl_client.get(), asl_message.get()); | 
|  | } | 
|  |  | 
|  | // |error| is strerror(errno) when a P* logging function is called. Pass | 
|  | // |nullptr| if no errno is set. | 
|  | void DoLogging(Level level, | 
|  | const char* fmt, | 
|  | va_list args, | 
|  | const std::string* error) { | 
|  | char message[4096]; | 
|  | int ret = vsnprintf(message, sizeof(message), fmt, args); | 
|  |  | 
|  | if (ret < 0) { | 
|  | SendAslLog(level, "warning: log message could not be formatted"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // int |ret| is not negative so casting to a larger type is safe. | 
|  | bool truncated = static_cast<unsigned long>(ret) > sizeof(message) - 1; | 
|  |  | 
|  | std::string final_message = message; | 
|  | if (error) | 
|  | final_message += ": " + *error; | 
|  |  | 
|  | SendAslLog(level, final_message.c_str()); | 
|  |  | 
|  | if (truncated) | 
|  | SendAslLog(level, "warning: previous log message truncated"); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void Info(const char* fmt, ...) { | 
|  | va_list args; | 
|  | va_start(args, fmt); | 
|  | DoLogging(Level::INFO, fmt, args, nullptr); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void Warning(const char* fmt, ...) { | 
|  | va_list args; | 
|  | va_start(args, fmt); | 
|  | DoLogging(Level::WARN, fmt, args, nullptr); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void Error(const char* fmt, ...) { | 
|  | va_list args; | 
|  | va_start(args, fmt); | 
|  | DoLogging(Level::ERR, fmt, args, nullptr); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void Fatal(const char* fmt, ...) { | 
|  | va_list args; | 
|  | va_start(args, fmt); | 
|  | DoLogging(Level::FATAL, fmt, args, nullptr); | 
|  | va_end(args); | 
|  |  | 
|  | ABORT(); | 
|  | } | 
|  |  | 
|  | void PError(const char* fmt, ...) { | 
|  | std::string error = strerror(errno); | 
|  | va_list args; | 
|  | va_start(args, fmt); | 
|  | DoLogging(Level::ERR, fmt, args, &error); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void PFatal(const char* fmt, ...) { | 
|  | std::string error = strerror(errno); | 
|  | va_list args; | 
|  | va_start(args, fmt); | 
|  | DoLogging(Level::FATAL, fmt, args, &error); | 
|  | va_end(args); | 
|  |  | 
|  | ABORT(); | 
|  | } | 
|  |  | 
|  | }  // namespace logging | 
|  |  | 
|  | }  // namespace sandbox |