使用PostgreSQL中hook,可以不重新编译中断或者改变PG行为。
1.经常使用的Hook汇总 Hook
Initial releasecheck_password_hook 9.0ClientAuthentication_hook 9.1ExecutorStart_hook 8.4ExecutorRun_hook 8.4ExecutorFinish 8.4ExecutorEnd_hook 8.4ExecutorCheckPerms_hook 9.1ProcessUtility_hook 9.0Hook Used in Initial releaseexplain_get_index_name_hook 8.3ExplainOneQuery_hookIndexAdvisor 8.3fmgr_hook sepgsql 9.1get_attavgwidth_hook 8.4get_index_stats_hook 8.4get_relation_info_hookplantuner 8.3get_relation_stats_hook 8.4join_search_hooksaio 8.3needs_fmgr_hook sepgsql 9.1object_access_hook sepgsql 9.1planner_hook planinstr 8.3shmem_startup_hook pg_stat_statements 8.4
2.Hook工作原理分析
PG中hook如何工作
Hooks存在一个全局函数指针;并初始为NULL;当PG想使用一个hook时,检查这个全局函数指针,假如被设置,则执行函数。
如何设置函数指针hook函数定义在共享库中;在加载的时候,PG调用共享库中的_PG_init()函数;_PG_init函数设置函数指针(一般会把原来的指针保存)。如何清除函数指针在卸载时,PG调用共享库中的_PG_fini()函数;_PG_fini函数需要重新设置函数指针(通常将原来的指针恢复)。
3 实例
3.1 ClientAuthentication_hook
定义
/* src/include/libpq/auth.h */ /* Hook for plugins to get control in ClientAuthentication() */typedef void (*ClientAuthentication_hook_type) (Port *, int);extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;
设置
/* src/backend/libpq/auth.c *//** This hook allows plugins to get control following client authentication, * but before the user has been informed about the results. It could be used * to record login events, insert a delay after failed authentication, etc. */ ClientAuthentication_hook_type ClientAuthentication_hook = NULL;
检查和执行
/* src/backend/libpq/auth.c *//** Client authentication starts here. If there is an error, this * function does not return and the backend process is terminated. */ voidClientAuthentication(Port *port){ //... if (ClientAuthentication_hook) (*ClientAuthentication_hook) (port, status); //... }
说明
控制:客户端认证之后,通知用户之前 用处:记录登录事件;认证失败后插入一段延迟
用到此hook的模块
auth_delay;sepgsql;connection_limits
ClientAuthentication_hook函数介绍
两个参数 f(Port *port, int status)
Port结构位于 src/include/libpq/libpq-be.h
部分字段如下
typedef struct Port{ pgsocket sock; /* File descriptor */ bool noblock; /* is the socket in non-blocking mode? */ ProtocolVersion proto; /* FE/BE protocol version */ SockAddr laddr; /* local addr (postmaster) */ SockAddr raddr; /* remote addr (client) */ char *remote_host; /* name (or ip addr) of remote host */ char *remote_hostname; /* name (not ip addr) of remote host, if available */ char *remote_port; /* text rep of remote port */ char *database_name; char *user_name; char *cmdline_options; List *guc_options;} Port;
Status为状态代码--STATUS_ERROR,STATUS_OK等
实例
写一个具体使用案例:假如某个文件存在则禁止连接。
需要两个函数:
安装hook检查文件可用性,决定允许或拒绝连接
添加contrib/my_client_auth目录
添加文件:Makefile my_client_auth.c
/* -----------------** my_client_auth.c ** Copyright (C) 2010-2011, PostgreSQL Global Development Group ** IDENTIFICATION ** contrib/my_client_auth/my_client_auth.c ** ----------------- */#include#include #include "postgres.h"#include "fmgr.h"#include "libpq/auth.h"PG_MODULE_MAGIC;void _PG_init(void);/* Original Hook */static ClientAuthentication_hook_type next_client_auth_hook = NULL;/** Check authentication */static voidmy_client_auth(Port *port, int status){ struct stat buf; if (next_client_auth_hook) (*next_client_auth_hook) (port, status); if (status != STATUS_OK) return; if(!stat("/tmp/connection.stopped", &buf)) ereport(FATAL, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("Connection not authorized!!")));}/** Module Load Callback */void_PG_init(void){ /* Install Hooks */ next_client_auth_hook = ClientAuthentication_hook; ClientAuthentication_hook = my_client_auth;}
#contrib/my_client_auth/MakefileMODULES = my_client_authPGFILEDESC = "my_client_auth - Writing My ClientAuthentication_hook"ifdef USE_PGXSPG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs)include $(PGXS)else subdir = contrib/my_client_auth top_builddir = ../..include $(top_builddir)/src/Makefile.global include $(top_srcdir)/contrib/contrib-global.mkendif
使用 make && make install
data/postgres.conf配置文件汇总添加:
shared_preload_libraries = 'my_client_auth'
重启数据库
/tmp下创建connection.stopped文件
psql连接报错:
psql: FATAL: Connection not authorized!!