Merge branch 'unstable' into feature224-envvarforscripts

This commit is contained in:
Jérôme Lebleu 2016-04-25 17:41:45 +02:00
commit b3cfbc21ac
72 changed files with 2561 additions and 2066 deletions

View file

@ -5,10 +5,6 @@ import os
import sys import sys
import argparse import argparse
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize, get_locale
# Either we are in a development environment or not # Either we are in a development environment or not
IN_DEVEL = False IN_DEVEL = False
@ -35,6 +31,11 @@ if IN_DEVEL:
LOG_DIR = os.path.join(basedir, 'log') LOG_DIR = os.path.join(basedir, 'log')
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize, get_locale
# Initialization & helpers functions ----------------------------------- # Initialization & helpers functions -----------------------------------
def _die(message, title='Error:'): def _die(message, title='Error:'):
@ -50,7 +51,7 @@ def _parse_cli_args():
help="Don't use actions map cache", help="Don't use actions map cache",
) )
parser.add_argument('--output-as', parser.add_argument('--output-as',
choices=['json', 'plain'], default=None, choices=['json', 'plain', 'none'], default=None,
help="Output result in another format", help="Output result in another format",
) )
parser.add_argument('--debug', parser.add_argument('--debug',
@ -149,6 +150,11 @@ def _init_moulinette(debug=False, verbose=False, quiet=False):
'handlers': [], 'handlers': [],
'propagate': True, 'propagate': True,
}, },
'moulinette.interface': {
'level': level,
'handlers': handlers,
'propagate': False,
},
}, },
'root': { 'root': {
'level': level, 'level': level,

View file

@ -5,10 +5,6 @@ import os
import sys import sys
import argparse import argparse
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize
# Either we are in a development environment or not # Either we are in a development environment or not
IN_DEVEL = False IN_DEVEL = False
@ -39,6 +35,11 @@ if IN_DEVEL:
LOG_DIR = os.path.join(basedir, 'log') LOG_DIR = os.path.join(basedir, 'log')
import moulinette
from moulinette.actionsmap import ActionsMap
from moulinette.interfaces.cli import colorize
# Initialization & helpers functions ----------------------------------- # Initialization & helpers functions -----------------------------------
def _die(message, title='Error:'): def _die(message, title='Error:'):

77
bin/yunopaste Executable file
View file

@ -0,0 +1,77 @@
#!/bin/bash
set -e
set -u
PASTE_URL="https://paste.yunohost.org"
_die() {
printf "Error: %s\n" "$*"
exit 1
}
check_dependencies() {
curl -V > /dev/null 2>&1 || _die "This script requires curl."
}
paste_data() {
json=$(curl -X POST -s -d "$1" "${PASTE_URL}/documents")
[[ -z "$json" ]] && _die "Unable to post the data to the server."
key=$(echo "$json" \
| python -c 'import json,sys;o=json.load(sys.stdin);print o["key"]' \
2>/dev/null)
[[ -z "$key" ]] && _die "Unable to parse the server response."
echo "${PASTE_URL}/${key}"
}
usage() {
printf "Usage: ${0} [OPTION]...
Read from input stream and paste the data to the YunoHost
Haste server.
For example, to paste the output of the YunoHost diagnosis, you
can simply execute the following:
yunohost tools diagnosis | ${0}
It will return the URL where you can access the pasted data.
Options:
-h, --help show this help message and exit
"
}
main() {
# parse options
while (( ${#} )); do
case "${1}" in
--help|-h)
usage
exit 0
;;
*)
echo "Unknown parameter detected: ${1}" >&2
echo >&2
usage >&2
exit 1
;;
esac
shift 1
done
# check input stream
read -t 0 || {
echo -e "Invalid usage: No input is provided.\n" >&2
usage
exit 1
}
paste_data "$(cat)"
}
check_dependencies
main "${@}"

View file

@ -516,6 +516,7 @@ app:
initdb: initdb:
action_help: Create database and initialize it with optionnal attached script action_help: Create database and initialize it with optionnal attached script
api: POST /tools/initdb api: POST /tools/initdb
deprecated: true
arguments: arguments:
user: user:
help: Name of the DB user help: Name of the DB user
@ -956,60 +957,36 @@ service:
default: 50 default: 50
type: int type: int
### service_regenconf() ### service_regen_conf()
regenconf: regen-conf:
action_help: > action_help: Regenerate the configuration file(s) for a service
Regenerate the configuration file(s) for a service and compare the result
with the existing configuration file.
Prints the differences between files if any.
api: PUT /services/regenconf api: PUT /services/regenconf
configuration: configuration:
lock: false lock: false
deprecated_alias:
- regenconf
arguments: arguments:
-s: names:
full: --service help: Services name to regenerate configuration of
help: Regenerate configuration for a specfic service nargs: "*"
-f: metavar: NAME
full: --force -d:
help: Override the current configuration with the newly generated one, even if it has been modified full: --with-diff
help: Show differences in case of configuration changes
action: store_true action: store_true
### service_safecopy()
safecopy:
action_help: >
Check if the specific file has been modified and display differences.
Stores the file hash in the services.yml file
arguments:
new_conf_file:
help: Path to the desired conf file
conf_file:
help: Path to the targeted conf file
-s:
full: --service
help: Service name attached to the conf file
extra:
required: True
-f: -f:
full: --force full: --force
help: Override the current configuration with the newly generated one, even if it has been modified help: >
Override all manual modifications in configuration
files
action: store_true action: store_true
-n:
### service_saferemove() full: --dry-run
saferemove: help: Show what would have been regenerated
action_help: > action: store_true
Check if the specific file has been modified before removing it. -p:
Backup the file in /home/yunohost.backup full: --list-pending
arguments: help: List pending configuration files and exit
conf_file:
help: Path to the targeted conf file
-s:
full: --service
help: Service name attached to the conf file
extra:
required: True
-f:
full: --force
help: Force file deletion
action: store_true action: store_true
############################# #############################
@ -1378,8 +1355,15 @@ hook:
nargs: "*" nargs: "*"
-a: -a:
full: --args full: --args
help: Ordered list of arguments to pass to the script help: Ordered list of arguments to pass to the scripts
nargs: "*" nargs: "*"
-q:
full: --no-trace
help: Do not print each command that will be executed
action: store_true
-d:
full: --chdir
help: The directory from where the scripts will be executed
### hook_exec() ### hook_exec()
exec: exec:
@ -1389,7 +1373,8 @@ hook:
help: Path of the script to execute help: Path of the script to execute
-a: -a:
full: --args full: --args
help: Arguments to pass to the script help: Ordered list of arguments to pass to the script
nargs: "*"
--raise-on-error: --raise-on-error:
help: Raise if the script returns a non-zero exit code help: Raise if the script returns a non-zero exit code
action: store_true action: store_true

View file

@ -48,27 +48,33 @@ ynh_package_install() {
# usage: ynh_package_install_from_equivs controlfile # usage: ynh_package_install_from_equivs controlfile
# | arg: controlfile - path of the equivs control file # | arg: controlfile - path of the equivs control file
ynh_package_install_from_equivs() { ynh_package_install_from_equivs() {
controlfile=$1
# install equivs package as needed
ynh_package_is_installed 'equivs' \ ynh_package_is_installed 'equivs' \
|| ynh_package_install equivs || ynh_package_install equivs
# retrieve package information # retrieve package information
pkgname=$(grep '^Package: ' $1 | cut -d' ' -f 2) pkgname=$(grep '^Package: ' $controlfile | cut -d' ' -f 2)
pkgversion=$(grep '^Version: ' $1 | cut -d' ' -f 2) pkgversion=$(grep '^Version: ' $controlfile | cut -d' ' -f 2)
[[ -z "$pkgname" || -z "$pkgversion" ]] \ [[ -z "$pkgname" || -z "$pkgversion" ]] \
&& echo "Invalid control file" && exit 1 && echo "Invalid control file" && exit 1
controlfile=$(readlink -f "$1")
# update packages cache # update packages cache
ynh_package_update ynh_package_update
# build and install the package # build and install the package
TMPDIR=$(ynh_mkdir_tmp) TMPDIR=$(ynh_mkdir_tmp)
(cd $TMPDIR \ (cp "$controlfile" "${TMPDIR}/control" \
&& equivs-build "$controlfile" 1>/dev/null \ && cd "$TMPDIR" \
&& equivs-build ./control 1>/dev/null \
&& sudo dpkg --force-depends \ && sudo dpkg --force-depends \
-i "./${pkgname}_${pkgversion}_all.deb" 2>&1 \ -i "./${pkgname}_${pkgversion}_all.deb" 2>&1 \
&& sudo apt-get -f -y -qq install) \ && sudo apt-get -f -y -qq install) \
&& ([[ -n "$TMPDIR" ]] && rm -rf $TMPDIR) && ([[ -n "$TMPDIR" ]] && rm -rf $TMPDIR)
# check if the package is actually installed
ynh_package_is_installed "$pkgname"
} }
# Remove package(s) # Remove package(s)

View file

@ -20,6 +20,17 @@ ynh_user_get_info() {
sudo yunohost user info "$1" --output-as plain | ynh_get_plain_key "$2" sudo yunohost user info "$1" --output-as plain | ynh_get_plain_key "$2"
} }
# Get the list of YunoHost users
#
# example: for u in $(ynh_user_list); do ...
#
# usage: ynh_user_list
# | ret: string - one username per line
ynh_user_list() {
sudo yunohost user list --output-as plain --quiet \
| awk '/^##username$/{getline; print}'
}
# Check if a user exists on the system # Check if a user exists on the system
# #
# usage: ynh_system_user_exists username # usage: ynh_system_user_exists username

View file

@ -1,4 +1,4 @@
backup_dir="$1/conf/ynh/mysql" backup_dir="$1/conf/ynh/mysql"
sudo mkdir -p $backup_dir sudo mkdir -p $backup_dir
sudo cp -a /etc/yunohost/mysql $backup_dir/ sudo cp -a /etc/yunohost/mysql "${backup_dir}/root_pwd"

View file

@ -1,7 +1,4 @@
backup_dir="$1/conf/ynh/" backup_dir="$1/conf/ynh"
backup_dir_legacy="$1/yunohost/"
sudo mkdir -p $backup_dir sudo mkdir -p $backup_dir
sudo mkdir -p $backup_dir_legacy
sudo cp -a /etc/yunohost/current_host $backup_dir sudo cp -a /etc/yunohost/current_host "${backup_dir}/current_host"
sudo cp -a /etc/yunohost/current_host $backup_dir_legacy

116
data/hooks/conf_regen/01-yunohost Normal file → Executable file
View file

@ -1,25 +1,111 @@
#!/bin/bash
set -e set -e
force=$1 services_path="/etc/yunohost/services.yml"
do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
cd /usr/share/yunohost/templates/yunohost cd /usr/share/yunohost/templates/yunohost
sudo mkdir -p /etc/yunohost [[ -d /etc/yunohost ]] || mkdir -p /etc/yunohost
if [ ! -f /etc/yunohost/current_host ]; then # set default current_host
echo "yunohost.org" | sudo tee /etc/yunohost/current_host [[ -f /etc/yunohost/current_host ]] \
|| echo "yunohost.org" > /etc/yunohost/current_host
# copy default services and firewall
[[ -f $services_path ]] \
|| cp services.yml "$services_path"
[[ -f /etc/yunohost/firewall.yml ]] \
|| cp firewall.yml /etc/yunohost/firewall.yml
# allow users to access /media directory
[[ -d /etc/skel/media ]] \
|| (mkdir -p /media && ln -s /media /etc/skel/media)
}
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/templates/yunohost
# update services.yml
if [[ -f $services_path ]]; then
tmp_services_path="${services_path}-tmp"
new_services_path="${services_path}-new"
sudo cp "$services_path" "$tmp_services_path"
_update_services "$new_services_path" || {
sudo mv "$tmp_services_path" "$services_path"
exit 1
}
if [[ -f $new_services_path ]]; then
# replace services.yml with new one
sudo mv "$new_services_path" "$services_path"
sudo mv "$tmp_services_path" "${services_path}-old"
else
sudo rm -f "$tmp_services_path"
fi fi
else
if [ ! -f /etc/yunohost/firewall.yml ]; then
sudo cp firewall.yml /etc/yunohost/firewall.yml
fi
if [ ! -f /etc/yunohost/services.yml ]; then
sudo cp services.yml /etc/yunohost/services.yml sudo cp services.yml /etc/yunohost/services.yml
fi fi
}
# Allow users to access /media directory _update_services() {
if [ ! -d /etc/skel/media ]; then sudo python2 - << EOF
mkdir -p /media import yaml
ln -s /media /etc/skel/ with open('services.yml') as f:
fi new_services = yaml.load(f)
with open('/etc/yunohost/services.yml') as f:
services = yaml.load(f)
updated = False
for service, conf in new_services.items():
# remove service with empty conf
if not conf:
if service in services:
print("removing '{0}' from services".format(service))
del services[service]
updated = True
# add new service
elif not services.get(service, None):
print("adding '{0}' to services".format(service))
services[service] = conf
updated = True
# update service conf
else:
conffiles = services[service].pop('conffiles', {})
if services[service] != conf:
print("update '{0}' service".format(service))
services[service].update(conf)
updated = True
if conffiles:
services[service]['conffiles'] = conffiles
if updated:
with open('/etc/yunohost/services.yml-new', 'w') as f:
yaml.safe_dump(services, f, default_flow_style=False)
EOF
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
;;
init)
do_init_regen
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

129
data/hooks/conf_regen/02-ssl Normal file → Executable file
View file

