Compare commits
59 Commits
239197ffed
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 878386bdd3 | |||
| 611850435d | |||
| 8244282fa4 | |||
| b67f987031 | |||
| 74cbb0bb5f | |||
| b4672e4057 | |||
| b91b30b1e0 | |||
| b01a9f3828 | |||
| a6737308c2 | |||
| a99c60d529 | |||
| bd9e0b01f6 | |||
| 82f1c744dc | |||
| 011e79e676 | |||
| 3ea4c96cfc | |||
| c8d080d7b0 | |||
| bf96109742 | |||
| 87e326e869 | |||
| dbb860a00b | |||
| c78bb31b69 | |||
| fa6c131e9b | |||
| 85654f51a0 | |||
| 2e8a323210 | |||
| fafcdfa9c7 | |||
| 7e6b3afcdc | |||
| 4036a703c8 | |||
| 7e91e04afb | |||
| 56adae89ec | |||
| c483366f4d | |||
| f1d16093b6 | |||
|
|
fa19c8327b | ||
| fa0ec74b35 | |||
| 7b98b16e6e | |||
| 7933a21766 | |||
|
|
3976307952 | ||
| 2311ff28c9 | |||
|
|
48cc6af869 | ||
| 7feea8ccef | |||
| b638d510a3 | |||
| 7ad6b001eb | |||
| 56d738bf8e | |||
| 171361480d | |||
| 2605fef9be | |||
| 08ff104659 | |||
| 719562239a | |||
|
|
ff8c7bae7b | ||
| c3a572619f | |||
| 7613ac88d9 | |||
| e7ca4cb434 | |||
| 486d9384cc | |||
| c981bfd033 | |||
| b55f72f2e4 | |||
| d406657bfb | |||
|
|
ccb471dcc2 | ||
| 2e2cf449a4 | |||
|
|
577ad19b9f | ||
| 30a556f8f0 | |||
| 024fe94ce9 | |||
| 3f96d27ee9 | |||
| e0011e421d |
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Build files
|
||||
target
|
||||
Cargo.lock
|
||||
2080
Cargo.lock
generated
Normal file
2080
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "cai-watchdog"
|
||||
version = "0.9.0"
|
||||
authors = ["Alexander I. Chebykin <alex.chebykin@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
file-utils = "0.1"
|
||||
chrono = "0.4"
|
||||
# Sync
|
||||
#reqwest = { version = "0.11", features = ["blocking"] }
|
||||
# Async
|
||||
reqwest = "0.12"
|
||||
tokio = { version = "1.43", features = ["full"] }
|
||||
ini = "1.3"
|
||||
exitcode = "1.1"
|
||||
sysinfo = "0.33"
|
||||
sys-locale = "0.3"
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
Copyright (c) 2022 Alexander I. Chebykin <alex.chebykin@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
182
README.md
182
README.md
@@ -1,3 +1,183 @@
|
||||
# CAI-Watchdog
|
||||
|
||||
Watchdog for monitoring web-services
|
||||
Watchdog for monitoring web-services and running processes
|
||||
|
||||
## Requirements
|
||||
|
||||
### On \*nix
|
||||
- OpenSSL 1.0.1, 1.0.2, 1.1.0, or 1.1.1 with headers (see https://github.com/sfackler/rust-openssl)
|
||||
- Perl
|
||||
- Curl
|
||||
|
||||
### On Windows
|
||||
- PowerShell
|
||||
|
||||
## Configuration file
|
||||
|
||||
Default configuration file location:
|
||||
|
||||
**On \*nix:** /etc/cai-watchdog.conf
|
||||
|
||||
**On Windows:** current directory
|
||||
|
||||
You can specify config file as parameter: ```cai-watchdog /path/to/config/config_file.conf``` (*nix) or ```cai-watchdog.exe drive:\path\to\config\config_file.ini``` (Windows)
|
||||
|
||||
### Configuration file parameters
|
||||
|
||||
```
|
||||
[main]
|
||||
check_interval = Interval between checks in seconds
|
||||
rules_count = Rules count to be loaded from config. Rules sections must be enumerated continuously [rule1], [rule2] ... etc
|
||||
on_start_command = Command to be executed when watchdog started
|
||||
|
||||
[rule1]
|
||||
service = Service name
|
||||
uri = URI to be checked
|
||||
process = process to be checked
|
||||
email = E-mail address for notifications
|
||||
command = Command to send notification
|
||||
```
|
||||
|
||||
In commands You can use fields ```<email>```, ```<subject>```, ```<message>```, ```<service>```, ```<uri>```, ```<process>``` and ```<state>```
|
||||
|
||||
- ```<email>``` - E-mail address for notifications
|
||||
- ```<subject>``` - E-mail subject
|
||||
- ```<message>``` - Message text
|
||||
- ```<service>``` - Service name
|
||||
- ```<uri>``` - Service URI (for web services)
|
||||
- ```<process>``` - Process name (for OS tasks)
|
||||
- ```<state>``` - Service states are ```online``` and ```offline```, process states are ```running``` and ```stopped```
|
||||
### Scripts configurations
|
||||
|
||||
#### *nix
|
||||
|
||||
##### send-mail
|
||||
|
||||
**Usage:** ```send-mail recipient 'subject' 'message'```
|
||||
|
||||
Configuration file ```/etc/cai-watchdog/email.conf```
|
||||
|
||||
- ```Username:``` - Set e-mail user name here
|
||||
- ```Password:``` - Set e-mail password here
|
||||
- ```SMTP Server:``` - Set SMTP server address here
|
||||
- ```Port:``` - Set SMTP port here
|
||||
|
||||
##### send-mail-[locale]
|
||||
|
||||
**Usage:** ```send-mail-[locale] recipient 'service' 'process-or-uri' 'state'```
|
||||
|
||||
Configuration file ```/etc/cai-watchdog/email.conf```
|
||||
|
||||
- ```Username:``` - Set e-mail user name here
|
||||
- ```Password:``` - Set e-mail password here
|
||||
- ```SMTP Server:``` - Set SMTP server address here
|
||||
- ```Port:``` - Set SMTP port here
|
||||
|
||||
##### send-telegram
|
||||
|
||||
**Usage:** ```send-telegram 'message'```
|
||||
|
||||
Configuration file ```/etc/cai-watchdog/telegram.conf```
|
||||
|
||||
- ```Group ID:``` - Set Telegram group ID here
|
||||
- ```Bot token:``` - Set Telegram token here
|
||||
|
||||
##### send-telegram-[locale]
|
||||
|
||||
**Usage:** ```send-telegram-[locale] 'service', 'process-or-uri', 'state'```
|
||||
|
||||
Configuration file ```/etc/cai-watchdog/telegram.conf```
|
||||
|
||||
- ```Group ID:``` - Set Telegram group ID here
|
||||
- ```Bot token:``` - Set Telegram token here
|
||||
|
||||
#### Windows
|
||||
|
||||
##### send-mail.ps1
|
||||
|
||||
**Usage:** ```send-mail.ps1 recipient 'subject' 'message'```
|
||||
|
||||
Next lines needs to be configured:
|
||||
|
||||
- ```$EmailFrom = "yourmailadress@somedomain.com"``` - Set sender e-mail address here
|
||||
- ```$SMTPServer = "smtp.somedomain.com"``` - Set SMTP-server address here
|
||||
- ```$SMTPClient.EnableSsl = $true``` - Set SSL flag here
|
||||
- ```$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("usr", "pass");``` - Set user ("usr") and password ("pass") here
|
||||
|
||||
##### send-telegram.ps1
|
||||
|
||||
**Usage:** ```send-telegram.ps1 'message'```
|
||||
|
||||
- ```$Telegramtoken = "Your_Telegram_Token"``` - Set Telegram token here
|
||||
- ```$Telegramchatid = "Your_Telegram_Chat_ID"``` - Set Telegram chat ID here
|
||||
|
||||
### How to get Telegram token and chat ID
|
||||
|
||||
1. Open ```@BotFather``` bot
|
||||
1. Run ```/newbot``` command and give name to your new bot
|
||||
1. Enter a username for the bot
|
||||
1. Take note of the API token. We will need this later. **Note:** it is case sensitive
|
||||
1. Click the link to open a chat with the newly created bot
|
||||
|
||||
Next you need to find your Telegram Chat ID.
|
||||
|
||||
1. From the Telegram home screen, search for ```chatid_echo_bot```. Click Chat ID Echo to open a chat
|
||||
1. Enter ```/start``` to get the bot to send you your Telegram Chat ID
|
||||
1. Take note of the Telegram Chat ID returned
|
||||
|
||||
### Service configuration (*nix)
|
||||
|
||||
If you want to get messages when watchdog service is stopped, uncomment following line: ```ExecStopPost=/etc/cai-watchdog/on-stop```
|
||||
|
||||
## User logins monitoring (*nix)
|
||||
|
||||
Watchdog can send notifications on user login. Just add to ```/etc/profile.d/sshinfo.sh``` next lines:
|
||||
|
||||
- For Telegram:
|
||||
```
|
||||
User=$(whoami)
|
||||
IP=$(echo $SSH_CONNECTION | awk '{ print $1 == "" ? "127.0.0.1" : $1 }')
|
||||
CAI_WATCHDOG_PATH=/etc/cai-watchdog
|
||||
source ${CAI_WATCHDOG_PATH}/inc-icons
|
||||
${CAI_WATCHDOG_PATH}/send-telegram "${ICON_INFO} SSH: User ${User} is logged in from ${IP}"
|
||||
```
|
||||
- For e-mail:
|
||||
```
|
||||
User=$(whoami)
|
||||
IP=$(echo $SSH_CONNECTION | awk '{ print $1 == "" ? "127.0.0.1" : $1 }')
|
||||
CAI_WATCHDOG_PATH=/etc/cai-watchdog
|
||||
source ${CAI_WATCHDOG_PATH}/inc-icons
|
||||
${CAI_WATCHDOG_PATH}/send-mail your@mail.addr '${ICON_INFO} SSH: User ${User} is logged in' '${ICON_INFO} SSH: User ${User} is logged in from ${IP}'
|
||||
```
|
||||
|
||||
## User logouts monitoring (*nix)
|
||||
|
||||
1. Create file ```/etc/pam.d/pam_session.sh``` with next content:
|
||||
|
||||
For Telegram:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
CAI_WATCHDOG_PATH=/etc/cai-watchdog
|
||||
if [ "$PAM_TYPE" = "close_session" ]; then
|
||||
${CAI_WATCHDOG_PATH}/send-telegram "SSH: User ${PAM_USER} is logged out"
|
||||
fi
|
||||
```
|
||||
|
||||
For e-mail:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
CAI_WATCHDOG_PATH=/etc/cai-watchdog
|
||||
if [ "$PAM_TYPE" = "close_session" ]; then
|
||||
${CAI_WATCHDOG_PATH}/send-mail your@mail.addr 'SSH: User ${PAM_USER} is logged out' 'SSH: User is logged out'
|
||||
fi
|
||||
```
|
||||
and set executable flag on it
|
||||
|
||||
1. Modify ```/etc/pam.d/sshd```, add line ```session optional pam_exec.so quiet /etc/pam.d/pam_session.sh```
|
||||
|
||||
## Localization
|
||||
You can find locales in ```/etc/cai-watchdog/locales/``` in *nix and in ```locales``` subfolder in Windows.
|
||||
|
||||
locale files must be named like locale_name.conf, for example: Russian locale is ru-RU.
|
||||
|
||||
25
scripts/unix/etc/cai-watchdog/cai-watchdog.conf.template
Normal file
25
scripts/unix/etc/cai-watchdog/cai-watchdog.conf.template
Normal file
@@ -0,0 +1,25 @@
|
||||
[main]
|
||||
check_interval = 10
|
||||
rules_count = 3
|
||||
on_start_command = /etc/cai-watchdog/on-start
|
||||
|
||||
[rule1]
|
||||
service = Test1
|
||||
uri = http://127.0.0.1:3000/api/v1/
|
||||
process =
|
||||
email = admin@server.local
|
||||
command = /etc/cai-watchdog/send-telegram <message>
|
||||
|
||||
[rule2]
|
||||
service = Test2
|
||||
uri = http://127.0.0.1:3300/api/v1/
|
||||
process =
|
||||
email = admin@server.local
|
||||
command = /etc/cai-watchdog/send-telegram <message>
|
||||
|
||||
[rule3]
|
||||
service = Apache
|
||||
uri =
|
||||
process = httpd
|
||||
email = admin@server.local
|
||||
command = /etc/cai-watchdog/send-telegram <message>
|
||||
4
scripts/unix/etc/cai-watchdog/email.conf.template
Normal file
4
scripts/unix/etc/cai-watchdog/email.conf.template
Normal file
@@ -0,0 +1,4 @@
|
||||
Username: user@domain.local
|
||||
Password: str0ngP@$$word
|
||||
SMTP Server: smtp.domain.local
|
||||
Port: 465
|
||||
5
scripts/unix/etc/cai-watchdog/inc-icons
Normal file
5
scripts/unix/etc/cai-watchdog/inc-icons
Normal file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
ICON_INFO=$(echo -e "\U2139")
|
||||
ICON_WARNING=$(echo -e "\U26A0")
|
||||
ICON_OK=$(echo -e "\U2705")
|
||||
ICON_FAIL=$(echo -e "\U274C")
|
||||
26
scripts/unix/etc/cai-watchdog/locales/ru-RU.conf
Normal file
26
scripts/unix/etc/cai-watchdog/locales/ru-RU.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
[rules]
|
||||
cant_find_config_file = Не могу найти файл конфигурации
|
||||
check_failed = Проверка завершилась неудачей
|
||||
check_is_ok = проверка завершилась успешно
|
||||
configuration_file = Конфигурационный файл
|
||||
error = Ошибка
|
||||
execute = Выполняю
|
||||
exiting = Завершаю работу...
|
||||
failed_to_execute_process = Не удалось выполнить процесс
|
||||
is_not_running = не запущен
|
||||
is_not_running_now = сейчас не запущен
|
||||
is_offline = неактивна
|
||||
is_offline_now = сейчас неактивна
|
||||
is_online = активна
|
||||
is_online_now = сейчас активна
|
||||
is_running = запущен
|
||||
is_running_now = сейчас запущен
|
||||
process = Процесс
|
||||
service = Служба
|
||||
service_started = Служба запущена
|
||||
state_changed_to_false = состояние изменилось на истину
|
||||
state_changed_to_true = состояние изменилось на ложь
|
||||
this_help_message = Это справочное сообщение
|
||||
usage = Использование
|
||||
ver = версия
|
||||
version_info = Информация о версии
|
||||
29
scripts/unix/etc/cai-watchdog/on-process-state-change
Normal file
29
scripts/unix/etc/cai-watchdog/on-process-state-change
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
##### DEFAULT PARAMS ORDER #####
|
||||
## $1 - <email>
|
||||
## $2 - <subject>
|
||||
## $3 - <message>
|
||||
## $4 - <service>
|
||||
## $5 - <process>
|
||||
## $6 - <status> [running | stopped]
|
||||
|
||||
EMAIL=$1
|
||||
SUBJECT=$2
|
||||
MESSAGE=$3
|
||||
SERVICE=$4
|
||||
PROCESS=$5
|
||||
STATUS=$6
|
||||
|
||||
## SAMPLE COMMANDS
|
||||
#${APP_PATH}/send-mail "${EMAIL}", "${SUBJECT}", "$MESSAGE"
|
||||
#${APP_PATH}/send-telegram-ru "${SERVICE}", "${PROCESS}", "$STATUS"
|
||||
|
||||
${APP_PATH}/send-telegram "$MESSAGE"
|
||||
11
scripts/unix/etc/cai-watchdog/on-start
Normal file
11
scripts/unix/etc/cai-watchdog/on-start
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
${APP_PATH}/send-telegram "${ICON_INFO} Watchdog is started!"
|
||||
11
scripts/unix/etc/cai-watchdog/on-start-ru
Normal file
11
scripts/unix/etc/cai-watchdog/on-start-ru
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
${APP_PATH}/send-telegram "${ICON_INFO} Watchdog запущен!"
|
||||
11
scripts/unix/etc/cai-watchdog/on-stop
Normal file
11
scripts/unix/etc/cai-watchdog/on-stop
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
${APP_PATH}/send-telegram "${ICON_WARNING} ATTENTION! Watchdog is stopped!"
|
||||
11
scripts/unix/etc/cai-watchdog/on-stop-ru
Normal file
11
scripts/unix/etc/cai-watchdog/on-stop-ru
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
${APP_PATH}/send-telegram "${ICON_WARNING} ВНИМАНИЕ! Watchdog остановлен!"
|
||||
29
scripts/unix/etc/cai-watchdog/on-uri-state-change
Normal file
29
scripts/unix/etc/cai-watchdog/on-uri-state-change
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
##### DEFAULT PARAMS ORDER #####
|
||||
## $1 - <email>
|
||||
## $2 - <subject>
|
||||
## $3 - <message>
|
||||
## $4 - <service>
|
||||
## $5 - <uri>
|
||||
## $6 - <status> [online | offline]
|
||||
|
||||
EMAIL=$1
|
||||
SUBJECT=$2
|
||||
MESSAGE=$3
|
||||
SERVICE=$4
|
||||
URI=$5
|
||||
STATUS=$6
|
||||
|
||||
## SAMPLE COMMANDS
|
||||
#${APP_PATH}/send-mail "${EMAIL}", "${SUBJECT}", "$MESSAGE"
|
||||
#${APP_PATH}/send-telegram-ru "${SERVICE}", "${URI}", "$STATUS"
|
||||
|
||||
${APP_PATH}/send-telegram "$MESSAGE"
|
||||
49
scripts/unix/etc/cai-watchdog/send-mail
Normal file
49
scripts/unix/etc/cai-watchdog/send-mail
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
rcpt=$1
|
||||
subj=$2
|
||||
message=$3
|
||||
|
||||
ARGS=$(xargs echo $(perl -anle 's/^[^:]+//g && s/:\s+//g && print' ${APP_PATH}/email.conf) < /dev/null)
|
||||
set -- $ARGS "$@";
|
||||
|
||||
declare -A email;
|
||||
email['user']=$1
|
||||
email['pass']=$2
|
||||
email['smtp']=$3
|
||||
email['port']=$4
|
||||
|
||||
email_content='From: "Watchdog" <'"${email['user']}"'>
|
||||
To: "Subscriber" <'"${rcpt}"'>
|
||||
Subject: '"${subj}"'
|
||||
Date: '"$(date)"'
|
||||
|
||||
'"${message}"'
|
||||
-----
|
||||
'"${HOSTNAME}"'
|
||||
';
|
||||
|
||||
|
||||
echo "$email_content" | curl -s \ # -vvv \
|
||||
--url "smtps://${email['smtp']}:${email['port']}" \
|
||||
--user "${email['user']}:${email['pass']}" \
|
||||
--mail-from "${email['user']}" \
|
||||
--mail-rcpt "${rcpt}" \
|
||||
--upload-file - \
|
||||
-T -
|
||||
|
||||
|
||||
if [[ $? == 0 ]]; then
|
||||
echo;
|
||||
echo 'okay';
|
||||
else
|
||||
echo "curl error code $?";
|
||||
man curl | grep "^ \+$? \+"
|
||||
fi
|
||||
81
scripts/unix/etc/cai-watchdog/send-mail-ru
Normal file
81
scripts/unix/etc/cai-watchdog/send-mail-ru
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
|
||||
RCPT=$1
|
||||
SERVICE=$2
|
||||
PROCESS_OR_URI=$3
|
||||
STATE=$4
|
||||
|
||||
case $4 in
|
||||
online)
|
||||
SERVICE_OR_PROCESS="Служба"
|
||||
STATE_STR="доступна"
|
||||
ICON=${ICON_OK}
|
||||
;;
|
||||
running)
|
||||
SERVICE_OR_PROCESS="Процесс"
|
||||
STATE_STR="запущен"
|
||||
ICON=${ICON_OK}
|
||||
;;
|
||||
offline)
|
||||
SERVICE_OR_PROCESS="Служба"
|
||||
STATE_STR="недоступна"
|
||||
ICON=${ICON_FAIL}
|
||||
;;
|
||||
stopped)
|
||||
SERVICE_OR_PROCESS="Процесс"
|
||||
STATE_STR="остановлен"
|
||||
ICON=${ICON_FAIL}
|
||||
;;
|
||||
esac
|
||||
|
||||
MESSAGE_TEXT="${ICON} ${SERVICE_OR_PROCESS} ${SERVICE} (${PROCESS_OR_URI}) ${STATE_STR}"
|
||||
|
||||
|
||||
#subj=$2
|
||||
#message=$3
|
||||
|
||||
ARGS=$(xargs echo $(perl -anle 's/^[^:]+//g && s/:\s+//g && print' ${APP_PATH}/email.conf) < /dev/null)
|
||||
set -- $ARGS "$@";
|
||||
|
||||
declare -A email;
|
||||
email['user']=$1
|
||||
email['pass']=$2
|
||||
email['smtp']=$3
|
||||
email['port']=$4
|
||||
|
||||
email_content='From: "Watchdog" <'"${email['user']}"'>
|
||||
To: "Subscriber" <'"${RCPT}"'>
|
||||
Subject: '"${MESSAGE_TEXT}"'
|
||||
Date: '"$(date)"'
|
||||
|
||||
'"${MESSAGE_TEXT}"'
|
||||
-----
|
||||
'"${HOSTNAME}"'
|
||||
';
|
||||
|
||||
|
||||
echo "$email_content" | curl -s \ # -vvv \
|
||||
--url "smtps://${email['smtp']}:${email['port']}" \
|
||||
--user "${email['user']}:${email['pass']}" \
|
||||
--mail-from "${email['user']}" \
|
||||
--mail-rcpt "${RCPT}" \
|
||||
--upload-file - \
|
||||
-T -
|
||||
|
||||
|
||||
if [[ $? == 0 ]]; then
|
||||
echo;
|
||||
echo 'okay';
|
||||
else
|
||||
echo "curl error code $?";
|
||||
man curl | grep "^ \+$? \+"
|
||||
fi
|
||||
38
scripts/unix/etc/cai-watchdog/send-telegram
Normal file
38
scripts/unix/etc/cai-watchdog/send-telegram
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
|
||||
SCRIPT_NAME=$0
|
||||
MESSAGE_TEXT=$1
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "You can pass only one argument. For string with spaces put it on quotes"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ARGS=$(xargs echo $(perl -anle 's/^[^:]+//g && s/:\s+//g && print' ${APP_PATH}/telegram.conf) < /dev/null)
|
||||
set -- $ARGS "$@";
|
||||
|
||||
GROUP_ID=$1
|
||||
BOT_TOKEN=$2
|
||||
|
||||
# this 3 checks (if) are not necessary but should be convenient
|
||||
if [ "${MESSAGE_TEXT}" == "-h" ]; then
|
||||
echo "Usage: `basename ${SCRIPT_NAME}` \"text message\""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${MESSAGE_TEXT}" ]
|
||||
then
|
||||
echo "Add message text as second arguments"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl -s --data "text=${MESSAGE_TEXT}" --data "chat_id=$GROUP_ID" 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage' > /dev/null
|
||||
65
scripts/unix/etc/cai-watchdog/send-telegram-ru
Normal file
65
scripts/unix/etc/cai-watchdog/send-telegram-ru
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
APP_PATH=$(dirname "$0") # relative
|
||||
APP_PATH=$(cd "$APP_PATH" && pwd) # absolutized and normalized
|
||||
if [[ -z "$APP_PATH" ]] ; then
|
||||
# error; for some reason, the path is not accessible
|
||||
# to the script (e.g. permissions re-evaled after suid)
|
||||
APP_PATH="/etc/cai-watchdog"
|
||||
fi
|
||||
|
||||
source ${APP_PATH}/inc-icons
|
||||
|
||||
SCRIPT_NAME=$0
|
||||
SERVICE=$1
|
||||
PROCESS_OR_URI=$2
|
||||
STATE=$3
|
||||
|
||||
case $3 in
|
||||
online)
|
||||
SERVICE_OR_PROCESS="Служба"
|
||||
STATE_STR="доступна"
|
||||
ICON=${ICON_OK}
|
||||
;;
|
||||
running)
|
||||
SERVICE_OR_PROCESS="Процесс"
|
||||
STATE_STR="запущен"
|
||||
ICON=${ICON_OK}
|
||||
;;
|
||||
offline)
|
||||
SERVICE_OR_PROCESS="Служба"
|
||||
STATE_STR="недоступна"
|
||||
ICON=${ICON_FAIL}
|
||||
;;
|
||||
stopped)
|
||||
SERVICE_OR_PROCESS="Процесс"
|
||||
STATE_STR="остановлен"
|
||||
ICON=${ICON_FAIL}
|
||||
;;
|
||||
esac
|
||||
|
||||
MESSAGE_TEXT="${ICON} ${SERVICE_OR_PROCESS} ${SERVICE} (${PROCESS_OR_URI}) ${STATE_STR}"
|
||||
|
||||
if [ "$#" -ne 3 ]; then
|
||||
echo "Вы должны передать 3 аргумента - название службы, uri или название процесса и статус. Аргументы с пробелами требуется взять в кавычки"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ARGS=$(xargs echo $(perl -anle 's/^[^:]+//g && s/:\s+//g && print' ${APP_PATH}/telegram.conf) < /dev/null)
|
||||
set -- $ARGS "$@";
|
||||
|
||||
GROUP_ID=$1
|
||||
BOT_TOKEN=$2
|
||||
|
||||
# this 3 checks (if) are not necessary but should be convenient
|
||||
if [ "${MESSAGE_TEXT}" == "-h" ]; then
|
||||
echo "Usage: `basename ${SCRIPT_NAME}` \"text message\""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${MESSAGE_TEXT}" ]
|
||||
then
|
||||
echo "Add message text as second arguments"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl -s --data "text=${MESSAGE_TEXT}" --data "chat_id=$GROUP_ID" 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage' > /dev/null
|
||||
2
scripts/unix/etc/cai-watchdog/telegram.conf.template
Normal file
2
scripts/unix/etc/cai-watchdog/telegram.conf.template
Normal file
@@ -0,0 +1,2 @@
|
||||
Group ID: Telegram_Group_ID
|
||||
Bot token: Telegram_Bot_Token
|
||||
17
scripts/unix/usr/lib/systemd/system/cai-watchdog.service
Normal file
17
scripts/unix/usr/lib/systemd/system/cai-watchdog.service
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Watchdog service
|
||||
|
||||
[Service]
|
||||
User=wwwrun
|
||||
Group=www
|
||||
Type=simple
|
||||
ExecStart=/usr/local/sbin/cai-watchdog
|
||||
ExecStopPost=/etc/cai-watchdog/on-stop
|
||||
|
||||
KillMode=control-group
|
||||
NotifyAccess=all
|
||||
SuccessExitStatus=2
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Alias=cai_watchdog
|
||||
25
scripts/windows/cai-watchdog.ini.template
Normal file
25
scripts/windows/cai-watchdog.ini.template
Normal file
@@ -0,0 +1,25 @@
|
||||
[main]
|
||||
check_interval = 10
|
||||
rules_count = 3
|
||||
on_start_command = ./on-start.ps1
|
||||
|
||||
[rule1]
|
||||
service = Test1
|
||||
uri = http://127.0.0.1:3000/api/v1/
|
||||
process =
|
||||
email = admin@server.local
|
||||
command = ./send-telegram.ps1 <message>
|
||||
|
||||
[rule2]
|
||||
service = Test2
|
||||
uri = http://127.0.0.1:3300/api/v1/
|
||||
process =
|
||||
email = admin@server.local
|
||||
command = ./send-telegram.ps1 <message>
|
||||
|
||||
[rule3]
|
||||
service = Apache
|
||||
uri =
|
||||
process = httpd.exe
|
||||
email = admin@server.local
|
||||
command = ./send-telegram.ps1 <message>
|
||||
26
scripts/windows/locales/ru-RU.conf
Normal file
26
scripts/windows/locales/ru-RU.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
[rules]
|
||||
cant_find_config_file = Не могу найти файл конфигурации
|
||||
check_failed = Проверка завершилась неудачей
|
||||
check_is_ok = проверка завершилась успешно
|
||||
configuration_file = Конфигурационный файл
|
||||
error = Ошибка
|
||||
execute = Выполняю
|
||||
exiting = Завершаю работу...
|
||||
failed_to_execute_process = Не удалось выполнить процесс
|
||||
is_not_running = не запущен
|
||||
is_not_running_now = сейчас не запущен
|
||||
is_offline = неактивна
|
||||
is_offline_now = сейчас неактивна
|
||||
is_online = активна
|
||||
is_online_now = сейчас активна
|
||||
is_running = запущен
|
||||
is_running_now = сейчас запущен
|
||||
process = Процесс
|
||||
service = Служба
|
||||
service_started = Служба запущена
|
||||
state_changed_to_false = состояние изменилось на истину
|
||||
state_changed_to_true = состояние изменилось на ложь
|
||||
this_help_message = Это справочное сообщение
|
||||
usage = Использование
|
||||
ver = версия
|
||||
version_info = Информация о версии
|
||||
9
scripts/windows/send-mail.ps1.template
Normal file
9
scripts/windows/send-mail.ps1.template
Normal file
@@ -0,0 +1,9 @@
|
||||
$EmailFrom = "yourmailadress@somedomain.com"
|
||||
$EmailTo = $args[0]
|
||||
$Subject = $args[1]
|
||||
$Body = $args[0]
|
||||
$SMTPServer = "smtp.somedomain.com"
|
||||
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
|
||||
$SMTPClient.EnableSsl = $true
|
||||
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("usr", "pass");
|
||||
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
|
||||
8
scripts/windows/send-telegram.ps1.template
Normal file
8
scripts/windows/send-telegram.ps1.template
Normal file
@@ -0,0 +1,8 @@
|
||||
$Telegramtoken = "Your_Telegram_Token"
|
||||
$Telegramchatid = "Your_Telegram_Chat_ID"
|
||||
|
||||
$Message = $args[0]
|
||||
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
$Response = Invoke-RestMethod -Uri "https://api.telegram.org/bot$($Telegramtoken)/sendMessage?chat_id=$($Telegramchatid)&text=$($Message)"
|
||||
291
src/main.rs
Normal file
291
src/main.rs
Normal file
@@ -0,0 +1,291 @@
|
||||
//! CAI-Watchdog is a lightweigth watchdog for *nix and Windows
|
||||
//! Author: Alexander I. Chebykin (CAI) <alex.chebykin@gmail.com>
|
||||
|
||||
#[macro_use]
|
||||
extern crate ini;
|
||||
extern crate exitcode;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
mod mods;
|
||||
|
||||
use crate::mods::mod_debug::*;
|
||||
use crate::mods::mod_fs::*;
|
||||
use crate::mods::mod_i18n::*;
|
||||
use crate::mods::mod_os::*;
|
||||
use crate::mods::mod_tasks::*;
|
||||
|
||||
/// Check rule
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rule` Rule to be checked
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// if check(&tasks[i]).await {
|
||||
/// println!("ok");
|
||||
/// } else {
|
||||
/// println!("uh-oh... something wrong!");
|
||||
/// }
|
||||
/// ```
|
||||
async fn check(rule: &Rule) -> bool {
|
||||
return if rule.uri.to_string() != "".to_string() {
|
||||
check_uri(rule.uri.clone()).await
|
||||
} else {
|
||||
check_process(rule.process.clone())
|
||||
};
|
||||
}
|
||||
|
||||
/// Checks web-service availability
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `uri` - Service URI to be checked
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// if check_uri("https://somesite.local/api/v1/".to_string()).await {
|
||||
/// println!("ok");
|
||||
/// } else {
|
||||
/// println!("uh-oh... something wrong!");
|
||||
/// }
|
||||
/// ```
|
||||
async fn check_uri(uri: String) -> bool {
|
||||
if let Err(_e) = reqwest::get(uri).await {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Print help and program version information
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `args` - Command-line arguments
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let args: Vec<String> = env::args().collect();
|
||||
///
|
||||
/// print_help(args.clone());
|
||||
/// ```
|
||||
fn print_help(args: Vec<String>) {
|
||||
let locale = Locale::new();
|
||||
|
||||
if cfg!(windows) {
|
||||
if args.len() > 1 && (args[1].to_string() == "/help".to_string() || args[1].to_string() == "/?".to_string()) {
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
println!("CAI Watchdog {} {}", locale.t().ver, VERSION);
|
||||
println!("");
|
||||
println!("Usage: {} [/? | /help | /v | /ver | config_file]", &args[0]);
|
||||
println!(" /? | /help : {}", locale.t().this_help_message);
|
||||
println!(" /v | /ver : {}", locale.t().version_info);
|
||||
println!(" config_file : {}", locale.t().configuration_file);
|
||||
|
||||
std::process::exit(exitcode::OK);
|
||||
} else if args.len() > 1
|
||||
&& (args[1].to_string() == "/ver".to_string() || args[1].to_string() == "/v".to_string()) {
|
||||
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
println!("CAI Watchdog {} {}", locale.t().ver, VERSION);
|
||||
|
||||
std::process::exit(exitcode::OK);
|
||||
}
|
||||
} else {
|
||||
if args.len() > 1 && (args[1].to_string() == "--help".to_string() || args[1].to_string() == "-h".to_string()) {
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
println!("CAI Watchdog {} {}", locale.t().ver, VERSION);
|
||||
println!("");
|
||||
println!("{}: {} [-h | --help | -v | --ver | config_file]", locale.t().usage, &args[0]);
|
||||
println!(" -h | --help : {}", locale.t().this_help_message);
|
||||
println!(" -v | --ver : {}", locale.t().version_info);
|
||||
println!(" config_file : {}", locale.t().configuration_file);
|
||||
|
||||
std::process::exit(exitcode::OK);
|
||||
} else if args.len() > 1
|
||||
&& (args[1].to_string() == "--ver".to_string() || args[1].to_string() == "-v".to_string()) {
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
println!("CAI Watchdog {} {}", locale.t().ver, VERSION);
|
||||
|
||||
std::process::exit(exitcode::OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let locale = Locale::new();
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
print_help(args.clone());
|
||||
|
||||
let mut config_file = get_exe_path();
|
||||
|
||||
if cfg!(windows) {
|
||||
config_file = format!("{}\\cai-watchdog.ini", config_file);
|
||||
} else {
|
||||
config_file = "/etc/cai-watchdog/cai-watchdog.conf".to_string();
|
||||
}
|
||||
|
||||
let cfg_file = if args.len() > 1 {
|
||||
&args[1]
|
||||
} else {
|
||||
&config_file
|
||||
};
|
||||
|
||||
if !Path::new(cfg_file).exists() {
|
||||
println!("\u{26a0} {}! {}.",
|
||||
locale.t().error,
|
||||
locale.t().cant_find_config_file
|
||||
);
|
||||
println!("{}", locale.t().exiting);
|
||||
|
||||
std::process::exit(exitcode::CONFIG);
|
||||
}
|
||||
|
||||
let cfg = ini!(cfg_file);
|
||||
|
||||
let check_interval = if cfg["main"].contains_key("check_interval") {
|
||||
cfg["main"]["check_interval"].clone().unwrap().parse::<u64>().unwrap() * 1000
|
||||
} else {
|
||||
10000
|
||||
};
|
||||
|
||||
let rules_count = if cfg["main"].contains_key("check_interval") {
|
||||
cfg["main"]["rules_count"].clone().unwrap().parse::<u8>().unwrap()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut tasks_prc = vec![];
|
||||
let mut tasks_web = vec![];
|
||||
|
||||
for i in 1..rules_count + 1 {
|
||||
let service = if cfg[&format!("rule{}", i)].contains_key("service") {
|
||||
cfg[&format!("rule{}", i)]["service"].clone().unwrap()
|
||||
} else {
|
||||
format!("Service {}", i)
|
||||
};
|
||||
|
||||
let uri = if cfg[&format!("rule{}", i)].contains_key("uri") {
|
||||
cfg[&format!("rule{}", i)]["uri"].clone().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let process = if cfg[&format!("rule{}", i)].contains_key("process") {
|
||||
cfg[&format!("rule{}", i)]["process"].clone().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let email = if cfg[&format!("rule{}", i)].contains_key("email") {
|
||||
cfg[&format!("rule{}", i)]["email"].clone().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let command = if cfg[&format!("rule{}", i)].contains_key("command") {
|
||||
cfg[&format!("rule{}", i)]["command"].clone().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
debug_log(format!("rule {}", i));
|
||||
debug_log(format!("service {}", service));
|
||||
debug_log(format!("uri {}", uri));
|
||||
debug_log(format!("process {}", process));
|
||||
debug_log(format!("email {}", email));
|
||||
debug_log(format!("command {}", command));
|
||||
|
||||
if uri != "".to_string() {
|
||||
tasks_web.push(
|
||||
Rule{
|
||||
service: service,
|
||||
uri: uri,
|
||||
process: process,
|
||||
email: email,
|
||||
command: command,
|
||||
last_state: false,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
tasks_prc.push(
|
||||
Rule{
|
||||
service: service,
|
||||
uri: uri,
|
||||
process: process,
|
||||
email: email,
|
||||
command: command,
|
||||
last_state: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let on_start_command = if cfg["main"].contains_key("on_start_command") {
|
||||
cfg["main"]["on_start_command"].clone().unwrap()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
debug_log(format!("\u{2139} {}", locale.t().service_started));
|
||||
|
||||
if on_start_command.to_string() != "" {
|
||||
execute(on_start_command);
|
||||
}
|
||||
|
||||
let mut just_started_web = true;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
for i in 0..tasks_web.len() {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
rt.block_on(async {
|
||||
if check(&tasks_web[i]).await {
|
||||
execute_web_task(&mut tasks_web[i], true, just_started_web);
|
||||
} else {
|
||||
execute_web_task(&mut tasks_web[i], false, just_started_web);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
just_started_web = false;
|
||||
|
||||
thread::sleep(Duration::from_millis(check_interval));
|
||||
}
|
||||
});
|
||||
|
||||
let mut just_started_prc = true;
|
||||
|
||||
loop {
|
||||
for i in 0..tasks_prc.len() {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
rt.block_on(async {
|
||||
if check(&tasks_prc[i]).await {
|
||||
execute_os_task(&mut tasks_prc[i], true, just_started_prc);
|
||||
} else {
|
||||
execute_os_task(&mut tasks_prc[i], false, just_started_prc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
just_started_prc = false;
|
||||
|
||||
thread::sleep(Duration::from_millis(check_interval));
|
||||
}
|
||||
}
|
||||
5
src/mods/mod.rs
Normal file
5
src/mods/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod mod_debug;
|
||||
pub mod mod_fs;
|
||||
pub mod mod_i18n;
|
||||
pub mod mod_os;
|
||||
pub mod mod_tasks;
|
||||
16
src/mods/mod_debug.rs
Normal file
16
src/mods/mod_debug.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
/// Output to console only in debug version
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `text` - Message text
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// debug_log("Debug version started".to_string());
|
||||
/// ```
|
||||
pub fn debug_log(text: String) {
|
||||
if cfg!(debug_assertions) {
|
||||
println!("{}", text);
|
||||
}
|
||||
}
|
||||
26
src/mods/mod_fs.rs
Normal file
26
src/mods/mod_fs.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
//! CAI-Watchdog filesystem module
|
||||
//! Author: Alexander I. Chebykin (CAI) <alex.chebykin@gmail.com>
|
||||
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Return executable path
|
||||
///
|
||||
/// Return executable path or . if can't determine it
|
||||
pub fn get_exe_path() -> String {
|
||||
let mut dir;
|
||||
let exe_path: String;
|
||||
|
||||
match env::current_exe() {
|
||||
Ok(full_name) => {
|
||||
dir = PathBuf::from(full_name);
|
||||
dir.pop();
|
||||
exe_path = dir.clone().into_os_string().into_string().unwrap();
|
||||
},
|
||||
Err(_e) => {
|
||||
exe_path = ".".to_string();
|
||||
},
|
||||
}
|
||||
|
||||
return exe_path;
|
||||
}
|
||||
185
src/mods/mod_i18n.rs
Normal file
185
src/mods/mod_i18n.rs
Normal file
@@ -0,0 +1,185 @@
|
||||
//! CAI-Watchdog localization module
|
||||
//! Author: Alexander I. Chebykin (CAI) <alex.chebykin@gmail.com>
|
||||
//!
|
||||
//! External localizations stored in /locales/ directory
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::mods::mod_fs;
|
||||
use sys_locale::get_locale;
|
||||
|
||||
pub struct Lang {
|
||||
pub cant_find_config_file: String,
|
||||
pub check_failed: String,
|
||||
pub check_is_ok: String,
|
||||
pub configuration_file: String,
|
||||
pub error: String,
|
||||
pub execute: String,
|
||||
pub exiting: String,
|
||||
pub failed_to_execute_process: String,
|
||||
pub is_not_running: String,
|
||||
pub is_not_running_now: String,
|
||||
pub is_offline: String,
|
||||
pub is_offline_now: String,
|
||||
pub is_online: String,
|
||||
pub is_online_now: String,
|
||||
pub is_running: String,
|
||||
pub is_running_now: String,
|
||||
pub process: String,
|
||||
pub service: String,
|
||||
pub service_started: String,
|
||||
pub state_changed_to_false: String,
|
||||
pub state_changed_to_true: String,
|
||||
pub this_help_message: String,
|
||||
pub usage: String,
|
||||
pub ver: String,
|
||||
pub version_info: String
|
||||
|
||||
}
|
||||
|
||||
pub struct Locale {
|
||||
pub locale:Lang,
|
||||
}
|
||||
|
||||
impl Locale {
|
||||
pub fn new() -> Self {
|
||||
let mut l:Lang = Lang{
|
||||
cant_find_config_file: "Can't find configuration file".to_string(),
|
||||
check_failed: "check failed".to_string(),
|
||||
check_is_ok: "check is ok".to_string(),
|
||||
configuration_file: "Configuration file".to_string(),
|
||||
error: "Error".to_string(),
|
||||
execute: "Execute".to_string(),
|
||||
exiting: "Exiting...".to_string(),
|
||||
failed_to_execute_process: "Can't execute process".to_string(),
|
||||
is_not_running: "is not running".to_string(),
|
||||
is_not_running_now: "is not running now".to_string(),
|
||||
is_offline: "is offline".to_string(),
|
||||
is_offline_now: "is offline now".to_string(),
|
||||
is_online: "is online".to_string(),
|
||||
is_online_now: "is online now".to_string(),
|
||||
is_running: "is running".to_string(),
|
||||
is_running_now: "is running now".to_string(),
|
||||
process: "Process".to_string(),
|
||||
service: "Service".to_string(),
|
||||
service_started: "Service started".to_string(),
|
||||
state_changed_to_false: "state changed to false".to_string(),
|
||||
state_changed_to_true: "state changed to true".to_string(),
|
||||
this_help_message: "This help message".to_string(),
|
||||
usage: "Usage".to_string(),
|
||||
ver: "ver".to_string(),
|
||||
version_info: "Version info".to_string()
|
||||
};
|
||||
|
||||
let locale = get_locale().unwrap_or_else(|| String::from("en-US"));
|
||||
|
||||
let mut locale_file = mod_fs::get_exe_path();
|
||||
|
||||
locale_file = if cfg!(windows) {
|
||||
format!("{}\\locales\\{}.conf", locale_file, locale)
|
||||
} else {
|
||||
format!("/etc/cai-watchdog/locales/{}.conf", locale)
|
||||
};
|
||||
|
||||
if Path::new(&locale_file).exists() {
|
||||
let cfg = ini!(&locale_file);
|
||||
|
||||
if cfg["rules"].contains_key("cant_find_config_file") {
|
||||
l.cant_find_config_file = cfg["rules"]["cant_find_config_file"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("check_failed") {
|
||||
l.check_failed = cfg["rules"]["check_failed"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("check_is_ok") {
|
||||
l.check_is_ok = cfg["rules"]["check_is_ok"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("configuration_file") {
|
||||
l.configuration_file = cfg["rules"]["configuration_file"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("error") {
|
||||
l.error = cfg["rules"]["error"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("execute") {
|
||||
l.execute = cfg["rules"]["execute"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("exiting") {
|
||||
l.exiting = cfg["rules"]["exiting"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("failed_to_execute_process") {
|
||||
l.failed_to_execute_process = cfg["rules"]["failed_to_execute_process"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_not_running") {
|
||||
l.is_not_running = cfg["rules"]["is_not_running"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_not_running_now") {
|
||||
l.is_not_running_now = cfg["rules"]["is_not_running_now"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_offline") {
|
||||
l.is_offline = cfg["rules"]["is_offline"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_offline_now") {
|
||||
l.is_offline_now = cfg["rules"]["is_offline_now"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_online") {
|
||||
l.is_online = cfg["rules"]["is_online"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_online_now") {
|
||||
l.is_online_now = cfg["rules"]["is_online_now"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_running") {
|
||||
l.is_running = cfg["rules"]["is_running"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("is_running_now") {
|
||||
l.is_running_now = cfg["rules"]["is_running_now"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("process") {
|
||||
l.process = cfg["rules"]["process"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("service") {
|
||||
l.service = cfg["rules"]["service"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("service_started") {
|
||||
l.service_started = cfg["rules"]["service_started"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("state_changed_to_false") {
|
||||
l.state_changed_to_false = cfg["rules"]["state_changed_to_false"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("state_changed_to_true") {
|
||||
l.state_changed_to_true = cfg["rules"]["state_changed_to_true"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("this_help_message") {
|
||||
l.this_help_message = cfg["rules"]["this_help_message"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("usage") {
|
||||
l.usage = cfg["rules"]["usage"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("ver") {
|
||||
l.ver = cfg["rules"]["ver"].clone().unwrap();
|
||||
}
|
||||
if cfg["rules"].contains_key("version_info") {
|
||||
l.version_info = cfg["rules"]["version_info"].clone().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
locale: l
|
||||
}
|
||||
}
|
||||
|
||||
/// Return object with locale data
|
||||
///
|
||||
/// If there is no localization file for current system locale, fallback (English) locale will be returned
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let locale = mod_locales::Locale::new();
|
||||
///
|
||||
/// println!("\u{26a0} {}! {}.", locale.t().error, locale.t().cant_find_config_file);
|
||||
/// println!("{}", locale.t().exiting);
|
||||
/// ```
|
||||
pub fn t(&self) -> &Lang {
|
||||
return self.locale.borrow();
|
||||
}
|
||||
}
|
||||
70
src/mods/mod_os.rs
Normal file
70
src/mods/mod_os.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::process::Command;
|
||||
use sysinfo::System;
|
||||
|
||||
use crate::mods::mod_debug::*;
|
||||
use crate::mods::mod_i18n::*;
|
||||
|
||||
/// Check is process running
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `process_name` Process name to be checked
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// if check_process("httpd".to_string()).await {
|
||||
/// println!("ok");
|
||||
/// } else {
|
||||
/// println!("uh-oh... something wrong!");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn check_process(process_name: String) -> bool {
|
||||
let mut sys = System::new_all();
|
||||
let mut result: bool = false;
|
||||
|
||||
sys.refresh_all();
|
||||
|
||||
for _process in sys.processes_by_exact_name(OsStr::new(&process_name)) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Execute shell command
|
||||
///
|
||||
/// In linux sh command interpreter will be called.
|
||||
/// In Windows PowerShell command interpreter will be called.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `command` - Command to be executed
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// execute("gedit".to_string());
|
||||
/// ```
|
||||
pub fn execute(command: String) {
|
||||
let locale = Locale::new();
|
||||
|
||||
debug_log(format!("{} {}", locale.t().execute, command));
|
||||
|
||||
let output = if cfg!(target_os = "windows") {
|
||||
Command::new("powershell")
|
||||
.args(["-NoLogo", "-NonInteractive", "-Command", &command])
|
||||
.output()
|
||||
.expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
|
||||
} else {
|
||||
Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&command)
|
||||
.output()
|
||||
.expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
|
||||
};
|
||||
|
||||
let result = output.stdout;
|
||||
debug_log(format!("{:?}", result));
|
||||
}
|
||||
206
src/mods/mod_tasks.rs
Normal file
206
src/mods/mod_tasks.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use crate::mods::mod_debug::*;
|
||||
use crate::mods::mod_i18n::*;
|
||||
use crate::mods::mod_os::*;
|
||||
|
||||
pub struct Rule {
|
||||
/// Monitored service name
|
||||
pub service: String,
|
||||
/// Monitored URI
|
||||
pub uri: String,
|
||||
/// Monitored process name
|
||||
pub process: String,
|
||||
/// E-mail address for messages
|
||||
pub email: String,
|
||||
/// This command will be executed on state change
|
||||
pub command: String,
|
||||
/// Last state
|
||||
pub last_state: bool,
|
||||
}
|
||||
|
||||
pub fn execute_web_task(task: &mut Rule, current_state:bool, just_started: bool) {
|
||||
let locale = Locale::new();
|
||||
|
||||
if task.last_state != current_state || just_started {
|
||||
if task.command.to_string() == "".to_string() {
|
||||
if current_state {
|
||||
println!("\u{2705} {} {}", task.uri, locale.t().state_changed_to_true.clone());
|
||||
} else {
|
||||
println!("\u{274c} {} {}", task.uri, locale.t().state_changed_to_false.clone());
|
||||
}
|
||||
} else {
|
||||
if current_state {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{2705} {} {}",
|
||||
task.uri,
|
||||
locale.t().state_changed_to_true.clone()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{274c} {} {}",
|
||||
task.uri,
|
||||
locale.t().state_changed_to_false.clone()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let subject: String = if current_state {
|
||||
format!(
|
||||
"\"\u{2705} {} {} ({}) {}\"",
|
||||
locale.t().service,
|
||||
task.service,
|
||||
task.uri,
|
||||
locale.t().is_online.clone()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"\"\u{274c} {} {} ({}) {}\"",
|
||||
locale.t().service,
|
||||
task.service,
|
||||
task.uri,
|
||||
locale.t().is_offline.clone()
|
||||
)
|
||||
};
|
||||
|
||||
let shell_cmd =
|
||||
task.command.to_string()
|
||||
.replace("<email>", &task.email)
|
||||
.replace("<subject>", &subject)
|
||||
.replace("<message>", &subject)
|
||||
.replace("<service>", &task.service)
|
||||
.replace("<uri>", &task.uri)
|
||||
.replace(
|
||||
"<state>",
|
||||
if current_state {
|
||||
"online"
|
||||
} else {
|
||||
"offline"
|
||||
}
|
||||
);
|
||||
|
||||
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
|
||||
|
||||
execute(shell_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
if current_state {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{2705} {} {}",
|
||||
task.uri,
|
||||
locale.t().check_is_ok.clone()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{274c} {} {}",
|
||||
task.uri,
|
||||
locale.t().check_failed.clone()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
task.last_state = current_state;
|
||||
}
|
||||
|
||||
pub fn execute_os_task(task: &mut Rule, current_state:bool, just_started: bool) {
|
||||
let locale = Locale::new();
|
||||
|
||||
if task.last_state != current_state || just_started {
|
||||
if task.command.to_string() == "".to_string() {
|
||||
if current_state {
|
||||
println!(
|
||||
"\u{2705} {} {}",
|
||||
task.uri,
|
||||
locale.t().state_changed_to_true.clone()
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"\u{274c} {} {}",
|
||||
task.uri,
|
||||
locale.t().state_changed_to_false.clone()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if current_state {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{2705} {} {}",
|
||||
task.uri,
|
||||
locale.t().state_changed_to_true.clone()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{274c} {} {}",
|
||||
task.uri,
|
||||
locale.t().state_changed_to_false.clone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let subject: String = if current_state {
|
||||
format!(
|
||||
"\"\u{2705} {} {} ({}) {}\"",
|
||||
locale.t().process,
|
||||
task.service,
|
||||
task.process,
|
||||
locale.t().is_running.clone()
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"\"\u{274c} {} {} ({}) {}\"",
|
||||
locale.t().process,
|
||||
task.service,
|
||||
task.process,
|
||||
locale.t().is_not_running.clone()
|
||||
)
|
||||
};
|
||||
|
||||
let shell_cmd =
|
||||
task.command.to_string()
|
||||
.replace("<email>", &task.email)
|
||||
.replace("<subject>", &subject)
|
||||
.replace("<message>", &subject)
|
||||
.replace("<service>", &task.service)
|
||||
.replace("<process>", &task.process)
|
||||
.replace(
|
||||
"<state>",
|
||||
if current_state {
|
||||
"running"
|
||||
} else {
|
||||
"stopped"
|
||||
}
|
||||
);
|
||||
|
||||
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
|
||||
|
||||
execute(shell_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
if current_state {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{2705} {} {}",
|
||||
task.process,
|
||||
locale.t().is_running
|
||||
)
|
||||
);
|
||||
} else {
|
||||
debug_log(
|
||||
format!(
|
||||
"\u{274c} {} {}",
|
||||
task.process,
|
||||
locale.t().is_not_running
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
task.last_state = current_state;
|
||||
}
|
||||
Reference in New Issue
Block a user