Files
C-exp-collection/exp0.5/task53_ai.c

206 lines
6.8 KiB
C
Raw Normal View History

2026-05-15 21:25:01 +08:00
/*
* task53.c
*
* : 5hash值(),
* hash值发生变化(), hash值
* log
*
* :
* ./task53 (5)
* ./task53 -f <> , (便)
* ./task53 test : hash算法正确性
*
* hash算法: 4, (),
* hash值4
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define INTERVAL_SEC (5 * 60) /* 默认检查间隔: 5分钟 */
#define LOG_FILE "log" /* 日志文件名 */
/*
* hash值()
* data: , len: ()
* : 32hash值
*/
static unsigned int compute_hash(const char *data, size_t len)
{
unsigned int hash = 0;
const unsigned char *p = (const unsigned char *)data;
size_t i, n = len / 4; /* 完整的4字节段个数 */
/* 每4个字节拼成一个无符号整数(小端序: 低地址为低字节) */
for (i = 0; i < n; i++) {
unsigned int val = (unsigned int)p[i * 4]
| ((unsigned int)p[i * 4 + 1] << 8)
| ((unsigned int)p[i * 4 + 2] << 16)
| ((unsigned int)p[i * 4 + 3] << 24);
hash += val; /* 累加到hash值 */
}
/* 处理末尾不足4字节的剩余部分, 逐字节累加 */
size_t rem = len % 4, base = n * 4;
for (i = 0; i < rem; i++)
hash += (unsigned int)p[base + i];
return hash;
}
/*
*
* path: , out_len: ,
* : (free), NULL
*/
static char *read_file(const char *path, size_t *out_len)
{
int fd = open(path, O_RDONLY);
if (fd < 0) return NULL; /* 文件打开失败 */
/* 通过lseek获取文件大小 */
off_t size = lseek(fd, 0, SEEK_END);
if (size < 0) { close(fd); return NULL; }
lseek(fd, 0, SEEK_SET); /* 回到文件开头 */
char *buf = malloc((size_t)size + 1); /* 多分配1字节放'\0' */
if (!buf) { close(fd); return NULL; }
ssize_t n = read(fd, buf, (size_t)size);
if (n < 0) { free(buf); close(fd); return NULL; }
buf[n] = '\0'; /* 字符串结尾 */
*out_len = (size_t)n;
close(fd);
return buf;
}
/*
*
* hash: hash值
* : YYYY-MM-DD HH:MM:SS hash=<>
*/
static void write_log(unsigned int hash)
{
FILE *fp = fopen(LOG_FILE, "a"); /* 追加模式打开日志 */
if (!fp) return;
time_t now = time(NULL); /* 获取当前时间 */
char ts[64];
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(fp, "%s hash=%u\n", ts, hash); /* 写入时间和hash */
fclose(fp);
}
/*
* (daemon)
* : fork子进程并退出父进程 -> -> IO
*/
static void daemonize(void)
{
pid_t pid = fork();
if (pid < 0) exit(1); /* fork失败 */
if (pid > 0) exit(0); /* 父进程退出, 子进程继续 */
setsid(); /* 创建新会话, 脱离控制终端 */
umask(0); /* 重置文件权限掩码 */
/* 关闭标准输入、输出、错误, 守护进程不需要终端IO */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
int main(int argc, char *argv[])
{
/*
* : ./task53 test
* hash算法 hash, hash
*/
if (argc > 1 && strcmp(argv[1], "test") == 0) {
printf("=== 自检模式 ===\n");
/* 测试1: 不同内容应产生不同hash值 */
const char *data1 = "hello world";
unsigned int h1 = compute_hash(data1, strlen(data1));
printf("hash(\"%s\") = %u\n", data1, h1);
const char *data2 = "hello worlD"; /* 仅一个字节不同 */
unsigned int h2 = compute_hash(data2, strlen(data2));
printf("hash(\"%s\") = %u\n", data2, h2);
if (h1 != h2)
printf("通过: 不同内容 -> 不同hash值\n");
else
printf("失败: 不同内容应产生不同的hash值\n");
/* 测试2: 相同内容应产生相同hash值 */
unsigned int h3 = compute_hash(data1, strlen(data1));
if (h1 == h3)
printf("通过: 相同内容 -> 相同hash值\n");
else
printf("失败: 相同内容应产生相同的hash值\n");
/* 测试3: 读取自身并计算hash */
size_t len;
char *self = read_file("task53.c", &len);
if (self) {
unsigned int hself = compute_hash(self, len);
printf("自身hash = %u, 文件大小 = %zu 字节\n", hself, len);
free(self);
}
return 0;
}
/*
*
* -f (daemonize), 便
*
*/
int foreground = (argc > 1 && strcmp(argv[1], "-f") == 0);
int interval = INTERVAL_SEC;
if (argc > 2) interval = atoi(argv[2]); /* 自定义检查间隔 */
if (interval <= 0) interval = INTERVAL_SEC;
if (!foreground) daemonize(); /* 后台模式则转为守护进程 */
/* 读取文件初始快照, 计算基准hash值 */
size_t len;
char *prev_data = read_file("task53.c", &len);
if (!prev_data) return 1; /* 读取失败则退出 */
unsigned int prev_hash = compute_hash(prev_data, len);
/* 主循环: 每隔interval秒检查一次 */
while (1) {
sleep(interval);
/* 重新读取文件内容 */
size_t cur_len;
char *cur_data = read_file("task53.c", &cur_len);
if (!cur_data) continue; /* 读取失败则跳过本次 */
/* 计算当前hash并与基准hash比较 */
unsigned int cur_hash = compute_hash(cur_data, cur_len);
if (cur_hash != prev_hash) {
/* 检测到篡改: 写入日志 */
write_log(cur_hash);
/* 更新基准快照, 避免同一修改被重复记录 */
free(prev_data);
prev_data = cur_data;
prev_hash = cur_hash;
} else {
free(cur_data); /* 未变化则释放当前数据 */
}
}
free(prev_data);
return 0;
}