@ -1,64 +1,93 @@
#!/bin/bash
set -e set -e
force=$1 ssl_dir="/usr/share/yunohost/yunohost-config/ssl/yunoCA"
function safe_copy () { do_init_regen() {
if [ ! -f /etc/yunohost/installed ]; then if [[ $EUID -ne 0 ]]; then
sudo cp $1 $2 echo "You must be root to run this script" 1>&2
else exit 1
if [ $force ]; then
sudo yunohost service safecopy \
-s ssl $1 $2 --force
else
sudo yunohost service safecopy \
-s ssl $1 $2
fi fi
# create certs and SSL directories
mkdir -p "/etc/yunohost/certs/yunohost.org"
mkdir -p "${ssl_dir}/"{ca,certs,crl,newcerts}
# initialize some files
[[ -f "${ssl_dir}/serial" ]] \
|| echo "00" > "${ssl_dir}/serial"
[[ -f "${ssl_dir}/index.txt" ]] \
|| touch "${ssl_dir}/index.txt"
openssl_conf="/usr/share/yunohost/templates/ssl/openssl.cnf"
# create default certificates
if [[ ! -f /etc/yunohost/certs/yunohost.org/ca.pem ]]; then
openssl req -x509 -new -config "$openssl_conf" \
-days 3650 -out "${ssl_dir}/ca/cacert.pem" \
-keyout "${ssl_dir}/ca/cakey.pem" -nodes -batch 2>&1
cp "${ssl_dir}/ca/cacert.pem" \
/etc/yunohost/certs/yunohost.org/ca.pem
ln -sf /etc/yunohost/certs/yunohost.org/ca.pem \
/etc/ssl/certs/ca-yunohost_crt.pem
update-ca-certificates
fi
if [[ ! -f /etc/yunohost/certs/yunohost.org/crt.pem ]]; then
openssl req -new -config "$openssl_conf" \
-days 730 -out "${ssl_dir}/certs/yunohost_csr.pem" \
-keyout "${ssl_dir}/certs/yunohost_key.pem" -nodes -batch 2>&1
openssl ca -config "$openssl_conf" \
-days 730 -in "${ssl_dir}/certs/yunohost_csr.pem" \
-out "${ssl_dir}/certs/yunohost_crt.pem" -batch 2>&1
last_cert=$(ls $ssl_dir/newcerts/*.pem | sort -V | tail -n 1)
chmod 640 "${ssl_dir}/certs/yunohost_key.pem"
chmod 640 "$last_cert"
cp "${ssl_dir}/certs/yunohost_key.pem" \
/etc/yunohost/certs/yunohost.org/key.pem
cp "$last_cert" \
/etc/yunohost/certs/yunohost.org/crt.pem
ln -sf /etc/yunohost/certs/yunohost.org/crt.pem \
/etc/ssl/certs/yunohost_crt.pem
ln -sf /etc/yunohost/certs/yunohost.org/key.pem \
/etc/ssl/private/yunohost_key.pem
fi fi
} }
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/templates/ssl cd /usr/share/yunohost/templates/ssl
ssl_dir=/usr/share/yunohost/yunohost-config/ssl/yunoCA
sudo mkdir -p /etc/yunohost/certs/yunohost.org install -D -m 644 openssl.cnf "${pending_dir}/${ssl_dir}/openssl.cnf"
sudo mkdir -p $ssl_dir/{ca,certs,crl,newcerts} }
safe_copy openssl.cnf $ssl_dir/openssl.cnf do_post_regen() {
regen_conf_files=$1
[ -f $ssl_dir/serial ] \ # TODO: regenerate certificates if conf changed?
|| (echo "00" | sudo tee $ssl_dir/serial) }
[ -f $ssl_dir/index.txt ] \ FORCE=${2:-0}
|| sudo touch $ssl_dir/index.txt DRY_RUN=${3:-0}
if [ ! -f /etc/yunohost/certs/yunohost.org/ca.pem ]; then case "$1" in
sudo openssl req -x509 -new -config $ssl_dir/openssl.cnf \ pre)
-days 3650 -out $ssl_dir/ca/cacert.pem \ do_pre_regen $4
-keyout $ssl_dir/ca/cakey.pem -nodes -batch ;;
sudo cp $ssl_dir/ca/cacert.pem \ post)
/etc/yunohost/certs/yunohost.org/ca.pem do_post_regen $4
sudo ln -sf /etc/yunohost/certs/yunohost.org/ca.pem \ ;;
/etc/ssl/certs/ca-yunohost_crt.pem init)
sudo update-ca-certificates do_init_regen
fi ;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
if [ ! -f /etc/yunohost/certs/yunohost.org/crt.pem ]; then exit 0
sudo openssl req -new -config $ssl_dir/openssl.cnf \
-days 730 -out $ssl_dir/certs/yunohost_csr.pem \
-keyout $ssl_dir/certs/yunohost_key.pem -nodes -batch
sudo openssl ca -config $ssl_dir/openssl.cnf \
-days 730 -in $ssl_dir/certs/yunohost_csr.pem \
-out $ssl_dir/certs/yunohost_crt.pem -batch
last_cert=$(ls $ssl_dir/newcerts/*.pem | sort -V | tail -n 1)
sudo chmod 640 $ssl_dir/certs/yunohost_key.pem
sudo chmod 640 $last_cert
sudo cp $ssl_dir/certs/yunohost_key.pem \
/etc/yunohost/certs/yunohost.org/key.pem
sudo cp $last_cert \
/etc/yunohost/certs/yunohost.org/crt.pem
sudo ln -sf /etc/yunohost/certs/yunohost.org/crt.pem \
/etc/ssl/certs/yunohost_crt.pem
sudo ln -sf /etc/yunohost/certs/yunohost.org/key.pem \
/etc/ssl/private/yunohost_key.pem
fi

57
data/hooks/conf_regen/03-ssh Normal file → Executable file
View file

@ -1,30 +1,45 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [ $force ]; then
sudo yunohost service safecopy \
-s ssh \
$1 $2 \
--force
else
sudo yunohost service safecopy \
-s ssh \
$1 $2
fi
}
cd /usr/share/yunohost/templates/ssh cd /usr/share/yunohost/templates/ssh
# Only overwrite SSH configuration on an ISO installation # only overwrite SSH configuration on an ISO installation
if [ ! -f /etc/yunohost/from_script ]; then if [[ ! -f /etc/yunohost/from_script ]]; then
# do not listen to IPv6 if unavailable
[[ -f /proc/net/if_inet6 ]] \
|| sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config
# Do not listen to IPv6 if unavailable install -D -m 644 sshd_config "${pending_dir}/etc/ssh/sshd_config"
if [ ! -f /proc/net/if_inet6 ]; then
sudo sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config
fi fi
safe_copy sshd_config /etc/ssh/sshd_config }
sudo service ssh restart do_post_regen() {
regen_conf_files=$1
if [[ ! -f /etc/yunohost/from_script ]]; then
[[ -z "$regen_conf_files" ]] \
|| sudo service ssh restart
fi fi
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

125
data/hooks/conf_regen/06-slapd Normal file → Executable file
View file

@ -1,67 +1,92 @@
#!/bin/bash
set -e set -e
force=$1 do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
function safe_copy () { do_pre_regen ""
if [ ! -f /etc/yunohost/installed ]; then
sudo cp $1 $2 # fix some permissions
else chown root:openldap /etc/ldap/slapd.conf
if [[ "$force" == "True" ]]; then chown -R openldap:openldap /etc/ldap/schema/
sudo yunohost service safecopy \
-s slapd $1 $2 --force # check the slapd config file at first
else slaptest -Q -u -f /etc/ldap/slapd.conf
sudo yunohost service safecopy \
-s slapd $1 $2 # regenerate LDAP config directory from slapd.conf
fi rm -Rf /etc/ldap/slapd.d
fi mkdir /etc/ldap/slapd.d
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1
chown -R openldap:openldap /etc/ldap/slapd.d/
service slapd restart
} }
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/templates/slapd cd /usr/share/yunohost/templates/slapd
# Remove legacy configuration file # remove legacy configuration file
[ ! -f /etc/yunohost/installed ] \ [ ! -f /etc/ldap/slapd-yuno.conf ] \
|| sudo yunohost service saferemove -s slapd \ || touch "${pending_dir}/etc/ldap/slapd-yuno.conf"
/etc/ldap/slapd-yuno.conf
# Retrieve current backend # create needed directories
backend=$(sudo slapcat -n 0 | sed -n 's/^dn: olcDatabase={1}\(.*\),cn=config$/\1/p') ldap_dir="${pending_dir}/etc/ldap"
schema_dir="${ldap_dir}/schema"
mkdir -p "$ldap_dir" "$schema_dir"
# Save current database in case of a backend change # copy configuration files
BACKEND_CHANGE=0 cp -a ldap.conf slapd.conf "$ldap_dir"
BACKUP_DIR="/var/backups/dc=yunohost,dc=org-${backend}-$(date +%s)" cp -a sudo.schema mailserver.schema "$schema_dir"
if [[ -n "$backend" && "$backend" != "mdb" && "$force" == "True" ]]; then
BACKEND_CHANGE=1
sudo mkdir -p "$BACKUP_DIR"
sudo slapcat -b dc=yunohost,dc=org \
-l "${BACKUP_DIR}/dc=yunohost-dc=org.ldif"
fi
safe_copy sudo.schema /etc/ldap/schema/sudo.schema install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd"
safe_copy mailserver.schema /etc/ldap/schema/mailserver.schema }
safe_copy ldap.conf /etc/ldap/ldap.conf
safe_copy slapd.default /etc/default/slapd
safe_copy slapd.conf /etc/ldap/slapd.conf
# Fix some permissions do_post_regen() {
regen_conf_files=$1
# fix some permissions
sudo chown root:openldap /etc/ldap/slapd.conf sudo chown root:openldap /etc/ldap/slapd.conf
sudo chown -R openldap:openldap /etc/ldap/schema/ sudo chown -R openldap:openldap /etc/ldap/schema/
sudo chown -R openldap:openldap /etc/ldap/slapd.d/ sudo chown -R openldap:openldap /etc/ldap/slapd.d/
# Check the slapd config file at first [ -z "$regen_conf_files" ] && exit 0
# retrieve current and new backends
curr_backend=$(sudo slapcat -n 0 \
| sed -n 's/^dn: olcDatabase={1}\(.*\),cn=config$/\1/p')
new_backend=$(grep '^database' /etc/ldap/slapd.conf | awk '{print $2}')
# save current database in case of a backend change
backend_change=0
backup_dir="/var/backups/dc=yunohost,dc=org-${curr_backend}-$(date +%s)"
if [[ -n "$curr_backend" && "$curr_backend" != "$new_backend" ]]; then
backend_change=1
sudo mkdir -p "$backup_dir"
sudo slapcat -b dc=yunohost,dc=org \
-l "${backup_dir}/dc=yunohost-dc=org.ldif"
fi
# check the slapd config file at first
sudo slaptest -Q -u -f /etc/ldap/slapd.conf sudo slaptest -Q -u -f /etc/ldap/slapd.conf
if [[ $BACKEND_CHANGE -eq 1 ]]; then if [[ $backend_change -eq 1 ]]; then
# Regenerate LDAP config directory and import database as root # regenerate LDAP config directory and import database as root
# since the admin user may be unavailable # since the admin user may be unavailable
sudo sh -c "rm -Rf /etc/ldap/slapd.d; sudo sh -c "rm -Rf /etc/ldap/slapd.d;
mkdir /etc/ldap/slapd.d; mkdir /etc/ldap/slapd.d;
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d; slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d;
chown -R openldap:openldap /etc/ldap/slapd.d; chown -R openldap:openldap /etc/ldap/slapd.d;
slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \
-l '${BACKUP_DIR}/dc=yunohost-dc=org.ldif'; -l '${backup_dir}/dc=yunohost-dc=org.ldif';
chown -R openldap:openldap /var/lib/ldap" 2>&1 chown -R openldap:openldap /var/lib/ldap" 2>&1
else else
# Regenerate LDAP config directory from slapd.conf # regenerate LDAP config directory from slapd.conf
sudo rm -Rf /etc/ldap/slapd.d sudo rm -Rf /etc/ldap/slapd.d
sudo mkdir /etc/ldap/slapd.d sudo mkdir /etc/ldap/slapd.d
sudo slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1 sudo slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d/ 2>&1
@ -69,3 +94,25 @@ else
fi fi
sudo service slapd force-reload sudo service slapd force-reload
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
init)
do_init_regen
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

48
data/hooks/conf_regen/09-nslcd Normal file → Executable file
View file

@ -1,26 +1,36 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s nslcd \
$1 $2 \
--force
else
sudo yunohost service safecopy \
-s nslcd \
$1 $2
fi
}
cd /usr/share/yunohost/templates/nslcd cd /usr/share/yunohost/templates/nslcd
safe_copy nslcd.conf /etc/nslcd.conf install -D -m 644 nslcd.conf "${pending_dir}/etc/nslcd.conf"
}
# Fix: Add a blank line at the end of the file do_post_regen() {
# to avoid nscld restart failure regen_conf_files=$1
echo -e "\n" | sudo tee -a /etc/nslcd.conf
sudo service nslcd restart [[ -z "$regen_conf_files" ]] \
|| sudo service nslcd restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

106
data/hooks/conf_regen/12-metronome Normal file → Executable file
View file

@ -1,66 +1,76 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s metronome \
$1 $2 \
--force
else
sudo yunohost service safecopy \
-s metronome \
$1 $2
fi
}
cd /usr/share/yunohost/templates/metronome cd /usr/share/yunohost/templates/metronome
# Copy configuration files # create directories for pending conf
metronome_dir="${pending_dir}/etc/metronome"
metronome_conf_dir="${metronome_dir}/conf.d"
mkdir -p "$metronome_conf_dir"
# retrieve variables
main_domain=$(cat /etc/yunohost/current_host) main_domain=$(cat /etc/yunohost/current_host)
cat metronome.cfg.lua.sed \ domain_list=$(sudo yunohost domain list --output-as plain --quiet)
| sed "s/{{ main_domain }}/$main_domain/g" \
| sudo tee metronome.cfg.lua
safe_copy metronome.cfg.lua /etc/metronome/metronome.cfg.lua
need_restart=False # install main conf file
sudo mkdir -p /etc/metronome/conf.d cat metronome.cfg.lua \
| sed "s/{{ main_domain }}/${main_domain}/g" \
> "${metronome_dir}/metronome.cfg.lua"
domain_list=$(sudo yunohost domain list --output-as plain) # add domain conf files
# Copy a configuration file for each YunoHost domain
for domain in $domain_list; do for domain in $domain_list; do
sanitzed_domain="$(echo $domain | sed 's/\./%2e/g')" cat domain.tpl.cfg.lua \
sudo mkdir -p /var/lib/metronome/$sanitzed_domain/pep | sed "s/{{ domain }}/${domain}/g" \
> "${metronome_conf_dir}/${domain}.cfg.lua"
cat domain.cfg.lua.sed \
| sed "s/{{ domain }}/$domain/g" \
| sudo tee $domain.cfg.lua
if [[ $(safe_copy $domain.cfg.lua /etc/metronome/conf.d/$domain.cfg.lua | tail -n1) == "True" ]]; then
need_restart=True
fi
done done
# Remove old domains files # remove old domain conf files
for file in /etc/metronome/conf.d/*; do conf_files=$(ls -1 /etc/metronome/conf.d \
domain=$(echo $file \ | awk '/^[^\.]+\.[^\.]+.*\.cfg\.lua$/ { print $1 }')
| sed 's|/etc/metronome/conf.d/||' \ for file in $conf_files; do
| sed 's|.cfg.lua||') domain=${file%.cfg.lua}
sanitzed_domain="$(echo $domain | sed 's/\./%2e/g')"
[[ $domain_list =~ $domain ]] \ [[ $domain_list =~ $domain ]] \
|| ([[ $(sudo yunohost service saferemove -s metronome $file | tail -n1) == "True" ]] \ || touch "${metronome_conf_dir}/${file}"
&& sudo rm -rf /var/lib/metronome/$sanitzed_domain)
done done
}
# Create domain directory do_post_regen() {
regen_conf_files=$1
# fix some permissions
sudo chown -R metronome: /var/lib/metronome/ sudo chown -R metronome: /var/lib/metronome/
sudo chown -R metronome: /etc/metronome/conf.d/ sudo chown -R metronome: /etc/metronome/conf.d/
# Restart if need be # retrieve variables
if [[ "$need_restart" == "True" ]]; then domain_list=$(sudo yunohost domain list --output-as plain --quiet)
sudo service metronome restart
else # create metronome directories for domains
sudo service metronome reload \ for domain in $domain_list; do
sudo mkdir -p "/var/lib/metronome/${domain//./%2e}/pep"
done
[[ -z "$regen_conf_files" ]] \
|| sudo service metronome restart || sudo service metronome restart
fi }
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

153
data/hooks/conf_regen/15-nginx Normal file → Executable file
View file

@ -1,86 +1,101 @@
#!/bin/bash
set -e set -e
force=$1 do_init_regen() {
if [[ $EUID -ne 0 ]]; then
echo "You must be root to run this script" 1>&2
exit 1
fi
function safe_copy () { do_pre_regen ""
if [ ! -f /etc/yunohost/installed ]; then
sudo cp $1 $2
else
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s nginx \
$1 $2 \
--force
else
sudo yunohost service safecopy \
-s nginx \
$1 $2
fi
fi
} }
do_pre_regen() {
pending_dir=$1
cd /usr/share/yunohost/templates/nginx cd /usr/share/yunohost/templates/nginx
# Copy plain single configuration files nginx_dir="${pending_dir}/etc/nginx"
files="ssowat.conf nginx_conf_dir="${nginx_dir}/conf.d"
global.conf mkdir -p "$nginx_conf_dir"
yunohost_admin.conf
yunohost_admin.conf.inc
yunohost_api.conf.inc
yunohost_panel.conf.inc"
for file in $files; do # install plain conf files
safe_copy $file /etc/nginx/conf.d/$file cp plain/* "$nginx_conf_dir"
done
# probably run with init: just disable default site, restart NGINX and exit
if [[ -z "$pending_dir" ]]; then
rm -f "${nginx_dir}/sites-enabled/default"
service nginx restart
exit 0
fi
if [ -f /etc/yunohost/installed ]; then # retrieve variables
need_restart=False
domain_list=$(sudo yunohost domain list --output-as plain)
# Copy a configuration file for each YunoHost domain
for domain in $domain_list; do
sudo mkdir -p /etc/nginx/conf.d/$domain.d
cat server.conf.sed \
| sed "s/{{ domain }}/$domain/g" \
| sudo tee $domain.conf
[[ $(safe_copy $domain.conf /etc/nginx/conf.d/$domain.conf | tail -n1) == "True" ]] \
&& need_restart=True
[ -f /etc/nginx/conf.d/$domain.d/yunohost_local.conf ] \
&& [[ $main_domain != $domain ]] \
&& sudo yunohost service saferemove -s nginx \
/etc/nginx/conf.d/$domain.d/yunohost_local.conf
done
# Copy 'yunohost.local' to the main domain conf directory
main_domain=$(cat /etc/yunohost/current_host) main_domain=$(cat /etc/yunohost/current_host)
safe_copy yunohost_local.conf \ domain_list=$(sudo yunohost domain list --output-as plain --quiet)
/etc/nginx/conf.d/$main_domain.d/yunohost_local.conf
# add domain conf files
for domain in $domain_list; do
domain_conf_dir="${nginx_conf_dir}/${domain}.d"
mkdir -p "$domain_conf_dir"
# Remove old domains files # NGINX server configuration
for file in /etc/nginx/conf.d/*.*.conf; do cat server.tpl.conf \
domain=$(echo $file \ | sed "s/{{ domain }}/${domain}/g" \
| sed 's|/etc/nginx/conf.d/||' \ > "${nginx_conf_dir}/${domain}.conf"
| sed 's|.conf||')
[[ $domain_list =~ $domain ]] \ [[ $main_domain != $domain ]] \
|| ([[ $(sudo yunohost service saferemove -s nginx $file) == "True" ]] \ && touch "${domain_conf_dir}/yunohost_local.conf" \
&& (sudo rm -r /etc/nginx/conf.d/$domain.d || true)) || cp yunohost_local.conf "${domain_conf_dir}/yunohost_local.conf"
done done
else # remove old domain conf files
[ ! -f /etc/nginx/sites-available/default ] \ conf_files=$(ls -1 /etc/nginx/conf.d \
|| sudo rm -f /etc/nginx/sites-enabled/default | awk '/^[^\.]+\.[^\.]+.*\.conf$/ { print $1 }')
need_restart=True for file in $conf_files; do
fi domain=${file%.conf}
[[ $domain_list =~ $domain ]] \
|| touch "${nginx_conf_dir}/${file}"
done
# disable default site
mkdir -p "${nginx_dir}/sites-enabled"
touch "${nginx_dir}/sites-enabled/default"
}
do_post_regen() {
regen_conf_files=$1
[ -z "$regen_conf_files" ] && exit 0
# retrieve variables
domain_list=$(sudo yunohost domain list --output-as plain --quiet)
# create NGINX conf directories for domains
for domain in $domain_list; do
sudo mkdir -p "/etc/nginx/conf.d/${domain}.d"
done
# Restart if need be
if [[ "$need_restart" == "True" ]]; then
sudo service nginx restart sudo service nginx restart
else }
sudo service nginx reload \
|| sudo service nginx restart FORCE=${2:-0}
fi DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
init)
do_init_regen
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

84
data/hooks/conf_regen/19-postfix Normal file → Executable file
View file

@ -1,56 +1,56 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s postfix \
$1 $2 \
--force
else
sudo yunohost service safecopy \
-s postfix \
$1 $2
fi
}
cd /usr/share/yunohost/templates/postfix cd /usr/share/yunohost/templates/postfix
# Copy plain single configuration files postfix_dir="${pending_dir}/etc/postfix"
files="header_checks mkdir -p "$postfix_dir"
ldap-accounts.cf
ldap-aliases.cf
ldap-domains.cf
master.cf
sender_canonical
smtp_reply_filter"
for file in $files; do # install plain conf files
safe_copy $file /etc/postfix/$file cp plain/* "$postfix_dir"
done
# prepare main.cf conf file
main_domain=$(cat /etc/yunohost/current_host) main_domain=$(cat /etc/yunohost/current_host)
cat main.cf \
| sed "s/{{ main_domain }}/${main_domain}/g" \
> "${postfix_dir}/main.cf"
# Replace main domain in the main configuration file # adapt it for IPv4-only hosts
cat main.cf.sed \
| sed "s/{{ main_domain }}/$main_domain/g" \
| sudo tee main.cf
# And adapt it to IPv4-only hosts
if [ ! -f /proc/net/if_inet6 ]; then if [ ! -f /proc/net/if_inet6 ]; then
sudo sed -i \ sed -i \
's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \ 's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \
main.cf "${postfix_dir}/main.cf"
sed -i \
sudo sed -i \ 's/inet_interfaces = all/&\ninet_protocols = ipv4/' \
's/inet_interfaces = all/inet_interfaces = all\ninet_protocols = ipv4/' \ "${postfix_dir}/main.cf"
main.cf
fi fi
}
if [[ $(safe_copy main.cf /etc/postfix/main.cf) == "True" ]]; then do_post_regen() {
sudo service postfix restart regen_conf_files=$1
else
sudo service postfix reload \ [[ -z "$regen_conf_files" ]] \
|| sudo service postfix restart || sudo service postfix restart
fi }
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View file

@ -1,43 +0,0 @@
set -e
# Execute this hook only if we force the configuration regeneration
if [[ "$1" == "True" ]]; then
# Add new email services
sudo yunohost service add rspamd -l /var/log/mail.log \
|| echo "rspamd is already listed in services"
sudo yunohost service add rmilter -l /var/log/mail.log \
|| echo "rmilter is already listed in services"
sudo yunohost service add redis-server -l /var/log/redis/redis-server.log \
|| echo "redis-server is already listed in services"
# Remove previous email services
systemctl is-enabled spamassassin > /dev/null 2>&1 \
&& sudo systemctl disable spamassassin
systemctl is-active spamassassin > /dev/null \
&& sudo systemctl stop spamassassin
sudo rm -f /etc/cron.daily/spamassassin
sudo yunohost service status spamassassin > /dev/null 2>&1 \
&& sudo yunohost service remove spamassassin
# 'systemctl is-enabled' does not work for service with no systemd unit file
sudo ls /etc/rc2.d/S??amavis > /dev/null 2>&1 \
|| sudo systemctl disable amavis
sudo systemctl is-active amavis > /dev/null \
&& sudo systemctl stop amavis
sudo yunohost service status amavis > /dev/null 2>&1 \
&& sudo yunohost service remove amavis
# 'systemctl is-enabled' does not work for service with no systemd unit file
sudo ls /etc/rc2.d/S??postgrey > /dev/null 2>&1 \
|| sudo systemctl disable postgrey
sudo systemctl is-active postgrey > /dev/null \
&& sudo systemctl stop postgrey
sudo yunohost service status postgrey > /dev/null 2>&1 \
&& sudo yunohost service remove postgrey
fi
exit 0

100
data/hooks/conf_regen/25-dovecot Normal file → Executable file
View file

@ -1,51 +1,69 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s dovecot $1 $2 --force
else
sudo yunohost service safecopy \
-s dovecot $1 $2
fi
}
cd /usr/share/yunohost/templates/dovecot cd /usr/share/yunohost/templates/dovecot
# Create vmail user dovecot_dir="${pending_dir}/etc/dovecot"
sudo id vmail > /dev/null 2>&1 \ mkdir -p "${dovecot_dir}/global_script"
# copy simple conf files
cp dovecot-ldap.conf "${dovecot_dir}/dovecot-ldap.conf"
cp dovecot.sieve "${dovecot_dir}/global_script/dovecot.sieve"
# prepare dovecot.conf conf file
main_domain=$(cat /etc/yunohost/current_host)
cat dovecot.conf \
| sed "s/{{ main_domain }}/${main_domain}/g" \
> "${dovecot_dir}/dovecot.conf"
# adapt it for IPv4-only hosts
if [ ! -f /proc/net/if_inet6 ]; then
sed -i \
's/^\(listen =\).*/\1 */' \
"${dovecot_dir}/dovecot.conf"
fi
}
do_post_regen() {
regen_conf_files=$1
# create vmail user
id vmail > /dev/null 2>&1 \
|| sudo adduser --system --ingroup mail --uid 500 vmail || sudo adduser --system --ingroup mail --uid 500 vmail
# fix permissions
# Replace main domain in the main configuration file
main_domain=$(cat /etc/yunohost/current_host)
cat dovecot.conf.sed \
| sed "s/{{ main_domain }}/$main_domain/g" \
| sudo tee dovecot.conf
# Handle IPv4 only systems
if [ ! -f /proc/net/if_inet6 ];
then
sudo sed -i 's/^listen.*/listen = \*/' dovecot.conf
fi
safe_copy dovecot.conf /etc/dovecot/dovecot.conf
safe_copy dovecot-ldap.conf /etc/dovecot/dovecot-ldap.conf
# Setup Sieve
sudo mkdir -p /etc/dovecot/global_script
sudo chmod -R 770 /etc/dovecot/global_script
safe_copy dovecot.sieve /etc/dovecot/global_script/dovecot.sieve
sudo chmod 660 /etc/dovecot/global_script/dovecot.sieve > /dev/null 2>&1 \
|| safe_copy dovecot.sieve /etc/dovecot/global_script/dovecot.sieve
sudo sievec /etc/dovecot/global_script/dovecot.sieve
sudo chmod 660 /etc/dovecot/global_script/dovecot.svbin
sudo chown -R vmail:mail /etc/dovecot/global_script sudo chown -R vmail:mail /etc/dovecot/global_script
sudo chmod 770 /etc/dovecot/global_script
[ -z "$regen_conf_files" ] && exit 0
# compile sieve script
[[ "$regen_conf_files" =~ dovecot\.sieve ]] && {
sudo sievec /etc/dovecot/global_script/dovecot.sieve
sudo chown -R vmail:mail /etc/dovecot/global_script
}
sudo service dovecot restart sudo service dovecot restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

88
data/hooks/conf_regen/28-rmilter Normal file → Executable file
View file

@ -1,43 +1,69 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s rmilter $1 $2 --force
else
sudo yunohost service safecopy \
-s rmilter $1 $2
fi
}
cd /usr/share/yunohost/templates/rmilter cd /usr/share/yunohost/templates/rmilter
# Copy Rmilter configuration install -D -m 644 rmilter.conf \
safe_copy rmilter.conf /etc/rmilter.conf "${pending_dir}/etc/rmilter.conf"
install -D -m 644 rmilter.socket \
"${pending_dir}/etc/systemd/system/rmilter.socket"
}
# Override socket configuration do_post_regen() {
safe_copy rmilter.socket /etc/systemd/system/rmilter.socket regen_conf_files=$1
# Create DKIM key for each YunoHost domain # retrieve variables
domain_list=$(sudo yunohost domain list --output-as plain --quiet)
# create DKIM directory
sudo mkdir -p /etc/dkim sudo mkdir -p /etc/dkim
domain_list=$(sudo yunohost domain list --output-as plain)
# create DKIM key for domains
for domain in $domain_list; do for domain in $domain_list; do
[ -f /etc/dkim/$domain.mail.key ] \ domain_key="/etc/dkim/${domain}.mail.key"
|| (sudo opendkim-genkey --domain=$domain \ [ ! -f $domain_key ] && {
--selector=mail\ sudo opendkim-genkey --domain="$domain" \
--directory=/etc/dkim \ --selector=mail --directory=/etc/dkim
&& sudo mv /etc/dkim/mail.private /etc/dkim/$domain.mail.key \ sudo mv /etc/dkim/mail.private "$domain_key"
&& sudo mv /etc/dkim/mail.txt /etc/dkim/$domain.mail.txt) sudo mv /etc/dkim/mail.txt "/etc/dkim/${domain}.mail.txt"
}
sudo chown _rmilter /etc/dkim/$domain.mail.key
sudo chmod 400 /etc/dkim/$domain.mail.key
done done
# Reload systemd daemon, ensure that the socket is listening and stop # fix DKIM keys permissions
# the service. It will be started again by the socket as needed. sudo chown _rmilter /etc/dkim/*.mail.key
sudo systemctl daemon-reload sudo chmod 400 /etc/dkim/*.mail.key
sudo systemctl start rmilter.socket
sudo systemctl stop rmilter.service 2>&1 || true [ -z "$regen_conf_files" ] && exit 0
# reload systemd daemon
[[ "$regen_conf_files" =~ rmilter\.socket ]] && {
sudo systemctl -q daemon-reload
}
# ensure that the socket is listening and stop the service - it will be
# started again by the socket as needed
sudo systemctl -q start rmilter.socket
sudo systemctl -q stop rmilter.service 2>&1 || true
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

63
data/hooks/conf_regen/31-rspamd Normal file → Executable file
View file

@ -1,33 +1,50 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s rspamd $1 $2 --force
else
sudo yunohost service safecopy \
-s rspamd $1 $2
fi
}
cd /usr/share/yunohost/templates/rspamd cd /usr/share/yunohost/templates/rspamd
# Create configuration directories install -D -m 644 metrics.local.conf \
sudo mkdir -p /etc/rspamd/local.d /etc/rspamd/override.d "${pending_dir}/etc/rspamd/local.d/metrics.conf"
install -D -m 644 rspamd.sieve \
"${pending_dir}/etc/dovecot/global_script/rspamd.sieve"
}
# Copy specific configuration to rewrite the defaults do_post_regen() {
safe_copy metrics.conf.local /etc/rspamd/local.d/metrics.conf regen_conf_files=$1
# Install Rspamd sieve script [ -z "$regen_conf_files" ] && exit 0
safe_copy rspamd.sieve /etc/dovecot/global_script/rspamd.sieve
# compile sieve script
[[ "$regen_conf_files" =~ rspamd\.sieve ]] && {
sudo sievec /etc/dovecot/global_script/rspamd.sieve sudo sievec /etc/dovecot/global_script/rspamd.sieve
sudo chmod 660 /etc/dovecot/global_script/rspamd.svbin
sudo chown -R vmail:mail /etc/dovecot/global_script sudo chown -R vmail:mail /etc/dovecot/global_script
# Ensure that the socket is listening and stop the service.
sudo systemctl stop rspamd.service 2>&1 || true
sudo systemctl start rspamd.socket
sudo systemctl restart dovecot sudo systemctl restart dovecot
}
# ensure that the socket is listening and stop the service - it will be
# started again by the socket as needed
sudo systemctl -q start rspamd.socket
sudo systemctl -q stop rspamd.service 2>&1 || true
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

97
data/hooks/conf_regen/34-mysql Normal file → Executable file
View file

@ -1,35 +1,82 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s mysql $1 $2 --force
else
sudo yunohost service safecopy \
-s mysql $1 $2
fi
}
function randpass () {
[ "$2" == "0" ] && CHAR="[:alnum:]" || CHAR="[:graph:]"
cat /dev/urandom | tr -cd "$CHAR" | head -c ${1:-32}
echo
}
cd /usr/share/yunohost/templates/mysql cd /usr/share/yunohost/templates/mysql
if [[ "$(safe_copy my.cnf /etc/mysql/my.cnf | tail -n1)" == "True" ]]; then install -D -m 644 my.cnf "${pending_dir}/etc/mysql/my.cnf"
sudo service mysql restart }
fi
do_post_regen() {
regen_conf_files=$1
if [ ! -f /etc/yunohost/mysql ]; then if [ ! -f /etc/yunohost/mysql ]; then
[[ $(/bin/ps aux | grep '[m]ysqld') == "0" ]] \ . /usr/share/yunohost/helpers.d/string
&& sudo service mysql start
mysql_password=$(randpass 10 0) # ensure that mysql is running
sudo mysqladmin -u root -pyunohost password $mysql_password service mysql status >/dev/null 2>&1 \
echo $mysql_password | sudo tee /etc/yunohost/mysql || service mysql start
# generate and set new root password
mysql_password=$(ynh_string_random 10)
sudo mysqladmin -s -u root -pyunohost password "$mysql_password" || {
if [ $FORCE -eq 1 ]; then
. /usr/share/yunohost/helpers.d/package
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, and is going to reset the MySQL root password." \
"You can find this new password in /etc/yunohost/mysql." >&2
# retrieve MySQL package provider
ynh_package_is_installed "mariadb-server-10.0" \
&& mysql_pkg="mariadb-server-10.0" \
|| mysql_pkg="mysql-server-5.5"
# set new password with debconf
sudo debconf-set-selections << EOF
$mysql_pkg mysql-server/root_password password $mysql_password
$mysql_pkg mysql-server/root_password_again password $mysql_password
EOF
# reconfigure Debian package
sudo dpkg-reconfigure -freadline -u "$mysql_pkg" 2>&1
else
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, but the MySQL root password is unknown." \
"You must either pass --force to reset the password or" \
"put the current one into the file /etc/yunohost/mysql." >&2
exit 1
fi
}
# store new root password
echo "$mysql_password" | sudo tee /etc/yunohost/mysql
sudo chmod 400 /etc/yunohost/mysql sudo chmod 400 /etc/yunohost/mysql
fi fi
[[ -z "$regen_conf_files" ]] \
|| sudo service mysql restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

46
data/hooks/conf_regen/37-avahi-daemon Normal file → Executable file
View file

@ -1,19 +1,37 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s avahi-daemon $1 $2 --force
else
sudo yunohost service safecopy \
-s avahi-daemon $1 $2
fi
}
cd /usr/share/yunohost/templates/avahi-daemon cd /usr/share/yunohost/templates/avahi-daemon
if [[ "$(safe_copy avahi-daemon.conf /etc/avahi/avahi-daemon.conf | tail -n1)" == "True" ]]; then install -D -m 644 avahi-daemon.conf \
sudo service avahi-daemon restart "${pending_dir}/etc/avahi/avahi-daemon.conf"
fi }
do_post_regen() {
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| sudo service avahi-daemon restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

45
data/hooks/conf_regen/40-glances Normal file → Executable file
View file

@ -1,19 +1,36 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s glances $1 $2 --force
else
sudo yunohost service safecopy \
-s glances $1 $2
fi
}
cd /usr/share/yunohost/templates/glances cd /usr/share/yunohost/templates/glances
if [[ "$(safe_copy glances.default /etc/default/glances | tail -n1)" == "True" ]]; then install -D -m 644 glances.default "${pending_dir}/etc/default/glances"
sudo service glances restart }
fi
do_post_regen() {
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| sudo service glances restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

87
data/hooks/conf_regen/43-dnsmasq Normal file → Executable file
View file

@ -1,53 +1,66 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
. /usr/share/yunohost/helpers # source ip helpers
. /usr/share/yunohost/helpers.d/ip
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s dnsmasq $1 $2 --force
else
sudo yunohost service safecopy \
-s dnsmasq $1 $2
fi
}
cd /usr/share/yunohost/templates/dnsmasq cd /usr/share/yunohost/templates/dnsmasq
# Get IPv4 address # create directory for pending conf
ip=$(curl -s -4 https://ip.yunohost.org 2>/dev/null || true) dnsmasq_dir="${pending_dir}/etc/dnsmasq.d"
ynh_validate_ip4 $ip || ip='0.0.0.0' mkdir -p "$dnsmasq_dir"
# Get IPv6 IP address # retrieve variables
ipv4=$(curl -s -4 https://ip.yunohost.org 2>/dev/null || true)
ynh_validate_ip4 "$ipv4" || ipv4='127.0.0.1'
ipv6=$(curl -s -6 http://ip6.yunohost.org 2>/dev/null || true) ipv6=$(curl -s -6 http://ip6.yunohost.org 2>/dev/null || true)
ynh_validate_ip6 $ipv6 || ipv6='' ynh_validate_ip6 "$ipv6" || ipv6=''
domain_list=$(sudo yunohost domain list --output-as plain --quiet)
sudo mkdir -p /etc/dnsmasq.d # add domain conf files
domain_list=$(sudo yunohost domain list --output-as plain)
# Copy a configuration file for each YunoHost domain
for domain in $domain_list; do for domain in $domain_list; do
cat domain.sed \ cat domain.tpl \
| sed "s/{{ domain }}/$domain/g" \ | sed "s/{{ domain }}/${domain}/g" \
| sed "s/{{ ip }}/$ip/g" \ | sed "s/{{ ip }}/${ipv4}/g" \
| sudo tee $domain > "${dnsmasq_dir}/${domain}"
[[ -n $ipv6 ]] \
if [[ "$ipv6" != "" ]]; then && echo "address=/${domain}/${ipv6}" >> "${dnsmasq_dir}/${domain}"
echo "address=/$domain/$ipv6" | sudo tee -a $domain
fi
safe_copy $domain /etc/dnsmasq.d/$domain
done done
# Remove old domains files # remove old domain conf files
for file in /etc/dnsmasq.d/*.*; do conf_files=$(ls -1 /etc/dnsmasq.d \
domain=$(echo $file | sed 's|/etc/dnsmasq.d/||') | awk '/^[^\.]+\.[^\.]+.*$/ { print $1 }')
for domain in $conf_files; do
[[ $domain_list =~ $domain ]] \ [[ $domain_list =~ $domain ]] \
|| sudo yunohost service saferemove -s dnsmasq $file || touch "${dnsmasq_dir}/${domain}"
done done
}
sudo service dnsmasq reload \ do_post_regen() {
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| sudo service dnsmasq restart || sudo service dnsmasq restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

45
data/hooks/conf_regen/46-nsswitch Normal file → Executable file
View file

@ -1,19 +1,36 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s nsswitch $1 $2 --force
else
sudo yunohost service safecopy \
-s nsswitch $1 $2
fi
}
cd /usr/share/yunohost/templates/nsswitch cd /usr/share/yunohost/templates/nsswitch
if [[ "$(safe_copy nsswitch.conf /etc/nsswitch.conf | tail -n1)" == "True" ]]; then install -D -m 644 nsswitch.conf "${pending_dir}/etc/nsswitch.conf"
sudo service nscd restart }
fi
do_post_regen() {
regen_conf_files=$1
[[ -z "$regen_conf_files" ]] \
|| sudo service nscd restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

54
data/hooks/conf_regen/52-fail2ban Normal file → Executable file
View file

@ -1,28 +1,40 @@
#!/bin/bash
set -e set -e
force=$1 do_pre_regen() {
pending_dir=$1
function safe_copy () {
if [[ "$force" == "True" ]]; then
sudo yunohost service safecopy \
-s fail2ban $1 $2 --force
else
sudo yunohost service safecopy \
-s fail2ban $1 $2
fi
}
cd /usr/share/yunohost/templates/fail2ban cd /usr/share/yunohost/templates/fail2ban
sudo mkdir -p /etc/fail2ban/filter.d fail2ban_dir="${pending_dir}/etc/fail2ban"
safe_copy yunohost.conf /etc/fail2ban/filter.d/yunohost.conf mkdir -p "${fail2ban_dir}/filter.d"
# Compatibility: change from HDB to MDB on Jessie cp yunohost.conf "${fail2ban_dir}/filter.d/yunohost.conf"
version=$(sed 's/\..*//' /etc/debian_version) cp jail.conf "${fail2ban_dir}/jail.conf"
[[ "$version" == '8' ]] \ }
&& sudo cp jail-jessie.conf jail.conf \
|| sudo cp jail-wheezy.conf jail.conf
if [[ $(safe_copy jail.conf /etc/fail2ban/jail.conf | tail -n1) == "True" ]]; then do_post_regen() {
sudo service fail2ban restart regen_conf_files=$1
fi
[[ -z "$regen_conf_files" ]] \
|| sudo service fail2ban restart
}
FORCE=${2:-0}
DRY_RUN=${3:-0}
case "$1" in
pre)
do_pre_regen $4
;;
post)
do_post_regen $4
;;
*)
echo "hook called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View file

