source: ksyslog/trunk/ksyslog.c @ 241

Revision 241, 15.5 KB checked in by atzm, 11 years ago (diff)

add nr_written and nr_dropped

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