Compare commits

..

43 Commits

Author SHA1 Message Date
a6737308c2 Merge pull request 'fixed typo in file name' (#22) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/22
2022-12-26 02:16:54 +03:00
a99c60d529 fixed typo in file name
fixed typo in file name
2022-12-15 20:58:09 +03:00
bd9e0b01f6 Merge pull request 'v1' (#21) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/21
2022-11-12 19:45:58 +03:00
82f1c744dc Tiny doc update
Tiny doc update
2022-11-12 19:40:13 +03:00
011e79e676 Locales are in external files now
Locales are in external files now
2022-11-12 19:08:33 +03:00
3ea4c96cfc Localization support added
Localization support added
2022-11-06 22:04:45 +03:00
c8d080d7b0 Merge pull request 'v1' (#20) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/20
2022-11-04 01:09:47 +03:00
bf96109742 Web-checks now runs in separate thread. Some fixes in scripts.
Web-checks now runs in separate thread. Some fixes in scripts.
2022-11-04 01:07:07 +03:00
87e326e869 Minor changes
Minor changes
2022-10-30 23:38:19 +03:00
dbb860a00b Merge pull request 'Update README.md' (#19) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/19
2022-10-30 23:02:49 +03:00
c78bb31b69 Update README.md 2022-10-30 22:59:39 +03:00
fa6c131e9b Merge pull request 'on-start script updated, localized scripts added' (#18) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/18
2022-10-29 21:15:37 +03:00
85654f51a0 on-start script updated, localized scripts added
on-start script updated, localized scripts added
2022-10-29 21:15:02 +03:00
2e8a323210 Merge pull request 'tiny bug fixed' (#17) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/17
2022-10-29 21:07:08 +03:00
fafcdfa9c7 tiny bug fixed
tiny bug fixed
2022-10-29 21:06:28 +03:00
7e6b3afcdc Merge pull request 'Localized sending scripts added' (#16) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/16
2022-10-29 20:55:32 +03:00
4036a703c8 Localized sending scripts added
Localized sending scripts added
2022-10-29 20:54:23 +03:00
7e91e04afb Merge pull request 'Three more report fields added - <service>, <uri> and <process>' (#15) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/15
2022-10-29 19:11:54 +03:00
56adae89ec Three more report fields added - <service>, <uri> and <process>
Three more report fields added - <service>, <uri> and <process>
2022-10-29 19:11:07 +03:00
c483366f4d Merge pull request 'v1' (#14) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/14
2022-10-29 16:59:57 +03:00
f1d16093b6 Now you can run script on watchdog startup. File structure changed on *nix. Some changes in config file and scripts, please check on upgrade.
Now you can run script on watchdog startup. File structure changed on *nix. Some changes in config file and scripts, please check on upgrade.
2022-10-29 16:57:57 +03:00
Alexander I. Chebykin
fa19c8327b Minor changes 2022-08-10 10:08:27 +03:00
fa0ec74b35 Merge branch 'v1' of https://www.cainet.info/git/cai/CAI-Watchdog into v1 2022-07-03 23:44:32 +03:00
7b98b16e6e Update main.rs 2022-07-03 23:44:07 +03:00
7933a21766 Merge pull request 'Update README.md' (#13) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/13
2022-07-01 12:12:53 +03:00
Alexander I. Chebykin
3976307952 Update README.md 2022-07-01 12:12:26 +03:00
2311ff28c9 Merge pull request 'Fixed some typos' (#12) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/12
2022-07-01 12:09:04 +03:00
Alexander I. Chebykin
48cc6af869 Fixed some typos
Fixed some typos
2022-07-01 12:06:57 +03:00
7feea8ccef Merge pull request 'Processes watching added' (#11) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/11
2022-06-30 00:02:45 +03:00
b638d510a3 Processes watching added
Processes watching added
2022-06-29 23:48:34 +03:00
7ad6b001eb Merge pull request 'Added unicode icons in messages' (#10) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/10
2022-06-29 01:41:40 +03:00
56d738bf8e Added unicode icons in messages
Added unicode icons in messages
2022-06-29 01:38:00 +03:00
171361480d Merge pull request 'README updated' (#9) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/9
2022-06-28 22:36:39 +03:00
2605fef9be README updated
README updated
2022-06-28 22:36:03 +03:00
08ff104659 Merge pull request 'v1' (#8) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/8
2022-06-28 22:34:01 +03:00
719562239a README updated
README updated
2022-06-28 22:32:49 +03:00
Alexander I. Chebykin
ff8c7bae7b Default values for settings added.
Default values for settings added. Print all services statuses on startup.
2022-06-28 18:22:37 +03:00
c3a572619f Merge pull request 'Minor changes.' (#7) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/7
2022-06-28 01:36:43 +03:00
7613ac88d9 Minor changes. 2022-06-28 01:36:02 +03:00
e7ca4cb434 Merge pull request '*nix send-mail script added.' (#6) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/6
2022-06-28 00:13:55 +03:00
486d9384cc *nix send-mail script added.
*nix send-mail script added. Scripts settings moved to config files
2022-06-28 00:13:07 +03:00
c981bfd033 Merge pull request 'Configuration file options modified.' (#5) from v1 into master
Reviewed-on: https://www.cainet.info/git/cai/CAI-Watchdog/pulls/5
2022-06-27 21:38:44 +03:00
b55f72f2e4 Configuration file options modified.
address renamed to uri, <header> renamed to <subject>
2022-06-27 21:37:11 +03:00
28 changed files with 1669 additions and 282 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@
# Build files # Build files
target target
Cargo.lock

587
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "cai-watchdog" name = "cai-watchdog"
version = "0.1.0" version = "0.8.0"
authors = ["Alexander I. Chebykin <alex.chebykin@gmail.com>"] authors = ["Alexander I. Chebykin <alex.chebykin@gmail.com>"]
edition = "2018" edition = "2018"
@@ -8,7 +8,7 @@ edition = "2018"
[dependencies] [dependencies]
file-utils = "0.1.5" file-utils = "0.1.5"
chrono = "0.4.19" chrono = "0.4.23"
# Sync # Sync
#reqwest = { version = "0.11", features = ["blocking"] } #reqwest = { version = "0.11", features = ["blocking"] }
# Async # Async
@@ -16,3 +16,6 @@ reqwest = "0.11"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
ini = "1.3.0" ini = "1.3.0"
exitcode = "1.1.2" exitcode = "1.1.2"
hashmap = "0.0.1"
sysinfo = "0.26.7"
sys-locale = "0.2.3"

182
README.md
View File

@@ -1,3 +1,183 @@
# CAI-Watchdog # 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.

View File

@@ -1,20 +0,0 @@
[main]
check_interval = 10000
rules_count = 2
[notifications]
email = admin@server.local
command = send-telegram <message>
service_start = true
[rule1]
service = Test1
address = http://127.0.0.1:3000/api/v1/
email = admin@server.local
command = send-telegram <message>
[rule2]
service = Test2
address = http://127.0.0.1:3300/api/v1/
email = admin@server.local
command = send-telegram <message>

View 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>

View File

@@ -0,0 +1,4 @@
Username: user@domain.local
Password: str0ngP@$$word
SMTP Server: smtp.domain.local
Port: 465

View 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")

View 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 = Информация о версии

View 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"

View 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!"

View 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 запущен!"

View 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!"

View 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 остановлен!"

View 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"

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,2 @@
Group ID: Telegram_Group_ID
Bot token: Telegram_Bot_Token

View File

@@ -1,23 +0,0 @@
#!/bin/bash
GROUP_ID=<group_id>
BOT_TOKEN=<bot_token>
# this 3 checks (if) are not necessary but should be convenient
if [ "$1" == "-h" ]; then
echo "Usage: `basename $0` \"text message\""
exit 0
fi
if [ -z "$1" ]
then
echo "Add message text as second arguments"
exit 0
fi
if [ "$#" -ne 1 ]; then
echo "You can pass only one argument. For string with spaces put it on quotes"
exit 0
fi
curl -s --data "text=$1" --data "chat_id=$GROUP_ID" 'https://api.telegram.org/bot'$BOT_TOKEN'/sendMessage' > /dev/null

View File

@@ -5,7 +5,8 @@ Description=Watchdog service
User=wwwrun User=wwwrun
Group=www Group=www
Type=simple Type=simple
ExecStart=/usr/local/bin/cai-watchdog ExecStart=/usr/local/sbin/cai-watchdog
ExecStopPost=/etc/cai-watchdog/on-stop
KillMode=control-group KillMode=control-group
NotifyAccess=all NotifyAccess=all

View File

@@ -1,20 +1,25 @@
[main] [main]
check_interval = 10000 check_interval = 10
rules_count = 2 rules_count = 3
on_start_command = ./on-start.ps1
[notifications]
email = admin@server.local
command = ./send-telegram.ps1 <message>
service_start = true
[rule1] [rule1]
service = Test1 service = Test1
address = http://127.0.0.1:3000/api/v1/ uri = http://127.0.0.1:3000/api/v1/
process =
email = admin@server.local email = admin@server.local
command = ./send-telegram.ps1 <message> command = ./send-telegram.ps1 <message>
[rule2] [rule2]
service = Test2 service = Test2
address = http://127.0.0.1:3300/api/v1/ 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 email = admin@server.local
command = ./send-telegram.ps1 <message> command = ./send-telegram.ps1 <message>

View 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 = Информация о версии

View File

@@ -1,5 +1,3 @@
Param([Parameter(Mandatory=$true)][String]$Message)
$Telegramtoken = "Your_Telegram_Token" $Telegramtoken = "Your_Telegram_Token"
$Telegramchatid = "Your_Telegram_Chat_ID" $Telegramchatid = "Your_Telegram_Chat_ID"

View File

@@ -1,18 +1,28 @@
//! CAI-Watchdog is a lightweigth watchdog for *nix and Windows
//! Author: Alexander I. Chebykin (CAI) <alex.chebykin@gmail.com>
#[macro_use] #[macro_use]
extern crate ini; extern crate ini;
extern crate exitcode; extern crate exitcode;
use std::env; use std::env;
use std::path::Path;
use std::process::Command;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use std::process::Command; use sysinfo::{System, SystemExt};
mod mod_locales;
mod mod_fs;
/// Rule description structure /// Rule description structure
pub struct Rule { struct Rule {
/// Monitored service name /// Monitored service name
pub service: String, pub service: String,
/// Monitored URI /// Monitored URI
pub address: String, pub uri: String,
/// Monitored process name
pub process: String,
/// E-mail address for messages /// E-mail address for messages
pub email: String, pub email: String,
/// This command will be executed on state change /// This command will be executed on state change
@@ -21,7 +31,58 @@ pub struct Rule {
pub last_state: bool, pub last_state: bool,
} }
/// Checks service availability /// 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())
};
}
/// 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!");
/// }
/// ```
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(&process_name) {
result = true;
}
return result;
}
/// Checks web-service availability
/// ///
/// # Arguments /// # Arguments
/// ///
@@ -30,13 +91,13 @@ pub struct Rule {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// if if check("https://somesite.local/api/v1/".to_string()).await { /// if check_uri("https://somesite.local/api/v1/".to_string()).await {
/// println!("ok"); /// println!("ok");
/// } else { /// } else {
/// println!("uh-oh... something wrong!"); /// println!("uh-oh... something wrong!");
/// } /// }
/// ``` /// ```
async fn check(uri: String) -> bool { async fn check_uri(uri: String) -> bool {
if let Err(_e) = reqwest::get(uri).await { if let Err(_e) = reqwest::get(uri).await {
return false; return false;
} else { } else {
@@ -76,19 +137,21 @@ fn debug_log(text: String) {
/// execute("gedit".to_string()); /// execute("gedit".to_string());
/// ``` /// ```
fn execute(command: String) { fn execute(command: String) {
debug_log(format!("execute {}", command)); let locale = mod_locales::Locale::new();
debug_log(format!("{} {}", locale.t().execute, command));
let output = if cfg!(target_os = "windows") { let output = if cfg!(target_os = "windows") {
Command::new("powershell") Command::new("powershell")
.args(["-NoLogo", "-NonInteractive", "-Command", &command]) .args(["-NoLogo", "-NonInteractive", "-Command", &command])
.output() .output()
.expect("failed to execute process") .expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
} else { } else {
Command::new("sh") Command::new("sh")
.arg("-c") .arg("-c")
.arg(&command) .arg(&command)
.output() .output()
.expect("failed to execute process") .expect(&locale.t().failed_to_execute_process) //.expect("failed to execute process")
}; };
let result = output.stdout; let result = output.stdout;
@@ -109,141 +172,380 @@ fn execute(command: String) {
/// print_help(args.clone()); /// print_help(args.clone());
/// ``` /// ```
fn print_help(args: Vec<String>) { fn print_help(args: Vec<String>) {
let locale = mod_locales::Locale::new();
if cfg!(windows) { if cfg!(windows) {
if args.len() > 1 && (args[1].to_string() == "/help".to_string() || args[1].to_string() == "/?".to_string()) { if args.len() > 1 && (args[1].to_string() == "/help".to_string() || args[1].to_string() == "/?".to_string()) {
println!("");
println!("Usage: {} [/? | /help | /v | /ver | config_file]", &args[0]);
println!(" /? | /help : This help message");
println!(" /v | /ver : Version info");
println!(" config_file : Configuration file");
} 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"); const VERSION: &str = env!("CARGO_PKG_VERSION");
println!("CAI Watchdog {} {}", locale.t().ver, VERSION);
println!(""); println!("");
println!("CAI Watchdog ver {}", VERSION); 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); 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 { } else {
if args.len() > 1 && (args[1].to_string() == "--help".to_string() || args[1].to_string() == "-h".to_string()) { 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!("");
println!("Usage: {} [-h | --help | -v | --ver | config_file]", &args[0]); println!("{}: {} [-h | --help | -v | --ver | config_file]", locale.t().usage, &args[0]);
println!(" -h | --help : This help message"); println!(" -h | --help : {}", locale.t().this_help_message);
println!(" -v | --ver : Version info"); println!(" -v | --ver : {}", locale.t().version_info);
println!(" config_file : Configuration file"); println!(" config_file : {}", locale.t().configuration_file);
std::process::exit(exitcode::OK);
} else if args.len() > 1 } else if args.len() > 1
&& (args[1].to_string() == "--ver".to_string() || args[1].to_string() == "-v".to_string()) { && (args[1].to_string() == "--ver".to_string() || args[1].to_string() == "-v".to_string()) {
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
println!(""); println!("CAI Watchdog {} {}", locale.t().ver, VERSION);
println!("CAI Watchdog ver {}", VERSION);
}
std::process::exit(exitcode::OK); std::process::exit(exitcode::OK);
} }
}
} }
fn main() { fn main() {
let locale = mod_locales::Locale::new();
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
print_help(args.clone()); print_help(args.clone());
let mut config_file = mod_fs::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 { let cfg_file = if args.len() > 1 {
&args[1] &args[1]
} else if cfg!(windows) {
"cai-watchdog.ini"
} else { } else {
"/etc/cai-watchdog.conf" &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 cfg = ini!(cfg_file);
let check_interval = cfg["main"]["check_interval"].clone().unwrap().parse::<u64>().unwrap(); let check_interval = if cfg["main"].contains_key("check_interval") {
let rules_count = cfg["main"]["rules_count"].clone().unwrap().parse::<u8>().unwrap(); cfg["main"]["check_interval"].clone().unwrap().parse::<u64>().unwrap() * 1000
} else {
10000
};
let mut tasks = vec![]; 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 { for i in 1..rules_count + 1 {
let service = cfg[&format!("{}{}", "rule", i)]["service"].clone().unwrap(); let service = if cfg[&format!("rule{}", i)].contains_key("service") {
let address = cfg[&format!("{}{}", "rule", i)]["address"].clone().unwrap(); cfg[&format!("rule{}", i)]["service"].clone().unwrap()
let email = cfg[&format!("{}{}", "rule", i)]["email"].clone().unwrap(); } else {
let command = cfg[&format!("{}{}", "rule", i)]["command"].clone().unwrap(); 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!("rule {}", i));
debug_log(format!("service {}", service)); debug_log(format!("service {}", service));
debug_log(format!("address {}", address)); debug_log(format!("uri {}", uri));
debug_log(format!("process {}", process));
debug_log(format!("email {}", email)); debug_log(format!("email {}", email));
debug_log(format!("command {}", command)); debug_log(format!("command {}", command));
tasks.push( if uri != "".to_string() {
tasks_web.push(
Rule{ Rule{
service: cfg[&format!("{}{}", "rule", i)]["service"].clone().unwrap().to_string(), service: service,
address: cfg[&format!("{}{}", "rule", i)]["address"].clone().unwrap().to_string(), uri: uri,
email: cfg[&format!("{}{}", "rule", i)]["email"].clone().unwrap().to_string(), process: process,
command: cfg[&format!("{}{}", "rule", i)]["command"].clone().unwrap().to_string(), 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, last_state: false,
} }
); );
} }
let start_notification = cfg["notifications"]["service_start"].clone().unwrap().to_string() == "true".to_string();
let notification_email = cfg["notifications"]["email"].clone().unwrap();
let notification_command = cfg["notifications"]["command"].clone().unwrap();
if start_notification {
debug_log(format!("Service started"));
let shell_cmd = notification_command.to_string()
.replace("<email>", &notification_email.to_string())
.replace("<header>", &format!("\"Watchdog service started\""))
.replace("<message>", &format!("\"Watchdog service started\""));
debug_log(format!("execute {}", shell_cmd));
execute(shell_cmd);
} }
loop { let on_start_command = if cfg["main"].contains_key("on_start_command") {
for i in 0..tasks.len() { cfg["main"]["on_start_command"].clone().unwrap()
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
if check(tasks[i].address.clone()).await {
if tasks[i].last_state != true {
debug_log(format!("{} state changed to true", tasks[i].address));
let shell_cmd = tasks[i].command.to_string()
.replace("<email>", &tasks[i].email)
.replace("<header>", &format!("\"Service {} ({}) is online\"", tasks[i].service, tasks[i].address))
.replace("<message>", &format!("\"Service {} ({}) is now online\"", tasks[i].service, tasks[i].address));
debug_log(format!("execute {}", shell_cmd));
execute(shell_cmd);
}
debug_log(format!("{} check is ok", tasks[i].address));
tasks[i].last_state = true
} else { } else {
if tasks[i].last_state != false { "".to_string()
debug_log(format!("{} state changed to false", tasks[i].address)); };
let shell_cmd = tasks[i].command.to_string() debug_log(format!("\u{2139} {}", locale.t().service_started));
.replace("<email>", &tasks[i].email)
.replace("<header>", &format!("\"Service {} ({}) is offline\"", tasks[i].service, tasks[i].address))
.replace("<message>", &format!("\"Service {} ({}) is now offline\"", tasks[i].service, tasks[i].address));
debug_log(format!("execute {}", shell_cmd)); if on_start_command.to_string() != "" {
execute(shell_cmd); execute(on_start_command);
} }
debug_log(format!("{} check failed", tasks[i].address)); let mut just_started_web = true;
tasks[i].last_state = false 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[i].uri.clone()).await {
if check(&tasks_web[i]).await {
if tasks_web[i].last_state != true || just_started_web {
if tasks_web[i].command.to_string() == "".to_string() {
println!("\u{2705} {} {}",
tasks_web[i].uri,
locale.t().state_changed_to_true
);
} else {
debug_log(format!("\u{2705} {} {}",
tasks_web[i].uri,
locale.t().state_changed_to_true)
);
let shell_cmd =
tasks_web[i].command.to_string()
.replace("<email>", &tasks_web[i].email)
.replace("<subject>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_online
)
)
.replace("<message>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_online_now
)
)
.replace("<service>", &tasks_web[i].service)
.replace("<uri>", &tasks_web[i].uri)
.replace("<state>", "online");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{2705} {} {}", tasks_web[i].uri, locale.t().check_is_ok));
tasks_web[i].last_state = true;
} else {
if tasks_web[i].last_state != false || just_started_web {
if tasks_web[i].command.to_string() == "".to_string() {
println!("\u{274c} {} {}", tasks_web[i].uri, locale.t().state_changed_to_false);
} else {
debug_log(format!("\u{274c} {} {}", tasks_web[i].uri, locale.t().state_changed_to_false));
let shell_cmd =
tasks_web[i].command.to_string()
.replace("<email>", &tasks_web[i].email)
.replace("<subject>", &
format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_offline
)
)
.replace("<message>", &
format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().service,
tasks_web[i].service,
tasks_web[i].uri,
locale.t().is_offline_now
)
)
.replace("<service>", &tasks_web[i].service)
.replace("<uri>", &tasks_web[i].uri)
.replace("<state>", "offline");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{274c} {} {}", tasks_web[i].uri, locale.t().check_failed));
tasks_web[i].last_state = false;
} }
}); });
} }
just_started_web = false;
thread::sleep(Duration::from_millis(check_interval));
}
});
let mut just_started_prc = true;
let locale = mod_locales::Locale::new();
loop {
for i in 0..tasks_prc.len() {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
//if check(tasks[i].uri.clone()).await {
if check(&tasks_prc[i]).await {
if tasks_prc[i].last_state != true || just_started_prc {
if tasks_prc[i].command.to_string() == "".to_string() {
println!("\u{2705} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_true);
} else {
debug_log(format!("\u{2705} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_true));
let shell_cmd =
tasks_prc[i].command.to_string()
.replace("<email>", &tasks_prc[i].email)
.replace("<subject>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_running
)
)
.replace("<message>",
&format!(
"\"\u{2705} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_running_now
)
)
.replace("<service>", &tasks_prc[i].service)
.replace("<process>", &tasks_prc[i].process)
.replace("<state>", "running");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{2705} {} {}", tasks_prc[i].process, locale.t().is_running));
tasks_prc[i].last_state = true;
} else {
if tasks_prc[i].last_state != false || just_started_prc {
if tasks_prc[i].command.to_string() == "".to_string() {
println!("\u{274c} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_false);
} else {
debug_log(format!("\u{274c} {} {}", tasks_prc[i].uri, locale.t().state_changed_to_false));
let shell_cmd =
tasks_prc[i].command.to_string()
.replace("<email>", &tasks_prc[i].email)
.replace("<subject>",
&format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_not_running
)
)
.replace("<message>",
&format!(
"\"\u{274c} {} {} ({}) {}\"",
locale.t().process,
tasks_prc[i].service,
tasks_prc[i].process,
locale.t().is_not_running_now
)
)
.replace("<service>", &tasks_prc[i].service)
.replace("<process>", &tasks_prc[i].process)
.replace("<state>", "stopped");
debug_log(format!("{} {}", locale.t().execute, shell_cmd));
execute(shell_cmd);
}
}
debug_log(format!("\u{274c} {} {}", tasks_prc[i].process, locale.t().is_not_running));
tasks_prc[i].last_state = false;
}
});
}
just_started_prc = false;
thread::sleep(Duration::from_millis(check_interval)); thread::sleep(Duration::from_millis(check_interval));
} }
} }

25
src/mod_fs.rs Normal file
View File

@@ -0,0 +1,25 @@
//! 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/mod_locales.rs Normal file
View 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::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().clone();
}
}