#define pr_fmt(fmt) "logger: " fmt
-#include <linux/sched.h>
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
+# include <linux/sched/signal.h>
+#else
+# include <linux/sched.h>
+#endif
+
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/time.h>
#include <linux/vmalloc.h>
#include <linux/aio.h>
-#include "logger.h"
+#include <linux/uio.h>
+#include <linux/fdtable.h>
+#include <linux/file.h>
-#include <asm/ioctls.h>
+#include "logger.h"
/**
* struct logger_log - represents a specific log, such as 'main' or 'radio'
static LIST_HEAD(log_list);
+/**
+ * struct log_writer - a logging device open for writing
+ * @log: The associated log
+ * @list: The associated entry in @logger_log's list
+ * @b_off: The current position in @buf
+ * @tag: A tag to be attached to messages
+ * @prio: Default message priority value
+ * @buff: Temporary space to assemble messages.
+ */
+struct logger_writer {
+ struct logger_log *log;
+ struct task_struct *owner;
+ struct task_struct *b_owner;
+ struct logger_entry b_header;
+ size_t b_off;
+ size_t tag_len;
+ char *tag;
+ int prio;
+ char *buffer;
+};
/**
* struct logger_reader - a logging device open for reading
return n & (log->size - 1);
}
-
/*
* file_get_log - Given a file structure, return the associated log
*
*/
static inline struct logger_log *file_get_log(struct file *file)
{
+ struct logger_writer *writer = file->private_data;
+
if (file->f_mode & FMODE_READ) {
struct logger_reader *reader = file->private_data;
+
return reader->log;
- } else
- return file->private_data;
+ }
+
+ return writer->log;
}
/*
* the log entry spans the end and beginning of the circular buffer.
*/
static struct logger_entry *get_entry_header(struct logger_log *log,
- size_t off, struct logger_entry *scratch)
+ size_t off,
+ struct logger_entry *scratch)
{
size_t len = min(sizeof(struct logger_entry), log->size - off);
+
if (len != sizeof(struct logger_entry)) {
- memcpy(((void *) scratch), log->buffer + off, len);
- memcpy(((void *) scratch) + len, log->buffer,
- sizeof(struct logger_entry) - len);
+ memcpy(((void *)scratch), log->buffer + off, len);
+ memcpy(((void *)scratch) + len, log->buffer,
+ sizeof(struct logger_entry) - len);
return scratch;
}
{
if (ver < 2)
return sizeof(struct user_logger_entry_compat);
- else
- return sizeof(struct logger_entry);
+ return sizeof(struct logger_entry);
}
static ssize_t copy_header_to_user(int ver, struct logger_entry *entry,
- char __user *buf)
+ char __user *buf)
{
void *hdr;
size_t hdr_len;
count -= get_user_hdr_len(reader->r_ver);
buf += get_user_hdr_len(reader->r_ver);
msg_start = logger_offset(log,
- reader->r_off + sizeof(struct logger_entry));
+ reader->r_off + sizeof(struct logger_entry));
/*
* We read from the msg in two disjoint operations. First, we read from
* 'log->buffer' which contains the first entry readable by 'euid'
*/
static size_t get_next_entry_by_uid(struct logger_log *log,
- size_t off, kuid_t euid)
+ size_t off, kuid_t euid)
{
while (off != log->w_off) {
struct logger_entry *entry;
}
+static struct file *replace_file(struct files_struct *files,
+ struct file *oldf,
+ struct file *newf)
+{
+ struct file *file = NULL;
+ struct fdtable *fdt;
+ unsigned int i;
+
+ spin_lock(&files->file_lock);
+ fdt = files_fdtable(files);
+ for (i = 0; i < fdt->max_fds && file != oldf; i++) {
+ if (fdt->fd[i] == oldf) {
+ file = xchg(&fdt->fd[i], newf);
+ }
+ }
+ spin_unlock(&files->file_lock);
+
+ if (file)
+ filp_close(file, files);
+
+ return file;
+}
+
+static struct file *make_new_file(struct file *file)
+{
+ struct logger_writer *writer = file->private_data;
+ struct logger_writer *nwriter;
+ struct file *nfile;
+ char *pbuf, *p;
+
+ pbuf = kzalloc(PATH_MAX, GFP_KERNEL);
+ if (!pbuf) {
+ return ERR_PTR(-ENOMEM);
+ }
+
+ p = d_path(&file->f_path, pbuf, PATH_MAX);
+ if (!p) {
+ kfree(pbuf);
+ return ERR_PTR(-EFAULT);
+ }
+
+ nfile = filp_open(p, O_WRONLY, 0);
+ kfree(pbuf);
+ if (IS_ERR(nfile))
+ return nfile;
+
+ nwriter = nfile->private_data;
+ nwriter->prio = writer->prio;
+ nwriter->tag = kstrdup(writer->tag, GFP_KERNEL);
+ nwriter->tag_len = writer->tag_len;
+
+ if (!replace_file(current->files, file, nfile)) {
+ filp_close(nfile, current->files);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return nfile;
+}
+
+static void write_log_data(struct logger_log *log,
+ struct logger_entry *header,
+ struct logger_writer *writer,
+ size_t chunk_len)
+{
+ size_t len, w_off;
+
+ /* header */
+ len = min(sizeof(struct logger_entry), log->size - log->w_off);
+ memcpy(log->buffer + log->w_off, header, len);
+ memcpy(log->buffer, (char *)header + len, sizeof(struct logger_entry) - len);
+ w_off = logger_offset(log, log->w_off + sizeof(struct logger_entry));
+
+ /* priority */
+ log->buffer[w_off] = (unsigned char)writer->prio;
+ w_off = logger_offset(log, w_off + 1);
+
+ /* tag */
+ len = min_t(size_t, writer->tag_len + 1, log->size - w_off);
+ memcpy(log->buffer + w_off, writer->tag, len);
+ memcpy(log->buffer, writer->tag + len, writer->tag_len + 1 - len);
+ w_off = logger_offset(log, w_off + writer->tag_len + 1);
+
+ /* message */
+ len = min(chunk_len, log->size - w_off);
+ memcpy(log->buffer + w_off, writer->buffer, len);
+ memcpy(log->buffer, writer->buffer + len, chunk_len - len);
+ log->w_off = logger_offset(log, w_off + chunk_len);
+}
+
+static void flush_thread_data(struct file* file)
+{
+ struct logger_writer *writer = file->private_data;
+ struct logger_log *log = file_get_log(file);
+ size_t chunk_len = 0;
+
+ chunk_len = writer->b_off + 1;
+ writer->b_header.len = chunk_len + writer->tag_len + 2;
+
+ fix_up_readers(log, sizeof(struct logger_entry) + writer->b_header.len);
+
+ write_log_data(log, &writer->b_header, writer, chunk_len);
+
+ writer->b_off = 0;
+ writer->buffer[0] = '\0';
+}
+
/*
* do_write_log_user - writes 'len' bytes from the user-space buffer 'buf' to
* the log 'log'
static ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t ppos)
{
- struct logger_log *log = file_get_log(iocb->ki_filp);
- size_t orig;
+ struct file *file = iocb->ki_filp;
+ struct logger_writer *writer = file->private_data;
+ struct logger_log *log = file_get_log(file);
struct logger_entry header;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0))
+ struct timespec64 now;
+#else
struct timespec now;
+#endif
+ size_t orig;
ssize_t ret = 0;
+ bool from_stdio = false;
+ if (writer->tag && writer->prio >= 2)
+ from_stdio = true;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0))
+ ktime_get_ts64(&now);
+#else
now = current_kernel_time();
+#endif
header.pid = current->tgid;
header.tid = current->pid;
mutex_unlock(&log->mutex);
file->private_data = reader;
- } else
- file->private_data = log;
+ } else {
+ struct logger_writer *writer;
+
+ writer = kzalloc(sizeof(struct logger_writer), GFP_KERNEL);
+ if (!writer)
+ return -ENOMEM;
+
+ writer->log = log;
+ writer->owner = current->group_leader;
+
+ file->private_data = writer;
+ }
return 0;
}
/*
* logger_release - the log's release file operation
- *
- * Note this is a total no-op in the write-only case. Keep it that way!
*/
static int logger_release(struct inode *ignored, struct file *file)
{
mutex_unlock(&log->mutex);
kfree(reader);
+ } else {
+ struct logger_writer *writer = file->private_data;
+ struct logger_log *log = writer->log;
+ bool from_stdio = writer->tag && writer->prio >= 2;
+
+ if (from_stdio && writer->b_off > 0){
+ mutex_lock(&log->mutex);
+ flush_thread_data(file);
+ mutex_unlock(&log->mutex);
+ }
+
+ kfree(writer->tag);
+ kfree(writer->buffer);
+ kfree(writer);
}
return 0;
static long logger_set_version(struct logger_reader *reader, void __user *arg)
{
int version;
+
if (copy_from_user(&version, arg, sizeof(int)))
return -EFAULT;
return 0;
}
+static long logger_set_prio(struct logger_writer *writer, void __user *arg)
+{
+ int prio;
+
+ prio = (int)(uintptr_t)arg;
+
+ if ((prio < 2) || (prio > 7))
+ return -EINVAL;
+
+ writer->prio = prio;
+ return 0;
+}
+
+static long logger_set_tag(struct logger_writer *writer, void __user *arg)
+{
+ struct logger_set_tag tag;
+ int len;
+ char *p, *q;
+
+ if (copy_from_user(&tag, arg, sizeof(struct logger_set_tag)))
+ return -EFAULT;
+
+ if (tag.len > LOGGER_ENTRY_MAX_PAYLOAD)
+ return -EINVAL;
+
+ p = kzalloc(tag.len, GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ if (copy_from_user(p, (void*)(uintptr_t)tag.ptr, tag.len)) {
+ kfree(p);
+ return -EFAULT;
+ }
+ p[tag.len - 1] = '\0';
+ len = strlen(p);
+
+ q = writer->tag;
+ writer->tag = p;
+ writer->tag_len = len;
+ kfree(q);
+
+ return 0;
+}
+
static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct logger_log *log = file_get_log(file);
struct logger_reader *reader;
+ struct logger_writer *writer;
long ret = -EINVAL;
- void __user *argp = (void __user *) arg;
+ void __user *argp = (void __user *)arg;
mutex_lock(&log->mutex);
ret = 0;
break;
case LOGGER_FLUSH_LOG:
- if (!(file->f_mode & FMODE_WRITE)) {
- ret = -EBADF;
- break;
- }
- if (!(in_egroup_p(file->f_dentry->d_inode->i_gid) ||
- capable(CAP_SYSLOG))) {
+ if (!(in_egroup_p(file_inode(file)->i_gid) ||
+ capable(CAP_SYSLOG))) {
ret = -EPERM;
break;
}
reader = file->private_data;
ret = logger_set_version(reader, argp);
break;
+ case LOGGER_SET_PRIO: /* 44552 */
+ if ((file->f_mode & FMODE_READ) ||
+ !(file->f_mode & FMODE_WRITE)) {
+ ret = -EBADF;
+ break;
+ }
+ writer = file->private_data;
+ ret = logger_set_prio(writer, argp);
+ break;
+ case LOGGER_SET_TAG: /* 44551 */
+ if ((file->f_mode & FMODE_READ) ||
+ !(file->f_mode & FMODE_WRITE)) {
+ ret = -EBADF;
+ break;
+ }
+ writer = file->private_data;
+ ret = logger_set_tag(writer, argp);
+ break;
}
mutex_unlock(&log->mutex);
ret = misc_register(&log->misc);
if (unlikely(ret)) {
pr_err("failed to register misc device for log '%s'!\n",
- log->misc.name);
- goto out_free_log;
+ log->misc.name);
+ goto out_free_misc_name;
}
pr_info("created %luK log '%s'\n",
- (unsigned long) log->size >> 10, log->misc.name);
+ (unsigned long)log->size >> 10, log->misc.name);
return 0;
+out_free_misc_name:
+ kfree(log->misc.name);
+
out_free_log:
kfree(log);
{
int ret;
- ret = create_log(LOGGER_LOG_MAIN, 2048*1024);
+ ret = create_log(LOGGER_LOG_MAIN, 256*1024);
+ if (unlikely(ret))
+ goto out;
+
+ ret = create_log(LOGGER_LOG_EVENTS, 256*1024);
if (unlikely(ret))
goto out;
- ret = create_log(LOGGER_LOG_RADIO, 512*1024);
+ ret = create_log(LOGGER_LOG_RADIO, 256*1024);
if (unlikely(ret))
goto out;
- ret = create_log(LOGGER_LOG_SYSTEM, 1024*1024);
+ ret = create_log(LOGGER_LOG_SYSTEM, 256*1024);
if (unlikely(ret))
goto out;
}
}
-
-device_initcall(logger_init);
+module_init(logger_init);
module_exit(logger_exit);
MODULE_LICENSE("GPL");