Compare commits
147 commits
openbsd-po
...
master
Author | SHA1 | Date | |
---|---|---|---|
Jérémy Lecour | 467ea511bc | ||
Jérémy Lecour | f1686874f6 | ||
Jérémy Lecour | f66fb16402 | ||
Jérémy Lecour | a997191d06 | ||
2560deec1e | |||
Jérémy Lecour | d01286e0b9 | ||
Jérémy Lecour | 2f2e723bc0 | ||
Jérémy Lecour | 252fe746a7 | ||
Jérémy Lecour | 8a7cf0a941 | ||
1cbe1a6c1e | |||
f594a54e07 | |||
33504c4c01 | |||
21f7464d42 | |||
95b45bac8e | |||
Mathieu Trossevin | 4ef8878bcf | ||
a33021b041 | |||
Jérémy Lecour | 9aa16dff73 | ||
Jérémy Lecour | b8bb014b8d | ||
Jérémy Lecour | 334ef62d43 | ||
Jérémy Lecour | c83a1043c7 | ||
Jérémy Lecour | 46b8015d93 | ||
Jérémy Lecour | 026fb9f5cd | ||
Jérémy Lecour | 88187912cd | ||
Jérémy Lecour | edca7cee17 | ||
Jérémy Lecour | 26d4a2e219 | ||
Jérémy Lecour | f5abdd2912 | ||
Jérémy Lecour | a811b008c7 | ||
29ac93e250 | |||
c88bc83145 | |||
bf6cf1bf00 | |||
Jérémy Lecour | 63c83ae1ee | ||
Jérémy Lecour | 15b5cd62bc | ||
Jérémy Lecour | c8155a8d99 | ||
Jérémy Lecour | a159f77649 | ||
Jérémy Lecour | 9f3e90766d | ||
Jérémy Lecour | a3fb426ecf | ||
Jérémy Lecour | 0503838b5b | ||
f7fa476de8 | |||
fe095a20f7 | |||
5ad41113ad | |||
8c6c7d5b00 | |||
Jérémy Lecour | afab06ee38 | ||
Jérémy Lecour | d449be172c | ||
Jérémy Lecour | 5a28e2bfb7 | ||
Jérémy Lecour | 68d88abb7c | ||
Jérémy Lecour | def18270f1 | ||
Jérémy Lecour | a36b0bed83 | ||
Jérémy Lecour | 22635267fe | ||
Jérémy Lecour | 13885a4743 | ||
Jérémy Lecour | b13c627230 | ||
Jérémy Lecour | 47c452f977 | ||
Jérémy Lecour | 8d0eddaf6f | ||
Jérémy Lecour | a7e1133408 | ||
Jérémy Lecour | bea327a254 | ||
Jérémy Lecour | b8b94a66ce | ||
Jérémy Lecour | c9c061f811 | ||
Jérémy Lecour | 80087f3c38 | ||
Jérémy Lecour | d05de0f925 | ||
Jérémy Lecour | 71d1fc417c | ||
Jérémy Lecour | ac86a97531 | ||
Jérémy Lecour | 8561d64c0e | ||
Jérémy Lecour | ac111c2742 | ||
Jérémy Lecour | c47ebee7c0 | ||
Jérémy Lecour | 72f7dc1159 | ||
Jérémy Lecour | 7a2945a1e3 | ||
Jérémy Lecour | 6d134e2d89 | ||
Jérémy Lecour | 42b781db96 | ||
Jérémy Lecour | ca9c7ec654 | ||
Jérémy Lecour | 0a6a45066d | ||
Jérémy Lecour | e0a75db015 | ||
Jérémy Lecour | 7b2a3fbcd1 | ||
Jérémy Lecour | d619072254 | ||
Jérémy Lecour | cc71b797c9 | ||
Jérémy Lecour | 5f06aec803 | ||
Jérémy Lecour | 7833c9c3ee | ||
Jérémy Lecour | 42bb96d005 | ||
Jérémy Lecour | 90e3bc188f | ||
Jérémy Lecour | 0514663977 | ||
Jérémy Lecour | b1e2a6ac0f | ||
Jérémy Lecour | 9f99b12cd4 | ||
Jérémy Lecour | 3ded64677f | ||
Jérémy Lecour | 434536b1e9 | ||
Jérémy Lecour | e31e5c73df | ||
Jérémy Lecour | 6568e66f73 | ||
Jérémy Lecour | 0c6123bb54 | ||
Jérémy Lecour | d01e79e832 | ||
Jérémy Lecour | ca62314864 | ||
Jérémy Lecour | 44d83dd474 | ||
Jérémy Lecour | cf05263adb | ||
Jérémy Lecour | 1bfe7c4b86 | ||
Jérémy Lecour | c2b44f0e48 | ||
Jérémy Lecour | 997eff6ca7 | ||
Jérémy Lecour | b9da112b6d | ||
Jérémy Lecour | 293c38fb83 | ||
Jérémy Lecour | 463555475b | ||
Jérémy Lecour | d9fedcdbcf | ||
Jérémy Lecour | 4e2d55ea5e | ||
Jérémy Lecour | b73bd72eac | ||
Jérémy Lecour | 6b70d2416c | ||
Jérémy Lecour | e528a27048 | ||
Jérémy Lecour | d26aea7ab2 | ||
Jérémy Lecour | 3dba4f7fc1 | ||
Jérémy Lecour | 5e0705391e | ||
Victor Laborie | 0621f62267 | ||
6d666a86a3 | |||
488a1e8ad7 | |||
Jérémy Lecour | 4f54f6473d | ||
Jérémy Lecour | 60dfac9d78 | ||
Jérémy Lecour | 2a70712d4b | ||
Jérémy Lecour | 717352d5b4 | ||
Jérémy Lecour | c6c4d1b2c1 | ||
Jérémy Lecour | a280185a04 | ||
Jérémy Lecour | 2cf7c5c3f3 | ||
Jérémy Lecour | 5fa7282b2f | ||
Jérémy Lecour | 6e377cedca | ||
Jérémy Lecour | 07f5f1a624 | ||
Jérémy Lecour | 1cf8780f6d | ||
Jérémy Lecour | 525308eda4 | ||
Jérémy Lecour | 1f54ba33e9 | ||
Jérémy Lecour | 2370c2f27e | ||
Jérémy Lecour | 94a143b874 | ||
Jérémy Lecour | 67a0cd5f48 | ||
Jérémy Lecour | 4bf2e9de65 | ||
Jérémy Lecour | d99956213b | ||
Jérémy Lecour | 4a8e5d89e5 | ||
Jérémy Lecour | fe0eccd53a | ||
Jérémy Lecour | 2f249eda73 | ||
Jérémy Lecour | ee93e19130 | ||
Jérémy Lecour | 7e5b571fc9 | ||
Jérémy Lecour | 07bb68ad60 | ||
Jérémy Lecour | c15ee205d2 | ||
Jérémy Lecour | 25d7f786bc | ||
Jérémy Lecour | 786db8b60b | ||
Jérémy Lecour | dfd9e431b4 | ||
Jérémy Lecour | a7c6c94626 | ||
Jérémy Lecour | 938574de5c | ||
Jérémy Lecour | 72b43f314e | ||
Jérémy Lecour | 5fd01a1614 | ||
Jérémy Lecour | 583642945b | ||
Jérémy Lecour | 1f7a91678f | ||
Jérémy Lecour | c02bdbc720 | ||
Jérémy Lecour | b1ae34fe47 | ||
Jérémy Lecour | 5a0603430a | ||
Jérémy Lecour | d3ffbc8225 | ||
Jérémy Lecour | 0cde3681a6 | ||
Jérémy Lecour | 05335d7f08 | ||
Jérémy Lecour | 20c69a8fac |
106
CHANGELOG.md
Normal file
106
CHANGELOG.md
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
and this project **does not adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)**.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add contrib/post-release.sh to help with post-release tasks
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
## [24.05] - 2024-05-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add missing (but documented) `--(no-)evocheck` options
|
||||||
|
|
||||||
|
## [23.10.1] - 2023-10-09
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Use a special variable name since USER is always defined from the environment
|
||||||
|
|
||||||
|
## [23.10] - 2023-10-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Force a user name with `-u,--user` option (default is still `logname(1)`).
|
||||||
|
* More people credited
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
* `--autosysadmin` is replaced by `--user autosysadmin`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [22.07] - 2022-07-05
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Add `--autosysadmin` flag
|
||||||
|
* Commit change in /etc of lxc containers
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
## [22.01] - 2022-01-25
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* version/host/user headers in sent email
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
New version pattern
|
||||||
|
|
||||||
|
## [0.6.4] - 2021-06-17
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* fallback if findmnt is absent
|
||||||
|
|
||||||
|
## [0.6.3] - 2020-02-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Notify syslog when partitions are re-mounted (Linux)
|
||||||
|
|
||||||
|
## [0.6.2] - 2020-02-02
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* better detection of read-only partitions (Linux)
|
||||||
|
|
||||||
|
## [0.6.0] - 2019-11-05
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* commit changes in /usr/share/scripts/ if needed
|
||||||
|
|
||||||
|
## Previous changelog
|
||||||
|
|
||||||
|
* 0.5.0 : options et mode interactif pour l'exécution des actions, meilleure compatibilité POSIX
|
||||||
|
* 0.4.1 : Utilisation de "printf" à la place de "echo" pour mieux gérer les sauts de ligne
|
||||||
|
* 0.4.0 : Amélioration de la récupération d'information (plus de cas gérés). Infos Git avant la saisie.
|
||||||
|
* 0.3.0 : Écriture dans un fichier de log, amélioration de la récupération d'informations, amélioration de la syntaxe shell
|
||||||
|
* 0.2.7 : Correction d'un bug lors de l'utilisation de '&' dans le texte
|
||||||
|
* 0.2.6 : Precision du charset dans les entetes du mail
|
||||||
|
* 0.2.5 : Correction d'un bug avec le path de sendmail sous OpenBSD
|
||||||
|
* 0.2.4 : Correction d'un bug lors de l'utilisation de '/' dans le texte
|
||||||
|
* 0.2.3 : Correction d'un bug avec $REALM
|
7
README
7
README
|
@ -1,7 +0,0 @@
|
||||||
script for Evolix maintenance
|
|
||||||
=============================
|
|
||||||
|
|
||||||
script evomaintenance.sh which sends a mail
|
|
||||||
for each intervention on a Pack Evolix server.
|
|
||||||
|
|
||||||
* Pb quand '&' dans le msg d'evomaintenance...
|
|
30
README.md
Normal file
30
README.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Evomaintenance
|
||||||
|
|
||||||
|
```.plain
|
||||||
|
$ evomaintenance --help
|
||||||
|
evomaintenance is a program that helps reporting what you've done on a server
|
||||||
|
|
||||||
|
Usage: evomaintenance
|
||||||
|
or evomaintenance --message="add new host"
|
||||||
|
or evomaintenance --no-api --no-mail --no-commit
|
||||||
|
or echo "add new vhost" | evomaintenance
|
||||||
|
|
||||||
|
Options
|
||||||
|
-m, --message=MESSAGE set the message from the command line
|
||||||
|
--mail enable the mail hook (default)
|
||||||
|
--no-mail disable the mail hook
|
||||||
|
--db enable the database hook
|
||||||
|
--no-db disable the database hook (default)
|
||||||
|
--api enable the API hook (default)
|
||||||
|
--no-api disable the API hook
|
||||||
|
--commit enable the commit hook (default)
|
||||||
|
--no-commit disable the commit hook
|
||||||
|
--evocheck enable evocheck execution (default)
|
||||||
|
--no-evocheck disable evocheck execution
|
||||||
|
--auto use "auto" mode
|
||||||
|
--no-auto use "manual" mode (default)
|
||||||
|
-v, --verbose increase verbosity
|
||||||
|
-n, --dry-run actions are not executed
|
||||||
|
--help print this message and exit
|
||||||
|
--version print version and exit
|
||||||
|
```
|
60
Vagrantfile
vendored
Normal file
60
Vagrantfile
vendored
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- mode: ruby -*-
|
||||||
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
Vagrant::DEFAULT_SERVER_URL.replace('https://vagrantcloud.com')
|
||||||
|
|
||||||
|
# Load ~/.VagrantFile if exist, permit local config provider
|
||||||
|
vagrantfile = File.join("#{Dir.home}", '.VagrantFile')
|
||||||
|
load File.expand_path(vagrantfile) if File.exists?(vagrantfile)
|
||||||
|
|
||||||
|
Vagrant.configure('2') do |config|
|
||||||
|
config.vm.synced_folder "./", "/vagrant", type: "rsync", rsync__exclude: [ '.vagrant', '.git' ]
|
||||||
|
config.ssh.shell = "/bin/sh"
|
||||||
|
|
||||||
|
$deps = <<SCRIPT
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get -yq install postgresql-client sudo sendmail
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
$install = <<SCRIPT
|
||||||
|
if [ ! -d /usr/share/scripts ]; then
|
||||||
|
mkdir /usr/share/scripts
|
||||||
|
chmod 700 /usr/share/scripts
|
||||||
|
chown root:$1 /usr/share/scripts
|
||||||
|
fi
|
||||||
|
ln -fs /vagrant/evomaintenance.sh /usr/share/scripts
|
||||||
|
ln -fs /vagrant/evomaintenance.cf /etc
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
$trap = <<SCRIPT
|
||||||
|
trap_cmd='trap "sudo /usr/share/scripts/evomaintenance.sh" 0'
|
||||||
|
if [ -f /home/vagrant/.bash_profile ]; then
|
||||||
|
if ! grep -q "$trap_cmd" /home/vagrant/.bash_profile; then
|
||||||
|
echo "$trap_cmd" >> /home/vagrant/.bash_profile
|
||||||
|
fi
|
||||||
|
elif [ -f /home/vagrant/.profile ]; then
|
||||||
|
if ! grep -q "$trap_cmd" /home/vagrant/.profile; then
|
||||||
|
echo "$trap_cmd" >> /home/vagrant/.profile
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$trap_cmd" > /home/vagrant/.profile
|
||||||
|
fi
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
nodes = [
|
||||||
|
{ :name => "debian", :box => "debian/stretch64", :group => "root" },
|
||||||
|
{ :name => "openbsd", :box => "generic/openbsd6", :group => "wheel" }
|
||||||
|
]
|
||||||
|
|
||||||
|
nodes.each do |i|
|
||||||
|
config.vm.define "#{i[:name]}" do |node|
|
||||||
|
node.vm.hostname = "evomaintenance-#{i[:name]}"
|
||||||
|
node.vm.box = "#{i[:box]}"
|
||||||
|
|
||||||
|
config.vm.provision "deps", type: "shell", :inline => $deps if "#{i[:name]}" == "debian"
|
||||||
|
config.vm.provision "deps", type: "shell", :inline => "pkg_add postgresql-client-10.5p1" if "#{i[:name]}" == "openbsd"
|
||||||
|
config.vm.provision "install", type: "shell", :inline => $install, :args => ["#{i[:group]}"]
|
||||||
|
config.vm.provision "trap", type: "shell", :inline => $trap
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,5 +0,0 @@
|
||||||
0.2.7 : Correction d'un bug lors de l'utilisation de '&' dans le texte
|
|
||||||
0.2.6 : Precision du charset dans les entetes du mail
|
|
||||||
0.2.5 : Correction d'un bug avec le path de sendmail sous OpenBSD
|
|
||||||
0.2.4 : Correction d'un bug lors de l'utilisation de '/' dans le texte
|
|
||||||
0.2.3 : Correction d'un bug avec $REALM
|
|
3
contrib/git-hook-post-checkout
Executable file
3
contrib/git-hook-post-checkout
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Git pre-checkout hook restoring permissions and ownerships.
|
||||||
|
mtree -u < .mtree
|
26
contrib/git-hook-pre-commit
Executable file
26
contrib/git-hook-pre-commit
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Git pre-commit hook storing permissions and ownerships.
|
||||||
|
|
||||||
|
mtreeignore=$(mktemp --suffix mtree)
|
||||||
|
|
||||||
|
mtree_exclude() {
|
||||||
|
echo .git
|
||||||
|
# Get ignored files from git https://stackoverflow.com/a/467053
|
||||||
|
find . -not -path './.git/*' | git check-ignore --stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
# In case .mtree doens't exists yet, we still want it in the specification
|
||||||
|
# to be generated.
|
||||||
|
if [ -f .mtree ]; then
|
||||||
|
touch .mtree
|
||||||
|
fi
|
||||||
|
|
||||||
|
mtree_exclude > "$mtreeignore"
|
||||||
|
trap 'rm --force "$mtreeignore"' EXIT
|
||||||
|
|
||||||
|
mtree -x -c \
|
||||||
|
-p . \
|
||||||
|
-k uname,gname,mode \
|
||||||
|
-X "$mtreeignore" > .mtree
|
||||||
|
|
||||||
|
git add .mtree
|
46
contrib/post-release.sh
Executable file
46
contrib/post-release.sh
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
# Post-release script
|
||||||
|
#
|
||||||
|
# It must me executed after a release,
|
||||||
|
# to copy parts to the correct downstream projects.
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
|
||||||
|
# Exit immediately if a pipeline (which may consist of a single simple command),
|
||||||
|
# a list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero
|
||||||
|
# status.
|
||||||
|
set -o errexit
|
||||||
|
# If expansion is attempted on an unset variable or parameter, the shell prints an
|
||||||
|
# error message, and, if not interactive, exits with a non-zero status.
|
||||||
|
set -o nounset
|
||||||
|
# The pipeline's return status is the value of the last (rightmost) command
|
||||||
|
# to exit with a non-zero status, or zero if all commands exit successfully.
|
||||||
|
set -o pipefail
|
||||||
|
# Enable trace mode if called with environment variable TRACE=1
|
||||||
|
if [[ "${TRACE-0}" == "1" ]]; then
|
||||||
|
set -o xtrace
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPOSITORIES_PARENT_DIR="${HOME}/GIT"
|
||||||
|
|
||||||
|
echo "Copy to ansible-roles.git :"
|
||||||
|
|
||||||
|
dest="${REPOSITORIES_PARENT_DIR}/ansible-roles/evomaintenance/files/upstream"
|
||||||
|
|
||||||
|
if [ ! -d "${dest}" ]; then
|
||||||
|
echo "Target doesn't look like an evomaintenance upstream directory. Check before running again."
|
||||||
|
else
|
||||||
|
cp CHANGELOG.md README.md evomaintenance.sh "${dest}/" && echo "Done!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Copy to EvoBSD.git :"
|
||||||
|
|
||||||
|
dest="${REPOSITORIES_PARENT_DIR}/EvoBSD/roles/evomaintenance/files/upstream"
|
||||||
|
|
||||||
|
if [ ! -d "${dest}" ]; then
|
||||||
|
echo "Target doesn't look like an evomaintenance upstream directory. Check before running again."
|
||||||
|
else
|
||||||
|
cp CHANGELOG.md README.md evomaintenance.sh "${dest}/" && echo "Done!"
|
||||||
|
fi
|
47
debian/changelog
vendored
47
debian/changelog
vendored
|
@ -1,3 +1,50 @@
|
||||||
|
evomaintenance (0.6.3-1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* fix partitions re-mounting before/after commits
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Mon, 3 Mar 2020 22:14:12 +0100
|
||||||
|
|
||||||
|
evomaintenance (0.6.0-1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* commit changes in /usr/share/scripts/ if needed
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Tue, 5 Nov 2019 14:50:12 +0100
|
||||||
|
|
||||||
|
evomaintenance (0.5.1-1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* verify commands presence only if needed
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Wed, 21 Aug 2019 15:38:12 +0200
|
||||||
|
|
||||||
|
evomaintenance (0.5.0-1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* options and interactive mode for selective hooks execution, and better POSIX compliance
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Sun, 26 Mar 2019 11:13:12 +0100
|
||||||
|
|
||||||
|
evomaintenance (0.4.1-1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* Use "printf" instead of "echo" for better line breaks support
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Tue, 25 Sep 2018 11:54:12 +0200
|
||||||
|
|
||||||
|
evomaintenance (0.4.0-1) UNRELEASED; urgency=low
|
||||||
|
|
||||||
|
* Git status printed before typing the message
|
||||||
|
* better information extraction (User, IP…)
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Tue, 25 Sep 2018 09:51:55 +0200
|
||||||
|
|
||||||
|
evomaintenance (0.3.0-1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* New upstream release.
|
||||||
|
* log file support.
|
||||||
|
* more information in Git commit.
|
||||||
|
* improve information gathering.
|
||||||
|
* improve shell syntax
|
||||||
|
|
||||||
|
-- Jérémy Lecour <jlecour@evolix.fr> Wed, 05 Sep 2018 18:48:55 +0200
|
||||||
|
|
||||||
evomaintenance (0.2.8-1) UNRELEASED; urgency=medium
|
evomaintenance (0.2.8-1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
* New upstream release.
|
* New upstream release.
|
||||||
|
|
3
debian/control
vendored
3
debian/control
vendored
|
@ -7,8 +7,7 @@ Standards-Version: 3.8.0
|
||||||
|
|
||||||
Package: evomaintenance
|
Package: evomaintenance
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: postgresql-client, sudo
|
Depends: coreutils, sudo, sed, hostname, postfix, git, postgresql-client
|
||||||
Description: script for Evolix maintenance
|
Description: script for Evolix maintenance
|
||||||
This package contains the script evomaintenance.sh
|
This package contains the script evomaintenance.sh
|
||||||
which sends a mail for each intervention on a Pack Evolix server.
|
which sends a mail for each intervention on a Pack Evolix server.
|
||||||
|
|
||||||
|
|
1
debian/rules
vendored
1
debian/rules
vendored
|
@ -24,7 +24,6 @@ install: build
|
||||||
dh_installdirs
|
dh_installdirs
|
||||||
install -m 700 -d $(CURDIR)/debian/evomaintenance/usr/share/scripts
|
install -m 700 -d $(CURDIR)/debian/evomaintenance/usr/share/scripts
|
||||||
install -m 700 evomaintenance.sh $(CURDIR)/debian/evomaintenance/usr/share/scripts/
|
install -m 700 evomaintenance.sh $(CURDIR)/debian/evomaintenance/usr/share/scripts/
|
||||||
install -m 600 evomaintenance.tpl $(CURDIR)/debian/evomaintenance/usr/share/scripts/
|
|
||||||
install -m 755 -d $(CURDIR)/debian/evomaintenance/etc
|
install -m 755 -d $(CURDIR)/debian/evomaintenance/etc
|
||||||
install -m 600 evomaintenance.cf $(CURDIR)/debian/evomaintenance/etc/
|
install -m 600 evomaintenance.cf $(CURDIR)/debian/evomaintenance/etc/
|
||||||
# Build architecture-independent files here.
|
# Build architecture-independent files here.
|
||||||
|
|
|
@ -13,3 +13,5 @@ FULLFROM="John Doe <jdoe@example.com>"
|
||||||
URGENCYFROM=mama.doe@example.com
|
URGENCYFROM=mama.doe@example.com
|
||||||
URGENCYTEL="06.00.00.00.00"
|
URGENCYTEL="06.00.00.00.00"
|
||||||
REALM=example.com
|
REALM=example.com
|
||||||
|
API_ENDPOINT=https://example.com/api/
|
||||||
|
API_KEY=secretkey
|
||||||
|
|
862
evomaintenance.sh
Normal file → Executable file
862
evomaintenance.sh
Normal file → Executable file
|
@ -1,76 +1,838 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
# EvoMaintenance script
|
VERSION="24.05"
|
||||||
# Dependencies (all OS): git postgresql-client
|
|
||||||
# Dependencies (Debian): sudo
|
|
||||||
|
|
||||||
# version 0.2
|
show_version() {
|
||||||
# Copyright 2007-2008 Gregory Colpart <reg@evolix.fr>, Evolix <info@evolix.fr>
|
cat <<END
|
||||||
|
evomaintenance version ${VERSION}
|
||||||
|
|
||||||
SYSTEME=$(uname -s)
|
Copyright 2007-2024 Evolix <info@evolix.fr>,
|
||||||
|
Gregory Colpart <reg@evolix.fr>,
|
||||||
|
Jérémy Lecour <jlecour@evolix.fr>,
|
||||||
|
Brice Waegeneire <bwaegeneire@evolix.fr>,
|
||||||
|
Mathieu Trossevin <mtrossevin@evolix.fr>
|
||||||
|
and others.
|
||||||
|
|
||||||
test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf
|
evomaintenance comes with ABSOLUTELY NO WARRANTY. This is free software,
|
||||||
|
and you are welcome to redistribute it under certain conditions.
|
||||||
|
See the GNU General Public Licence for details.
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
[ $HOSTNAME ] || HOSTNAME=$(hostname)
|
show_help() {
|
||||||
[ $EVOMAINTMAIL ] || EVOMAINTMAIL=evomaintenance-$(echo $HOSTNAME | cut -d- -f1)@$REALM
|
cat <<END
|
||||||
|
evomaintenance is a program that helps reporting what you've done on a server
|
||||||
|
|
||||||
PATH=$PATH:/usr/sbin
|
Usage: evomaintenance
|
||||||
SENDMAIL=$(which sendmail)
|
or evomaintenance --message="add new host"
|
||||||
# get ipaddress
|
or evomaintenance --no-api --no-mail --no-commit
|
||||||
|
or echo "add new vhost" | evomaintenance
|
||||||
|
|
||||||
BEGIN_YEAR=$(date "+%Y")
|
Options
|
||||||
|
-m, --message=MESSAGE set the message from the command line
|
||||||
|
--mail enable the mail hook (default)
|
||||||
|
--no-mail disable the mail hook
|
||||||
|
--db enable the database hook
|
||||||
|
--no-db disable the database hook (default)
|
||||||
|
--api enable the API hook (default)
|
||||||
|
--no-api disable the API hook
|
||||||
|
--commit enable the commit hook (default)
|
||||||
|
--no-commit disable the commit hook
|
||||||
|
--evocheck enable evocheck execution (default)
|
||||||
|
--no-evocheck disable evocheck execution
|
||||||
|
--auto use "auto" mode
|
||||||
|
--no-auto use "manual" mode (default)
|
||||||
|
-u, --user=USER force USER value (default: logname(1))
|
||||||
|
-v, --verbose increase verbosity
|
||||||
|
-n, --dry-run actions are not executed
|
||||||
|
--help print this message and exit
|
||||||
|
-V, --version print version and exit
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
if [ $SYSTEME = "Linux" ]; then
|
syslog() {
|
||||||
IP=$(LC_ALL=C who | grep $(ps -o tty= | tail -1) | tr -s ' ' | cut -d" " -f6 | sed -e "s/^(// ; s/)$//")
|
if [ -x "${LOGGER_BIN}" ]; then
|
||||||
BEGIN_DATE=$(LC_ALL=C who | grep $(ps -o tty= | tail -1) | tr -s ' ' | cut -d" " -f3,4,5)
|
${LOGGER_BIN} -t "evomaintenance" "$1"
|
||||||
_USER=$SUDO_USER
|
fi
|
||||||
elif [ $SYSTEME = "OpenBSD" ]; then
|
}
|
||||||
IP=$(LC_ALL=C who | grep $(env | grep SSH_TTY | cut -d"/" -f3) | tr -s ' ' | cut -d" " -f6 | sed -e "s/^(// ; s/)$//")
|
|
||||||
BEGIN_DATE=$(LC_ALL=C who | grep $(env | grep SSH_TTY | cut -d"/" -f3) | tr -s ' ' | cut -d" " -f3,4,5)
|
get_system() {
|
||||||
_USER=$USER
|
uname -s
|
||||||
|
}
|
||||||
|
|
||||||
|
get_fqdn() {
|
||||||
|
if [ "$(get_system)" = "Linux" ]; then
|
||||||
|
hostname --fqdn
|
||||||
|
elif [ "$(get_system)" = "OpenBSD" ]; then
|
||||||
|
hostname
|
||||||
else
|
else
|
||||||
echo "OS not detected!"
|
echo "OS not detected!"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# end_date
|
get_tty() {
|
||||||
#now()
|
if [ "$(get_system)" = "Linux" ]; then
|
||||||
|
ps -o tty= | tail -1
|
||||||
|
elif [ "$(get_system)" = "OpenBSD" ]; then
|
||||||
|
env | grep SSH_TTY | cut -d"/" -f3
|
||||||
|
else
|
||||||
|
echo "OS not detected!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# details
|
get_who() {
|
||||||
echo "Please, enter details about your maintenance"
|
who=$(LC_ALL=C who -m | tr -s ' ')
|
||||||
read TEXTE
|
|
||||||
|
|
||||||
if [ "$TEXTE" = "" ]; then
|
if [ -n "${who}" ]; then
|
||||||
|
echo "${who}"
|
||||||
|
else
|
||||||
|
LC_ALL=C who | grep "$(get_tty)" | tr -s ' '
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_begin_date() {
|
||||||
|
# XXX A begin date isn't applicable when used in autosysadmin, so we
|
||||||
|
# use the same date as the end date.
|
||||||
|
if is_autosysadmin; then
|
||||||
|
get_end_date
|
||||||
|
else
|
||||||
|
printf "%s %s" "$(date "+%Y")" "$(get_who | cut -d" " -f3,4,5)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_ip() {
|
||||||
|
ip=$(get_who | cut -d" " -f6 | sed -e "s/^(// ; s/)$//")
|
||||||
|
if is_autosysadmin || [ "${ip}" = ":0" ]; then
|
||||||
|
ip="localhost"
|
||||||
|
elif [ -z "${ip}" ]; then
|
||||||
|
ip="unknown (no tty)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${ip}"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_end_date() {
|
||||||
|
date +"%Y %b %d %H:%M"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_now() {
|
||||||
|
date +"%Y-%m-%dT%H:%M:%S%z"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_user() {
|
||||||
|
if [ -n "${FORCE_USER}" ]; then
|
||||||
|
echo "${FORCE_USER}"
|
||||||
|
else
|
||||||
|
logname
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_complete_hostname() {
|
||||||
|
REAL_HOSTNAME=$(get_fqdn)
|
||||||
|
if [ "${HOSTNAME}" = "${REAL_HOSTNAME}" ]; then
|
||||||
|
echo "${HOSTNAME}"
|
||||||
|
else
|
||||||
|
echo "${HOSTNAME} (${REAL_HOSTNAME})"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_repository_status() {
|
||||||
|
dir=$1
|
||||||
|
# tell Git where to find the repository and the work tree (no need to `cd …` there)
|
||||||
|
export GIT_DIR="${dir}/.git" GIT_WORK_TREE="${dir}"
|
||||||
|
# If the repository and the work tree exist, try to commit changes
|
||||||
|
if [ -d "${GIT_DIR}" ] && [ -d "${GIT_WORK_TREE}" ]; then
|
||||||
|
CHANGED_LINES=$(${GIT_BIN} status --porcelain | wc -l | tr -d ' ')
|
||||||
|
if [ "${CHANGED_LINES}" != "0" ]; then
|
||||||
|
STATUS=$(${GIT_BIN} status --short | tail -n ${GIT_STATUS_MAX_LINES})
|
||||||
|
printf "%s\n%s\n" "${GIT_DIR} (last ${GIT_STATUS_MAX_LINES} lines)" "${STATUS}" | sed -e '/^$/d'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# unset environment variables to prevent accidental influence on other git commands
|
||||||
|
unset GIT_DIR GIT_WORK_TREE
|
||||||
|
}
|
||||||
|
|
||||||
|
get_evocheck() {
|
||||||
|
if [ -x "${EVOCHECK_BIN}" ]; then
|
||||||
|
printf "Evocheck status :"
|
||||||
|
EVOCHECK_OUT=$(${EVOCHECK_BIN})
|
||||||
|
EVOCHECK_RC=$?
|
||||||
|
|
||||||
|
if [ "${EVOCHECK_RC}" = "0" ] && [ -z "${EVOCHECK_OUT}" ]; then
|
||||||
|
printf " OK\n\n"
|
||||||
|
else
|
||||||
|
printf " ERROR\n%s\n\n" "${EVOCHECK_OUT}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_log() {
|
||||||
|
printf "*********** %s ***************\n" "$(get_now)"
|
||||||
|
print_session_data
|
||||||
|
printf "Hooks : commit=%s db=%s api=%s mail=%s\n"\
|
||||||
|
"${HOOK_COMMIT}" "${HOOK_DB}" "${HOOK_API}" "${HOOK_MAIL}"
|
||||||
|
if [ "${HOOK_MAIL}" = "1" ]; then
|
||||||
|
printf "Mailto : %s\n" "${EVOMAINTMAIL}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_session_data() {
|
||||||
|
printf "Host : %s\n" "${HOSTNAME_TEXT}"
|
||||||
|
printf "User : %s\n" "${USER}"
|
||||||
|
printf "IP : %s\n" "${IP}"
|
||||||
|
printf "Begin : %s\n" "${BEGIN_DATE}"
|
||||||
|
printf "End : %s\n" "${END_DATE}"
|
||||||
|
printf "Message : %s\n" "${MESSAGE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_autosysadmin() {
|
||||||
|
test "${USER}" = "autosysadmin"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_repository_readonly() {
|
||||||
|
if [ "$(get_system)" = "OpenBSD" ]; then
|
||||||
|
partition=$(stat -f '%Sd' $1)
|
||||||
|
mount | grep ${partition} | grep -q "read-only"
|
||||||
|
elif command -v findmnt >/dev/null; then
|
||||||
|
mountpoint=$(stat -c '%m' $1)
|
||||||
|
findmnt ${mountpoint} --noheadings --output OPTIONS -O ro
|
||||||
|
else
|
||||||
|
grep /usr /proc/mounts | grep -E '\bro\b'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
remount_repository_readwrite() {
|
||||||
|
if [ "$(get_system)" = "OpenBSD" ]; then
|
||||||
|
partition=$(stat -f '%Sd' $1)
|
||||||
|
mount -u -w /dev/${partition} 2>/dev/null
|
||||||
|
else
|
||||||
|
mountpoint=$(stat -c '%m' $1)
|
||||||
|
mount -o remount,rw ${mountpoint}
|
||||||
|
syslog "Re-mount ${mountpoint} as read-write to commit in repository $1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
remount_repository_readonly() {
|
||||||
|
if [ "$(get_system)" = "OpenBSD" ]; then
|
||||||
|
partition=$(stat -f '%Sd' $1)
|
||||||
|
mount -u -r /dev/${partition} 2>/dev/null
|
||||||
|
else
|
||||||
|
mountpoint=$(stat -c '%m' $1)
|
||||||
|
mount -o remount,ro ${mountpoint} 2>/dev/null
|
||||||
|
syslog "Re-mount ${mountpoint} as read-only after commit to repository $1"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hook_commit() {
|
||||||
|
if [ -x "${GIT_BIN}" ]; then
|
||||||
|
# loop on possible directories managed by GIT
|
||||||
|
for dir in ${GIT_REPOSITORIES}; do
|
||||||
|
# tell Git where to find the repository and the work tree (no need to `cd …` there)
|
||||||
|
export GIT_DIR="${dir}/.git" GIT_WORK_TREE="${dir}"
|
||||||
|
# reset variable used to track if a mount point is readonly
|
||||||
|
READONLY_ORIG=0
|
||||||
|
# If the repository and the work tree exist, try to commit changes
|
||||||
|
if [ -d "${GIT_DIR}" ] && [ -d "${GIT_WORK_TREE}" ]; then
|
||||||
|
CHANGED_LINES=$(${GIT_BIN} status --porcelain | wc -l | tr -d ' ')
|
||||||
|
if [ "${CHANGED_LINES}" != "0" ]; then
|
||||||
|
if [ "${DRY_RUN}" = "1" ]; then
|
||||||
|
# STATS_SHORT=$(${GIT_BIN} diff --stat | tail -1)
|
||||||
|
STATS=$(${GIT_BIN} diff --stat | tail -n ${GIT_STATUS_MAX_LINES})
|
||||||
|
# GIT_COMMITS_SHORT=$(printf "%s\n%s : %s" "${GIT_COMMITS_SHORT}" "${GIT_DIR}" "${STATS_SHORT}" | sed -e '/^$/d')
|
||||||
|
GIT_COMMITS=$(printf "%s\n%s\n%s" "${GIT_COMMITS}" "${GIT_DIR}" "${STATS}" | sed -e '/^$/d')
|
||||||
|
else
|
||||||
|
# remount mount point read-write if currently readonly
|
||||||
|
is_repository_readonly ${dir} && { READONLY_ORIG=1; remount_repository_readwrite ${dir}; }
|
||||||
|
# commit changes
|
||||||
|
${GIT_BIN} add --all
|
||||||
|
${GIT_BIN} commit --message "${MESSAGE}" --author="${USER} <${USER}@evolix.net>" --quiet
|
||||||
|
# remount mount point read-only if it was before
|
||||||
|
test "$READONLY_ORIG" = "1" && remount_repository_readonly ${dir}
|
||||||
|
# Add the SHA to the log file if something has been committed
|
||||||
|
SHA=$(${GIT_BIN} rev-parse --short HEAD)
|
||||||
|
# STATS_SHORT=$(${GIT_BIN} show --stat | tail -1)
|
||||||
|
STATS=$(${GIT_BIN} show --stat --pretty=format:"" | tail -n ${GIT_STATUS_MAX_LINES})
|
||||||
|
# append commit data, without empty lines
|
||||||
|
# GIT_COMMITS_SHORT=$(printf "%s\n%s : %s –%s" "${GIT_COMMITS_SHORT}" "${GIT_DIR}" "${SHA}" "${STATS_SHORT}" | sed -e '/^$/d')
|
||||||
|
GIT_COMMITS=$(printf "%s\n%s : %s\n%s" "${GIT_COMMITS}" "${GIT_DIR}" "${SHA}" "${STATS}" | sed -e '/^$/d')
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# unset environment variables to prevent accidental influence on other git commands
|
||||||
|
unset GIT_DIR GIT_WORK_TREE
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "${GIT_COMMITS}" ]; then
|
||||||
|
# if [ "${VERBOSE}" = "1" ]; then
|
||||||
|
printf "\n********** Commits ****************\n%s\n***********************************\n" "${GIT_COMMITS}"
|
||||||
|
# fi
|
||||||
|
if [ "${DRY_RUN}" != "1" ]; then
|
||||||
|
echo "${GIT_COMMITS}" >> "${LOGFILE}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hook_db() {
|
||||||
|
SQL_DETAILS=$(echo "${MESSAGE}" | sed "s/'/''/g")
|
||||||
|
PG_QUERY="INSERT INTO evomaint(hostname,userid,ipaddress,begin_date,end_date,details) VALUES ('${HOSTNAME}','${USER}','${IP}','${BEGIN_DATE}',now(),'${SQL_DETAILS}')"
|
||||||
|
|
||||||
|
if [ "${VERBOSE}" = "1" ]; then
|
||||||
|
printf "\n********** DB query **************\n%s\n***********************************\n" "${PG_QUERY}"
|
||||||
|
fi
|
||||||
|
if [ "${DRY_RUN}" != "1" ] && [ -x "${PSQL_BIN}" ]; then
|
||||||
|
echo "${PG_QUERY}" | ${PSQL_BIN} "${PGDB}" "${PGTABLE}" -h "${PGHOST}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hook_api() {
|
||||||
|
if [ "${VERBOSE}" = "1" ]; then
|
||||||
|
printf "\n********** API call **************\n"
|
||||||
|
printf "curl -f -s -S -X POST [REDACTED] -k -F api_key=[REDACTED] -F action=insertEvoMaintenance -F hostname=%s -F userid=%s -F ipaddress=%s -F begin_date=%s -F end_date='now()' -F details=%s" \
|
||||||
|
"${HOSTNAME}" "${USER}" "${IP}" "${BEGIN_DATE}" "${MESSAGE}"
|
||||||
|
printf "\n***********************************\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${DRY_RUN}" != "1" ] && [ -x "${CURL_BIN}" ]; then
|
||||||
|
API_RETURN_STATUS=$(curl -f -s -S -X POST \
|
||||||
|
"${API_ENDPOINT}" -k \
|
||||||
|
-F api_key="${API_KEY}" \
|
||||||
|
-F action=insertEvoMaintenance \
|
||||||
|
-F hostname="${HOSTNAME}" \
|
||||||
|
-F userid="${USER}" \
|
||||||
|
-F ipaddress="${IP}" \
|
||||||
|
-F begin_date="${BEGIN_DATE}" \
|
||||||
|
-F end_date='now()' \
|
||||||
|
-F details="${MESSAGE}")
|
||||||
|
|
||||||
|
# either cURL or the API backend can throw an error, otherwise it returns this JSON response
|
||||||
|
if [ "$API_RETURN_STATUS" = '{"status":"Ok"}' ]; then
|
||||||
|
echo "API call OK."
|
||||||
|
else
|
||||||
|
echo "API call FAILED."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
format_mail() {
|
||||||
|
cat <<EOTEMPLATE
|
||||||
|
From: ${FULLFROM}
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
X-Evomaintenance-Version: ${VERSION}
|
||||||
|
X-Evomaintenance-Host: ${HOSTNAME_TEXT}
|
||||||
|
X-Evomaintenance-User: ${USER}
|
||||||
|
To: ${EVOMAINTMAIL}
|
||||||
|
Subject: [evomaintenance] Intervention sur ${HOSTNAME_TEXT} (${USER})
|
||||||
|
|
||||||
|
Bonjour,
|
||||||
|
|
||||||
|
Une intervention vient de se terminer sur votre serveur.
|
||||||
|
|
||||||
|
Nom du serveur : ${HOSTNAME_TEXT}
|
||||||
|
Personne ayant réalisée l'intervention : ${USER}
|
||||||
|
Intervention réalisée depuis : ${IP}
|
||||||
|
Début de l'intervention : ${BEGIN_DATE}
|
||||||
|
Fin de l'intervention : ${END_DATE}
|
||||||
|
|
||||||
|
### Renseignements sur l'intervention
|
||||||
|
${MESSAGE}
|
||||||
|
###
|
||||||
|
|
||||||
|
EOTEMPLATE
|
||||||
|
|
||||||
|
if [ -n "${GIT_COMMITS}" ]; then
|
||||||
|
cat << EOTEMPLATE
|
||||||
|
### Commits
|
||||||
|
${GIT_COMMITS}
|
||||||
|
###
|
||||||
|
|
||||||
|
EOTEMPLATE
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<EOTEMPLATE
|
||||||
|
Pour réagir à cette intervention, vous pouvez répondre à ce message
|
||||||
|
(sur l'adresse mail ${FROM}). En cas d'urgence, utilisez
|
||||||
|
l'adresse ${URGENCYFROM} ou notre téléphone portable d'astreinte
|
||||||
|
(${URGENCYTEL})
|
||||||
|
|
||||||
|
Cordialement,
|
||||||
|
--
|
||||||
|
${FULLFROM}
|
||||||
|
EOTEMPLATE
|
||||||
|
}
|
||||||
|
|
||||||
|
hook_mail() {
|
||||||
|
MAIL_CONTENT=$(format_mail)
|
||||||
|
|
||||||
|
if [ "${VERBOSE}" = "1" ]; then
|
||||||
|
printf "\n********** Mail *******************\n%s\n***********************************\n" "${MAIL_CONTENT}"
|
||||||
|
fi
|
||||||
|
if [ "${DRY_RUN}" != "1" ] && [ -x "${SENDMAIL_BIN}" ]; then
|
||||||
|
echo "${MAIL_CONTENT}" | ${SENDMAIL_BIN} -oi -t -f "${FROM}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hook_log() {
|
||||||
|
if [ "${VERBOSE}" = "1" ]; then
|
||||||
|
print_log
|
||||||
|
fi
|
||||||
|
if [ "${DRY_RUN}" != "1" ]; then
|
||||||
|
print_log >> "${LOGFILE}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# load configuration if present.
|
||||||
|
test -f /etc/evomaintenance.cf && . /etc/evomaintenance.cf
|
||||||
|
|
||||||
|
HOSTNAME=${HOSTNAME:-$(get_fqdn)}
|
||||||
|
EVOMAINTMAIL=${EVOMAINTMAIL:-"evomaintenance-$(echo "${HOSTNAME}" | cut -d- -f1)@${REALM}"}
|
||||||
|
LOGFILE=${LOGFILE:-"/var/log/evomaintenance.log"}
|
||||||
|
HOOK_COMMIT=${HOOK_COMMIT:-"1"}
|
||||||
|
HOOK_DB=${HOOK_DB:-"0"}
|
||||||
|
HOOK_API=${HOOK_API:-"1"}
|
||||||
|
HOOK_MAIL=${HOOK_MAIL:-"1"}
|
||||||
|
DRY_RUN=${DRY_RUN:-"0"}
|
||||||
|
VERBOSE=${VERBOSE:-"0"}
|
||||||
|
AUTO=${AUTO:-"0"}
|
||||||
|
EVOCHECK=${EVOCHECK:-"0"}
|
||||||
|
GIT_STATUS_MAX_LINES=${GIT_STATUS_MAX_LINES:-20}
|
||||||
|
API_ENDPOINT=${API_ENDPOINT:-""}
|
||||||
|
FORCE_USER=${FORCE_USER:-""}
|
||||||
|
|
||||||
|
# initialize variables
|
||||||
|
MESSAGE=""
|
||||||
|
# GIT_COMMITS_SHORT=""
|
||||||
|
GIT_COMMITS=""
|
||||||
|
|
||||||
|
# Parse options
|
||||||
|
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
|
||||||
|
while :; do
|
||||||
|
case $1 in
|
||||||
|
-h|-\?|--help)
|
||||||
|
show_help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-V|--version)
|
||||||
|
show_version
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-m|--message)
|
||||||
|
# message options, with value speparated by space
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
MESSAGE=$2
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--message=?*)
|
||||||
|
# message options, with value speparated by =
|
||||||
|
MESSAGE=${1#*=}
|
||||||
|
;;
|
||||||
|
--message=)
|
||||||
|
# message options, without value
|
||||||
|
printf 'ERROR: "--message" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
--no-evocheck)
|
||||||
|
# disable evocheck hook
|
||||||
|
EVOCHECK=0
|
||||||
|
;;
|
||||||
|
--evocheck)
|
||||||
|
# enable evocheck hook
|
||||||
|
EVOCHECK=1
|
||||||
|
;;
|
||||||
|
--no-commit)
|
||||||
|
# disable commit hook
|
||||||
|
HOOK_COMMIT=0
|
||||||
|
;;
|
||||||
|
--commit)
|
||||||
|
# enable commit hook
|
||||||
|
HOOK_COMMIT=1
|
||||||
|
;;
|
||||||
|
--no-db)
|
||||||
|
# disable DB hook
|
||||||
|
HOOK_DB=0
|
||||||
|
;;
|
||||||
|
--db)
|
||||||
|
# enable DB hook
|
||||||
|
HOOK_DB=1
|
||||||
|
;;
|
||||||
|
--no-api)
|
||||||
|
# disable API hook
|
||||||
|
HOOK_API=0
|
||||||
|
;;
|
||||||
|
--api)
|
||||||
|
# enable API hook
|
||||||
|
HOOK_API=1
|
||||||
|
;;
|
||||||
|
--no-mail)
|
||||||
|
# disable mail hook
|
||||||
|
HOOK_MAIL=0
|
||||||
|
;;
|
||||||
|
--mail)
|
||||||
|
# enable mail hook
|
||||||
|
HOOK_MAIL=1
|
||||||
|
;;
|
||||||
|
--no-auto)
|
||||||
|
# use "manual" mode
|
||||||
|
AUTO=0
|
||||||
|
;;
|
||||||
|
--auto)
|
||||||
|
# use "auto" mode
|
||||||
|
AUTO=1
|
||||||
|
;;
|
||||||
|
--autosysadmin)
|
||||||
|
# Deprecated, backward compatibility
|
||||||
|
# author change as autosysadmin
|
||||||
|
printf 'WARNING: "--autosysadmin" is deprecated, use "--user autosysadmin".\n' >&2
|
||||||
|
FORCE_USER="autosysadmin"
|
||||||
|
;;
|
||||||
|
-u|--user)
|
||||||
|
# user options, with value speparated by space
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
FORCE_USER=$2
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
printf 'ERROR: "--user" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
--user=?*)
|
||||||
|
# message options, with value speparated by =
|
||||||
|
FORCE_USER=${1#*=}
|
||||||
|
;;
|
||||||
|
--user=)
|
||||||
|
# message options, without value
|
||||||
|
printf 'ERROR: "--user" requires a non-empty option argument.\n' >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
-n|--dry-run)
|
||||||
|
# disable actual commands
|
||||||
|
DRY_RUN=1
|
||||||
|
;;
|
||||||
|
-v|--verbose)
|
||||||
|
# print verbose information
|
||||||
|
VERBOSE=1
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
# End of all options.
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
-?*|[[:alnum:]]*)
|
||||||
|
# ignore unknown options
|
||||||
|
printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Default case: If no more options then break out of the loop.
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Treat unset variables as an error when substituting.
|
||||||
|
# Only after this line, because some config variables might be missing.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
# Gather information
|
||||||
|
HOSTNAME_TEXT=$(get_complete_hostname)
|
||||||
|
# TTY=$(get_tty)
|
||||||
|
# WHO=$(get_who)
|
||||||
|
IP=$(get_ip)
|
||||||
|
BEGIN_DATE=$(get_begin_date)
|
||||||
|
END_DATE=$(get_end_date)
|
||||||
|
USER=$(get_user)
|
||||||
|
|
||||||
|
PATH=${PATH}:/usr/sbin
|
||||||
|
|
||||||
|
SENDMAIL_BIN=$(command -v sendmail)
|
||||||
|
readonly SENDMAIL_BIN
|
||||||
|
if [ "${HOOK_MAIL}" = "1" ] && [ -z "${SENDMAIL_BIN}" ]; then
|
||||||
|
echo "No \`sendmail' command has been found, can't send mail." 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
GIT_BIN=$(command -v git)
|
||||||
|
readonly GIT_BIN
|
||||||
|
if [ "${HOOK_COMMIT}" = "1" ] && [ -z "${GIT_BIN}" ]; then
|
||||||
|
echo "No \`git' command has been found, can't commit changes" 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PSQL_BIN=$(command -v psql)
|
||||||
|
readonly PSQL_BIN
|
||||||
|
if [ "${HOOK_DB}" = "1" ] && [ -z "${PSQL_BIN}" ]; then
|
||||||
|
echo "No \`psql' command has been found, can't save to the database." 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
CURL_BIN=$(command -v curl)
|
||||||
|
readonly CURL_BIN
|
||||||
|
if [ "${HOOK_API}" = "1" ] && [ -z "${CURL_BIN}" ]; then
|
||||||
|
echo "No \`curl' command has been found, can't call the API." 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOGGER_BIN=$(command -v logger)
|
||||||
|
readonly LOGGER_BIN
|
||||||
|
|
||||||
|
if [ "${HOOK_API}" = "1" ] && [ -z "${API_ENDPOINT}" ]; then
|
||||||
|
echo "No API endpoint specified, can't call the API." 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
EVOCHECK_BIN="/usr/share/scripts/evocheck.sh"
|
||||||
|
|
||||||
|
GIT_REPOSITORIES="/etc /etc/bind /usr/share/scripts"
|
||||||
|
|
||||||
|
# Add /etc directories from lxc containers if they are git directories
|
||||||
|
if [ -d /var/lib/lxc ]; then
|
||||||
|
GIT_REPOSITORIES="${GIT_REPOSITORIES} $(find -L /var/lib/lxc/ -maxdepth 3 -name 'etc' | tr '\n' ' ' | sed 's/[[:space:]]\+$//')"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# initialize variable
|
||||||
|
GIT_STATUSES=""
|
||||||
|
# git statuses
|
||||||
|
if [ -x "${GIT_BIN}" ]; then
|
||||||
|
# loop on possible directories managed by GIT
|
||||||
|
for dir in ${GIT_REPOSITORIES}; do
|
||||||
|
RESULT=$(get_repository_status "${dir}")
|
||||||
|
if [ -n "${RESULT}" ]; then
|
||||||
|
# append diff data, without empty lines
|
||||||
|
GIT_STATUSES=$(printf "%s\n%s\n" "${GIT_STATUSES}" "${RESULT}" | sed -e '/^$/d')
|
||||||
|
fi
|
||||||
|
unset RESULT
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# find out if running in interactive mode, or not
|
||||||
|
if [ -t 0 ]; then
|
||||||
|
INTERACTIVE=1
|
||||||
|
else
|
||||||
|
INTERACTIVE=0
|
||||||
|
fi
|
||||||
|
readonly INTERACTIVE
|
||||||
|
|
||||||
|
if [ "${INTERACTIVE}" = "1" ] && [ "${EVOCHECK}" = "1" ]; then
|
||||||
|
get_evocheck
|
||||||
|
fi
|
||||||
|
if [ -n "${GIT_STATUSES}" ] && [ "${INTERACTIVE}" = "1" ]; then
|
||||||
|
printf "/!\\\ There are some uncommited changes.\n%s\n\n" "${GIT_STATUSES}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${MESSAGE}" ]; then
|
||||||
|
if [ "${INTERACTIVE}" = "1" ]; then
|
||||||
|
printf "> Please, enter details about your maintenance:\n"
|
||||||
|
fi
|
||||||
|
read -r MESSAGE
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${MESSAGE}" ]; then
|
||||||
echo "no value..."
|
echo "no value..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SQL
|
print_session_data
|
||||||
TEXTE=`echo "$TEXTE" | sed "s/'/\\\\\\'/g ; s@/@\\\\\/@g ; s@\\&@et@g"`
|
|
||||||
|
|
||||||
# recapitulatif
|
if [ "${INTERACTIVE}" = "1" ] && [ "${AUTO}" = "0" ]; then
|
||||||
echo "hostname = $HOSTNAME"
|
if [ "${HOOK_COMMIT}" = "1" ] || [ "${HOOK_MAIL}" = "1" ] || [ "${HOOK_DB}" = "1" ]; then
|
||||||
echo "user = $_USER"
|
printf "\nActions to execute:\n"
|
||||||
echo "IP = $IP"
|
if [ "${HOOK_COMMIT}" = "1" ]; then
|
||||||
echo "begin date = $BEGIN_YEAR $BEGIN_DATE"
|
printf "* commit changes in repositories\n"
|
||||||
echo "details = $TEXTE"
|
fi
|
||||||
|
if [ "${HOOK_MAIL}" = "1" ]; then
|
||||||
|
printf "* send mail to %s\n" "${EVOMAINTMAIL}"
|
||||||
|
fi
|
||||||
|
if [ "${HOOK_DB}" = "1" ]; then
|
||||||
|
printf "* save metadata to the database\n"
|
||||||
|
fi
|
||||||
|
if [ "${HOOK_API}" = "1" ]; then
|
||||||
|
printf "* send metadata to the API\n"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
echo "To cancel, [Ctrl+C] else press [Enter]"
|
answer=""
|
||||||
read enter
|
while :; do
|
||||||
|
printf "> Let's continue? [Y,n,i,?] "
|
||||||
# git stuff
|
read -r answer
|
||||||
if (test -x /usr/bin/git); then
|
case $answer in
|
||||||
cd /etc/
|
[Yy]|"" )
|
||||||
git add .
|
# force "auto" mode, but keep hooks settings
|
||||||
GIT_COMMIT=$(git commit -a -m "$TEXTE" --author="$_USER <$_USER@evolix.net>")
|
AUTO=1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Nn] )
|
||||||
|
# force "auto" mode, and disable all hooks
|
||||||
|
HOOK_COMMIT=0
|
||||||
|
HOOK_MAIL=0
|
||||||
|
HOOK_DB=0
|
||||||
|
HOOK_API=0
|
||||||
|
AUTO=1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Ii] )
|
||||||
|
# force "manual" mode
|
||||||
|
AUTO=0
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
printf "y - yes, execute actions and exit\n"
|
||||||
|
printf "n - no, don't execute actions and exit\n"
|
||||||
|
printf "i - switch to interactive mode\n"
|
||||||
|
printf "? - print this help\n"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "
|
if [ "${INTERACTIVE}" = "1" ] && [ "${AUTO}" = "0" ]; then
|
||||||
INSERT INTO evomaint(hostname,userid,ipaddress,begin_date,end_date,details)
|
# Commit hook
|
||||||
VALUES ('$HOSTNAME','$_USER','$IP','$BEGIN_YEAR $BEGIN_DATE',now(),'$TEXTE') " | \
|
if [ -n "${GIT_STATUSES}" ] && [ "${HOOK_COMMIT}" = "1" ]; then
|
||||||
psql $PGDB $PGTABLE -h $PGHOST
|
printf "/!\ There are some uncommited changes.\n%s\n\n" "${GIT_STATUSES}"
|
||||||
|
|
||||||
cat /usr/share/scripts/evomaintenance.tpl | \
|
y="Y"; n="n"
|
||||||
sed -e "s/__TO__/$EVOMAINTMAIL/ ; s/__HOSTNAME__/$HOSTNAME/ ; s/__USER__/$_USER/ ; s/__BEGIN_DATE__/$BEGIN_YEAR $BEGIN_DATE/ ; s/__TEXTE__/$TEXTE/ ; s/__IP__/$IP/ ; s/__FULLFROM__/$FULLFROM/ ; s/__FROM__/$FROM/ ; s/__URGENCYFROM__/$URGENCYFROM/ ; s/__URGENCYTEL__/$URGENCYTEL/"| \
|
answer=""
|
||||||
$SENDMAIL -oi -t -f $FROM
|
while :; do
|
||||||
|
printf "> Do you want to commit the changes? [%s] " "${y},${n}"
|
||||||
|
read -r answer
|
||||||
|
case $answer in
|
||||||
|
[Yy] )
|
||||||
|
hook_commit;
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Nn] )
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"" )
|
||||||
|
if [ "${HOOK_COMMIT}" = "1" ]; then
|
||||||
|
hook_commit
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
echo "answer with a valid choice"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Mail hook
|
||||||
|
if [ "${HOOK_MAIL}" = "1" ]; then
|
||||||
|
y="Y"; n="n"
|
||||||
|
else
|
||||||
|
y="y"; n="N"
|
||||||
|
fi
|
||||||
|
answer=""
|
||||||
|
while :; do
|
||||||
|
printf "> Do you want to send an email to <%s>? [%s] " "${EVOMAINTMAIL}" "${y},${n},e"
|
||||||
|
read -r answer
|
||||||
|
case $answer in
|
||||||
|
[Yy] )
|
||||||
|
hook_mail;
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Nn] )
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Ee] )
|
||||||
|
printf "> To: [%s] " "${EVOMAINTMAIL}"
|
||||||
|
read -r mail_recipient
|
||||||
|
if [ -n "${mail_recipient}" ]; then
|
||||||
|
EVOMAINTMAIL="${mail_recipient}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"" )
|
||||||
|
if [ "${HOOK_MAIL}" = "1" ]; then
|
||||||
|
hook_mail
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
echo "answer with a valid choice"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Database hook
|
||||||
|
if [ "${HOOK_DB}" = "1" ]; then
|
||||||
|
y="Y"; n="n"
|
||||||
|
else
|
||||||
|
y="y"; n="N"
|
||||||
|
fi
|
||||||
|
answer=""
|
||||||
|
while :; do
|
||||||
|
printf "> Do you want to insert your message into the database? [%s] " "${y},${n}"
|
||||||
|
read -r answer
|
||||||
|
case $answer in
|
||||||
|
[Yy] )
|
||||||
|
hook_db;
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Nn] )
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"" )
|
||||||
|
if [ "${HOOK_DB}" = "1" ]; then
|
||||||
|
hook_db
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
echo "answer with a valid choice"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# API hook
|
||||||
|
if [ "${HOOK_API}" = "1" ]; then
|
||||||
|
y="Y"; n="n"
|
||||||
|
else
|
||||||
|
y="y"; n="N"
|
||||||
|
fi
|
||||||
|
answer=""
|
||||||
|
while :; do
|
||||||
|
printf "> Do you want to send the metadata to the API? [%s] " "${y},${n}"
|
||||||
|
read -r answer
|
||||||
|
case $answer in
|
||||||
|
[Yy] )
|
||||||
|
hook_api;
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
[Nn] )
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"" )
|
||||||
|
if [ "${HOOK_API}" = "1" ]; then
|
||||||
|
hook_api
|
||||||
|
fi
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
echo "answer with a valid choice"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log hook
|
||||||
|
hook_log
|
||||||
|
|
||||||
|
if [ "${INTERACTIVE}" = "0" ] || [ "${AUTO}" = "1" ]; then
|
||||||
|
if [ "${HOOK_COMMIT}" = "1" ]; then
|
||||||
|
hook_commit
|
||||||
|
fi
|
||||||
|
if [ "${HOOK_MAIL}" = "1" ]; then
|
||||||
|
hook_mail
|
||||||
|
fi
|
||||||
|
if [ "${HOOK_DB}" = "1" ]; then
|
||||||
|
hook_db
|
||||||
|
fi
|
||||||
|
if [ "${HOOK_API}" = "1" ]; then
|
||||||
|
hook_api
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
From: __FULLFROM__
|
|
||||||
Content-Type: text/plain; charset=UTF-8
|
|
||||||
MIME-Version: 1.0
|
|
||||||
Content-Transfer-Encoding: 8bit
|
|
||||||
To: __TO__
|
|
||||||
Subject: [evomaintenance] Intervention sur __HOSTNAME__ (__USER__)
|
|
||||||
|
|
||||||
Bonjour,
|
|
||||||
|
|
||||||
Une intervention vient de se terminer sur votre serveur.
|
|
||||||
Voici les renseignements sur l'intervention :
|
|
||||||
|
|
||||||
Nom du serveur : __HOSTNAME__
|
|
||||||
Personne ayant réalisée l'intervention : __USER__
|
|
||||||
Intervention réalisée depuis : __IP__
|
|
||||||
Date/heure du début de l'intervention : __BEGIN_DATE__
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
Renseignements sur l'intervention :
|
|
||||||
__TEXTE__
|
|
||||||
###
|
|
||||||
|
|
||||||
|
|
||||||
Pour réagir à cette intervention, vous pouvez répondre à ce message
|
|
||||||
(sur l'adresse mail __FROM__). En cas d'urgence, utilisez
|
|
||||||
l'adresse __URGENCYFROM__ ou notre téléphone portable d'astreinte
|
|
||||||
(__URGENCYTEL__)
|
|
||||||
|
|
||||||
Cordialement,
|
|
||||||
--
|
|
||||||
__FULLFROM__
|
|
Loading…
Reference in a new issue