О демонах

Прежде чем писать настоящих демонов попробуем начать с чего-то попроще.

Надо понимать, что демон — это программа, которая работает потенциально бесконечно долго и ждет указаний.

Возможно самый близкий вам пример демона будет СУБД. То есть это некий сервис, у которого есть набор файлов, которыми он управляет, а если его хорошо попросить он с вами еще и какой-то информацией поделится

Попробуем написать программу которая будет мониторить папку и сообщать об изменениях которые в ней происходит.

Для отслеживания изменений в linux используется механизм inotify, про него даже можно прочитать в мануале

man inotify

но там много английских непонятных слов, так что давайте рассмотрим, как с ним работать на примере.

Создаем болванку проекта

Создадим проект

mkdir cpp05
cd $_

создадим файлик main.cpp и запишем в него

#include <iostream>
#include <sys/inotify.h> // для работы с inotify
#include <unistd.h> // для чтения изменений

using namespace std;

int main(int argc, char* argv[]) {
        cout << "проверка" << endl;
        return 0;
}

можно скомпилировать и убедится, что все работает

Создаем Makefile

Кстати, давайте, чтобы не тратить время постоянно на пересборку и запуск программы, создадим специальный файлик Makefile и пропишем в него команды которые соберут проект, а потом запустят

и напишем в него

build: 
	g++ main.cpp -o main
	./main

очень важно чтобы отступы были сделаны табами.

А теперь смотрите, достаточно написать

make

и наша программа автоматически соберется и запуститься.

если вы не хотите, чтобы писались команды, которые использовались для сборки или запуска, добавьте перед ними @

build: 
	@g++ main.cpp -o main
	@./main

Вообще, Makefile – это специальные файлы с помощью которых упрощают процесс сборки для С/C++ проектов, в том числе и ядро линукс использует Makefile, но они как правило очень большие. И есть даже специальные утилиты для генерации Makefile`ов

Мониторим папку с inotify

Схема работы с inotify в принципе достаточно простая

#include <iostream>
#include <sys/inotify.h>
#include <unistd.h>

using namespace std;

int main(int argc, char* argv[]) {
	// создаем файловый дескриптор для отслеживания
	int inotify_fd = inotify_init();

	// подключаем к inotify_fd мониторинг,
	// "/home/m" -- эта папка которую мониторим
	// IN_MODIFY | IN_CREATE | IN_DELETE -- события которые мониторим
	int watch_fd = inotify_add_watch(inotify_fd, "/home/m", IN_MODIFY | IN_CREATE | IN_DELETE);


	// А ТУТ СЕЙЧАС ХИТРОСТИ НАЧНУТЬСЯ

	return 0;
}

так как мы пишем демон, а демон работает бесконечно долго, то мы заводим бесконечный цикл и в этом цикле просто пытаемся читать с файлового дескриптора inotify_fd

// это мы выше написали
int inotify_fd = inotify_init();
int watch_fd = inotify_add_watch(inotify_fd, "/home/m", IN_MODIFY | IN_CREATE | IN_DELETE);


// а тут новое пошло 
// сначала бесконечный цикл
while(true) {
	char buffer[10000]; // буфер под события изменений
	
	// подключаемся к inotify_fd на чтение 
	// эта строчка блокирует программу, 
	// пока не произойдет какое-нибудь изменение
	int length = read(inotify_fd, buffer, 10000);

	// так как событие может прийти несколько то надо их все обойти
	int i = 0;
	while (i < length) {
		// вытаскиваем инфу по событию
		struct inotify_event *event = (struct inotify_event *)&buffer[i];

		// проверим тип события
		if (event->mask & IN_MODIFY) {
			// и выведем сообщение о событии
			// в event->name лежит имя файла
			cout << "изменился" << event->name << endl;
		}
		else if (event->mask & IN_CREATE) {
			cout << "создали файл" << event->name << endl;
		}

		// сдвигаемся на следующее событие
		i += sizeof(struct inotify_event) + event->len;
	}
	
}

inotify_rm_watch(inotify_fd, watch_fd);
close(inotify_fd);
return 0;

можно потестить

Теперь попробуйте сделать задание. Так как в задании надо читать и писать в файл, вот вам болванки кода, под эти операции.

Чтобы прочитать содержимое файла, используйте следующий код

std::ifstream ifs("/home/m/file.txt"); // читаем файл по пути
std::string content; // переменная под содержимое файл
if (ifs.is_open()) {
	// считываем содержимое файла в переменную
	content.assign((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); 
	ifs.close();
}

чтобы записать что-нибудь в файл использует такой код

ofstream ofs("/home/m/file.txt");
if (ofs.is_open()) {
	ofs << str << endl;
	ofs.close();
}

Задание

Напишите программу, которая мониторит папку и пишет информацию об изменениях в этой папке в файл лога, примерно в таком виде

---
Файл file.txt добавлен
---
Файл file.txt изменился, его новое содержимое:
    очень длинной содержимое файла
    в несколько строк даже
---
Файл file.txt удален
...