source: ksyslog/trunk/ksyslog.c @ 238

Revision 238, 12.3 KB checked in by atzm, 11 years ago (diff)

add initialization for entry->rcu

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