From b4f1779922ebdbd79fb92800cfb0e6a4a724c3d3 Mon Sep 17 00:00:00 2001 From: Arity-T Date: Tue, 14 Oct 2025 15:00:29 +0300 Subject: [PATCH] Lab 3 daemon.c --- lab3/daemon.c | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 lab3/daemon.c diff --git a/lab3/daemon.c b/lab3/daemon.c new file mode 100644 index 0000000..92bd407 --- /dev/null +++ b/lab3/daemon.c @@ -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 + +Завершить работу демона: +kill +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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; +} \ No newline at end of file