Thread: [Netadm-devel] gwc/pf sysklog.h,NONE,1.1 sysklog.c,NONE,1.1
Status: Beta
Brought to you by:
linuxpark
From: linuxpark <lin...@us...> - 2006-05-09 00:47:16
|
Update of /cvsroot/netadm/gwc/pf In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv26320a Added Files: sysklog.h sysklog.c Log Message: Initial Check-In sysklog : kernel logging module just only for gwc kernel modules kernel module must not use printk instead, use "gwc_printk" int gwc_printk (flag, fmt, args ...) flag: definition: include/global.h GWC_PF_IDX : packet or session log, log file: gwc_pf.log GWC_KSYS_IDX : system log, log file: gwc_ksys.log fmt, args : it is same as printk () example) gwc_printk (GWC_PF_IDX, "%s: some ugly packet IP: %u.%u.%u.%u received\n", DEVICE_NAME, NIPQUAD (addr)); --- NEW FILE: sysklog.c --- /* Title : sysklog.c Author : Jeho-Park <de...@sk...> Created date : 2006. 05. 05. (Fri) 15:02:31 KST Description : gwc kernel log module for logging other modules of gwc's idea and base code was from linux/kernel/printk.c and fs/proc/misc.c */ #ident "@(#) $Header: /cvsroot/netadm/gwc/pf/sysklog.c,v 1.1 2006/05/06 15:17:47 linuxpark Exp $" #include <linux/kernel.h> #include <linux/mm.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/smp_lock.h> #include <linux/console.h> #include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> /* For in_interrupt() */ #include <linux/sched.h> #include <linux/config.h> #include <linux/delay.h> #include <linux/smp.h> #include <linux/security.h> #include <linux/syscalls.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/time.h> #include <linux/poll.h> #include <linux/fs.h> #include <asm/io.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include "sysklog.h" #include "../include/global.h" static const char gwc_klog_driver_name[] = "gwc-klog"; static const char gwc_klog_driver_string[] = "gwc kernel log module"; #define gwc_klog_driver_version "0.0.1" MODULE_AUTHOR("jeho park <de...@sk...>"); MODULE_DESCRIPTION("gwc kernel log module"); MODULE_LICENSE("GPL"); MODULE_VERSION(gwc_klog_driver_version); static unsigned long debug = DBG_GENERAL; module_param(debug, ulong, 0644); MODULE_PARM_DESC(debug, "debug bitmap"); /* We show everything that is MORE important than this.. */ /* * TODO: if you want more sub entry, follow these step * 1. Add index GWC_XXX_IDX and recount MAXSYSIDX * 2. Add proc entry * 3. Add proc handlers * * --LP */ #ifndef SET_MODULE_OWNER #define SET_MODULE_OWNER(p) do { p->owner = THIS_MODULE; } while (0) #endif #if defined (USE_KLOG_PROC) && defined (CONFIG_PROC_FS) static struct proc_dir_entry *proc_gwc_root; static struct proc_dir_entry *gwc_pf_entry; static struct proc_dir_entry *gwc_sysk_entry; #else static int kctl_rcnt [MAXSYSIDX]; #endif static spinlock_t logbuf_lock [MAXSYSIDX]; static wait_queue_head_t log_wait [MAXSYSIDX]; /* * The indices into log_buf are not constrained to log_buf_len - they * must be masked before subscripting */ static unsigned long log_start [MAXSYSIDX]; static unsigned long log_end [MAXSYSIDX]; static char __log_buf[MAXSYSIDX][__SYS_LOG_BUFLEN]; static char *log_buf [MAXSYSIDX]; static int log_buf_len = __SYS_LOG_BUFLEN; static unsigned long logged_chars[MAXSYSIDX]; static void init_gwc_logbuf (void ) { int i; for (i = 0; i < MAXSYSIDX; i++) { logbuf_lock [i] = SPIN_LOCK_UNLOCKED; log_buf [i] = __log_buf [i]; } } #undef LOG_BUF_MASK #define LOG_BUF_MASK (log_buf_len-1) #undef LOG_BUF #define LOG_BUF(idx, w) (log_buf[idx][(w) & LOG_BUF_MASK]) static void init_gwc_wait_queue_heads (wait_queue_head_t *wq, int max) { int i; for (i = 0; i < max; i++) { wq[i].lock = SPIN_LOCK_UNLOCKED; wq[i].task_list.next = wq[i].task_list.prev = &wq[i].task_list; } } /* * Commands to do_gwc_sysklog: * * 0 -- Close the log. Currently a NOP. * 1 -- Open the log. Currently a NOP. * 2 -- Read from the log. * 3 -- Read all messages remaining in the ring buffer. * 4 -- Read and clear all messages remaining in the ring buffer * 5 -- Clear ring buffer. * 6 -- Disable printk's to console * 7 -- Enable printk's to console * 8 -- Set level of messages printed to console * 9 -- Return number of unread characters in the log buffer * 10 -- Return size of the log buffer */ int do_gwc_sysklog(int idx, int type, char __user * buf, int len) { unsigned long i, j, limit, count; int do_clear = 0; char c; int error = 0; error = security_syslog(type); if (error) return error; switch (type) { case 0: /* Close log */ dprintk("\n"); if (kctl_rcnt [idx] <= 0 ) { printk ("%s: Device(type: %d) already released for reading\n", gwc_klog_driver_name, kctl_rcnt [idx]); return error; } else { --kctl_rcnt [idx]; } module_put(THIS_MODULE); break; case 1: /* Open log */ dprintk("\n"); if (kctl_rcnt [idx]) { printk ("%s: Device(type: %d) already open for reading\n", gwc_klog_driver_name, kctl_rcnt [idx]); return -EBUSY; } else { ++kctl_rcnt [idx]; } try_module_get(THIS_MODULE); break; case 2: /* Read from log */ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } error = wait_event_interruptible(log_wait[idx], (log_start[idx] - log_end[idx])); if (error) goto out; i = 0; spin_lock_irq(&logbuf_lock[idx]); while (!error && (log_start[idx] != log_end[idx]) && i < len) { c = LOG_BUF(idx, log_start[idx]); log_start[idx]++; spin_unlock_irq(&logbuf_lock[idx]); error = __put_user(c,buf); buf++; i++; cond_resched(); spin_lock_irq(&logbuf_lock[idx]); } spin_unlock_irq(&logbuf_lock[idx]); if (!error) error = i; break; case 4: /* Read/clear last kernel messages */ dprintk("\n"); /* TODO: delete me --LP */ do_clear = 1; /* FALL THRU */ case 3: /* Read last kernel messages */ dprintk("\n");/* TODO: delete me --LP */ error = -EINVAL; if (!buf || len < 0) goto out; error = 0; if (!len) goto out; if (!access_ok(VERIFY_WRITE, buf, len)) { error = -EFAULT; goto out; } count = len; if (count > log_buf_len) count = log_buf_len; spin_lock_irq(&logbuf_lock[idx]); if (count > logged_chars[idx]) count = logged_chars[idx]; if (do_clear) logged_chars[idx] = 0; limit = log_end [idx]; /* * __put_user() could sleep, and while we sleep * printk() could overwrite the messages * we try to copy to user space. Therefore * the messages are copied in reverse. <manfreds> */ for(i = 0; i < count && !error; i++) { j = limit-1-i; if (j + log_buf_len < log_end [idx]) break; c = LOG_BUF(idx, j); spin_unlock_irq(&logbuf_lock[idx]); error = __put_user(c,&buf[count-1-i]); cond_resched(); spin_lock_irq(&logbuf_lock [idx]); } spin_unlock_irq(&logbuf_lock[idx]); if (error) break; error = i; if(i != count) { int offset = count-error; /* buffer overflow during copy, correct user buffer. */ for(i=0;i<error;i++) { if (__get_user(c,&buf[i+offset]) || __put_user(c,&buf[i])) { error = -EFAULT; break; } cond_resched(); } } break; case 5: /* Clear ring buffer */ logged_chars[idx] = 0; break; case 9: /* Number of chars in the log buffer */ error = log_end[idx] - log_start[idx]; break; case 10: /* Size of the log buffer */ error = log_buf_len; break; default: error = -EINVAL; break; } out: return error; } #if 0 /* i don't have interest this syscall 3 --LP */ #ifdef CONFIG_GWC_SYSLOG_SYSCALL asmlinkage long sys_gwc_syslog(int idx, int type, char __user * buf, int len) { return do_gwc_sysklog(idx, type, buf, len); } #endif /* #ifdef CONFIG_GWC_SYSLOG_SYSCALL */ #endif static void emit_log_char(int idx, char c) { LOG_BUF(idx, log_end[idx]) = c; log_end[idx]++; if (log_end[idx] - log_start[idx] > log_buf_len) log_start[idx] = log_end[idx] - log_buf_len; if (logged_chars[idx] < log_buf_len) logged_chars[idx]++; } /* * Zap console related locks when oopsing. Only zap at most once * every 10 seconds, to leave time for slow consoles to print a * full oops. */ static void zap_locks(int idx) { static unsigned long oops_timestamp; if (time_after_eq(jiffies, oops_timestamp) && !time_after(jiffies, oops_timestamp + 30*HZ)) return; oops_timestamp = jiffies; /* If a crash is occurring, make sure we can't deadlock */ spin_lock_init(&logbuf_lock [idx]); } static int printk_time = 1; __attribute__((weak)) unsigned long long gwc_printk_clock(void) { return (unsigned long long)jiffies * (1000000000 / HZ); } /* * gwc_printk rate limiting, lifted from the networking subsystem. * * This enforces a rate limit: not more than one kernel message * every printk_ratelimit_jiffies to make a denial-of-service * attack impossible. */ int __gwc_printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst) { static DEFINE_SPINLOCK(ratelimit_lock); static unsigned long toks = 10*5*HZ; static unsigned long last_msg; static int missed; unsigned long flags; unsigned long now = jiffies; spin_lock_irqsave(&ratelimit_lock, flags); toks += now - last_msg; last_msg = now; if (toks > (ratelimit_burst * ratelimit_jiffies)) toks = ratelimit_burst * ratelimit_jiffies; if (toks >= ratelimit_jiffies) { int lost = missed; missed = 0; toks -= ratelimit_jiffies; spin_unlock_irqrestore(&ratelimit_lock, flags); if (lost) printk(KERN_WARNING "printk: %d messages suppressed.\n", lost); return 1; } missed++; spin_unlock_irqrestore(&ratelimit_lock, flags); return 0; } /* minimum time in jiffies between messages */ static int printk_ratelimit_jiffies = 1*HZ; /* number of messages we send before ratelimiting */ static int printk_ratelimit_burst = 1; int gwc_printk_ratelimit(void) { return __gwc_printk_ratelimit(printk_ratelimit_jiffies, printk_ratelimit_burst); } /* cpu currently holding logbuf_lock */ static volatile unsigned int printk_cpu = UINT_MAX; asmlinkage int gwc_vprintk(int idx, const char *fmt, va_list args) { unsigned long flags; int printed_len; char *p; static char printk_buf[1024]; static int log_level_unknown = 1; preempt_disable(); if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id()) /* If a crash is occurring during printk() on this CPU, * make sure we can't deadlock */ zap_locks(idx); /* This stops the holder of console_sem just where we want him */ spin_lock_irqsave(&logbuf_lock[idx], flags); printk_cpu = smp_processor_id(); /* Emit the output into the temporary buffer */ printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); /* * Copy the output into log_buf. If the caller didn't provide * appropriate log level tags, we insert them here */ for (p = printk_buf; *p; p++) { if (log_level_unknown) { /* log_level_unknown signals the start of a new line */ if (printk_time) { int loglev_char; char tbuf[50], *tp; unsigned tlen; unsigned long long t; unsigned long nanosec_rem; /***********************************************************************************/ /* TODO: * force the log level token to be * before the time output. * *********************************************************************************/ if (p[0] == '<' && p[1] >='0' && p[1] <= '9' && p[2] == '>') { loglev_char = p[1]; p += 3; printed_len += 3; } else { loglev_char = default_message_loglevel + '0'; } t = gwc_printk_clock(); nanosec_rem = do_div(t, 1000000000); tlen = sprintf(tbuf, "<%c>[%5lu.%06lu] ", loglev_char, (unsigned long)t, nanosec_rem/1000); for (tp = tbuf; tp < tbuf + tlen; tp++) emit_log_char(idx, *tp); printed_len += tlen - 3; } else { if (p[0] != '<' || p[1] < '0' || p[1] > '9' || p[2] != '>') { emit_log_char(idx, '<'); emit_log_char(idx, default_message_loglevel + '0'); emit_log_char(idx, '>'); } printed_len += 3; } log_level_unknown = 0; if (!*p) break; } emit_log_char(idx, *p); if (*p == '\n') log_level_unknown = 1; } if (!cpu_online(smp_processor_id())) { /* * Some console drivers may assume that per-cpu resources have * been allocated. So don't allow them to be called by this * CPU until it is officially up. We shouldn't be calling into * random console drivers on a CPU which doesn't exist yet.. */ printk_cpu = UINT_MAX; spin_unlock_irqrestore(&logbuf_lock, flags); goto out; } printk_cpu = UINT_MAX; spin_unlock_irqrestore(&logbuf_lock, flags); out: preempt_enable(); return printed_len; } /* * This is gwc_printk. It can be called from any context. We want it to work. */ asmlinkage int gwc_printk(int idx, const char *fmt, ...) { va_list args; int r; va_start(args, fmt); r = gwc_vprintk(idx, fmt, args); va_end(args); wake_up_interruptible (&log_wait[idx]); return r; } EXPORT_SYMBOL_GPL (gwc_printk); /* * gwc kctl file handler lists * * @sys_xxxmsg_{open|release|read|poll|} */ static int gwc_pf_open(struct inode * inode, struct file * file) { return do_gwc_sysklog(GWC_PF_IDX, 1, NULL, 0); } static int gwc_pf_release(struct inode * inode, struct file * file) { (void) do_gwc_sysklog(GWC_PF_IDX, 0, NULL, 0); return 0; } static ssize_t gwc_pf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { if ((file->f_flags & O_NONBLOCK) && !do_gwc_sysklog(GWC_PF_IDX, 9, NULL, 0)) return -EAGAIN; return do_gwc_sysklog(GWC_PF_IDX, 2, buf, count); } static unsigned int gwc_pf_poll(struct file *file, poll_table *wait) { poll_wait(file, &log_wait[GWC_PF_IDX], wait); if (do_gwc_sysklog(GWC_PF_IDX, 9, NULL, 0)) return POLLIN | POLLRDNORM; return 0; } struct file_operations gwc_pf_operations = { .read = gwc_pf_read, .poll = gwc_pf_poll, .open = gwc_pf_open, .release = gwc_pf_release, }; static int gwc_sys_open(struct inode * inode, struct file * file) { dprintk ("\n"); return do_gwc_sysklog(GWC_KSYS_IDX, 1, NULL, 0); } static int gwc_sys_release(struct inode * inode, struct file * file) { dprintk ("\n"); (void) do_gwc_sysklog(GWC_KSYS_IDX, 0, NULL, 0); return 0; } static ssize_t gwc_sys_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { if ((file->f_flags & O_NONBLOCK) && !do_gwc_sysklog(GWC_KSYS_IDX, 9, NULL, 0)) return -EAGAIN; return do_gwc_sysklog(GWC_KSYS_IDX, 2, buf, count); } static unsigned int gwc_sys_poll(struct file *file, poll_table *wait) { static int i = 0; printk ("------->wait ... (%d)\n", i++); poll_wait(file, &log_wait[GWC_KSYS_IDX], wait); printk ("-------------------->cool !(%d)\n", i++); if (do_gwc_sysklog(GWC_KSYS_IDX, 9, NULL, 0)) return POLLIN | POLLRDNORM; return 0; } struct file_operations gwc_sys_operations = { .read = gwc_sys_read, .poll = gwc_sys_poll, .open = gwc_sys_open, .release = gwc_sys_release, }; static void init_gwc_kctl (void) { #if defined (USE_KLOG_PROC) && defined (CONFIG_PROC_FS) proc_gwc_root = proc_mkdir(GWC_PROC_DIR, &proc_root); if (proc_gwc_root) { SET_MODULE_OWNER(proc_gwc_root); } else { printk (KERN_WARNING "%s: Failed to create \'%s\' proc dir\n", gwc_klog_driver_name, GWC_PROC_DIR); return; } gwc_pf_entry = create_proc_entry(PF_PROC_ENTRY, S_IFREG | S_IRUSR, proc_gwc_root); if (gwc_pf_entry) { gwc_pf_entry->proc_fops = &gwc_pf_operations; SET_MODULE_OWNER(gwc_pf_entry); } else { printk (KERN_WARNING "%s: Failed to create \'%s\' proc entry\n", gwc_klog_driver_name, PF_PROC_ENTRY); } gwc_sysk_entry = create_proc_entry("syskmsg", S_IFREG | S_IRUSR, proc_gwc_root); if (gwc_sysk_entry) { gwc_sysk_entry->proc_fops = &gwc_sys_operations; SET_MODULE_OWNER(gwc_sysk_entry); } else { printk (KERN_WARNING "%s: Failed to create \'%s\' proc entry\n", gwc_klog_driver_name, SYS_PROC_ENTRY); } #else int i; if (register_chrdev(GWC_PF_MAJORNUM, GWC_PF_DNAME, &gwc_pf_operations) < 0) { printk(KERN_WARNING "%s: Failed to Registrate %s\n", gwc_klog_driver_name, GWC_PF_DNAME); return; } if (register_chrdev(GWC_SYS_MAJORNUM, GWC_SYS_DNAME, &gwc_sys_operations) < 0) { printk(KERN_WARNING "%s: Failed to Registrate %s\n", gwc_klog_driver_name, GWC_SYS_DNAME); return; } for (i = 0; i < MAXSYSIDX; i++) kctl_rcnt [i] = 0; #endif printk (KERN_INFO "%s: Success to load gwc kctl module ...\n", gwc_klog_driver_name); } static void destroy_gwc_proc (void) { #if defined (USE_KLOG_PROC) && defined (CONFIG_PROC_FS) remove_proc_entry (PF_PROC_ENTRY, proc_gwc_root); remove_proc_entry (SYS_PROC_ENTRY, proc_gwc_root); remove_proc_entry (GWC_PROC_DIR, &proc_root); #else printk (KERN_INFO "%s: Unloading gwc kctl module ...\n", gwc_klog_driver_name); if (unregister_chrdev(GWC_PF_MAJORNUM, GWC_PF_DNAME) < 0) { printk(KERN_WARNING "%s: Failed to Unregister %s\n", gwc_klog_driver_name, GWC_PF_DNAME); } if (unregister_chrdev(GWC_SYS_MAJORNUM, GWC_SYS_DNAME) < 0) { printk(KERN_WARNING "%s: Failed to Unregister %s\n", gwc_klog_driver_name, GWC_SYS_DNAME); } printk (KERN_INFO "%s: Success to unregister gwc kctl module ...\n", gwc_klog_driver_name); #endif } static int gwc_klog_init(void) { printk (KERN_INFO "Registering %s-%s ...\n", gwc_klog_driver_name, gwc_klog_driver_version); init_gwc_wait_queue_heads (log_wait, MAXSYSIDX); init_gwc_logbuf (); init_gwc_kctl (); gwc_printk_ratelimit (); return 0; } module_init(gwc_klog_init); static void __exit gwc_klog_exit(void) { printk (KERN_INFO "Unregistering %s-%s ...\n", gwc_klog_driver_name, gwc_klog_driver_version); destroy_gwc_proc (); } module_exit(gwc_klog_exit); --- NEW FILE: sysklog.h --- /* Title : sysklog.h Author : Jeho-Park <de...@sk...> Created date : 2006. 05. 05. (Fri) 15:02:31 KST Description : gwc kernel log module header for logging other modules of gwc's idea and base code was from linux/kernel/printk.c and fs/proc/misc.c */ #ident "@(#) $Header: /cvsroot/netadm/gwc/pf/sysklog.h,v 1.1 2006/05/06 15:17:47 linuxpark Exp $" #ifndef __SYS_SYSKLOG_H #define __SYS_SYSKLOG_H #include "../include/global.h" #define __SYS_LOG_BUFLEN (1 << CONFIG_LOG_BUF_SHIFT) /* We show everything that is MORE important than this.. */ /* * TODO: if you want more sub entry, follow these step * 1. Add index GWC_XXX_IDX and recount MAXSYSIDX * 2. Add proc entry * 3. Add proc handlers * * --LP */ #ifndef SET_MODULE_OWNER #define SET_MODULE_OWNER(p) do { p->owner = THIS_MODULE; } while (0) #endif /* * Commands to do_gwc_sysklog: * * 0 -- Close the log. Currently a NOP. * 1 -- Open the log. Currently a NOP. * 2 -- Read from the log. * 3 -- Read all messages remaining in the ring buffer. * 4 -- Read and clear all messages remaining in the ring buffer * 5 -- Clear ring buffer. * 6 -- Disable printk's to console * 7 -- Enable printk's to console * 8 -- Set level of messages printed to console * 9 -- Return number of unread characters in the log buffer * 10 -- Return size of the log buffer */ #if 0 int do_gwc_sysklog(int idx, int type, char __user * buf, int len); #endif #ifdef CONFIG_GWC_SYSLOG_SYSCALL asmlinkage long sys_gwc_syslog(int idx, int type, char __user * buf, int len); #endif /* #ifdef CONFIG_GWC_SYSLOG_SYSCALL */ /* * This is gwc_printk. It can be called from any context. We want it to work. * * We try to grab the console_sem. If we succeed, it's easy - we log the output and * call the console drivers. If we fail to get the semaphore we place the output * into the log buffer and return. The current holder of the console_sem will * notice the new output in release_console_sem() and will send it to the * consoles before releasing the semaphore. * * One effect of this deferred printing is that code which calls printk() and * then changes console_loglevel may break. This is because console_loglevel * is inspected when the actual printing occurs. */ asmlinkage int gwc_printk(int idx, const char *fmt, ...); #if 0 /* TODO: compile error --LP */ static inline int gwc_pflog ( const char *fmt, args...) { return gwc_printk (GWC_PF_IDX, fmt, args); } static inline int gwc_sysklog ( const char *fmt, args...) { return gwc_printk (GWC_KSYS_IDX, fmt, args); } #define gwc_pflog ( fmt, args...) do { \ gwc_printk (GWC_PF_IDX, fmt, ##args) \ } while (0) #define gwc_syslog ( fmt, args...) do { \ gwc_printk (GWC_KSYS_IDX, fmt, ##args) \ } while (0) #endif #define DBG_GENERAL 0 #define dprintk(fmt, args... ) do { \ if (debug) \ printk ("gwc-klog-%s: [%s:%d] " fmt, \ gwc_klog_driver_version, __FUNCTION__, __LINE__, ##args); \ } while(0) #endif /* __SYS_SYSKLOG_H */ |