param.cpp 5.82 KB
Newer Older
lishen's avatar
lishen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <algorithm>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include <pwd.h>

#include "param.h"
#include "debug.h"

namespace sccl {

/**
 * 获取当前用户的主目录路径
 *
 * @return 返回指向用户主目录路径的指针,如果获取失败则返回NULL
 */
const char* userHomeDir() {
    struct passwd* pwUser = getpwuid(getuid());
    return pwUser == NULL ? NULL : pwUser->pw_dir;
}

/**
 * @brief 从指定文件中读取环境变量并设置到系统环境
 *
 * 该函数读取指定格式的配置文件,每行格式为"VAR=VALUE",
 * 忽略以#开头的注释行,并将解析出的环境变量设置到当前进程环境。
 *
 * @param fileName 环境变量配置文件路径
 */
void setEnvFile(const char* fileName) {
    FILE* file = fopen(fileName, "r");
    if(file == NULL)
        return;

    char* line = NULL;
    char envVar[1024];
    char envValue[1024];
    size_t n = 0;
    ssize_t read;
    while((read = getline(&line, &n, file)) != -1) {
        if(line[0] == '#')
            continue;
        if(line[read - 1] == '\n')
            line[read - 1] = '\0';
        int s = 0; // Env Var Size
        while(line[s] != '\0' && line[s] != '=')
            s++;
        if(line[s] == '\0')
            continue;
        strncpy(envVar, line, std::min(1023, s));
        envVar[std::min(1023, s)] = '\0';
        s++;
        strncpy(envValue, line + s, 1023);
        envValue[1023] = '\0';
        setenv(envVar, envValue, 0);
        // printf("%s : %s->%s\n", fileName, envVar, envValue);
    }
    if(line)
        free(line);
    fclose(file);
}

/**
 * 初始化环境配置函数
 *
 * 该函数用于加载SCCL配置文件,按照以下顺序查找:
 * 1. 首先检查环境变量"SCCL_CONF_FILE"指定的文件
 * 2. 其次查找用户主目录下的".sccl.conf"文件
 * 3. 最后尝试加载系统默认的"/etc/sccl.conf"文件
 *
 * 每个找到的配置文件都会被通过setEnvFile函数加载
 */
static void initEnvFunc() {
    char confFilePath[1024];
    const char* userFile = getenv("SCCL_CONF_FILE");
    if(userFile && strlen(userFile) > 0) {
        snprintf(confFilePath, sizeof(confFilePath), "%s", userFile);
        setEnvFile(confFilePath);
    } else {
        const char* userDir = userHomeDir();
        if(userDir) {
            snprintf(confFilePath, sizeof(confFilePath), "%s/.sccl.conf", userDir);
            setEnvFile(confFilePath);
        }
    }
    snprintf(confFilePath, sizeof(confFilePath), "/etc/sccl.conf");
    setEnvFile(confFilePath);
    return;
}

/**
 * 初始化环境变量(线程安全)
 *
 * 使用pthread_once确保initEnvFunc仅被调用一次
 * 适用于多线程环境下环境变量的初始化
 */
void initEnv() {
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    pthread_once(&once, initEnvFunc);
    return;
}

/**
 * @brief 加载环境变量参数并缓存
 *
 * 该函数用于从环境变量中读取整型参数值,并进行缓存以避免重复读取。
 * 如果环境变量未设置或解析失败,则使用默认值。
 *
 * @param env 环境变量名
 * @param deftVal 默认值
 * @param uninitialized 未初始化标记值
 * @param cache 用于缓存参数值的指针
 *
 * @note 该函数是线程安全的,使用互斥锁保护缓存操作
 */
void scclLoadParam(char const* env, int64_t deftVal, int64_t uninitialized, int64_t* cache) {
    static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_lock(&mutex);
    if(__atomic_load_n(cache, __ATOMIC_RELAXED) == uninitialized) {
        const char* str = scclGetEnv(env);
        int64_t value   = deftVal;
        if(str && strlen(str) > 0) {
            errno = 0;
            value = strtoll(str, nullptr, 0);
            if(errno) {
                value = deftVal;
                INFO(SCCL_LOG_CODEALL, "Invalid value %s for %s, using default %lld.", str, env, (long long)deftVal);
            } else {
                INFO(SCCL_LOG_TRANSPORT, "%s set by environment to %lld.", env, (long long)value);
            }
        }
        __atomic_store_n(cache, value, __ATOMIC_RELAXED);
    }
    pthread_mutex_unlock(&mutex);
    return;
}

/**
 * 获取环境变量的值
 *
 * @param name 环境变量名称
 * @return 环境变量的值,如果未找到则返回NULL
 *
 * @note 该函数会先初始化环境变量
 */
const char* scclGetEnv(const char* name) {
    initEnv();
    return getenv(name);
}

#define SCCL_THREAD_NAMELEN 16
SCCL_PARAM(SetThreadName, "SET_THREAD_NAME", 0);

/**
 * @brief 设置指定线程的名称
 *
 * 该函数使用GNU扩展的pthread_setname_np来设置线程名称。名称通过可变参数格式化字符串生成,
 * 最大长度为SCCL_THREAD_NAMELEN。仅在启用了_GNU_SOURCE宏且scclParamSetThreadName()返回1时生效。
 *
 * @param thread 要设置名称的线程句柄
 * @param fmt 格式化字符串,用于生成线程名称
 * @param ... 可变参数,用于格式化字符串
 */
void scclSetThreadName(pthread_t thread, const char* fmt, ...) {
    // pthread_setname_np 是 GNU 的非标准扩展
    // 需要以下特性测试宏
#ifdef _GNU_SOURCE
    // 检查是否启用了设置线程名称的功能,如果未启用则直接返回
    if(scclParamSetThreadName() != 1)
        return;

    // 定义一个足够长的字符数组用于存储线程名
    char threadName[SCCL_THREAD_NAMELEN];
    // 声明可变参数列表变量
    va_list vargs;

    // 初始化可变参数列表
    va_start(vargs, fmt);
    // 使用可变参数和格式化字符串生成线程名,并写入 threadName 数组
    vsnprintf(threadName, SCCL_THREAD_NAMELEN, fmt, vargs);
    // 结束可变参数列表的使用
    va_end(vargs);

    // 使用 pthread_setname_np 设置线程名称
    pthread_setname_np(thread, threadName);
#endif
}

} // namespace sccl