@ -1,6 +1,42 @@
backup_dir="$1/conf/ynh/mysql" backup_dir="$1/conf/ynh/mysql"
sudo service mysql restart # ensure that mysql is running
sudo cp -a $backup_dir/mysql /etc/yunohost/mysql service mysql status >/dev/null 2>&1 \
mysqlpwd=$(sudo cat /etc/yunohost/mysql) || service mysql start
sudo mysqladmin flush-privileges -p"$mysqlpwd"
# retrieve current and new password
[ -f /etc/yunohost/mysql ] \
&& curr_pwd=$(sudo cat /etc/yunohost/mysql) \
|| curr_pwd="yunohost"
new_pwd=$(sudo cat "${backup_dir}/root_pwd" || sudo cat "${backup_dir}/mysql")
# attempt to change it
sudo mysqladmin -s -u root -p"$curr_pwd" password "$new_pwd" || {
. /usr/share/yunohost/helpers.d/package
echo "It seems that you have already configured MySQL." \
"YunoHost needs to have a root access to MySQL to runs its" \
"applications, and is going to reset the MySQL root password." \
"You can find this new password in /etc/yunohost/mysql." >&2
# retrieve MySQL package provider
ynh_package_is_installed "mariadb-server-10.0" \
&& mysql_pkg="mariadb-server-10.0" \
|| mysql_pkg="mysql-server-5.5"
# set new password with debconf
sudo debconf-set-selections << EOF
$mysql_pkg mysql-server/root_password password $new_pwd
$mysql_pkg mysql-server/root_password_again password $new_pwd
EOF
# reconfigure Debian package
sudo dpkg-reconfigure -freadline -u "$mysql_pkg" 2>&1
}
# store new root password
echo "$new_pwd" | sudo tee /etc/yunohost/mysql
sudo chmod 400 /etc/yunohost/mysql
# reload the grant tables
sudo mysqladmin -s -u root -p"$new_pwd" reload

View file

@ -1 +1,3 @@
backup_dir="$1/conf/ynh"
sudo cp -a "${backup_dir}/current_host" /etc/yunohost/current_host

View file

