Файл: Федеральное государственное автономное образовательное учреждение высшего образования казанский (приволжский) федеральный университет высшая школа информационных технологий и информационных систем.docx
Добавлен: 08.11.2023
Просмотров: 92
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Файл MainDaemon.cpp
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "tinystr.h"
#include "tinyxml.h"
#include
#include
#include
#include
#include "string.h"
#include
#include
#include
using namespace std;
// лимит для установки максимально кол-во открытых дискрипторов
#define FD_LIMIT 1024*10
// константы для кодов завершения процесса
#define CHILD_NEED_WORK 1
#define CHILD_NEED_TERMINATE 2
#define PID_FILE "/var/run/my_daemon.pid"
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int node_status;
int need_start_app;
int diff_need_start;
int diff_app_status;
int my_app_status;
time_t last_time;
int stop_app;
int timeout;
string log_file;
string my_ip;
int my_port;
string diff_ip;
int diff_port;
string my_app;
// функция записи лога
int WriteLog(string Msg, ...) {
FILE *f = fopen(log_file.c_str(), "a");
if (f == NULL) {
return -1;
} else {
char buffer[80];
time_t seconds = time(NULL);
struct tm *timeinfo = localtime(&seconds);
strftime(buffer, 80, "%a %B %d %H:%M:%S %Y", timeinfo);
char third[1024];
snprintf(third, sizeof third, "%s\t%s", buffer, Msg.c_str());
fputs(third, f);
}
fclose(f);
return 1;
}
// функция загрузки конфига
int LoadConfig(char* FileName) {
TiXmlDocument *xml_file = new TiXmlDocument(FileName);
if (!xml_file->LoadFile()) {
printf("file have errors");
return -1;
}
TiXmlElement *xml_1 = 0;
xml_1 = xml_file->FirstChildElement();
TiXmlElement *xml_2 = 0;
xml_2 = xml_1->FirstChildElement("log_file");
log_file = xml_2->GetText();
xml_2 = xml_1->FirstChildElement("my_ip");
my_ip = xml_2->GetText();
xml_2 = xml_1->FirstChildElement("listen_port");
my_port = atoi(xml_2->GetText());
xml_2 = xml_1->FirstChildElement("another_ip");
diff_ip = xml_2->GetText();
xml_2 = xml_1->FirstChildElement("another_port");
diff_port = atoi(xml_2->GetText());
xml_2 = xml_1->FirstChildElement("timeout");
timeout = atoi(xml_2->GetText());
xml_2 = xml_1->FirstChildElement("my_app");
my_app = xml_2->GetText();
return 1;
}
void *Client(void*) {
int m_sock; // дескриптор сокета
sockaddr_in m_addr; // переменная адреса интерфейса
sockaddr_in s_addr; // переменная адреса получателя
string my_addr = my_ip; // Адрес локального интерфейса
string ip_addr = diff_ip; // Адрес получателя
/* Создание сокета и присвоение значения дескриптору сокета для UDP пакетов
* PF_INET - IP protocol family
* SOCK_DGRAM - Raw protocol interface */
if ((m_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
pthread_exit(NULL);
}
WriteLog("[CLIENT] Started\n");
/* Обнуляем переменную m_addr и забиваем её нужными значениями */
bzero(&m_addr, sizeof(m_addr));
m_addr.sin_family = AF_INET; // обязательно AF_INET!
m_addr.sin_port = htons(0); // 0 - выдать порт автоматом
/* Переводим адрес в нужный нам формат */
if (inet_aton(my_addr.c_str(), &m_addr.sin_addr) == 0) {
WriteLog("[CLIENT] Creating address failed\n");
perror("inet_aton");
close(m_sock);
pthread_exit(NULL);
}
WriteLog("[CLIENT] Bind\n");
/* Биндим сокет */
if (bind(m_sock, (struct sockaddr*) &m_addr, sizeof(m_addr)) < 0) {
WriteLog("[CLIENT] Bind failed\n");
perror("bind");
close(m_sock);
pthread_exit(NULL);
}
/* Обнуляем переменную s_addr и забиваем её нужными значениями */
bzero(&s_addr, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(diff_port);
/* Переводим адрес в нужный нам формат */
if (inet_aton(ip_addr.c_str(), &s_addr.sin_addr) == 0) {
WriteLog("[Client] Inet ation failed\n");
perror("inet_aton");
close(m_sock);
pthread_exit(NULL);
}
/* Засыпаем адресата пачкой пакетов */
string msg;
while (1) {
pthread_mutex_lock(&mutex1);
WriteLog("[CLIENT] Sent\n");
if ((my_app_status == 1) && (diff_need_start == 1)) {
msg = "10";
}
if ((my_app_status == 1) && (diff_need_start == 0)) {
msg = "10";
}
if ((my_app_status == 0) && (diff_need_start == 1)) {
msg = "01";
}
if ((my_app_status == 0) && (diff_need_start == 0)) {
msg = "00";
}
if (sendto(m_sock, msg.c_str(), 3, MSG_NOSIGNAL,
(struct sockaddr*) &s_addr, sizeof(s_addr)) < 0) {
perror("sendto");
close(m_sock);
pthread_exit(NULL);
}
pthread_mutex_unlock(&mutex1);
sleep(2);
}
WriteLog("[CLIENT] Failed/n");
/* Закрываем сокет и выходим из программы */
close(m_sock);
pthread_exit(NULL);
}
void *Server(void*) {
int m_sock; // дескриптор сокета
sockaddr_in m_addr; // переменная адреса интерфейса
string my_addr = my_ip; // Адрес локального интерфейса
/* Создание сокета и присвоение значения дескриптору сокета для UDP пакетов
* PF_INET - IP protocol family
* SOCK_DGRAM - Raw protocol interface */
if ((m_sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket");
pthread_exit(NULL);
}
WriteLog("[SERVER] Started\n");
/* Обнуляем переменную m_addr и забиваем её нужными значениями */
bzero(&m_addr, sizeof(m_addr));
m_addr.sin_family = AF_INET; // обязательно AF_INET!
m_addr.sin_port = htons(my_port); // 0 - выдать порт автоматом
/* Переводим адрес в нужный нам формат */
if (inet_aton(my_addr.c_str(), &m_addr.sin_addr) == 0) {
WriteLog("[SERVER] Creating address failed\n");
perror("inet_aton");
close(m_sock);
pthread_exit(NULL);
}
WriteLog("[SERVER] Bind\n");
/* Биндим сокет */
if (bind(m_sock, (struct sockaddr*) &m_addr, sizeof(m_addr)) < 0) {
WriteLog("[SERVER] Bind failed\n");
perror("bind");
close(m_sock);
pthread_exit(NULL);
}
/* Получаем пакеты от отправителя */
char recvline[3];
int bytesrecv;
while (1) {
if ((bytesrecv = recvfrom(m_sock, recvline, 3, MSG_NOSIGNAL,
(sockaddr*) NULL, NULL)) < 0) {
perror("recvfrom");
close(m_sock);
pthread_exit(NULL);
}
pthread_mutex_lock(&mutex1);
last_time = time(NULL);
if (node_status == 0) {
WriteLog("[SERVER] Another node online\n");
node_status = 1;
}
WriteLog("[SERVER] Heartbeat\n");
if (strcmp(recvline, "00") == 0) {
diff_app_status = 0;
}
if (strcmp(recvline, "01") == 0) {
if ((my_app_status == 0) && (diff_app_status == 0)) {
need_start_app = 1;
}
}
if (strcmp(recvline, "10") == 0) {
diff_app_status == 1;
}
pthread_mutex_unlock(&mutex1);
}
/* Закрываем сокет и выходим из программы */
WriteLog("[SERVER] Failed/n");
close(m_sock);
pthread_exit(NULL);
}
void *Monitoring(void*) {
WriteLog("[MONITOR] Started\n");
while (1) {
pthread_mutex_lock(&mutex1);
WriteLog("[MONITOR] Status check\n");
stringstream ss;
if (need_start_app == 1) {
need_start_app = 0;
if ((my_app_status == 0) && (diff_app_status == 0)) {
WriteLog("[MONITOR] Start application\n");
my_app_status = 1;
//start app
}
}
if (node_status == 1) {
time_t current_time = time(NULL);
double diff = difftime(current_time, last_time);
if (diff > timeout) {
WriteLog("[MONITOR] Another node offline\n");
node_status = 0;
WriteLog(
"[MONITOR] Checking the status of the another application\n");
if (diff_app_status == 1) {
WriteLog("[MONITOR] Start application\n");
diff_app_status = 0;
my_app_status = 1;
//need start app
} else {
WriteLog(
"[MONITOR] Application on another server is not running\n");
}
}
}
pthread_mutex_unlock(&mutex1);
sleep(7);
}
pthread_exit(NULL);
}
// функция мониторинга
int InitWorkThread() {
int rc1, rc2, rc3;
pthread_t thread1, thread2, thread3;
/* Create independent threads each of which will execute functionC */
if ((rc1 = pthread_create(&thread1, NULL, &Client, NULL))) {
WriteLog("[DEAMON] Client creation failed");
}
if ((rc2 = pthread_create(&thread2, NULL, &Server, NULL))) {
WriteLog("[DEAMON] Server creation failed");
}
if ((rc3 = pthread_create(&thread3, NULL, &Monitoring, NULL))) {
WriteLog("[DEAMON] Monitor creation failed");
}
/* Wait till threads are complete before main continues. Unless we */
/* wait we run the risk of executing an exit which will terminate */
/* the process and all threads before the threads have completed. */
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_join(thread3, NULL);
return 1;
}
// функция обработки сигналов
static void signal_error(int sig, siginfo_t *si, void *ptr) {
void* ErrorAddr;
void* Trace[16];
int x;
int TraceSize;
char** Messages;
// запишем в лог что за сигнал пришел
WriteLog("[DAEMON] Signal: %s, Addr: 0x%0.16X\n", strsignal(sig),
si->si_addr);
/*
#if __WORDSIZE == 64 // если дело имеем с 64 битной ОС
// получим адрес инструкции которая вызвала ошибку
ErrorAddr = (void*)((ucontext_t*)ptr)->uc_mcontext.gregs[REG_RIP];
#else
// получим адрес инструкции которая вызвала ошибку
ErrorAddr = (void*) ((ucontext_t*) ptr)->uc_mcontext.gregs[REG_EIP];
#endif
*/
// произведем backtrace чтобы получить весь стек вызовов
TraceSize = backtrace(Trace, 16);
Trace[1] = ErrorAddr;
// получим расшифровку трасировки
Messages = backtrace_symbols(Trace, TraceSize);
if (Messages) {
WriteLog("== Backtrace ==\n");
// запишем в лог
for (x = 1; x < TraceSize; x++) {
WriteLog("%s\n", Messages[x]);
}
WriteLog("== End Backtrace ==\n");
free(Messages);
}
WriteLog("[DAEMON] Stopped\n");
// остановим все рабочие потоки и корректно закроем всё что надо
DestroyWorkThread();
// завершим процесс с кодом требующим перезапуска
exit(CHILD_NEED_WORK);
}
// функция установки максимального кол-во дескрипторов которое может быть открыто
int SetFdLimit(int MaxFd) {
struct rlimit lim;
int status;
// зададим текущий лимит на кол-во открытых дискриптеров
lim.rlim_cur = MaxFd;
// зададим максимальный лимит на кол-во открытых дискриптеров
lim.rlim_max = MaxFd;
// установим указанное кол-во
status = setrlimit(RLIMIT_NOFILE, &lim);
return status;
}
int WorkProc() {
struct sigaction sigact;
sigset_t sigset;
int signo;
int status;
// сигналы об ошибках в программе будут обрататывать более тщательно
// указываем что хотим получать расширенную информацию об ошибках
sigact.sa_flags = SA_SIGINFO;
// задаем функцию обработчик сигналов
sigact.sa_sigaction = signal_error;
sigemptyset(&sigact.sa_mask);
// установим наш обработчик на сигналы
sigaction(SIGFPE, &sigact, 0); // ошибка FPU
sigaction(SIGILL, &sigact, 0); // ошибочная инструкция
sigaction(SIGSEGV, &sigact, 0); // ошибка доступа к памяти
sigaction(SIGBUS, &sigact, 0); // ошибка шины, при обращении к физической памяти
sigemptyset(&sigset);
// блокируем сигналы которые будем ожидать
// сигнал остановки процесса пользователем
sigaddset(&sigset, SIGQUIT);
// сигнал для остановки процесса пользователем с терминала
sigaddset(&sigset, SIGINT);
// сигнал запроса завершения процесса
sigaddset(&sigset, SIGTERM);
// пользовательский сигнал который мы будем использовать для обновления конфига
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
// Установим максимальное кол-во дискрипторов которое можно открыть
SetFdLimit(FD_LIMIT);
// запускаем все рабочие потоки
status = InitWorkThread();
if (!status) {
// цикл ожижания сообщений
for (;;) {
// ждем указанных сообщений
sigwait(&sigset, &signo);
break;
}
}
WriteLog("[MONITOR] Stopped/n");
// вернем код не требующим перезапуска
return CHILD_NEED_TERMINATE;
}
void SetPidFile(string Filename) {
FILE* f;
f = fopen(Filename.c_str(), "w+");
if (f) {
fprintf(f, "%u", getpid());
fclose(f);
}
}
void Init_vars() {
node_status = 0;
need_start_app = 0;
diff_app_status = 0;
my_app_status = 0;
stop_app = 0;
diff_need_start = 0;
last_time = time(NULL);
}
int Daemon() {
int pid;
int status;
int need_start = 1;
sigset_t sigset;
siginfo_t siginfo;
WriteLog("[DAEMON] Started\n");
// настраиваем сигналы которые будем обрабатывать
sigemptyset(&sigset);
// сигнал остановки процесса пользователем
sigaddset(&sigset, SIGQUIT);
// сигнал для остановки процесса пользователем с терминала
sigaddset(&sigset, SIGINT);
// сигнал запроса завершения процесса
sigaddset(&sigset, SIGTERM);
// сигнал посылаемый при изменении статуса дочернего процесс
sigaddset(&sigset, SIGCHLD);
// сигнал посылаемый при изменении статуса дочернего процесс
sigaddset(&sigset, SIGCHLD);
// пользовательский сигнал который мы будем использовать для обновления конфига
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_BLOCK, &sigset, NULL);
// данная функция создат файл с нашим PID'ом
SetPidFile(PID_FILE);
Init_vars();
// создаём потомка
pid = fork();
if (pid == -1) // если произошла ошибка
{
// запишем в лог сообщение об этом
WriteLog("[MONITOR] Fork failed (%s)\n", strerror(errno));
} else if (!pid) // если мы потомок
{
// данный код выполняется в потомке
// запустим функцию отвечающую за работу демона
status = WorkProc();
// завершим процесс
exit(status);
} else // если мы родитель
{
for (;;) {
// данный код выполняется в родителе
// ожидаем поступление сигнала
sigwaitinfo(&sigset, &siginfo);
// если пришел сигнал от потомка
if (siginfo.si_signo == SIGCHLD) {
// получаем статус завершение
wait(&status);
// преобразуем статус в нормальный вид
status = WEXITSTATUS(status);
// если потомок завершил работу с кодом говорящем о том, что нет нужны дальше работать
if (status == CHILD_NEED_TERMINATE) {
// запишем в лог сообщени об этом
WriteLog("[DAEMON] Childer stopped\n");
// прервем цикл
break;
} else if (status == CHILD_NEED_WORK) // если требуется перезапустить потомка
{
// запишем в лог данное событие
WriteLog("[DAEMON] Childer restart\n");
}
} else // если пришел какой-либо другой ожидаемый сигнал
{
// запишем в лог информацию о пришедшем сигнале
WriteLog("[DAEMON] Signal %s\n", strsignal(siginfo.si_signo));
// убьем потомка
kill(pid, SIGTERM);
status = 0;
break;
}
}
}
// запишем в лог, что мы остановились
WriteLog("[DAEMON] Stopped\n");
// удалим файл с PID'ом
unlink(PID_FILE);
return status;
}
int main(int argc, char** argv) {
int status;
int pid;
// если параметров командной строки меньше двух, то покажем как использовать демана
if (argc != 2) {
printf("Usage: ./my_daemon filename.cfg\n");
return -1;
}
// загружаем файл конфигурации
status = LoadConfig(argv[1]);
if (!status) // если произошла ошибка загрузки конфига
{
printf("Error: Load config failed\n");
WriteLog("Error: Load config failed\n");
return -1;
}
FILE *f = fopen(log_file.c_str(), "w");
if (f == NULL) {
printf("Could not open log file");
} else {
char buffer[80];
time_t seconds = time(NULL);
struct tm *timeinfo = localtime(&seconds);
strftime(buffer, 80, "%a %B %d %H:%M:%S %Y", timeinfo);
char third[1024];
snprintf(third, sizeof third, "%s\t%s", buffer, "Start\n");
fputs(third, f);
}
fclose(f);
// создаем потомка
pid = fork();
if (pid == -1) // если не удалось запустить потомка
{
// выведем на экран ошибку и её описание
printf("Start Daemon Error: %s\n", strerror(errno));
return -1;
} else if (!pid) // если это потомок
{
// данный код уже выполняется в процессе потомка
// разрешаем выставлять все биты прав на создаваемые файлы,
// иначе у нас могут быть проблемы с правами доступа
umask(0);
// создаём новый сеанс, чтобы не зависеть от родителя
setsid();
// переходим в корень диска, если мы этого не сделаем, то могут быть проблемы.
// к примеру с размантированием дисков
chdir("/");
printf("run!");
// закрываем дискрипторы ввода/вывода/ошибок, так как нам они больше не понадобятся
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// Данная функция будет осуществлять слежение за процессом
status = Daemon();
return status;
} else // если это родитель
{
// завершим процес, т.к. основную свою задачу (запуск демона) мы выполнили
return 0;
}
}
Service.py
from flask import Flask, render_template, redirect, url_for
from flask import request
app = Flask(__name__)
app_status="off"
node_status="on"
@app.route('/', methods=['GET', 'POST'])
def contact():
global app_status
global node_status
if request.method == 'POST':
if request.form['submit'] == 'This node':
app_status= "on"
return redirect(url_for('.contact'))
elif request.form['submit'] == 'Another node':
app_status="on"
return redirect(url_for('.contact'))
elif request.form['submit'] == 'Stop app':
app_status="off"
return redirect(url_for('.contact'))
else:
pass # unknown
elif request.method == 'GET':
return render_template('index_3.html',node_status=node_status, app_statu s=app_status )
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8081,debug=True)
Client.py
from flask import Flask, render_template, redirect, url_for
from flask import request
app = Flask(__name__)
@app.route('/test', methods=['GET', 'POST'])
def contact1():
if request.method == 'POST':
if request.form['submit'] == 'Return':
return redirect(url_for('.contact'))
#pass # do somethin
#print "Left gjhgjh"
else:
pass # unknown
elif request.method == 'GET':
return render_template('index_2.html')
@app.route('/', methods=['GET', 'POST'])
def my_client():
if request.method == 'POST':
if request.form['submit'] == 'Left':
#pass # do somethin
print ("Left")
return redirect(url_for('.my_client'))
elif request.form['submit'] == 'Right':
print ("Roght")
return redirect(url_for('. my_client'))
else:
pass # unknown
elif request.method == 'GET':
return render_template('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8081,debug=True)
index.html
Client
Service app
Node 1
1 2 3 4 5 6