Lab 3 daemon.c
This commit is contained in:
304
lab3/daemon.c
Normal file
304
lab3/daemon.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user