source: ksyslog/trunk/ksyslog.c @ 233

Revision 233, 11.6 KB checked in by atzm, 11 years ago (diff)

change /proc/ksyslog/queue format

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