您的位置:首页 > 数据库 > MySQL

MySQL5.5-audit plugin的函数调用流程分析

2011-07-10 13:47 381 查看
继续研究audit plugin,首先gdb源代码
启动mysqld,加上--debug选项
进入gdb:
(gdb) attach 19079 (mysqld的进程号)

(gdb) b mysql_audit_notify(THD*, unsigned int, unsigned int, ...) (猜测该函数会调用audit的notify函数)

(gdb) c
Continuing.

启动mysql客户端
Breakpoint 1, mysql_audit_notify (thd=0x16fe09b0, event_class=1, event_subtype=0)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
207 audit_handler_t *handlers= audit_handlers + event_class;
(gdb) bt
#0 mysql_audit_notify (thd=0x16fe09b0, event_class=1, event_subtype=0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
#1 0x00000000006943d6 in thd_prepare_connection (thd=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:715
#2 0x0000000000694c61 in do_handle_one_connection (thd_arg=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:780
#3 0x0000000000694d3b in handle_one_connection (arg=0x16fe09b0) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:706
#4 0x00000034f7e064a7 in start_thread () from /lib64/libpthread.so.0
#5 0x00000034f72d3c2d in clone () from /lib64/libc.so.6
(gdb)

可见,在准备创建客户端连接时也会调用audit,在gdb过程中,发现该函数被调用了很多次:
Breakpoint 1, mysql_audit_notify (thd=0x1706c790, event_class=1, event_subtype=0)

Breakpoint 1, mysql_audit_notify (thd=0x16fe09b0, event_class=0, event_subtype=3)

查了一下notify函数的记录,写入了如下操作:
select @@version_comment limit 1;
select @@version_comment limit 1;
mysql > use yinfeng
SELECT DATABASE();
SELECT DATABASE();

看起来,似乎每次操作之前都会调用两次mysql_audit_notify函数,即使执行一次错误的操作(比如向一个不存在的表插入数据)
————————————————————————————————————————————————
但当执行select、create table之类的命令时,则是先完成命令,然后才会跳到断点
#0 mysql_audit_notify (thd=0x1706c790, event_class=0, event_subtype=2) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.cc:207
#1 0x00000000005c7f84 in mysql_audit_general (thd=0x1706c790, event_subtype=2, error_code=0, msg=0x0)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_audit.h:131
#2 0x00000000005c9d22 in dispatch_command (command=COM_QUERY, thd=0x1706c790, packet=0x1706f601 "", packet_length=27)
at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_parse.cc:1398
#3 0x00000000005ca0f8 in do_command (thd=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_parse.cc:771
#4 0x0000000000694c7e in do_handle_one_connection (thd_arg=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:787
#5 0x0000000000694d3b in handle_one_connection (arg=0x1706c790) at /home/yinfeng.zwx/mysql-5.5.14/sql/sql_connect.cc:706
#6 0x00000034f7e064a7 in start_thread () from /lib64/libpthread.so.0
#7 0x00000034f72d3c2d in clone () from /lib64/libc.so.6

看起来dispatch_command函数似乎是audit的入口,简要的归纳一下流程:
1.do_handle_one_connection()
2. do_command
3.dispatch_command(sql_parse.cc)

1397   if (!thd->is_error() && !thd->killed_errno())
1398     mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_RESULT, 0, 0);                                                                
1399   
1400   mysql_audit_general(thd, MYSQL_AUDIT_GENERAL_STATUS,
1401                       thd->stmt_da->is_error() ? thd->stmt_da->sql_errno() : 0,
1402                       command_name[command].str);

这里会调用两次mysql_audit_general函数,分别对应不同的事件类型
4.mysql_audit_general() (sql_audit.h)

