/* * task53.c — 关键文件内容监控守护进程 * * 功能: 每隔5分钟读取自身文件内容并计算hash值(数字指纹), * 若hash值发生变化(文件被篡改), 则将检测时间和新hash值 * 写入日志文件 log。 * * 用法: * ./task53 以守护进程方式后台运行(每5分钟检查一次) * ./task53 -f <秒> 前台运行, 可自定义检查间隔(便于测试) * ./task53 test 自检模式: 验证hash算法正确性 * * hash算法: 将文件按4字节切分, 每段视为一个无符号整数(小端序), * 累加各段得到hash值。不足4字节的剩余部分按单字节累加。 */ #include #include #include #include #include #include #include #include #define INTERVAL_SEC (5 * 60) /* 默认检查间隔: 5分钟 */ #define LOG_FILE "log" /* 日志文件名 */ /* * 计算数据的hash值(数字指纹) * data: 文件内容指针, len: 数据长度(字节) * 返回: 32位无符号hash值 */ 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; }