2015年2月2日星期一

系统panic后主动广播最后内核dmesg信息-一个几乎可以使用的方案

本邮件内容由第三方提供,如果您不想继续收到该邮件,可 点此退订
系统panic后主动广播最后内核dmesg信息-一个几乎可以使用的方案  阅读原文»

在《远程触发SYSRQ获取最新的dmesg信息-一个几乎没有什么用的方案》中,我认为远程触发SYSRQ并没有什么实际的用处,系统没有挂起时,用SSH等标准方式会好很多,系统挂起时,远程触发在多数情况下均无法得到响应。那么有什么方法在系统panic的时候通知外部呢?
当然采用crash kexec kernel的方式会是一个好方法,但是那毕竟是为了debug而生的,有的时候只是想知道dmesg的最后几行就好,要知道此时系统已经完全没有机会记 录日志了,用户态的任何进程均已经全军覆没。但是别忘了还有一个口子可以将最后的信息送出去,那就是网络。为了使得信息更加可能被接收,我觉得采用广播会 比较好。具体的方式我在《远程触发SYSRQ获取最新的dmesg信息-一个几乎没有什么用的方案》的最后已经给出了。本文主要给出一个切实可以运行的代码。这个代码和平时的代码有所不同,那就是这个代码是在系统已经panic了以后被触发的,此时的系统很可能全然混乱,因此此时最好的做法就是:
1.减少内存操作;
2.不处理异常流;
3.尽可能简化,不再计算校验值;
4.避免ARP交互,直接广播。
代码的逻辑如下:
1.加载模块:预先分配skb和get device,为了不在panic以后处理内存;注册通知链。
2.panic之后:调用通知链的通知操作,用最简化的方式广播没有校验码的数据包。

代码如下:

  #include <linux/module.h>  #include <linux/skbuff.h>  #include <net/ip.h>  struct sk_buff * skb = NULL;  struct net_device * dev = NULL;  u8 ethhdr [ETH_HLEN] = {          0xff, 0xff, 0xff, 0xff, 0xff, 0xff,          0x00, 0x0c, 0x29, 0xdd, 0xaa, 0xfd,          0x00, 0x00  };  u8 iphdr [20] = {          0x45,          0x00,          0x00, 0x00,  //total length          0x00, 0x00,  //ID          0x40, 0x00,  //Don't fragment          0x40,        //TTL          0xff,        // UDP?          0x00, 0x00,  //checksum          0x02, 0x02, 0x02, 0x02,          0xff, 0xff, 0xff, 0xff  };  u8 data[1024] = {0};  void do_nothing (struct sk_buff *skb)  {      /* 不再进行任何内存操作 */      //dev_put(skb->dev);  }  static int send_last_msg(struct notifier_block *self,              unsigned long event, void *unused)  {      int ret;      struct ethhdr *eth;      struct iphdr *ip;      char *p;      /* 获取内核缓冲区中的数据 */      kernel_log_buffer(data, sizeof(data));      skb->dev = dev;      skb->pkt_type = PACKET_OTHERHOST;      skb->protocol = htons(ETH_P_IP);      skb->ip_summed = CHECKSUM_NONE;      skb->destructor = do_nothing;      skb->priority = 0;      /* 保留skb区域 */      skb_reserve (skb, 2 + ETH_HLEN + sizeof(struct iphdr) + sizeof(data));      /* 构造数据区(使用UDP会比较好,但是懒的封装了) */      p = skb_push(skb, sizeof(data));      memcpy(p, &data[0], sizeof(data));      skb_reset_transport_header(skb);      /* 构造IP头 */      p = skb_push(skb, sizeof(struct iphdr));      memcpy(p, &iphdr, sizeof(struct iphdr));      ip = (struct iphdr*)p;      ip->tot_len = htons(sizeof(data) + sizeof(struct iphdr));      skb_reset_network_header(skb);      /* 构造以太头 */      p = skb_push(skb, sizeof(struct ethhdr));      eth = (struct ethhdr*)p;      eth->h_proto = htons(ETH_P_IP);      memcpy(p, hdr, sizeof(struct ethhdr));      skb_reset_mac_header(skb);      /* 发射 */      ret = dev_queue_xmit(skb);      if (ret < 0) {          /* 由于panic了,不再处理内存,不处理异常流 */          //kfree_skb (skb);          //dev_put(dev);          goto out;      }  out:      return ret;  }  static struct notifier_block on_panic_send = {          .notifier_call = send_last_msg,  };  static int __init  panic_sendmsg_init(void)  {      int ret = -1;      dev = dev_get_by_name (&init_net, "eth2");      if (!dev) {          printk("Can't get device\n");          goto out;      }      /* 不在通知链中分配,因为那时已经panic,故预先分配 */      skb = alloc_skb(1500, GFP_ATOMIC);      if (!skb) {          dev_put(dev);          printk("Alloc skb failed\n");          goto out;      }      /* 注册 */      ret = atomic_notifier_chain_register(&panic_notifier_list, &on_panic_send);      if (ret) {          dev_put(dev);          kfree_skb (skb);          printk("Register notifier chain failed\n");          goto out;      }      return 0;  out:      return ret;  }  static void __exit  panic_sendmsg_exit(void)  {      atomic_notifier_chain_unregister(&panic_notifier_list, &on_panic_send);      if (dev) {          dev_put(dev);      }      if (skb) {          kfree_skb (skb);      }  }  module_init(panic_sendmsg_init);  module_exit(panic_sendmsg_exit);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("marywangran <marywangran@126.com>");  


