asmlinkage long sys_syslog(int type, char __user * buf,
int len)
{
return do_syslog(type, buf, len);
}
int do_syslog(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:/*关闭日志*/
break;
case 1:/*打开日志*/
break;
case 2:/*读取日志信息*/
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;
}
//当log_start - log_end为0时,表示环形缓冲区无数据可读,把当前进程放入
等待队列log_wait
error = wait_event_interruptible(log_wait,(log_start - log_end));
if (error)
goto out;
i = 0;
spin_lock_irq(&logbuf_lock);
while (!error && (log_start != log_end)
&& i < len) {
c = LOG_BUF(log_start);//从环形缓冲区得到读取位置log_start
log_start++;
spin_unlock_irq(&logbuf_lock);
error = __put_user(c,buf);//将c地址的字符传递到用户空间的buf中
buf++;
i++;
cond_resched();//条件调度,让其他进程有运行时间
spin_lock_irq(&logbuf_lock);
}
spin_unlock_irq(&logbuf_lock);
if (!error)
error = i;
break;
case 4:/*读/清除上一次内核消息*/
do_clear = 1;
/* FALL THRU */
case 3:/*读取上一次内核消息*/
error = -EINVAL;
if (!buf || len < 0)
goto out;
error = 0;
if (!len)//读取长度为0
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);
if (count > logged_chars)// logged_chars是上次读/清除以来产生的日志字符数
count = logged_chars;
if (do_clear)
logged_chars = 0;
limit = log_end;
/* __put_user()可以睡眠,当__put_user睡眠时,printk()可能覆盖写正在
拷贝到用户空间的消息,因此,这些消息被反方向拷贝,将buf覆盖部分的数据重写到buf的起始位置*/
for (i = 0; i < count && !error; i++) {//读取count个字符
j = limit-1-i;
if (j + log_buf_len < log_end)
break;
c = LOG_BUF(j); //从环形缓冲区得到读取位置j
spin_unlock_irq(&logbuf_lock);
//将c位置的字符传递到用户空间的buf中,如果发生错误,将发生错误的c位置给error
error = __put_user(c,&buf[count-1-i]);
cond_resched();
spin_lock_irq(&logbuf_lock);
}
spin_unlock_irq(&logbuf_lock);
if (error)
break;
error = i;
if (i != count) {//表示__put_user没有拷贝完成
int offset = count-error;
/*拷贝期间缓冲区溢出,纠正用户空间缓冲区*/
for (i = 0; i < error; i++) {
if (__get_user(c,&buf[i+offset]) ||
__put_user(c,&buf[i])) {//将覆盖部分的数据
重写到buf的起始位置
error = -EFAULT;
break;
}
cond_resched();
}
}
break;
case 5:/*清除环形缓冲区*/
logged_chars = 0;
break;
case 6:/*关闭向控制台输出消息*/
console_loglevel = minimum_console_loglevel;
break;
case 7:/*开启向控制台输出消息*/
console_loglevel = default_console_loglevel;
break;
case 8:/*设置打印到控制台的日志级别*/
error = -EINVAL;
if (len < 1 || len > 8)
goto out;
if (len < minimum_console_loglevel)
len = minimum_console_loglevel;
console_loglevel = len;
error = 0;
break;
case 9:/*得到日志消息所占缓冲区的大小*/
error = log_end - log_start;
break;
case 10:/*返回环形缓冲区的大小*/
error = log_buf_len;
break;
default:
error = -EINVAL;
break;
}
out:
return error;
}