source: ksyslog/trunk/ksyslog.c @ 242

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

move ksyslog_queue_lock into struct ksyslog_queue

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