Lab 3 daemon.c

This commit is contained in:
2025-10-14 15:00:29 +03:00
parent 615210a55a
commit b4f1779922

304
lab3/daemon.c Normal file
View File

@@ -0,0 +1,304 @@
/* Демпон для отслеживания изменений в файлах в заданной директории
Формат конфигурационного файла (по умолчанию /etc/my-daemon/config):
directory=/home/arity
interval=20
Компиляция и запуск:
gcc -o daemon daemon.c
./daemon
./daemon /home/arity/daemon/config
Просмотр логов:
sudo journalctl -f -t MY-DAEMON
sudo journalctl -f -t MY-DAEMON --since "now" -p info
Узнать PID демона:
ps -u arity | grep daemon
Перечитать конфигурационный файл:
kill -SIGHUP <pid>
Завершить работу демона:
kill <pid>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <syslog.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <dirent.h>
#include <stdio.h>
pid_t pid, sid;
const char * CONFIG_PATH;
char * DIR_PATH;
int INTERVAL;
time_t LAST_CHECK_TIME;
int sighup = 0;
int stop = 0;
void update_parameter(char * key, char * value) {
syslog(LOG_DEBUG, "Found parameter %s = %s", key, value);
if (strcmp(key, "directory") == 0) {
free(DIR_PATH);
DIR_PATH = strdup(value);
syslog(LOG_DEBUG, "Updated directory path to %s", DIR_PATH);
} else if (strcmp(key, "interval") == 0) {
INTERVAL = atoi(value);
syslog(LOG_DEBUG, "Updated interval to %d", INTERVAL);
} else {
syslog(LOG_CRIT, "Unknown parameter %s in config file %s",
key, CONFIG_PATH);
}
}
void read_config() {
syslog(LOG_INFO, "Read config file %s", CONFIG_PATH);
/* Открываем файловый дескриптор */
int fd = open(CONFIG_PATH, O_RDONLY);
if (fd < 0) {
syslog(LOG_CRIT,
"Unable to read config file %s",
CONFIG_PATH);
exit(EXIT_FAILURE);
}
/* Парсим конфигурационный файл */
char c;
char * key = NULL;
char * value = NULL;
size_t key_len = 0;
size_t val_len = 0;
int in_value = 0;
while (read(fd, & c, 1) == 1) {
if (c == '\n' || c == '\r') {
if (key_len > 0 && val_len > 0) {
key[key_len] = '\0';
value[val_len] = '\0';
update_parameter(key, value);
}
// сбрасываем под следующую строку
free(key);
free(value);
key = value = NULL;
key_len = val_len = 0;
in_value = 0;
} else if (c == '=') {
in_value = 1;
} else {
if (!in_value) {
key = realloc(key, key_len + 2);
if (key == NULL) {
syslog(LOG_CRIT, "Unable to allocate memory");
exit(EXIT_FAILURE);
}
key[key_len++] = c;
} else {
value = realloc(value, val_len + 2);
if (value == NULL) {
syslog(LOG_CRIT, "Unable to allocate memory");
exit(EXIT_FAILURE);
}
value[val_len++] = c;
}
}
}
close(fd);
// обработка последней строки без \n
if (key_len > 0 && val_len > 0) {
key[key_len] = '\0';
value[val_len] = '\0';
update_parameter(key, value);
}
free(key);
free(value);
syslog(LOG_DEBUG, "Read values from config file successfully");
}
void signal_handler(int sig) {
switch (sig) {
case SIGHUP:
syslog(LOG_DEBUG, "hangup signal");
sighup = 1;
break;
case SIGTERM:
syslog(LOG_INFO, "Terminate signal catched. Stopping daemon...");
stop = 1;
break;
}
}
void check_file(const char * path) {
struct stat st;
if (lstat(path, &st) != 0) {
syslog(LOG_WARNING, "Cannot stat %s", path);
return;
}
syslog(LOG_DEBUG, "Found file %s, size=%ld", path, st.st_size);
// Обязательно нестрогое сравнение, иначе не заметим изменения,
// которые произошли в секунду обновления LAST_CHECK_TIME
if (st.st_mtime >= LAST_CHECK_TIME) {
if (st.st_mode & S_IFDIR) {
syslog(LOG_INFO, "Directory %s was modified at %s", path, ctime(&st.st_mtime));
} else {
syslog(LOG_INFO, "File %s was modified at %s", path, ctime(&st.st_mtime));
}
}
}
void check_directory(const char * path) {
syslog(LOG_DEBUG, "Recursively checking directory %s", path);
DIR * dir = opendir(path);
if (dir == NULL) {
syslog(LOG_CRIT, "Unable to open directory %s", path);
return;
}
struct dirent * entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry -> d_name, ".") == 0 || strcmp(entry -> d_name, "..") == 0) {
continue;
}
size_t path_len = strlen(path);
size_t name_len = strlen(entry -> d_name);
size_t full_path_len = path_len + name_len + 2;
char * full_path = malloc(full_path_len);
if (full_path == NULL) {
syslog(LOG_CRIT, "Unable to allocate memory");
exit(EXIT_FAILURE);
}
memcpy(full_path, path, path_len);
full_path[path_len] = '/';
memcpy(full_path + path_len + 1, entry -> d_name, name_len);
full_path[full_path_len - 1] = '\0';
struct stat st;
if (lstat(full_path, & st) != 0) {
syslog(LOG_WARNING, "Cannot stat %s", full_path);
} else {
// Рекурсивно проверяем подиректории
check_file(full_path);
if (st.st_mode & S_IFDIR) {
check_directory(full_path);
}
}
free(full_path);
}
closedir(dir);
}
void do_daemons_job() {
// Сохраняем время до начала проверки, иначе в теории можем незаметить изменения,
// которые произошли в течение проверки.
int new_check_time = time(NULL);
check_directory(DIR_PATH);
LAST_CHECK_TIME = new_check_time;
}
void daemonize() {
openlog("MY-DAEMON", 0, LOG_DAEMON);
/* Закрываем стандартные файловые дескприторы */
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
pid = fork();
if (pid < 0) {
syslog(LOG_CRIT, "Unable to fork process!");
exit(EXIT_FAILURE);
}
/* Если fork получился, то родительский процесс можно завершить */
if (pid > 0) {
syslog(LOG_DEBUG, "Killed parent process");
exit(EXIT_SUCCESS);
}
umask(0);
/* Sid для дочернего процесса */
sid = setsid();
if (sid < 0) {
syslog(LOG_CRIT, "Unable to set session id");
exit(EXIT_FAILURE);
}
/* Изменяем текущий рабочий каталог */
if ((chdir("/")) < 0) {
syslog(LOG_CRIT, "Unable to change working directory");
exit(EXIT_FAILURE);
}
}
int main(int argc, char * argv[]) {
syslog(LOG_DEBUG, " --- STARTING DAEMON --- ");
daemonize();
syslog(LOG_DEBUG, "Daemon started successfully");
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
DIR_PATH = NULL;
INTERVAL = 0;
if (argc > 1) {
CONFIG_PATH = argv[1];
} else {
CONFIG_PATH = "/etc/my-daemon/config";
}
read_config();
if (DIR_PATH == NULL || INTERVAL == 0) {
syslog(LOG_CRIT, "Directory path or interval is not set");
exit(EXIT_FAILURE);
}
LAST_CHECK_TIME = time(NULL);
while (!stop) {
if (sighup) {
read_config();
sighup = 0;
}
do_daemons_job();
sleep(INTERVAL);
}
if (DIR_PATH) { free(DIR_PATH); DIR_PATH = NULL; }
syslog(LOG_INFO, "Daemon stopped");
closelog();
return 0;
}