source: ksyslog/trunk/ksyslog.c @ 263

Revision 263, 16.5 KB checked in by atzm, 11 years ago (diff)

set segment on close

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