@ -1,346 +0,0 @@
# Fail2Ban configuration file.
#
# This file was composed for Debian systems from the original one
# provided now under /usr/share/doc/fail2ban/examples/jail.conf
# for additional examples.
#
# To avoid merges during upgrades DO NOT MODIFY THIS FILE
# and rather provide your changes in /etc/fail2ban/jail.local
#
# Author: Yaroslav O. Halchenko <debian@onerussian.com>
#
# $Revision$
#
# The DEFAULT allows a global definition of the options. They can be overridden
# in each jail afterwards.
[DEFAULT]
# "ignoreip" can be an IP address, a CIDR mask or a DNS host
ignoreip = 127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
bantime = 600
maxretry = 3
# "backend" specifies the backend used to get files modification. Available
# options are "gamin", "polling" and "auto".
# yoh: For some reason Debian shipped python-gamin didn't work as expected
# This issue left ToDo, so polling is default backend for now
backend = auto
#
# Destination email address used solely for the interpolations in
# jail.{conf,local} configuration files.
destemail = root@localhost
#
# ACTIONS
#
# Default banning action (e.g. iptables, iptables-new,
# iptables-multiport, shorewall, etc) It is used to define
# action_* variables. Can be overridden globally or per
# section within jail.local file
banaction = iptables-multiport
# email action. Since 0.8.1 upstream fail2ban uses sendmail
# MTA for the mailing. Change mta configuration parameter to mail
# if you want to revert to conventional 'mail'.
mta = sendmail
# Default protocol
protocol = tcp
# Specify chain where jumps would need to be added in iptables-* actions
chain = INPUT
#
# Action shortcuts. To be used to define action parameter
# The simplest action to take: ban only
action_ = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report to the destemail.
action_mw = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois[name=%(__name__)s, dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"]
# ban & send an e-mail with whois report and relevant log lines
# to the destemail.
action_mwl = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"]
%(mta)s-whois-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"]
# Choose default action. To change, just override value of 'action' with the
# interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local
# globally (section [DEFAULT]) or per specific section
action = %(action_)s
#
# JAILS
#
# Next jails corresponds to the standard configuration in Fail2ban 0.6 which
# was shipped in Debian. Enable any defined here jail by including
#
# [SECTION_NAME]
# enabled = true
#
# in /etc/fail2ban/jail.local.
#
# Optionally you may override any other parameter (e.g. banaction,
# action, port, logpath, etc) in that section within jail.local
[ssh]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 6
[dropbear]
enabled = false
port = ssh
filter = sshd
logpath = /var/log/dropbear
maxretry = 6
# Generic filter for pam. Has to be used with action which bans all ports
# such as iptables-allports, shorewall
[pam-generic]
enabled = false
# pam-generic filter can be customized to monitor specific subset of 'tty's
filter = pam-generic
# port actually must be irrelevant but lets leave it all for some possible uses
port = all
banaction = iptables-allports
port = anyport
logpath = /var/log/auth.log
maxretry = 6
[xinetd-fail]
enabled = false
filter = xinetd-fail
port = all
banaction = iptables-multiport-log
logpath = /var/log/daemon.log
maxretry = 2
[ssh-ddos]
enabled = false
port = ssh
filter = sshd-ddos
logpath = /var/log/auth.log
maxretry = 6
#
# HTTP servers
#
[apache]
enabled = false
port = http,https
filter = apache-auth
logpath = /var/log/apache*/*error.log
maxretry = 6
# default action is now multiport, so apache-multiport jail was left
# for compatibility with previous (<0.7.6-2) releases
[apache-multiport]
enabled = false
port = http,https
filter = apache-auth
logpath = /var/log/apache*/*error.log
maxretry = 6
[apache-noscript]
enabled = false
port = http,https
filter = apache-noscript
logpath = /var/log/apache*/*error.log
maxretry = 6
[apache-overflows]
enabled = false
port = http,https
filter = apache-overflows
logpath = /var/log/apache*/*error.log
maxretry = 2
#
# FTP servers
#
[vsftpd]
enabled = false
port = ftp,ftp-data,ftps,ftps-data
filter = vsftpd
logpath = /var/log/vsftpd.log
# or overwrite it in jails.local to be
# logpath = /var/log/auth.log
# if you want to rely on PAM failed login attempts
# vsftpd's failregex should match both of those formats
maxretry = 6
[proftpd]
enabled = false
port = ftp,ftp-data,ftps,ftps-data
filter = proftpd
logpath = /var/log/proftpd/proftpd.log
maxretry = 6
[pure-ftpd]
enabled = false
port = ftp,ftp-data,ftps,ftps-data
filter = pure-ftpd
logpath = /var/log/auth.log
maxretry = 6
[wuftpd]
enabled = false
port = ftp,ftp-data,ftps,ftps-data
filter = wuftpd
logpath = /var/log/auth.log
maxretry = 6
#
# Mail servers
#
[postfix]
enabled = true
port = smtp,ssmtp
filter = postfix
logpath = /var/log/mail.log
[couriersmtp]
enabled = false
port = smtp,ssmtp
filter = couriersmtp
logpath = /var/log/mail.log
#
# Mail servers authenticators: might be used for smtp,ftp,imap servers, so
# all relevant ports get banned
#
[courierauth]
enabled = false
port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s
filter = courierlogin
logpath = /var/log/mail.log
[sasl]
enabled = true
port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s
filter = sasl
# You might consider monitoring /var/log/mail.warn instead if you are
# running postfix since it would provide the same log lines at the
# "warn" level but overall at the smaller filesize.
logpath = /var/log/mail.log
[dovecot]
enabled = true
port = smtp,ssmtp,imap2,imap3,imaps,pop3,pop3s
filter = dovecot
logpath = /var/log/mail.log
# DNS Servers
# These jails block attacks against named (bind9). By default, logging is off
# with bind9 installation. You will need something like this:
#
# logging {
# channel security_file {
# file "/var/log/named/security.log" versions 3 size 30m;
# severity dynamic;
# print-time yes;
# };
# category security {
# security_file;
# };
# };
#
# in your named.conf to provide proper logging
# !!! WARNING !!!
# Since UDP is connection-less protocol, spoofing of IP and imitation
# of illegal actions is way too simple. Thus enabling of this filter
# might provide an easy way for implementing a DoS against a chosen
# victim. See
# http://nion.modprobe.de/blog/archives/690-fail2ban-+-dns-fail.html
# Please DO NOT USE this jail unless you know what you are doing.
#[named-refused-udp]
#
#enabled = false
#port = domain,953
#protocol = udp
#filter = named-refused
#logpath = /var/log/named/security.log
[named-refused-tcp]
enabled = false
port = domain,953
protocol = tcp
filter = named-refused
logpath = /var/log/named/security.log
[nginx]
enabled = true
port = http,https
filter = apache-auth
logpath = /var/log/nginx*/*error.log
maxretry = 6
[nginx-noscript]
enabled = false
port = http,https
filter = apache-noscript
logpath = /var/log/nginx*/*error.log
maxretry = 6
[nginx-overflows]
enabled = false
port = http,https
filter = apache-overflows
logpath = /var/log/nginx*/*error.log
maxretry = 4
[yunohost]
enabled = true
port = http,https
protocol = tcp
filter = yunohost
logpath = /var/log/nginx/*.log
maxretry = 6

View file

@ -15,8 +15,13 @@ postfix:
log: [/var/log/mail.log,/var/log/mail.err] log: [/var/log/mail.log,/var/log/mail.err]
rmilter: rmilter:
status: systemctl status rmilter.socket status: systemctl status rmilter.socket
log: /var/log/mail.log
rspamd: rspamd:
status: systemctl status rspamd.socket status: systemctl status rspamd.socket
log: /var/log/mail.log
redis-server:
status: service
log: /var/log/redis/redis-server.log
mysql: mysql:
status: service status: service
log: [/var/log/mysql.log,/var/log/mysql.err] log: [/var/log/mysql.log,/var/log/mysql.err]
@ -39,9 +44,6 @@ yunohost-api:
log: /var/log/yunohost/yunohost-api.log log: /var/log/yunohost/yunohost-api.log
yunohost-firewall: yunohost-firewall:
status: service status: service
postgrey:
status: service
log: /var/log/mail.log
nslcd: nslcd:
status: service status: service
log: /var/log/syslog log: /var/log/syslog
@ -49,3 +51,6 @@ nsswitch:
status: service status: service
udisks2: udisks2:
status: service status: service
amavis: null
postgrey: null
spamassassin: null

39
debian/changelog vendored
View file

@ -1,3 +1,42 @@
yunohost (2.3.12.1) testing; urgency=low
* [deb] Rely on dh_installinit to restart yunohost-firewall after upgrade
* [deb] Add Install section to yunohost-firewall.service
-- Jérôme Lebleu <jerome@yunohost.org> Sat, 09 Apr 2016 17:22:40 +0200
yunohost (2.3.12) testing; urgency=low
[ Jérôme Lebleu ]
* [enh] Use new rspamd configuration system to override metrics
* [enh] Allow to set script execution directory in hook_exec
* [enh] Add a ynh_user_list helper
* [enh] Call app remove script if installation fails
* [fix] Move imports at the top in yunohost and yunohost-api
* [fix] Use rspamd local.d folder to allow users to override the defaults
* [fix] Execute backup/restore app scripts from the backup dir (bugfix #139)
* [fix] Regenerate SSOwat conf after apps restoration
* [fix] Move imports at the top in backup.py
* [fix] Check if the package is actually installed in equivs helper
* [fix] Improve control file management in equivs helper
* [fix] Remove ending comma in backup.py
* [fix] Call yunohost commands with --quiet in setting helpers
* [fix] Check for tty in root_handlers before remove it in bin/yunohost
* [fix] Use dyndns.yunohost.org instead of dynhost.yunohost.org
* [fix] Set found private key and don't validate it in dyndns_update
* [fix] Update first registered domain with DynDNS instead of current_host
* [i18n] Rename app_requirements_failed err named variable
* [i18n] Update translations from Weblate
[ opi ]
* [enh] Better message during service regenconf.
* [enh] Display hook path on error message.
* [enh] Use named arguments when calling m18n in service.py
* [enh] Use named arguments with m18n.
* [enh] Use named arguments for user_unknown string.
-- Jérôme Lebleu <jerome@yunohost.org> Sat, 09 Apr 2016 12:13:10 +0200
moulinette-yunohost (2.2.4) stable; urgency=low moulinette-yunohost (2.2.4) stable; urgency=low
[ Jérôme Lebleu ] [ Jérôme Lebleu ]

4
debian/control vendored
View file

@ -10,7 +10,7 @@ Homepage: https://yunohost.org/
Package: yunohost Package: yunohost
Architecture: all Architecture: all
Depends: ${python:Depends}, ${misc:Depends} Depends: ${python:Depends}, ${misc:Depends}
, moulinette (>= 2.3.4) , moulinette (>= 2.3.5.1)
, python-psutil, python-requests, python-dnspython , python-psutil, python-requests, python-dnspython
, python-apt, python-miniupnpc , python-apt, python-miniupnpc
, glances , glances
@ -27,7 +27,7 @@ Depends: ${python:Depends}, ${misc:Depends}
, rspamd (>= 1.2.0), rmilter (>=1.7.0), redis-server, opendkim-tools , rspamd (>= 1.2.0), rmilter (>=1.7.0), redis-server, opendkim-tools
Recommends: yunohost-admin Recommends: yunohost-admin
, openssh-server, ntp, inetutils-ping | iputils-ping , openssh-server, ntp, inetutils-ping | iputils-ping
, bash-completion, rsyslog , bash-completion, rsyslog, etckeeper
, php5-gd, php5-curl, php-gettext, php5-mcrypt , php5-gd, php5-curl, php-gettext, php5-mcrypt
, python-pip , python-pip
, unattended-upgrades , unattended-upgrades

41
debian/postinst vendored
View file

@ -6,13 +6,13 @@ do_configure() {
rm -rf /var/cache/moulinette/* rm -rf /var/cache/moulinette/*
if [ ! -f /etc/yunohost/installed ]; then if [ ! -f /etc/yunohost/installed ]; then
bash /usr/share/yunohost/hooks/conf_regen/01-yunohost True bash /usr/share/yunohost/hooks/conf_regen/01-yunohost init
bash /usr/share/yunohost/hooks/conf_regen/02-ssl True bash /usr/share/yunohost/hooks/conf_regen/02-ssl init
bash /usr/share/yunohost/hooks/conf_regen/06-slapd True bash /usr/share/yunohost/hooks/conf_regen/06-slapd init
bash /usr/share/yunohost/hooks/conf_regen/15-nginx True bash /usr/share/yunohost/hooks/conf_regen/15-nginx init
else else
echo "Regenerating configuration, this might take a while..." echo "Regenerating configuration, this might take a while..."
yunohost service regenconf yunohost service regen-conf --output-as none
# restart yunohost-firewall if it's running # restart yunohost-firewall if it's running
service yunohost-firewall status >/dev/null \ service yunohost-firewall status >/dev/null \
@ -28,23 +28,18 @@ do_configure() {
restart_yunohost_firewall() { restart_yunohost_firewall() {
echo "Restarting YunoHost firewall..." echo "Restarting YunoHost firewall..."
if [ -x /etc/init.d/yunohost-firewall ]; then
update-rc.d yunohost-firewall defaults >/dev/null || true
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
else
invoke-rc.d yunohost-firewall start >/dev/null || true
fi
fi
deb-systemd-helper unmask yunohost-firewall.service >/dev/null || true deb-systemd-helper unmask yunohost-firewall.service >/dev/null || true
if deb-systemd-helper --quiet was-enabled yunohost-firewall.service; then if deb-systemd-helper --quiet was-enabled yunohost-firewall.service; then
deb-systemd-helper enable yunohost-firewall.service >/dev/null || true deb-systemd-helper enable yunohost-firewall.service >/dev/null || true
fi else
deb-systemd-helper update-state yunohost-firewall.service >/dev/null || true deb-systemd-helper update-state yunohost-firewall.service >/dev/null || true
if [ -d /run/systemd/system ]; then fi
systemctl --system daemon-reload >/dev/null || true
deb-systemd-invoke try-restart yunohost-firewall.service >/dev/null || true if [ -x /etc/init.d/yunohost-firewall ]; then
update-rc.d yunohost-firewall enable >/dev/null
if [ -n "$2" ]; then
invoke-rc.d yunohost-firewall restart >/dev/null || exit $?
fi
fi fi
} }
@ -71,16 +66,6 @@ case "$1" in
;; ;;
esac esac
# Enable and start yunohost-api sysv service
if [ -x /etc/init.d/yunohost-api ]; then
update-rc.d yunohost-api defaults >/dev/null
if [ -d /run/systemd/system ]; then
systemctl --system daemon-reload >/dev/null || true
else
invoke-rc.d yunohost-api start || exit $?
fi
fi
#DEBHELPER# #DEBHELPER#
exit 0 exit 0

1
debian/postrm vendored
View file

@ -3,7 +3,6 @@
set -e set -e
if [ "$1" = "purge" ]; then if [ "$1" = "purge" ]; then
update-rc.d yunohost-api remove >/dev/null
update-rc.d yunohost-firewall remove >/dev/null update-rc.d yunohost-firewall remove >/dev/null
fi fi

1
debian/prerm vendored
View file

@ -3,7 +3,6 @@
set -e set -e
if [ -x /etc/init.d/yunohost-api ] && ! [ -d /run/systemd/system ]; then if [ -x /etc/init.d/yunohost-api ] && ! [ -d /run/systemd/system ]; then
invoke-rc.d yunohost-api stop || exit $?
invoke-rc.d yunohost-firewall stop || true invoke-rc.d yunohost-firewall stop || true
fi fi

12
debian/rules vendored
View file

@ -8,12 +8,14 @@
dh ${@} --with=python2,systemd dh ${@} --with=python2,systemd
override_dh_installinit: override_dh_installinit:
dh_installinit -pyunohost --name=yunohost-api --noscripts dh_installinit -pyunohost --name=yunohost-api --restart-after-upgrade
dh_installinit -pyunohost --name=yunohost-firewall --noscripts dh_installinit -pyunohost --name=yunohost-firewall --noscripts
override_dh_systemd_enable: override_dh_systemd_enable:
dh_systemd_enable --name=yunohost-api dh_systemd_enable --name=yunohost-api \
dh_systemd_enable --name=yunohost-firewall --no-enable yunohost-api.service
dh_systemd_enable --name=yunohost-firewall --no-enable \
yunohost-firewall.service
override_dh_systemd_start: #override_dh_systemd_start:
dh_systemd_start --restart-after-upgrade yunohost-api.service # dh_systemd_start --restart-after-upgrade yunohost-api.service

View file

@ -9,3 +9,6 @@ ExecStart=/usr/bin/yunohost firewall reload
ExecReload=/usr/bin/yunohost firewall reload ExecReload=/usr/bin/yunohost firewall reload
ExecStop=/usr/bin/yunohost firewall stop ExecStop=/usr/bin/yunohost firewall stop
RemainAfterExit=yes RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View file

@ -1,9 +1,9 @@
{ {
"action_invalid": "Ungültige Aktion '{:s}'", "action_invalid": "Ungültige Aktion '{action:s}'",
"admin_password": "Verwaltungspasswort", "admin_password": "Verwaltungspasswort",
"admin_password_change_failed": "Passwort kann nicht geändert werden", "admin_password_change_failed": "Passwort kann nicht geändert werden",
"admin_password_changed": "Verwaltungspasswort wurde erfolgreich geändert", "admin_password_changed": "Verwaltungspasswort wurde erfolgreich geändert",
"app_already_installed": "{:s} ist schon installiert", "app_already_installed": "{app:s} ist schon installiert",
"app_argument_choice_invalid": "Invalide Auswahl für Argument '{name:s}'. Muss einer der folgenden Werte sein {choices:s}", "app_argument_choice_invalid": "Invalide Auswahl für Argument '{name:s}'. Muss einer der folgenden Werte sein {choices:s}",
"app_argument_invalid": "Das Argument '{name:s}' hat einen falschen Wert: {error:s}", "app_argument_invalid": "Das Argument '{name:s}' hat einen falschen Wert: {error:s}",
"app_argument_required": "Argument '{name:s}' wird benötigt", "app_argument_required": "Argument '{name:s}' wird benötigt",
@ -14,13 +14,13 @@
"app_location_install_failed": "Die App kann an diesem Ort nicht installiert werden", "app_location_install_failed": "Die App kann an diesem Ort nicht installiert werden",
"app_manifest_invalid": "Ungültiges App Manifest", "app_manifest_invalid": "Ungültiges App Manifest",
"app_no_upgrade": "Keine Aktualisierungen für Apps verfügbar", "app_no_upgrade": "Keine Aktualisierungen für Apps verfügbar",
"app_not_installed": "{:s} ist nicht intalliert", "app_not_installed": "{app:s} ist nicht intalliert",
"app_recent_version_required": "Für {:s} benötigt eine aktuellere Version von moulinette", "app_recent_version_required": "Für {:s} benötigt eine aktuellere Version von moulinette",
"app_removed": "{:s} wurde erfolgreich entfernt", "app_removed": "{app:s} wurde erfolgreich entfernt",
"app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden", "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden",
"app_unknown": "Unbekannte App", "app_unknown": "Unbekannte App",
"app_upgrade_failed": "Apps konnten nicht aktualisiert werden", "app_upgrade_failed": "Apps konnten nicht aktualisiert werden",
"app_upgraded": "{:s} wurde erfolgreich aktualisiert", "app_upgraded": "{app:s} wurde erfolgreich aktualisiert",
"appslist_fetched": "Liste der Apps wurde erfolgreich heruntergelanden", "appslist_fetched": "Liste der Apps wurde erfolgreich heruntergelanden",
"appslist_removed": "Appliste erfolgreich entfernt", "appslist_removed": "Appliste erfolgreich entfernt",
"appslist_retrieve_error": "Entfernte App Liste kann nicht gezogen werden", "appslist_retrieve_error": "Entfernte App Liste kann nicht gezogen werden",
@ -54,7 +54,7 @@
"backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden", "backup_output_directory_required": "Für die Datensicherung muss ein Zielverzeichnis angegeben werden",
"backup_running_app_script": "Datensicherung für App '{app:s}' wurd durchgeführt...", "backup_running_app_script": "Datensicherung für App '{app:s}' wurd durchgeführt...",
"backup_running_hooks": "Datensicherunghook wird ausgeführt...", "backup_running_hooks": "Datensicherunghook wird ausgeführt...",
"custom_app_url_required": "Es muss eine URL angegeben um deine benutzerdefinierte App {:s} zu aktualisieren", "custom_app_url_required": "Es muss eine URL angegeben um deine benutzerdefinierte App {app:s} zu aktualisieren",
"custom_appslist_name_required": "Du musst einen Namen für deine benutzerdefinierte Appliste angeben", "custom_appslist_name_required": "Du musst einen Namen für deine benutzerdefinierte Appliste angeben",
"dnsmasq_isnt_installed": "dnsmasq scheint nicht installiert zu sein. Bitte führe 'apt-get remove bind9 && apt-get install dnsmasq' aus", "dnsmasq_isnt_installed": "dnsmasq scheint nicht installiert zu sein. Bitte führe 'apt-get remove bind9 && apt-get install dnsmasq' aus",
"domain_cert_gen_failed": "Zertifikat konnte nicht erzeugt werden", "domain_cert_gen_failed": "Zertifikat konnte nicht erzeugt werden",
@ -79,7 +79,7 @@
"dyndns_ip_updated": "IP Adresse wurde erfolgreich für DynDNS aktualisiert", "dyndns_ip_updated": "IP Adresse wurde erfolgreich für DynDNS aktualisiert",
"dyndns_key_generating": "DNS Schlüssel wird generiert, das könnte eine Weile dauern...", "dyndns_key_generating": "DNS Schlüssel wird generiert, das könnte eine Weile dauern...",
"dyndns_registered": "DynDNS Domain erfolgreich registriert", "dyndns_registered": "DynDNS Domain erfolgreich registriert",
"dyndns_registration_failed": "DynDNS Domain {:s} konnte nicht registriert werden", "dyndns_registration_failed": "DynDNS Domain konnte nicht registriert werden: {error:s}",
"dyndns_unavailable": "DynDNS Subdomain ist nicht verfügbar", "dyndns_unavailable": "DynDNS Subdomain ist nicht verfügbar",
"executing_command": "Führe Kommendo '{command:s}' aus...", "executing_command": "Führe Kommendo '{command:s}' aus...",
"executing_script": "Skript '{script:s}' wird ausgeührt...", "executing_script": "Skript '{script:s}' wird ausgeührt...",
@ -94,16 +94,16 @@
"hook_exec_failed": "Skriptausführung fehlgeschlagen", "hook_exec_failed": "Skriptausführung fehlgeschlagen",
"hook_exec_not_terminated": "Skriptausführung noch nicht beendet", "hook_exec_not_terminated": "Skriptausführung noch nicht beendet",
"hook_list_by_invalid": "Ungültiger Wert zur Anzeige von Hooks", "hook_list_by_invalid": "Ungültiger Wert zur Anzeige von Hooks",
"hook_name_unknown": "Hook '{:s}' ist nicht bekannt", "hook_name_unknown": "Hook '{name:s}' ist nicht bekannt",
"installation_complete": "Installation vollständig", "installation_complete": "Installation vollständig",
"installation_failed": "Installation fehlgeschlagen", "installation_failed": "Installation fehlgeschlagen",
"ip6tables_unavailable": "ip6tables kann nicht verwendet werden. Du befindest dich entweder in einem Container, oder es wird nicht vom Kernel unterstützt.", "ip6tables_unavailable": "ip6tables kann nicht verwendet werden. Du befindest dich entweder in einem Container, oder es wird nicht vom Kernel unterstützt.",
"iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container, oder es wird nicht vom Kernel unterstützt.", "iptables_unavailable": "iptables kann nicht verwendet werden. Du befindest dich entweder in einem Container, oder es wird nicht vom Kernel unterstützt.",
"ldap_initialized": "LDAP erfolgreich initialisiert", "ldap_initialized": "LDAP erfolgreich initialisiert",
"license_undefined": "Undeiniert", "license_undefined": "Undeiniert",
"mail_alias_remove_failed": "E-Mail Alias '{:s}' konnte nicht entfernt werden", "mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden",
"mail_domain_unknown": "Unbekannte Mail Domain '{:s}'", "mail_domain_unknown": "Unbekannte Mail Domain '{domain:s}'",
"mail_forward_remove_failed": "Mailweiterleitung '{:s}' konnte nicht entfernt werden", "mail_forward_remove_failed": "Mailweiterleitung '{mail:s}' konnte nicht entfernt werden",
"maindomain_change_failed": "Hauptdomain konnte nicht geändert werden", "maindomain_change_failed": "Hauptdomain konnte nicht geändert werden",
"maindomain_changed": "Hauptdomain wurde erfolgreich geändert", "maindomain_changed": "Hauptdomain wurde erfolgreich geändert",
"monitor_disabled": "Servermonitoring erfolgreich deaktiviert", "monitor_disabled": "Servermonitoring erfolgreich deaktiviert",
@ -128,7 +128,7 @@
"no_restore_script": "Es konnte kein Wiederherstellungsskript für '{app:s}' gefunden werden", "no_restore_script": "Es konnte kein Wiederherstellungsskript für '{app:s}' gefunden werden",
"no_such_conf_file": "Datei {file:s}: konnte nicht kopiert werden, da diese nicht existiert", "no_such_conf_file": "Datei {file:s}: konnte nicht kopiert werden, da diese nicht existiert",
"packages_no_upgrade": "Es müssen keine Pakete aktualisiert werden", "packages_no_upgrade": "Es müssen keine Pakete aktualisiert werden",
"packages_upgrade_critical_later": "Wichtiges Paket ({:s}) wird später aktualisiert", "packages_upgrade_critical_later": "Wichtiges Paket ({packages:s}) wird später aktualisiert",
"packages_upgrade_failed": "Es konnten nicht alle Pakete aktualisiert werden", "packages_upgrade_failed": "Es konnten nicht alle Pakete aktualisiert werden",
"path_removal_failed": "Pfad {:s} konnte nicht entfernt werden", "path_removal_failed": "Pfad {:s} konnte nicht entfernt werden",
"pattern_backup_archive_name": "Ein gültiger Dateiname kann nur aus alphanumerischen und -_. bestehen", "pattern_backup_archive_name": "Ein gültiger Dateiname kann nur aus alphanumerischen und -_. bestehen",
@ -142,10 +142,10 @@
"pattern_port": "Es muss ein valider Port (zwischen 0 und 65535) angegeben werden", "pattern_port": "Es muss ein valider Port (zwischen 0 und 65535) angegeben werden",
"pattern_port_or_range": "Muss ein valider Port (z.B. 0-65535) oder ein Bereich (z.B. 100:200) sein", "pattern_port_or_range": "Muss ein valider Port (z.B. 0-65535) oder ein Bereich (z.B. 100:200) sein",
"pattern_username": "Darf nur aus klein geschriebenen alphanumerischen Zeichen und Unterstrichen bestehen", "pattern_username": "Darf nur aus klein geschriebenen alphanumerischen Zeichen und Unterstrichen bestehen",
"port_already_closed": "Port {} wurde bereits für {:s} Verbindungen geschlossen", "port_already_closed": "Port {port:d} wurde bereits für {ip_version:s} Verbindungen geschlossen",
"port_already_opened": "Der Port {} wird bereits von {:s} benutzt", "port_already_opened": "Der Port {port:d} wird bereits von {ip_version:s} benutzt",
"port_available": "Port {} ist verfügbar", "port_available": "Port {port:d} ist verfügbar",
"port_unavailable": "Der Port {} ist nicht verfügbar", "port_unavailable": "Der Port {port:d} ist nicht verfügbar",
"restore_action_required": "Du musst etwas zum Wiederherstellen auswählen", "restore_action_required": "Du musst etwas zum Wiederherstellen auswählen",
"restore_already_installed_app": "Es ist bereits eine App mit der ID '{app:s}' installiet", "restore_already_installed_app": "Es ist bereits eine App mit der ID '{app:s}' installiet",
"restore_app_failed": "App '{app:s}' konnte nicht wiederhergestellt werden", "restore_app_failed": "App '{app:s}' konnte nicht wiederhergestellt werden",
@ -158,25 +158,25 @@
"restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...", "restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...",
"restore_running_hooks": "Wiederherstellung wird gestartet...", "restore_running_hooks": "Wiederherstellung wird gestartet...",
"service_add_configuration": "Füge Konfigurationsdatei {file:s} hinzu", "service_add_configuration": "Füge Konfigurationsdatei {file:s} hinzu",
"service_add_failed": "Dienst '{:s}' kann nicht hinzugefügt werden", "service_add_failed": "Dienst '{service:s}' kann nicht hinzugefügt werden",
"service_added": "Service erfolgreich hinzugefügt", "service_added": "Service erfolgreich hinzugefügt",
"service_already_started": "Der Dienst '{:s}' läutt bereits", "service_already_started": "Der Dienst '{service:s}' läutt bereits",
"service_already_stopped": "Dienst '{:s}' wurde bereits gestoppt", "service_already_stopped": "Dienst '{service:s}' wurde bereits gestoppt",
"service_cmd_exec_failed": "Kommando '{:s}' kann nicht ausgeführt werden", "service_cmd_exec_failed": "Kommando '{command:s}' kann nicht ausgeführt werden",
"service_configuration_conflict": "Die Datei {file:s} wurde zwischenzeitlich verändert. Bitte übernehme die Änderungen manuell oder nutze die Option --force (diese wird alle Änderungen überschreiben).", "service_configuration_conflict": "Die Datei {file:s} wurde zwischenzeitlich verändert. Bitte übernehme die Änderungen manuell oder nutze die Option --force (diese wird alle Änderungen überschreiben).",
"service_disable_failed": "Dienst'{:s}' konnte nicht deaktiviert werden", "service_disable_failed": "Dienst '{service:s}' konnte nicht deaktiviert werden",
"service_disabled": "Der Dienst '{:s}' wurde erfolgreich deaktiviert", "service_disabled": "Der Dienst '{service:s}' wurde erfolgreich deaktiviert",
"service_enable_failed": "Dienst '{:s}' konnte nicht aktiviert werden", "service_enable_failed": "Dienst '{service:s}' konnte nicht aktiviert werden",
"service_enabled": "Dienst '{:s}' erfolgreich aktiviert", "service_enabled": "Dienst '{service:s}' erfolgreich aktiviert",
"service_no_log": "Für den Dienst '{:s}' kann kein Log angezeigt werden", "service_no_log": "Für den Dienst '{service:s}' kann kein Log angezeigt werden",
"service_remove_failed": "Dienst '{:s}' konnte nicht entfernt werden", "service_remove_failed": "Dienst '{service:s}' konnte nicht entfernt werden",
"service_removed": "Dienst erfolgreich enternt", "service_removed": "Dienst erfolgreich enternt",
"service_start_failed": "Dienst '{:s}' konnte nicht gestartet werden", "service_start_failed": "Dienst '{service:s}' konnte nicht gestartet werden",
"service_started": "der Dienst '{:s}' wurde erfolgreich gestartet", "service_started": "der Dienst '{service:s}' wurde erfolgreich gestartet",
"service_status_failed": "Status von '{:s}' kann nicht festgestellt werden", "service_status_failed": "Status von '{service:s}' kann nicht festgestellt werden",
"service_stop_failed": "Dienst '{:s}' kann nicht gestoppt werden", "service_stop_failed": "Dienst '{service:s}' kann nicht gestoppt werden",
"service_stopped": "Dienst '{:s}' wurde erfolgreich beendet", "service_stopped": "Dienst '{service:s}' wurde erfolgreich beendet",
"service_unknown": "Unbekannte Dienst '{:s}'", "service_unknown": "Unbekannte Dienst '{service:s}'",
"services_configured": "Konfiguration erfolgreich erstellt", "services_configured": "Konfiguration erfolgreich erstellt",
"show_diff": "Es gibt folgende Änderungen:\n{diff:s}", "show_diff": "Es gibt folgende Änderungen:\n{diff:s}",
"ssowat_conf_generated": "Konfiguration von SSOwat erfolgreich", "ssowat_conf_generated": "Konfiguration von SSOwat erfolgreich",
@ -185,7 +185,7 @@
"system_username_exists": "Der Benutzername existiert bereits", "system_username_exists": "Der Benutzername existiert bereits",
"unbackup_app": "App '{app:s}' konnte nicht gespeichert werden", "unbackup_app": "App '{app:s}' konnte nicht gespeichert werden",
"unexpected_error": "Ein unerwarteter Fehler ist aufgetreten", "unexpected_error": "Ein unerwarteter Fehler ist aufgetreten",
"unit_unknown": "Unbekannte Einheit '{:s}'", "unit_unknown": "Unbekannte Einheit '{unit:s}'",
"unlimit": "Kein Kontingent", "unlimit": "Kein Kontingent",
"unrestore_app": "App '{app:s}' kann nicht Wiederhergestellt werden", "unrestore_app": "App '{app:s}' kann nicht Wiederhergestellt werden",
"update_cache_failed": "Konnte APT cache nicht aktualisieren", "update_cache_failed": "Konnte APT cache nicht aktualisieren",

View file

@ -22,7 +22,7 @@
"custom_app_url_required" : "You must provide an URL to upgrade your custom app {app:s}", "custom_app_url_required" : "You must provide an URL to upgrade your custom app {app:s}",
"app_requirements_checking" : "Checking required packages...", "app_requirements_checking" : "Checking required packages...",
"app_requirements_unmeet" : "Requirements are not met, the package {pkgname} ({version}) must be {spec}", "app_requirements_unmeet" : "Requirements are not met, the package {pkgname} ({version}) must be {spec}",
"app_requirements_failed" : "Unable to meet requirements: {err}", "app_requirements_failed" : "Unable to meet requirements: {error}",
"app_upgraded" : "{app:s} successfully upgraded", "app_upgraded" : "{app:s} successfully upgraded",
"app_upgrade_failed" : "Unable to upgrade {app:s}", "app_upgrade_failed" : "Unable to upgrade {app:s}",
"app_id_invalid" : "Invalid app id", "app_id_invalid" : "Invalid app id",
@ -34,6 +34,8 @@
"app_extraction_failed" : "Unable to extract installation files", "app_extraction_failed" : "Unable to extract installation files",
"app_install_files_invalid" : "Invalid installation files", "app_install_files_invalid" : "Invalid installation files",
"app_manifest_invalid" : "Invalid app manifest", "app_manifest_invalid" : "Invalid app manifest",
"app_incompatible" : "The app is incompatible with your YunoHost version",
"app_package_need_update" : "The app package need to be updated to follow YunoHost changes",
"app_argument_choice_invalid" : "Invalid choice for argument '{name:s}', it must be one of {choices:s}", "app_argument_choice_invalid" : "Invalid choice for argument '{name:s}', it must be one of {choices:s}",
"app_argument_invalid" : "Invalid value for argument '{name:s}': {error:s}", "app_argument_invalid" : "Invalid value for argument '{name:s}': {error:s}",
"app_argument_required" : "Argument '{name:s}' is required", "app_argument_required" : "Argument '{name:s}' is required",
@ -128,12 +130,20 @@
"service_status_failed" : "Unable to determine status of service '{service:s}'", "service_status_failed" : "Unable to determine status of service '{service:s}'",
"service_no_log" : "No log to display for service '{service:s}'", "service_no_log" : "No log to display for service '{service:s}'",
"service_cmd_exec_failed" : "Unable to execute command '{command:s}'", "service_cmd_exec_failed" : "Unable to execute command '{command:s}'",
"service_configured": "Configuration successfully generated for service '{service:s}'", "service_regenconf_failed" : "Unable to regenerate the configuration for service(s): {services}",
"service_configured_all": "Configuration successfully generated for every services", "service_regenconf_pending_applying" : "Applying pending configuration for service '{service}'...",
"service_configuration_conflict": "The file {file:s} has been changed since its last generation. Please apply the modifications manually or use the option --force (it will erase all the modifications previously done to the file).", "service_regenconf_dry_pending_applying" : "Checking pending configuration which would have been applied for service '{service}'...",
"no_such_conf_file": "Unable to copy the file {file:s}: the file does not exist", "service_conf_file_manually_removed" : "The configuration file '{conf}' has been manually removed and will not be created",
"service_add_configuration": "Adding the configuration file {file:s}", "service_conf_file_manually_modified" : "The configuration file '{conf}' has been manually modified and will not be updated",
"show_diff": "Here are the differences:\n{diff:s}", "service_conf_file_not_managed" : "The configuration file '{conf}' is not managed yet and will not be updated",
"service_conf_file_backed_up" : "The configuration file '{conf}' has been backed up to '{backup}'",
"service_conf_file_removed" : "The configuration file '{conf}' has been removed",
"service_conf_file_remove_failed" : "Unable to remove the configuration file '{conf}'",
"service_conf_file_updated" : "The configuration file '{conf}' has been updated",
"service_conf_file_copy_failed" : "Unable to copy the new configuration file '{new}' to '{conf}'",
"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_would_be_updated" : "The configuration would have been updated for service '{service}'",
"network_check_smtp_ok" : "Outbound mail (SMTP port 25) is not blocked", "network_check_smtp_ok" : "Outbound mail (SMTP port 25) is not blocked",
"network_check_smtp_ko" : "Outbound mail (SMTP port 25) seems to be blocked by your network", "network_check_smtp_ko" : "Outbound mail (SMTP port 25) seems to be blocked by your network",

View file

@ -1,9 +1,9 @@
{ {
"action_invalid": "Acción inválida '{:s}'", "action_invalid": "Acción inválida '{action:s}'",
"admin_password": "Contraseña administrativa", "admin_password": "Contraseña administrativa",
"admin_password_change_failed": "No se pudo cambiar la contraseña", "admin_password_change_failed": "No se pudo cambiar la contraseña",
"admin_password_changed": "Contraseña administrativa se cambió con éxito", "admin_password_changed": "Contraseña administrativa se cambió con éxito",
"app_already_installed": "{:s} ya está instalado ", "app_already_installed": "{app:s} ya está instalado",
"app_extraction_failed": "No se pudo extraer los archivos de instalación ", "app_extraction_failed": "No se pudo extraer los archivos de instalación ",
"app_id_invalid": "id de la aplicación inválida ", "app_id_invalid": "id de la aplicación inválida ",
"app_install_files_invalid": "Archivos de instalación inválidos ", "app_install_files_invalid": "Archivos de instalación inválidos ",
@ -11,13 +11,13 @@
"app_location_install_failed": "No se pudo instalar la aplicación en esta lugar", "app_location_install_failed": "No se pudo instalar la aplicación en esta lugar",
"app_manifest_invalid": "Manifesto de la aplicación es inválido", "app_manifest_invalid": "Manifesto de la aplicación es inválido",
"app_no_upgrade": "Ninguna app a actualizar", "app_no_upgrade": "Ninguna app a actualizar",
"app_not_installed": "{:s} no está instalado.", "app_not_installed": "{app:s} no está instalado",
"app_recent_version_required": "{:s} requiere una versión más reciente de moulinette ", "app_recent_version_required": "{:s} requiere una versión más reciente de moulinette ",
"app_removed": "{:s} era eliminado con éxito ", "app_removed": "{app:s} era eliminado con éxito",
"app_sources_fetch_failed": "No se pudo descargar los archivos de códigos fuentes", "app_sources_fetch_failed": "No se pudo descargar los archivos de códigos fuentes",
"app_unknown": "App desconocida", "app_unknown": "App desconocida",
"app_upgrade_failed": "No se pudo actualizar todas las aplicaciones ", "app_upgrade_failed": "No se pudo actualizar todas las aplicaciones ",
"app_upgraded": "{:s} actualizado con éxito", "app_upgraded": "{app:s} actualizado con éxito",
"appslist_fetched": "Lista de aplicaciones se trajo con éxito", "appslist_fetched": "Lista de aplicaciones se trajo con éxito",
"appslist_removed": "Lista de aplicaciones se eliminó con éxito", "appslist_removed": "Lista de aplicaciones se eliminó con éxito",
"appslist_retrieve_error": "No se pudo recuperar la lista de aplicaciones a distancia ", "appslist_retrieve_error": "No se pudo recuperar la lista de aplicaciones a distancia ",
@ -41,7 +41,7 @@
"backup_output_directory_not_empty": "La carpeta de salida no está vacía", "backup_output_directory_not_empty": "La carpeta de salida no está vacía",
"backup_output_directory_required": "Debe proporcionar un directorio de salida para el backup", "backup_output_directory_required": "Debe proporcionar un directorio de salida para el backup",
"backup_running_hooks": "Ejecutando los hooks de backup...", "backup_running_hooks": "Ejecutando los hooks de backup...",
"custom_app_url_required": " Debe proporcionar una URL para actualizar su aplicación personalizada {:s} ", "custom_app_url_required": "Debe proporcionar una URL para actualizar su aplicación personalizada {app:s}",
"custom_appslist_name_required": "Debe proporcionar un nombre para la lista de aplicaciones personalizadas ", "custom_appslist_name_required": "Debe proporcionar un nombre para la lista de aplicaciones personalizadas ",
"dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, por favor, ejecuta 'apt-get remove bind9 && apt-get install dnsmasq'", "dnsmasq_isnt_installed": "Parece que dnsmasq no está instalado, por favor, ejecuta 'apt-get remove bind9 && apt-get install dnsmasq'",
"domain_cert_gen_failed": "No se pudo crear certificado", "domain_cert_gen_failed": "No se pudo crear certificado",
@ -66,7 +66,7 @@
"dyndns_ip_updated": "La dirección IP era actualizado en DynDNS con éxito", "dyndns_ip_updated": "La dirección IP era actualizado en DynDNS con éxito",
"dyndns_key_generating": "Generación del llave de DNS está en curso. Este podría durar unos momentos...", "dyndns_key_generating": "Generación del llave de DNS está en curso. Este podría durar unos momentos...",
"dyndns_registered": "El dominio DynDNS era registrado con éxito.", "dyndns_registered": "El dominio DynDNS era registrado con éxito.",
"dyndns_registration_failed": "No se pudo registrar el dominio DynDNS: {:s}", "dyndns_registration_failed": "No se pudo registrar el dominio DynDNS: {error:s}",
"dyndns_unavailable": "Subdominio DynDNS no disponible", "dyndns_unavailable": "Subdominio DynDNS no disponible",
"executing_script": "Ejecutando script...", "executing_script": "Ejecutando script...",
"extracting": "Extrayendo...", "extracting": "Extrayendo...",
@ -77,16 +77,16 @@
"hook_argument_missing": "Falta un parámetro '{:s}'", "hook_argument_missing": "Falta un parámetro '{:s}'",
"hook_choice_invalid": "Selección inválida '{:s}'", "hook_choice_invalid": "Selección inválida '{:s}'",
"hook_list_by_invalid": "La propiedad de este hook es inválida", "hook_list_by_invalid": "La propiedad de este hook es inválida",
"hook_name_unknown": "Hook desconocido '{:s}'", "hook_name_unknown": "Hook desconocido '{name:s}'",
"installation_complete": "La instalación se ha completado", "installation_complete": "La instalación se ha completado",
"installation_failed": "La Instalación se ha fracasado", "installation_failed": "La Instalación se ha fracasado",
"ip6tables_unavailable": "No puedes modificar los ip6tables aquí. Eres en un contenedor o su kernel no soporte este opción.", "ip6tables_unavailable": "No puedes modificar los ip6tables aquí. Eres en un contenedor o su kernel no soporte este opción.",
"iptables_unavailable": "No puedes modificar los iptables aquí. Eres en un contenedor o su kernel no soporte este opción.", "iptables_unavailable": "No puedes modificar los iptables aquí. Eres en un contenedor o su kernel no soporte este opción.",
"ldap_initialized": "LDAP se inició con éxito", "ldap_initialized": "LDAP se inició con éxito",
"license_undefined": "indefinido", "license_undefined": "indefinido",
"mail_alias_remove_failed": "No se pudo quitar el alias de correos '{:s}'", "mail_alias_remove_failed": "No se pudo quitar el alias de correos '{mail:s}'",
"mail_domain_unknown": "El dominio de correos '{:s}' es desconocido", "mail_domain_unknown": "El dominio de correos '{domain:s}' es desconocido",
"mail_forward_remove_failed": "No se pudo quitar la reenvía de correos '{:s}'", "mail_forward_remove_failed": "No se pudo quitar la reenvía de correos '{mail:s}'",
"maindomain_change_failed": "No se pudo cambiar el dominio principal", "maindomain_change_failed": "No se pudo cambiar el dominio principal",
"maindomain_changed": "Dominio principal se cambió con éxito", "maindomain_changed": "Dominio principal se cambió con éxito",
"monitor_disabled": "Supervisión del sistema era desactivado con éxito", "monitor_disabled": "Supervisión del sistema era desactivado con éxito",
@ -105,7 +105,7 @@
"no_appslist_found": "No se encontró ninguna lista de Apps", "no_appslist_found": "No se encontró ninguna lista de Apps",
"no_internet_connection": "El servidor no está conectado al Internet.", "no_internet_connection": "El servidor no está conectado al Internet.",
"packages_no_upgrade": "No hay actualización por ningun paquete", "packages_no_upgrade": "No hay actualización por ningun paquete",
"packages_upgrade_critical_later": "Los paquetes críticos ({:s}) se actualizarán más tarde", "packages_upgrade_critical_later": "Los paquetes críticos ({packages:s}) se actualizarán más tarde",
"packages_upgrade_failed": "No se pudo actualizar todo de los paquetes", "packages_upgrade_failed": "No se pudo actualizar todo de los paquetes",
"path_removal_failed": "No se pudo quitar la ruta {:s}", "path_removal_failed": "No se pudo quitar la ruta {:s}",
"pattern_backup_archive_name": "Debe que ser un nombre de archivo válido con los caracteres alfanumericos, o los -_.", "pattern_backup_archive_name": "Debe que ser un nombre de archivo válido con los caracteres alfanumericos, o los -_.",
@ -118,39 +118,39 @@
"pattern_port": "El numéro del puerto debe ser válido (i.e. 0-65535)", "pattern_port": "El numéro del puerto debe ser válido (i.e. 0-65535)",
"pattern_port_or_range": "El numéro del puerto debe ser válido (i.e. 0-65535) o un intervalo de puertos (e.g. 100:200)", "pattern_port_or_range": "El numéro del puerto debe ser válido (i.e. 0-65535) o un intervalo de puertos (e.g. 100:200)",
"pattern_username": "Debe contener solamente caracteres alfanuméricos o la guion bajo", "pattern_username": "Debe contener solamente caracteres alfanuméricos o la guion bajo",
"port_already_closed": "El puerto {} ya está cerrado por {:s} connecciones.", "port_already_closed": "El puerto {port:d} ya está cerrado por {ip_version:s} connecciones",
"port_already_opened": "El puerto {} ya está abierto por {:s} connecciones", "port_already_opened": "El puerto {port:d} ya está abierto por {ip_version:s} connecciones",
"port_available": "El puerto {} está disponible", "port_available": "El puerto {port:d} está disponible",
"port_unavailable": "El puerto {} no está disponible", "port_unavailable": "El puerto {port:d} no está disponible",
"restore_complete": "Restauración se ha completado", "restore_complete": "Restauración se ha completado",
"restore_confirm_yunohost_installed": "Estás seguro que quieres restaurar a un sistema que ya está instalado? [{answers:s}]", "restore_confirm_yunohost_installed": "Estás seguro que quieres restaurar a un sistema que ya está instalado? [{answers:s}]",
"restore_failed": "No se pudo restaurar el sistema", "restore_failed": "No se pudo restaurar el sistema",
"restore_running_hooks": "Ejecutando hooks de restauración...", "restore_running_hooks": "Ejecutando hooks de restauración...",
"service_add_failed": "No se pudo añadir el servicio '{:s}'", "service_add_failed": "No se pudo añadir el servicio '{service:s}'",
"service_added": "Servicio añadido con éxito", "service_added": "Servicio añadido con éxito",
"service_already_started": "El servicio '{:s}' ya se ha empezado", "service_already_started": "El servicio '{service:s}' ya se ha empezado",
"service_already_stopped": "El servicio '{:s}' ya está parado ", "service_already_stopped": "El servicio '{service:s}' ya está parado",
"service_cmd_exec_failed": "No se pudo ejecutar comando '{:s}'", "service_cmd_exec_failed": "No se pudo ejecutar comando '{command:s}'",
"service_disable_failed": "No se pudo desactivar el servicio '{:s}'", "service_disable_failed": "No se pudo desactivar el servicio '{service:s}'",
"service_disabled": "Servicio '{:s}' desactivado con éxito", "service_disabled": "Servicio '{service:s}' desactivado con éxito",
"service_enable_failed": "No se pudo activar el servicio '{:s}'", "service_enable_failed": "No se pudo activar el servicio '{service:s}'",
"service_enabled": "Servicio '{:s}' activado con éxito", "service_enabled": "Servicio '{service:s}' activado con éxito",
"service_no_log": "No hay archivo historial del servicio '{:s}' a exhibir", "service_no_log": "No hay archivo historial del servicio '{service:s}' a exhibir",
"service_remove_failed": "No se pudo quitar el servicio '{:s}'", "service_remove_failed": "No se pudo quitar el servicio '{service:s}'",
"service_removed": "Servicio quitado con éxito", "service_removed": "Servicio quitado con éxito",
"service_start_failed": "No se pudo empezar el servicio '{:s}'", "service_start_failed": "No se pudo empezar el servicio '{service:s}'",
"service_started": "El servicio '{:s}' se empezó con éxito", "service_started": "El servicio '{service:s}' se empezó con éxito",
"service_status_failed": "No se pudo discernir el estado del servicio '{:s}'", "service_status_failed": "No se pudo discernir el estado del servicio '{service:s}'",
"service_stop_failed": "No se pudo parar el servicio '{:s}'", "service_stop_failed": "No se pudo parar el servicio '{service:s}'",
"service_stopped": "Servicio '{:s}' parado con éxito", "service_stopped": "Servicio '{service:s}' parado con éxito",
"service_unknown": "Servicio desconocido '{:s}'", "service_unknown": "Servicio desconocido '{service:s}'",
"ssowat_conf_generated": "Configuración SSOwat generado con éxito ", "ssowat_conf_generated": "Configuración SSOwat generado con éxito ",
"ssowat_conf_updated": "Configuración persistente SSOwat actualizada con éxito", "ssowat_conf_updated": "Configuración persistente SSOwat actualizada con éxito",
"system_upgraded": "Actualización del sistema se ha completado con éxito.", "system_upgraded": "Actualización del sistema se ha completado con éxito.",
"system_username_exists": "Nombre de usuario ya existe en los usuarios del sistema", "system_username_exists": "Nombre de usuario ya existe en los usuarios del sistema",
"unbackup_app": "La App '{:s}' no será guardada", "unbackup_app": "La App '{:s}' no será guardada",
"unexpected_error": "Un error ha ocurrido", "unexpected_error": "Un error ha ocurrido",
"unit_unknown": "Unidad '{:s}' desconocido", "unit_unknown": "Unidad '{unit:s}' desconocido",
"unrestore_app": "La App '{:s}' no será restaurada", "unrestore_app": "La App '{:s}' no será restaurada",
"update_cache_failed": "No se pudo actualizar el cache APT", "update_cache_failed": "No se pudo actualizar el cache APT",
"updating_apt_cache": "Actualizando la lista de paquetes disponibles...", "updating_apt_cache": "Actualizando la lista de paquetes disponibles...",

View file

@ -1,5 +1,5 @@
{ {
"action_invalid": "Action « {:s} » incorrecte", "action_invalid": "Action « {action:s} » incorrecte",
"admin_password": "Mot de passe d'administration", "admin_password": "Mot de passe d'administration",
"admin_password_change_failed": "Impossible de modifier le mot de passe d'administration", "admin_password_change_failed": "Impossible de modifier le mot de passe d'administration",
"admin_password_changed": "Mot de passe d'administration modifié avec succès", "admin_password_changed": "Mot de passe d'administration modifié avec succès",
@ -17,8 +17,12 @@
"app_no_upgrade": "Aucune application à mettre à jour", "app_no_upgrade": "Aucune application à mettre à jour",
"app_not_correctly_installed": "{app:s} semble être mal installé", "app_not_correctly_installed": "{app:s} semble être mal installé",
"app_not_installed": "{app:s} n'est pas installé", "app_not_installed": "{app:s} n'est pas installé",
"app_not_properly_removed": "{app:s} n'a pas été supprimé correctement",
"app_recent_version_required": "{app:s} nécessite une version plus récente de YunoHost", "app_recent_version_required": "{app:s} nécessite une version plus récente de YunoHost",
"app_removed": "{app:s} supprimé avec succès", "app_removed": "{app:s} supprimé avec succès",
"app_requirements_checking": "Vérification des paquets requis...",
"app_requirements_failed": "Impossible de satisfaire les pré-requis : {error}",
"app_requirements_unmeet": "Les pré-requis ne sont pas satisfaits, le paquet {pkgname} ({version}) doit être {spec}",
"app_sources_fetch_failed": "Impossible de récupérer les fichiers sources", "app_sources_fetch_failed": "Impossible de récupérer les fichiers sources",
"app_unknown": "Application inconnue", "app_unknown": "Application inconnue",
"app_unsupported_remote_type": "Le type distant utilisé par l'application n'est pas supporté", "app_unsupported_remote_type": "Le type distant utilisé par l'application n'est pas supporté",
@ -52,13 +56,19 @@
"backup_hook_unknown": "Script de sauvegarde « {hook:s} » inconnu", "backup_hook_unknown": "Script de sauvegarde « {hook:s} » inconnu",
"backup_invalid_archive": "Archive de sauvegarde incorrecte", "backup_invalid_archive": "Archive de sauvegarde incorrecte",
"backup_nothings_done": "Il n'y a rien à sauvegarder", "backup_nothings_done": "Il n'y a rien à sauvegarder",
"backup_output_directory_forbidden": "Dossier de sortie interdit", "backup_output_directory_forbidden": "Dossier de sortie interdit. Les sauvegardes ne peuvent être créées dans les dossiers /bin, /boot, /dev, /etc, /lib, /root, /run, /sbin, /sys, /usr, /var ou /home/yunohost.backup/archives.",
"backup_output_directory_not_empty": "Le dossier de sortie n'est pas vide", "backup_output_directory_not_empty": "Le dossier de sortie n'est pas vide",
"backup_output_directory_required": "Vous devez spécifier un dossier de sortie pour la sauvegarde", "backup_output_directory_required": "Vous devez spécifier un dossier de sortie pour la sauvegarde",
"backup_running_app_script": "Lancement du script de sauvegarde de l'application « {app:s} »...", "backup_running_app_script": "Lancement du script de sauvegarde de l'application « {app:s} »...",
"backup_running_hooks": "Exécution des scripts de sauvegarde...", "backup_running_hooks": "Exécution des scripts de sauvegarde...",
"custom_app_url_required": "Vous devez spécifier une URL pour mettre à jour votre application locale {app:s}", "custom_app_url_required": "Vous devez spécifier une URL pour mettre à jour votre application locale {app:s}",
"custom_appslist_name_required": "Vous devez spécifier un nom pour votre liste d'applications personnalisée", "custom_appslist_name_required": "Vous devez spécifier un nom pour votre liste d'applications personnalisée",
"diagnostic_debian_version_error": "Impossible de déterminer la version de Debian : {error}",
"diagnostic_kernel_version_error": "Impossible de récupérer la version du noyau : {error}",
"diagnostic_monitor_disk_error": "Impossible de superviser les disques : {error}",
"diagnostic_monitor_network_error": "Impossible de superviser le réseau : {error}",
"diagnostic_monitor_system_error": "Impossible de superviser le système : {error}",
"diagnostic_no_apps": "Aucune application installée",
"dnsmasq_isnt_installed": "dnsmasq ne semble pas être installé, veuillez lancer « apt-get remove bind9 && apt-get install dnsmasq »", "dnsmasq_isnt_installed": "dnsmasq ne semble pas être installé, veuillez lancer « apt-get remove bind9 && apt-get install dnsmasq »",
"domain_cert_gen_failed": "Impossible de générer le certificat", "domain_cert_gen_failed": "Impossible de générer le certificat",
"domain_created": "Domaine créé avec succès", "domain_created": "Domaine créé avec succès",
@ -81,8 +91,10 @@
"dyndns_ip_update_failed": "Impossible de mettre à jour l'adresse IP sur le domaine DynDNS", "dyndns_ip_update_failed": "Impossible de mettre à jour l'adresse IP sur le domaine DynDNS",
"dyndns_ip_updated": "Adresse IP mise à jour avec succès sur le domaine DynDNS", "dyndns_ip_updated": "Adresse IP mise à jour avec succès sur le domaine DynDNS",
"dyndns_key_generating": "La clé DNS est en cours de génération, cela peut prendre du temps...", "dyndns_key_generating": "La clé DNS est en cours de génération, cela peut prendre du temps...",
"dyndns_key_not_found": "Clé DNS introuvable pour le domaine",
"dyndns_no_domain_registered": "Aucun domaine n'a été enregistré avec DynDNS",
"dyndns_registered": "Domaine DynDNS enregistré avec succès", "dyndns_registered": "Domaine DynDNS enregistré avec succès",
"dyndns_registration_failed": "Impossible d'enregistrer le domaine DynDNS : {:s}", "dyndns_registration_failed": "Impossible d'enregistrer le domaine DynDNS : {error:s}",
"dyndns_unavailable": "Sous-domaine DynDNS indisponible", "dyndns_unavailable": "Sous-domaine DynDNS indisponible",
"executing_command": "Exécution de la commande « {command:s} »...", "executing_command": "Exécution de la commande « {command:s} »...",
"executing_script": "Exécution du script « {script:s} »...", "executing_script": "Exécution du script « {script:s} »...",
@ -94,19 +106,19 @@
"format_datetime_short": "%d/%m/%Y %H:%M", "format_datetime_short": "%d/%m/%Y %H:%M",
"hook_argument_missing": "Argument manquant : '{:s}'", "hook_argument_missing": "Argument manquant : '{:s}'",
"hook_choice_invalid": "Choix incorrect : '{:s}'", "hook_choice_invalid": "Choix incorrect : '{:s}'",
"hook_exec_failed": "Échec de l'exécution du script", "hook_exec_failed": "Échec de l'exécution du script « {path:s} »",
"hook_exec_not_terminated": "L'exécution du script ne s'est pas terminée", "hook_exec_not_terminated": "L'exécution du script « {path:s} » ne s'est pas terminée",
"hook_list_by_invalid": "Propriété pour lister les scripts incorrecte", "hook_list_by_invalid": "Propriété pour lister les scripts incorrecte",
"hook_name_unknown": "Nom de script « {:s} » inconnu", "hook_name_unknown": "Nom de script « {name:s} » inconnu",
"installation_complete": "Installation terminée", "installation_complete": "Installation terminée",
"installation_failed": "Échec de l'installation", "installation_failed": "Échec de l'installation",
"ip6tables_unavailable": "Vous ne pouvez pas jouer avec ip6tables ici. Vous êtes sûrement dans un conteneur, ou alors votre noyau ne le supporte pas.", "ip6tables_unavailable": "Vous ne pouvez pas jouer avec ip6tables ici. Vous êtes sûrement dans un conteneur, ou alors votre noyau ne le supporte pas.",
"iptables_unavailable": "Vous ne pouvez pas jouer avec iptables ici. Vous êtes sûrement dans un conteneur, autrement votre noyau ne le supporte pas.", "iptables_unavailable": "Vous ne pouvez pas jouer avec iptables ici. Vous êtes sûrement dans un conteneur, autrement votre noyau ne le supporte pas.",
"ldap_initialized": "Répertoire LDAP initialisé avec succès", "ldap_initialized": "Répertoire LDAP initialisé avec succès",
"license_undefined": "indéfinie", "license_undefined": "indéfinie",
"mail_alias_remove_failed": "Impossible de supprimer l'adresse mail supplémentaire « {:s} »", "mail_alias_remove_failed": "Impossible de supprimer l'adresse mail supplémentaire « {mail:s} »",
"mail_domain_unknown": "Domaine « {:s} » de l'adresse mail inconnu", "mail_domain_unknown": "Domaine « {domain:s} » de l'adresse mail inconnu",
"mail_forward_remove_failed": "Impossible de supprimer l'adresse courriel de transfert « {:s} »", "mail_forward_remove_failed": "Impossible de supprimer l'adresse courriel de transfert « {mail:s} »",
"maindomain_change_failed": "Impossible de modifier le domaine principal", "maindomain_change_failed": "Impossible de modifier le domaine principal",
"maindomain_changed": "Domaine principal modifié avec succès", "maindomain_changed": "Domaine principal modifié avec succès",
"monitor_disabled": "Le suivi de l'état du serveur a été désactivé avec succès", "monitor_disabled": "Le suivi de l'état du serveur a été désactivé avec succès",
@ -131,8 +143,11 @@
"no_restore_script": "Le script de sauvegarde n'a pas été trouvé pour l'application « {app:s} »", "no_restore_script": "Le script de sauvegarde n'a pas été trouvé pour l'application « {app:s} »",
"no_such_conf_file": "Le fichier {file:s} nexiste pas, il ne peut pas être copié", "no_such_conf_file": "Le fichier {file:s} nexiste pas, il ne peut pas être copié",
"not_enough_disk_space": "L'espace disque est insuffisant sur « {path:s} »", "not_enough_disk_space": "L'espace disque est insuffisant sur « {path:s} »",
"package_not_installed": "Le paquet « {pkgname} » n'est pas installé",
"package_unexpected_error": "Une erreur inattendue est survenue avec le paquet « {pkgname} »",
"package_unknown": "Paquet « {pkgname} » inconnu",
"packages_no_upgrade": "Il n'y a aucun paquet à mettre à jour", "packages_no_upgrade": "Il n'y a aucun paquet à mettre à jour",
"packages_upgrade_critical_later": "Les paquets critiques ({:s}) seront mis à jour ultérieurement", "packages_upgrade_critical_later": "Les paquets critiques ({packages:s}) seront mis à jour ultérieurement",
"packages_upgrade_failed": "Impossible de mettre à jour tous les paquets", "packages_upgrade_failed": "Impossible de mettre à jour tous les paquets",
"path_removal_failed": "Impossible de supprimer le chemin {:s}", "path_removal_failed": "Impossible de supprimer le chemin {:s}",
"pattern_backup_archive_name": "Doit être un nom de fichier valide composé de caractères alphanumérique et -_. uniquement", "pattern_backup_archive_name": "Doit être un nom de fichier valide composé de caractères alphanumérique et -_. uniquement",
@ -147,8 +162,8 @@
"pattern_port_or_range": "Doit être un numéro de port valide (ex. : 0-65535) ou une gamme de ports (ex. : 100:200)", "pattern_port_or_range": "Doit être un numéro de port valide (ex. : 0-65535) ou une gamme de ports (ex. : 100:200)",
"pattern_positive_number": "Doit être un nombre positif", "pattern_positive_number": "Doit être un nombre positif",
"pattern_username": "Doit être composé uniquement de caractères alphanumériques minuscules et de tirets bas", "pattern_username": "Doit être composé uniquement de caractères alphanumériques minuscules et de tirets bas",
"port_already_closed": "Le port {} est déjà fermé pour les connexions {:s}", "port_already_closed": "Le port {port:d} est déjà fermé pour les connexions {ip_version:s}",
"port_already_opened": "Le port {} est déjà ouvert pour les connexions {:s}", "port_already_opened": "Le port {port:d} est déjà ouvert pour les connexions {ip_version:s}",
"port_available": "Le port {port:d} est disponible", "port_available": "Le port {port:d} est disponible",
"port_unavailable": "Le port {port:d} n'est pas disponible", "port_unavailable": "Le port {port:d} n'est pas disponible",
"restore_action_required": "Vous devez préciser ce qui est à restaurer", "restore_action_required": "Vous devez préciser ce qui est à restaurer",
@ -163,25 +178,27 @@
"restore_running_app_script": "Lancement du script de restauration pour l'application « {app:s} »...", "restore_running_app_script": "Lancement du script de restauration pour l'application « {app:s} »...",
"restore_running_hooks": "Exécution des scripts de restauration...", "restore_running_hooks": "Exécution des scripts de restauration...",
"service_add_configuration": "Ajout du fichier de configuration {file:s}", "service_add_configuration": "Ajout du fichier de configuration {file:s}",
"service_add_failed": "Impossible d'ajouter le service « {:s} »", "service_add_failed": "Impossible d'ajouter le service « {service:s} »",
"service_added": "Service ajouté avec succès", "service_added": "Service « {service:s} » ajouté avec succès",
"service_already_started": "Le service « {:s} » est déjà démarré", "service_already_started": "Le service « {service:s} » est déjà démarré",
"service_already_stopped": "Le service « {:s} » est déjà arrêté", "service_already_stopped": "Le service « {service:s} » est déjà arrêté",
"service_cmd_exec_failed": "Impossible d'exécuter la commande « {:s} »", "service_cmd_exec_failed": "Impossible d'exécuter la commande « {command:s} »",
"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 loption --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 loption --force (ce qui écrasera toutes les modifications effectuées sur le fichier).",
"service_disable_failed": "Impossible de désactiver le service « {:s} »", "service_configured": "La configuration du service « {service:s} » a été générée avec succès",
"service_disabled": "Service « {:s} » désactivé avec succès", "service_configured_all": "La configuration de tous les services a été générée avec succès",
"service_enable_failed": "Impossible d'activer le service « {:s} »", "service_disable_failed": "Impossible de désactiver le service « {service:s} »",
"service_enabled": "Service « {:s} » activé avec succès", "service_disabled": "Service « {service:s} » désactivé avec succès",
"service_no_log": "Aucun journal à afficher pour le service « {:s} »", "service_enable_failed": "Impossible d'activer le service « {service:s} »",
"service_remove_failed": "Impossible d'enlever le service « {:s} »", "service_enabled": "Service « {service:s} » activé avec succès",
"service_removed": "Service enlevé avec succès", "service_no_log": "Aucun journal à afficher pour le service « {service:s} »",
"service_start_failed": "Impossible de démarrer le service « {:s} »", "service_remove_failed": "Impossible d'enlever le service « {service:s} »",
"service_started": "Le service « {:s} » a démarré avec succès", "service_removed": "Service « {service:s} » enlevé avec succès",
"service_status_failed": "Impossible de déterminer le statut du service « {:s} »", "service_start_failed": "Impossible de démarrer le service « {service:s} »",
"service_stop_failed": "Impossible d'arrêter le service « {:s} »", "service_started": "Le service « {service:s} » a démarré avec succès",
"service_stopped": "Service « {:s} » arrêté avec succès", "service_status_failed": "Impossible de déterminer le statut du service « {service:s} »",
"service_unknown": "Service « {:s} » inconnu", "service_stop_failed": "Impossible d'arrêter le service « {service:s} »",
"service_stopped": "Service « {service:s} » arrêté avec succès",
"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",
"show_diff": "Voici les différences :\n{diff:s}", "show_diff": "Voici les différences :\n{diff:s}",
"ssowat_conf_generated": "Configuration de SSOwat générée avec succès", "ssowat_conf_generated": "Configuration de SSOwat générée avec succès",
@ -190,7 +207,7 @@
"system_username_exists": "Le nom d'utilisateur existe déjà dans les utilisateurs système", "system_username_exists": "Le nom d'utilisateur existe déjà dans les utilisateurs système",
"unbackup_app": "L'application « {app:s} » ne sera pas sauvegardée", "unbackup_app": "L'application « {app:s} » ne sera pas sauvegardée",
"unexpected_error": "Une erreur inattendue est survenue", "unexpected_error": "Une erreur inattendue est survenue",
"unit_unknown": "Unité « {:s} » inconnue", "unit_unknown": "Unité « {unit:s} » inconnue",
"unlimit": "Pas de quota", "unlimit": "Pas de quota",
"unrestore_app": "L'application « {app:s} » ne sera pas restaurée", "unrestore_app": "L'application « {app:s} » ne sera pas restaurée",
"update_cache_failed": "Impossible de mettre à jour le cache de l'APT", "update_cache_failed": "Impossible de mettre à jour le cache de l'APT",
@ -207,7 +224,7 @@
"user_deletion_failed": "Impossible de supprimer l'utilisateur", "user_deletion_failed": "Impossible de supprimer l'utilisateur",
"user_home_creation_failed": "Impossible de créer le dossier personnel de l'utilisateur", "user_home_creation_failed": "Impossible de créer le dossier personnel de l'utilisateur",
"user_info_failed": "Impossible de récupérer les informations de l'utilisateur", "user_info_failed": "Impossible de récupérer les informations de l'utilisateur",
"user_unknown": "Utilisateur inconnu", "user_unknown": "Utilisateur « {user:s} » inconnu",
"user_update_failed": "Impossible de modifier l'utilisateur", "user_update_failed": "Impossible de modifier l'utilisateur",
"user_updated": "Utilisateur modifié avec succès", "user_updated": "Utilisateur modifié avec succès",
"yunohost_already_installed": "YunoHost est déjà installé", "yunohost_already_installed": "YunoHost est déjà installé",

View file

@ -1,7 +1,7 @@
{ {
"app_already_installed": "{:s} è già installato", "app_already_installed": "{app:s} è già installato",
"app_extraction_failed": "Impossibile estrarre i file di installazione", "app_extraction_failed": "Impossibile estrarre i file di installazione",
"app_not_installed": "{:s} non è installato", "app_not_installed": "{app:s} non è installato",
"app_unknown": "Applicazione sconosciuta", "app_unknown": "Applicazione sconosciuta",
"ask_email": "Indirizzo email", "ask_email": "Indirizzo email",
"ask_password": "Password", "ask_password": "Password",
@ -9,21 +9,21 @@
"backup_complete": "Backup completo", "backup_complete": "Backup completo",
"backup_invalid_archive": "Archivio di backup non valido", "backup_invalid_archive": "Archivio di backup non valido",
"backup_output_directory_not_empty": "Directory di output non è vuota", "backup_output_directory_not_empty": "Directory di output non è vuota",
"backup_running_app_script": "Esecuzione script di backup dell'applicazione '{:s}'...", "backup_running_app_script": "Esecuzione script di backup dell'applicazione '{app:s}'...",
"domain_created": "Dominio creato con successo", "domain_created": "Dominio creato con successo",
"domain_dyndns_invalid": "Dominio non valido da utilizzare con DynDNS", "domain_dyndns_invalid": "Dominio non valido da utilizzare con DynDNS",
"domain_exists": "Dominio esiste già", "domain_exists": "Dominio esiste già",
"ldap_initialized": "LDAP inizializzato con successo", "ldap_initialized": "LDAP inizializzato con successo",
"pattern_email": "Deve essere un indirizzo e-mail valido (es someone@domain.org)", "pattern_email": "Deve essere un indirizzo e-mail valido (es someone@domain.org)",
"pattern_mailbox_quota": "Deve essere una dimensione con un suffisso b/k/M/G/T o 0 per disabilitare la quota", "pattern_mailbox_quota": "Deve essere una dimensione con un suffisso b/k/M/G/T o 0 per disabilitare la quota",
"port_already_opened": "Port {} è già aperto per {:s} connessioni", "port_already_opened": "Port {port:d} è già aperto per {ip_version:s} connessioni",
"port_unavailable": "Porta {} non è disponibile", "port_unavailable": "Porta {port:d} non è disponibile",
"service_add_failed": "Impossibile aggiungere servizio '{:s}'", "service_add_failed": "Impossibile aggiungere servizio '{service:s}'",
"service_cmd_exec_failed": "Impossibile eseguire il comando '{:s}'", "service_cmd_exec_failed": "Impossibile eseguire il comando '{command:s}'",
"service_disabled": "Servizio '{:s}' disattivato con successo", "service_disabled": "Servizio '{service:s}' disattivato con successo",
"service_remove_failed": "Impossibile rimuovere il servizio '{:s}'", "service_remove_failed": "Impossibile rimuovere il servizio '{service:s}'",
"service_removed": "Servizio rimosso con successo", "service_removed": "Servizio rimosso con successo",
"service_stop_failed": "Impossibile arrestare il servizio '{:s}'", "service_stop_failed": "Impossibile arrestare il servizio '{service:s}'",
"system_username_exists": "Nome utente esiste già negli utenti del sistema", "system_username_exists": "Nome utente esiste già negli utenti del sistema",
"unrestore_app": "Applicazione '{app:s}' non verrà ripristinato", "unrestore_app": "Applicazione '{app:s}' non verrà ripristinato",
"upgrading_packages": "Aggiornamento dei pacchetti...", "upgrading_packages": "Aggiornamento dei pacchetti...",

View file

@ -1,8 +1,8 @@
{ {
"action_invalid": "Ongeldige actie '{:s}'", "action_invalid": "Ongeldige actie '{action:s}'",
"admin_password": "Administration password", "admin_password": "Administration password",
"admin_password_changed": "Het admin-wachtwoord is gewijzigd", "admin_password_changed": "Het admin-wachtwoord is gewijzigd",
"app_already_installed": "{:s} is al geïnstalleerd", "app_already_installed": "{app:s} is al geïnstalleerd",
"app_argument_invalid": "'{name:s}' bevat geldige waarde: {error:s}", "app_argument_invalid": "'{name:s}' bevat geldige waarde: {error:s}",
"app_argument_required": "Het '{name:s}' moet ingevuld worden", "app_argument_required": "Het '{name:s}' moet ingevuld worden",
"app_extraction_failed": "Kan installatiebestanden niet uitpakken", "app_extraction_failed": "Kan installatiebestanden niet uitpakken",
@ -12,13 +12,13 @@
"app_location_install_failed": "Kan app niet installeren op deze locatie", "app_location_install_failed": "Kan app niet installeren op deze locatie",
"app_manifest_invalid": "Ongeldig app-manifest", "app_manifest_invalid": "Ongeldig app-manifest",
"app_no_upgrade": "Geen apps op te upgraden", "app_no_upgrade": "Geen apps op te upgraden",
"app_not_installed": "{:s} is niet geinstalleerd ", "app_not_installed": "{app:s} is niet geinstalleerd",
"app_recent_version_required": "{:s} vereist een nieuwere versie van moulinette", "app_recent_version_required": "{:s} vereist een nieuwere versie van moulinette",
"app_removed": "{:s} succesvol verwijderd", "app_removed": "{app:s} succesvol verwijderd",
"app_sources_fetch_failed": "Kan bronbestanden niet ophalen", "app_sources_fetch_failed": "Kan bronbestanden niet ophalen",
"app_unknown": "Onbekende app", "app_unknown": "Onbekende app",
"app_upgrade_failed": "Kan niet alle apps updaten", "app_upgrade_failed": "Kan niet alle apps updaten",
"app_upgraded": "{:s} succesvol geüpgrade ", "app_upgraded": "{app:s} succesvol geüpgrade",
"appslist_fetched": "App-lijst succesvol aangemaakt.", "appslist_fetched": "App-lijst succesvol aangemaakt.",
"appslist_removed": "App-lijst succesvol verwijderd", "appslist_removed": "App-lijst succesvol verwijderd",
"appslist_unknown": "Onbekende app-lijst", "appslist_unknown": "Onbekende app-lijst",
@ -34,7 +34,7 @@
"backup_invalid_archive": "Ongeldig backup archief", "backup_invalid_archive": "Ongeldig backup archief",
"backup_output_directory_not_empty": "Doelmap is niet leeg", "backup_output_directory_not_empty": "Doelmap is niet leeg",
"backup_running_app_script": "Backup script voor app '{app:s}' is gestart...", "backup_running_app_script": "Backup script voor app '{app:s}' is gestart...",
"custom_app_url_required": "U moet een URL opgeven om uw aangepaste app {:s} bij te werken", "custom_app_url_required": "U moet een URL opgeven om uw aangepaste app {app:s} bij te werken",
"custom_appslist_name_required": "U moet een naam opgeven voor uw aangepaste app-lijst", "custom_appslist_name_required": "U moet een naam opgeven voor uw aangepaste app-lijst",
"dnsmasq_isnt_installed": "dnsmasq lijkt niet geïnstalleerd te zijn, voer alstublieft het volgende commando uit: 'apt-get remove bind9 && apt-get install dnsmasq'", "dnsmasq_isnt_installed": "dnsmasq lijkt niet geïnstalleerd te zijn, voer alstublieft het volgende commando uit: 'apt-get remove bind9 && apt-get install dnsmasq'",
"domain_cert_gen_failed": "Kan certificaat niet genereren", "domain_cert_gen_failed": "Kan certificaat niet genereren",
@ -63,7 +63,7 @@
"installation_failed": "Installatie gefaald", "installation_failed": "Installatie gefaald",
"ldap_initialized": "LDAP staat klaar voor gebruik", "ldap_initialized": "LDAP staat klaar voor gebruik",
"license_undefined": "undefined", "license_undefined": "undefined",
"mail_alias_remove_failed": "Kan mail alias niet verwijderen '{:s}'", "mail_alias_remove_failed": "Kan mail alias niet verwijderen '{mail:s}'",
"monitor_stats_no_update": "Er zijn geen recente monitoringstatistieken bij te werken", "monitor_stats_no_update": "Er zijn geen recente monitoringstatistieken bij te werken",
"mysql_db_creation_failed": "Aanmaken MySQL database gefaald", "mysql_db_creation_failed": "Aanmaken MySQL database gefaald",
"mysql_db_init_failed": "Initialiseren MySQL database gefaald", "mysql_db_init_failed": "Initialiseren MySQL database gefaald",
@ -77,20 +77,20 @@
"pattern_listname": "Slechts cijfers, letters en '_' zijn toegelaten", "pattern_listname": "Slechts cijfers, letters en '_' zijn toegelaten",
"pattern_mailbox_quota": "Mailbox quota moet een waarde bevatten met b/k/M/G/T erachter of 0 om geen quota in te stellen", "pattern_mailbox_quota": "Mailbox quota moet een waarde bevatten met b/k/M/G/T erachter of 0 om geen quota in te stellen",
"pattern_password": "Wachtwoord moet tenminste 3 karakters lang zijn", "pattern_password": "Wachtwoord moet tenminste 3 karakters lang zijn",
"port_already_closed": "Poort {} is al gesloten voor {:s} verbindingen", "port_already_closed": "Poort {port:d} is al gesloten voor {ip_version:s} verbindingen",
"port_already_opened": "Poort {} is al open voor {:s} verbindingen", "port_already_opened": "Poort {port:d} is al open voor {ip_version:s} verbindingen",
"port_available": "Poort {} is beschikbaar", "port_available": "Poort {port:d} is beschikbaar",
"port_unavailable": "Poort {} is niet beschikbaar", "port_unavailable": "Poort {port:d} is niet beschikbaar",
"restore_app_failed": "De app '{app:s}' kon niet worden terug gezet", "restore_app_failed": "De app '{app:s}' kon niet worden terug gezet",
"restore_hook_unavailable": "De restauration hook '{hook:s}' is niet beschikbaar op dit systeem", "restore_hook_unavailable": "De restauration hook '{hook:s}' is niet beschikbaar op dit systeem",
"service_add_failed": "Kan service '{:s}' niet toevoegen", "service_add_failed": "Kan service '{service:s}' niet toevoegen",
"service_already_started": "Service '{:s}' draait al", "service_already_started": "Service '{service:s}' draait al",
"service_cmd_exec_failed": "Kan '{:s}' niet uitvoeren", "service_cmd_exec_failed": "Kan '{command:s}' niet uitvoeren",
"service_disabled": "Service '{:s}' is uitgeschakeld", "service_disabled": "Service '{service:s}' is uitgeschakeld",
"service_remove_failed": "Kan service '{:s}' niet verwijderen", "service_remove_failed": "Kan service '{service:s}' niet verwijderen",
"service_removed": "Service werd verwijderd", "service_removed": "Service werd verwijderd",
"service_stop_failed": "Kan service '{:s}' niet stoppen", "service_stop_failed": "Kan service '{service:s}' niet stoppen",
"service_unknown": "De service '{:s}' bestaat niet", "service_unknown": "De service '{service:s}' bestaat niet",
"show_diff": "Let op de volgende verschillen zijn:\n{diff:s}", "show_diff": "Let op de volgende verschillen zijn:\n{diff:s}",
"unexpected_error": "Er is een onbekende fout opgetreden", "unexpected_error": "Er is een onbekende fout opgetreden",
"unrestore_app": "App '{app:s}' wordt niet teruggezet", "unrestore_app": "App '{app:s}' wordt niet teruggezet",

View file

@ -1,9 +1,9 @@
{ {
"action_invalid": "Acção Inválida '{:s}'", "action_invalid": "Acção Inválida '{action:s}'",
"admin_password": "Senha de administração", "admin_password": "Senha de administração",
"admin_password_change_failed": "Não foi possível alterar a senha", "admin_password_change_failed": "Não foi possível alterar a senha",
"admin_password_changed": "Senha de administração alterada com êxito", "admin_password_changed": "Senha de administração alterada com êxito",
"app_already_installed": "{:s} já está instalada", "app_already_installed": "{app:s} já está instalada",
"app_extraction_failed": "Não foi possível extrair os ficheiros para instalação", "app_extraction_failed": "Não foi possível extrair os ficheiros para instalação",
"app_id_invalid": "ID da aplicação invélida", "app_id_invalid": "ID da aplicação invélida",
"app_install_files_invalid": "Ficheiros para instalação corrompidos", "app_install_files_invalid": "Ficheiros para instalação corrompidos",
@ -11,13 +11,13 @@
"app_location_install_failed": "Não foi possível instalar a aplicação neste diretório", "app_location_install_failed": "Não foi possível instalar a aplicação neste diretório",
"app_manifest_invalid": "Manifesto da aplicação inválido", "app_manifest_invalid": "Manifesto da aplicação inválido",
"app_no_upgrade": "Não existem aplicações para atualizar", "app_no_upgrade": "Não existem aplicações para atualizar",
"app_not_installed": "{:s} não está instalada", "app_not_installed": "{app:s} não está instalada",
"app_recent_version_required": "{:s} requer uma versão mais recente da moulinette", "app_recent_version_required": "{:s} requer uma versão mais recente da moulinette",
"app_removed": "{:s} removida com êxito", "app_removed": "{app:s} removida com êxito",
"app_sources_fetch_failed": "Impossível obter os códigos fontes", "app_sources_fetch_failed": "Impossível obter os códigos fontes",
"app_unknown": "Aplicação desconhecida", "app_unknown": "Aplicação desconhecida",
"app_upgrade_failed": "Unable to upgrade all apps", "app_upgrade_failed": "Unable to upgrade all apps",
"app_upgraded": "{:s} atualizada com êxito", "app_upgraded": "{app:s} atualizada com êxito",
"appslist_fetched": "Lista de aplicações processada com êxito", "appslist_fetched": "Lista de aplicações processada com êxito",
"appslist_removed": "Lista de aplicações removida com êxito", "appslist_removed": "Lista de aplicações removida com êxito",
"appslist_retrieve_error": "Não foi possível obter a lista de aplicações remotas", "appslist_retrieve_error": "Não foi possível obter a lista de aplicações remotas",
@ -34,7 +34,7 @@
"backup_creating_archive": "A criar ficheiro de backup...", "backup_creating_archive": "A criar ficheiro de backup...",
"backup_invalid_archive": "Arquivo de backup inválido", "backup_invalid_archive": "Arquivo de backup inválido",
"backup_output_directory_not_empty": "A pasta de destino não se encontra vazia", "backup_output_directory_not_empty": "A pasta de destino não se encontra vazia",
"custom_app_url_required": "Deve proporcionar uma URL para atualizar a sua aplicação personalizada {:s}", "custom_app_url_required": "Deve proporcionar uma URL para atualizar a sua aplicação personalizada {app:s}",
"custom_appslist_name_required": "Deve fornecer um nome para a sua lista de aplicações personalizada", "custom_appslist_name_required": "Deve fornecer um nome para a sua lista de aplicações personalizada",
"domain_cert_gen_failed": "Não foi possível gerar o certificado", "domain_cert_gen_failed": "Não foi possível gerar o certificado",
"domain_created": "Domínio criado com êxito", "domain_created": "Domínio criado com êxito",
@ -58,7 +58,7 @@
"dyndns_ip_updated": "Endereço IP atualizado com êxito a partir de DynDNS", "dyndns_ip_updated": "Endereço IP atualizado com êxito a partir de DynDNS",
"dyndns_key_generating": "A chave DNS está a ser gerada, isto pode demorar um pouco...", "dyndns_key_generating": "A chave DNS está a ser gerada, isto pode demorar um pouco...",
"dyndns_registered": "Dom+inio DynDNS registado com êxito", "dyndns_registered": "Dom+inio DynDNS registado com êxito",
"dyndns_registration_failed": "Não foi possível registar o domínio DynDNS: {:s}", "dyndns_registration_failed": "Não foi possível registar o domínio DynDNS: {error:s}",
"dyndns_unavailable": "Subdomínio DynDNS indisponível", "dyndns_unavailable": "Subdomínio DynDNS indisponível",
"executing_script": "A executar o script...", "executing_script": "A executar o script...",
"extracting": "Extração em curso...", "extracting": "Extração em curso...",
@ -71,9 +71,9 @@
"iptables_unavailable": "Não pode alterar aqui a iptables. Ou o seu kernel não o suporta ou está num espaço reservado.", "iptables_unavailable": "Não pode alterar aqui a iptables. Ou o seu kernel não o suporta ou está num espaço reservado.",
"ldap_initialized": "LDAP inicializada com êxito", "ldap_initialized": "LDAP inicializada com êxito",
"license_undefined": "indefinido", "license_undefined": "indefinido",
"mail_alias_remove_failed": "Não foi possível remover a etiqueta de correio '{:s}'", "mail_alias_remove_failed": "Não foi possível remover a etiqueta de correio '{mail:s}'",
"mail_domain_unknown": "Domínio de endereço de correio desconhecido '{:s}'", "mail_domain_unknown": "Domínio de endereço de correio desconhecido '{domain:s}'",
"mail_forward_remove_failed": "Não foi possível remover o reencaminhamento de correio '{:s}'", "mail_forward_remove_failed": "Não foi possível remover o reencaminhamento de correio '{mail:s}'",
"maindomain_change_failed": "Incapaz alterar o domínio raiz", "maindomain_change_failed": "Incapaz alterar o domínio raiz",
"maindomain_changed": "Domínio raiz alterado com êxito", "maindomain_changed": "Domínio raiz alterado com êxito",
"monitor_disabled": "Monitorização do servidor parada com êxito", "monitor_disabled": "Monitorização do servidor parada com êxito",
@ -92,7 +92,7 @@
"no_appslist_found": "Não foi encontrada a lista de aplicações", "no_appslist_found": "Não foi encontrada a lista de aplicações",
"no_internet_connection": "O servidor não está ligado à Internet", "no_internet_connection": "O servidor não está ligado à Internet",
"packages_no_upgrade": "Não existem pacotes para atualizar", "packages_no_upgrade": "Não existem pacotes para atualizar",
"packages_upgrade_critical_later": "Os pacotes críticos ({:s}) serão atualizados depois", "packages_upgrade_critical_later": "Os pacotes críticos ({packages:s}) serão atualizados depois",
"packages_upgrade_failed": "Não foi possível atualizar todos os pacotes", "packages_upgrade_failed": "Não foi possível atualizar todos os pacotes",
"path_removal_failed": "Incapaz remover o caminho {:s}", "path_removal_failed": "Incapaz remover o caminho {:s}",
"pattern_domain": "Deve ser um nome de domínio válido (p.e. meu-dominio.org)", "pattern_domain": "Deve ser um nome de domínio válido (p.e. meu-dominio.org)",
@ -104,30 +104,30 @@
"pattern_port": "Deve ser um número de porta válido (entre 0-65535)", "pattern_port": "Deve ser um número de porta válido (entre 0-65535)",
"pattern_username": "Must be lower-case alphanumeric and underscore characters only", "pattern_username": "Must be lower-case alphanumeric and underscore characters only",
"restore_confirm_yunohost_installed": "Quer mesmo restaurar um sistema já instalado? [{answers:s}]", "restore_confirm_yunohost_installed": "Quer mesmo restaurar um sistema já instalado? [{answers:s}]",
"service_add_failed": "Incapaz adicionar serviço '{:s}'", "service_add_failed": "Incapaz adicionar serviço '{service:s}'",
"service_added": "Serviço adicionado com êxito", "service_added": "Serviço adicionado com êxito",
"service_already_started": "O serviço '{:s}' já está em execussão", "service_already_started": "O serviço '{service:s}' já está em execussão",
"service_already_stopped": "O serviço '{:s}' já está parado", "service_already_stopped": "O serviço '{service:s}' já está parado",
"service_cmd_exec_failed": "Incapaz executar o comando '{:s}'", "service_cmd_exec_failed": "Incapaz executar o comando '{command:s}'",
"service_disable_failed": "Incapaz desativar o serviço '{:s}'", "service_disable_failed": "Incapaz desativar o serviço '{service:s}'",
"service_disabled": "O serviço '{:s}' foi desativado com êxito", "service_disabled": "O serviço '{service:s}' foi desativado com êxito",
"service_enable_failed": "Incapaz de ativar o serviço '{:s}'", "service_enable_failed": "Incapaz de ativar o serviço '{service:s}'",
"service_enabled": "Serviço '{:s}' ativado com êxito", "service_enabled": "Serviço '{service:s}' ativado com êxito",
"service_no_log": "Não existem registos para mostrar do serviço '{:s}'", "service_no_log": "Não existem registos para mostrar do serviço '{service:s}'",
"service_remove_failed": "Incapaz de remover o serviço '{:s}'", "service_remove_failed": "Incapaz de remover o serviço '{service:s}'",
"service_removed": "Serviço eliminado com êxito", "service_removed": "Serviço eliminado com êxito",
"service_start_failed": "Não foi possível iniciar o serviço '{:s}'", "service_start_failed": "Não foi possível iniciar o serviço '{service:s}'",
"service_started": "O serviço '{:s} foi iniciado com êxito", "service_started": "O serviço '{service:s}' foi iniciado com êxito",
"service_status_failed": "Incapaz determinar o estado do serviço '{:s}'", "service_status_failed": "Incapaz determinar o estado do serviço '{service:s}'",
"service_stop_failed": "Incapaz parar o serviço '{:s}", "service_stop_failed": "Incapaz parar o serviço '{service:s}'",
"service_stopped": "O serviço '{:s}' foi parado com êxito", "service_stopped": "O serviço '{service:s}' foi parado com êxito",
"service_unknown": "Serviço desconhecido '{:s}'", "service_unknown": "Serviço desconhecido '{service:s}'",
"ssowat_conf_generated": "Configuração SSOwat gerada com êxito", "ssowat_conf_generated": "Configuração SSOwat gerada com êxito",
"ssowat_conf_updated": "Configuração persistente SSOwat atualizada com êxito", "ssowat_conf_updated": "Configuração persistente SSOwat atualizada com êxito",
"system_upgraded": "Sistema atualizado com êxito", "system_upgraded": "Sistema atualizado com êxito",
"system_username_exists": "O utilizador já existe no registo do sistema", "system_username_exists": "O utilizador já existe no registo do sistema",
"unexpected_error": "Ocorreu um erro inesperado", "unexpected_error": "Ocorreu um erro inesperado",
"unit_unknown": "Unidade desconhecida '{:s}'", "unit_unknown": "Unidade desconhecida '{unit:s}'",
"update_cache_failed": "Não foi possível atualizar os cabeçalhos APT", "update_cache_failed": "Não foi possível atualizar os cabeçalhos APT",
"updating_apt_cache": "A atualizar a lista de pacotes disponíveis...", "updating_apt_cache": "A atualizar a lista de pacotes disponíveis...",
"upgrade_complete": "Atualização completa", "upgrade_complete": "Atualização completa",

View file

@ -1413,12 +1413,24 @@ def _encode_string(value):
def _check_manifest_requirements(manifest): def _check_manifest_requirements(manifest):
"""Check if required packages are met from the manifest""" """Check if required packages are met from the manifest"""
requirements = manifest.get('requirements', dict()) requirements = manifest.get('requirements', dict())
# FIXME: Deprecate min_version key # FIXME: Deprecate min_version key
if 'min_version' in manifest: if 'min_version' in manifest:
requirements['yunohost'] = '>> {0}'.format(manifest['min_version']) requirements['yunohost'] = '>> {0}'.format(manifest['min_version'])
logger.debug("the manifest key 'min_version' is deprecated, " logger.debug("the manifest key 'min_version' is deprecated, "
"use 'requirements' instead.") "use 'requirements' instead.")
if not requirements:
# Validate multi-instance app
if manifest.get('multi_instance', False):
# Handle backward-incompatible change introduced in yunohost >= 2.3.6
# See https://dev.yunohost.org/issues/156
yunohost_req = requirements.get('yunohost', None)
if (not yunohost_req or
not packages.SpecifierSet(yunohost_req) & '>= 2.3.6'):
raise MoulinetteError(errno.EINVAL, '{0}{1}'.format(
m18n.g('colon', m18n.n('app_incompatible')),
m18n.n('app_package_need_update')))
elif not requirements:
return return
logger.info(m18n.n('app_requirements_checking')) logger.info(m18n.n('app_requirements_checking'))
@ -1429,7 +1441,8 @@ def _check_manifest_requirements(manifest):
*requirements.keys(), strict=True, as_dict=True) *requirements.keys(), strict=True, as_dict=True)
except packages.PackageException as e: except packages.PackageException as e:
raise MoulinetteError(errno.EINVAL, raise MoulinetteError(errno.EINVAL,
m18n.n('app_requirements_failed', err=str(e))) m18n.n('app_requirements_failed',
error=str(e)))
# Iterate over requirements # Iterate over requirements
for pkgname, spec in requirements.items(): for pkgname, spec in requirements.items():

View file

@ -39,7 +39,9 @@ from moulinette.core import MoulinetteError
from moulinette.utils import filesystem from moulinette.utils import filesystem
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.app import app_info, app_ssowatconf, _is_installed, _parse_app_instance_name from yunohost.app import (
app_info, app_ssowatconf, _is_installed, _parse_app_instance_name
)
from yunohost.hook import ( from yunohost.hook import (
hook_info, hook_callback, hook_exec, custom_hook_folder hook_info, hook_callback, hook_exec, custom_hook_folder
) )
@ -384,11 +386,11 @@ def backup_restore(auth, name, hooks=[], ignore_hooks=False,
else: else:
# Retrieve the domain from the backup # Retrieve the domain from the backup
try: try:
with open("%s/yunohost/current_host" % tmp_dir, 'r') as f: with open("%s/conf/ynh/current_host" % tmp_dir, 'r') as f:
domain = f.readline().rstrip() domain = f.readline().rstrip()
except IOError: except IOError:
logger.debug("unable to retrieve domain from " logger.debug("unable to retrieve current_host from the backup",
"'%s/yunohost/current_host'", tmp_dir, exc_info=1) exc_info=1)
raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive')) raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive'))
logger.debug("executing the post-install...") logger.debug("executing the post-install...")

View file

@ -37,6 +37,8 @@ from urllib import urlopen
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils.log import getActionLogger from moulinette.utils.log import getActionLogger
from yunohost.service import service_regen_conf
logger = getActionLogger('yunohost.domain') logger = getActionLogger('yunohost.domain')
@ -78,7 +80,6 @@ def domain_add(auth, domain, dyndns=False):
dyndns -- Subscribe to DynDNS dyndns -- Subscribe to DynDNS
""" """
from yunohost.service import service_regenconf
from yunohost.hook import hook_callback from yunohost.hook import hook_callback
attr_dict = { 'objectClass' : ['mailDomain', 'top'] } attr_dict = { 'objectClass' : ['mailDomain', 'top'] }
@ -157,10 +158,8 @@ def domain_add(auth, domain, dyndns=False):
try: try:
with open('/etc/yunohost/installed', 'r') as f: with open('/etc/yunohost/installed', 'r') as f:
service_regenconf(service='nginx') service_regen_conf(names=[
service_regenconf(service='metronome') 'nginx', 'metronome', 'dnsmasq', 'rmilter'])
service_regenconf(service='dnsmasq')
service_regenconf(service='rmilter')
os.system('yunohost app ssowatconf > /dev/null 2>&1') os.system('yunohost app ssowatconf > /dev/null 2>&1')
except IOError: pass except IOError: pass
except: except:
@ -183,7 +182,6 @@ def domain_remove(auth, domain, force=False):
force -- Force the domain removal force -- Force the domain removal
""" """
from yunohost.service import service_regenconf
from yunohost.hook import hook_callback from yunohost.hook import hook_callback
if not force and domain not in domain_list(auth)['domains']: if not force and domain not in domain_list(auth)['domains']:
@ -206,9 +204,7 @@ def domain_remove(auth, domain, force=False):
else: else:
raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed'))
service_regenconf(service='nginx') service_regen_conf(names=['nginx', 'metronome', 'dnsmasq'])
service_regenconf(service='metronome')
service_regenconf(service='dnsmasq')
os.system('yunohost app ssowatconf > /dev/null 2>&1') os.system('yunohost app ssowatconf > /dev/null 2>&1')
hook_callback('post_domain_remove', args=[domain]) hook_callback('post_domain_remove', args=[domain])

View file

@ -176,6 +176,8 @@ def hook_list(action, list_by='name', show_info=False):
def _append_folder(d, folder): def _append_folder(d, folder):
# Iterate over and add hook from a folder # Iterate over and add hook from a folder
for f in os.listdir(folder + action): for f in os.listdir(folder + action):
if f[0] == '.' or f[-1] == '~':
continue
path = '%s%s/%s' % (folder, action, f) path = '%s%s/%s' % (folder, action, f)
priority, name = _extract_filename_parts(f) priority, name = _extract_filename_parts(f)
_append_hook(d, priority, name, path) _append_hook(d, priority, name, path)
@ -205,14 +207,22 @@ def hook_list(action, list_by='name', show_info=False):
return { 'hooks': result } return { 'hooks': result }
def hook_callback(action, hooks=[], args=None): def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None,
pre_callback=None, post_callback=None):
""" """
Execute all scripts binded to an action Execute all scripts binded to an action
Keyword argument: Keyword argument:
action -- Action name action -- Action name
hooks -- List of hooks names to execute hooks -- List of hooks names to execute
args -- Ordered list of arguments to pass to the script args -- Ordered list of arguments to pass to the scripts
no_trace -- Do not print each command that will be executed
chdir -- The directory from where the scripts will be executed
pre_callback -- An object to call before each script execution with
(name, priority, path, args) as arguments and which must return
the arguments to pass to the script
post_callback -- An object to call after each script execution with
(name, priority, path, succeed) as arguments
""" """
result = { 'succeed': {}, 'failed': {} } result = { 'succeed': {}, 'failed': {} }
@ -252,26 +262,34 @@ def hook_callback(action, hooks=[], args=None):
if not hooks_dict: if not hooks_dict:
return result return result
# Format arguments # Validate callbacks
if args is None: if not callable(pre_callback):
args = [] pre_callback = lambda name, priority, path, args: args
elif not isinstance(args, list): if not callable(post_callback):
args = [args] post_callback = lambda name, priority, path, succeed: None
# Iterate over hooks and execute them # Iterate over hooks and execute them
for priority in sorted(hooks_dict): for priority in sorted(hooks_dict):
for name, info in iter(hooks_dict[priority].items()): for name, info in iter(hooks_dict[priority].items()):
state = 'succeed' state = 'succeed'
filename = '%s-%s' % (priority, name) path = info['path']
try: try:
hook_exec(info['path'], args=args, raise_on_error=True) hook_args = pre_callback(name=name, priority=priority,
path=path, args=args)
hook_exec(path, args=hook_args, chdir=chdir,
no_trace=no_trace, raise_on_error=True)
except MoulinetteError as e: except MoulinetteError as e:
logger.error(str(e))
state = 'failed' state = 'failed'
logger.error(str(e))
post_callback(name=name, priority=priority, path=path,
succeed=False)
else:
post_callback(name=name, priority=priority, path=path,
succeed=True)
try: try:
result[state][name].append(info['path']) result[state][name].append(path)
except KeyError: except KeyError:
result[state][name] = [info['path']] result[state][name] = [path]
return result return result
@ -282,7 +300,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False,
Keyword argument: Keyword argument:
path -- Path of the script to execute path -- Path of the script to execute
args -- A list of arguments to pass to the script args -- Ordered list of arguments to pass to the script
raise_on_error -- Raise if the script returns a non-zero exit code raise_on_error -- Raise if the script returns a non-zero exit code
no_trace -- Do not print each command that will be executed no_trace -- Do not print each command that will be executed
chdir -- The directory from where the script will be executed chdir -- The directory from where the script will be executed

View file

@ -30,20 +30,18 @@ import glob
import subprocess import subprocess
import errno import errno
import shutil import shutil
import difflib
import hashlib import hashlib
from difflib import unified_diff
from moulinette.core import MoulinetteError from moulinette.core import MoulinetteError
from moulinette.utils import log from moulinette.utils import log, filesystem
template_dir = os.getenv( from yunohost.hook import hook_list, hook_callback
'YUNOHOST_TEMPLATE_DIR',
'/usr/share/yunohost/templates'
) base_conf_path = '/home/yunohost.conf'
conf_backup_dir = os.getenv( backup_conf_dir = os.path.join(base_conf_path, 'backup')
'YUNOHOST_CONF_BACKUP_DIR', pending_conf_dir = os.path.join(base_conf_path, 'pending')
'/home/yunohost.backup/conffiles'
)
logger = log.getActionLogger('yunohost.service') logger = log.getActionLogger('yunohost.service')
@ -273,26 +271,196 @@ def service_log(name, number=50):
return result return result
def service_regenconf(service=None, force=False): def service_regen_conf(names=[], with_diff=False, force=False, dry_run=False,
list_pending=False):
""" """
Regenerate the configuration file(s) for a service and compare the result Regenerate the configuration file(s) for a service
with the existing configuration file.
Prints the differences between files if any.
Keyword argument: Keyword argument:
service -- Regenerate configuration for a specfic service names -- Services name to regenerate configuration of
force -- Override the current configuration with the newly generated with_diff -- Show differences in case of configuration changes
one, even if it has been modified force -- Override all manual modifications in configuration files
dry_run -- Show what would have been regenerated
list_pending -- List pending configuration files and exit
""" """
from yunohost.hook import hook_callback result = {}
if service is not None: # Return the list of pending conf
hook_callback('conf_regen', [service], args=[force]) if list_pending:
logger.success(m18n.n('service_configured', service=service)) pending_conf = _get_pending_conf(names)
if with_diff:
for service, conf_files in pending_conf.items():
for system_path, pending_path in conf_files.items():
pending_conf[service][system_path] = {
'pending_conf': pending_path,
'diff': _get_files_diff(
system_path, pending_path, True),
}
return pending_conf
# Clean pending conf directory
shutil.rmtree(pending_conf_dir, ignore_errors=True)
filesystem.mkdir(pending_conf_dir, 0755, True)
# Format common hooks arguments
common_args = [1 if force else 0, 1 if dry_run else 0]
# Execute hooks for pre-regen
pre_args = ['pre',] + common_args
def _pre_call(name, priority, path, args):
# create the pending conf directory for the service
service_pending_path = os.path.join(pending_conf_dir, name)
filesystem.mkdir(service_pending_path, 0755, True, uid='admin')
# return the arguments to pass to the script
return pre_args + [service_pending_path,]
pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call)
# Update the services name
names = pre_result['succeed'].keys()
if not names:
raise MoulinetteError(errno.EIO,
m18n.n('service_regenconf_failed',
services=', '.join(pre_result['failed'])))
# Set the processing method
_regen = _process_regen_conf if not dry_run else lambda *a, **k: True
# Iterate over services and process pending conf
for service, conf_files in _get_pending_conf(names).items():
logger.info(m18n.n(
'service_regenconf_pending_applying' if not dry_run else \
'service_regenconf_dry_pending_applying',
service=service))
conf_hashes = _get_conf_hashes(service)
succeed_regen = {}
failed_regen = {}
for system_path, pending_path in conf_files.items():
logger.debug("processing pending conf '%s' to system conf '%s'",
pending_path, system_path)
conf_status = None
regenerated = False
# Get the diff between files
conf_diff = _get_files_diff(
system_path, pending_path, True) if with_diff else None
# Check if the conf must be removed
to_remove = True if os.path.getsize(pending_path) == 0 else False
# Retrieve and calculate hashes
current_hash = conf_hashes.get(system_path, None)
system_hash = _calculate_hash(system_path)
new_hash = None if to_remove else _calculate_hash(pending_path)
# -> system conf does not exists
if not system_hash:
if to_remove:
logger.debug("> system conf is already removed")
os.remove(pending_path)
continue
if not current_hash or force:
if force:
logger.debug("> system conf has been manually removed")
conf_status = 'force-created'
else: else:
hook_callback('conf_regen', args=[force]) logger.debug("> system conf does not exist yet")
logger.success(m18n.n('service_configured_all')) conf_status = 'created'
regenerated = _regen(
system_path, pending_path, save=False)
else:
logger.warning(m18n.n(
'service_conf_file_manually_removed',
conf=system_path))
conf_status = 'removed'
# -> system conf is not managed yet
elif not current_hash:
logger.debug("> system conf is not managed yet")
if system_hash == new_hash:
logger.debug("> no changes to system conf has been made")
conf_status = 'managed'
regenerated = True
elif force and to_remove:
regenerated = _regen(system_path)
conf_status = 'force-removed'
elif force:
regenerated = _regen(system_path, pending_path)
conf_status = 'force-updated'
else:
logger.warning(m18n.n('service_conf_file_not_managed',
conf=system_path))
conf_status = 'unmanaged'
# -> system conf has not been manually modified
elif system_hash == current_hash:
if to_remove:
regenerated = _regen(system_path)
conf_status = 'removed'
elif system_hash != new_hash:
regenerated = _regen(system_path, pending_path)
conf_status = 'updated'
else:
logger.debug("> system conf is already up-to-date")
os.remove(pending_path)
continue
else:
logger.debug("> system conf has been manually modified")
if force:
regenerated = _regen(system_path, pending_path)
conf_status = 'force-updated'
else:
logger.warning(m18n.n(
'service_conf_file_manually_modified',
conf=system_path))
conf_status = 'modified'
# Store the result
conf_result = {'status': conf_status}
if conf_diff is not None:
conf_result['diff'] = conf_diff
if regenerated:
succeed_regen[system_path] = conf_result
conf_hashes[system_path] = new_hash
if os.path.isfile(pending_path):
os.remove(pending_path)
else:
failed_regen[system_path] = conf_result
# Check for service conf changes
if not succeed_regen and not failed_regen:
logger.info(m18n.n('service_conf_up_to_date', service=service))
continue
elif not failed_regen:
logger.success(m18n.n(
'service_conf_updated' if not dry_run else \
'service_conf_would_be_updated',
service=service))
if succeed_regen and not dry_run:
_update_conf_hashes(service, conf_hashes)
# Append the service results
result[service] = {
'applied': succeed_regen,
'pending': failed_regen
}
# Return in case of dry run
if dry_run:
return result
# Execute hooks for post-regen
post_args = ['post',] + common_args
def _pre_call(name, priority, path, args):
# append coma-separated applied changes for the service
if name in result and result[name]['applied']:
regen_conf_files = ','.join(result[name]['applied'].keys())
else:
regen_conf_files = ''
return post_args + [regen_conf_files,]
hook_callback('conf_regen', names, pre_callback=_pre_call)
return result
def _run_service_command(action, service): def _run_service_command(action, service):
@ -380,175 +548,141 @@ def _tail(file, n, offset=None):
except IOError: return [] except IOError: return []
def _get_diff(string, filename): def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True):
""" """Compare two files and return the differences
Show differences between a string and a file's content
Keyword argument: Read and compare two files. The differences are returned either as a delta
string -- The string in unified diff format or a formatted string if as_string is True. The
filename -- The file to compare with header can also be removed if skip_header is True.
""" """
contents = [[], []]
for i, path in enumerate((orig_file, new_file)):
try: try:
with open(filename, 'r') as f: with open(path, 'r') as f:
file_lines = f.readlines() contents[i] = f.readlines()
except IOError:
pass
string = string + '\n' # Compare files and format output
new_lines = string.splitlines(True) diff = unified_diff(contents[0], contents[1])
if file_lines: if skip_header:
while '\n' == file_lines[-1]: for i in range(2):
del file_lines[-1] try:
return difflib.unified_diff(file_lines, new_lines) next(diff)
except IOError: return [] except:
break
if as_string:
result = ''.join(line for line in diff)
return result.rstrip()
return diff
def _hash(filename): def _calculate_hash(path):
""" """Calculate the MD5 hash of a file"""
Calculate a MD5 hash of a file
Keyword argument:
filename -- The file to hash
"""
hasher = hashlib.md5() hasher = hashlib.md5()
try: try:
with open(filename, 'rb') as f: with open(path, 'rb') as f:
buf = f.read() hasher.update(f.read())
hasher.update(buf)
return hasher.hexdigest() return hasher.hexdigest()
except IOError: except IOError:
return 'no hash yet' return None
def service_saferemove(service, conf_file, force=False): def _get_pending_conf(services=[]):
""" """Get pending configuration for service(s)
Check if the specific file has been modified before removing it.
Backup the file in /home/yunohost.backup
Keyword argument: Iterate over the pending configuration directory for given service(s) - or
service -- Service name of the file to delete all if empty - and look for files inside. Each file is considered as a
conf_file -- The file to write pending configuration file and therefore must be in the same directory
force -- Force file deletion tree than the system file that it replaces.
The result is returned as a dict of services with pending configuration as
key and a dict of `system_conf_path` => `pending_conf_path` as value.
""" """
deleted = False result = {}
services = _get_services() if not os.path.isdir(pending_conf_dir):
return result
if not services:
services = os.listdir(pending_conf_dir)
for name in services:
service_conf = {}
service_pending_path = os.path.join(pending_conf_dir, name)
path_index = len(service_pending_path)
for root, dirs, files in os.walk(service_pending_path):
for filename in files:
pending_path = os.path.join(root, filename)
service_conf[pending_path[path_index:]] = pending_path
if service_conf:
result[name] = service_conf
return result
if not os.path.exists(conf_file):
def _get_conf_hashes(service):
"""Get the registered conf hashes for a service"""
try: try:
del services[service]['conffiles'][conf_file] return _get_services()[service]['conffiles']
except KeyError: pass except:
logger.debug("unable to retrieve conf hashes for %s",
service, exc_info=1)
return {}
def _update_conf_hashes(service, hashes):
"""Update the registered conf hashes for a service"""
logger.debug("updating conf hashes for '%s' with: %s",
service, hashes)
services = _get_services()
service_conf = services.get(service, {})
service_conf['conffiles'] = hashes
services[service] = service_conf
_save_services(services)
def _process_regen_conf(system_conf, new_conf=None, save=True):
"""Regenerate a given system configuration file
Replace a given system configuration file by a new one or delete it if
new_conf is None. A backup of the file - keeping its directory tree - will
be done in the backup conf directory before any operation if save is True.
"""
if save:
backup_path = os.path.join(backup_conf_dir, '{0}-{1}'.format(
system_conf.lstrip('/'), time.strftime("%Y%m%d.%H%M%S")))
backup_dir = os.path.dirname(backup_path)
if not os.path.isdir(backup_dir):
filesystem.mkdir(backup_dir, 0755, True)
shutil.copy2(system_conf, backup_path)
logger.info(m18n.n('service_conf_file_backed_up',
conf=system_conf, backup=backup_path))
try:
if not new_conf:
os.remove(system_conf)
logger.info(m18n.n('service_conf_file_removed',
conf=system_conf))
else:
system_dir = os.path.dirname(system_conf)
if not os.path.isdir(system_dir):
filesystem.mkdir(system_dir, 0755, True)
shutil.copyfile(new_conf, system_conf)
logger.info(m18n.n('service_conf_file_updated',
conf=system_conf))
except:
if not new_conf and os.path.exists(system_conf):
logger.warning(m18n.n('service_conf_file_remove_failed',
conf=system_conf),
exc_info=1)
return False
elif new_conf:
try:
copy_succeed = os.path.samefile(system_conf, new_conf)
except:
copy_succeed = False
finally:
if not copy_succeed:
logger.warning(m18n.n('service_conf_file_copy_failed',
conf=system_conf, new=new_conf),
exc_info=1)
return False
return True return True
# Backup existing file
date = time.strftime("%Y%m%d.%H%M%S")
conf_backup_file = conf_backup_dir + conf_file +'-'+ date
process = subprocess.Popen(
['install', '-D', conf_file, conf_backup_file]
)
process.wait()
# Retrieve hashes
if not 'conffiles' in services[service]:
services[service]['conffiles'] = {}
if conf_file in services[service]['conffiles']:
previous_hash = services[service]['conffiles'][conf_file]
else:
previous_hash = 'no hash yet'
current_hash = _hash(conf_file)
# Handle conflicts
if force or previous_hash == current_hash:
os.remove(conf_file)
try:
del services[service]['conffiles'][conf_file]
except KeyError: pass
deleted = True
else:
services[service]['conffiles'][conf_file] = previous_hash
os.remove(conf_backup_file)
if len(previous_hash) == 32 or previous_hash[-32:] != current_hash:
logger.warning(m18n.n('service_configuration_conflict',
file=conf_file))
_save_services(services)
return deleted
def service_safecopy(service, new_conf_file, conf_file, force=False):
"""
Check if the specific file has been modified and display differences.
Stores the file hash in the services.yml file
Keyword argument:
service -- Service name attached to the conf file
new_conf_file -- Path to the desired conf file
conf_file -- Path to the targeted conf file
force -- Force file overriding
"""
regenerated = False
services = _get_services()
if not os.path.exists(new_conf_file):
raise MoulinetteError(errno.EIO, m18n.n('no_such_conf_file', file=new_conf_file))
with open(new_conf_file, 'r') as f:
new_conf = ''.join(f.readlines()).rstrip()
# Backup existing file
date = time.strftime("%Y%m%d.%H%M%S")
conf_backup_file = conf_backup_dir + conf_file +'-'+ date
if os.path.exists(conf_file):
process = subprocess.Popen(
['install', '-D', conf_file, conf_backup_file]
)
process.wait()
else:
logger.info(m18n.n('service_add_configuration', file=conf_file))
# Add the service if it does not exist
if service not in services.keys():
services[service] = {}
# Retrieve hashes
if not 'conffiles' in services[service]:
services[service]['conffiles'] = {}
if conf_file in services[service]['conffiles']:
previous_hash = services[service]['conffiles'][conf_file]
else:
previous_hash = 'no hash yet'
current_hash = _hash(conf_file)
diff = list(_get_diff(new_conf, conf_file))
# Handle conflicts
if force or previous_hash == current_hash:
with open(conf_file, 'w') as f: f.write(new_conf)
new_hash = _hash(conf_file)
if previous_hash != new_hash:
regenerated = True
elif len(diff) == 0:
new_hash = _hash(conf_file)
else:
new_hash = previous_hash
if (len(previous_hash) == 32 or previous_hash[-32:] != current_hash):
logger.warning('{0} {1}'.format(
m18n.n('service_configuration_conflict', file=conf_file),
m18n.n('show_diff', diff=''.join(diff))))
# Remove the backup file if the configuration has not changed
if new_hash == previous_hash:
try:
os.remove(conf_backup_file)
except OSError: pass
services[service]['conffiles'][conf_file] = new_hash
_save_services(services)
return regenerated

View file

@ -43,7 +43,7 @@ from yunohost.app import app_fetchlist, app_info, app_upgrade, app_ssowatconf, a
from yunohost.domain import domain_add, domain_list, get_public_ip from yunohost.domain import domain_add, domain_list, get_public_ip
from yunohost.dyndns import dyndns_subscribe from yunohost.dyndns import dyndns_subscribe
from yunohost.firewall import firewall_upnp, firewall_reload from yunohost.firewall import firewall_upnp, firewall_reload
from yunohost.service import service_status, service_regenconf, service_log from yunohost.service import service_status, service_regen_conf, service_log
from yunohost.monitor import monitor_disk, monitor_network, monitor_system from yunohost.monitor import monitor_disk, monitor_network, monitor_system
from yunohost.utils.packages import ynh_packages_version from yunohost.utils.packages import ynh_packages_version
@ -152,7 +152,7 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False):
try: try:
with open('/etc/yunohost/installed', 'r') as f: with open('/etc/yunohost/installed', 'r') as f:
service_regenconf() service_regen_conf()
except IOError: pass except IOError: pass
logger.success(m18n.n('maindomain_changed')) logger.success(m18n.n('maindomain_changed'))
@ -170,13 +170,10 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
""" """
dyndns = not ignore_dyndns dyndns = not ignore_dyndns
try: # Do some checks at first
with open('/etc/yunohost/installed') as f: pass if os.path.isfile('/etc/yunohost/installed'):
except IOError: raise MoulinetteError(errno.EPERM,
logger.info(m18n.n('yunohost_installing')) m18n.n('yunohost_already_installed'))
else:
raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed'))
if len(domain.split('.')) >= 3 and not ignore_dyndns: if len(domain.split('.')) >= 3 and not ignore_dyndns:
try: try:
r = requests.get('https://dyndns.yunohost.org/domains') r = requests.get('https://dyndns.yunohost.org/domains')
@ -192,6 +189,19 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
raise MoulinetteError(errno.EEXIST, raise MoulinetteError(errno.EEXIST,
m18n.n('dyndns_unavailable')) m18n.n('dyndns_unavailable'))
logger.info(m18n.n('yunohost_installing'))
# Instantiate LDAP Authenticator
auth = init_authenticator(('ldap', 'default'),
{'uri': "ldap://localhost:389",
'base_dn': "dc=yunohost,dc=org",
'user_rdn': "cn=admin" })
auth.authenticate('yunohost')
# Initialize LDAP for YunoHost
# TODO: Improve this part by integrate ldapinit into conf_regen hook
tools_ldapinit(auth)
# Create required folders # Create required folders
folders_to_create = [ folders_to_create = [
'/etc/yunohost/apps', '/etc/yunohost/apps',
@ -230,6 +240,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
os.system('chmod 644 /etc/ssowat/conf.json.persistent') os.system('chmod 644 /etc/ssowat/conf.json.persistent')
# Create SSL CA # Create SSL CA
service_regen_conf(['ssl'], force=True)
ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA' ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA'
command_list = [ command_list = [
'echo "01" > %s/serial' % ssl_dir, 'echo "01" > %s/serial' % ssl_dir,
@ -247,16 +258,6 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
raise MoulinetteError(errno.EPERM, raise MoulinetteError(errno.EPERM,
m18n.n('yunohost_ca_creation_failed')) m18n.n('yunohost_ca_creation_failed'))
# Instantiate LDAP Authenticator
auth = init_authenticator(('ldap', 'default'),
{ 'uri': "ldap://localhost:389",
'base_dn': "dc=yunohost,dc=org",
'user_rdn': "cn=admin" })
auth.authenticate('yunohost')
# Initialize YunoHost LDAP base
tools_ldapinit(auth)
# New domain config # New domain config
tools_maindomain(auth, old_domain='yunohost.org', new_domain=domain, dyndns=dyndns) tools_maindomain(auth, old_domain='yunohost.org', new_domain=domain, dyndns=dyndns)
@ -275,7 +276,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False):
os.system('update-rc.d yunohost-firewall enable') os.system('update-rc.d yunohost-firewall enable')
os.system('service yunohost-firewall start') os.system('service yunohost-firewall start')
service_regenconf(force=True) service_regen_conf(force=True)
logger.success(m18n.n('yunohost_configured')) logger.success(m18n.n('yunohost_configured'))

View file

@ -98,6 +98,7 @@ class Specifier(object):
} }
def __init__(self, spec): def __init__(self, spec):
if isinstance(spec, basestring):
match = self._regex.search(spec) match = self._regex.search(spec)
if not match: if not match:
raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
@ -106,9 +107,13 @@ class Specifier(object):
match.group("relation").strip(), match.group("relation").strip(),
match.group("version").strip(), match.group("version").strip(),
) )
elif isinstance(spec, self.__class__):
self._spec = spec._spec
else:
return NotImplemented
def __repr__(self): def __repr__(self):
return "<Specifier({1!r})>".format(str(self)) return "<Specifier({0!r})>".format(str(self))
def __str__(self): def __str__(self):
return "{0}{1}".format(*self._spec) return "{0}{1}".format(*self._spec)
@ -138,6 +143,12 @@ class Specifier(object):
return self._spec != other._spec return self._spec != other._spec
def __and__(self, other):
return self.intersection(other)
def __or__(self, other):
return self.union(other)
def _get_relation(self, op): def _get_relation(self, op):
return getattr(self, "_compare_{0}".format(self._relations[op])) return getattr(self, "_compare_{0}".format(self._relations[op]))
@ -167,7 +178,79 @@ class Specifier(object):
def __contains__(self, item): def __contains__(self, item):
return self.contains(item) return self.contains(item)
def intersection(self, other):
"""Make the intersection of two specifiers
Return a new `SpecifierSet` with version specifier(s) common to the
specifier and the other.
Example:
>>> Specifier('>= 2.2') & '>> 2.2.1' == '>> 2.2.1'
>>> Specifier('>= 2.2') & '<< 2.3' == '>= 2.2, << 2.3'
"""
if isinstance(other, basestring):
try:
other = self.__class__(other)
except InvalidSpecifier:
return NotImplemented
elif not isinstance(other, self.__class__):
return NotImplemented
# store spec parts for easy access
rel1, v1 = self.relation, self.version
rel2, v2 = other.relation, other.version
result = []
if other == self:
result = [other]
elif rel1 == '=':
result = [self] if v1 in other else None
elif rel2 == '=':
result = [other] if v2 in self else None
elif v1 == v2:
result = [other if rel1[1] == '=' else self]
elif v2 in self or v1 in other:
is_self_greater = version_compare(v1, v2) > 0
if rel1[0] == rel2[0]:
if rel1[0] == '>':
result = [self if is_self_greater else other]
else:
result = [other if is_self_greater else self]
else:
result = [self, other]
return SpecifierSet(result if result is not None else '')
def union(self, other):
"""Make the union of two version specifiers
Return a new `SpecifierSet` with version specifiers from the
specifier and the other.
Example:
>>> Specifier('>= 2.2') | '<< 2.3' == '>= 2.2, << 2.3'
"""
if isinstance(other, basestring):
try:
other = self.__class__(other)
except InvalidSpecifier:
return NotImplemented
elif not isinstance(other, self.__class__):
return NotImplemented
return SpecifierSet([self, other])
def contains(self, item): def contains(self, item):
"""Check if the specifier contains an other
Return whether the item is contained in the version specifier.
Example:
>>> '2.2.1' in Specifier('<< 2.3')
>>> '2.4' not in Specifier('<< 2.3')
"""
return self._get_relation(self.relation)(item, self.version) return self._get_relation(self.relation)(item, self.version)
@ -181,7 +264,9 @@ class SpecifierSet(object):
""" """
def __init__(self, specifiers): def __init__(self, specifiers):
specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] if isinstance(specifiers, basestring):
specifiers = [s.strip() for s in specifiers.split(",")
if s.strip()]
parsed = set() parsed = set()
for specifier in specifiers: for specifier in specifiers:
@ -190,7 +275,7 @@ class SpecifierSet(object):
self._specs = frozenset(parsed) self._specs = frozenset(parsed)
def __repr__(self): def __repr__(self):
return "<SpecifierSet({1!r})>".format(str(self)) return "<SpecifierSet({0!r})>".format(str(self))
def __str__(self): def __str__(self):
return ",".join(sorted(str(s) for s in self._specs)) return ",".join(sorted(str(s) for s in self._specs))
@ -199,14 +284,10 @@ class SpecifierSet(object):
return hash(self._specs) return hash(self._specs)
def __and__(self, other): def __and__(self, other):
if isinstance(other, basestring): return self.intersection(other)
other = SpecifierSet(other)
elif not isinstance(other, SpecifierSet):
return NotImplemented
specifier = SpecifierSet() def __or__(self, other):
specifier._specs = frozenset(self._specs | other._specs) return self.union(other)
return specifiers
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, basestring): if isinstance(other, basestring):
@ -237,7 +318,69 @@ class SpecifierSet(object):
def __contains__(self, item): def __contains__(self, item):
return self.contains(item) return self.contains(item)
def intersection(self, other):
"""Make the intersection of two specifiers sets
Return a new `SpecifierSet` with version specifier(s) common to the
set and the other.
Example:
>>> SpecifierSet('>= 2.2') & '>> 2.2.1' == '>> 2.2.1'
>>> SpecifierSet('>= 2.2, << 2.4') & '<< 2.3' == '>= 2.2, << 2.3'
>>> SpecifierSet('>= 2.2, << 2.3') & '>= 2.4' == ''
"""
if isinstance(other, basestring):
other = SpecifierSet(other)
elif not isinstance(other, SpecifierSet):
return NotImplemented
specifiers = set(self._specs | other._specs)
intersection = [specifiers.pop()] if specifiers else []
for specifier in specifiers:
parsed = set()
for spec in intersection:
inter = spec & specifier
if not inter:
parsed.clear()
break
# TODO: validate with other specs in parsed
parsed.update(inter._specs)
intersection = parsed
if not intersection:
break
return SpecifierSet(intersection)
def union(self, other):
"""Make the union of two specifiers sets
Return a new `SpecifierSet` with version specifiers from the set
and the other.
Example:
>>> SpecifierSet('>= 2.2') | '<< 2.3' == '>= 2.2, << 2.3'
"""
if isinstance(other, basestring):
other = SpecifierSet(other)
elif not isinstance(other, SpecifierSet):
return NotImplemented
specifiers = SpecifierSet([])
specifiers._specs = frozenset(self._specs | other._specs)
return specifiers
def contains(self, item): def contains(self, item):
"""Check if the set contains a version specifier
Return whether the item is contained in all version specifiers.
Example:
>>> '2.2.1' in SpecifierSet('>= 2.2, << 2.3')
>>> '2.4' not in SpecifierSet('>= 2.2, << 2.3')
"""
return all( return all(
s.contains(item) s.contains(item)
for s in self._specs for s in self._specs