OpenGauss线程管理-审计线程-pgaudit
这个线程使用重定向的方式从管理线程、后台线程以及其他子线程获取审计数据,并保存在审计文件中。审计文件的尺寸和保留时间可以使用postgresql.conf配置文件的参数进行配置,如果达到任一个限制,则审计线程会停止对当前审计文件的写入,重新创建一个审计文件,开始写入审计信息。
术语解释
- 管道:一种通信方式。管道可以是双向的,也可以是单向的。在Opengauss中,主要使用了单向管道,用在后台线程向运行日志守护线程发送运行日志信息时使用。
- logrotate(日志滚动):把旧的日志文件移动并改名,同时创建一个新的空日志文件用来记录新日志,当旧日志文件超出保存的范围时就删除。
- 系统表:系统表又称为数据字典或者元数据,存储管理数据库对象的定义信息,如表、索引、触发器等。用户可通过系统表查询用户定义的具体对象信息,如表的每个字段类型。
pgaudit线程
路径:openGauss-server/src/gausskernel/process/postmaster/pgaudit.cpp
在主线程中的使用
在GaussDbThreadMain中通过thread_role进入pgauditor线程
代码理解
PgAuditorMain
pgaudit线程argc/argv参数的主入口点仅在EXEC_BACKEND情况下有效。
NON_EXEC_STATIC void PgAuditorMain()
{
char auditbuffer[READ_BUF_SIZE + 1] = {0};
int bytes_in_auditbuffer = 0;
int currentAuditRotationAge;
int currentAuditRemainThreshold;
pg_time_t now;
IsUnderPostmaster = true; /* 我们现在是postmaster子流程 */
t_thrd.proc_cxt.MyProcPid = gs_thread_self(); /* 重置_thrd.proc_cxt.MyProcPid */
t_thrd.proc_cxt.MyStartTime = time(NULL); /* 设置我们的开始时间,以防我们打电话给elog*/
now = t_thrd.proc_cxt.MyStartTime;
t_thrd.role = AUDITOR;
/* 在此处获取线程索引,索引0是默认的审计主线程 */
(void)audit_load_thread_index();
init_ps_display("auditor process", "", "", "");
/* 初始化专用闩锁以供信号处理程序使用 */
InitLatch(&t_thrd.audit.sysAuditorLatch);
/*
* 正确接受或忽略postmaster可能发送给我们的信号
*
* 注意:我们忽略所有终止信号,而只在所有上游进程都结束时退出,
* 以确保我们不会错过任何后端崩溃的喘息。。。
*/
init_audit_signal_handlers();
if (t_thrd.mem_cxt.pgAuditLocalContext == NULL)
t_thrd.mem_cxt.pgAuditLocalContext = AllocSetContextCreate(t_thrd.top_mem_cxt,
"audit memory context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE * 3,
ALLOCSET_DEFAULT_MAXSIZE * 3);
on_shmem_exit(pgauditor_kill, (Datum)0);
(void)MemoryContextSwitchTo(t_thrd.mem_cxt.pgAuditLocalContext);
/* 如果遇到异常,将在此处继续处理。 */
sigjmp_buf local_sigjmp_buf;
int curTryCounter;
int* oldTryCounter = NULL;
if (sigsetjmp(local_sigjmp_buf, 1) != 0) {
gstrace_tryblock_exit(true, oldTryCounter);
pgaudit_handle_exception();
}
oldTryCounter = gstrace_tryblock_entry(&curTryCounter);
t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; /* 我们现在可以处理安装(错误) */
/* 记住活动的auditfile参数 */
currentAuditRotationAge = u_sess->attr.attr_security.Audit_RotationAge;
currentAuditRemainThreshold = u_sess->attr.attr_security.Audit_RemainThreshold;
/* 审核主线程*/
if (t_thrd.audit.cur_thread_idx == 0) {
m_curlUtils.initialize(false, "", "", "");
elasic_search_connection_test();
}
/* 设置下一个计划的轮换时间 */
set_next_rotation_time();
auditfile_init();
/* 主辅助回路 */
for (;;) {
bool time_based_rotation = false;
bool size_based_rotation = false;
long cur_timeout;
unsigned int cur_flags;
unsigned int rc = 0;
/* 清除所有已挂起的唤醒 */
ResetLatch(&t_thrd.audit.sysAuditorLatch);
/*
* 如果我们从postmaster那里得到SIGQUIT,就退出。
*/
if (t_thrd.audit.need_exit) {
sig_thread_quit_handler();
break;
}
if (t_thrd.audit.cur_thread_idx == 0) {
policy_auditfile_rotate();
pgaudit_send_data_to_elastic();
}
/*
* 处理最近收到的任何请求或信号。
*/
if (t_thrd.audit.got_SIGHUP) {
t_thrd.audit.got_SIGHUP = false;
sig_thread_config_handler(currentAuditRotationAge, currentAuditRemainThreshold);
}
if (u_sess->attr.attr_security.Audit_RotationAge > 0 && !t_thrd.audit.rotation_disabled) {
/* 如果时间允许,请执行审核文件轮换 */
now = (pg_time_t)time(NULL);
if (now >= t_thrd.audit.next_rotation_time)
t_thrd.audit.rotation_requested = time_based_rotation = true;
}
if (!t_thrd.audit.rotation_requested && u_sess->attr.attr_security.Audit_RotationSize > 0 &&
!t_thrd.audit.rotation_disabled) {
int64 filesize = (t_thrd.audit.sysauditFile != NULL) ? ftell(t_thrd.audit.sysauditFile) : 0;
/* 如果文件太大,请执行旋转 */
if (filesize >= (int64)u_sess->attr.attr_security.Audit_RotationSize * 1024L ||
filesize >= (int64)u_sess->attr.attr_security.Audit_SpaceLimit * 1024L) {
t_thrd.audit.rotation_requested = size_based_rotation = true;
}
}
if (t_thrd.audit.rotation_requested) {
/*
* 当两个值都为零时强制旋转。这意味着请求是通过pg_rotate_auditfile发送的。
*/
if (!time_based_rotation && !size_based_rotation) {
size_based_rotation = true;
}
auditfile_rotate(time_based_rotation, size_based_rotation);
}
if (t_thrd.audit.cur_thread_idx == 0) {
pgaudit_cleanup();
}
/*
* 计算下一次基于时间的旋转之前的时间,这样我们就不会睡得比这个时间长了。
* 我们假设上面得到的“现在”值仍然足够接近。注意,在调用auditfile_rotate()之前,
* 我们无法进行此计算,因为它将提前t_thrd.audit.next_rotation_time。
*/
if (u_sess->attr.attr_security.Audit_RotationAge > 0 && !t_thrd.audit.rotation_disabled) {
if (now < t_thrd.audit.next_rotation_time)
cur_timeout = (t_thrd.audit.next_rotation_time - now) * 1000L; /* msec */
else
cur_timeout = 0;
cur_flags = WL_TIMEOUT;
} else {
cur_timeout = -1L;
cur_flags = 0;
}
/*
* 睡觉,直到有事情可做
*/
rc = WaitLatchOrSocket(&t_thrd.audit.sysAuditorLatch, WL_LATCH_SET | WL_SOCKET_READABLE | cur_flags,
g_instance.audit_cxt.sys_audit_pipes[PIPE_READ_INDEX(t_thrd.audit.cur_thread_idx)],
cur_timeout);
if (rc & WL_SOCKET_READABLE) {
int bytesRead = read(g_instance.audit_cxt.sys_audit_pipes[PIPE_READ_INDEX(t_thrd.audit.cur_thread_idx)],
auditbuffer + bytes_in_auditbuffer, sizeof(auditbuffer) - bytes_in_auditbuffer - 1);
if (bytesRead > 0) {
/* 检查当前审核文件 */
CheckAuditFile();
}
if (bytesRead < 0) {
if (errno != EINTR)
ereport(LOG, (errcode_for_socket_access(), errmsg("could not read from auditor pipe: %m")));
} else if (bytesRead > 0) {
bytes_in_auditbuffer += bytesRead;
process_pipe_input(auditbuffer, &bytes_in_auditbuffer);
continue;
} else {
/*
* select()表示read ready时读取的零字节数意味着管道上的EOF:也就是说,
* 不再有任何进程打开管道写端。因此,邮局主管和所有后端都已关闭,
* 我们已经完成了。
*/
t_thrd.audit.pipe_eof_seen = true;
/* 如果还有数据,现在就强制输出 */
flush_pipe_input(auditbuffer, &bytes_in_auditbuffer);
}
}
if (t_thrd.audit.pipe_eof_seen) {
break;
}
}
/*
* 在真正的stderr上看到这条消息很烦人,所以我们将其设置为DEBUG1以在正常使用中禁止显示。
*/
ereport(DEBUG1, (errmsg("auditor shutting down")));
if (t_thrd.audit.sysauditFile) {
fclose(t_thrd.audit.sysauditFile);
t_thrd.audit.sysauditFile = NULL;
}
/* 政策审计 */
if (t_thrd.audit.policyauditFile) {
fclose(t_thrd.audit.policyauditFile);
t_thrd.audit.policyauditFile = NULL;
}
/* 释放内存(如果已分配) */
if (t_thrd.mem_cxt.pgAuditLocalContext != NULL) {
MemoryContextDelete(t_thrd.mem_cxt.pgAuditLocalContext);
t_thrd.mem_cxt.pgAuditLocalContext = NULL;
}
if (t_thrd.audit.cur_thread_idx == 0) {
m_curlUtils.~CurlUtils();
}
proc_exit(0);
}
pgaudit_start_all——启动所有审核子流程
void pgaudit_start_all(void)
{
if (g_instance.pid_cxt.PgAuditPID == NULL) {
return;
}
audit_process_cxt_init();
for (int i = 0; i < g_instance.audit_cxt.thread_num; ++i) {
if (g_instance.pid_cxt.PgAuditPID[i] == 0) {
g_instance.pid_cxt.PgAuditPID[i] = pgaudit_start();
ereport(LOG, (errmsg("auditor process %d started, pid=%lu", i, g_instance.pid_cxt.PgAuditPID[i])));
}
}
}
pgaudit_stop_all——停止所有审核子流程
void pgaudit_stop_all(void)
{
for (int i = 0; i < g_instance.audit_cxt.thread_num; ++i) {
if (g_instance.pid_cxt.PgAuditPID[i] != 0) {
Assert(!dummyStandbyMode);
signal_child(g_instance.pid_cxt.PgAuditPID[i], SIGQUIT, -1);
}
}
audit_process_cxt_exit();
}
其他函数
函数 | 功能 |
---|---|
ThreadId pgaudit_start(void) | Postmaster子例程以启动sysauditor子进程 |
static void process_pipe_input(char* auditbuffer, int* bytes_in_auditbuffer) | 处理通过sysuditor管道接收的数据 |
static void flush_pipe_input(char* auditbuffer, int* bytes_in_auditbuffer) | 强制输出任何缓冲数据 |
static void pgaudit_write_file(char* buffer, int count) | 将数据写入当前打开的审核文件 |
static void auditfile_init(bool allow_errors) | 初始化审核文件 |
static FILE *auditfile_open(pg_time_t timestamp, const char *mode, bool allow_errors, const char *_filename, bool ignore_num) | 打开一个新的审核文件 |
static void auditfile_close(AuditFileType flag) | 关闭审核文件 |
static void policy_auditfile_rotate() | 策略审核文件旋转 |
static void auditfile_rotate(bool time_based_rotation, bool size_based_rotation) | 执行审核文件旋转 |
static void set_next_rotation_time(void) | 确定下一个计划的旋转时间,并存储在t_thrd.audit.next_rotation_time中 |
static void pgaudit_cleanup(void) | 检查审核数据清理条件并删除旧审核文件,然后返回 |
static bool audit_type_validcheck(AuditType type) | 检查特定审核类型的有效性 |
static bool audit_get_clientinfo(AuditType type, const char* object_name, AuditEventInfo &event_info) | 从进程端口获取所有审核事件信息 |
void audit_report(AuditType type, AuditResult result, const char *object_name, const char *detail_info, AuditClassType ctype) | 向系统审核员报告审核信息 |
static void pgaudit_read_indexfile(const char* audit_directory) | 将索引表从文件读入内存 |
static void pgaudit_update_indexfile(const char* mode, bool allow_errors) | 将索引表从内存写入文件 |
static bool pgaudit_find_indexfile(void) | 查找pgaudit旧索引文件函数 |
static void pgaudit_indexfile_upgrade(void) | 索引表文件升级功能 |
static void pgaudit_indexfile_sync(const char* mode, bool allow_errors) | 同步新旧索引表文件功能 |
static void pgaudit_rewrite_indexfile(void) | 读取旧索引文件并重写为新索引文件函数 |
static void pgaudit_indextbl_init_new(void) | 初始化内存中的索引表 |
static void pgaudit_reset_indexfile() | 基于新参数重置索引文件 |
static const char* pgaudit_string_field(AuditData* adata, int num) | 获取指定的字符串字段 |
static void deserialization_to_tuple(Datum (&values)[PGAUDIT_QUERY_COLS], AuditData *adata, const AuditMsgHdr &header) | 将指定的审核文件扫描为元组 |
static void pgaudit_delete_file(uint32 fnum, TimestampTz begtime, TimestampTz endtime) | 扫描指定的审核文件以删除审核 |
static bool pgaudit_check_system(TimestampTz begtime, TimestampTz endtime, uint32 index) | 审核人员将审核数据写入当前文件时,检查系统是否已更改 |
static void pgaudit_query_valid_check(const ReturnSetInfo *rsinfo, FunctionCallInfoData *fcinfo, TupleDesc &tupdesc) | 是否允许调用进行查询审计 |
Datum pg_query_audit(PG_FUNCTION_ARGS) | 在开始时间和结束时间之间查询审核信息 |
Datum pg_delete_audit(PG_FUNCTION_ARGS) | 删除开始时间和结束时间之间的审核信息 |
static void CheckAuditFile(void) | 检查并重新初始化审计文件 |
static void pgaudit_mark_corrupt_info(uint32 fnum) | postgres线程在审计线程中标记损坏的fnum,用于reinit审计文件 |
void audit_process_cxt_exit() | 审核过程退出 |
void audit_process_cxt_init() | 初始化多线程管理的审计进程 |
int audit_load_thread_index() | 加载审核线程索引 |
uint32 pgaudit_get_auditfile_num() | 获取当前线程索引的审核文件编号 |
void pgaudit_update_auditfile_time(pg_time_t timestamp, bool exist) | 打开新文件时更新索引表文件 |
void pgaudit_switch_next_auditfile() | 迭代下一个审计文件&更新索引表信息 |
static void pgauditor_kill(int code, Datum arg) | 当线程退出表信息时,线程索引是否会减少 |
bool pg_auditor_thread(ThreadId pid) | 检查审核员线程 |