mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Merge branch 'unstable' into stretch-unstable
This commit is contained in:
commit
dd8af083b3
20 changed files with 2973 additions and 683 deletions
|
@ -117,7 +117,10 @@ ynh_remove_systemd_config () {
|
||||||
|
|
||||||
# Create a dedicated nginx config
|
# Create a dedicated nginx config
|
||||||
#
|
#
|
||||||
# usage: ynh_add_nginx_config
|
# usage: ynh_add_nginx_config "list of others variables to replace"
|
||||||
|
#
|
||||||
|
# | arg: list of others variables to replace separeted by a space
|
||||||
|
# | for example : 'path_2 port_2 ...'
|
||||||
#
|
#
|
||||||
# This will use a template in ../conf/nginx.conf
|
# This will use a template in ../conf/nginx.conf
|
||||||
# __PATH__ by $path_url
|
# __PATH__ by $path_url
|
||||||
|
@ -126,8 +129,13 @@ ynh_remove_systemd_config () {
|
||||||
# __NAME__ by $app
|
# __NAME__ by $app
|
||||||
# __FINALPATH__ by $final_path
|
# __FINALPATH__ by $final_path
|
||||||
#
|
#
|
||||||
|
# And dynamic variables (from the last example) :
|
||||||
|
# __PATH_2__ by $path_2
|
||||||
|
# __PORT_2__ by $port_2
|
||||||
|
#
|
||||||
ynh_add_nginx_config () {
|
ynh_add_nginx_config () {
|
||||||
finalnginxconf="/etc/nginx/conf.d/$domain.d/$app.conf"
|
finalnginxconf="/etc/nginx/conf.d/$domain.d/$app.conf"
|
||||||
|
local others_var=${1:-}
|
||||||
ynh_backup_if_checksum_is_different "$finalnginxconf"
|
ynh_backup_if_checksum_is_different "$finalnginxconf"
|
||||||
sudo cp ../conf/nginx.conf "$finalnginxconf"
|
sudo cp ../conf/nginx.conf "$finalnginxconf"
|
||||||
|
|
||||||
|
@ -151,6 +159,22 @@ ynh_add_nginx_config () {
|
||||||
if test -n "${final_path:-}"; then
|
if test -n "${final_path:-}"; then
|
||||||
ynh_replace_string "__FINALPATH__" "$final_path" "$finalnginxconf"
|
ynh_replace_string "__FINALPATH__" "$final_path" "$finalnginxconf"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Replace all other variable given as arguments
|
||||||
|
for var_to_replace in $others_var
|
||||||
|
do
|
||||||
|
# ${var_to_replace^^} make the content of the variable on upper-cases
|
||||||
|
# ${!var_to_replace} get the content of the variable named $var_to_replace
|
||||||
|
ynh_replace_string "__${var_to_replace^^}__" "${!var_to_replace}" "$finalnginxconf"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${path_url:-}" != "/" ]
|
||||||
|
then
|
||||||
|
ynh_replace_string "^#sub_path_only" "" "$finalnginxconf"
|
||||||
|
else
|
||||||
|
ynh_replace_string "^#root_path_only" "" "$finalnginxconf"
|
||||||
|
fi
|
||||||
|
|
||||||
ynh_store_file_checksum "$finalnginxconf"
|
ynh_store_file_checksum "$finalnginxconf"
|
||||||
|
|
||||||
sudo systemctl reload nginx
|
sudo systemctl reload nginx
|
||||||
|
|
|
@ -30,7 +30,7 @@ ynh_package_version() {
|
||||||
#
|
#
|
||||||
# usage: ynh_apt update
|
# usage: ynh_apt update
|
||||||
ynh_apt() {
|
ynh_apt() {
|
||||||
DEBIAN_FRONTEND=noninteractive sudo apt-get -y -qq $@
|
DEBIAN_FRONTEND=noninteractive sudo apt-get -y $@
|
||||||
}
|
}
|
||||||
|
|
||||||
# Update package index files
|
# Update package index files
|
||||||
|
@ -167,4 +167,4 @@ EOF
|
||||||
ynh_remove_app_dependencies () {
|
ynh_remove_app_dependencies () {
|
||||||
local dep_app=${app//_/-} # Replace all '_' by '-'
|
local dep_app=${app//_/-} # Replace all '_' by '-'
|
||||||
ynh_package_autopurge ${dep_app}-ynh-deps # Remove the fake package and its dependencies if they not still used.
|
ynh_package_autopurge ${dep_app}-ynh-deps # Remove the fake package and its dependencies if they not still used.
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,16 +37,23 @@ ynh_get_plain_key() {
|
||||||
ynh_restore_upgradebackup () {
|
ynh_restore_upgradebackup () {
|
||||||
echo "Upgrade failed." >&2
|
echo "Upgrade failed." >&2
|
||||||
local app_bck=${app//_/-} # Replace all '_' by '-'
|
local app_bck=${app//_/-} # Replace all '_' by '-'
|
||||||
|
|
||||||
# Check if an existing backup can be found before removing and restoring the application.
|
NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
|
||||||
if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$backup_number
|
|
||||||
then
|
if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
|
||||||
# Remove the application then restore it
|
then
|
||||||
sudo yunohost app remove $app
|
# Check if an existing backup can be found before removing and restoring the application.
|
||||||
# Restore the backup
|
if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$backup_number
|
||||||
sudo yunohost backup restore --ignore-system $app_bck-pre-upgrade$backup_number --apps $app --force --verbose
|
then
|
||||||
ynh_die "The app was restored to the way it was before the failed upgrade."
|
# Remove the application then restore it
|
||||||
fi
|
sudo yunohost app remove $app
|
||||||
|
# Restore the backup
|
||||||
|
sudo yunohost backup restore --ignore-system $app_bck-pre-upgrade$backup_number --apps $app --force --verbose
|
||||||
|
ynh_die "The app was restored to the way it was before the failed upgrade."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "\$NO_BACKUP_UPGRADE is set, that means there's no backup to restore. You have to fix this upgrade by yourself !" >&2
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Make a backup in case of failed upgrade
|
# Make a backup in case of failed upgrade
|
||||||
|
@ -67,28 +74,34 @@ ynh_backup_before_upgrade () {
|
||||||
backup_number=1
|
backup_number=1
|
||||||
local old_backup_number=2
|
local old_backup_number=2
|
||||||
local app_bck=${app//_/-} # Replace all '_' by '-'
|
local app_bck=${app//_/-} # Replace all '_' by '-'
|
||||||
|
NO_BACKUP_UPGRADE=${NO_BACKUP_UPGRADE:-0}
|
||||||
|
|
||||||
# Check if a backup already exists with the prefix 1
|
if [ "$NO_BACKUP_UPGRADE" -eq 0 ]
|
||||||
if sudo yunohost backup list | grep -q $app_bck-pre-upgrade1
|
then
|
||||||
then
|
# Check if a backup already exists with the prefix 1
|
||||||
# Prefix becomes 2 to preserve the previous backup
|
if sudo yunohost backup list | grep -q $app_bck-pre-upgrade1
|
||||||
backup_number=2
|
|
||||||
old_backup_number=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create backup
|
|
||||||
sudo BACKUP_CORE_ONLY=1 yunohost backup create --ignore-system --apps $app --name $app_bck-pre-upgrade$backup_number --verbose
|
|
||||||
if [ "$?" -eq 0 ]
|
|
||||||
then
|
|
||||||
# If the backup succeeded, remove the previous backup
|
|
||||||
if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$old_backup_number
|
|
||||||
then
|
then
|
||||||
# Remove the previous backup only if it exists
|
# Prefix becomes 2 to preserve the previous backup
|
||||||
sudo yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null
|
backup_number=2
|
||||||
|
old_backup_number=1
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
ynh_die "Backup failed, the upgrade process was aborted."
|
# Create backup
|
||||||
fi
|
sudo BACKUP_CORE_ONLY=1 yunohost backup create --ignore-system --apps $app --name $app_bck-pre-upgrade$backup_number --verbose
|
||||||
|
if [ "$?" -eq 0 ]
|
||||||
|
then
|
||||||
|
# If the backup succeeded, remove the previous backup
|
||||||
|
if sudo yunohost backup list | grep -q $app_bck-pre-upgrade$old_backup_number
|
||||||
|
then
|
||||||
|
# Remove the previous backup only if it exists
|
||||||
|
sudo yunohost backup delete $app_bck-pre-upgrade$old_backup_number > /dev/null
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
ynh_die "Backup failed, the upgrade process was aborted."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "\$NO_BACKUP_UPGRADE is set, backup will be avoided. Be careful, this upgrade is going to be operated without a security backup"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Download, check integrity, uncompress and patch the source from app.src
|
# Download, check integrity, uncompress and patch the source from app.src
|
||||||
|
@ -109,6 +122,10 @@ ynh_backup_before_upgrade () {
|
||||||
# # (Optionnal) Name of the local archive (offline setup support)
|
# # (Optionnal) Name of the local archive (offline setup support)
|
||||||
# # default: ${src_id}.${src_format}
|
# # default: ${src_id}.${src_format}
|
||||||
# SOURCE_FILENAME=example.tar.gz
|
# SOURCE_FILENAME=example.tar.gz
|
||||||
|
# # (Optional) If it set as false don't extract the source.
|
||||||
|
# # (Useful to get a debian package or a python wheel.)
|
||||||
|
# # default: true
|
||||||
|
# SOURCE_EXTRACT=(true|false)
|
||||||
#
|
#
|
||||||
# Details:
|
# Details:
|
||||||
# This helper downloads sources from SOURCE_URL if there is no local source
|
# This helper downloads sources from SOURCE_URL if there is no local source
|
||||||
|
@ -137,6 +154,7 @@ ynh_setup_source () {
|
||||||
local src_sum=$(grep 'SOURCE_SUM=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
local src_sum=$(grep 'SOURCE_SUM=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
||||||
local src_sumprg=$(grep 'SOURCE_SUM_PRG=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
local src_sumprg=$(grep 'SOURCE_SUM_PRG=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
||||||
local src_format=$(grep 'SOURCE_FORMAT=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
local src_format=$(grep 'SOURCE_FORMAT=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
||||||
|
local src_extract=$(grep 'SOURCE_EXTRACT=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
||||||
local src_in_subdir=$(grep 'SOURCE_IN_SUBDIR=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
local src_in_subdir=$(grep 'SOURCE_IN_SUBDIR=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
||||||
local src_filename=$(grep 'SOURCE_FILENAME=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
local src_filename=$(grep 'SOURCE_FILENAME=' "$YNH_CWD/../conf/${src_id}.src" | cut -d= -f2-)
|
||||||
|
|
||||||
|
@ -145,6 +163,7 @@ ynh_setup_source () {
|
||||||
src_in_subdir=${src_in_subdir:-true}
|
src_in_subdir=${src_in_subdir:-true}
|
||||||
src_format=${src_format:-tar.gz}
|
src_format=${src_format:-tar.gz}
|
||||||
src_format=$(echo "$src_format" | tr '[:upper:]' '[:lower:]')
|
src_format=$(echo "$src_format" | tr '[:upper:]' '[:lower:]')
|
||||||
|
src_extract=${src_extract:-true}
|
||||||
if [ "$src_filename" = "" ] ; then
|
if [ "$src_filename" = "" ] ; then
|
||||||
src_filename="${src_id}.${src_format}"
|
src_filename="${src_id}.${src_format}"
|
||||||
fi
|
fi
|
||||||
|
@ -163,7 +182,11 @@ ynh_setup_source () {
|
||||||
|
|
||||||
# Extract source into the app dir
|
# Extract source into the app dir
|
||||||
mkdir -p "$dest_dir"
|
mkdir -p "$dest_dir"
|
||||||
if [ "$src_format" = "zip" ]
|
|
||||||
|
if ! "$src_extract"
|
||||||
|
then
|
||||||
|
mv $src_filename $dest_dir
|
||||||
|
elif [ "$src_format" = "zip" ]
|
||||||
then
|
then
|
||||||
# Zip format
|
# Zip format
|
||||||
# Using of a temp directory, because unzip doesn't manage --strip-components
|
# Using of a temp directory, because unzip doesn't manage --strip-components
|
||||||
|
|
|
@ -61,11 +61,17 @@ do_pre_regen() {
|
||||||
_update_services() {
|
_update_services() {
|
||||||
sudo python2 - << EOF
|
sudo python2 - << EOF
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
with open('services.yml') as f:
|
with open('services.yml') as f:
|
||||||
new_services = yaml.load(f)
|
new_services = yaml.load(f)
|
||||||
|
|
||||||
with open('/etc/yunohost/services.yml') as f:
|
with open('/etc/yunohost/services.yml') as f:
|
||||||
services = yaml.load(f)
|
services = yaml.load(f)
|
||||||
|
|
||||||
updated = False
|
updated = False
|
||||||
|
|
||||||
|
|
||||||
for service, conf in new_services.items():
|
for service, conf in new_services.items():
|
||||||
# remove service with empty conf
|
# remove service with empty conf
|
||||||
if conf is None:
|
if conf is None:
|
||||||
|
@ -73,20 +79,32 @@ for service, conf in new_services.items():
|
||||||
print("removing '{0}' from services".format(service))
|
print("removing '{0}' from services".format(service))
|
||||||
del services[service]
|
del services[service]
|
||||||
updated = True
|
updated = True
|
||||||
|
|
||||||
# add new service
|
# add new service
|
||||||
elif not services.get(service, None):
|
elif not services.get(service, None):
|
||||||
print("adding '{0}' to services".format(service))
|
print("adding '{0}' to services".format(service))
|
||||||
services[service] = conf
|
services[service] = conf
|
||||||
updated = True
|
updated = True
|
||||||
|
|
||||||
# update service conf
|
# update service conf
|
||||||
else:
|
else:
|
||||||
conffiles = services[service].pop('conffiles', {})
|
conffiles = services[service].pop('conffiles', {})
|
||||||
|
|
||||||
|
# status need to be removed
|
||||||
|
if "status" not in conf and "status" in services[service]:
|
||||||
|
print("update '{0}' service status access".format(service))
|
||||||
|
del services[service]["status"]
|
||||||
|
updated = True
|
||||||
|
|
||||||
if services[service] != conf:
|
if services[service] != conf:
|
||||||
print("update '{0}' service".format(service))
|
print("update '{0}' service".format(service))
|
||||||
services[service].update(conf)
|
services[service].update(conf)
|
||||||
updated = True
|
updated = True
|
||||||
|
|
||||||
if conffiles:
|
if conffiles:
|
||||||
services[service]['conffiles'] = conffiles
|
services[service]['conffiles'] = conffiles
|
||||||
|
|
||||||
|
|
||||||
if updated:
|
if updated:
|
||||||
with open('/etc/yunohost/services.yml-new', 'w') as f:
|
with open('/etc/yunohost/services.yml-new', 'w') as f:
|
||||||
yaml.safe_dump(services, f, default_flow_style=False)
|
yaml.safe_dump(services, f, default_flow_style=False)
|
||||||
|
|
|
@ -22,8 +22,13 @@ server {
|
||||||
ssl_certificate_key /etc/yunohost/certs/yunohost.org/key.pem;
|
ssl_certificate_key /etc/yunohost/certs/yunohost.org/key.pem;
|
||||||
ssl_session_timeout 5m;
|
ssl_session_timeout 5m;
|
||||||
ssl_session_cache shared:SSL:50m;
|
ssl_session_cache shared:SSL:50m;
|
||||||
|
|
||||||
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
|
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
|
||||||
ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
|
# (this doesn't work on jessie though ...?)
|
||||||
|
# ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
|
||||||
|
|
||||||
|
# As suggested by https://cipherli.st/
|
||||||
|
ssl_ecdh_curve secp384r1;
|
||||||
|
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,13 @@ server {
|
||||||
ssl_certificate_key /etc/yunohost/certs/{{ domain }}/key.pem;
|
ssl_certificate_key /etc/yunohost/certs/{{ domain }}/key.pem;
|
||||||
ssl_session_timeout 5m;
|
ssl_session_timeout 5m;
|
||||||
ssl_session_cache shared:SSL:50m;
|
ssl_session_cache shared:SSL:50m;
|
||||||
|
|
||||||
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
|
# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519
|
||||||
ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
|
# (this doesn't work on jessie though ...?)
|
||||||
|
# ssl_ecdh_curve secp521r1:secp384r1:prime256v1;
|
||||||
|
|
||||||
|
# As suggested by https://cipherli.st/
|
||||||
|
ssl_ecdh_curve secp384r1;
|
||||||
|
|
||||||
ssl_prefer_server_ciphers on;
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,11 @@ smtp_tls_exclude_ciphers = $smtpd_tls_exclude_ciphers
|
||||||
smtp_tls_mandatory_ciphers= $smtpd_tls_mandatory_ciphers
|
smtp_tls_mandatory_ciphers= $smtpd_tls_mandatory_ciphers
|
||||||
smtp_tls_loglevel=1
|
smtp_tls_loglevel=1
|
||||||
|
|
||||||
|
# Configure Root CA certificates
|
||||||
|
# (for example, avoids getting "Untrusted TLS connection established to" messages in logs)
|
||||||
|
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
|
||||||
|
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
|
||||||
|
|
||||||
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
|
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
|
||||||
# information on enabling SSL in the smtp client.
|
# information on enabling SSL in the smtp client.
|
||||||
|
|
||||||
|
|
|
@ -1,54 +1,42 @@
|
||||||
nginx:
|
nginx:
|
||||||
status: service
|
log: /var/log/nginx
|
||||||
log: /var/log/nginx
|
|
||||||
avahi-daemon:
|
avahi-daemon:
|
||||||
status: service
|
log: /var/log/daemon.log
|
||||||
log: /var/log/daemon.log
|
|
||||||
dnsmasq:
|
dnsmasq:
|
||||||
status: service
|
log: /var/log/daemon.log
|
||||||
log: /var/log/daemon.log
|
|
||||||
fail2ban:
|
fail2ban:
|
||||||
status: service
|
log: /var/log/fail2ban.log
|
||||||
log: /var/log/fail2ban.log
|
|
||||||
dovecot:
|
dovecot:
|
||||||
status: service
|
log: [/var/log/mail.log,/var/log/mail.err]
|
||||||
log: [/var/log/mail.log,/var/log/mail.err]
|
|
||||||
postfix:
|
postfix:
|
||||||
status: service
|
log: [/var/log/mail.log,/var/log/mail.err]
|
||||||
log: [/var/log/mail.log,/var/log/mail.err]
|
|
||||||
rspamd:
|
rspamd:
|
||||||
status: systemctl status rspamd.service
|
log: /var/log/rspamd/rspamd.log
|
||||||
log: /var/log/rspamd/rspamd.log
|
|
||||||
redis-server:
|
redis-server:
|
||||||
status: service
|
log: /var/log/redis/redis-server.log
|
||||||
log: /var/log/redis/redis-server.log
|
|
||||||
mysql:
|
mysql:
|
||||||
status: service
|
log: [/var/log/mysql.log,/var/log/mysql.err]
|
||||||
log: [/var/log/mysql.log,/var/log/mysql.err]
|
glances: {}
|
||||||
glances:
|
|
||||||
status: service
|
|
||||||
ssh:
|
ssh:
|
||||||
status: service
|
log: /var/log/auth.log
|
||||||
log: /var/log/auth.log
|
ssl:
|
||||||
|
status: null
|
||||||
metronome:
|
metronome:
|
||||||
status: metronomectl status
|
log: [/var/log/metronome/metronome.log,/var/log/metronome/metronome.err]
|
||||||
log: [/var/log/metronome/metronome.log,/var/log/metronome/metronome.err]
|
|
||||||
slapd:
|
slapd:
|
||||||
status: service
|
log: /var/log/syslog
|
||||||
log: /var/log/syslog
|
|
||||||
php7.0-fpm:
|
php7.0-fpm:
|
||||||
status: service
|
log: /var/log/php7.0-fpm.log
|
||||||
log: /var/log/php7.0-fpm.log
|
|
||||||
yunohost-api:
|
yunohost-api:
|
||||||
status: service
|
log: /var/log/yunohost/yunohost-api.log
|
||||||
log: /var/log/yunohost/yunohost-api.log
|
|
||||||
yunohost-firewall:
|
yunohost-firewall:
|
||||||
status: service
|
need_lock: true
|
||||||
need_lock: true
|
|
||||||
nslcd:
|
nslcd:
|
||||||
status: service
|
log: /var/log/syslog
|
||||||
log: /var/log/syslog
|
nsswitch:
|
||||||
nsswitch: {}
|
status: null
|
||||||
|
yunohost:
|
||||||
|
status: null
|
||||||
bind9: null
|
bind9: null
|
||||||
tahoe-lafs: null
|
tahoe-lafs: null
|
||||||
memcached: null
|
memcached: null
|
||||||
|
|
28
debian/changelog
vendored
28
debian/changelog
vendored
|
@ -24,6 +24,34 @@ yunohost (3.0.0~beta1) testing; urgency=low
|
||||||
|
|
||||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 03 May 2018 03:04:45 +0000
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 03 May 2018 03:04:45 +0000
|
||||||
|
|
||||||
|
yunohost (2.7.13.2) testing; urgency=low
|
||||||
|
|
||||||
|
* [fix] Fix an error with services marked as None (#466)
|
||||||
|
* [fix] Issue with nginx not upgrading correctly /etc/nginx/nginx.conf if it was manually modified
|
||||||
|
|
||||||
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Fri, 11 May 2018 02:06:42 +0000
|
||||||
|
|
||||||
|
yunohost (2.7.13.1) testing; urgency=low
|
||||||
|
|
||||||
|
* [fix] Misc fixes on stretch migration following feedback
|
||||||
|
|
||||||
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 09 May 2018 00:44:50 +0000
|
||||||
|
|
||||||
|
yunohost (2.7.13) testing; urgency=low
|
||||||
|
|
||||||
|
* [enh] Add 'manual migration' mechanism to the migration framework (#429)
|
||||||
|
* [enh] Add Stretch migration (#433)
|
||||||
|
* [enh] Use recommended ECDH curves (#454)
|
||||||
|
|
||||||
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 06 May 2018 23:10:13 +0000
|
||||||
|
|
||||||
|
yunohost (2.7.12) stable; urgency=low
|
||||||
|
|
||||||
|
* [i18n] Improve translation for Portuguese
|
||||||
|
* Bump version number for stable release
|
||||||
|
|
||||||
|
-- Alexandre Aubin <alex.aubin@mailoo.org> Sun, 06 May 2018 16:40:11 +0000
|
||||||
|
|
||||||
yunohost (2.7.11.1) testing; urgency=low
|
yunohost (2.7.11.1) testing; urgency=low
|
||||||
|
|
||||||
* [fix] Nginx Regression typo (#459)
|
* [fix] Nginx Regression typo (#459)
|
||||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -12,7 +12,7 @@ Architecture: all
|
||||||
Depends: ${python:Depends}, ${misc:Depends}
|
Depends: ${python:Depends}, ${misc:Depends}
|
||||||
, moulinette (>= 2.7.1), ssowat (>= 2.7.1)
|
, moulinette (>= 2.7.1), ssowat (>= 2.7.1)
|
||||||
, python-psutil, python-requests, python-dnspython, python-openssl
|
, python-psutil, python-requests, python-dnspython, python-openssl
|
||||||
, python-apt, python-miniupnpc
|
, python-apt, python-miniupnpc, python-dbus
|
||||||
, glances
|
, glances
|
||||||
, dnsutils, bind9utils, unzip, git, curl, cron, wget
|
, dnsutils, bind9utils, unzip, git, curl, cron, wget
|
||||||
, ca-certificates, netcat-openbsd, iproute
|
, ca-certificates, netcat-openbsd, iproute
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"admin_password_changed": "تم تعديل الكلمة السرية الإدارية",
|
"admin_password_changed": "تم تعديل الكلمة السرية الإدارية",
|
||||||
"app_already_installed": "{app:s} تم تنصيبه مِن قبل",
|
"app_already_installed": "{app:s} تم تنصيبه مِن قبل",
|
||||||
"app_already_installed_cant_change_url": "",
|
"app_already_installed_cant_change_url": "",
|
||||||
"app_already_up_to_date": "",
|
"app_already_up_to_date": "{app:s} تم تحديثه مِن قَبل",
|
||||||
"app_argument_choice_invalid": "",
|
"app_argument_choice_invalid": "",
|
||||||
"app_argument_invalid": "",
|
"app_argument_invalid": "",
|
||||||
"app_argument_required": "",
|
"app_argument_required": "",
|
||||||
|
@ -24,11 +24,11 @@
|
||||||
"app_location_unavailable": "This url is not available or conflicts with an already installed app",
|
"app_location_unavailable": "This url is not available or conflicts with an already installed app",
|
||||||
"app_manifest_invalid": "Invalid app manifest: {error}",
|
"app_manifest_invalid": "Invalid app manifest: {error}",
|
||||||
"app_no_upgrade": "البرمجيات لا تحتاج إلى تحديث",
|
"app_no_upgrade": "البرمجيات لا تحتاج إلى تحديث",
|
||||||
"app_not_correctly_installed": "{app:s} seems to be incorrectly installed",
|
"app_not_correctly_installed": "يبدو أن التطبيق {app:s} لم يتم تنصيبه بشكل صحيح",
|
||||||
"app_not_installed": "{app:s} is not installed",
|
"app_not_installed": "إنّ التطبيق {app:s} غير مُنصَّب",
|
||||||
"app_not_properly_removed": "{app:s} has not been properly removed",
|
"app_not_properly_removed": "{app:s} has not been properly removed",
|
||||||
"app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes",
|
"app_package_need_update": "The app {app} package needs to be updated to follow YunoHost changes",
|
||||||
"app_removed": "{app:s} has been removed",
|
"app_removed": "تمت إزالة تطبيق {app:s}",
|
||||||
"app_requirements_checking": "Checking required packages for {app}...",
|
"app_requirements_checking": "Checking required packages for {app}...",
|
||||||
"app_requirements_failed": "Unable to meet requirements for {app}: {error}",
|
"app_requirements_failed": "Unable to meet requirements for {app}: {error}",
|
||||||
"app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}",
|
"app_requirements_unmeet": "Requirements are not met for {app}, the package {pkgname} ({version}) must be {spec}",
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
"app_upgrade_app_name": "جارٍ تحديث برنامج {app}...",
|
"app_upgrade_app_name": "جارٍ تحديث برنامج {app}...",
|
||||||
"app_upgrade_failed": "تعذرت عملية ترقية {app:s}",
|
"app_upgrade_failed": "تعذرت عملية ترقية {app:s}",
|
||||||
"app_upgrade_some_app_failed": "تعذرت عملية ترقية بعض البرمجيات",
|
"app_upgrade_some_app_failed": "تعذرت عملية ترقية بعض البرمجيات",
|
||||||
"app_upgraded": "{app:s} has been upgraded",
|
"app_upgraded": "تم تحديث التطبيق {app:s}",
|
||||||
"appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.",
|
"appslist_corrupted_json": "Could not load the application lists. It looks like {filename:s} is corrupted.",
|
||||||
"appslist_could_not_migrate": "Could not migrate app list {appslist:s} ! Unable to parse the url... The old cron job has been kept in {bkp_file:s}.",
|
"appslist_could_not_migrate": "Could not migrate app list {appslist:s} ! Unable to parse the url... The old cron job has been kept in {bkp_file:s}.",
|
||||||
"appslist_fetched": "The application list {appslist:s} has been fetched",
|
"appslist_fetched": "The application list {appslist:s} has been fetched",
|
||||||
|
@ -115,8 +115,8 @@
|
||||||
"certmanager_cannot_read_cert": "Something wrong happened when trying to open current certificate for domain {domain:s} (file: {file:s}), reason: {reason:s}",
|
"certmanager_cannot_read_cert": "Something wrong happened when trying to open current certificate for domain {domain:s} (file: {file:s}), reason: {reason:s}",
|
||||||
"certmanager_cert_install_success": "تمت عملية تنصيب شهادة Let's Encrypt بنجاح على النطاق {domain:s}!",
|
"certmanager_cert_install_success": "تمت عملية تنصيب شهادة Let's Encrypt بنجاح على النطاق {domain:s}!",
|
||||||
"certmanager_cert_install_success_selfsigned": "Successfully installed a self-signed certificate for domain {domain:s}!",
|
"certmanager_cert_install_success_selfsigned": "Successfully installed a self-signed certificate for domain {domain:s}!",
|
||||||
"certmanager_cert_renew_success": "Successfully renewed Let's Encrypt certificate for domain {domain:s}!",
|
"certmanager_cert_renew_success": "نجحت عملية تجديد شهادة Let's Encrypt الخاصة باسم النطاق {domain:s} !",
|
||||||
"certmanager_cert_signing_failed": "Signing the new certificate failed",
|
"certmanager_cert_signing_failed": "فشل إجراء توقيع الشهادة الجديدة",
|
||||||
"certmanager_certificate_fetching_or_enabling_failed": "Sounds like enabling the new certificate for {domain:s} failed somehow...",
|
"certmanager_certificate_fetching_or_enabling_failed": "Sounds like enabling the new certificate for {domain:s} failed somehow...",
|
||||||
"certmanager_conflicting_nginx_file": "Unable to prepare domain for ACME challenge: the nginx configuration file {filepath:s} is conflicting and should be removed first",
|
"certmanager_conflicting_nginx_file": "Unable to prepare domain for ACME challenge: the nginx configuration file {filepath:s} is conflicting and should be removed first",
|
||||||
"certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted - please try again later.",
|
"certmanager_couldnt_fetch_intermediate_cert": "Timed out when trying to fetch intermediate certificate from Let's Encrypt. Certificate installation/renewal aborted - please try again later.",
|
||||||
|
@ -139,13 +139,13 @@
|
||||||
"diagnosis_monitor_disk_error": "Can't monitor disks: {error}",
|
"diagnosis_monitor_disk_error": "Can't monitor disks: {error}",
|
||||||
"diagnosis_monitor_network_error": "Can't monitor network: {error}",
|
"diagnosis_monitor_network_error": "Can't monitor network: {error}",
|
||||||
"diagnosis_monitor_system_error": "Can't monitor system: {error}",
|
"diagnosis_monitor_system_error": "Can't monitor system: {error}",
|
||||||
"diagnosis_no_apps": "No installed application",
|
"diagnosis_no_apps": "لم تقم بتنصيب أية تطبيقات بعد",
|
||||||
"dnsmasq_isnt_installed": "dnsmasq does not seem to be installed, please run 'apt-get remove bind9 && apt-get install dnsmasq'",
|
"dnsmasq_isnt_installed": "dnsmasq does not seem to be installed, please run 'apt-get remove bind9 && apt-get install dnsmasq'",
|
||||||
"domain_cannot_remove_main": "Cannot remove main domain. Set a new main domain first",
|
"domain_cannot_remove_main": "Cannot remove main domain. Set a new main domain first",
|
||||||
"domain_cert_gen_failed": "Unable to generate certificate",
|
"domain_cert_gen_failed": "Unable to generate certificate",
|
||||||
"domain_created": "The domain has been created",
|
"domain_created": "تم إنشاء النطاق",
|
||||||
"domain_creation_failed": "Unable to create domain",
|
"domain_creation_failed": "تعذرت عملية إنشاء النطاق",
|
||||||
"domain_deleted": "The domain has been deleted",
|
"domain_deleted": "تم حذف النطاق",
|
||||||
"domain_deletion_failed": "Unable to delete domain",
|
"domain_deletion_failed": "Unable to delete domain",
|
||||||
"domain_dns_conf_is_just_a_recommendation": "This command shows you what is the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.",
|
"domain_dns_conf_is_just_a_recommendation": "This command shows you what is the *recommended* configuration. It does not actually set up the DNS configuration for you. It is your responsability to configure your DNS zone in your registrar according to this recommendation.",
|
||||||
"domain_dyndns_already_subscribed": "You've already subscribed to a DynDNS domain",
|
"domain_dyndns_already_subscribed": "You've already subscribed to a DynDNS domain",
|
||||||
|
@ -222,7 +222,7 @@
|
||||||
"migrate_tsig_wait_4": "30 ثانية …",
|
"migrate_tsig_wait_4": "30 ثانية …",
|
||||||
"migrate_tsig_not_needed": "You do not appear to use a dyndns domain, so no migration is needed !",
|
"migrate_tsig_not_needed": "You do not appear to use a dyndns domain, so no migration is needed !",
|
||||||
"migrations_backward": "Migrating backward.",
|
"migrations_backward": "Migrating backward.",
|
||||||
"migrations_bad_value_for_target": "Invalide number for target argument, available migrations numbers are 0 or {}",
|
"migrations_bad_value_for_target": "Invalid number for target argument, available migrations numbers are 0 or {}",
|
||||||
"migrations_cant_reach_migration_file": "Can't access migrations files at path %s",
|
"migrations_cant_reach_migration_file": "Can't access migrations files at path %s",
|
||||||
"migrations_current_target": "Migration target is {}",
|
"migrations_current_target": "Migration target is {}",
|
||||||
"migrations_error_failed_to_load_migration": "ERROR: failed to load migration {number} {name}",
|
"migrations_error_failed_to_load_migration": "ERROR: failed to load migration {number} {name}",
|
||||||
|
@ -257,18 +257,18 @@
|
||||||
"package_not_installed": "Package '{pkgname}' is not installed",
|
"package_not_installed": "Package '{pkgname}' is not installed",
|
||||||
"package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'",
|
"package_unexpected_error": "An unexpected error occurred processing the package '{pkgname}'",
|
||||||
"package_unknown": "Unknown package '{pkgname}'",
|
"package_unknown": "Unknown package '{pkgname}'",
|
||||||
"packages_no_upgrade": "There is no package to upgrade",
|
"packages_no_upgrade": "لا يوجد هناك أية حزمة بحاجة إلى تحديث",
|
||||||
"packages_upgrade_critical_later": "Critical packages ({packages:s}) will be upgraded later",
|
"packages_upgrade_critical_later": "Critical packages ({packages:s}) will be upgraded later",
|
||||||
"packages_upgrade_failed": "Unable to upgrade all of the packages",
|
"packages_upgrade_failed": "Unable to upgrade all of the packages",
|
||||||
"path_removal_failed": "Unable to remove path {:s}",
|
"path_removal_failed": "Unable to remove path {:s}",
|
||||||
"pattern_backup_archive_name": "Must be a valid filename with max 30 characters, and alphanumeric and -_. characters only",
|
"pattern_backup_archive_name": "Must be a valid filename with max 30 characters, and alphanumeric and -_. characters only",
|
||||||
"pattern_domain": "Must be a valid domain name (e.g. my-domain.org)",
|
"pattern_domain": "يتوجب أن يكون إسم نطاق صالح (مثل my-domain.org)",
|
||||||
"pattern_email": "Must be a valid email address (e.g. someone@domain.org)",
|
"pattern_email": "يتوجب أن يكون عنوان بريد إلكتروني صالح (مثل someone@domain.org)",
|
||||||
"pattern_firstname": "Must be a valid first name",
|
"pattern_firstname": "Must be a valid first name",
|
||||||
"pattern_lastname": "Must be a valid last name",
|
"pattern_lastname": "Must be a valid last name",
|
||||||
"pattern_listname": "Must be alphanumeric and underscore characters only",
|
"pattern_listname": "Must be alphanumeric and underscore characters only",
|
||||||
"pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to disable the quota",
|
"pattern_mailbox_quota": "Must be a size with b/k/M/G/T suffix or 0 to disable the quota",
|
||||||
"pattern_password": "Must be at least 3 characters long",
|
"pattern_password": "يتوجب أن تكون مكونة من 3 حروف على الأقل",
|
||||||
"pattern_port": "يجب أن يكون رقم منفذ صالح (مثال 0-65535)",
|
"pattern_port": "يجب أن يكون رقم منفذ صالح (مثال 0-65535)",
|
||||||
"pattern_port_or_range": "Must be a valid port number (i.e. 0-65535) or range of ports (e.g. 100:200)",
|
"pattern_port_or_range": "Must be a valid port number (i.e. 0-65535) or range of ports (e.g. 100:200)",
|
||||||
"pattern_positive_number": "يجب أن يكون عددا إيجابيا",
|
"pattern_positive_number": "يجب أن يكون عددا إيجابيا",
|
||||||
|
@ -283,22 +283,22 @@
|
||||||
"restore_cleaning_failed": "Unable to clean-up the temporary restoration directory",
|
"restore_cleaning_failed": "Unable to clean-up the temporary restoration directory",
|
||||||
"restore_complete": "Restore complete",
|
"restore_complete": "Restore complete",
|
||||||
"restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]",
|
"restore_confirm_yunohost_installed": "Do you really want to restore an already installed system? [{answers:s}]",
|
||||||
"restore_extracting": "Extracting needed files from the archive...",
|
"restore_extracting": "فك الضغط عن الملفات التي نحتاجها من النسخة الإحتياطية ...",
|
||||||
"restore_failed": "Unable to restore the system",
|
"restore_failed": "Unable to restore the system",
|
||||||
"restore_hook_unavailable": "Restoration script for '{part:s}' not available on your system and not in the archive either",
|
"restore_hook_unavailable": "Restoration script for '{part:s}' not available on your system and not in the archive either",
|
||||||
"restore_may_be_not_enough_disk_space": "Your system seems not to have enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)",
|
"restore_may_be_not_enough_disk_space": "Your system seems not to have enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)",
|
||||||
"restore_mounting_archive": "Mounting archive into '{path:s}'",
|
"restore_mounting_archive": "تنصيب النسخة الإحتياطية على المسار '{path:s}'",
|
||||||
"restore_not_enough_disk_space": "Not enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)",
|
"restore_not_enough_disk_space": "Not enough disk space (freespace: {free_space:d} B, needed space: {needed_space:d} B, security margin: {margin:d} B)",
|
||||||
"restore_nothings_done": "Nothing has been restored",
|
"restore_nothings_done": "Nothing has been restored",
|
||||||
"restore_removing_tmp_dir_failed": "Unable to remove an old temporary directory",
|
"restore_removing_tmp_dir_failed": "Unable to remove an old temporary directory",
|
||||||
"restore_running_app_script": "Running restore script of app '{app:s}'...",
|
"restore_running_app_script": "Running restore script of app '{app:s}'...",
|
||||||
"restore_running_hooks": "Running restoration hooks...",
|
"restore_running_hooks": "Running restoration hooks...",
|
||||||
"restore_system_part_failed": "Unable to restore the '{part:s}' system part",
|
"restore_system_part_failed": "Unable to restore the '{part:s}' system part",
|
||||||
"server_shutdown": "The server will shutdown",
|
"server_shutdown": "سوف ينطفئ الخادوم",
|
||||||
"server_shutdown_confirm": "The server will shutdown immediatly, are you sure? [{answers:s}]",
|
"server_shutdown_confirm": "سوف ينطفئ الخادوم حالا. متأكد ؟ [{answers:s}]",
|
||||||
"server_reboot": "The server will reboot",
|
"server_reboot": "The server will reboot",
|
||||||
"server_reboot_confirm": "The server will reboot immediatly, are you sure? [{answers:s}]",
|
"server_reboot_confirm": "The server will reboot immediatly, are you sure? [{answers:s}]",
|
||||||
"service_add_failed": "Unable to add service '{service:s}'",
|
"service_add_failed": "تعذرت إضافة خدمة '{service:s}'",
|
||||||
"service_added": "The service '{service:s}' has been added",
|
"service_added": "The service '{service:s}' has been added",
|
||||||
"service_already_started": "Service '{service:s}' has already been started",
|
"service_already_started": "Service '{service:s}' has already been started",
|
||||||
"service_already_stopped": "Service '{service:s}' has already been stopped",
|
"service_already_stopped": "Service '{service:s}' has already been stopped",
|
||||||
|
@ -315,9 +315,9 @@
|
||||||
"service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'",
|
"service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'",
|
||||||
"service_conf_updated": "The configuration has been updated for service '{service}'",
|
"service_conf_updated": "The configuration has been updated for service '{service}'",
|
||||||
"service_conf_would_be_updated": "The configuration would have been updated for service '{service}'",
|
"service_conf_would_be_updated": "The configuration would have been updated for service '{service}'",
|
||||||
"service_disable_failed": "Unable to disable service '{service:s}'",
|
"service_disable_failed": "",
|
||||||
"service_disabled": "The service '{service:s}' has been disabled",
|
"service_disabled": "The service '{service:s}' has been disabled",
|
||||||
"service_enable_failed": "Unable to enable service '{service:s}'",
|
"service_enable_failed": "",
|
||||||
"service_enabled": "The service '{service:s}' has been enabled",
|
"service_enabled": "The service '{service:s}' has been enabled",
|
||||||
"service_no_log": "No log to display for service '{service:s}'",
|
"service_no_log": "No log to display for service '{service:s}'",
|
||||||
"service_regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for service '{service}'...",
|
"service_regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for service '{service}'...",
|
||||||
|
@ -325,10 +325,10 @@
|
||||||
"service_regenconf_pending_applying": "Applying pending configuration for service '{service}'...",
|
"service_regenconf_pending_applying": "Applying pending configuration for service '{service}'...",
|
||||||
"service_remove_failed": "Unable to remove service '{service:s}'",
|
"service_remove_failed": "Unable to remove service '{service:s}'",
|
||||||
"service_removed": "The service '{service:s}' has been removed",
|
"service_removed": "The service '{service:s}' has been removed",
|
||||||
"service_start_failed": "Unable to start service '{service:s}'",
|
"service_start_failed": "",
|
||||||
"service_started": "The service '{service:s}' has been started",
|
"service_started": "The service '{service:s}' has been started",
|
||||||
"service_status_failed": "Unable to determine status of service '{service:s}'",
|
"service_status_failed": "Unable to determine status of service '{service:s}'",
|
||||||
"service_stop_failed": "Unable to stop service '{service:s}'",
|
"service_stop_failed": "",
|
||||||
"service_stopped": "The service '{service:s}' has been stopped",
|
"service_stopped": "The service '{service:s}' has been stopped",
|
||||||
"service_unknown": "Unknown service '{service:s}'",
|
"service_unknown": "Unknown service '{service:s}'",
|
||||||
"ssowat_conf_generated": "The SSOwat configuration has been generated",
|
"ssowat_conf_generated": "The SSOwat configuration has been generated",
|
||||||
|
@ -364,5 +364,10 @@
|
||||||
"yunohost_ca_creation_success": "تم إنشاء هيئة الشهادات المحلية.",
|
"yunohost_ca_creation_success": "تم إنشاء هيئة الشهادات المحلية.",
|
||||||
"yunohost_configured": "YunoHost has been configured",
|
"yunohost_configured": "YunoHost has been configured",
|
||||||
"yunohost_installing": "عملية تنصيب يونوهوست جارية …",
|
"yunohost_installing": "عملية تنصيب يونوهوست جارية …",
|
||||||
"yunohost_not_installed": "YunoHost is not or not correctly installed. Please execute 'yunohost tools postinstall'"
|
"yunohost_not_installed": "YunoHost is not or not correctly installed. Please execute 'yunohost tools postinstall'",
|
||||||
|
"migration_description_0003_migrate_to_stretch": "تحديث النظام إلى ديبيان ستريتش و واي يونوهوست 3.0",
|
||||||
|
"migration_0003_patching_sources_list": "عملية تعديل ملف المصادر sources.lists جارية ...",
|
||||||
|
"migration_0003_main_upgrade": "بداية عملية التحديث الأساسية ...",
|
||||||
|
"migration_0003_fail2ban_upgrade": "بداية عملية تحديث fail2ban ...",
|
||||||
|
"migration_0003_not_jessie": "إن توزيعة ديبيان الحالية تختلف عن جيسي !"
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,6 +232,7 @@
|
||||||
"migration_0003_patching_sources_list": "Patching the sources.lists ...",
|
"migration_0003_patching_sources_list": "Patching the sources.lists ...",
|
||||||
"migration_0003_main_upgrade": "Starting main upgrade ...",
|
"migration_0003_main_upgrade": "Starting main upgrade ...",
|
||||||
"migration_0003_fail2ban_upgrade": "Starting the fail2ban upgrade ...",
|
"migration_0003_fail2ban_upgrade": "Starting the fail2ban upgrade ...",
|
||||||
|
"migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset back to its original state first... The previous file will be available as {backup_dest}.",
|
||||||
"migration_0003_yunohost_upgrade": "Starting the yunohost package upgrade ... The migration will end, but the actual upgrade will happen right after. After the operation is complete, you might have to re-log on the webadmin.",
|
"migration_0003_yunohost_upgrade": "Starting the yunohost package upgrade ... The migration will end, but the actual upgrade will happen right after. After the operation is complete, you might have to re-log on the webadmin.",
|
||||||
"migration_0003_not_jessie": "The current debian distribution is not Jessie !",
|
"migration_0003_not_jessie": "The current debian distribution is not Jessie !",
|
||||||
"migration_0003_system_not_fully_up_to_date": "Your system is not fully up to date. Please perform a regular upgrade before running the migration to stretch.",
|
"migration_0003_system_not_fully_up_to_date": "Your system is not fully up to date. Please perform a regular upgrade before running the migration to stretch.",
|
||||||
|
@ -338,9 +339,9 @@
|
||||||
"service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'",
|
"service_conf_up_to_date": "The configuration is already up-to-date for service '{service}'",
|
||||||
"service_conf_updated": "The configuration has been updated for service '{service}'",
|
"service_conf_updated": "The configuration has been updated for service '{service}'",
|
||||||
"service_conf_would_be_updated": "The configuration would have been updated for service '{service}'",
|
"service_conf_would_be_updated": "The configuration would have been updated for service '{service}'",
|
||||||
"service_disable_failed": "Unable to disable service '{service:s}'",
|
"service_disable_failed": "Unable to disable service '{service:s}'\n\nRecent service logs:{logs:s}",
|
||||||
"service_disabled": "The service '{service:s}' has been disabled",
|
"service_disabled": "The service '{service:s}' has been disabled",
|
||||||
"service_enable_failed": "Unable to enable service '{service:s}'",
|
"service_enable_failed": "Unable to enable service '{service:s}'\n\nRecent service logs:{logs:s}",
|
||||||
"service_enabled": "The service '{service:s}' has been enabled",
|
"service_enabled": "The service '{service:s}' has been enabled",
|
||||||
"service_no_log": "No log to display for service '{service:s}'",
|
"service_no_log": "No log to display for service '{service:s}'",
|
||||||
"service_regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for service '{service}'...",
|
"service_regenconf_dry_pending_applying": "Checking pending configuration which would have been applied for service '{service}'...",
|
||||||
|
@ -348,10 +349,10 @@
|
||||||
"service_regenconf_pending_applying": "Applying pending configuration for service '{service}'...",
|
"service_regenconf_pending_applying": "Applying pending configuration for service '{service}'...",
|
||||||
"service_remove_failed": "Unable to remove service '{service:s}'",
|
"service_remove_failed": "Unable to remove service '{service:s}'",
|
||||||
"service_removed": "The service '{service:s}' has been removed",
|
"service_removed": "The service '{service:s}' has been removed",
|
||||||
"service_start_failed": "Unable to start service '{service:s}'",
|
"service_start_failed": "Unable to start service '{service:s}'\n\nRecent service logs:{logs:s}",
|
||||||
"service_started": "The service '{service:s}' has been started",
|
"service_started": "The service '{service:s}' has been started",
|
||||||
"service_status_failed": "Unable to determine status of service '{service:s}'",
|
"service_status_failed": "Unable to determine status of service '{service:s}'",
|
||||||
"service_stop_failed": "Unable to stop service '{service:s}'",
|
"service_stop_failed": "Unable to stop service '{service:s}'\n\nRecent service logs:{logs:s}",
|
||||||
"service_stopped": "The service '{service:s}' has been stopped",
|
"service_stopped": "The service '{service:s}' has been stopped",
|
||||||
"service_unknown": "Unknown service '{service:s}'",
|
"service_unknown": "Unknown service '{service:s}'",
|
||||||
"ssowat_conf_generated": "The SSOwat configuration has been generated",
|
"ssowat_conf_generated": "The SSOwat configuration has been generated",
|
||||||
|
|
|
@ -200,9 +200,9 @@
|
||||||
"service_configuration_conflict": "Le fichier {file:s} a été modifié depuis sa dernière génération. Veuillez y appliquer les modifications manuellement ou utiliser l’option --force (ce qui écrasera toutes les modifications effectuées sur le fichier).",
|
"service_configuration_conflict": "Le fichier {file:s} a été modifié depuis sa dernière génération. Veuillez y appliquer les modifications manuellement ou utiliser l’option --force (ce qui écrasera toutes les modifications effectuées sur le fichier).",
|
||||||
"service_configured": "La configuration du service « {service:s} » a été générée avec succès",
|
"service_configured": "La configuration du service « {service:s} » a été générée avec succès",
|
||||||
"service_configured_all": "La configuration de tous les services a été générée avec succès",
|
"service_configured_all": "La configuration de tous les services a été générée avec succès",
|
||||||
"service_disable_failed": "Impossible de désactiver le service « {service:s} »",
|
"service_disable_failed": "Impossible de désactiver le service « {service:s} »\n\nJournaux récents : {logs:s}",
|
||||||
"service_disabled": "Le service « {service:s} » a été désactivé",
|
"service_disabled": "Le service « {service:s} » a été désactivé",
|
||||||
"service_enable_failed": "Impossible d'activer le service « {service:s} »",
|
"service_enable_failed": "Impossible d’activer le service « {service:s} »\n\nJournaux récents : {logs:s}",
|
||||||
"service_enabled": "Le service « {service:s} » a été activé",
|
"service_enabled": "Le service « {service:s} » a été activé",
|
||||||
"service_no_log": "Aucun journal à afficher pour le service « {service:s} »",
|
"service_no_log": "Aucun journal à afficher pour le service « {service:s} »",
|
||||||
"service_regenconf_dry_pending_applying": "Vérification des configurations en attentes qui pourraient être appliquées pour le service « {service} »…",
|
"service_regenconf_dry_pending_applying": "Vérification des configurations en attentes qui pourraient être appliquées pour le service « {service} »…",
|
||||||
|
@ -210,10 +210,10 @@
|
||||||
"service_regenconf_pending_applying": "Application des configurations en attentes pour le service « {service} »…",
|
"service_regenconf_pending_applying": "Application des configurations en attentes pour le service « {service} »…",
|
||||||
"service_remove_failed": "Impossible d'enlever le service « {service:s} »",
|
"service_remove_failed": "Impossible d'enlever le service « {service:s} »",
|
||||||
"service_removed": "Le service « {service:s} » a été enlevé",
|
"service_removed": "Le service « {service:s} » a été enlevé",
|
||||||
"service_start_failed": "Impossible de démarrer le service « {service:s} »",
|
"service_start_failed": "Impossible de démarrer le service « {service:s} »\n\nJournaux récents : {logs:s}",
|
||||||
"service_started": "Le service « {service:s} » a été démarré",
|
"service_started": "Le service « {service:s} » a été démarré",
|
||||||
"service_status_failed": "Impossible de déterminer le statut du service « {service:s} »",
|
"service_status_failed": "Impossible de déterminer le statut du service « {service:s} »",
|
||||||
"service_stop_failed": "Impossible d'arrêter le service « {service:s} »",
|
"service_stop_failed": "Impossible d’arrêter le service « {service:s} »\n\nJournaux récents : {logs:s}",
|
||||||
"service_stopped": "Le service « {service:s} » a été arrêté",
|
"service_stopped": "Le service « {service:s} » a été arrêté",
|
||||||
"service_unknown": "Service « {service:s} » inconnu",
|
"service_unknown": "Service « {service:s} » inconnu",
|
||||||
"services_configured": "La configuration a été générée avec succès",
|
"services_configured": "La configuration a été générée avec succès",
|
||||||
|
@ -379,5 +379,24 @@
|
||||||
"migrate_tsig_wait_3": "1 minute…",
|
"migrate_tsig_wait_3": "1 minute…",
|
||||||
"migrate_tsig_wait_4": "30 secondes…",
|
"migrate_tsig_wait_4": "30 secondes…",
|
||||||
"migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine dyndns, donc aucune migration n’est nécessaire !",
|
"migrate_tsig_not_needed": "Il ne semble pas que vous utilisez un domaine dyndns, donc aucune migration n’est nécessaire !",
|
||||||
"app_checkurl_is_deprecated": "Packagers /!\\ 'app checkurl' est obsolète ! Utilisez 'app register-url' en remplacement !"
|
"app_checkurl_is_deprecated": "Packagers /!\\ 'app checkurl' est obsolète ! Utilisez 'app register-url' en remplacement !",
|
||||||
|
"migration_description_0001_change_cert_group_to_sslcert": "Change les permissions de groupe des certificats de « metronome » à « ssl-cert »",
|
||||||
|
"migration_description_0002_migrate_to_tsig_sha256": "Améliore la sécurité de DynDNDS TSIG en utilisant SHA512 au lieu de MD5",
|
||||||
|
"migration_description_0003_migrate_to_stretch": "Mise à niveau du système vers Debian Stretch et YunoHost 3.0",
|
||||||
|
"migration_0003_backward_impossible": "La migration Stretch n’est pas réversible.",
|
||||||
|
"migration_0003_start": "Démarrage de la migration vers Stretch. Les journaux seront disponibles dans {logfile}.",
|
||||||
|
"migration_0003_patching_sources_list": "Modification de sources.lists…",
|
||||||
|
"migration_0003_main_upgrade": "Démarrage de la mise à niveau principale…",
|
||||||
|
"migration_0003_fail2ban_upgrade": "Démarrage de la mise à niveau de fail2ban…",
|
||||||
|
"migration_0003_restoring_origin_nginx_conf": "Votre fichier /etc/nginx/nginx.conf a été modifié d’une manière ou d’une autre. La migration va d’abords le réinitialiser à son état initial… Le fichier précédent sera disponible en tant que {backup_dest}.",
|
||||||
|
"migration_0003_yunohost_upgrade": "Démarrage de la mise à niveau du paquet YunoHost… La migration terminera, mais la mise à jour réelle aura lieu immédiatement après. Après cette opération terminée, vous pourriez avoir à vous reconnecter à l’administration web.",
|
||||||
|
"migration_0003_not_jessie": "La distribution Debian actuelle n’est pas Jessie !",
|
||||||
|
"migration_0003_system_not_fully_up_to_date": "Votre système n’est pas complètement à jour. Veuillez mener une mise à jour classique avant de lancer à migration à Stretch.",
|
||||||
|
"migration_0003_still_on_jessie_after_main_upgrade": "Quelque chose s’est ma passé pendant la mise à niveau principale : le système est toujours sur Jessie ?!? Pour investiguer le problème, veuillez regarder {log} 🙁…",
|
||||||
|
"migration_0003_general_warning": "Veuillez noter que cette migration est une opération délicate. Si l’équipe YunoHost a fait de son mieux pour la relire et la tester, la migration pourrait tout de même casser des parties de votre système ou de vos applications.\n\nEn conséquence, nous vous recommandons :\n - de lancer une sauvegarde de vos données ou applications critiques ;\n - d’être patient après avoir lancé la migration : selon votre connexion internet et matériel, cela pourrait prendre jusqu'à quelques heures pour que tout soit à niveau.",
|
||||||
|
"migration_0003_problematic_apps_warning": "Veuillez noter que les applications suivantes, éventuellement problématiques, ont été détectées. Il semble qu’elles n’aient pas été installées depuis une liste d’application ou qu’elles ne soit pas marquées «working ». En conséquence, nous ne pouvons pas garantir qu’elles fonctionneront après la mise à niveau : {problematic_apps}",
|
||||||
|
"migration_0003_modified_files": "Veuillez noter que les fichiers suivants ont été détectés comme modifiés manuellement et pourraient être écrasés à la fin de la mise à niveau : {manually_modified_files}",
|
||||||
|
"migrations_list_conflict_pending_done": "Vous ne pouvez pas utiliser --previous et --done simultanément.",
|
||||||
|
"migrations_to_be_ran_manually": "La migration {number} {name} doit être lancée manuellement. Veuillez aller dans Outils > Migration dans l’interface admin, ou lancer `yunohost tools migrations migrate`.",
|
||||||
|
"migrations_need_to_accept_disclaimer": "Pour lancer la migration {number} {name}, vous devez accepter cette clause de non-responsabilité :\n---\n{disclaimer}\n---\nSi vous acceptez de lancer la migration, veuillez relancer la commande avec l’option --accept-disclaimer."
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,86 @@
|
||||||
"admin_password_change_failed": "Impossible de cambiar lo senhal",
|
"admin_password_change_failed": "Impossible de cambiar lo senhal",
|
||||||
"admin_password_changed": "Lo senhal d'administracion es ben estat cambiat",
|
"admin_password_changed": "Lo senhal d'administracion es ben estat cambiat",
|
||||||
"app_already_installed": "{app:s} es ja installat",
|
"app_already_installed": "{app:s} es ja installat",
|
||||||
"app_already_up_to_date": "{app:s} es ja a jorn"
|
"app_already_up_to_date": "{app:s} es ja a jorn",
|
||||||
|
"installation_complete": "Installacion acabada",
|
||||||
|
"app_id_invalid": "Id d’aplicacion incorrècte",
|
||||||
|
"app_install_files_invalid": "Fichièrs d’installacion incorrèctes",
|
||||||
|
"app_no_upgrade": "Pas cap d’aplicacion de metre a jorn",
|
||||||
|
"app_not_correctly_installed": "{app:s} sembla pas ben installat",
|
||||||
|
"app_not_installed": "{app:s} es pas installat",
|
||||||
|
"app_not_properly_removed": "{app:s} es pas estat corrèctament suprimit",
|
||||||
|
"app_removed": "{app:s} es estat suprimit",
|
||||||
|
"app_unknown": "Aplicacion desconeguda",
|
||||||
|
"app_upgrade_app_name": "Mesa a jorn de l’aplicacion {app}...",
|
||||||
|
"app_upgrade_failed": "Impossible de metre a jorn {app:s}",
|
||||||
|
"app_upgrade_some_app_failed": "D’aplicacions se pòdon pas metre a jorn",
|
||||||
|
"app_upgraded": "{app:s} es estat mes a jorn",
|
||||||
|
"appslist_fetched": "Recuperacion de la lista d’aplicacions {appslist:s} corrèctament realizada",
|
||||||
|
"appslist_migrating": "Migracion de la lista d’aplicacion{appslist:s}…",
|
||||||
|
"appslist_name_already_tracked": "I a ja una lista d’aplicacion enregistrada amb lo nom {name:s}.",
|
||||||
|
"appslist_removed": "Supression de la lista d’aplicacions {appslist:s} corrèctament realizada",
|
||||||
|
"appslist_retrieve_bad_format": "Lo fichièr recuperat per la lista d’aplicacions {appslist:s} es pas valid",
|
||||||
|
"appslist_unknown": "La lista d’aplicacions {appslist:s} es desconeguda.",
|
||||||
|
"appslist_url_already_tracked": "I a ja una lista d’aplicacions enregistrada amb l’URL {url:s}.",
|
||||||
|
"ask_current_admin_password": "Senhal administrator actual",
|
||||||
|
"ask_email": "Adreça de corrièl",
|
||||||
|
"ask_firstname": "Prenom",
|
||||||
|
"ask_lastname": "Nom",
|
||||||
|
"ask_list_to_remove": "Lista de suprimir",
|
||||||
|
"ask_main_domain": "Domeni màger",
|
||||||
|
"ask_new_admin_password": "Nòu senhal administrator",
|
||||||
|
"ask_password": "Senhal",
|
||||||
|
"ask_path": "Camin",
|
||||||
|
"backup_action_required": "Devètz precisar çò que cal salvagardar",
|
||||||
|
"backup_app_failed": "Impossible de salvagardar l’aplicacion « {app:s} »",
|
||||||
|
"backup_applying_method_copy": "Còpia de totes los fichièrs dins la salvagarda…",
|
||||||
|
"backup_applying_method_tar": "Creacion de l’archiu tar de la salvagarda…",
|
||||||
|
"backup_archive_name_exists": "Un archiu de salvagarda amb aquesta nom existís ja",
|
||||||
|
"backup_archive_name_unknown": "L’archiu local de salvagarda apelat « {name:s} » es desconegut",
|
||||||
|
"action_invalid": "Accion « {action:s} » incorrècte",
|
||||||
|
"app_argument_choice_invalid": "Causida invalida pel paramètre « {name:s} », cal que siá un de {choices:s}",
|
||||||
|
"app_argument_invalid": "Valor invalida pel paramètre « {name:s} » : {error:s}",
|
||||||
|
"app_argument_required": "Lo paramètre « {name:s} » es requesit",
|
||||||
|
"app_change_url_failed_nginx_reload": "La reaviada de nginx a fracassat. Vaquí la sortida de « nginx -t » :\n{nginx_errors:s}",
|
||||||
|
"app_change_url_identical_domains": "L’ancian e lo novèl coble domeni/camin son identics per {domain:s}{path:s}, pas res a far.",
|
||||||
|
"app_change_url_success": "L’URL de l’aplicacion {app:s} a cambiat per {domain:s}{path:s}",
|
||||||
|
"app_checkurl_is_deprecated": "Packagers /!\\ 'app checkurl' es obsolèt ! Utilizatz 'app register-url' a la plaça !",
|
||||||
|
"app_extraction_failed": "Extraccion dels fichièrs d’installacion impossibla",
|
||||||
|
"app_incompatible": "L’aplicacion {app} es pas compatibla amb vòstra version de YunoHost",
|
||||||
|
"app_location_already_used": "L’aplicacion « {app} » es ja installada a aqueste emplaçament ({path})",
|
||||||
|
"app_manifest_invalid": "Manifest d’aplicacion incorrècte : {error}",
|
||||||
|
"app_package_need_update": "Lo paquet de l’aplicacion {app} deu èsser mes a jorn per seguir los cambiaments de YunoHost",
|
||||||
|
"app_requirements_checking": "Verificacion dels paquets requesida per {app}...",
|
||||||
|
"app_sources_fetch_failed": "Recuperacion dels fichièrs fonts impossibla",
|
||||||
|
"app_unsupported_remote_type": "Lo tipe alonhat utilizat per l’aplicacion es pas suportat",
|
||||||
|
"appslist_retrieve_error": "Impossible de recuperar la lista d’aplicacions alonhadas {appslist:s} : {error:s}",
|
||||||
|
"backup_archive_app_not_found": "L’aplicacion « {app:s} » es pas estada trobada dins l’archiu de la salvagarda",
|
||||||
|
"backup_archive_broken_link": "Impossible d‘accedir a l’archiu de salvagarda (ligam invalid cap a {path:s})",
|
||||||
|
"backup_archive_mount_failed": "Lo montatge de l’archiu de salvagarda a fracassat",
|
||||||
|
"backup_archive_open_failed": "Impossible de dobrir l’archiu de salvagarda",
|
||||||
|
"backup_archive_system_part_not_available": "La part « {part:s} » del sistèma es pas disponibla dins aquesta salvagarda",
|
||||||
|
"backup_cleaning_failed": "Impossible de netejar lo repertòri temporari de salvagarda",
|
||||||
|
"backup_copying_to_organize_the_archive": "Còpia de {size:s} Mio per organizar l’archiu",
|
||||||
|
"backup_created": "Salvagarda acabada",
|
||||||
|
"backup_creating_archive": "Creacion de l’archiu de salvagarda...",
|
||||||
|
"backup_creation_failed": "Impossible de crear la salvagarda",
|
||||||
|
"app_already_installed_cant_change_url": "Aquesta aplicacion es ja installada. Aquesta foncion pòt pas simplament cambiar l’URL. Agachatz « app changeurl » s’es disponible.",
|
||||||
|
"app_change_no_change_url_script": "L’aplicacion {app_name:s} pren pas en compte lo cambiament d’URL, poiretz aver de la metre a jorn.",
|
||||||
|
"app_change_url_no_script": "L’aplicacion {app_name:s} pren pas en compte lo cambiament d’URL, benlèu que vos cal la metre a jorn.",
|
||||||
|
"app_make_default_location_already_used": "Impossible de configurar l’aplicacion « {app} » per defaut pel domeni {domain} perque es ja utilizat per l’aplicacion {other_app}",
|
||||||
|
"app_location_install_failed": "Impossible d’installar l’aplicacion a aqueste emplaçament per causa de conflicte amb l’aplicacion {other_app} qu’es ja installada sus {other_path}",
|
||||||
|
"app_location_unavailable": "Aquesta URL es pas disponibla o en conflicte amb una aplicacion existenta",
|
||||||
|
"appslist_corrupted_json": "Cargament impossible de la lista d’aplicacion. Sembla que {filename:s} siá gastat.",
|
||||||
|
"backup_delete_error": "Impossible de suprimir « {path:s} »",
|
||||||
|
"backup_deleted": "La salvagarda es estada suprimida",
|
||||||
|
"backup_hook_unknown": "Script de salvagarda « {hook:s} » desconegut",
|
||||||
|
"backup_invalid_archive": "Archiu de salvagarda incorrècte",
|
||||||
|
"backup_method_borg_finished": "La salvagarda dins Borg es acabada",
|
||||||
|
"backup_method_copy_finished": "La còpia de salvagarda es acabada",
|
||||||
|
"backup_method_tar_finished": "L’archiu tar de la salvagarda es estat creat",
|
||||||
|
"backup_output_directory_not_empty": "Lo dorsièr de sortida es pas void",
|
||||||
|
"backup_output_directory_required": "Vos cal especificar un dorsièr de sortida per la salvagarda",
|
||||||
|
"backup_running_app_script": "Lançament de l’escript de salvagarda de l’aplicacion « {app:s} »...",
|
||||||
|
"backup_running_hooks": "Execucion dels scripts de salvagarda...",
|
||||||
|
"backup_system_part_failed": "Impossible de salvagardar la part « {part:s} » del sistèma"
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,5 +162,11 @@
|
||||||
"backup_extracting_archive": "Extraindo arquivo de backup...",
|
"backup_extracting_archive": "Extraindo arquivo de backup...",
|
||||||
"backup_hook_unknown": "Gancho de backup '{hook:s}' desconhecido",
|
"backup_hook_unknown": "Gancho de backup '{hook:s}' desconhecido",
|
||||||
"backup_nothings_done": "Não há nada para guardar",
|
"backup_nothings_done": "Não há nada para guardar",
|
||||||
"backup_output_directory_forbidden": "Diretório de saída proibido. Os backups não podem ser criados em /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives subpastas"
|
"backup_output_directory_forbidden": "Diretório de saída proibido. Os backups não podem ser criados em /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives subpastas",
|
||||||
|
"app_already_installed_cant_change_url": "Este aplicativo já está instalado. A URL não pode ser alterada apenas por esta função. Olhe para o `app changeurl` se estiver disponível.",
|
||||||
|
"app_already_up_to_date": "{app:s} já está atualizado",
|
||||||
|
"app_argument_choice_invalid": "Escolha inválida para o argumento '{name:s}', deve ser um dos {choices:s}",
|
||||||
|
"app_argument_invalid": "Valor inválido de argumento '{name:s}': {error:s}",
|
||||||
|
"app_argument_required": "O argumento '{name:s}' é obrigatório",
|
||||||
|
"app_change_url_failed_nginx_reload": "Falha ao reiniciar o nginx. Aqui está o retorno de 'nginx -t':\n{nginx_errors:s}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import requests
|
|
||||||
import base64
|
|
||||||
import time
|
|
||||||
import json
|
|
||||||
import errno
|
|
||||||
import platform
|
|
||||||
from shutil import copy2
|
from shutil import copy2
|
||||||
|
|
||||||
from moulinette import m18n, msettings
|
from moulinette import m18n, msettings
|
||||||
|
@ -16,13 +10,16 @@ from moulinette.utils.filesystem import read_file
|
||||||
|
|
||||||
from yunohost.tools import Migration
|
from yunohost.tools import Migration
|
||||||
from yunohost.app import unstable_apps
|
from yunohost.app import unstable_apps
|
||||||
from yunohost.service import _run_service_command, service_regen_conf, manually_modified_files
|
from yunohost.service import (_run_service_command,
|
||||||
|
manually_modified_files,
|
||||||
|
manually_modified_files_compared_to_debian_default)
|
||||||
from yunohost.utils.filesystem import free_space_in_directory
|
from yunohost.utils.filesystem import free_space_in_directory
|
||||||
from yunohost.utils.packages import get_installed_version
|
from yunohost.utils.packages import get_installed_version
|
||||||
|
|
||||||
logger = getActionLogger('yunohost.migration')
|
logger = getActionLogger('yunohost.migration')
|
||||||
|
|
||||||
YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat" ]
|
YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat"]
|
||||||
|
|
||||||
|
|
||||||
class MyMigration(Migration):
|
class MyMigration(Migration):
|
||||||
"Upgrade the system to Debian Stretch and Yunohost 3.0"
|
"Upgrade the system to Debian Stretch and Yunohost 3.0"
|
||||||
|
@ -42,6 +39,8 @@ class MyMigration(Migration):
|
||||||
logger.warning(m18n.n("migration_0003_start", logfile=self.logfile))
|
logger.warning(m18n.n("migration_0003_start", logfile=self.logfile))
|
||||||
|
|
||||||
# Preparing the upgrade
|
# Preparing the upgrade
|
||||||
|
self.restore_original_nginx_conf_if_needed()
|
||||||
|
|
||||||
logger.warning(m18n.n("migration_0003_patching_sources_list"))
|
logger.warning(m18n.n("migration_0003_patching_sources_list"))
|
||||||
self.patch_apt_sources_list()
|
self.patch_apt_sources_list()
|
||||||
self.backup_files_to_keep()
|
self.backup_files_to_keep()
|
||||||
|
@ -53,7 +52,7 @@ class MyMigration(Migration):
|
||||||
# Main dist-upgrade
|
# Main dist-upgrade
|
||||||
logger.warning(m18n.n("migration_0003_main_upgrade"))
|
logger.warning(m18n.n("migration_0003_main_upgrade"))
|
||||||
_run_service_command("stop", "mysql")
|
_run_service_command("stop", "mysql")
|
||||||
self.apt_dist_upgrade(conf_flags=["old", "def"])
|
self.apt_dist_upgrade(conf_flags=["old", "miss", "def"])
|
||||||
_run_service_command("start", "mysql")
|
_run_service_command("start", "mysql")
|
||||||
if self.debian_major_version() == 8:
|
if self.debian_major_version() == 8:
|
||||||
raise MoulinetteError(m18n.n("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile))
|
raise MoulinetteError(m18n.n("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile))
|
||||||
|
@ -80,7 +79,11 @@ class MyMigration(Migration):
|
||||||
self.upgrade_yunohost_packages()
|
self.upgrade_yunohost_packages()
|
||||||
|
|
||||||
def debian_major_version(self):
|
def debian_major_version(self):
|
||||||
return int(platform.dist()[1][0])
|
# We rely on lsb_release instead of the python module "platform",
|
||||||
|
# because "platform" relies on uname, which on some weird setups does
|
||||||
|
# not behave correctly (still says running Jessie when lsb_release says
|
||||||
|
# Stretch...)
|
||||||
|
return int(check_output("lsb_release -r").split("\t")[1][0])
|
||||||
|
|
||||||
def yunohost_major_version(self):
|
def yunohost_major_version(self):
|
||||||
return int(get_installed_version("yunohost").split('.')[0])
|
return int(get_installed_version("yunohost").split('.')[0])
|
||||||
|
@ -92,7 +95,7 @@ class MyMigration(Migration):
|
||||||
# in the middle and debian version could be >= 9.x but yunohost package
|
# in the middle and debian version could be >= 9.x but yunohost package
|
||||||
# would still be in 2.x...
|
# would still be in 2.x...
|
||||||
if not self.debian_major_version() == 8 \
|
if not self.debian_major_version() == 8 \
|
||||||
and not self.yunohost_major_version() == 2:
|
and not self.yunohost_major_version() == 2:
|
||||||
raise MoulinetteError(m18n.n("migration_0003_not_jessie"))
|
raise MoulinetteError(m18n.n("migration_0003_not_jessie"))
|
||||||
|
|
||||||
# Have > 1 Go free space on /var/ ?
|
# Have > 1 Go free space on /var/ ?
|
||||||
|
@ -102,7 +105,7 @@ class MyMigration(Migration):
|
||||||
# Check system is up to date
|
# Check system is up to date
|
||||||
# (but we don't if 'stretch' is already in the sources.list ...
|
# (but we don't if 'stretch' is already in the sources.list ...
|
||||||
# which means maybe a previous upgrade crashed and we're re-running it)
|
# which means maybe a previous upgrade crashed and we're re-running it)
|
||||||
if not " stretch " in read_file("/etc/apt/sources.list"):
|
if " stretch " not in read_file("/etc/apt/sources.list"):
|
||||||
self.apt_update()
|
self.apt_update()
|
||||||
apt_list_upgradable = check_output("apt list --upgradable -a")
|
apt_list_upgradable = check_output("apt list --upgradable -a")
|
||||||
if "upgradable" in apt_list_upgradable:
|
if "upgradable" in apt_list_upgradable:
|
||||||
|
@ -117,16 +120,20 @@ class MyMigration(Migration):
|
||||||
# in the middle and debian version could be >= 9.x but yunohost package
|
# in the middle and debian version could be >= 9.x but yunohost package
|
||||||
# would still be in 2.x...
|
# would still be in 2.x...
|
||||||
if not self.debian_major_version() == 8 \
|
if not self.debian_major_version() == 8 \
|
||||||
and not self.yunohost_major_version() == 2:
|
and not self.yunohost_major_version() == 2:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Get list of problematic apps ? I.e. not official or community+working
|
# Get list of problematic apps ? I.e. not official or community+working
|
||||||
problematic_apps = unstable_apps()
|
problematic_apps = unstable_apps()
|
||||||
problematic_apps = "".join(["\n - "+app for app in problematic_apps ])
|
problematic_apps = "".join(["\n - " + app for app in problematic_apps])
|
||||||
|
|
||||||
# Manually modified files ? (c.f. yunohost service regen-conf)
|
# Manually modified files ? (c.f. yunohost service regen-conf)
|
||||||
modified_files = manually_modified_files()
|
modified_files = manually_modified_files()
|
||||||
modified_files = "".join(["\n - "+f for f in modified_files ])
|
# We also have a specific check for nginx.conf which some people
|
||||||
|
# modified and needs to be upgraded...
|
||||||
|
if "/etc/nginx/nginx.conf" in manually_modified_files_compared_to_debian_default():
|
||||||
|
modified_files.append("/etc/nginx/nginx.conf")
|
||||||
|
modified_files = "".join(["\n - " + f for f in modified_files])
|
||||||
|
|
||||||
message = m18n.n("migration_0003_general_warning")
|
message = m18n.n("migration_0003_general_warning")
|
||||||
|
|
||||||
|
@ -146,12 +153,13 @@ class MyMigration(Migration):
|
||||||
# This :
|
# This :
|
||||||
# - replace single 'jessie' occurence by 'stretch'
|
# - replace single 'jessie' occurence by 'stretch'
|
||||||
# - comments lines containing "backports"
|
# - comments lines containing "backports"
|
||||||
# - replace 'jessie/updates' by 'strech/updates'
|
# - replace 'jessie/updates' by 'strech/updates' (or same with a -)
|
||||||
# - switch yunohost's repo to forge
|
# - switch yunohost's repo to forge
|
||||||
for f in sources_list:
|
for f in sources_list:
|
||||||
command = "sed -i -e 's@ jessie @ stretch @g' " \
|
command = "sed -i -e 's@ jessie @ stretch @g' " \
|
||||||
"-e '/backports/ s@^#*@#@' " \
|
"-e '/backports/ s@^#*@#@' " \
|
||||||
"-e 's@ jessie/updates @ stretch/updates @g' " \
|
"-e 's@ jessie/updates @ stretch/updates @g' " \
|
||||||
|
"-e 's@ jessie-updates @ stretch-updates @g' " \
|
||||||
"-e 's@repo.yunohost@forge.yunohost@g' " \
|
"-e 's@repo.yunohost@forge.yunohost@g' " \
|
||||||
"{}".format(f)
|
"{}".format(f)
|
||||||
os.system(command)
|
os.system(command)
|
||||||
|
@ -209,14 +217,13 @@ class MyMigration(Migration):
|
||||||
|
|
||||||
wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK)
|
wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK)
|
||||||
|
|
||||||
command = "({} && {}; echo 'Done!') &".format(wait_until_end_of_yunohost_command,
|
command = "({} && {}; echo 'Migration complete!') &".format(wait_until_end_of_yunohost_command,
|
||||||
upgrade_command)
|
upgrade_command)
|
||||||
|
|
||||||
logger.debug("Running command :\n{}".format(command))
|
logger.debug("Running command :\n{}".format(command))
|
||||||
|
|
||||||
os.system(command)
|
os.system(command)
|
||||||
|
|
||||||
|
|
||||||
def apt_dist_upgrade(self, conf_flags):
|
def apt_dist_upgrade(self, conf_flags):
|
||||||
|
|
||||||
# Make apt-get happy
|
# Make apt-get happy
|
||||||
|
@ -248,7 +255,6 @@ class MyMigration(Migration):
|
||||||
# enabled if the user explicitly add --verbose ...
|
# enabled if the user explicitly add --verbose ...
|
||||||
os.system(command)
|
os.system(command)
|
||||||
|
|
||||||
|
|
||||||
# Those are files that should be kept and restored before the final switch
|
# Those are files that should be kept and restored before the final switch
|
||||||
# to yunohost 3.x... They end up being modified by the various dist-upgrades
|
# to yunohost 3.x... They end up being modified by the various dist-upgrades
|
||||||
# (or need to be taken out momentarily), which then blocks the regen-conf
|
# (or need to be taken out momentarily), which then blocks the regen-conf
|
||||||
|
@ -289,3 +295,58 @@ class MyMigration(Migration):
|
||||||
dest_file = f.strip('/').replace("/", "_")
|
dest_file = f.strip('/').replace("/", "_")
|
||||||
copy2(os.path.join(tmp_dir, dest_file), f)
|
copy2(os.path.join(tmp_dir, dest_file), f)
|
||||||
|
|
||||||
|
# On some setups, /etc/nginx/nginx.conf got edited. But this file needs
|
||||||
|
# to be upgraded because of the way the new module system works for nginx.
|
||||||
|
# (in particular, having the line that include the modules at the top)
|
||||||
|
#
|
||||||
|
# So here, if it got edited, we force the restore of the original conf
|
||||||
|
# *before* starting the actual upgrade...
|
||||||
|
#
|
||||||
|
# An alternative strategy that was attempted was to hold the nginx-common
|
||||||
|
# package and have a specific upgrade for it like for fail2ban, but that
|
||||||
|
# leads to apt complaining about not being able to upgrade for shitty
|
||||||
|
# reasons >.>
|
||||||
|
def restore_original_nginx_conf_if_needed(self):
|
||||||
|
if "/etc/nginx/nginx.conf" not in manually_modified_files_compared_to_debian_default():
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists("/etc/nginx/nginx.conf"):
|
||||||
|
return
|
||||||
|
|
||||||
|
# If stretch is in the sources.list, we already started migrating on
|
||||||
|
# stretch so we don't re-do this
|
||||||
|
if " stretch " in read_file("/etc/apt/sources.list"):
|
||||||
|
return
|
||||||
|
|
||||||
|
backup_dest = "/home/yunohost.conf/backup/nginx.conf.bkp_before_stretch"
|
||||||
|
|
||||||
|
logger.warning(m18n.n("migration_0003_restoring_origin_nginx_conf",
|
||||||
|
backup_dest=backup_dest))
|
||||||
|
|
||||||
|
os.system("mv /etc/nginx/nginx.conf %s" % backup_dest)
|
||||||
|
|
||||||
|
command = ""
|
||||||
|
command += " DEBIAN_FRONTEND=noninteractive"
|
||||||
|
command += " APT_LISTCHANGES_FRONTEND=none"
|
||||||
|
command += " apt-get"
|
||||||
|
command += " --fix-broken --show-upgraded --assume-yes"
|
||||||
|
command += ' -o Dpkg::Options::="--force-confmiss"'
|
||||||
|
command += " install --reinstall"
|
||||||
|
command += " nginx-common"
|
||||||
|
|
||||||
|
logger.debug("Running apt command :\n{}".format(command))
|
||||||
|
|
||||||
|
command += " 2>&1 | tee -a {}".format(self.logfile)
|
||||||
|
|
||||||
|
is_api = msettings.get('interface') == 'api'
|
||||||
|
if is_api:
|
||||||
|
callbacks = (
|
||||||
|
lambda l: logger.info(l.rstrip()),
|
||||||
|
lambda l: logger.warning(l.rstrip()),
|
||||||
|
)
|
||||||
|
call_async_output(command, callbacks, shell=True)
|
||||||
|
else:
|
||||||
|
# We do this when running from the cli to have the output of the
|
||||||
|
# command showing in the terminal, since 'info' channel is only
|
||||||
|
# enabled if the user explicitly add --verbose ...
|
||||||
|
os.system(command)
|
||||||
|
|
|
@ -27,12 +27,13 @@ import os
|
||||||
import time
|
import time
|
||||||
import yaml
|
import yaml
|
||||||
import json
|
import json
|
||||||
import glob
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import errno
|
import errno
|
||||||
import shutil
|
import shutil
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
from difflib import unified_diff
|
from difflib import unified_diff
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from moulinette import m18n
|
from moulinette import m18n
|
||||||
from moulinette.core import MoulinetteError
|
from moulinette.core import MoulinetteError
|
||||||
|
@ -75,6 +76,7 @@ def service_add(name, status=None, log=None, runlevel=None):
|
||||||
try:
|
try:
|
||||||
_save_services(services)
|
_save_services(services)
|
||||||
except:
|
except:
|
||||||
|
# we'll get a logger.warning with more details in _save_services
|
||||||
raise MoulinetteError(errno.EIO, m18n.n('service_add_failed', service=name))
|
raise MoulinetteError(errno.EIO, m18n.n('service_add_failed', service=name))
|
||||||
|
|
||||||
logger.success(m18n.n('service_added', service=name))
|
logger.success(m18n.n('service_added', service=name))
|
||||||
|
@ -98,6 +100,7 @@ def service_remove(name):
|
||||||
try:
|
try:
|
||||||
_save_services(services)
|
_save_services(services)
|
||||||
except:
|
except:
|
||||||
|
# we'll get a logger.warning with more details in _save_services
|
||||||
raise MoulinetteError(errno.EIO, m18n.n('service_remove_failed', service=name))
|
raise MoulinetteError(errno.EIO, m18n.n('service_remove_failed', service=name))
|
||||||
|
|
||||||
logger.success(m18n.n('service_removed', service=name))
|
logger.success(m18n.n('service_removed', service=name))
|
||||||
|
@ -113,13 +116,16 @@ def service_start(names):
|
||||||
"""
|
"""
|
||||||
if isinstance(names, str):
|
if isinstance(names, str):
|
||||||
names = [names]
|
names = [names]
|
||||||
|
|
||||||
for name in names:
|
for name in names:
|
||||||
if _run_service_command('start', name):
|
if _run_service_command('start', name):
|
||||||
logger.success(m18n.n('service_started', service=name))
|
logger.success(m18n.n('service_started', service=name))
|
||||||
else:
|
else:
|
||||||
if service_status(name)['status'] != 'running':
|
if service_status(name)['status'] != 'running':
|
||||||
raise MoulinetteError(errno.EPERM,
|
raise MoulinetteError(errno.EPERM,
|
||||||
m18n.n('service_start_failed', service=name))
|
m18n.n('service_start_failed',
|
||||||
|
service=name,
|
||||||
|
logs=_get_journalctl_logs(name)))
|
||||||
logger.info(m18n.n('service_already_started', service=name))
|
logger.info(m18n.n('service_already_started', service=name))
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,7 +145,9 @@ def service_stop(names):
|
||||||
else:
|
else:
|
||||||
if service_status(name)['status'] != 'inactive':
|
if service_status(name)['status'] != 'inactive':
|
||||||
raise MoulinetteError(errno.EPERM,
|
raise MoulinetteError(errno.EPERM,
|
||||||
m18n.n('service_stop_failed', service=name))
|
m18n.n('service_stop_failed',
|
||||||
|
service=name,
|
||||||
|
logs=_get_journalctl_logs(name)))
|
||||||
logger.info(m18n.n('service_already_stopped', service=name))
|
logger.info(m18n.n('service_already_stopped', service=name))
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,7 +166,9 @@ def service_enable(names):
|
||||||
logger.success(m18n.n('service_enabled', service=name))
|
logger.success(m18n.n('service_enabled', service=name))
|
||||||
else:
|
else:
|
||||||
raise MoulinetteError(errno.EPERM,
|
raise MoulinetteError(errno.EPERM,
|
||||||
m18n.n('service_enable_failed', service=name))
|
m18n.n('service_enable_failed',
|
||||||
|
service=name,
|
||||||
|
logs=_get_journalctl_logs(name)))
|
||||||
|
|
||||||
|
|
||||||
def service_disable(names):
|
def service_disable(names):
|
||||||
|
@ -176,7 +186,9 @@ def service_disable(names):
|
||||||
logger.success(m18n.n('service_disabled', service=name))
|
logger.success(m18n.n('service_disabled', service=name))
|
||||||
else:
|
else:
|
||||||
raise MoulinetteError(errno.EPERM,
|
raise MoulinetteError(errno.EPERM,
|
||||||
m18n.n('service_disable_failed', service=name))
|
m18n.n('service_disable_failed',
|
||||||
|
service=name,
|
||||||
|
logs=_get_journalctl_logs(name)))
|
||||||
|
|
||||||
|
|
||||||
def service_status(names=[]):
|
def service_status(names=[]):
|
||||||
|
@ -202,46 +214,54 @@ def service_status(names=[]):
|
||||||
raise MoulinetteError(errno.EINVAL,
|
raise MoulinetteError(errno.EINVAL,
|
||||||
m18n.n('service_unknown', service=name))
|
m18n.n('service_unknown', service=name))
|
||||||
|
|
||||||
status = None
|
# this "service" isn't a service actually so we skip it
|
||||||
if services[name].get('status') == 'service':
|
#
|
||||||
status = 'service %s status' % name
|
# the historical reason is because regenconf has been hacked into the
|
||||||
elif "status" in services[name]:
|
# service part of YunoHost will in some situation we need to regenconf
|
||||||
status = str(services[name]['status'])
|
# for things that aren't services
|
||||||
else:
|
# the hack was to add fake services...
|
||||||
|
# we need to extract regenconf from service at some point, also because
|
||||||
|
# some app would really like to use it
|
||||||
|
if "status" in services[name] and services[name]["status"] is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
runlevel = 5
|
status = _get_service_information_from_systemd(name)
|
||||||
if 'runlevel' in services[name].keys():
|
|
||||||
runlevel = int(services[name]['runlevel'])
|
|
||||||
|
|
||||||
result[name] = {'status': 'unknown', 'loaded': 'unknown'}
|
result[name] = {
|
||||||
|
'status': str(status.get("SubState", "unknown")),
|
||||||
# Retrieve service status
|
'loaded': "enabled" if str(status.get("LoadState", "unknown")) == "loaded" else str(status.get("LoadState", "unknown")),
|
||||||
try:
|
'active': str(status.get("ActiveState", "unknown")),
|
||||||
ret = subprocess.check_output(status, stderr=subprocess.STDOUT,
|
'active_at': {
|
||||||
shell=True)
|
"timestamp": str(status.get("ActiveEnterTimestamp", "unknown")),
|
||||||
except subprocess.CalledProcessError as e:
|
"human": datetime.fromtimestamp(status.get("ActiveEnterTimestamp") / 1000000).strftime("%F %X"),
|
||||||
if 'usage:' in e.output.lower():
|
},
|
||||||
logger.warning(m18n.n('service_status_failed', service=name))
|
'description': str(status.get("Description", "")),
|
||||||
else:
|
'service_file_path': str(status.get("FragmentPath", "unknown")),
|
||||||
result[name]['status'] = 'inactive'
|
}
|
||||||
else:
|
|
||||||
result[name]['status'] = 'running'
|
|
||||||
|
|
||||||
# Retrieve service loading
|
|
||||||
rc_path = glob.glob("/etc/rc%d.d/S[0-9][0-9]%s" % (runlevel, name))
|
|
||||||
if len(rc_path) == 1 and os.path.islink(rc_path[0]):
|
|
||||||
result[name]['loaded'] = 'enabled'
|
|
||||||
elif os.path.isfile("/etc/init.d/%s" % name):
|
|
||||||
result[name]['loaded'] = 'disabled'
|
|
||||||
else:
|
|
||||||
result[name]['loaded'] = 'not-found'
|
|
||||||
|
|
||||||
if len(names) == 1:
|
if len(names) == 1:
|
||||||
return result[names[0]]
|
return result[names[0]]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _get_service_information_from_systemd(service):
|
||||||
|
"this is the equivalent of 'systemctl status $service'"
|
||||||
|
import dbus
|
||||||
|
|
||||||
|
d = dbus.SystemBus()
|
||||||
|
|
||||||
|
systemd = d.get_object('org.freedesktop.systemd1','/org/freedesktop/systemd1')
|
||||||
|
manager = dbus.Interface(systemd, 'org.freedesktop.systemd1.Manager')
|
||||||
|
|
||||||
|
service_path = manager.GetUnit(service + ".service")
|
||||||
|
service_proxy = d.get_object('org.freedesktop.systemd1', service_path)
|
||||||
|
|
||||||
|
# unit_proxy = dbus.Interface(service_proxy, 'org.freedesktop.systemd1.Unit',)
|
||||||
|
properties_interface = dbus.Interface(service_proxy, 'org.freedesktop.DBus.Properties')
|
||||||
|
|
||||||
|
return properties_interface.GetAll('org.freedesktop.systemd1.Unit')
|
||||||
|
|
||||||
|
|
||||||
def service_log(name, number=50):
|
def service_log(name, number=50):
|
||||||
"""
|
"""
|
||||||
Log every log files of a service
|
Log every log files of a service
|
||||||
|
@ -256,21 +276,33 @@ def service_log(name, number=50):
|
||||||
if name not in services.keys():
|
if name not in services.keys():
|
||||||
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=name))
|
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=name))
|
||||||
|
|
||||||
if 'log' in services[name]:
|
if 'log' not in services[name]:
|
||||||
log_list = services[name]['log']
|
|
||||||
result = {}
|
|
||||||
if not isinstance(log_list, list):
|
|
||||||
log_list = [log_list]
|
|
||||||
|
|
||||||
for log_path in log_list:
|
|
||||||
if os.path.isdir(log_path):
|
|
||||||
for log in [f for f in os.listdir(log_path) if os.path.isfile(os.path.join(log_path, f)) and f[-4:] == '.log']:
|
|
||||||
result[os.path.join(log_path, log)] = _tail(os.path.join(log_path, log), int(number))
|
|
||||||
else:
|
|
||||||
result[log_path] = _tail(log_path, int(number))
|
|
||||||
else:
|
|
||||||
raise MoulinetteError(errno.EPERM, m18n.n('service_no_log', service=name))
|
raise MoulinetteError(errno.EPERM, m18n.n('service_no_log', service=name))
|
||||||
|
|
||||||
|
log_list = services[name]['log']
|
||||||
|
|
||||||
|
if not isinstance(log_list, list):
|
||||||
|
log_list = [log_list]
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for log_path in log_list:
|
||||||
|
# log is a file, read it
|
||||||
|
if not os.path.isdir(log_path):
|
||||||
|
result[log_path] = _tail(log_path, int(number))
|
||||||
|
continue
|
||||||
|
|
||||||
|
for log_file in os.listdir(log_path):
|
||||||
|
log_file_path = os.path.join(log_path, log_file)
|
||||||
|
# not a file : skip
|
||||||
|
if not os.path.isfile(log_file_path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not log_file.endswith(".log"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
result[log_file_path] = _tail(log_file_path, int(number))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -292,14 +324,19 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
# Return the list of pending conf
|
# Return the list of pending conf
|
||||||
if list_pending:
|
if list_pending:
|
||||||
pending_conf = _get_pending_conf(names)
|
pending_conf = _get_pending_conf(names)
|
||||||
if with_diff:
|
|
||||||
for service, conf_files in pending_conf.items():
|
if not with_diff:
|
||||||
for system_path, pending_path in conf_files.items():
|
return pending_conf
|
||||||
pending_conf[service][system_path] = {
|
|
||||||
'pending_conf': pending_path,
|
for service, conf_files in pending_conf.items():
|
||||||
'diff': _get_files_diff(
|
for system_path, pending_path in conf_files.items():
|
||||||
system_path, pending_path, True),
|
|
||||||
}
|
pending_conf[service][system_path] = {
|
||||||
|
'pending_conf': pending_path,
|
||||||
|
'diff': _get_files_diff(
|
||||||
|
system_path, pending_path, True),
|
||||||
|
}
|
||||||
|
|
||||||
return pending_conf
|
return pending_conf
|
||||||
|
|
||||||
# Clean pending conf directory
|
# Clean pending conf directory
|
||||||
|
@ -323,12 +360,15 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
# create the pending conf directory for the service
|
# create the pending conf directory for the service
|
||||||
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
|
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
|
||||||
filesystem.mkdir(service_pending_path, 0755, True, uid='root')
|
filesystem.mkdir(service_pending_path, 0755, True, uid='root')
|
||||||
|
|
||||||
# return the arguments to pass to the script
|
# return the arguments to pass to the script
|
||||||
return pre_args + [service_pending_path, ]
|
return pre_args + [service_pending_path, ]
|
||||||
|
|
||||||
pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call)
|
pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call)
|
||||||
|
|
||||||
# Update the services name
|
# Update the services name
|
||||||
names = pre_result['succeed'].keys()
|
names = pre_result['succeed'].keys()
|
||||||
|
|
||||||
if not names:
|
if not names:
|
||||||
raise MoulinetteError(errno.EIO,
|
raise MoulinetteError(errno.EIO,
|
||||||
m18n.n('service_regenconf_failed',
|
m18n.n('service_regenconf_failed',
|
||||||
|
@ -386,6 +426,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
'service_conf_file_manually_removed',
|
'service_conf_file_manually_removed',
|
||||||
conf=system_path))
|
conf=system_path))
|
||||||
conf_status = 'removed'
|
conf_status = 'removed'
|
||||||
|
|
||||||
# -> system conf is not managed yet
|
# -> system conf is not managed yet
|
||||||
elif not saved_hash:
|
elif not saved_hash:
|
||||||
logger.debug("> system conf is not managed yet")
|
logger.debug("> system conf is not managed yet")
|
||||||
|
@ -409,6 +450,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
logger.warning(m18n.n('service_conf_file_kept_back',
|
logger.warning(m18n.n('service_conf_file_kept_back',
|
||||||
conf=system_path, service=service))
|
conf=system_path, service=service))
|
||||||
conf_status = 'unmanaged'
|
conf_status = 'unmanaged'
|
||||||
|
|
||||||
# -> system conf has not been manually modified
|
# -> system conf has not been manually modified
|
||||||
elif system_hash == saved_hash:
|
elif system_hash == saved_hash:
|
||||||
if to_remove:
|
if to_remove:
|
||||||
|
@ -421,6 +463,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
logger.debug("> system conf is already up-to-date")
|
logger.debug("> system conf is already up-to-date")
|
||||||
os.remove(pending_path)
|
os.remove(pending_path)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.debug("> system conf has been manually modified")
|
logger.debug("> system conf has been manually modified")
|
||||||
if system_hash == new_hash:
|
if system_hash == new_hash:
|
||||||
|
@ -457,6 +500,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
'service_conf_updated' if not dry_run else
|
'service_conf_updated' if not dry_run else
|
||||||
'service_conf_would_be_updated',
|
'service_conf_would_be_updated',
|
||||||
service=service))
|
service=service))
|
||||||
|
|
||||||
if succeed_regen and not dry_run:
|
if succeed_regen and not dry_run:
|
||||||
_update_conf_hashes(service, conf_hashes)
|
_update_conf_hashes(service, conf_hashes)
|
||||||
|
|
||||||
|
@ -480,6 +524,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
|
||||||
else:
|
else:
|
||||||
regen_conf_files = ''
|
regen_conf_files = ''
|
||||||
return post_args + [regen_conf_files, ]
|
return post_args + [regen_conf_files, ]
|
||||||
|
|
||||||
hook_callback('conf_regen', names, pre_callback=_pre_call)
|
hook_callback('conf_regen', names, pre_callback=_pre_call)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -498,11 +543,11 @@ def _run_service_command(action, service):
|
||||||
if service not in services.keys():
|
if service not in services.keys():
|
||||||
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=service))
|
raise MoulinetteError(errno.EINVAL, m18n.n('service_unknown', service=service))
|
||||||
|
|
||||||
cmd = None
|
possible_actions = ['start', 'stop', 'restart', 'reload', 'enable', 'disable']
|
||||||
if action in ['start', 'stop', 'restart', 'reload', 'enable', 'disable']:
|
if action not in possible_actions:
|
||||||
cmd = 'systemctl %s %s' % (action, service)
|
raise ValueError("Unknown action '%s', available actions are: %s" % (action, ", ".join(possible_actions)))
|
||||||
else:
|
|
||||||
raise ValueError("Unknown action '%s'" % action)
|
cmd = 'systemctl %s %s' % (action, service)
|
||||||
|
|
||||||
need_lock = services[service].get('need_lock', False) \
|
need_lock = services[service].get('need_lock', False) \
|
||||||
and action in ['start', 'stop', 'restart', 'reload']
|
and action in ['start', 'stop', 'restart', 'reload']
|
||||||
|
@ -517,14 +562,17 @@ def _run_service_command(action, service):
|
||||||
PID = _give_lock(action, service, p)
|
PID = _give_lock(action, service, p)
|
||||||
# Wait for the command to complete
|
# Wait for the command to complete
|
||||||
p.communicate()
|
p.communicate()
|
||||||
# Remove the lock if one was given
|
|
||||||
if need_lock and PID != 0:
|
|
||||||
_remove_lock(PID)
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
# TODO: Log output?
|
# TODO: Log output?
|
||||||
logger.warning(m18n.n('service_cmd_exec_failed', command=' '.join(e.cmd)))
|
logger.warning(m18n.n('service_cmd_exec_failed', command=' '.join(e.cmd)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Remove the lock if one was given
|
||||||
|
if need_lock and PID != 0:
|
||||||
|
_remove_lock(PID)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -557,6 +605,7 @@ def _give_lock(action, service, p):
|
||||||
return son_PID
|
return son_PID
|
||||||
|
|
||||||
def _remove_lock(PID_to_remove):
|
def _remove_lock(PID_to_remove):
|
||||||
|
# FIXME ironically not concurrency safe because it's not atomic...
|
||||||
|
|
||||||
PIDs = filesystem.read_file(MOULINETTE_LOCK).split("\n")
|
PIDs = filesystem.read_file(MOULINETTE_LOCK).split("\n")
|
||||||
PIDs_to_keep = [ PID for PID in PIDs if int(PID) != PID_to_remove ]
|
PIDs_to_keep = [ PID for PID in PIDs if int(PID) != PID_to_remove ]
|
||||||
|
@ -574,6 +623,12 @@ def _get_services():
|
||||||
except:
|
except:
|
||||||
return {}
|
return {}
|
||||||
else:
|
else:
|
||||||
|
# some services are marked as None to remove them from YunoHost
|
||||||
|
# filter this
|
||||||
|
for key, value in services.items():
|
||||||
|
if value is None:
|
||||||
|
del services[key]
|
||||||
|
|
||||||
return services
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
@ -585,12 +640,15 @@ def _save_services(services):
|
||||||
services -- A dict of managed services with their parameters
|
services -- A dict of managed services with their parameters
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# TODO: Save to custom services.yml
|
try:
|
||||||
with open('/etc/yunohost/services.yml', 'w') as f:
|
with open('/etc/yunohost/services.yml', 'w') as f:
|
||||||
yaml.safe_dump(services, f, default_flow_style=False)
|
yaml.safe_dump(services, f, default_flow_style=False)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning('Error while saving services, exception: %s', e, exc_info=1)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def _tail(file, n, offset=None):
|
def _tail(file, n):
|
||||||
"""
|
"""
|
||||||
Reads a n lines from f with an offset of offset lines. The return
|
Reads a n lines from f with an offset of offset lines. The return
|
||||||
value is a tuple in the form ``(lines, has_more)`` where `has_more` is
|
value is a tuple in the form ``(lines, has_more)`` where `has_more` is
|
||||||
|
@ -598,7 +656,7 @@ def _tail(file, n, offset=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
avg_line_length = 74
|
avg_line_length = 74
|
||||||
to_read = n + (offset or 0)
|
to_read = n
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(file, 'r') as f:
|
with open(file, 'r') as f:
|
||||||
|
@ -609,13 +667,17 @@ def _tail(file, n, offset=None):
|
||||||
# woops. apparently file is smaller than what we want
|
# woops. apparently file is smaller than what we want
|
||||||
# to step back, go to the beginning instead
|
# to step back, go to the beginning instead
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
|
|
||||||
pos = f.tell()
|
pos = f.tell()
|
||||||
lines = f.read().splitlines()
|
lines = f.read().splitlines()
|
||||||
|
|
||||||
if len(lines) >= to_read or pos == 0:
|
if len(lines) >= to_read or pos == 0:
|
||||||
return lines[-to_read:offset and -offset or None]
|
return lines[-to_read]
|
||||||
|
|
||||||
avg_line_length *= 1.3
|
avg_line_length *= 1.3
|
||||||
|
|
||||||
except IOError:
|
except IOError as e:
|
||||||
|
logger.warning("Error while tailing file '%s': %s", file, e, exc_info=1)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@ -627,36 +689,39 @@ def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True):
|
||||||
header can also be removed if skip_header is True.
|
header can also be removed if skip_header is True.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
contents = [[], []]
|
with open(orig_file, 'r') as orig_file:
|
||||||
for i, path in enumerate((orig_file, new_file)):
|
orig_file = orig_file.readlines()
|
||||||
try:
|
|
||||||
with open(path, 'r') as f:
|
with open(new_file, 'r') as new_file:
|
||||||
contents[i] = f.readlines()
|
new_file.readlines()
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Compare files and format output
|
# Compare files and format output
|
||||||
diff = unified_diff(contents[0], contents[1])
|
diff = unified_diff(orig_file, new_file)
|
||||||
|
|
||||||
if skip_header:
|
if skip_header:
|
||||||
for i in range(2):
|
try:
|
||||||
try:
|
next(diff)
|
||||||
next(diff)
|
next(diff)
|
||||||
except:
|
except:
|
||||||
break
|
pass
|
||||||
|
|
||||||
if as_string:
|
if as_string:
|
||||||
result = ''.join(line for line in diff)
|
return ''.join(diff).rstrip()
|
||||||
return result.rstrip()
|
|
||||||
return diff
|
return diff
|
||||||
|
|
||||||
|
|
||||||
def _calculate_hash(path):
|
def _calculate_hash(path):
|
||||||
"""Calculate the MD5 hash of a file"""
|
"""Calculate the MD5 hash of a file"""
|
||||||
hasher = hashlib.md5()
|
hasher = hashlib.md5()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
hasher.update(f.read())
|
hasher.update(f.read())
|
||||||
return hasher.hexdigest()
|
return hasher.hexdigest()
|
||||||
except IOError:
|
|
||||||
|
except IOError as e:
|
||||||
|
logger.warning("Error while calculating file '%s' hash: %s", path, e, exc_info=1)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -672,25 +737,33 @@ def _get_pending_conf(services=[]):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
if not os.path.isdir(PENDING_CONF_DIR):
|
if not os.path.isdir(PENDING_CONF_DIR):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
if not services:
|
if not services:
|
||||||
services = os.listdir(PENDING_CONF_DIR)
|
services = os.listdir(PENDING_CONF_DIR)
|
||||||
|
|
||||||
for name in services:
|
for name in services:
|
||||||
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
|
service_pending_path = os.path.join(PENDING_CONF_DIR, name)
|
||||||
|
|
||||||
if not os.path.isdir(service_pending_path):
|
if not os.path.isdir(service_pending_path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
path_index = len(service_pending_path)
|
path_index = len(service_pending_path)
|
||||||
service_conf = {}
|
service_conf = {}
|
||||||
|
|
||||||
for root, dirs, files in os.walk(service_pending_path):
|
for root, dirs, files in os.walk(service_pending_path):
|
||||||
for filename in files:
|
for filename in files:
|
||||||
pending_path = os.path.join(root, filename)
|
pending_path = os.path.join(root, filename)
|
||||||
service_conf[pending_path[path_index:]] = pending_path
|
service_conf[pending_path[path_index:]] = pending_path
|
||||||
|
|
||||||
if service_conf:
|
if service_conf:
|
||||||
result[name] = service_conf
|
result[name] = service_conf
|
||||||
else:
|
else:
|
||||||
# remove empty directory
|
# remove empty directory
|
||||||
shutil.rmtree(service_pending_path, ignore_errors=True)
|
shutil.rmtree(service_pending_path, ignore_errors=True)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -702,9 +775,11 @@ def _get_conf_hashes(service):
|
||||||
if service not in services:
|
if service not in services:
|
||||||
logger.debug("Service %s is not in services.yml yet.", service)
|
logger.debug("Service %s is not in services.yml yet.", service)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
elif services[service] is None or 'conffiles' not in services[service]:
|
elif services[service] is None or 'conffiles' not in services[service]:
|
||||||
logger.debug("No configuration files for service %s.", service)
|
logger.debug("No configuration files for service %s.", service)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return services[service]['conffiles']
|
return services[service]['conffiles']
|
||||||
|
|
||||||
|
@ -737,11 +812,14 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
|
||||||
backup_path = os.path.join(BACKUP_CONF_DIR, '{0}-{1}'.format(
|
backup_path = os.path.join(BACKUP_CONF_DIR, '{0}-{1}'.format(
|
||||||
system_conf.lstrip('/'), time.strftime("%Y%m%d.%H%M%S")))
|
system_conf.lstrip('/'), time.strftime("%Y%m%d.%H%M%S")))
|
||||||
backup_dir = os.path.dirname(backup_path)
|
backup_dir = os.path.dirname(backup_path)
|
||||||
|
|
||||||
if not os.path.isdir(backup_dir):
|
if not os.path.isdir(backup_dir):
|
||||||
filesystem.mkdir(backup_dir, 0755, True)
|
filesystem.mkdir(backup_dir, 0755, True)
|
||||||
|
|
||||||
shutil.copy2(system_conf, backup_path)
|
shutil.copy2(system_conf, backup_path)
|
||||||
logger.info(m18n.n('service_conf_file_backed_up',
|
logger.info(m18n.n('service_conf_file_backed_up',
|
||||||
conf=system_conf, backup=backup_path))
|
conf=system_conf, backup=backup_path))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not new_conf:
|
if not new_conf:
|
||||||
os.remove(system_conf)
|
os.remove(system_conf)
|
||||||
|
@ -749,19 +827,26 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
|
||||||
conf=system_conf))
|
conf=system_conf))
|
||||||
else:
|
else:
|
||||||
system_dir = os.path.dirname(system_conf)
|
system_dir = os.path.dirname(system_conf)
|
||||||
|
|
||||||
if not os.path.isdir(system_dir):
|
if not os.path.isdir(system_dir):
|
||||||
filesystem.mkdir(system_dir, 0755, True)
|
filesystem.mkdir(system_dir, 0755, True)
|
||||||
|
|
||||||
shutil.copyfile(new_conf, system_conf)
|
shutil.copyfile(new_conf, system_conf)
|
||||||
logger.info(m18n.n('service_conf_file_updated',
|
logger.info(m18n.n('service_conf_file_updated',
|
||||||
conf=system_conf))
|
conf=system_conf))
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.warning("Exception while trying to regenerate conf '%s': %s", system_conf, e, exc_info=1)
|
||||||
if not new_conf and os.path.exists(system_conf):
|
if not new_conf and os.path.exists(system_conf):
|
||||||
logger.warning(m18n.n('service_conf_file_remove_failed',
|
logger.warning(m18n.n('service_conf_file_remove_failed',
|
||||||
conf=system_conf),
|
conf=system_conf),
|
||||||
exc_info=1)
|
exc_info=1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
elif new_conf:
|
elif new_conf:
|
||||||
try:
|
try:
|
||||||
|
# From documentation:
|
||||||
|
# Raise an exception if an os.stat() call on either pathname fails.
|
||||||
|
# (os.stats returns a series of information from a file like type, size...)
|
||||||
copy_succeed = os.path.samefile(system_conf, new_conf)
|
copy_succeed = os.path.samefile(system_conf, new_conf)
|
||||||
except:
|
except:
|
||||||
copy_succeed = False
|
copy_succeed = False
|
||||||
|
@ -771,8 +856,10 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
|
||||||
conf=system_conf, new=new_conf),
|
conf=system_conf, new=new_conf),
|
||||||
exc_info=1)
|
exc_info=1)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def manually_modified_files():
|
def manually_modified_files():
|
||||||
|
|
||||||
# We do this to have --quiet, i.e. don't throw a whole bunch of logs
|
# We do this to have --quiet, i.e. don't throw a whole bunch of logs
|
||||||
|
@ -793,3 +880,21 @@ def manually_modified_files():
|
||||||
output.append(filename)
|
output.append(filename)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def _get_journalctl_logs(service):
|
||||||
|
try:
|
||||||
|
return subprocess.check_output("journalctl -xn -u %s" % service, shell=True)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
return "error while get services logs from journalctl:\n%s" % traceback.format_exc()
|
||||||
|
|
||||||
|
|
||||||
|
def manually_modified_files_compared_to_debian_default():
|
||||||
|
|
||||||
|
# from https://serverfault.com/a/90401
|
||||||
|
r = subprocess.check_output("dpkg-query -W -f='${Conffiles}\n' '*' \
|
||||||
|
| awk 'OFS=\" \"{print $2,$1}' \
|
||||||
|
| md5sum -c 2>/dev/null \
|
||||||
|
| awk -F': ' '$2 !~ /OK/{print $1}'", shell=True)
|
||||||
|
return r.strip().split("\n")
|
||||||
|
|
|
@ -668,7 +668,7 @@ def _check_if_vulnerable_to_meltdown():
|
||||||
stderr=subprocess.STDOUT)
|
stderr=subprocess.STDOUT)
|
||||||
|
|
||||||
output, _ = call.communicate()
|
output, _ = call.communicate()
|
||||||
assert call.returncode == 0
|
assert call.returncode in (0, 2, 3), "Return code: %s" % call.returncode
|
||||||
|
|
||||||
CVEs = json.loads(output)
|
CVEs = json.loads(output)
|
||||||
assert len(CVEs) == 1
|
assert len(CVEs) == 1
|
||||||
|
|
|
@ -1,16 +1,57 @@
|
||||||
Spectre & Meltdown Checker
|
Spectre & Meltdown Checker
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
A simple shell script to tell if your Linux installation is vulnerable against the 3 "speculative execution" CVEs that were made public early 2018.
|
A shell script to tell if your system is vulnerable against the 3 "speculative execution" CVEs that were made public early 2018.
|
||||||
|
|
||||||
Without options, it'll inspect your currently running kernel.
|
Supported operating systems:
|
||||||
You can also specify a kernel image on the command line, if you'd like to inspect a kernel you're not running.
|
- Linux (all versions, flavors and distros)
|
||||||
|
- BSD (FreeBSD, NetBSD, DragonFlyBSD)
|
||||||
|
|
||||||
The script will do its best to detect mitigations, including backported non-vanilla patches, regardless of the advertised kernel version number.
|
Supported architectures:
|
||||||
|
- x86 (32 bits)
|
||||||
|
- amd64/x86_64 (64 bits)
|
||||||
|
- ARM and ARM64
|
||||||
|
- other architectures will work, but mitigations (if they exist) might not always be detected
|
||||||
|
|
||||||
|
For Linux systems, the script will detect mitigations, including backported non-vanilla patches, regardless of the advertised kernel version number and the distribution (such as Debian, Ubuntu, CentOS, RHEL, Fedora, openSUSE, Arch, ...), it also works if you've compiled your own kernel.
|
||||||
|
|
||||||
|
For BSD systems, the detection will work as long as the BSD you're using supports `cpuctl` and `linprocfs` (this is not the case of OpenBSD for example).
|
||||||
|
|
||||||
|
## Easy way to run the script
|
||||||
|
|
||||||
|
- Get the latest version of the script using `curl` *or* `wget`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -L https://meltdown.ovh -o spectre-meltdown-checker.sh
|
||||||
|
wget https://meltdown.ovh -O spectre-meltdown-checker.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
- Inspect the script. You never blindly run scripts you downloaded from the Internet, do you?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
vim spectre-meltdown-checker.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
- When you're ready, run the script as root
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x spectre-meltdown-checker.sh
|
||||||
|
sudo ./spectre-meltdown-checker.sh
|
||||||
|
```
|
||||||
|
|
||||||
## Example of script output
|
## Example of script output
|
||||||
|
|
||||||

|
- Intel Haswell CPU running under Ubuntu 16.04 LTS
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- AMD Ryzen running under OpenSUSE Tumbleweed
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Batch mode (JSON flavor)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Quick summary of the CVEs
|
## Quick summary of the CVEs
|
||||||
|
|
||||||
|
@ -38,8 +79,10 @@ The script will do its best to detect mitigations, including backported non-vani
|
||||||
This tool does its best to determine whether your system is immune (or has proper mitigations in place) for the collectively named "speculative execution" vulnerabilities. It doesn't attempt to run any kind of exploit, and can't guarantee that your system is secure, but rather helps you verifying whether your system has the known correct mitigations in place.
|
This tool does its best to determine whether your system is immune (or has proper mitigations in place) for the collectively named "speculative execution" vulnerabilities. It doesn't attempt to run any kind of exploit, and can't guarantee that your system is secure, but rather helps you verifying whether your system has the known correct mitigations in place.
|
||||||
However, some mitigations could also exist in your kernel that this script doesn't know (yet) how to detect, or it might falsely detect mitigations that in the end don't work as expected (for example, on backported or modified kernels).
|
However, some mitigations could also exist in your kernel that this script doesn't know (yet) how to detect, or it might falsely detect mitigations that in the end don't work as expected (for example, on backported or modified kernels).
|
||||||
|
|
||||||
Your system exposure also depends on your CPU. As of now, AMD and ARM processors are marked as immune to some or all of these vulnerabilities (except some specific ARM models). All Intel processors manufactured since circa 1995 are thought to be vulnerable. Whatever processor one uses, one might seek more information from the manufacturer of that processor and/or of the device in which it runs.
|
Your system exposure also depends on your CPU. As of now, AMD and ARM processors are marked as immune to some or all of these vulnerabilities (except some specific ARM models). All Intel processors manufactured since circa 1995 are thought to be vulnerable, except some specific/old models, such as some early Atoms. Whatever processor one uses, one might seek more information from the manufacturer of that processor and/or of the device in which it runs.
|
||||||
|
|
||||||
The nature of the discovered vulnerabilities being quite new, the landscape of vulnerable processors can be expected to change over time, which is why this script makes the assumption that all CPUs are vulnerable, except if the manufacturer explicitly stated otherwise in a verifiable public announcement.
|
The nature of the discovered vulnerabilities being quite new, the landscape of vulnerable processors can be expected to change over time, which is why this script makes the assumption that all CPUs are vulnerable, except if the manufacturer explicitly stated otherwise in a verifiable public announcement.
|
||||||
|
|
||||||
|
Please also note that for Spectre vulnerabilities, all software can possibly be exploited, this tool only verifies that the kernel (which is the core of the system) you're using has the proper protections in place. Verifying all the other software is out of the scope of this tool. As a general measure, ensure you always have the most up to date stable versions of all the software you use, especially for those who are exposed to the world, such as network daemons and browsers.
|
||||||
|
|
||||||
This tool has been released in the hope that it'll be useful, but don't use it to jump to conclusions about your security.
|
This tool has been released in the hope that it'll be useful, but don't use it to jump to conclusions about your security.
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue