OpenGauss线程管理-审计线程-pgaudit

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)检查审核员线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值