source: ksyslog/trunk/ksyslog.c @ 247

Revision 247, 15.4 KB checked in by atzm, 11 years ago (diff)

udp_encap_enable() is needed in kernel 3.5.0 or later.

RevLine 
[232]1/*
2 * ksyslog: In-kernel syslog receiver
3 * Copyright(C) 2013 Atzm WATANABE All rights reserved
4 * Distributed under the GPL
5 */
6
[246]7#include <linux/version.h>
[232]8#include <linux/module.h>
[243]9#include <linux/inet.h>
[232]10#include <linux/ip.h>
11#include <linux/udp.h>
12#include <linux/namei.h>
[246]13#include <linux/proc_fs.h>
[232]14#include <net/udp.h>
[246]15#include "compat.h"
[232]16#include "ksyslog.h"
17
18static DEFINE_SPINLOCK(ksyslog_vfs_lock);
19
[242]20static struct ksyslog_queue ksyslog_queue;
[232]21static struct socket *ksyslog_rcv_sk = NULL;
22
23static struct delayed_work ksyslog_work;
24static struct workqueue_struct *ksyslog_wq = NULL;
25
26#ifdef CONFIG_PROC_FS
27static struct proc_dir_entry *ksyslog_procdir = NULL;
28static struct proc_dir_entry *ksyslog_proc_queue = NULL;
[240]29static struct proc_dir_entry *ksyslog_proc_nr_queued = NULL;
[241]30static struct proc_dir_entry *ksyslog_proc_nr_written = NULL;
31static struct proc_dir_entry *ksyslog_proc_nr_dropped = NULL;
[232]32#endif
33
34static char *ksyslog_host = "0.0.0.0";
35static ushort ksyslog_port = 514;
36static char *ksyslog_path = "/var/log/ksyslog.log";
[242]37static ulong ksyslog_queue_length = 4096;
[232]38static ulong ksyslog_flush_interval = 45;  /* milliseconds */
39
40module_param(ksyslog_host, charp, 0444);
41module_param(ksyslog_port, ushort, 0444);
42module_param(ksyslog_path, charp, 0644);
[241]43module_param(ksyslog_queue_length, ulong, 0644);
[232]44module_param(ksyslog_flush_interval, ulong, 0644);
45
[241]46static void
47ksyslog_queue_init(struct ksyslog_queue *queue)
48{
49        memset(queue, 0, sizeof(*queue));
50        INIT_LIST_HEAD(&queue->head);
[242]51        spin_lock_init(&queue->lock);
[241]52        atomic64_set(&queue->nr_queued, 0);
53        atomic64_set(&queue->nr_written, 0);
54        atomic64_set(&queue->nr_dropped, 0);
55}
56
[232]57static int
58ksyslog_close(struct file *file)
59{
60        return filp_close(file, NULL);
61}
62
63static struct file *
64ksyslog_open(const char *path)
65{
66        struct file *file;
[243]67        struct path ppath;
[232]68        mm_segment_t oldfs;
69
70        oldfs = get_fs();
71        set_fs(get_ds());
72
[243]73        if (unlikely(kern_path(path, LOOKUP_OPEN|LOOKUP_FOLLOW, &ppath)))
[232]74                file = filp_open(path, O_CREAT|O_WRONLY|O_APPEND|O_LARGEFILE, 0600);
75        else
76                file = filp_open(path, O_WRONLY|O_APPEND|O_LARGEFILE, 0);
77
[234]78        if (unlikely(IS_ERR(file)))
[232]79                goto out;
80
[234]81        if (unlikely(S_ISDIR(file->f_path.dentry->d_inode->i_mode))) {
[232]82                ksyslog_close(file);
83                file = ERR_PTR(-EISDIR);
84                goto out;
85        }
86
[234]87        if (unlikely(file->f_pos < 0)) {
[232]88                ksyslog_close(file);
89                file = ERR_PTR(-EIO);
90                goto out;
91        }
92
93out:
94        set_fs(oldfs);
95        return file;
96}
97
98static int
99ksyslog_write(struct file *file, const char *buf, const size_t length)
100{
101        int err;
102        mm_segment_t oldfs;
103
104        oldfs = get_fs();
105        set_fs(get_ds());
106
107        err = vfs_write(file, (__force void __user *)buf, length, &file->f_pos);
108
109        set_fs(oldfs);
110        return err;
111}
112
113static void
[234]114ksyslog_drop_warning(const struct ksyslog_entry *entry)
[232]115{
116        pr_warn("dropped: %llu %s.%s %u.%u.%u.%u %.*s\n",
117                timeval_to_ns(&entry->tv) / 1000 / 1000 / 1000,
118                ksyslog_facility_str(entry->facility),
119                ksyslog_severity_str(entry->severity),
120                entry->saddr.addr8[0], entry->saddr.addr8[1],
121                entry->saddr.addr8[2], entry->saddr.addr8[3],
122                (int)entry->length, entry->data);
123}
124
125static int
[234]126ksyslog_format(char **buf, const struct ksyslog_entry *entry)
[232]127{
128        *buf = kzalloc(54 + entry->length + 2, GFP_ATOMIC);
[234]129        if (unlikely(*buf == NULL))
[232]130                return -ENOMEM;
131
132        return sprintf(*buf, "%llu %s.%s %u.%u.%u.%u %.*s\n",
133                       timeval_to_ns(&entry->tv) / 1000 / 1000 / 1000,
134                       ksyslog_facility_str(entry->facility),
135                       ksyslog_severity_str(entry->severity),
136                       entry->saddr.addr8[0], entry->saddr.addr8[1],
137                       entry->saddr.addr8[2], entry->saddr.addr8[3],
138                       (int)entry->length, entry->data);
139}
140
141static struct ksyslog_entry *
[234]142ksyslog_entry_create(const struct sk_buff *skb,
143                     const struct iphdr *iph, const struct udphdr *udph)
[232]144{
145        struct ksyslog_entry *entry;
[235]146        unsigned int priority, facility, severity, month, day, hour, minute, second;
147        unsigned char *start, month_s[4];
148        struct tm tm;
[232]149        int length, i;
150
[235]151        if (sscanf(skb->data, "<%3u>%3s %2u %2u:%2u:%2u ",
152                   &priority, month_s, &day, &hour, &minute, &second) != 6)
[232]153                return ERR_PTR(-EINVAL);
154
155        start = memchr(skb->data, '>', 5);
156        if (start == NULL)
157                return ERR_PTR(-EINVAL);
158        start++;
159
160        facility = priority >> 3;
161        severity = priority & 7;
162
163        if (facility >= __KSYSLOG_F_MAX)
164                return ERR_PTR(-EINVAL);
165        if (severity >= __KSYSLOG_S_MAX)
166                return ERR_PTR(-EINVAL);
167
[235]168        month = ksyslog_month_num(month_s);
169        if (!month)
170                return ERR_PTR(-EINVAL);
171        if (day > 31)
172                return ERR_PTR(-EINVAL);
173        if (hour > 23)
174                return ERR_PTR(-EINVAL);
175        if (minute > 59)
176                return ERR_PTR(-EINVAL);
177        if (second > 59)
178                return ERR_PTR(-EINVAL);
179
[232]180        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
[234]181        if (unlikely(entry == NULL))
[232]182                return ERR_PTR(-ENOMEM);
183
184        length = skb->len - (start - skb->data);
185        entry->data = kzalloc(length, GFP_ATOMIC);
[234]186        if (unlikely(entry->data == NULL)) {
[232]187                kfree(entry);
188                return ERR_PTR(-ENOMEM);
189        }
190
191        if (skb->tstamp.tv64)
192                entry->tv = ktime_to_timeval(skb->tstamp);
193        else
194                do_gettimeofday(&entry->tv);
195
[235]196        time_to_tm(entry->tv.tv_sec, 0, &tm);
197        entry->time = mktime(tm.tm_year + 1900, month, day, hour, minute, second);
198
199        entry->priority = priority;
[234]200        entry->facility = facility;
201        entry->severity = severity;
202
[232]203        entry->daddr.addr32 = iph->daddr;
204        entry->saddr.addr32 = iph->saddr;
205
206        entry->dport = udph->dest;
207        entry->sport = udph->source;
208
209        entry->length = length;
210        memcpy(entry->data, start, length);
211
212        for (i = 0; i < length; i++)
[234]213                if (unlikely(entry->data[i] == '\n'))
[232]214                        entry->data[i] = ' ';
215
216        return entry;
217}
218
219static void
220ksyslog_entry_free(struct rcu_head *head)
221{
222        struct ksyslog_entry *entry = container_of(head, struct ksyslog_entry, rcu);
223        kfree(entry->data);
224        kfree(entry);
225}
226
227static int
228ksyslog_entry_add(struct ksyslog_queue *queue, struct ksyslog_entry *entry)
229{
[241]230        if (unlikely(atomic64_read(&queue->nr_queued) >= ksyslog_queue_length))
[232]231                return -ENOBUFS;
232        list_add_tail_rcu(&entry->list, &queue->head);
[241]233        WARN_ON(atomic64_inc_return(&queue->nr_queued) > ksyslog_queue_length);
[232]234        return 0;
235}
236
237static void
238ksyslog_entry_del(struct ksyslog_queue *queue, struct ksyslog_entry *entry, bool free)
239{
[241]240        WARN_ON(atomic64_dec_return(&queue->nr_queued) < 0);
[232]241        list_del_rcu(&entry->list);
242        if (free)
243                call_rcu(&entry->rcu, ksyslog_entry_free);
244}
245
246static void
247ksyslog_entry_destroy(struct ksyslog_queue *queue)
248{
249        struct ksyslog_entry *entry, *next;
250
251        list_for_each_entry_safe(entry, next, &queue->head, list)
252                ksyslog_entry_del(queue, entry, true);
253}
254
255static void
256ksyslog_entry_migrate(struct ksyslog_queue *from, struct ksyslog_queue *to)
257{
258        struct ksyslog_entry *entry, *next;
259
260        list_for_each_entry_safe(entry, next, &from->head, list) {
261                ksyslog_entry_del(from, entry, false);
[234]262                if (unlikely(ksyslog_entry_add(to, entry))) {
[241]263                        atomic64_inc(&from->nr_dropped);
264                        atomic64_inc(&to->nr_dropped);
[232]265                        ksyslog_drop_warning(entry);
266                        call_rcu(&entry->rcu, ksyslog_entry_free);
267                }
268        }
269}
270
271static void
272ksyslog_work_register(unsigned long timer)
273{
274        queue_delayed_work(ksyslog_wq, &ksyslog_work, timer * HZ / 1000);
275}
276
277static void
278ksyslog_work_unregister(void)
279{
280        cancel_delayed_work_sync(&ksyslog_work);
281}
282
283static void
284ksyslog_work_handler(struct work_struct *work)
285{
286        struct file *file = NULL;
287        struct ksyslog_entry *entry, *next;
288        struct ksyslog_queue write_queue;
289
[241]290        ksyslog_queue_init(&write_queue);
[232]291
[242]292        spin_lock_bh(&ksyslog_queue.lock);
[232]293        ksyslog_entry_migrate(&ksyslog_queue, &write_queue);
[242]294        spin_unlock_bh(&ksyslog_queue.lock);
[232]295
[241]296        if (atomic64_read(&write_queue.nr_queued) <= 0)
[232]297                goto out;
298
299        spin_lock(&ksyslog_vfs_lock);
300
301        file = ksyslog_open(ksyslog_path);
[234]302        if (unlikely(IS_ERR(file))) {
[232]303                spin_unlock(&ksyslog_vfs_lock);
304
[242]305                spin_lock_bh(&ksyslog_queue.lock);
[232]306                ksyslog_entry_migrate(&write_queue, &ksyslog_queue);
[242]307                spin_unlock_bh(&ksyslog_queue.lock);
[232]308
309                goto out;
310        }
311
312        list_for_each_entry_safe(entry, next, &write_queue.head, list) {
313                int length;
314                char *buf;
315
316                ksyslog_entry_del(&write_queue, entry, false);
317
318                length = ksyslog_format(&buf, entry);
[234]319                if (unlikely(length < 0))
[232]320                        goto restore;
321
[244]322                if (unlikely(ksyslog_write(file, buf, length) != length)) {
[232]323                        kfree(buf);
324                        goto restore;
325                }
326
[241]327                atomic64_inc(&ksyslog_queue.nr_written);
[232]328                kfree(buf);
329                call_rcu(&entry->rcu, ksyslog_entry_free);
330                continue;
331
332restore:
[242]333                spin_lock_bh(&ksyslog_queue.lock);
[234]334                if (unlikely(ksyslog_entry_add(&ksyslog_queue, entry))) {
[241]335                        atomic64_inc(&ksyslog_queue.nr_dropped);
[232]336                        ksyslog_drop_warning(entry);
337                        call_rcu(&entry->rcu, ksyslog_entry_free);
338                }
[242]339                spin_unlock_bh(&ksyslog_queue.lock);
[232]340        }
[234]341
[232]342        ksyslog_close(file);
343        spin_unlock(&ksyslog_vfs_lock);
344
345out:
346        ksyslog_work_register(ksyslog_flush_interval);
347}
348
349static int
350ksyslog_rcv(struct sock *sk, struct sk_buff *skb)
351{
352        int err;
353        struct iphdr *iph;
354        struct udphdr *udph;
355        struct ksyslog_entry *entry;
356
[241]357        if (unlikely(skb_linearize(skb)))
358                goto err;
[232]359
360        iph = ip_hdr(skb);
361        udph = udp_hdr(skb);
362
[241]363        if (unlikely(!skb_pull(skb, sizeof(*udph))))
364                goto err;
[232]365
366        entry = ksyslog_entry_create(skb, iph, udph);
[241]367        if (unlikely(IS_ERR(entry)))
368                goto err;
[232]369
[242]370        spin_lock_bh(&ksyslog_queue.lock);
[232]371        err = ksyslog_entry_add(&ksyslog_queue, entry);
[242]372        spin_unlock_bh(&ksyslog_queue.lock);
[232]373
[241]374        if (unlikely(err)) {
375                ksyslog_drop_warning(entry);
[232]376                ksyslog_entry_free(&entry->rcu);
[241]377                goto err;
378        }
[232]379
380out:
381        consume_skb(skb);
382        return 0;
[241]383
384err:
385        atomic64_inc(&ksyslog_queue.nr_dropped);
386        goto out;
[232]387}
388
389#ifdef CONFIG_PROC_FS
390static void *
[240]391ksyslog_rculist_seq_start(struct seq_file *seq, loff_t *pos)
[232]392{
393        struct list_head *lh, *head = seq->private;
394        loff_t ppos = *pos;
395
396        rcu_read_lock();
397
398        __list_for_each_rcu(lh, head)
399                if (ppos-- == 0)
400                        return lh;
401
402        return NULL;
403}
404
405static void *
[240]406ksyslog_rculist_seq_next(struct seq_file *seq, void *v, loff_t *pos)
[232]407{
408        struct list_head *lh = rcu_dereference(((struct list_head *)v)->next);
409        ++(*pos);
410        return lh == seq->private ? NULL : lh;
411}
412
413static void
[240]414ksyslog_rculist_seq_stop(struct seq_file *seq, void *v)
[232]415{
416        rcu_read_unlock();
417}
418
419static int
420ksyslog_queue_seq_show(struct seq_file *seq, void *v)
421{
422        const struct ksyslog_entry *entry = list_entry_rcu(v, struct ksyslog_entry, list);
423
[233]424        seq_printf(seq, "%llu %s.%s %u.%u.%u.%u %.*s\n",
[232]425                   timeval_to_ns(&entry->tv) / 1000 / 1000 / 1000,
[233]426                   ksyslog_facility_str(entry->facility),
427                   ksyslog_severity_str(entry->severity),
[232]428                   entry->saddr.addr8[0], entry->saddr.addr8[1],
429                   entry->saddr.addr8[2], entry->saddr.addr8[3],
430                   (int)entry->length, entry->data);
431
432        return 0;
433}
434
435static struct seq_operations ksyslog_queue_seq_ops = {
[240]436        .start = ksyslog_rculist_seq_start,
437        .next  = ksyslog_rculist_seq_next,
438        .stop  = ksyslog_rculist_seq_stop,
[232]439        .show  = ksyslog_queue_seq_show,
440};
441
442static int
443ksyslog_queue_seq_open(struct inode *inode, struct file *file)
444{
445        int err = seq_open(file, &ksyslog_queue_seq_ops);
446
447        if (!err)
[246]448                ((struct seq_file *)file->private_data)->private = PDE_DATA(inode);
[232]449
450        return err;
451}
452
453static struct file_operations ksyslog_queue_fops = {
454        .owner   = THIS_MODULE,
455        .open    = ksyslog_queue_seq_open,
456        .read    = seq_read,
457        .llseek  = seq_lseek,
458        .release = seq_release,
459};
460
461static int
[240]462ksyslog_nr_queued_seq_show(struct seq_file *seq, void *v)
463{
[241]464        seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_queued));
[240]465        return 0;
466}
467
468static int
469ksyslog_nr_queued_seq_open(struct inode *inode, struct file *file)
470{
[246]471        return single_open(file, ksyslog_nr_queued_seq_show, PDE_DATA(inode));
[240]472}
473
[241]474static int
475ksyslog_nr_written_seq_show(struct seq_file *seq, void *v)
476{
477        seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_written));
478        return 0;
479}
480
481static int
482ksyslog_nr_written_seq_open(struct inode *inode, struct file *file)
483{
[246]484        return single_open(file, ksyslog_nr_written_seq_show, PDE_DATA(inode));
[241]485}
486
487static int
488ksyslog_nr_dropped_seq_show(struct seq_file *seq, void *v)
489{
490        seq_printf(seq, "%lu\n", atomic64_read(&ksyslog_queue.nr_dropped));
491        return 0;
492}
493
494static int
495ksyslog_nr_dropped_seq_open(struct inode *inode, struct file *file)
496{
[246]497        return single_open(file, ksyslog_nr_dropped_seq_show, PDE_DATA(inode));
[241]498}
499
[240]500static struct file_operations ksyslog_nr_queued_fops = {
501        .owner   = THIS_MODULE,
502        .open    = ksyslog_nr_queued_seq_open,
503        .read    = seq_read,
504        .llseek  = seq_lseek,
505        .release = single_release,
506};
507
[241]508static struct file_operations ksyslog_nr_written_fops = {
509        .owner   = THIS_MODULE,
510        .open    = ksyslog_nr_written_seq_open,
511        .read    = seq_read,
512        .llseek  = seq_lseek,
513        .release = single_release,
514};
515
516static struct file_operations ksyslog_nr_dropped_fops = {
517        .owner   = THIS_MODULE,
518        .open    = ksyslog_nr_dropped_seq_open,
519        .read    = seq_read,
520        .llseek  = seq_lseek,
521        .release = single_release,
522};
523
524static void
525ksyslog_proc_destroy(void)
526{
527        if (ksyslog_proc_queue)
[246]528                remove_proc_entry("queue", ksyslog_procdir);
[241]529        ksyslog_proc_queue = NULL;
530
531        if (ksyslog_proc_nr_queued)
[246]532                remove_proc_entry("nr_queued", ksyslog_procdir);
[241]533        ksyslog_proc_nr_queued = NULL;
534
535        if (ksyslog_proc_nr_written)
[246]536                remove_proc_entry("nr_written", ksyslog_procdir);
[241]537        ksyslog_proc_nr_written = NULL;
538
539        if (ksyslog_proc_nr_dropped)
[246]540                remove_proc_entry("nr_dropped", ksyslog_procdir);
[241]541        ksyslog_proc_nr_dropped = NULL;
542
543        if (ksyslog_procdir)
[246]544                remove_proc_entry("ksyslog", NULL);
[241]545        ksyslog_procdir = NULL;
546}
547
[240]548static int
[232]549ksyslog_proc_init(void)
550{
551        ksyslog_procdir = proc_mkdir("ksyslog", NULL);
552        if (ksyslog_procdir == NULL) {
553                pr_err("proc_mkdir failed\n");
[241]554                goto err;
[232]555        }
556
[239]557        ksyslog_proc_queue = proc_create_data("queue", S_IRUGO, ksyslog_procdir,
558                                              &ksyslog_queue_fops, &ksyslog_queue.head);
[232]559        if (ksyslog_proc_queue == NULL) {
[241]560                pr_err("proc_create(queue) failed\n");
561                goto err;
[232]562        }
563
[240]564        ksyslog_proc_nr_queued = proc_create("nr_queued", S_IRUGO, ksyslog_procdir,
[245]565                                             &ksyslog_nr_queued_fops);
[240]566        if (ksyslog_proc_nr_queued == NULL) {
[241]567                pr_err("proc_create(nr_queued) failed\n");
568                goto err;
[240]569        }
570
[241]571        ksyslog_proc_nr_written = proc_create("nr_written", S_IRUGO, ksyslog_procdir,
572                                              &ksyslog_nr_written_fops);
573        if (ksyslog_proc_nr_written == NULL) {
574                pr_err("proc_create(nr_written) failed\n");
575                goto err;
576        }
[232]577
[241]578        ksyslog_proc_nr_dropped = proc_create("nr_dropped", S_IRUGO, ksyslog_procdir,
579                                              &ksyslog_nr_dropped_fops);
580        if (ksyslog_proc_nr_dropped == NULL) {
581                pr_err("proc_create(nr_dropped) failed\n");
582                goto err;
583        }
[240]584
[241]585        return 0;
[232]586
[241]587err:
588        ksyslog_proc_destroy();
589        return -ENOMEM;
[232]590}
591#endif
592
593static void
594ksyslog_finish(void)
595{
596        if (ksyslog_rcv_sk)
597                sock_release(ksyslog_rcv_sk);
598        ksyslog_rcv_sk = NULL;
599
600        if (ksyslog_wq) {
601                ksyslog_work_unregister();
602                destroy_workqueue(ksyslog_wq);
603        }
604        ksyslog_wq = NULL;
605
606#ifdef CONFIG_PROC_FS
607        ksyslog_proc_destroy();
608#endif
609
610        ksyslog_entry_destroy(&ksyslog_queue);
[237]611        rcu_barrier();
[232]612}
613
614static int __init
615ksyslog_init(void)
616{
617        int err;
618        struct sockaddr_in sin;
619
[241]620        ksyslog_queue_init(&ksyslog_queue);
[232]621
622#ifdef CONFIG_PROC_FS
623        err = ksyslog_proc_init();
624        if (err)
625                goto err;
626#endif
627
628        ksyslog_wq = create_singlethread_workqueue("ksyslog");
629        if (ksyslog_wq == NULL) {
630                pr_err("create_workqueue failed\n");
631                err = -ENOMEM;
632                goto err;
633        }
634
635        INIT_DELAYED_WORK(&ksyslog_work, ksyslog_work_handler);
636
637        err = sock_create(AF_INET, SOCK_DGRAM, 0, &ksyslog_rcv_sk);
638        if (err) {
639                pr_err("sock_create failed\n");
640                goto err;
641        }
642
643        sin.sin_family = AF_INET;
644        sin.sin_addr.s_addr = in_aton(ksyslog_host);
645        sin.sin_port = htons(ksyslog_port);
646
647        err = kernel_bind(ksyslog_rcv_sk, (struct sockaddr *)&sin,
648                          sizeof(struct sockaddr_in));
649        if (err) {
650                pr_err("kernel_bind failed\n");
651                goto err;
652        }
653
[247]654        ksyslog_work_register(ksyslog_flush_interval);
655
[232]656        udp_sk(ksyslog_rcv_sk->sk)->encap_type = UDP_ENCAP_KSYSLOG;
657        udp_sk(ksyslog_rcv_sk->sk)->encap_rcv = ksyslog_rcv;
[247]658        udp_encap_enable();
[232]659
660        return 0;
661
662err:
663        ksyslog_finish();
664        return err;
665}
666
667static void __exit
668ksyslog_exit(void)
669{
670        ksyslog_finish();
671}
672
673module_init(ksyslog_init);
674module_exit(ksyslog_exit);
675
676MODULE_AUTHOR("Atzm WATANABE");
677MODULE_DESCRIPTION("In-kernel syslog receiver");
678MODULE_LICENSE("GPL");
Note: See TracBrowser for help on using the repository browser.