source: ksyslog/trunk/ksyslog.c @ 240

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