100 static inline
101 void mysql_audit_general(THD *thd, uint event_subtype,
102                          int error_code, const char *msg)
103 {  
104 #ifndef EMBEDDED_LIBRARY
105   if (mysql_global_audit_mask[0] & MYSQL_AUDIT_GENERAL_CLASSMASK)
106   {
107     time_t time= my_time(0);
108     uint msglen= msg ? strlen(msg) : 0;
109     const char *user;
110     uint userlen;
111     char user_buff[MAX_USER_HOST_SIZE];
112     CSET_STRING query;
113     ha_rows rows;
114 
115     if (thd)
116     {
117       query= thd->query_string;
118       user= user_buff;
119       userlen= make_user_name(thd, user_buff);
120       rows= thd->warning_info->current_row_for_warning();
121     }
122     else
123     {
124       user= 0;
125       userlen= 0;
126       rows= 0;
127     }
128 
129     mysql_audit_notify(thd, MYSQL_AUDIT_GENERAL_CLASS, event_subtype,
130                        error_code, time, user, userlen, msg, msglen,
131                        query.str(), query.length(), query.charset(), rows);
132   }
133 #endif
134 }


5.mysql_audit_notify() (sql_audit.cc),该函数比较简短,会调用一个函数指针
audit_handler_t *handlers= audit_handlers + event_class;

这里,以event_class作为偏移量,来调用相应的函数指针:
audit_handlers作为全局变量,定义:

114 static audit_handler_t audit_handlers[] =
115 {
116   general_class_handler, connection_class_handler
117 };


5.1 general_class_handler()
event_class_dispatch(thd, MYSQL_AUDIT_GENERAL_CLASS, &event);
5.2 connection_class_handler()
event_class_dispatch(thd, MYSQL_AUDIT_CONNECTION_CLASS, &event);
可以看到上述两个函数都会调用event_class_dispatch,该函数的参数形式与自定义的插件函数参数完全一致,

深入代码可以发现,在创建连接的时候调用了上述两个函数都被调用了,其中conneciton_class_handler()在函数thd_prepare_connection中被调用:
710 bool thd_prepare_connection(THD *thd)
711 {
712   bool rc;
713   lex_start(thd);
714   rc= login_connection(thd);
715   MYSQL_AUDIT_NOTIFY_CONNECTION_CONNECT(thd);
…… ……


6.event_class_dispatch() (sql_audit.cc:466-491)

466 static void event_class_dispatch(THD *thd, unsigned int event_class,
467                                  const void *event)
468 {
469   struct st_mysql_event_generic event_generic;
470   event_generic.event_class= event_class;
471   event_generic.event= event;
472   /*
473     Check if we are doing a slow global dispatch. This event occurs when
474     thd == NULL as it is not associated with any particular thread.
475   */
476   if (unlikely(!thd))
477   {
478     plugin_foreach(thd, plugins_dispatch, MYSQL_AUDIT_PLUGIN, &event_generic);
479   }
480   else
481   {
482     plugin_ref *plugins, *plugins_last;
483 
484     /* Use the cached set of audit plugins */
485     plugins= (plugin_ref*) thd->audit_class_plugins.buffer;
486     plugins_last= plugins + thd->audit_class_plugins.elements;
487 
488     for (; plugins < plugins_last; plugins++)
489       plugins_dispatch(thd, *plugins, &event_generic);
490   }
491 }

7. plugins_dispatch()

439 static my_bool plugins_dispatch(THD *thd, plugin_ref plugin, void *arg)
440 {
441   const struct st_mysql_event_generic *event_generic=
442     (const struct st_mysql_event_generic *) arg;
443   unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
444   st_mysql_audit *data= plugin_data(plugin, struct st_mysql_audit *);
445 
446   set_audit_mask(event_class_mask, event_generic->event_class);
447 
448   /* Check to see if the plugin is interested in this event */
449   if (check_audit_mask(data->class_mask, event_class_mask))
450     return 0;
451 
452   /* Actually notify the plugin */
453   data->event_notify(thd, event_generic->event_class, event_generic->event);
454 
455   return 0;
456 }
最终,我们到达了实际调用插件函数,使用函数指针来实现:

data->event_notify(thd, event_generic->event_class, event_generic->event);

————————————————————————————————————————————————————————————

下一步:
尝试对该audit审计插件的执行流程进行修改,来满足一些有趣的需求
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: