HOOK IN POSTGRESQL 初探

前言

众所周知,PostgreSQL具有很好的扩展性,是一个可以"开发"的数据库。在PostgreSQL里面,你可以定制你自己的Types、Function、Operation,支持各种过程语言等等。还可以依据自己的喜好修改源码增加新功能。为方便扩展,PostgreSQL提供了很多有用的API、头文件扩展文件夹等等。然而其中一个很重要hook机制却不是特别为人所知,今天我们就来学习下PostgreSQL的hook机制。

hook机制来源于Windows平台。钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。


1.常用的hook

PostgreSQL中提供了很多种hook,与上面提到的类似,但是更多的是中断和替换操作。更重要的是该机制提供了一种修改PostgreSQL内核功能却不必修改内核代码的手段,且可以轻松的加载和还原。

首先我们还是看下PostgreSQL中都有哪些常用的hook吧。

其他的还有这些:


2.内部机制

所有的hook都和一个全局函数指针相关。在初始时该函数指针被设置为NULL,表示hook未被使用,当PostgreSQL执行到hook处的时候,如果hook为NULL它什么额外的事情也不做,但是如果hook被设置为某个函数的地址时(当然是你增加的那个函数了,通常是做成一个共享库咯),程序就开始转向你的新增函数中,执行你所期望的各种新功能啦。

那么问题来了,如何设置hook连接到你设置的共享库呢?PostgreSQL的配置文件中给出了shared_preload_libraries参数来加载你的共享库(当然了,设置完是需要重启的)。那么问题就又回到了:如何写一个被PostgreSQL所接受的共享库呢?

你至少要实现这三个函数:

_PG_init()
your_hook_function()
_PG_fini()

我们慢慢说。

首先,当PostgreSQL加载共享库(create extension xxxx)时,它需要把共享库加载到内存中。这时需要你写一个_PG_init函数,这种函数你在PostgreSQL源码的contrib目录下很容易找到,大概就像下面这样:

_PG_init()
{
prev_ExecutorRun_hook = ExecutorRun_hook;
ExecutorRun_hook = your_function_hook;
}

保存当前的hook值(保证你移除修改后能够还原),并将你的hook挂在到PostgreSQL的hook函数指针上。

那么与之相对应的,你要写一个_PG_fini函数在卸载(drop extension xxxx)的时候使用,也就是移除你的hook并且把它重置为之前的指针值。

_PG_fini()
{
ExecutorRun_hook = prev_ExecutorRun_hook;
}

这两个函数加上你的hook函数(your_hook_function())就构成了最基础的PostgreSQL的hook了。


3.举例说明

下面我们以上面提到的ClientAuthentication_hook举例说明吧。

这个hook的作用是帮助你在client得到验证之后并且服务端还未给client反馈的时候运行你的代码(contrib目录下的auth_delay和sepgsql使用了这个hook)。

首先,这个hook指针声明在

src/include/libpq/auth.h, line 27

/* Hook for plugins to get control in ClientAuthentication() */
typedef void (*ClientAuthentication_hook_type) (Port *, int);
extern PGDLLIMPORT ClientAuthentication_hook_type ClientAuthentication_hook;

它的调用点,也就是hook所在的位置是:

src/backend/libpq/auth.c, line 215

/*
* 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;

我们可以看到该hook初始化为NULL。具体到函数里,它出现在:

src/backend/libpq/auth.c, line 580

if (ClientAuthentication_hook)
(*ClientAuthentication_hook) (port, status);

这很明显,就是如果你写好了一个ClientAuthentication_hook并且通过我们上面提到的方法把它挂到了ClientAuthentication_hook上,那么你的hook就会在这里被调用。

对于ClientAuthentication_hook,我们看到他有两个入参,分别是:

port  PostGreSQL内部的一个Port结构体(定义在include/libpq/libpq-be.h)
Status 是PostgreSQL内部的状态码:STATUS_ERROR, STATUS_OK

那么我们写一个完整的extension吧,它的作用是 will deny connections if a specific file is present。

首先写一个初始化函数初始化hook。

static ClientAuthentication_hook_type next_client_auth_hook = NULL;
/* Module entry point */
void
_PG_init(void)
{
next_client_auth_hook = ClientAuthentication_hook;
ClientAuthentication_hook = my_client_auth;
}

很简单,我们先保存之前的hook值,在设置上我们自己的hook函数。

记住,初始化必须要在_PG_init函数里面做,该函数在PostgreSQL加载你的共享库的时候被调用。

然后就是咱们的hook函数,在这里你自由发挥,写下你想干的事儿,比如:

static void my_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!!")));
}

这里,如果之前已经设置过ClientAuthentication_hook的话,我们不妨大方的先让他做完好了,然后如果Client的Authentication都不OK的话(都没通过验证),那我们后面的deny操作不是多余么,那就return吧。最后,我们再做我们事:如果不存在connection.stopped文件,我们拒绝connection。Wow,很cool很上帝。

最后,你再写一个_PG_fini咯:

_PG_fini(void)
{
ClientAuthentication_hook = next_client_auth_hook;
}

自此,c文件里面的活我们干完了。剩下的我们写这几个文件

Makefile
your_extension--1.0.control
your_extension--1.0.sql

首先,对于Makefile我们知道是用来编译c程序的。它大概是这样:

MODULE_big = your_hook
OBJS = your_hook.o
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/your_hook
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

通常你是要在PostgreSQL的源码目录结构中编译它,这样你就要在contrib目录下建立你自己的目录(这一点可以参考该目录下的其他extension),然后make。如果你不想在PostgreSQL的源码目录结构中编译它,of course,你可以使用PGXS选项,它定义在PostgreSQL的pg_config命令里。然后你就执行make USE_PGXS=1 吧。当然,你需要设置pg_config到你的PATH里面。

