--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * libsystem
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#include <time.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "log.h"
+
+#define FILEBUF 1024
+
+static inline void __cleanup_free_func(void *p)
+{
+ free(*(void**) p);
+}
+
+static inline bool isempty(const char *p)
+{
+ return !p || !p[0];
+}
+
+#define _cleanup_(x) __attribute__((cleanup(x)))
+
+#define _cleanup_free_ _cleanup_(__cleanup_free_func)
+
+#define new(t, n) ((t*) malloc(sizeof(t) * (n)))
+
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
+ for ((state) = NULL, (word) = split((s), &(length), (separator), &(state)); (word); (word) = split((s), &(length), (separator), &(state)))
+
+#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
+
+static FILE *log_f[LOG_TYPE_MAX];
+static int stdout_bak = -1;
+static int stderr_bak = -1;
+
+static int log_level = LOG_DEBUG;
+
+static const char const *log_priority_name[] = {
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_DEBUG] = "debug",
+ [LOG_EMERG] = "emerg",
+ [LOG_ERR] = "err",
+ [LOG_INFO] = "info",
+ [LOG_NOTICE] = "notice",
+ [LOG_WARNING] = "warning"
+};
+
+void set_log_level(int level)
+{
+ log_level = level;
+}
+
+int get_log_level(void)
+{
+ return log_level;
+}
+
+static bool __quote_complete(char *str, size_t l, char q)
+{
+ char *s, *s2;
+
+ assert(str);
+
+ if (!l)
+ return true;
+
+ s = strchr(str, q);
+ if (!s || (s - str) > l)
+ return true;
+
+ s = strchr(s + 1, q);
+ if (!s || (s - str) > l)
+ return false;
+
+ s2 = strchr(s + 1, q);
+ if (!s2 || (s2 - str) > l)
+ return true;
+
+ return __quote_complete(s + 1, l - (s + 1 - str), q);
+}
+
+#define QUOTES "\"\'"
+static bool quote_complete(char *str, size_t l)
+{
+ char quotes[] = QUOTES;
+ int i;
+
+ assert(str);
+
+ if (!l)
+ return true;
+
+ for (i = 0; quotes[i]; i++) {
+ if (!__quote_complete(str, l, quotes[i]))
+ return false;
+ }
+
+ return true;
+}
+
+static char *split(const char *c, size_t *l, const char *separator, char **state)
+{
+ bool separator_include_quotes;
+ char *current;
+ size_t s;
+
+ assert(c);
+ assert(l);
+ assert(separator);
+ assert(state);
+
+ current = *state ? *state : (char *) c;
+ if (!*current || *c == 0)
+ return NULL;
+
+ *l = 0;
+ separator_include_quotes = !!strspn(separator, QUOTES);
+ current += strspn(current, separator);
+
+ while ((s = strcspn(current + *l, separator))) {
+ *l += s;
+ if (separator_include_quotes ||
+ quote_complete(current, *l))
+ break;
+ (*l)++;
+ }
+
+ *state = current + *l;
+
+ return (char *) current;
+}
+
+static int stdpath_dup_and_backup(FILE *f)
+{
+ int r, fd;
+
+ stdout_bak = dup(STDOUT_FILENO);
+ stderr_bak = dup(STDERR_FILENO);
+
+ fd = fileno(f);
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0)
+ return -errno;
+
+ r = dup2(fd, STDERR_FILENO);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int stdpath_restore(void)
+{
+ int r;
+
+ if (stdout_bak >= 0) {
+ r = dup2(stdout_bak, STDOUT_FILENO);
+ if (r < 0)
+ return -errno;
+
+ close(stdout_bak);
+ stdout_bak = -1;
+ }
+
+ if (stderr_bak >= 0) {
+ r = dup2(stderr_bak, STDERR_FILENO);
+ if (r < 0)
+ return -errno;
+
+ close(stderr_bak);
+ stderr_bak = -1;
+ }
+
+ return 0;
+}
+
+int log_open(enum log_mask log_set, const char *path)
+{
+ int r;
+
+ if (log_set & LOG_MASK_FILE) {
+ assert(path);
+
+ log_f[LOG_TYPE_FILE] = fopen(path, "w+");
+ if (!log_f[LOG_TYPE_FILE])
+ return -errno;
+
+ /* If standard output/error were not set, redirect
+ * standard output/error to log file */
+ if (!(log_set & LOG_MASK_STANDARD)) {
+ r = stdpath_dup_and_backup(log_f[LOG_TYPE_FILE]);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ if (log_set & LOG_MASK_KMSG) {
+ log_f[LOG_TYPE_KMSG] = fopen("/dev/kmsg", "we");
+ if (!log_f[LOG_TYPE_KMSG])
+ return -errno;
+
+ /* If standard output/error or log file were not set,
+ * redirect standard output/error to kmsg */
+ if (!(log_set & LOG_MASK_STANDARD) &&
+ !(log_set & LOG_MASK_FILE)) {
+ r = stdpath_dup_and_backup(log_f[LOG_TYPE_KMSG]);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
+int log_close(void)
+{
+ enum log_type type;
+
+ for (type = 0; type < LOG_TYPE_MAX; type++) {
+ if (type == LOG_TYPE_STANDARD || !log_f[type])
+ continue;
+
+ fclose(log_f[type]);
+ log_f[type] = NULL;
+ }
+
+ return stdpath_restore();
+}
+
+static int _log_write(
+ int level,
+ const char *file,
+ int line,
+ const char *func,
+ const char *format,
+ va_list ap)
+{
+ char buff[LINE_MAX];
+ enum log_type type;
+
+ vsnprintf(buff, LINE_MAX, format, ap);
+
+ for (type = 0; type < LOG_TYPE_MAX; type++) {
+ if (type != LOG_TYPE_STANDARD && !log_f[type])
+ continue;
+
+ switch (type) {
+ case LOG_TYPE_STANDARD:
+ fprintf(level <= LOG_ERR ? stderr : stdout,
+ "%s\n", buff);
+ break;
+ case LOG_TYPE_FILE:
+ fprintf(log_f[LOG_TYPE_FILE],
+ "%-8s: %s\n", log_priority_name[level], buff);
+ fflush(log_f[LOG_TYPE_FILE]);
+ break;
+ case LOG_TYPE_KMSG:
+ fprintf(log_f[LOG_TYPE_KMSG],
+ "initrd: %s\n", buff);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int log_write(
+ int level,
+ const char*file,
+ int line,
+ const char *func,
+ const char *format, ...)
+{
+ va_list ap;
+ int r;
+
+ va_start(ap, format);
+ r = _log_write(level, file, line, func, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int log_move(const char *to)
+{
+ _cleanup_free_ char *buf = NULL;
+ FILE *w = NULL;
+ size_t len;
+ int r;
+
+ assert(to);
+
+ if (!log_f[LOG_TYPE_FILE])
+ return 0;
+
+ buf = new(char, FILEBUF);
+ if (!buf) {
+ r = -ENOMEM;
+ goto error;
+ }
+
+ w = fopen(to, "a+");
+ if (!w) {
+ r = -errno;
+ goto error;
+ }
+
+ r = fseek(log_f[LOG_TYPE_FILE], 0, SEEK_SET);
+ if (r < 0) {
+ r = -errno;
+ goto error;
+ }
+
+ r = fflush(log_f[LOG_TYPE_FILE]);
+ if (r < 0) {
+ r = -errno;
+ goto error;
+ }
+
+ while ((len = fread(buf, sizeof(char), FILEBUF, log_f[LOG_TYPE_FILE])) > 0) {
+ if (fwrite(buf, sizeof(char), len, w) != len) {
+ r = -errno;
+ goto error;
+ }
+ }
+
+ fclose(log_f[LOG_TYPE_FILE]);
+ log_f[LOG_TYPE_FILE] = w;
+
+ r = stdpath_dup_and_backup(log_f[LOG_TYPE_FILE]);
+ if (r < 0)
+ goto error;
+
+ return 0;
+
+error:
+ if (w)
+ fclose(w);
+
+ log_dbg("Failed to move log: %s\n", strerror(-r));
+
+ return r;
+}
+
+enum log_mask parse_log_type(const char *type)
+{
+ char *word, *state;
+ enum log_mask mask = 0;
+ size_t l;
+
+ if (!type)
+ return LOG_MASK_INVALID;
+
+ FOREACH_WORD_SEPARATOR(word, l, type, "+", state) {
+
+ if (strncaseeq(word, "standard", l))
+ mask |= LOG_MASK_STANDARD;
+ else if (strncaseeq(word, "file", l))
+ mask |= LOG_MASK_FILE;
+ else if (strncaseeq(word, "kmsg", l))
+ mask |= LOG_MASK_KMSG;
+ else
+ return LOG_MASK_INVALID;
+ }
+
+ return mask;
+}
+
+int config_parse_log_type(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data)
+{
+
+ enum log_mask *mask = data;
+
+ if (isempty(rvalue))
+ return 0;
+
+ *mask = parse_log_type(rvalue);
+
+ return *mask == LOG_MASK_INVALID ? -EINVAL : 0;
+}
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/*
+ * libsystem
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/syslog.h>
+#include <stdbool.h>
+
+enum log_type {
+ LOG_TYPE_INVALID = -1,
+ LOG_TYPE_STANDARD = 0,
+ LOG_TYPE_FILE,
+ LOG_TYPE_KMSG,
+ LOG_TYPE_MAX
+};
+
+enum log_mask {
+ LOG_MASK_INVALID = -1,
+ LOG_MASK_STANDARD = 1 << LOG_TYPE_STANDARD,
+ LOG_MASK_FILE = 1 << LOG_TYPE_FILE,
+ LOG_MASK_KMSG = 1 << LOG_TYPE_KMSG,
+};
+
+#define log_full(level, ...) \
+ do { \
+ log_write((level), __FILE__, __LINE__, __func__, __VA_ARGS__); \
+ } while (0)
+
+void set_log_level(int level);
+int get_log_level(void);
+int log_open(enum log_mask log_set, const char *path);
+int log_close(void);
+int log_write(int level, const char*file, int line, const char *func, const char *format, ...);
+int log_move(const char *to);
+enum log_mask parse_log_type(const char *type);
+int config_parse_log_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data);
+
+#define log_dbg(format, ...) \
+ do { \
+ if (get_log_level() >= LOG_DEBUG) \
+ log_full(LOG_DEBUG, format, ##__VA_ARGS__); \
+ } while (0)
+
+#define log_err(format, ...) \
+ do { \
+ if (get_log_level() >= LOG_ERR) \
+ log_full(LOG_ERR, format, ##__VA_ARGS__); \
+ } while (0)
+
+#define log_info(format, ...) \
+ do { \
+ if (get_log_level() >= LOG_INFO) \
+ log_full(LOG_INFO, format, ##__VA_ARGS__); \
+ } while (0)