需要指出的 是kernel_log_buffer接口,这个接口是我自己添加进内核并EXPORT出来的。我认为这是必要的,printk将信息放在了内核的一个缓 冲区中,这个缓冲区可以被用户态进程通过syslog系统调用读取。我们知道,常规的办法是内核信息被syslog守护进程读取,然后发送到网络或者保存 成文件。但是panic之后呢?既然用户态已经完全不再运行,那么就不存在通过什么守护进程存日志的希望了,当然在内核panic通知链钩子中直接操作磁 盘日志文件是可以的,但是那太不可靠,因为磁盘的IO逻辑同样存在于内核态代码或者驱动代码中,此时内核已经panic了...虽然从网络发送数据也同样 要用到已经panic的内核执行流,但是毕竟协议栈操作要比磁盘操作的调用深度要浅很多(数据的最终解释,排序,保存操作都在对端进行,磁盘IO则不 然)。

不管怎么样,不管是磁盘操作还是网络操作,都需要将内核log缓冲区中的内存dump下来才好,然而内核log缓冲区本身并没有被EXPORT,它只能通过不多的几个系统调用接口来获取,而这些接口很难在内核里面调用,因此我自己添加了一个:

  int kernel_log_buffer(char *buf, int max_len);  


其中,buf为内核log保存到的缓冲区,max_len为buf的长度,返回值为实际返回信息的长度。这个接口完全类似于read系统调用。

Shell脚本监控WEB服务是否正常  阅读原文»

每日博报 精彩不止一点关闭
Shell脚本监控WEB服务是否正常

Step1.安装sendmail来发邮件

# yum -y install sendmail

# /etc/init.d/sendmail start

# chkconfig sendmail on

Step2.安装mutt邮件客户端,并设置相关信息

# yum -y install mutt

# vim/etc/Muttrc

set charset="utf-8" #设置发邮件编码

set envelope_from=yes

set rfc2047_parameters=yes #解决附件乱码问题

set realname="报警" #发件人别名

set use_from=yes #指定是否显示别名

set from=monitor@test.com #发送人地址

Step3.脚本信息

1.url文件如下

# cat url

www.baidu.com

www.sina.com

2.脚本如下

#Author:lizhenliang-EMail:zhenliang369@163.com
#Lastmodified:2015-2-1
#Filename:web_mon.sh
#Description:wgetmonitoringwebisnormal
#blog:lizhenliang.blog.51cto.com
Mail="baojingtongzhi@163.com"
GetUrlStatus(){
for((i=1;i<=3;i++))#使用i++判断访问次数,如果wget两次超时则判断网站异常
wget-T3--tries=1--spiderhttp://${1}>/dev/null2>&1
#-T超时时间,--tries尝试1次,--spider蜘蛛
[$?-ne0]&&letFailCount+=1;#访问超时时,$?不等于0,则FailCount加1
if[$FailCount-gt1];then
Date=`date+%F""%H:%M`
echo-e"Date:$Date\nProblem:$urlisnotrunning."mutt-s"URLMonitor"$Mail
return$Retval#如果返回值为0,就正常退出循环,不为0则继续循环
forurlin`caturlsed'/^#/d'`
#GetUrlStatus$url&&echoyesechono
GetUrlStatus$url
sleep2m#死循环,设置没2分钟运行一次

本文出自 ""企鹅"那点事儿" 博客,请务必保留此出处http://lizhenliang.blog.51cto.com/7876557/1610408

分享至 一键收藏,随时查看,分享好友!
昵称:
登录快速注册
内容:

没有评论:

发表评论