编译完了之后,你就make install吧,把你的共享库装载到PostgreSQL的lib目录中去。

而对于剩下的两个文件最后会被安装到PostgreSQL的share/extension目录下。

your_extension--1.0.control里面主要是写一些控制信息,

your_extension--1.0.sql用于创建一些你需要的数据库对象,比如表,触发器,函数等等。

而这两个文件在这个简单的例子里暂时用不到。我们后面再细说。

在这之后把你的共享库加入到Postgreql.conf文件的shared_preload_libraries中,重启数据库。

然后你就尽情的用起来吧。

这里说的比较简单,敬请期待第二部进阶版+_+

参考文献:http://wiki.postgresql.org/images/e/e3/Hooks_in_postgresql.pdf

hook in PostgreSQL初探的更多相关文章

  1. PostgreSQL的hook机制初步学习

    磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面:PostgreSQL内部结构与源代码研究索引页    回到顶级页面:PostgreSQL索引页 本文的目的一是为了备忘,二是为了抛砖引玉,希望 ...

  2. 初探hook的键盘获取

    初探hook的键盘获取 import pyHook import pythoncom class e(): keyIsPressed = False #键盘是否按下 按住.. def onKeyDow ...

  3. postgreSQL数据库的初探

    kali是黑客的强大武器,还有一个也是哦——Metasploit postgreSQL数据库是Metasploit的默认数据库哦! 启动postgresql: service postgresql s ...

  4. [React]Hook初探

    Hook是什么 Hook是React从16.8开始支持的特性,使用Hook可以在不使用class时使用state Hook支持在不需要修改组件状态的情况下复用逻辑状态,从而解决使用render pro ...

  5. [转]初探Metasploit的自动攻击

    1. 科普Metasploit   以前只是个Back Track操作系统(简称:BT) 下的攻击框架,自成继承了后攻击渗透模块,隐隐有成为攻击平台的趋势. 我们都戏称它为美少妇,很简单,msf. 它 ...

  6. PostgreSQL与RPM

    如何查看使用PostgreSQL的RPM包安装后的文件目录及相关路径(PostgreSQLRPM的spec文件已经帮我们创建好了postgres用户及postgres组). 查看RPM文档信息:/us ...

  7. PostgreSQL内部结构与源代码研究索引页

    磨砺技术珠矶,践行数据之道,追求卓越价值 luckyjackgao@gmail.com 返回顶级页:PostgreSQL索引页 本页记录所有本人所写的PostgreSQL的内部结构和源代码研究相关文摘 ...

  8. Linux系统初探过程总结

    Linux系统初探的过程大约用了一周的时间,这周基本将Linux系统安装,PostgreSQL安装,Nginx服务器安装,ASP.NET Core应用部署都走了一遍.由于以前没有怎么接触和使用过Lin ...

  9. 钩子编程(HOOK) 屏蔽全部按键、鼠标及系统功能键 (4)

    摘要:上篇文章<钩子编程(HOOK) 安装系统全局钩子>已经具体的解说了全局钩子的安装.本文将增强一下钩子的功能.实现屏蔽全部按键鼠标与系统功能键.要实现这个功能.须要安装两个全局钩子,& ...

随机推荐

  1. C++格式化输出的好东西

    s = FormatFloat("0.######", d); 最多保留6位s = FormatFloat("0.000000", d); 始终保留6位s = ...

  2. 实例讲解webpack的基本使用第二篇

    这一篇来讲解一下如何设置webpack的配置文件webpack.config.js 我们新建一个webpack-demo的项目文件夹,然后安装webpack 执行如下命令 在项目文件夹下,建一个dis ...

  3. es6函数的rest参数和拓展运算符(...)的解析

    es6的新特性对函数的功能新增加了rest参数和...的拓展运算符.这是两个什么东西呢? 先来看一个问题:如何获取一个函数除了定义的参数之外的其他参数?传统的做法是借助函数的arguments关键字来 ...

  4. WINDOWS XP中用命令行管理用户 net user命令

    net user <username> [password or *] [/add] [options] [/domain] net user <username] /delete ...

  5. 代理模式与java中的动态代理

    前言    代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景    李雷是一个唱片公司的大老板,很忙, ...

  6. Win7 32位系统下Sublime text 3的安装以及配置C/C++、java、python的开发环境方法

    本人初学者,此文仅是对这几天鼓捣subime text 3一点微不足道的经验总结,如有明显错误,欢迎指正! 好了,废话少说,进入正题,之前编程java一直用的是eclipse,java的主流IDE,后 ...

  7. ArcGIS RunTime SDK for Android之Features and graphics

    今天是我开通博客园的第一天,希望以后可以多在博客园上分享自己的学习心得,记录自己的学习历程.最近在学习ArcGIS RunTime SDK for Android,所以第一篇随笔就从这里来吧.官网的教 ...

  8. 微信小程序的跨平台图表库开发

    写在前面 微信小程序出来已经有一段时间了,github上也有很多人开源了很多项目.但是由于微信平台的限制(底层Canvas能力调用为一系列JSBridge封装),图表的制作一直是个比较头疼的问题.当前 ...

  9. MongoDB学习教程(1)

    1.简介: MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.在高负载的情况下,添加更多的节点,可以保证服务器性能.MongoDB 旨在为WEB应用提供可扩展的高性能数据 ...

  10. vue2组件之select2调用

    目前,项目中使用了纯前端的静态项目+RESTFul接口的模式.为了更好的对数据进行操作,前端使用了vue2的mvvm功能,但是由于不是单页面应用,所以,并没有涉及到其它的如vue-route等功能,也 ...