From dea0c165405579d43cfa6d791219767e8b91b0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 2 Apr 2016 11:49:01 +0200 Subject: [PATCH 01/86] [enh] Add a ynh_user_list helper --- data/helpers.d/user | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/data/helpers.d/user b/data/helpers.d/user index bfd044070..5ee6acd68 100644 --- a/data/helpers.d/user +++ b/data/helpers.d/user @@ -20,6 +20,17 @@ ynh_user_get_info() { 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 # # usage: ynh_system_user_exists username From dc3e07c4bd85b312906cfbfd5ee621eaab70d074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 3 Apr 2016 11:28:53 +0200 Subject: [PATCH 02/86] [fix] Check if the package is actually installed in equivs helper --- data/helpers.d/package | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/helpers.d/package b/data/helpers.d/package index dd78692f3..68e0361ed 100644 --- a/data/helpers.d/package +++ b/data/helpers.d/package @@ -69,6 +69,10 @@ ynh_package_install_from_equivs() { -i "./${pkgname}_${pkgversion}_all.deb" 2>&1 \ && sudo apt-get -f -y -qq install) \ && ([[ -n "$TMPDIR" ]] && rm -rf $TMPDIR) + + # check if the package is installed + dpkg-query -W -f='${Status}' "$pkgname" 2>/dev/null \ + | grep 'installed' >/dev/null } # Remove package(s) From e4ec67d72783f4afdb2aab76d3f63987697f62a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 4 Apr 2016 20:18:09 +0200 Subject: [PATCH 03/86] [fix] Improve control file management in equivs helper --- data/helpers.d/package | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/data/helpers.d/package b/data/helpers.d/package index 68e0361ed..8955d5b25 100644 --- a/data/helpers.d/package +++ b/data/helpers.d/package @@ -48,31 +48,33 @@ ynh_package_install() { # usage: ynh_package_install_from_equivs controlfile # | arg: controlfile - path of the equivs control file ynh_package_install_from_equivs() { + controlfile=$1 + + # install equivs package as needed ynh_package_is_installed 'equivs' \ || ynh_package_install equivs # retrieve package information - pkgname=$(grep '^Package: ' $1 | cut -d' ' -f 2) - pkgversion=$(grep '^Version: ' $1 | cut -d' ' -f 2) + pkgname=$(grep '^Package: ' $controlfile | cut -d' ' -f 2) + pkgversion=$(grep '^Version: ' $controlfile | cut -d' ' -f 2) [[ -z "$pkgname" || -z "$pkgversion" ]] \ && echo "Invalid control file" && exit 1 - controlfile=$(readlink -f "$1") # update packages cache ynh_package_update # build and install the package TMPDIR=$(ynh_mkdir_tmp) - (cd $TMPDIR \ - && equivs-build "$controlfile" 1>/dev/null \ + (cp "$controlfile" "${TMPDIR}/control" \ + && cd "$TMPDIR" \ + && equivs-build ./control 1>/dev/null \ && sudo dpkg --force-depends \ -i "./${pkgname}_${pkgversion}_all.deb" 2>&1 \ && sudo apt-get -f -y -qq install) \ && ([[ -n "$TMPDIR" ]] && rm -rf $TMPDIR) - # check if the package is installed - dpkg-query -W -f='${Status}' "$pkgname" 2>/dev/null \ - | grep 'installed' >/dev/null + # check if the package is actually installed + ynh_package_is_installed "$pkgname" } # Remove package(s) From 0339d8160dca07648a1bfc61c4908bd9d94d9b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 4 Apr 2016 20:18:44 +0200 Subject: [PATCH 04/86] [fix] Remove ending comma in backup.py --- src/yunohost/backup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 7c01ddc15..94b15ff42 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -39,7 +39,7 @@ from moulinette.core import MoulinetteError from moulinette.utils import filesystem from moulinette.utils.log import getActionLogger -from yunohost.app import app_info, app_ssowatconf, _is_installed, +from yunohost.app import app_info, app_ssowatconf, _is_installed from yunohost.hook import ( hook_info, hook_callback, hook_exec, custom_hook_folder ) From a948be035a871c6cec100af9576b478522352114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 22:44:31 +0200 Subject: [PATCH 05/86] [i18n] Rename app_requirements_failed err named variable --- locales/en.json | 2 +- src/yunohost/app.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 10c056d9b..06a91abf0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -22,7 +22,7 @@ "custom_app_url_required" : "You must provide an URL to upgrade your custom app {app:s}", "app_requirements_checking" : "Checking required packages...", "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_upgrade_failed" : "Unable to upgrade {app:s}", "app_id_invalid" : "Invalid app id", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 1659aa793..032f7fb4a 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1401,7 +1401,8 @@ def _check_manifest_requirements(manifest): *requirements.keys(), strict=True, as_dict=True) except packages.PackageException as e: raise MoulinetteError(errno.EINVAL, - m18n.n('app_requirements_failed', err=str(e))) + m18n.n('app_requirements_failed', + error=str(e))) # Iterate over requirements for pkgname, spec in requirements.items(): From 82df046e9d5783fdb120a78b9b6f545d5230a2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Wed, 6 Apr 2016 22:03:45 +0200 Subject: [PATCH 06/86] Translated using Weblate (French) Currently translated at 98.2% (224 of 228 strings) --- locales/fr.json | 77 +++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index b483e111a..26daffcc4 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,5 +1,5 @@ { - "action_invalid": "Action « {:s} » incorrecte", + "action_invalid": "Action « {action:s} » incorrecte", "admin_password": "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", @@ -17,8 +17,10 @@ "app_no_upgrade": "Aucune application à mettre à jour", "app_not_correctly_installed": "{app:s} semble être mal 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_removed": "{app:s} supprimé avec succès", + "app_requirements_checking": "Vérification des paquets requis...", "app_sources_fetch_failed": "Impossible de récupérer les fichiers sources", "app_unknown": "Application inconnue", "app_unsupported_remote_type": "Le type distant utilisé par l'application n'est pas supporté", @@ -52,13 +54,18 @@ "backup_hook_unknown": "Script de sauvegarde « {hook:s} » inconnu", "backup_invalid_archive": "Archive de sauvegarde incorrecte", "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_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_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_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}", "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_created": "Domaine créé avec succès", @@ -81,8 +88,9 @@ "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_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_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", "executing_command": "Exécution de la commande « {command:s} »...", "executing_script": "Exécution du script « {script:s} »...", @@ -94,19 +102,19 @@ "format_datetime_short": "%d/%m/%Y %H:%M", "hook_argument_missing": "Argument manquant : '{:s}'", "hook_choice_invalid": "Choix incorrect : '{:s}'", - "hook_exec_failed": "Échec de l'exécution du script", - "hook_exec_not_terminated": "L'exécution du script ne s'est pas terminée", + "hook_exec_failed": "Échec de l'exécution du script « {path:s} »", + "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_name_unknown": "Nom de script « {:s} » inconnu", + "hook_name_unknown": "Nom de script « {name:s} » inconnu", "installation_complete": "Installation terminée", "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.", "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", "license_undefined": "indéfinie", - "mail_alias_remove_failed": "Impossible de supprimer l'adresse mail supplémentaire « {:s} »", - "mail_domain_unknown": "Domaine « {:s} » de l'adresse mail inconnu", - "mail_forward_remove_failed": "Impossible de supprimer l'adresse courriel de transfert « {:s} »", + "mail_alias_remove_failed": "Impossible de supprimer l'adresse mail supplémentaire « {mail:s} »", + "mail_domain_unknown": "Domaine « {domain:s} » de l'adresse mail inconnu", + "mail_forward_remove_failed": "Impossible de supprimer l'adresse courriel de transfert « {mail:s} »", "maindomain_change_failed": "Impossible de modifier le domaine principal", "maindomain_changed": "Domaine principal modifié avec succès", "monitor_disabled": "Le suivi de l'état du serveur a été désactivé avec succès", @@ -131,8 +139,11 @@ "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} n’existe pas, il ne peut pas être copié", "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_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", "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", @@ -147,8 +158,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_positive_number": "Doit être un nombre positif", "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_opened": "Le port {} est déjà ouvert 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 {port:d} est déjà ouvert pour les connexions {ip_version:s}", "port_available": "Le port {port:d} est disponible", "port_unavailable": "Le port {port:d} n'est pas disponible", "restore_action_required": "Vous devez préciser ce qui est à restaurer", @@ -163,25 +174,27 @@ "restore_running_app_script": "Lancement du script de restauration pour l'application « {app:s} »...", "restore_running_hooks": "Exécution des scripts de restauration...", "service_add_configuration": "Ajout du fichier de configuration {file:s}", - "service_add_failed": "Impossible d'ajouter le service « {:s} »", - "service_added": "Service ajouté avec succès", - "service_already_started": "Le service « {:s} » est déjà démarré", - "service_already_stopped": "Le service « {:s} » est déjà arrêté", - "service_cmd_exec_failed": "Impossible d'exécuter la commande « {:s} »", + "service_add_failed": "Impossible d'ajouter le service « {service:s} »", + "service_added": "Service « {service:s} » ajouté avec succès", + "service_already_started": "Le service « {service:s} » est déjà démarré", + "service_already_stopped": "Le service « {service:s} » est déjà arrêté", + "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 l’option --force (ce qui écrasera toutes les modifications effectuées sur le fichier).", - "service_disable_failed": "Impossible de désactiver le service « {:s} »", - "service_disabled": "Service « {:s} » désactivé avec succès", - "service_enable_failed": "Impossible d'activer le service « {:s} »", - "service_enabled": "Service « {:s} » activé avec succès", - "service_no_log": "Aucun journal à afficher pour le service « {:s} »", - "service_remove_failed": "Impossible d'enlever le service « {:s} »", - "service_removed": "Service enlevé avec succès", - "service_start_failed": "Impossible de démarrer le service « {:s} »", - "service_started": "Le service « {:s} » a démarré avec succès", - "service_status_failed": "Impossible de déterminer le statut du service « {:s} »", - "service_stop_failed": "Impossible d'arrêter le service « {:s} »", - "service_stopped": "Service « {:s} » arrêté avec succès", - "service_unknown": "Service « {:s} » inconnu", + "service_configured": "La configuration du service « {service:s} » a été générée avec succès", + "service_configured_all": "La configuration de tous les services a été générée avec succès", + "service_disable_failed": "Impossible de désactiver le service « {service:s} »", + "service_disabled": "Service « {service:s} » désactivé avec succès", + "service_enable_failed": "Impossible d'activer le service « {service:s} »", + "service_enabled": "Service « {service:s} » activé avec succès", + "service_no_log": "Aucun journal à afficher pour le service « {service:s} »", + "service_remove_failed": "Impossible d'enlever le service « {service:s} »", + "service_removed": "Service « {service:s} » enlevé avec succès", + "service_start_failed": "Impossible de démarrer le service « {service:s} »", + "service_started": "Le service « {service:s} » a démarré avec succès", + "service_status_failed": "Impossible de déterminer le statut du service « {service:s} »", + "service_stop_failed": "Impossible d'arrêter le service « {service:s} »", + "service_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", "show_diff": "Voici les différences :\n{diff:s}", "ssowat_conf_generated": "Configuration de SSOwat générée avec succès", @@ -190,7 +203,7 @@ "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", "unexpected_error": "Une erreur inattendue est survenue", - "unit_unknown": "Unité « {:s} » inconnue", + "unit_unknown": "Unité « {unit:s} » inconnue", "unlimit": "Pas de quota", "unrestore_app": "L'application « {app:s} » ne sera pas restaurée", "update_cache_failed": "Impossible de mettre à jour le cache de l'APT", @@ -207,7 +220,7 @@ "user_deletion_failed": "Impossible de supprimer 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_unknown": "Utilisateur inconnu", + "user_unknown": "Utilisateur « {user:s} » inconnu", "user_update_failed": "Impossible de modifier l'utilisateur", "user_updated": "Utilisateur modifié avec succès", "yunohost_already_installed": "YunoHost est déjà installé", From 5ea8488e44dca64adb160cf3bf8e6e7144c6619e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 22:48:18 +0200 Subject: [PATCH 07/86] Translated using Weblate (French) Currently translated at 100.0% (228 of 228 strings) --- locales/fr.json | 462 ++++++++++++++++++++++++------------------------ 1 file changed, 233 insertions(+), 229 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 26daffcc4..f0665d4f5 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,231 +1,235 @@ { - "action_invalid": "Action « {action:s} » incorrecte", - "admin_password": "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", - "app_already_installed": "{app:s} est déjà installé", - "app_argument_choice_invalid": "Choix invalide pour le paramètre « {name:s} », il doit être l'un de {choices:s}", - "app_argument_invalid": "Valeur invalide pour le paramètre « {name:s} » : {error:s}", - "app_argument_missing": "Paramètre manquant « {:s} »", - "app_argument_required": "Le paramètre « {name:s} » est requis", - "app_extraction_failed": "Impossible d'extraire les fichiers d'installation", - "app_id_invalid": "Id d'application incorrect", - "app_install_files_invalid": "Fichiers d'installation incorrects", - "app_location_already_used": "Une application est déjà installée à cet emplacement", - "app_location_install_failed": "Impossible d'installer l'application à cet emplacement", - "app_manifest_invalid": "Manifeste d'application incorrect", - "app_no_upgrade": "Aucune application à mettre à jour", - "app_not_correctly_installed": "{app:s} semble être mal 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_removed": "{app:s} supprimé avec succès", - "app_requirements_checking": "Vérification des paquets requis...", - "app_sources_fetch_failed": "Impossible de récupérer les fichiers sources", - "app_unknown": "Application inconnue", - "app_unsupported_remote_type": "Le type distant utilisé par l'application n'est pas supporté", - "app_upgrade_failed": "Impossible de mettre à jour {app:s}", - "app_upgraded": "{app:s} mis à jour avec succès", - "appslist_fetched": "Liste d'applications récupérée avec succès", - "appslist_removed": "Liste d'applications supprimée avec succès", - "appslist_retrieve_error": "Impossible de récupérer la liste d'applications distante", - "appslist_unknown": "Liste d'applications inconnue", - "ask_current_admin_password": "Mot de passe d'administration actuel", - "ask_email": "Adresse courriel", - "ask_firstname": "Prénom", - "ask_lastname": "Nom", - "ask_list_to_remove": "Liste à supprimer", - "ask_main_domain": "Domaine principal", - "ask_new_admin_password": "Nouveau mot de passe d'administration", - "ask_password": "Mot de passe", - "backup_action_required": "Vous devez préciser ce qui est à sauvegarder", - "backup_app_failed": "Impossible de sauvegarder l'application « {app:s} »", - "backup_archive_app_not_found": "L'application « {app:s} » n'a pas été trouvée dans l'archive de la sauvegarde", - "backup_archive_hook_not_exec": "Le script « {hook:s} » n'a pas été exécuté dans cette sauvegarde", - "backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà", - "backup_archive_name_unknown": "L'archive locale de sauvegarde nommée « {name:s} » est inconnue", - "backup_archive_open_failed": "Impossible d'ouvrir l'archive de sauvegarde", - "backup_cleaning_failed": "Impossible de nettoyer le dossier temporaire de sauvegarde", - "backup_complete": "Sauvegarde terminée", - "backup_creating_archive": "Création de l'archive de sauvegarde...", - "backup_delete_error": "Impossible de supprimer « {path:s} »", - "backup_deleted": "La sauvegarde a bien été supprimée", - "backup_extracting_archive": "Extraction de l'archive de sauvegarde...", - "backup_hook_unknown": "Script de sauvegarde « {hook:s} » inconnu", - "backup_invalid_archive": "Archive de sauvegarde incorrecte", - "backup_nothings_done": "Il n'y a rien à sauvegarder", - "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_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_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_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}", - "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_created": "Domaine créé avec succès", - "domain_creation_failed": "Impossible de créer le domaine", - "domain_deleted": "Domaine supprimé avec succès", - "domain_deletion_failed": "Impossible de supprimer le domaine", - "domain_dyndns_already_subscribed": "Vous avez déjà souscris à un domaine DynDNS", - "domain_dyndns_invalid": "Domaine incorrect pour un usage avec DynDNS", - "domain_dyndns_root_unknown": "Domaine DynDNS principal inconnu", - "domain_exists": "Le domaine existe déjà", - "domain_uninstall_app_first": "Une ou plusieurs applications sont installées sur ce domaine. Veuillez d'abord les désinstaller avant de supprimer ce domaine.", - "domain_unknown": "Domaine inconnu", - "domain_zone_exists": "Le fichier de zone DNS existe déjà", - "domain_zone_not_found": "Fichier de zone DNS introuvable pour le domaine {:s}", - "done": "Terminé.", - "downloading": "Téléchargement...", - "dyndns_cron_installed": "Tâche cron pour DynDNS installée avec succès", - "dyndns_cron_remove_failed": "Impossible d'enlever la tâche cron pour DynDNS", - "dyndns_cron_removed": "La tâche cron pour DynDNS a été enlevée avec succès", - "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_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_registered": "Domaine DynDNS enregistré avec succès", - "dyndns_registration_failed": "Impossible d'enregistrer le domaine DynDNS : {error:s}", - "dyndns_unavailable": "Sous-domaine DynDNS indisponible", - "executing_command": "Exécution de la commande « {command:s} »...", - "executing_script": "Exécution du script « {script:s} »...", - "extracting": "Extraction...", - "field_invalid": "Champ incorrect : « {:s} »", - "firewall_reload_failed": "Impossible de recharger le pare-feu", - "firewall_reloaded": "Pare-feu rechargé avec succès", - "firewall_rules_cmd_failed": "Certaines règles du pare-feu n'ont pas pu être appliquées. Pour plus d'informations, consultez le journal.", - "format_datetime_short": "%d/%m/%Y %H:%M", - "hook_argument_missing": "Argument manquant : '{:s}'", - "hook_choice_invalid": "Choix incorrect : '{:s}'", - "hook_exec_failed": "Échec de l'exécution du script « {path:s} »", - "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_name_unknown": "Nom de script « {name:s} » inconnu", - "installation_complete": "Installation terminée", - "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.", - "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", - "license_undefined": "indéfinie", - "mail_alias_remove_failed": "Impossible de supprimer l'adresse mail supplémentaire « {mail:s} »", - "mail_domain_unknown": "Domaine « {domain:s} » de l'adresse mail inconnu", - "mail_forward_remove_failed": "Impossible de supprimer l'adresse courriel de transfert « {mail:s} »", - "maindomain_change_failed": "Impossible de modifier le domaine principal", - "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_enabled": "Suivi de l'état du serveur activé avec succès", - "monitor_glances_con_failed": "Impossible de se connecter au serveur Glances", - "monitor_not_enabled": "Le suivi de l'état du serveur n'est pas activé", - "monitor_period_invalid": "Période de temps incorrecte", - "monitor_stats_file_not_found": "Le fichier de statistiques est introuvable", - "monitor_stats_no_update": "Aucune donnée de l'état du serveur à mettre à jour", - "monitor_stats_period_unavailable": "Aucune statistique n'est disponible pour la période", - "mountpoint_unknown": "Point de montage inconnu", - "mysql_db_creation_failed": "Impossible de créer la base de données MySQL", - "mysql_db_init_failed": "Impossible d'initialiser la base de données MySQL", - "mysql_db_initialized": "Base de donnée MySQL initialisée avec succès", - "network_check_mx_ko": "L'enregistrement DNS MX n'est pas précisé", - "network_check_smtp_ko": "Le trafic mail sortant (port 25 SMTP) semble bloqué par votre réseau", - "network_check_smtp_ok": "Le trafic courriel sortant (port 25 SMTP) n'est pas bloqué", - "new_domain_required": "Vous devez spécifier le nouveau domaine principal", - "no_appslist_found": "Aucune liste d'applications trouvée", - "no_internet_connection": "Le serveur n'est pas connecté à internet", - "no_ipv6_connectivity": "La connectivité IPv6 n'est pas disponible", - "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} n’existe pas, il ne peut pas être copié", - "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_upgrade_critical_later": "Les paquets critiques ({packages:s}) seront mis à jour ultérieurement", - "packages_upgrade_failed": "Impossible de mettre à jour tous les paquets", - "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_domain": "Doit être un nom de domaine valide (ex : mon-domaine.org)", - "pattern_email": "Doit être une adresse courriel valide (ex. : someone@domain.org)", - "pattern_firstname": "Doit être un prénom valide", - "pattern_lastname": "Doit être un nom valide", - "pattern_listname": "Doit être composé uniquement de caractères alphanumériques et de tirets bas", - "pattern_mailbox_quota": "Doit être une taille avec le suffixe b/k/M/G/T ou 0 pour désactiver le quota", - "pattern_password": "Doit être composé d'au moins 3 caractères", - "pattern_port": "Doit être un numéro de port valide (ex. : 0-65535)", - "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_username": "Doit être composé uniquement de caractères alphanumériques minuscules et de tirets bas", - "port_already_closed": "Le port {port:d} est déjà fermé pour les connexions {ip_version: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_unavailable": "Le port {port:d} n'est pas disponible", - "restore_action_required": "Vous devez préciser ce qui est à restaurer", - "restore_already_installed_app": "Une application est déjà installée avec l'id « {app:s} »", - "restore_app_failed": "Impossible de restaurer l'application « {app:s} »", - "restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration", - "restore_complete": "Restauration terminée", - "restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]", - "restore_failed": "Impossible de restaurer le système", - "restore_hook_unavailable": "Le script de restauration « {hook:s} » n'est pas disponible sur votre système", - "restore_nothings_done": "Rien n'a été restauré", - "restore_running_app_script": "Lancement du script de restauration pour l'application « {app:s} »...", - "restore_running_hooks": "Exécution des scripts de restauration...", - "service_add_configuration": "Ajout du fichier de configuration {file:s}", - "service_add_failed": "Impossible d'ajouter le service « {service:s} »", - "service_added": "Service « {service:s} » ajouté avec succès", - "service_already_started": "Le service « {service:s} » est déjà démarré", - "service_already_stopped": "Le service « {service:s} » est déjà arrêté", - "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 l’option --force (ce qui écrasera toutes les modifications effectuées sur le fichier).", - "service_configured": "La configuration du service « {service:s} » a été générée avec succès", - "service_configured_all": "La configuration de tous les services a été générée avec succès", - "service_disable_failed": "Impossible de désactiver le service « {service:s} »", - "service_disabled": "Service « {service:s} » désactivé avec succès", - "service_enable_failed": "Impossible d'activer le service « {service:s} »", - "service_enabled": "Service « {service:s} » activé avec succès", - "service_no_log": "Aucun journal à afficher pour le service « {service:s} »", - "service_remove_failed": "Impossible d'enlever le service « {service:s} »", - "service_removed": "Service « {service:s} » enlevé avec succès", - "service_start_failed": "Impossible de démarrer le service « {service:s} »", - "service_started": "Le service « {service:s} » a démarré avec succès", - "service_status_failed": "Impossible de déterminer le statut du service « {service:s} »", - "service_stop_failed": "Impossible d'arrêter le service « {service:s} »", - "service_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", - "show_diff": "Voici les différences :\n{diff:s}", - "ssowat_conf_generated": "Configuration de SSOwat générée avec succès", - "ssowat_conf_updated": "La configuration persistante de SSOwat a été mise à jour avec succès", - "system_upgraded": "Système mis à jour avec succès", - "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", - "unexpected_error": "Une erreur inattendue est survenue", - "unit_unknown": "Unité « {unit:s} » inconnue", - "unlimit": "Pas de quota", - "unrestore_app": "L'application « {app:s} » ne sera pas restaurée", - "update_cache_failed": "Impossible de mettre à jour le cache de l'APT", - "updating_apt_cache": "Mise à jour de la liste des paquets disponibles...", - "upgrade_complete": "Mise à jour terminée", - "upgrading_packages": "Mise à jour des paquets...", - "upnp_dev_not_found": "Aucun périphérique compatible UPnP n'a été trouvé", - "upnp_disabled": "UPnP désactivé avec succès", - "upnp_enabled": "UPnP activé avec succès", - "upnp_port_open_failed": "Impossible d'ouvrir les ports avec UPnP", - "user_created": "Utilisateur créé avec succès", - "user_creation_failed": "Impossible de créer l'utilisateur", - "user_deleted": "Utilisateur supprimé avec succès", - "user_deletion_failed": "Impossible de supprimer 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_unknown": "Utilisateur « {user:s} » inconnu", - "user_update_failed": "Impossible de modifier l'utilisateur", - "user_updated": "Utilisateur modifié avec succès", - "yunohost_already_installed": "YunoHost est déjà installé", - "yunohost_ca_creation_failed": "Impossible de créer l'autorité de certification", - "yunohost_configured": "YunoHost configuré avec succès", - "yunohost_installing": "Installation de YunoHost...", + "action_invalid": "Action « {action:s} » incorrecte", + "admin_password": "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", + "app_already_installed": "{app:s} est déjà installé", + "app_argument_choice_invalid": "Choix invalide pour le paramètre « {name:s} », il doit être l'un de {choices:s}", + "app_argument_invalid": "Valeur invalide pour le paramètre « {name:s} » : {error:s}", + "app_argument_missing": "Paramètre manquant « {:s} »", + "app_argument_required": "Le paramètre « {name:s} » est requis", + "app_extraction_failed": "Impossible d'extraire les fichiers d'installation", + "app_id_invalid": "Id d'application incorrect", + "app_install_files_invalid": "Fichiers d'installation incorrects", + "app_location_already_used": "Une application est déjà installée à cet emplacement", + "app_location_install_failed": "Impossible d'installer l'application à cet emplacement", + "app_manifest_invalid": "Manifeste d'application incorrect", + "app_no_upgrade": "Aucune application à mettre à jour", + "app_not_correctly_installed": "{app:s} semble être mal 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_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_unknown": "Application inconnue", + "app_unsupported_remote_type": "Le type distant utilisé par l'application n'est pas supporté", + "app_upgrade_failed": "Impossible de mettre à jour {app:s}", + "app_upgraded": "{app:s} mis à jour avec succès", + "appslist_fetched": "Liste d'applications récupérée avec succès", + "appslist_removed": "Liste d'applications supprimée avec succès", + "appslist_retrieve_error": "Impossible de récupérer la liste d'applications distante", + "appslist_unknown": "Liste d'applications inconnue", + "ask_current_admin_password": "Mot de passe d'administration actuel", + "ask_email": "Adresse courriel", + "ask_firstname": "Prénom", + "ask_lastname": "Nom", + "ask_list_to_remove": "Liste à supprimer", + "ask_main_domain": "Domaine principal", + "ask_new_admin_password": "Nouveau mot de passe d'administration", + "ask_password": "Mot de passe", + "backup_action_required": "Vous devez préciser ce qui est à sauvegarder", + "backup_app_failed": "Impossible de sauvegarder l'application « {app:s} »", + "backup_archive_app_not_found": "L'application « {app:s} » n'a pas été trouvée dans l'archive de la sauvegarde", + "backup_archive_hook_not_exec": "Le script « {hook:s} » n'a pas été exécuté dans cette sauvegarde", + "backup_archive_name_exists": "Une archive de sauvegarde avec ce nom existe déjà", + "backup_archive_name_unknown": "L'archive locale de sauvegarde nommée « {name:s} » est inconnue", + "backup_archive_open_failed": "Impossible d'ouvrir l'archive de sauvegarde", + "backup_cleaning_failed": "Impossible de nettoyer le dossier temporaire de sauvegarde", + "backup_complete": "Sauvegarde terminée", + "backup_creating_archive": "Création de l'archive de sauvegarde...", + "backup_delete_error": "Impossible de supprimer « {path:s} »", + "backup_deleted": "La sauvegarde a bien été supprimée", + "backup_extracting_archive": "Extraction de l'archive de sauvegarde...", + "backup_hook_unknown": "Script de sauvegarde « {hook:s} » inconnu", + "backup_invalid_archive": "Archive de sauvegarde incorrecte", + "backup_nothings_done": "Il n'y a rien à sauvegarder", + "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_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_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_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 »", + "domain_cert_gen_failed": "Impossible de générer le certificat", + "domain_created": "Domaine créé avec succès", + "domain_creation_failed": "Impossible de créer le domaine", + "domain_deleted": "Domaine supprimé avec succès", + "domain_deletion_failed": "Impossible de supprimer le domaine", + "domain_dyndns_already_subscribed": "Vous avez déjà souscris à un domaine DynDNS", + "domain_dyndns_invalid": "Domaine incorrect pour un usage avec DynDNS", + "domain_dyndns_root_unknown": "Domaine DynDNS principal inconnu", + "domain_exists": "Le domaine existe déjà", + "domain_uninstall_app_first": "Une ou plusieurs applications sont installées sur ce domaine. Veuillez d'abord les désinstaller avant de supprimer ce domaine.", + "domain_unknown": "Domaine inconnu", + "domain_zone_exists": "Le fichier de zone DNS existe déjà", + "domain_zone_not_found": "Fichier de zone DNS introuvable pour le domaine {:s}", + "done": "Terminé.", + "downloading": "Téléchargement...", + "dyndns_cron_installed": "Tâche cron pour DynDNS installée avec succès", + "dyndns_cron_remove_failed": "Impossible d'enlever la tâche cron pour DynDNS", + "dyndns_cron_removed": "La tâche cron pour DynDNS a été enlevée avec succès", + "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_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_registration_failed": "Impossible d'enregistrer le domaine DynDNS : {error:s}", + "dyndns_unavailable": "Sous-domaine DynDNS indisponible", + "executing_command": "Exécution de la commande « {command:s} »...", + "executing_script": "Exécution du script « {script:s} »...", + "extracting": "Extraction...", + "field_invalid": "Champ incorrect : « {:s} »", + "firewall_reload_failed": "Impossible de recharger le pare-feu", + "firewall_reloaded": "Pare-feu rechargé avec succès", + "firewall_rules_cmd_failed": "Certaines règles du pare-feu n'ont pas pu être appliquées. Pour plus d'informations, consultez le journal.", + "format_datetime_short": "%d/%m/%Y %H:%M", + "hook_argument_missing": "Argument manquant : '{:s}'", + "hook_choice_invalid": "Choix incorrect : '{:s}'", + "hook_exec_failed": "Échec de l'exécution du script « {path:s} »", + "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_name_unknown": "Nom de script « {name:s} » inconnu", + "installation_complete": "Installation terminée", + "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.", + "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", + "license_undefined": "indéfinie", + "mail_alias_remove_failed": "Impossible de supprimer l'adresse mail supplémentaire « {mail:s} »", + "mail_domain_unknown": "Domaine « {domain:s} » de l'adresse mail inconnu", + "mail_forward_remove_failed": "Impossible de supprimer l'adresse courriel de transfert « {mail:s} »", + "maindomain_change_failed": "Impossible de modifier le domaine principal", + "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_enabled": "Suivi de l'état du serveur activé avec succès", + "monitor_glances_con_failed": "Impossible de se connecter au serveur Glances", + "monitor_not_enabled": "Le suivi de l'état du serveur n'est pas activé", + "monitor_period_invalid": "Période de temps incorrecte", + "monitor_stats_file_not_found": "Le fichier de statistiques est introuvable", + "monitor_stats_no_update": "Aucune donnée de l'état du serveur à mettre à jour", + "monitor_stats_period_unavailable": "Aucune statistique n'est disponible pour la période", + "mountpoint_unknown": "Point de montage inconnu", + "mysql_db_creation_failed": "Impossible de créer la base de données MySQL", + "mysql_db_init_failed": "Impossible d'initialiser la base de données MySQL", + "mysql_db_initialized": "Base de donnée MySQL initialisée avec succès", + "network_check_mx_ko": "L'enregistrement DNS MX n'est pas précisé", + "network_check_smtp_ko": "Le trafic mail sortant (port 25 SMTP) semble bloqué par votre réseau", + "network_check_smtp_ok": "Le trafic courriel sortant (port 25 SMTP) n'est pas bloqué", + "new_domain_required": "Vous devez spécifier le nouveau domaine principal", + "no_appslist_found": "Aucune liste d'applications trouvée", + "no_internet_connection": "Le serveur n'est pas connecté à internet", + "no_ipv6_connectivity": "La connectivité IPv6 n'est pas disponible", + "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} n’existe pas, il ne peut pas être copié", + "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_upgrade_critical_later": "Les paquets critiques ({packages:s}) seront mis à jour ultérieurement", + "packages_upgrade_failed": "Impossible de mettre à jour tous les paquets", + "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_domain": "Doit être un nom de domaine valide (ex : mon-domaine.org)", + "pattern_email": "Doit être une adresse courriel valide (ex. : someone@domain.org)", + "pattern_firstname": "Doit être un prénom valide", + "pattern_lastname": "Doit être un nom valide", + "pattern_listname": "Doit être composé uniquement de caractères alphanumériques et de tirets bas", + "pattern_mailbox_quota": "Doit être une taille avec le suffixe b/k/M/G/T ou 0 pour désactiver le quota", + "pattern_password": "Doit être composé d'au moins 3 caractères", + "pattern_port": "Doit être un numéro de port valide (ex. : 0-65535)", + "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_username": "Doit être composé uniquement de caractères alphanumériques minuscules et de tirets bas", + "port_already_closed": "Le port {port:d} est déjà fermé pour les connexions {ip_version: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_unavailable": "Le port {port:d} n'est pas disponible", + "restore_action_required": "Vous devez préciser ce qui est à restaurer", + "restore_already_installed_app": "Une application est déjà installée avec l'id « {app:s} »", + "restore_app_failed": "Impossible de restaurer l'application « {app:s} »", + "restore_cleaning_failed": "Impossible de nettoyer le dossier temporaire de restauration", + "restore_complete": "Restauration terminée", + "restore_confirm_yunohost_installed": "Voulez-vous vraiment restaurer un système déjà installé ? [{answers:s}]", + "restore_failed": "Impossible de restaurer le système", + "restore_hook_unavailable": "Le script de restauration « {hook:s} » n'est pas disponible sur votre système", + "restore_nothings_done": "Rien n'a été restauré", + "restore_running_app_script": "Lancement du script de restauration pour l'application « {app:s} »...", + "restore_running_hooks": "Exécution des scripts de restauration...", + "service_add_configuration": "Ajout du fichier de configuration {file:s}", + "service_add_failed": "Impossible d'ajouter le service « {service:s} »", + "service_added": "Service « {service:s} » ajouté avec succès", + "service_already_started": "Le service « {service:s} » est déjà démarré", + "service_already_stopped": "Le service « {service:s} » est déjà arrêté", + "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 l’option --force (ce qui écrasera toutes les modifications effectuées sur le fichier).", + "service_configured": "La configuration du service « {service:s} » a été générée avec succès", + "service_configured_all": "La configuration de tous les services a été générée avec succès", + "service_disable_failed": "Impossible de désactiver le service « {service:s} »", + "service_disabled": "Service « {service:s} » désactivé avec succès", + "service_enable_failed": "Impossible d'activer le service « {service:s} »", + "service_enabled": "Service « {service:s} » activé avec succès", + "service_no_log": "Aucun journal à afficher pour le service « {service:s} »", + "service_remove_failed": "Impossible d'enlever le service « {service:s} »", + "service_removed": "Service « {service:s} » enlevé avec succès", + "service_start_failed": "Impossible de démarrer le service « {service:s} »", + "service_started": "Le service « {service:s} » a démarré avec succès", + "service_status_failed": "Impossible de déterminer le statut du service « {service:s} »", + "service_stop_failed": "Impossible d'arrêter le service « {service:s} »", + "service_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", + "show_diff": "Voici les différences :\n{diff:s}", + "ssowat_conf_generated": "Configuration de SSOwat générée avec succès", + "ssowat_conf_updated": "La configuration persistante de SSOwat a été mise à jour avec succès", + "system_upgraded": "Système mis à jour avec succès", + "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", + "unexpected_error": "Une erreur inattendue est survenue", + "unit_unknown": "Unité « {unit:s} » inconnue", + "unlimit": "Pas de quota", + "unrestore_app": "L'application « {app:s} » ne sera pas restaurée", + "update_cache_failed": "Impossible de mettre à jour le cache de l'APT", + "updating_apt_cache": "Mise à jour de la liste des paquets disponibles...", + "upgrade_complete": "Mise à jour terminée", + "upgrading_packages": "Mise à jour des paquets...", + "upnp_dev_not_found": "Aucun périphérique compatible UPnP n'a été trouvé", + "upnp_disabled": "UPnP désactivé avec succès", + "upnp_enabled": "UPnP activé avec succès", + "upnp_port_open_failed": "Impossible d'ouvrir les ports avec UPnP", + "user_created": "Utilisateur créé avec succès", + "user_creation_failed": "Impossible de créer l'utilisateur", + "user_deleted": "Utilisateur supprimé avec succès", + "user_deletion_failed": "Impossible de supprimer 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_unknown": "Utilisateur « {user:s} » inconnu", + "user_update_failed": "Impossible de modifier l'utilisateur", + "user_updated": "Utilisateur modifié avec succès", + "yunohost_already_installed": "YunoHost est déjà installé", + "yunohost_ca_creation_failed": "Impossible de créer l'autorité de certification", + "yunohost_configured": "YunoHost configuré avec succès", + "yunohost_installing": "Installation de YunoHost...", "yunohost_not_installed": "YunoHost n'est pas ou pas correctement installé. Veuillez exécuter « yunohost tools postinstall »." -} \ No newline at end of file +} From 84dba5bff2b8363bb5d748170f02d4f1eaae731c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 23:13:20 +0200 Subject: [PATCH 08/86] Translated using Weblate (Spanish) Currently translated at 71.9% (164 of 228 strings) --- locales/es.json | 66 ++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/locales/es.json b/locales/es.json index 7eaf09dd9..c38e496fc 100644 --- a/locales/es.json +++ b/locales/es.json @@ -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_change_failed": "No se pudo cambiar la contraseña", "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_id_invalid": "id de la aplicación inválida ", "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_manifest_invalid": "Manifesto de la aplicación es inválido", "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_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_unknown": "App desconocida", "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_removed": "Lista de aplicaciones se eliminó con éxito", "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_required": "Debe proporcionar un directorio de salida para el 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 ", "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", @@ -66,7 +66,7 @@ "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_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", "executing_script": "Ejecutando script...", "extracting": "Extrayendo...", @@ -77,16 +77,16 @@ "hook_argument_missing": "Falta un parámetro '{:s}'", "hook_choice_invalid": "Selección inválida '{:s}'", "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_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.", "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", "license_undefined": "indefinido", - "mail_alias_remove_failed": "No se pudo quitar el alias de correos '{:s}'", - "mail_domain_unknown": "El dominio de correos '{:s}' es desconocido", - "mail_forward_remove_failed": "No se pudo quitar la reenvía de correos '{:s}'", + "mail_alias_remove_failed": "No se pudo quitar el alias de correos '{mail:s}'", + "mail_domain_unknown": "El dominio de correos '{domain:s}' es desconocido", + "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_changed": "Dominio principal se cambió 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_internet_connection": "El servidor no está conectado al Internet.", "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", "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 -_.", @@ -118,39 +118,39 @@ "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_username": "Debe contener solamente caracteres alfanuméricos o la guion bajo", - "port_already_closed": "El puerto {} ya está cerrado por {:s} connecciones.", - "port_already_opened": "El puerto {} ya está abierto por {:s} connecciones", - "port_available": "El puerto {} está disponible", - "port_unavailable": "El puerto {} no está disponible", + "port_already_closed": "El puerto {port:d} ya está cerrado por {ip_version:s} connecciones", + "port_already_opened": "El puerto {port:d} ya está abierto por {ip_version:s} connecciones", + "port_available": "El puerto {port:d} está disponible", + "port_unavailable": "El puerto {port:d} no está disponible", "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_failed": "No se pudo restaurar el sistema", "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_already_started": "El servicio '{:s}' ya se ha empezado", - "service_already_stopped": "El servicio '{:s}' ya está parado ", - "service_cmd_exec_failed": "No se pudo ejecutar comando '{:s}'", - "service_disable_failed": "No se pudo desactivar el servicio '{:s}'", - "service_disabled": "Servicio '{:s}' desactivado con éxito", - "service_enable_failed": "No se pudo activar el servicio '{:s}'", - "service_enabled": "Servicio '{:s}' activado con éxito", - "service_no_log": "No hay archivo historial del servicio '{:s}' a exhibir", - "service_remove_failed": "No se pudo quitar el servicio '{:s}'", + "service_already_started": "El servicio '{service:s}' ya se ha empezado", + "service_already_stopped": "El servicio '{service:s}' ya está parado", + "service_cmd_exec_failed": "No se pudo ejecutar comando '{command:s}'", + "service_disable_failed": "No se pudo desactivar el servicio '{service:s}'", + "service_disabled": "Servicio '{service:s}' desactivado con éxito", + "service_enable_failed": "No se pudo activar el servicio '{service:s}'", + "service_enabled": "Servicio '{service:s}' activado con éxito", + "service_no_log": "No hay archivo historial del servicio '{service:s}' a exhibir", + "service_remove_failed": "No se pudo quitar el servicio '{service:s}'", "service_removed": "Servicio quitado con éxito", - "service_start_failed": "No se pudo empezar el servicio '{:s}'", - "service_started": "El servicio '{:s}' se empezó con éxito", - "service_status_failed": "No se pudo discernir el estado del servicio '{:s}'", - "service_stop_failed": "No se pudo parar el servicio '{:s}'", - "service_stopped": "Servicio '{:s}' parado con éxito", - "service_unknown": "Servicio desconocido '{:s}'", + "service_start_failed": "No se pudo empezar el servicio '{service:s}'", + "service_started": "El servicio '{service:s}' se empezó con éxito", + "service_status_failed": "No se pudo discernir el estado del servicio '{service:s}'", + "service_stop_failed": "No se pudo parar el servicio '{service:s}'", + "service_stopped": "Servicio '{service:s}' parado con éxito", + "service_unknown": "Servicio desconocido '{service:s}'", "ssowat_conf_generated": "Configuración SSOwat generado con éxito ", "ssowat_conf_updated": "Configuración persistente SSOwat actualizada 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", "unbackup_app": "La App '{:s}' no será guardada", "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", "update_cache_failed": "No se pudo actualizar el cache APT", "updating_apt_cache": "Actualizando la lista de paquetes disponibles...", From 4a1a30970f1679f947903d7370a74fdaf1896755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 22:59:35 +0200 Subject: [PATCH 09/86] Translated using Weblate (Dutch) Currently translated at 45.1% (103 of 228 strings) --- locales/nl.json | 214 ++++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/locales/nl.json b/locales/nl.json index 9da7e9e4a..a9be4310d 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -1,109 +1,109 @@ { - "action_invalid": "Ongeldige actie '{:s}'", - "admin_password": "Administration password", - "admin_password_changed": "Het admin-wachtwoord is gewijzigd", - "app_already_installed": "{:s} is al geïnstalleerd", - "app_argument_invalid": "'{name:s}' bevat geldige waarde: {error:s}", - "app_argument_required": "Het '{name:s}' moet ingevuld worden", - "app_extraction_failed": "Kan installatiebestanden niet uitpakken", - "app_id_invalid": "Ongeldige app-id", - "app_install_files_invalid": "Ongeldige installatiebestanden", - "app_location_already_used": "Er is al een app geïnstalleerd op deze locatie", - "app_location_install_failed": "Kan app niet installeren op deze locatie", - "app_manifest_invalid": "Ongeldig app-manifest", - "app_no_upgrade": "Geen apps op te upgraden", - "app_not_installed": "{:s} is niet geinstalleerd ", - "app_recent_version_required": "{:s} vereist een nieuwere versie van moulinette", - "app_removed": "{:s} succesvol verwijderd", - "app_sources_fetch_failed": "Kan bronbestanden niet ophalen", - "app_unknown": "Onbekende app", - "app_upgrade_failed": "Kan niet alle apps updaten", - "app_upgraded": "{:s} succesvol geüpgrade ", - "appslist_fetched": "App-lijst succesvol aangemaakt.", - "appslist_removed": "App-lijst succesvol verwijderd", - "appslist_unknown": "Onbekende app-lijst", - "ask_current_admin_password": "Huidig administratorwachtwoord", - "ask_email": "Email-adres", - "ask_firstname": "Voornaam", - "ask_lastname": "Achternaam", - "ask_new_admin_password": "Nieuw administratorwachtwoord", - "ask_password": "Wachtwoord", - "backup_archive_name_exists": "Backuparchief bestaat al", - "backup_cleaning_failed": "Kan tijdelijke backup directory niet leeg maken", - "backup_creating_archive": "Backup wordt gestart...", - "backup_invalid_archive": "Ongeldig backup archief", - "backup_output_directory_not_empty": "Doelmap is niet leeg", - "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_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'", - "domain_cert_gen_failed": "Kan certificaat niet genereren", - "domain_created": "Domein succesvol aangemaakt", - "domain_creation_failed": "Kan domein niet aanmaken", - "domain_deleted": "Domein succesvol verwijderd", - "domain_deletion_failed": "Kan domein niet verwijderen", - "domain_dyndns_already_subscribed": "Dit domein is al geregistreed bij DynDNS", - "domain_dyndns_invalid": "Het domein is ongeldig voor DynDNS", - "domain_dyndns_root_unknown": "Onbekend DynDNS root domein", - "domain_exists": "Domein bestaat al", - "domain_uninstall_app_first": "Een of meerdere apps zijn geïnstalleerd op dit domein, verwijder deze voordat u het domein verwijderd.", - "domain_unknown": "Onbekend domein", - "domain_zone_exists": "DNS zone bestand bestaat al", - "domain_zone_not_found": "DNS zone bestand niet gevonden voor domein: {:s}", - "done": "Voltooid.", - "downloading": "Downloaden...", - "dyndns_cron_remove_failed": "De cron-job voor DynDNS kon niet worden verwijderd", - "dyndns_ip_update_failed": "Kan het IP adres niet updaten bij DynDNS", - "dyndns_ip_updated": "IP adres is aangepast bij DynDNS", - "dyndns_key_generating": "DNS sleutel word aangemaakt, wacht een moment...", - "dyndns_unavailable": "DynDNS subdomein is niet beschikbaar", - "executing_script": "Script uitvoeren...", - "extracting": "Uitpakken...", - "installation_complete": "Installatie voltooid", - "installation_failed": "Installatie gefaald", - "ldap_initialized": "LDAP staat klaar voor gebruik", - "license_undefined": "undefined", - "mail_alias_remove_failed": "Kan mail alias niet verwijderen '{:s}'", - "monitor_stats_no_update": "Er zijn geen recente monitoringstatistieken bij te werken", - "mysql_db_creation_failed": "Aanmaken MySQL database gefaald", - "mysql_db_init_failed": "Initialiseren MySQL database gefaald", - "mysql_db_initialized": "MySQL database succesvol geïnitialiseerd", - "network_check_smtp_ko": "Uitgaande mail (SMPT port 25) wordt blijkbaar geblokkeerd door uw het netwerk", - "no_appslist_found": "Geen app-lijsten gevonden", - "no_internet_connection": "Server is niet verbonden met het internet", - "no_ipv6_connectivity": "IPv6-stack is onbeschikbaar", - "path_removal_failed": "Kan pad niet verwijderen {:s}", - "pattern_email": "Moet een geldig emailadres bevatten (bv. abc@example.org)", - "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_password": "Wachtwoord moet tenminste 3 karakters lang zijn", - "port_already_closed": "Poort {} is al gesloten voor {:s} verbindingen", - "port_already_opened": "Poort {} is al open voor {:s} verbindingen", - "port_available": "Poort {} is beschikbaar", - "port_unavailable": "Poort {} is niet beschikbaar", - "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", - "service_add_failed": "Kan service '{:s}' niet toevoegen", - "service_already_started": "Service '{:s}' draait al", - "service_cmd_exec_failed": "Kan '{:s}' niet uitvoeren", - "service_disabled": "Service '{:s}' is uitgeschakeld", - "service_remove_failed": "Kan service '{:s}' niet verwijderen", - "service_removed": "Service werd verwijderd", - "service_stop_failed": "Kan service '{:s}' niet stoppen", - "service_unknown": "De service '{:s}' bestaat niet", - "show_diff": "Let op de volgende verschillen zijn:\n{diff:s}", - "unexpected_error": "Er is een onbekende fout opgetreden", - "unrestore_app": "App '{app:s}' wordt niet teruggezet", - "updating_apt_cache": "Lijst van beschikbare pakketen wordt bijgewerkt", - "upgrade_complete": "Upgrade voltooid", - "upgrading_packages": "Pakketten worden geüpdate...", - "upnp_dev_not_found": "Geen UPnP apparaten gevonden", - "upnp_disabled": "UPnP successvol uitgeschakeld", - "upnp_enabled": "UPnP succesvol ingeschakeld", - "upnp_port_open_failed": "Kan UPnP poorten niet openen", - "user_deleted": "Gebruiker werd verwijderd", - "user_home_creation_failed": "Kan de map voor deze gebruiker niet aanmaken", - "user_unknown": "Gebruikersnaam is onbekend", - "user_update_failed": "Kan gebruiker niet bijwerken", + "action_invalid": "Ongeldige actie '{action:s}'", + "admin_password": "Administration password", + "admin_password_changed": "Het admin-wachtwoord is gewijzigd", + "app_already_installed": "{app:s} is al geïnstalleerd", + "app_argument_invalid": "'{name:s}' bevat geldige waarde: {error:s}", + "app_argument_required": "Het '{name:s}' moet ingevuld worden", + "app_extraction_failed": "Kan installatiebestanden niet uitpakken", + "app_id_invalid": "Ongeldige app-id", + "app_install_files_invalid": "Ongeldige installatiebestanden", + "app_location_already_used": "Er is al een app geïnstalleerd op deze locatie", + "app_location_install_failed": "Kan app niet installeren op deze locatie", + "app_manifest_invalid": "Ongeldig app-manifest", + "app_no_upgrade": "Geen apps op te upgraden", + "app_not_installed": "{app:s} is niet geinstalleerd", + "app_recent_version_required": "{:s} vereist een nieuwere versie van moulinette", + "app_removed": "{app:s} succesvol verwijderd", + "app_sources_fetch_failed": "Kan bronbestanden niet ophalen", + "app_unknown": "Onbekende app", + "app_upgrade_failed": "Kan niet alle apps updaten", + "app_upgraded": "{app:s} succesvol geüpgrade", + "appslist_fetched": "App-lijst succesvol aangemaakt.", + "appslist_removed": "App-lijst succesvol verwijderd", + "appslist_unknown": "Onbekende app-lijst", + "ask_current_admin_password": "Huidig administratorwachtwoord", + "ask_email": "Email-adres", + "ask_firstname": "Voornaam", + "ask_lastname": "Achternaam", + "ask_new_admin_password": "Nieuw administratorwachtwoord", + "ask_password": "Wachtwoord", + "backup_archive_name_exists": "Backuparchief bestaat al", + "backup_cleaning_failed": "Kan tijdelijke backup directory niet leeg maken", + "backup_creating_archive": "Backup wordt gestart...", + "backup_invalid_archive": "Ongeldig backup archief", + "backup_output_directory_not_empty": "Doelmap is niet leeg", + "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 {app:s} bij te werken", + "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'", + "domain_cert_gen_failed": "Kan certificaat niet genereren", + "domain_created": "Domein succesvol aangemaakt", + "domain_creation_failed": "Kan domein niet aanmaken", + "domain_deleted": "Domein succesvol verwijderd", + "domain_deletion_failed": "Kan domein niet verwijderen", + "domain_dyndns_already_subscribed": "Dit domein is al geregistreed bij DynDNS", + "domain_dyndns_invalid": "Het domein is ongeldig voor DynDNS", + "domain_dyndns_root_unknown": "Onbekend DynDNS root domein", + "domain_exists": "Domein bestaat al", + "domain_uninstall_app_first": "Een of meerdere apps zijn geïnstalleerd op dit domein, verwijder deze voordat u het domein verwijderd.", + "domain_unknown": "Onbekend domein", + "domain_zone_exists": "DNS zone bestand bestaat al", + "domain_zone_not_found": "DNS zone bestand niet gevonden voor domein: {:s}", + "done": "Voltooid.", + "downloading": "Downloaden...", + "dyndns_cron_remove_failed": "De cron-job voor DynDNS kon niet worden verwijderd", + "dyndns_ip_update_failed": "Kan het IP adres niet updaten bij DynDNS", + "dyndns_ip_updated": "IP adres is aangepast bij DynDNS", + "dyndns_key_generating": "DNS sleutel word aangemaakt, wacht een moment...", + "dyndns_unavailable": "DynDNS subdomein is niet beschikbaar", + "executing_script": "Script uitvoeren...", + "extracting": "Uitpakken...", + "installation_complete": "Installatie voltooid", + "installation_failed": "Installatie gefaald", + "ldap_initialized": "LDAP staat klaar voor gebruik", + "license_undefined": "undefined", + "mail_alias_remove_failed": "Kan mail alias niet verwijderen '{mail:s}'", + "monitor_stats_no_update": "Er zijn geen recente monitoringstatistieken bij te werken", + "mysql_db_creation_failed": "Aanmaken MySQL database gefaald", + "mysql_db_init_failed": "Initialiseren MySQL database gefaald", + "mysql_db_initialized": "MySQL database succesvol geïnitialiseerd", + "network_check_smtp_ko": "Uitgaande mail (SMPT port 25) wordt blijkbaar geblokkeerd door uw het netwerk", + "no_appslist_found": "Geen app-lijsten gevonden", + "no_internet_connection": "Server is niet verbonden met het internet", + "no_ipv6_connectivity": "IPv6-stack is onbeschikbaar", + "path_removal_failed": "Kan pad niet verwijderen {:s}", + "pattern_email": "Moet een geldig emailadres bevatten (bv. abc@example.org)", + "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_password": "Wachtwoord moet tenminste 3 karakters lang zijn", + "port_already_closed": "Poort {port:d} is al gesloten voor {ip_version:s} verbindingen", + "port_already_opened": "Poort {port:d} is al open voor {ip_version:s} verbindingen", + "port_available": "Poort {port:d} is beschikbaar", + "port_unavailable": "Poort {port:d} is niet beschikbaar", + "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", + "service_add_failed": "Kan service '{service:s}' niet toevoegen", + "service_already_started": "Service '{service:s}' draait al", + "service_cmd_exec_failed": "Kan '{command:s}' niet uitvoeren", + "service_disabled": "Service '{service:s}' is uitgeschakeld", + "service_remove_failed": "Kan service '{service:s}' niet verwijderen", + "service_removed": "Service werd verwijderd", + "service_stop_failed": "Kan service '{service:s}' niet stoppen", + "service_unknown": "De service '{service:s}' bestaat niet", + "show_diff": "Let op de volgende verschillen zijn:\n{diff:s}", + "unexpected_error": "Er is een onbekende fout opgetreden", + "unrestore_app": "App '{app:s}' wordt niet teruggezet", + "updating_apt_cache": "Lijst van beschikbare pakketen wordt bijgewerkt", + "upgrade_complete": "Upgrade voltooid", + "upgrading_packages": "Pakketten worden geüpdate...", + "upnp_dev_not_found": "Geen UPnP apparaten gevonden", + "upnp_disabled": "UPnP successvol uitgeschakeld", + "upnp_enabled": "UPnP succesvol ingeschakeld", + "upnp_port_open_failed": "Kan UPnP poorten niet openen", + "user_deleted": "Gebruiker werd verwijderd", + "user_home_creation_failed": "Kan de map voor deze gebruiker niet aanmaken", + "user_unknown": "Gebruikersnaam is onbekend", + "user_update_failed": "Kan gebruiker niet bijwerken", "yunohost_configured": "YunoHost configuratie is OK" -} \ No newline at end of file +} From b5222c6fcb597f3508d196e6f9e84153aea53fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 23:04:10 +0200 Subject: [PATCH 10/86] Translated using Weblate (German) Currently translated at 87.7% (200 of 228 strings) --- locales/de.json | 422 ++++++++++++++++++++++++------------------------ 1 file changed, 211 insertions(+), 211 deletions(-) diff --git a/locales/de.json b/locales/de.json index 519584426..b5a33ba26 100644 --- a/locales/de.json +++ b/locales/de.json @@ -1,213 +1,213 @@ { - "action_invalid": "Ungültige Aktion '{:s}'", - "admin_password": "Verwaltungspasswort", - "admin_password_change_failed": "Passwort kann nicht geändert werden", - "admin_password_changed": "Verwaltungspasswort wurde erfolgreich geändert", - "app_already_installed": "{: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_invalid": "Das Argument '{name:s}' hat einen falschen Wert: {error:s}", - "app_argument_required": "Argument '{name:s}' wird benötigt", - "app_extraction_failed": "Installationsdateien konnten nicht entpackt werden", - "app_id_invalid": "Falsche App ID", - "app_install_files_invalid": "Ungültige Installationsdateien", - "app_location_already_used": "Eine andere App ist bereits an diesem Ort installiert", - "app_location_install_failed": "Die App kann an diesem Ort nicht installiert werden", - "app_manifest_invalid": "Ungültiges App Manifest", - "app_no_upgrade": "Keine Aktualisierungen für Apps verfügbar", - "app_not_installed": "{:s} ist nicht intalliert", - "app_recent_version_required": "Für {:s} benötigt eine aktuellere Version von moulinette", - "app_removed": "{:s} wurde erfolgreich entfernt", - "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden", - "app_unknown": "Unbekannte App", - "app_upgrade_failed": "Apps konnten nicht aktualisiert werden", - "app_upgraded": "{:s} wurde erfolgreich aktualisiert", - "appslist_fetched": "Liste der Apps wurde erfolgreich heruntergelanden", - "appslist_removed": "Appliste erfolgreich entfernt", - "appslist_retrieve_error": "Entfernte App Liste kann nicht gezogen werden", - "appslist_unknown": "Unbekannte App Liste", - "ask_current_admin_password": "Derzeitiges Verwaltungspasswort", - "ask_email": "E-Mail Adresse", - "ask_firstname": "Vorname", - "ask_lastname": "Nachname", - "ask_list_to_remove": "Liste enternen", - "ask_main_domain": "Hauptdomain", - "ask_new_admin_password": "Neues Verwaltungskennwort", - "ask_password": "Passwort", - "backup_action_required": "Du musst etwas zum Speichern auswählen", - "backup_app_failed": "Konnte keine Sicherung für '{app:s}' erstellen", - "backup_archive_app_not_found": "App '{app:s}' konnte in keiner Datensicherung gefunden werden", - "backup_archive_hook_not_exec": "Hook '{hook:s}' konnte für diese Datensicherung nicht ausgeführt werden", - "backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits", - "backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden", - "backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen", - "backup_cleaning_failed": "Verzeichnis von temporäre Sicherungsdaten konnte nicht geleert werden", - "backup_complete": "Datensicherung komplett", - "backup_creating_archive": "Datensicherung wird erstellt...", - "backup_delete_error": "Pfad '{path:s}' konnte nicht gelöscht werden", - "backup_deleted": "Datensicherung erfolgreich gelöscht", - "backup_extracting_archive": "Entpacke Sicherungsarchiv...", - "backup_hook_unknown": "Datensicherungshook '{hook:s}' unbekannt", - "backup_invalid_archive": "Ungültige Datensicherung", - "backup_nothings_done": "Es gibt keine Änderungen zur Speicherung", - "backup_output_directory_forbidden": "Verbotenes Ausgabeverzeichnis", - "backup_output_directory_not_empty": "Ausgabeordner ist nicht leer", - "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_hooks": "Datensicherunghook wird ausgeführt...", - "custom_app_url_required": "Es muss eine URL angegeben um deine benutzerdefinierte App {:s} zu aktualisieren", - "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", - "domain_cert_gen_failed": "Zertifikat konnte nicht erzeugt werden", - "domain_created": "Domain erfolgreich erzeugt", - "domain_creation_failed": "Konnte Domain nicht erzeugen", - "domain_deleted": "Domain erfolgreich gelöscht", - "domain_deletion_failed": "Konnte Domain nicht löschen", - "domain_dyndns_already_subscribed": "Du hast dich schon für einen DynDNS-Domain angemeldet", - "domain_dyndns_invalid": "Domain nicht mittels DynDNS nutzbar", - "domain_dyndns_root_unknown": "Unbekannte DynDNS Hauptdomain", - "domain_exists": "Die Domain existiert bereits", - "domain_uninstall_app_first": "Mindestens eine App ist noch für diese Domain installiert. Bitte zuerst die App deinstallieren und erst dann die Domain löschen..", - "domain_unknown": "Unbekannte Domain", - "domain_zone_exists": "DNS Zonen Datei existiert bereits", - "domain_zone_not_found": "DNS Zonen Datei kann nicht für Domäne {:s} gefunden werden", - "done": "Erledigt.", - "downloading": "Wird heruntergeladen...", - "dyndns_cron_installed": "DynDNS Cronjob erfolgreich installiert", - "dyndns_cron_remove_failed": "DynDNS Cronjob konnte nicht entfernt werden", - "dyndns_cron_removed": "DynDNS Cronjob wurde erfolgreich gelöscht", - "dyndns_ip_update_failed": "IP Adresse konnte nicht für DynDNS aktualisiert werden", - "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_registered": "DynDNS Domain erfolgreich registriert", - "dyndns_registration_failed": "DynDNS Domain {:s} konnte nicht registriert werden", - "dyndns_unavailable": "DynDNS Subdomain ist nicht verfügbar", - "executing_command": "Führe Kommendo '{command:s}' aus...", - "executing_script": "Skript '{script:s}' wird ausgeührt...", - "extracting": "Wird entpackt...", - "field_invalid": "Feld '{:s}' ist unbekannt", - "firewall_reload_failed": "Firewall konnte nicht neu geladen werden", - "firewall_reloaded": "Firewall erfolgreich neu geladen", - "firewall_rules_cmd_failed": "Einzelne Firewallregeln konnten nicht übernommen werden. Mehr Informationen sind im Log zu finden.", - "format_datetime_short": "%m/%d/%Y %I:%M %p", - "hook_argument_missing": "Fehlend Argument '{:s}'", - "hook_choice_invalid": "ungültige Wahl '{:s}'", - "hook_exec_failed": "Skriptausführung fehlgeschlagen", - "hook_exec_not_terminated": "Skriptausführung noch nicht beendet", - "hook_list_by_invalid": "Ungültiger Wert zur Anzeige von Hooks", - "hook_name_unknown": "Hook '{:s}' ist nicht bekannt", - "installation_complete": "Installation vollständig", - "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.", - "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", - "license_undefined": "Undeiniert", - "mail_alias_remove_failed": "E-Mail Alias '{:s}' konnte nicht entfernt werden", - "mail_domain_unknown": "Unbekannte Mail Domain '{:s}'", - "mail_forward_remove_failed": "Mailweiterleitung '{:s}' konnte nicht entfernt werden", - "maindomain_change_failed": "Hauptdomain konnte nicht geändert werden", - "maindomain_changed": "Hauptdomain wurde erfolgreich geändert", - "monitor_disabled": "Servermonitoring erfolgreich deaktiviert", - "monitor_enabled": "Servermonitoring erfolgreich aktiviert", - "monitor_glances_con_failed": "Verbindung mit Glances nicht möglich", - "monitor_not_enabled": "Servermonitoring ist nicht aktiviert", - "monitor_period_invalid": "Falscher Zeitraum", - "monitor_stats_file_not_found": "Statistikdatei nicht gefunden", - "monitor_stats_no_update": "Keine Monitoringstatistik zur Aktualisierung", - "monitor_stats_period_unavailable": "Keine Statistiken für den gewählten Zeitraum verfügbar", - "mountpoint_unknown": "Unbekannten Einhängepunkt", - "mysql_db_creation_failed": "MySQL Datenbankerzeugung fehlgeschlagen", - "mysql_db_init_failed": "MySQL Datenbankinitialisierung fehlgeschlagen", - "mysql_db_initialized": "MySQL Datenbank erfolgreich initialisiert", - "network_check_mx_ko": "Es ist kein DNS MX Eintrag vorhanden", - "network_check_smtp_ko": "Ausgehender Mailverkehr (SMTP Port 25) scheint in deinem Netzwerk blockiert zu sein", - "network_check_smtp_ok": "Ausgehender Mailverkehr (SMTP Port 25) ist blockiert", - "new_domain_required": "Du musst eine neue Hauptdomain angeben", - "no_appslist_found": "Keine Appliste gefunden", - "no_internet_connection": "Der Server ist nicht mit dem Internet verbunden", - "no_ipv6_connectivity": "Eine IPv6 Verbindung steht nicht zur Verfügung", - "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", - "packages_no_upgrade": "Es müssen keine Pakete aktualisiert werden", - "packages_upgrade_critical_later": "Wichtiges Paket ({:s}) wird später aktualisiert", - "packages_upgrade_failed": "Es konnten nicht alle Pakete aktualisiert 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_domain": "Muss ein gültiger Domainname sein (z.B. meine-domain.org)", - "pattern_email": "Muss eine gültige E-Mail Adresse sein (z.B. someone@domain.org)", - "pattern_firstname": "Muss ein gültiger Vorname sein", - "pattern_lastname": "Muss ein gültiger Nachname sein", - "pattern_listname": "Kann nur Alphanumerische Zeichen oder Unterstriche enthalten", - "pattern_mailbox_quota": "Muss eine Größe inkl. b/k/M/G/T Suffix, oder 0 zum deaktivieren sein", - "pattern_password": "Muss mindestens drei Zeichen lang sein", - "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_username": "Darf nur aus klein geschriebenen alphanumerischen Zeichen und Unterstrichen bestehen", - "port_already_closed": "Port {} wurde bereits für {:s} Verbindungen geschlossen", - "port_already_opened": "Der Port {} wird bereits von {:s} benutzt", - "port_available": "Port {} ist verfügbar", - "port_unavailable": "Der Port {} ist nicht verfügbar", - "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_app_failed": "App '{app:s}' konnte nicht wiederhergestellt werden", - "restore_cleaning_failed": "Temporäres Wiederherstellungsverzeichnis konnte nicht geleert werden", - "restore_complete": "Wiederherstellung abgeschlossen", - "restore_confirm_yunohost_installed": "Möchtest du die Wiederherstellung wirklich starten? [{answers:s}]", - "restore_failed": "System kann nicht Wiederhergestellt werden", - "restore_hook_unavailable": "Der Wiederherstellungshook '{hook:s}' steht auf deinem System nicht zur Verfügung", - "restore_nothings_done": "Es wurde nicht wiederhergestellt", - "restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...", - "restore_running_hooks": "Wiederherstellung wird gestartet...", - "service_add_configuration": "Füge Konfigurationsdatei {file:s} hinzu", - "service_add_failed": "Dienst '{:s}' kann nicht hinzugefügt werden", - "service_added": "Service erfolgreich hinzugefügt", - "service_already_started": "Der Dienst '{:s}' läutt bereits", - "service_already_stopped": "Dienst '{:s}' wurde bereits gestoppt", - "service_cmd_exec_failed": "Kommando '{: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_disable_failed": "Dienst'{:s}' konnte nicht deaktiviert werden", - "service_disabled": "Der Dienst '{:s}' wurde erfolgreich deaktiviert", - "service_enable_failed": "Dienst '{:s}' konnte nicht aktiviert werden", - "service_enabled": "Dienst '{:s}' erfolgreich aktiviert", - "service_no_log": "Für den Dienst '{:s}' kann kein Log angezeigt werden", - "service_remove_failed": "Dienst '{:s}' konnte nicht entfernt werden", - "service_removed": "Dienst erfolgreich enternt", - "service_start_failed": "Dienst '{:s}' konnte nicht gestartet werden", - "service_started": "der Dienst '{:s}' wurde erfolgreich gestartet", - "service_status_failed": "Status von '{:s}' kann nicht festgestellt werden", - "service_stop_failed": "Dienst '{:s}' kann nicht gestoppt werden", - "service_stopped": "Dienst '{:s}' wurde erfolgreich beendet", - "service_unknown": "Unbekannte Dienst '{:s}'", - "services_configured": "Konfiguration erfolgreich erstellt", - "show_diff": "Es gibt folgende Änderungen:\n{diff:s}", - "ssowat_conf_generated": "Konfiguration von SSOwat erfolgreich", - "ssowat_conf_updated": "Persistente SSOwat Einstellung erfolgreich aktualisiert", - "system_upgraded": "System wurde erfolgreich aktualisiert", - "system_username_exists": "Der Benutzername existiert bereits", - "unbackup_app": "App '{app:s}' konnte nicht gespeichert werden", - "unexpected_error": "Ein unerwarteter Fehler ist aufgetreten", - "unit_unknown": "Unbekannte Einheit '{:s}'", - "unlimit": "Kein Kontingent", - "unrestore_app": "App '{app:s}' kann nicht Wiederhergestellt werden", - "update_cache_failed": "Konnte APT cache nicht aktualisieren", - "updating_apt_cache": "Liste der verfügbaren Pakete wird aktualisiert...", - "upgrade_complete": "Upgrade vollständig", - "upgrading_packages": "Pakete werden aktualisiert...", - "upnp_dev_not_found": "Es konnten keine UPnP Geräte gefunden werden", - "upnp_disabled": "UPnP wurde erfolgreich deaktiviert", - "upnp_enabled": "UPnP wurde aktiviert", - "upnp_port_open_failed": "UPnP Ports konnten nicht geöffnet werden", - "user_created": "Benutzer erfolgreich erstellt", - "user_creation_failed": "Nutzer konnte nicht erstellt werden", - "user_deleted": "Benutzer wurde erfolgreich entfernt", - "user_deletion_failed": "Nutzer konnte nicht gelöscht werden", - "user_home_creation_failed": "Benutzer Home konnte nicht erstellt werden", - "user_info_failed": "Nutzerinformationen können nicht angezeigt werden", - "user_unknown": "Unbekannter Benutzer", - "user_update_failed": "Benutzer kann nicht aktualisiert werden", - "user_updated": "Benutzer wurde erfolgreich aktualisiert", - "yunohost_already_installed": "YunoHost ist bereits installiert", - "yunohost_ca_creation_failed": "Zertifikatsstelle konnte nicht erstellt werden", - "yunohost_configured": "YunoHost wurde erfolgreich konfiguriert", - "yunohost_installing": "YunoHost wird installiert...", + "action_invalid": "Ungültige Aktion '{action:s}'", + "admin_password": "Verwaltungspasswort", + "admin_password_change_failed": "Passwort kann nicht geändert werden", + "admin_password_changed": "Verwaltungspasswort wurde erfolgreich geändert", + "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_invalid": "Das Argument '{name:s}' hat einen falschen Wert: {error:s}", + "app_argument_required": "Argument '{name:s}' wird benötigt", + "app_extraction_failed": "Installationsdateien konnten nicht entpackt werden", + "app_id_invalid": "Falsche App ID", + "app_install_files_invalid": "Ungültige Installationsdateien", + "app_location_already_used": "Eine andere App ist bereits an diesem Ort installiert", + "app_location_install_failed": "Die App kann an diesem Ort nicht installiert werden", + "app_manifest_invalid": "Ungültiges App Manifest", + "app_no_upgrade": "Keine Aktualisierungen für Apps verfügbar", + "app_not_installed": "{app:s} ist nicht intalliert", + "app_recent_version_required": "Für {:s} benötigt eine aktuellere Version von moulinette", + "app_removed": "{app:s} wurde erfolgreich entfernt", + "app_sources_fetch_failed": "Quelldateien konnten nicht abgerufen werden", + "app_unknown": "Unbekannte App", + "app_upgrade_failed": "Apps konnten nicht aktualisiert werden", + "app_upgraded": "{app:s} wurde erfolgreich aktualisiert", + "appslist_fetched": "Liste der Apps wurde erfolgreich heruntergelanden", + "appslist_removed": "Appliste erfolgreich entfernt", + "appslist_retrieve_error": "Entfernte App Liste kann nicht gezogen werden", + "appslist_unknown": "Unbekannte App Liste", + "ask_current_admin_password": "Derzeitiges Verwaltungspasswort", + "ask_email": "E-Mail Adresse", + "ask_firstname": "Vorname", + "ask_lastname": "Nachname", + "ask_list_to_remove": "Liste enternen", + "ask_main_domain": "Hauptdomain", + "ask_new_admin_password": "Neues Verwaltungskennwort", + "ask_password": "Passwort", + "backup_action_required": "Du musst etwas zum Speichern auswählen", + "backup_app_failed": "Konnte keine Sicherung für '{app:s}' erstellen", + "backup_archive_app_not_found": "App '{app:s}' konnte in keiner Datensicherung gefunden werden", + "backup_archive_hook_not_exec": "Hook '{hook:s}' konnte für diese Datensicherung nicht ausgeführt werden", + "backup_archive_name_exists": "Datensicherung mit dem selben Namen existiert bereits", + "backup_archive_name_unknown": "Unbekanntes lokale Datensicherung mit Namen '{name:s}' gefunden", + "backup_archive_open_failed": "Kann Sicherungsarchiv nicht öfnen", + "backup_cleaning_failed": "Verzeichnis von temporäre Sicherungsdaten konnte nicht geleert werden", + "backup_complete": "Datensicherung komplett", + "backup_creating_archive": "Datensicherung wird erstellt...", + "backup_delete_error": "Pfad '{path:s}' konnte nicht gelöscht werden", + "backup_deleted": "Datensicherung erfolgreich gelöscht", + "backup_extracting_archive": "Entpacke Sicherungsarchiv...", + "backup_hook_unknown": "Datensicherungshook '{hook:s}' unbekannt", + "backup_invalid_archive": "Ungültige Datensicherung", + "backup_nothings_done": "Es gibt keine Änderungen zur Speicherung", + "backup_output_directory_forbidden": "Verbotenes Ausgabeverzeichnis", + "backup_output_directory_not_empty": "Ausgabeordner ist nicht leer", + "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_hooks": "Datensicherunghook wird ausgeführt...", + "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", + "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_created": "Domain erfolgreich erzeugt", + "domain_creation_failed": "Konnte Domain nicht erzeugen", + "domain_deleted": "Domain erfolgreich gelöscht", + "domain_deletion_failed": "Konnte Domain nicht löschen", + "domain_dyndns_already_subscribed": "Du hast dich schon für einen DynDNS-Domain angemeldet", + "domain_dyndns_invalid": "Domain nicht mittels DynDNS nutzbar", + "domain_dyndns_root_unknown": "Unbekannte DynDNS Hauptdomain", + "domain_exists": "Die Domain existiert bereits", + "domain_uninstall_app_first": "Mindestens eine App ist noch für diese Domain installiert. Bitte zuerst die App deinstallieren und erst dann die Domain löschen..", + "domain_unknown": "Unbekannte Domain", + "domain_zone_exists": "DNS Zonen Datei existiert bereits", + "domain_zone_not_found": "DNS Zonen Datei kann nicht für Domäne {:s} gefunden werden", + "done": "Erledigt.", + "downloading": "Wird heruntergeladen...", + "dyndns_cron_installed": "DynDNS Cronjob erfolgreich installiert", + "dyndns_cron_remove_failed": "DynDNS Cronjob konnte nicht entfernt werden", + "dyndns_cron_removed": "DynDNS Cronjob wurde erfolgreich gelöscht", + "dyndns_ip_update_failed": "IP Adresse konnte nicht für DynDNS aktualisiert werden", + "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_registered": "DynDNS Domain erfolgreich registriert", + "dyndns_registration_failed": "DynDNS Domain konnte nicht registriert werden: {error:s}", + "dyndns_unavailable": "DynDNS Subdomain ist nicht verfügbar", + "executing_command": "Führe Kommendo '{command:s}' aus...", + "executing_script": "Skript '{script:s}' wird ausgeührt...", + "extracting": "Wird entpackt...", + "field_invalid": "Feld '{:s}' ist unbekannt", + "firewall_reload_failed": "Firewall konnte nicht neu geladen werden", + "firewall_reloaded": "Firewall erfolgreich neu geladen", + "firewall_rules_cmd_failed": "Einzelne Firewallregeln konnten nicht übernommen werden. Mehr Informationen sind im Log zu finden.", + "format_datetime_short": "%m/%d/%Y %I:%M %p", + "hook_argument_missing": "Fehlend Argument '{:s}'", + "hook_choice_invalid": "ungültige Wahl '{:s}'", + "hook_exec_failed": "Skriptausführung fehlgeschlagen", + "hook_exec_not_terminated": "Skriptausführung noch nicht beendet", + "hook_list_by_invalid": "Ungültiger Wert zur Anzeige von Hooks", + "hook_name_unknown": "Hook '{name:s}' ist nicht bekannt", + "installation_complete": "Installation vollständig", + "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.", + "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", + "license_undefined": "Undeiniert", + "mail_alias_remove_failed": "E-Mail Alias '{mail:s}' konnte nicht entfernt werden", + "mail_domain_unknown": "Unbekannte Mail Domain '{domain:s}'", + "mail_forward_remove_failed": "Mailweiterleitung '{mail:s}' konnte nicht entfernt werden", + "maindomain_change_failed": "Hauptdomain konnte nicht geändert werden", + "maindomain_changed": "Hauptdomain wurde erfolgreich geändert", + "monitor_disabled": "Servermonitoring erfolgreich deaktiviert", + "monitor_enabled": "Servermonitoring erfolgreich aktiviert", + "monitor_glances_con_failed": "Verbindung mit Glances nicht möglich", + "monitor_not_enabled": "Servermonitoring ist nicht aktiviert", + "monitor_period_invalid": "Falscher Zeitraum", + "monitor_stats_file_not_found": "Statistikdatei nicht gefunden", + "monitor_stats_no_update": "Keine Monitoringstatistik zur Aktualisierung", + "monitor_stats_period_unavailable": "Keine Statistiken für den gewählten Zeitraum verfügbar", + "mountpoint_unknown": "Unbekannten Einhängepunkt", + "mysql_db_creation_failed": "MySQL Datenbankerzeugung fehlgeschlagen", + "mysql_db_init_failed": "MySQL Datenbankinitialisierung fehlgeschlagen", + "mysql_db_initialized": "MySQL Datenbank erfolgreich initialisiert", + "network_check_mx_ko": "Es ist kein DNS MX Eintrag vorhanden", + "network_check_smtp_ko": "Ausgehender Mailverkehr (SMTP Port 25) scheint in deinem Netzwerk blockiert zu sein", + "network_check_smtp_ok": "Ausgehender Mailverkehr (SMTP Port 25) ist blockiert", + "new_domain_required": "Du musst eine neue Hauptdomain angeben", + "no_appslist_found": "Keine Appliste gefunden", + "no_internet_connection": "Der Server ist nicht mit dem Internet verbunden", + "no_ipv6_connectivity": "Eine IPv6 Verbindung steht nicht zur Verfügung", + "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", + "packages_no_upgrade": "Es müssen keine Pakete aktualisiert werden", + "packages_upgrade_critical_later": "Wichtiges Paket ({packages:s}) wird später aktualisiert", + "packages_upgrade_failed": "Es konnten nicht alle Pakete aktualisiert 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_domain": "Muss ein gültiger Domainname sein (z.B. meine-domain.org)", + "pattern_email": "Muss eine gültige E-Mail Adresse sein (z.B. someone@domain.org)", + "pattern_firstname": "Muss ein gültiger Vorname sein", + "pattern_lastname": "Muss ein gültiger Nachname sein", + "pattern_listname": "Kann nur Alphanumerische Zeichen oder Unterstriche enthalten", + "pattern_mailbox_quota": "Muss eine Größe inkl. b/k/M/G/T Suffix, oder 0 zum deaktivieren sein", + "pattern_password": "Muss mindestens drei Zeichen lang sein", + "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_username": "Darf nur aus klein geschriebenen alphanumerischen Zeichen und Unterstrichen bestehen", + "port_already_closed": "Port {port:d} wurde bereits für {ip_version:s} Verbindungen geschlossen", + "port_already_opened": "Der Port {port:d} wird bereits von {ip_version:s} benutzt", + "port_available": "Port {port:d} ist verfügbar", + "port_unavailable": "Der Port {port:d} ist nicht verfügbar", + "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_app_failed": "App '{app:s}' konnte nicht wiederhergestellt werden", + "restore_cleaning_failed": "Temporäres Wiederherstellungsverzeichnis konnte nicht geleert werden", + "restore_complete": "Wiederherstellung abgeschlossen", + "restore_confirm_yunohost_installed": "Möchtest du die Wiederherstellung wirklich starten? [{answers:s}]", + "restore_failed": "System kann nicht Wiederhergestellt werden", + "restore_hook_unavailable": "Der Wiederherstellungshook '{hook:s}' steht auf deinem System nicht zur Verfügung", + "restore_nothings_done": "Es wurde nicht wiederhergestellt", + "restore_running_app_script": "Wiederherstellung wird ausfeührt für App '{app:s}'...", + "restore_running_hooks": "Wiederherstellung wird gestartet...", + "service_add_configuration": "Füge Konfigurationsdatei {file:s} hinzu", + "service_add_failed": "Dienst '{service:s}' kann nicht hinzugefügt werden", + "service_added": "Service erfolgreich hinzugefügt", + "service_already_started": "Der Dienst '{service:s}' läutt bereits", + "service_already_stopped": "Dienst '{service:s}' wurde bereits gestoppt", + "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_disable_failed": "Dienst '{service:s}' konnte nicht deaktiviert werden", + "service_disabled": "Der Dienst '{service:s}' wurde erfolgreich deaktiviert", + "service_enable_failed": "Dienst '{service:s}' konnte nicht aktiviert werden", + "service_enabled": "Dienst '{service:s}' erfolgreich aktiviert", + "service_no_log": "Für den Dienst '{service:s}' kann kein Log angezeigt werden", + "service_remove_failed": "Dienst '{service:s}' konnte nicht entfernt werden", + "service_removed": "Dienst erfolgreich enternt", + "service_start_failed": "Dienst '{service:s}' konnte nicht gestartet werden", + "service_started": "der Dienst '{service:s}' wurde erfolgreich gestartet", + "service_status_failed": "Status von '{service:s}' kann nicht festgestellt werden", + "service_stop_failed": "Dienst '{service:s}' kann nicht gestoppt werden", + "service_stopped": "Dienst '{service:s}' wurde erfolgreich beendet", + "service_unknown": "Unbekannte Dienst '{service:s}'", + "services_configured": "Konfiguration erfolgreich erstellt", + "show_diff": "Es gibt folgende Änderungen:\n{diff:s}", + "ssowat_conf_generated": "Konfiguration von SSOwat erfolgreich", + "ssowat_conf_updated": "Persistente SSOwat Einstellung erfolgreich aktualisiert", + "system_upgraded": "System wurde erfolgreich aktualisiert", + "system_username_exists": "Der Benutzername existiert bereits", + "unbackup_app": "App '{app:s}' konnte nicht gespeichert werden", + "unexpected_error": "Ein unerwarteter Fehler ist aufgetreten", + "unit_unknown": "Unbekannte Einheit '{unit:s}'", + "unlimit": "Kein Kontingent", + "unrestore_app": "App '{app:s}' kann nicht Wiederhergestellt werden", + "update_cache_failed": "Konnte APT cache nicht aktualisieren", + "updating_apt_cache": "Liste der verfügbaren Pakete wird aktualisiert...", + "upgrade_complete": "Upgrade vollständig", + "upgrading_packages": "Pakete werden aktualisiert...", + "upnp_dev_not_found": "Es konnten keine UPnP Geräte gefunden werden", + "upnp_disabled": "UPnP wurde erfolgreich deaktiviert", + "upnp_enabled": "UPnP wurde aktiviert", + "upnp_port_open_failed": "UPnP Ports konnten nicht geöffnet werden", + "user_created": "Benutzer erfolgreich erstellt", + "user_creation_failed": "Nutzer konnte nicht erstellt werden", + "user_deleted": "Benutzer wurde erfolgreich entfernt", + "user_deletion_failed": "Nutzer konnte nicht gelöscht werden", + "user_home_creation_failed": "Benutzer Home konnte nicht erstellt werden", + "user_info_failed": "Nutzerinformationen können nicht angezeigt werden", + "user_unknown": "Unbekannter Benutzer", + "user_update_failed": "Benutzer kann nicht aktualisiert werden", + "user_updated": "Benutzer wurde erfolgreich aktualisiert", + "yunohost_already_installed": "YunoHost ist bereits installiert", + "yunohost_ca_creation_failed": "Zertifikatsstelle konnte nicht erstellt werden", + "yunohost_configured": "YunoHost wurde erfolgreich konfiguriert", + "yunohost_installing": "YunoHost wird installiert...", "yunohost_not_installed": "Die YunoHost ist unvollständig. Bitte 'yunohost tools postinstall' ausführen." -} \ No newline at end of file +} From 1e8236c1f7352a68bba4ed91d9731d80cdbc134e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 23:05:35 +0200 Subject: [PATCH 11/86] Translated using Weblate (Italian) Currently translated at 12.2% (28 of 228 strings) --- locales/it.json | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/locales/it.json b/locales/it.json index db9898485..14c84e1aa 100644 --- a/locales/it.json +++ b/locales/it.json @@ -1,31 +1,31 @@ { - "app_already_installed": "{:s} è già installato", - "app_extraction_failed": "Impossibile estrarre i file di installazione", - "app_not_installed": "{:s} non è installato", - "app_unknown": "Applicazione sconosciuta", - "ask_email": "Indirizzo email", - "ask_password": "Password", - "backup_archive_name_exists": "Il nome dell'archivio del backup esiste già", - "backup_complete": "Backup completo", - "backup_invalid_archive": "Archivio di backup non valido", - "backup_output_directory_not_empty": "Directory di output non è vuota", - "backup_running_app_script": "Esecuzione script di backup dell'applicazione '{:s}'...", - "domain_created": "Dominio creato con successo", - "domain_dyndns_invalid": "Dominio non valido da utilizzare con DynDNS", - "domain_exists": "Dominio esiste già", - "ldap_initialized": "LDAP inizializzato con successo", - "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", - "port_already_opened": "Port {} è già aperto per {:s} connessioni", - "port_unavailable": "Porta {} non è disponibile", - "service_add_failed": "Impossibile aggiungere servizio '{:s}'", - "service_cmd_exec_failed": "Impossibile eseguire il comando '{:s}'", - "service_disabled": "Servizio '{:s}' disattivato con successo", - "service_remove_failed": "Impossibile rimuovere il servizio '{:s}'", - "service_removed": "Servizio rimosso con successo", - "service_stop_failed": "Impossibile arrestare il servizio '{:s}'", - "system_username_exists": "Nome utente esiste già negli utenti del sistema", - "unrestore_app": "Applicazione '{app:s}' non verrà ripristinato", - "upgrading_packages": "Aggiornamento dei pacchetti...", + "app_already_installed": "{app:s} è già installato", + "app_extraction_failed": "Impossibile estrarre i file di installazione", + "app_not_installed": "{app:s} non è installato", + "app_unknown": "Applicazione sconosciuta", + "ask_email": "Indirizzo email", + "ask_password": "Password", + "backup_archive_name_exists": "Il nome dell'archivio del backup esiste già", + "backup_complete": "Backup completo", + "backup_invalid_archive": "Archivio di backup non valido", + "backup_output_directory_not_empty": "Directory di output non è vuota", + "backup_running_app_script": "Esecuzione script di backup dell'applicazione '{app:s}'...", + "domain_created": "Dominio creato con successo", + "domain_dyndns_invalid": "Dominio non valido da utilizzare con DynDNS", + "domain_exists": "Dominio esiste già", + "ldap_initialized": "LDAP inizializzato con successo", + "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", + "port_already_opened": "Port {port:d} è già aperto per {ip_version:s} connessioni", + "port_unavailable": "Porta {port:d} non è disponibile", + "service_add_failed": "Impossibile aggiungere servizio '{service:s}'", + "service_cmd_exec_failed": "Impossibile eseguire il comando '{command:s}'", + "service_disabled": "Servizio '{service:s}' disattivato con successo", + "service_remove_failed": "Impossibile rimuovere il servizio '{service:s}'", + "service_removed": "Servizio rimosso con successo", + "service_stop_failed": "Impossibile arrestare il servizio '{service:s}'", + "system_username_exists": "Nome utente esiste già negli utenti del sistema", + "unrestore_app": "Applicazione '{app:s}' non verrà ripristinato", + "upgrading_packages": "Aggiornamento dei pacchetti...", "user_deleted": "Utente cancellato con successo" -} \ No newline at end of file +} From ee64e227662555edabdbfe838f180301f886ce1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 8 Apr 2016 23:08:49 +0200 Subject: [PATCH 12/86] Translated using Weblate (Portuguese) Currently translated at 60.5% (138 of 228 strings) --- locales/pt.json | 292 ++++++++++++++++++++++++------------------------ 1 file changed, 146 insertions(+), 146 deletions(-) diff --git a/locales/pt.json b/locales/pt.json index d2fecf2c8..f46cf91ed 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -1,148 +1,148 @@ { - "action_invalid": "Acção Inválida '{:s}'", - "admin_password": "Senha de administração", - "admin_password_change_failed": "Não foi possível alterar a senha", - "admin_password_changed": "Senha de administração alterada com êxito", - "app_already_installed": "{:s} já está instalada", - "app_extraction_failed": "Não foi possível extrair os ficheiros para instalação", - "app_id_invalid": "ID da aplicação invélida", - "app_install_files_invalid": "Ficheiros para instalação corrompidos", - "app_location_already_used": "Já existe uma aplicação instalada 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_no_upgrade": "Não existem aplicações para atualizar", - "app_not_installed": "{:s} não está instalada", - "app_recent_version_required": "{:s} requer uma versão mais recente da moulinette", - "app_removed": "{:s} removida com êxito", - "app_sources_fetch_failed": "Impossível obter os códigos fontes", - "app_unknown": "Aplicação desconhecida", - "app_upgrade_failed": "Unable to upgrade all apps", - "app_upgraded": "{:s} atualizada com êxito", - "appslist_fetched": "Lista de aplicações processada 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_unknown": "Lista de aplicaçoes desconhecida", - "ask_current_admin_password": "Senha de administração atual", - "ask_email": "Correio eletrónico", - "ask_firstname": "Primeiro nome", - "ask_lastname": "Último nome", - "ask_list_to_remove": "Lista para remover", - "ask_main_domain": "Domínio principal", - "ask_new_admin_password": "Senha de administração nova", - "ask_password": "Senha", - "backup_complete": "Backup completo", - "backup_creating_archive": "A criar ficheiro de backup...", - "backup_invalid_archive": "Arquivo de backup inválido", - "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_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_created": "Domínio criado com êxito", - "domain_creation_failed": "Não foi possível criar o domínio", - "domain_deleted": "Domínio removido com êxito", - "domain_deletion_failed": "Não foi possível eliminar o domínio", - "domain_dyndns_already_subscribed": "Já subscreveu um domínio DynDNS", - "domain_dyndns_invalid": "Domínio inválido para ser utilizado com DynDNS", - "domain_dyndns_root_unknown": "Domínio root (administrador) DynDNS desconhecido", - "domain_exists": "O domínio já existe", - "domain_uninstall_app_first": "Existem uma ou mais aplicações instaladas neste domínio. Por favor desinstale-as antes de proceder com a remoção do domínio.", - "domain_unknown": "Domínio desconhecido", - "domain_zone_exists": "Ficheiro para zona DMZ já existe", - "domain_zone_not_found": "Ficheiro para zona DMZ não encontrado no domínio {:s}", - "done": "Concluído.", - "downloading": "Transferência em curso...", - "dyndns_cron_installed": "Gestor de tarefas cron DynDNS instalado com êxito", - "dyndns_cron_remove_failed": "Não foi possível remover o gestor de tarefas cron DynDNS", - "dyndns_cron_removed": "Gestor de tarefas cron DynDNS removido com êxito", - "dyndns_ip_update_failed": "Não foi possível atualizar o endereço IP 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_registered": "Dom+inio DynDNS registado com êxito", - "dyndns_registration_failed": "Não foi possível registar o domínio DynDNS: {:s}", - "dyndns_unavailable": "Subdomínio DynDNS indisponível", - "executing_script": "A executar o script...", - "extracting": "Extração em curso...", - "field_invalid": "Campo inválido '{:s}'", - "firewall_reloaded": "Firewall recarregada com êxito", - "hook_argument_missing": "Argumento em falta '{:s}'", - "hook_choice_invalid": "Escolha inválida '{:s}'", - "installation_complete": "Instalação concluída", - "installation_failed": "A instalação falhou", - "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", - "license_undefined": "indefinido", - "mail_alias_remove_failed": "Não foi possível remover a etiqueta de correio '{:s}'", - "mail_domain_unknown": "Domínio de endereço de correio desconhecido '{:s}'", - "mail_forward_remove_failed": "Não foi possível remover o reencaminhamento de correio '{:s}'", - "maindomain_change_failed": "Incapaz alterar o domínio raiz", - "maindomain_changed": "Domínio raiz alterado com êxito", - "monitor_disabled": "Monitorização do servidor parada com êxito", - "monitor_enabled": "Monitorização do servidor ativada com êxito", - "monitor_glances_con_failed": "Não foi possível ligar ao servidor Glances", - "monitor_not_enabled": "A monitorização do servidor não está ativa", - "monitor_period_invalid": "Período de tempo inválido", - "monitor_stats_file_not_found": "Ficheiro de estatísticas não encontrado", - "monitor_stats_no_update": "Não existem estatísticas de monitorização para atualizar", - "monitor_stats_period_unavailable": "Não existem estatísticas disponíveis para este período", - "mountpoint_unknown": "Ponto de montagem desconhecido", - "mysql_db_creation_failed": "Criação da base de dados MySQL falhou", - "mysql_db_init_failed": "Inicialização da base de dados MySQL falhou", - "mysql_db_initialized": "Base de dados MySQL iniciada com êxito", - "new_domain_required": "Deve escrever um novo domínio principal", - "no_appslist_found": "Não foi encontrada a lista de aplicações", - "no_internet_connection": "O servidor não está ligado à Internet", - "packages_no_upgrade": "Não existem pacotes para atualizar", - "packages_upgrade_critical_later": "Os pacotes críticos ({:s}) serão atualizados depois", - "packages_upgrade_failed": "Não foi possível atualizar todos os pacotes", - "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_email": "Deve ser um endereço de correio válido (p.e. alguem@dominio.org)", - "pattern_firstname": "Deve ser um primeiro nome válido", - "pattern_lastname": "Deve ser um último nome válido", - "pattern_listname": "Apenas são permitidos caracteres alfanuméricos e travessões", - "pattern_password": "Deve ter no mínimo 3 caracteres", - "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", - "restore_confirm_yunohost_installed": "Quer mesmo restaurar um sistema já instalado? [{answers:s}]", - "service_add_failed": "Incapaz adicionar serviço '{:s}'", - "service_added": "Serviço adicionado com êxito", - "service_already_started": "O serviço '{:s}' já está em execussão", - "service_already_stopped": "O serviço '{:s}' já está parado", - "service_cmd_exec_failed": "Incapaz executar o comando '{:s}'", - "service_disable_failed": "Incapaz desativar o serviço '{:s}'", - "service_disabled": "O serviço '{:s}' foi desativado com êxito", - "service_enable_failed": "Incapaz de ativar o serviço '{:s}'", - "service_enabled": "Serviço '{:s}' ativado com êxito", - "service_no_log": "Não existem registos para mostrar do serviço '{:s}'", - "service_remove_failed": "Incapaz de remover o serviço '{:s}'", - "service_removed": "Serviço eliminado com êxito", - "service_start_failed": "Não foi possível iniciar o serviço '{:s}'", - "service_started": "O serviço '{:s} foi iniciado com êxito", - "service_status_failed": "Incapaz determinar o estado do serviço '{:s}'", - "service_stop_failed": "Incapaz parar o serviço '{:s}", - "service_stopped": "O serviço '{:s}' foi parado com êxito", - "service_unknown": "Serviço desconhecido '{:s}'", - "ssowat_conf_generated": "Configuração SSOwat gerada com êxito", - "ssowat_conf_updated": "Configuração persistente SSOwat atualizada com êxito", - "system_upgraded": "Sistema atualizado com êxito", - "system_username_exists": "O utilizador já existe no registo do sistema", - "unexpected_error": "Ocorreu um erro inesperado", - "unit_unknown": "Unidade desconhecida '{:s}'", - "update_cache_failed": "Não foi possível atualizar os cabeçalhos APT", - "updating_apt_cache": "A atualizar a lista de pacotes disponíveis...", - "upgrade_complete": "Atualização completa", - "upgrading_packages": "Atualização de pacotes em curso...", - "user_created": "Utilizador criado com êxito", - "user_creation_failed": "Não foi possível criar o utilizador", - "user_deleted": "Utilizador eliminado com êxito", - "user_deletion_failed": "Incapaz eliminar o utilizador", - "user_info_failed": "Incapaz obter informações sobre o utilizador", - "user_unknown": "Utilizador desconhecido", - "user_update_failed": "Não foi possível atualizar o utilizador", - "user_updated": "Utilizador atualizado com êxito", - "yunohost_already_installed": "AYunoHost já está instalado", - "yunohost_ca_creation_failed": "Incapaz criar o certificado de autoridade", - "yunohost_configured": "YunoHost configurada com êxito", - "yunohost_installing": "A instalar a YunoHost...", + "action_invalid": "Acção Inválida '{action:s}'", + "admin_password": "Senha de administração", + "admin_password_change_failed": "Não foi possível alterar a senha", + "admin_password_changed": "Senha de administração alterada com êxito", + "app_already_installed": "{app:s} já está instalada", + "app_extraction_failed": "Não foi possível extrair os ficheiros para instalação", + "app_id_invalid": "ID da aplicação invélida", + "app_install_files_invalid": "Ficheiros para instalação corrompidos", + "app_location_already_used": "Já existe uma aplicação instalada 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_no_upgrade": "Não existem aplicações para atualizar", + "app_not_installed": "{app:s} não está instalada", + "app_recent_version_required": "{:s} requer uma versão mais recente da moulinette", + "app_removed": "{app:s} removida com êxito", + "app_sources_fetch_failed": "Impossível obter os códigos fontes", + "app_unknown": "Aplicação desconhecida", + "app_upgrade_failed": "Unable to upgrade all apps", + "app_upgraded": "{app:s} atualizada com êxito", + "appslist_fetched": "Lista de aplicações processada 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_unknown": "Lista de aplicaçoes desconhecida", + "ask_current_admin_password": "Senha de administração atual", + "ask_email": "Correio eletrónico", + "ask_firstname": "Primeiro nome", + "ask_lastname": "Último nome", + "ask_list_to_remove": "Lista para remover", + "ask_main_domain": "Domínio principal", + "ask_new_admin_password": "Senha de administração nova", + "ask_password": "Senha", + "backup_complete": "Backup completo", + "backup_creating_archive": "A criar ficheiro de backup...", + "backup_invalid_archive": "Arquivo de backup inválido", + "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 {app:s}", + "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_created": "Domínio criado com êxito", + "domain_creation_failed": "Não foi possível criar o domínio", + "domain_deleted": "Domínio removido com êxito", + "domain_deletion_failed": "Não foi possível eliminar o domínio", + "domain_dyndns_already_subscribed": "Já subscreveu um domínio DynDNS", + "domain_dyndns_invalid": "Domínio inválido para ser utilizado com DynDNS", + "domain_dyndns_root_unknown": "Domínio root (administrador) DynDNS desconhecido", + "domain_exists": "O domínio já existe", + "domain_uninstall_app_first": "Existem uma ou mais aplicações instaladas neste domínio. Por favor desinstale-as antes de proceder com a remoção do domínio.", + "domain_unknown": "Domínio desconhecido", + "domain_zone_exists": "Ficheiro para zona DMZ já existe", + "domain_zone_not_found": "Ficheiro para zona DMZ não encontrado no domínio {:s}", + "done": "Concluído.", + "downloading": "Transferência em curso...", + "dyndns_cron_installed": "Gestor de tarefas cron DynDNS instalado com êxito", + "dyndns_cron_remove_failed": "Não foi possível remover o gestor de tarefas cron DynDNS", + "dyndns_cron_removed": "Gestor de tarefas cron DynDNS removido com êxito", + "dyndns_ip_update_failed": "Não foi possível atualizar o endereço IP 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_registered": "Dom+inio DynDNS registado com êxito", + "dyndns_registration_failed": "Não foi possível registar o domínio DynDNS: {error:s}", + "dyndns_unavailable": "Subdomínio DynDNS indisponível", + "executing_script": "A executar o script...", + "extracting": "Extração em curso...", + "field_invalid": "Campo inválido '{:s}'", + "firewall_reloaded": "Firewall recarregada com êxito", + "hook_argument_missing": "Argumento em falta '{:s}'", + "hook_choice_invalid": "Escolha inválida '{:s}'", + "installation_complete": "Instalação concluída", + "installation_failed": "A instalação falhou", + "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", + "license_undefined": "indefinido", + "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 '{domain: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_changed": "Domínio raiz alterado com êxito", + "monitor_disabled": "Monitorização do servidor parada com êxito", + "monitor_enabled": "Monitorização do servidor ativada com êxito", + "monitor_glances_con_failed": "Não foi possível ligar ao servidor Glances", + "monitor_not_enabled": "A monitorização do servidor não está ativa", + "monitor_period_invalid": "Período de tempo inválido", + "monitor_stats_file_not_found": "Ficheiro de estatísticas não encontrado", + "monitor_stats_no_update": "Não existem estatísticas de monitorização para atualizar", + "monitor_stats_period_unavailable": "Não existem estatísticas disponíveis para este período", + "mountpoint_unknown": "Ponto de montagem desconhecido", + "mysql_db_creation_failed": "Criação da base de dados MySQL falhou", + "mysql_db_init_failed": "Inicialização da base de dados MySQL falhou", + "mysql_db_initialized": "Base de dados MySQL iniciada com êxito", + "new_domain_required": "Deve escrever um novo domínio principal", + "no_appslist_found": "Não foi encontrada a lista de aplicações", + "no_internet_connection": "O servidor não está ligado à Internet", + "packages_no_upgrade": "Não existem pacotes para atualizar", + "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", + "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_email": "Deve ser um endereço de correio válido (p.e. alguem@dominio.org)", + "pattern_firstname": "Deve ser um primeiro nome válido", + "pattern_lastname": "Deve ser um último nome válido", + "pattern_listname": "Apenas são permitidos caracteres alfanuméricos e travessões", + "pattern_password": "Deve ter no mínimo 3 caracteres", + "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", + "restore_confirm_yunohost_installed": "Quer mesmo restaurar um sistema já instalado? [{answers:s}]", + "service_add_failed": "Incapaz adicionar serviço '{service:s}'", + "service_added": "Serviço adicionado com êxito", + "service_already_started": "O serviço '{service:s}' já está em execussão", + "service_already_stopped": "O serviço '{service:s}' já está parado", + "service_cmd_exec_failed": "Incapaz executar o comando '{command:s}'", + "service_disable_failed": "Incapaz desativar o serviço '{service:s}'", + "service_disabled": "O serviço '{service:s}' foi desativado com êxito", + "service_enable_failed": "Incapaz de ativar o serviço '{service:s}'", + "service_enabled": "Serviço '{service:s}' ativado com êxito", + "service_no_log": "Não existem registos para mostrar do serviço '{service:s}'", + "service_remove_failed": "Incapaz de remover o serviço '{service:s}'", + "service_removed": "Serviço eliminado com êxito", + "service_start_failed": "Não foi possível iniciar o serviço '{service:s}'", + "service_started": "O serviço '{service:s}' foi iniciado com êxito", + "service_status_failed": "Incapaz determinar o estado do serviço '{service:s}'", + "service_stop_failed": "Incapaz parar o serviço '{service:s}'", + "service_stopped": "O serviço '{service:s}' foi parado com êxito", + "service_unknown": "Serviço desconhecido '{service:s}'", + "ssowat_conf_generated": "Configuração SSOwat gerada com êxito", + "ssowat_conf_updated": "Configuração persistente SSOwat atualizada com êxito", + "system_upgraded": "Sistema atualizado com êxito", + "system_username_exists": "O utilizador já existe no registo do sistema", + "unexpected_error": "Ocorreu um erro inesperado", + "unit_unknown": "Unidade desconhecida '{unit:s}'", + "update_cache_failed": "Não foi possível atualizar os cabeçalhos APT", + "updating_apt_cache": "A atualizar a lista de pacotes disponíveis...", + "upgrade_complete": "Atualização completa", + "upgrading_packages": "Atualização de pacotes em curso...", + "user_created": "Utilizador criado com êxito", + "user_creation_failed": "Não foi possível criar o utilizador", + "user_deleted": "Utilizador eliminado com êxito", + "user_deletion_failed": "Incapaz eliminar o utilizador", + "user_info_failed": "Incapaz obter informações sobre o utilizador", + "user_unknown": "Utilizador desconhecido", + "user_update_failed": "Não foi possível atualizar o utilizador", + "user_updated": "Utilizador atualizado com êxito", + "yunohost_already_installed": "AYunoHost já está instalado", + "yunohost_ca_creation_failed": "Incapaz criar o certificado de autoridade", + "yunohost_configured": "YunoHost configurada com êxito", + "yunohost_installing": "A instalar a YunoHost...", "yunohost_not_installed": "YunoHost ainda não está corretamente configurado. Por favor execute as 'ferramentas pós-instalação yunohost'." -} \ No newline at end of file +} From cc60c2a08d021fc801d2ea4091182ca3ea1b94b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 9 Apr 2016 12:17:29 +0200 Subject: [PATCH 13/86] Update changelog for 2.3.12 release --- debian/changelog | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/debian/changelog b/debian/changelog index 81124e12b..bf794631b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,35 @@ +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 Sat, 09 Apr 2016 12:13:10 +0200 + moulinette-yunohost (2.2.4) stable; urgency=low [ Jérôme Lebleu ] From 3b3680c2d20f3e8d9df9d9e067148bff826b9d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 9 Apr 2016 17:02:48 +0200 Subject: [PATCH 14/86] [deb] Rely on dh_installinit to restart yunohost-firewall after upgrade --- debian/postinst | 31 ++++++++----------------------- debian/postrm | 1 - debian/prerm | 1 - debian/rules | 12 +++++++----- 4 files changed, 15 insertions(+), 30 deletions(-) diff --git a/debian/postinst b/debian/postinst index 513d9bf35..85aed7217 100644 --- a/debian/postinst +++ b/debian/postinst @@ -28,23 +28,18 @@ do_configure() { restart_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 if deb-systemd-helper --quiet was-enabled yunohost-firewall.service; then deb-systemd-helper enable yunohost-firewall.service >/dev/null || true + else + deb-systemd-helper update-state yunohost-firewall.service >/dev/null || true fi - deb-systemd-helper update-state yunohost-firewall.service >/dev/null || true - if [ -d /run/systemd/system ]; then - 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 defaults >/dev/null + if [ -n "$2" ]; then + invoke-rc.d yunohost-firewall restart >/dev/null || exit $? + fi fi } @@ -71,16 +66,6 @@ case "$1" in ;; 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# exit 0 diff --git a/debian/postrm b/debian/postrm index 2a711b136..2bbdd496b 100644 --- a/debian/postrm +++ b/debian/postrm @@ -3,7 +3,6 @@ set -e if [ "$1" = "purge" ]; then - update-rc.d yunohost-api remove >/dev/null update-rc.d yunohost-firewall remove >/dev/null fi diff --git a/debian/prerm b/debian/prerm index 85f17dde6..35486ea2d 100644 --- a/debian/prerm +++ b/debian/prerm @@ -3,7 +3,6 @@ set -e 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 fi diff --git a/debian/rules b/debian/rules index 8d280ff77..ce03d0e31 100755 --- a/debian/rules +++ b/debian/rules @@ -8,12 +8,14 @@ dh ${@} --with=python2,systemd 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 override_dh_systemd_enable: - dh_systemd_enable --name=yunohost-api - dh_systemd_enable --name=yunohost-firewall --no-enable + dh_systemd_enable --name=yunohost-api \ + yunohost-api.service + dh_systemd_enable --name=yunohost-firewall --no-enable \ + yunohost-firewall.service -override_dh_systemd_start: - dh_systemd_start --restart-after-upgrade yunohost-api.service +#override_dh_systemd_start: +# dh_systemd_start --restart-after-upgrade yunohost-api.service From 2c4253a99f8530b78f364ccea1e3e8dc9ee44ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 9 Apr 2016 17:04:49 +0200 Subject: [PATCH 15/86] [deb] Add Install section to yunohost-firewall.service --- debian/yunohost-firewall.service | 3 +++ 1 file changed, 3 insertions(+) diff --git a/debian/yunohost-firewall.service b/debian/yunohost-firewall.service index f61ca1f64..1dd46f477 100644 --- a/debian/yunohost-firewall.service +++ b/debian/yunohost-firewall.service @@ -9,3 +9,6 @@ ExecStart=/usr/bin/yunohost firewall reload ExecReload=/usr/bin/yunohost firewall reload ExecStop=/usr/bin/yunohost firewall stop RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target From 85d9acdfd004ea02fc01bb5342f2b89dff1323dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 9 Apr 2016 17:22:53 +0200 Subject: [PATCH 16/86] Update changelog for 2.3.12.1 release --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index bf794631b..4b5f37391 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +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 Sat, 09 Apr 2016 17:22:40 +0200 + yunohost (2.3.12) testing; urgency=low [ Jérôme Lebleu ] From 3b70601254cc32ed268da0e8ffeda5ce21cf33c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 9 Apr 2016 17:35:27 +0200 Subject: [PATCH 17/86] [deb] Enable yunohost-firewall on service restart at postinst --- debian/postinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/postinst b/debian/postinst index 85aed7217..34f6cd24e 100644 --- a/debian/postinst +++ b/debian/postinst @@ -36,7 +36,7 @@ restart_yunohost_firewall() { fi if [ -x /etc/init.d/yunohost-firewall ]; then - update-rc.d yunohost-firewall defaults >/dev/null + update-rc.d yunohost-firewall enable >/dev/null if [ -n "$2" ]; then invoke-rc.d yunohost-firewall restart >/dev/null || exit $? fi From 91856cb7703fd49f7255261ee5cc58091b0ba037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 9 Apr 2016 19:35:53 +0200 Subject: [PATCH 18/86] [fix] Import moulinette after dev env check in bin/yunohost{-api,} --- bin/yunohost | 9 +++++---- bin/yunohost-api | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index 3e6f031b5..798b0f6b4 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -5,10 +5,6 @@ import os import sys 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 IN_DEVEL = False @@ -35,6 +31,11 @@ if IN_DEVEL: 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 ----------------------------------- def _die(message, title='Error:'): diff --git a/bin/yunohost-api b/bin/yunohost-api index f1370205e..d2b219f8b 100755 --- a/bin/yunohost-api +++ b/bin/yunohost-api @@ -5,10 +5,6 @@ import os import sys import argparse -import moulinette -from moulinette.actionsmap import ActionsMap -from moulinette.interfaces.cli import colorize - # Either we are in a development environment or not IN_DEVEL = False @@ -39,6 +35,11 @@ if IN_DEVEL: LOG_DIR = os.path.join(basedir, 'log') +import moulinette +from moulinette.actionsmap import ActionsMap +from moulinette.interfaces.cli import colorize + + # Initialization & helpers functions ----------------------------------- def _die(message, title='Error:'): From a7657c1a3952fd944d42e1efe90f5451140c3c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 10 Apr 2016 10:42:30 +0200 Subject: [PATCH 19/86] [ref] Deprecate and rename regenconf action to regen-conf --- bin/yunohost | 5 +++++ data/actionsmap/yunohost.yml | 6 ++++-- debian/control | 2 +- src/yunohost/service.py | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index 798b0f6b4..cb663306c 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -150,6 +150,11 @@ def _init_moulinette(debug=False, verbose=False, quiet=False): 'handlers': [], 'propagate': True, }, + 'moulinette.interface': { + 'level': level, + 'handlers': handlers, + 'propagate': False, + }, }, 'root': { 'level': level, diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index e538bfc47..9105e3daa 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -956,8 +956,8 @@ service: default: 50 type: int - ### service_regenconf() - regenconf: + ### service_regen_conf() + regen-conf: action_help: > Regenerate the configuration file(s) for a service and compare the result with the existing configuration file. @@ -965,6 +965,8 @@ service: api: PUT /services/regenconf configuration: lock: false + deprecated: + - regenconf arguments: -s: full: --service diff --git a/debian/control b/debian/control index 2053e9d60..b25925fc1 100644 --- a/debian/control +++ b/debian/control @@ -10,7 +10,7 @@ Homepage: https://yunohost.org/ Package: yunohost Architecture: all Depends: ${python:Depends}, ${misc:Depends} - , moulinette (>= 2.3.4) + , moulinette (>= 2.3.5) , python-psutil, python-requests, python-dnspython , python-apt, python-miniupnpc , glances diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 7443ffab2..8552a1b1f 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -273,7 +273,7 @@ def service_log(name, number=50): return result -def service_regenconf(service=None, force=False): +def service_regen_conf(service=None, force=False): """ Regenerate the configuration file(s) for a service and compare the result with the existing configuration file. From cf077a50aaa20cf8a3ba85bfafc317bb876b67ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 10 Apr 2016 16:48:46 +0200 Subject: [PATCH 20/86] [enh] Add pre/post script execution callbacks to hook_callback --- data/actionsmap/yunohost.yml | 12 ++++++++++-- src/yunohost/hook.py | 38 ++++++++++++++++++++++++++++-------- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 9105e3daa..89bb0b882 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1380,8 +1380,15 @@ hook: nargs: "*" -a: full: --args - help: Ordered list of arguments to pass to the script + help: Ordered list of arguments to pass to the scripts 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() exec: @@ -1391,7 +1398,8 @@ hook: help: Path of the script to execute -a: full: --args - help: Arguments to pass to the script + help: Ordered list of arguments to pass to the script + nargs: "*" --raise-on-error: help: Raise if the script returns a non-zero exit code action: store_true diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index e6e2e797e..c90a74ca2 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -205,14 +205,22 @@ def hook_list(action, list_by='name', show_info=False): 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 Keyword argument: action -- Action name 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': {} } @@ -258,20 +266,34 @@ def hook_callback(action, hooks=[], args=None): elif not isinstance(args, list): args = [args] + # Validate callbacks + if not callable(pre_callback): + pre_callback = lambda name, priority, path, args: args + if not callable(post_callback): + post_callback = lambda name, priority, path, succeed: None + # Iterate over hooks and execute them for priority in sorted(hooks_dict): for name, info in iter(hooks_dict[priority].items()): state = 'succeed' - filename = '%s-%s' % (priority, name) + path = info['path'] 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: - logger.error(str(e)) 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: - result[state][name].append(info['path']) + result[state][name].append(path) except KeyError: - result[state][name] = [info['path']] + result[state][name] = [path] return result @@ -282,7 +304,7 @@ def hook_exec(path, args=None, raise_on_error=False, no_trace=False, Keyword argument: 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 no_trace -- Do not print each command that will be executed chdir -- The directory from where the script will be executed From a26d921c0cc579cb7f1b7796a19605d3a0caa9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 10 Apr 2016 16:50:39 +0200 Subject: [PATCH 21/86] [fix] Formatting 'args' in hook_callback is useless --- src/yunohost/hook.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index c90a74ca2..00e06d365 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -260,12 +260,6 @@ def hook_callback(action, hooks=[], args=None, no_trace=False, chdir=None, if not hooks_dict: return result - # Format arguments - if args is None: - args = [] - elif not isinstance(args, list): - args = [args] - # Validate callbacks if not callable(pre_callback): pre_callback = lambda name, priority, path, args: args From c09e322d24307d0fafd4166f4235dba5b6841b78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 10 Apr 2016 16:57:09 +0200 Subject: [PATCH 22/86] [fix] Use new service_regen_conf method name --- src/yunohost/domain.py | 18 +++++++++--------- src/yunohost/tools.py | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index c078013c1..ec003a79b 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -78,7 +78,7 @@ def domain_add(auth, domain, dyndns=False): dyndns -- Subscribe to DynDNS """ - from yunohost.service import service_regenconf + from yunohost.service import service_regen_conf from yunohost.hook import hook_callback attr_dict = { 'objectClass' : ['mailDomain', 'top'] } @@ -157,10 +157,10 @@ def domain_add(auth, domain, dyndns=False): try: with open('/etc/yunohost/installed', 'r') as f: - service_regenconf(service='nginx') - service_regenconf(service='metronome') - service_regenconf(service='dnsmasq') - service_regenconf(service='rmilter') + service_regen_conf(service='nginx') + service_regen_conf(service='metronome') + service_regen_conf(service='dnsmasq') + service_regen_conf(service='rmilter') os.system('yunohost app ssowatconf > /dev/null 2>&1') except IOError: pass except: @@ -183,7 +183,7 @@ def domain_remove(auth, domain, force=False): force -- Force the domain removal """ - from yunohost.service import service_regenconf + from yunohost.service import service_regen_conf from yunohost.hook import hook_callback if not force and domain not in domain_list(auth)['domains']: @@ -206,9 +206,9 @@ def domain_remove(auth, domain, force=False): else: raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) - service_regenconf(service='nginx') - service_regenconf(service='metronome') - service_regenconf(service='dnsmasq') + service_regen_conf(service='nginx') + service_regen_conf(service='metronome') + service_regen_conf(service='dnsmasq') os.system('yunohost app ssowatconf > /dev/null 2>&1') hook_callback('post_domain_remove', args=[domain]) diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a17128ed0..3fcd3dd48 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -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.dyndns import dyndns_subscribe 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.utils.packages import ynh_packages_version @@ -152,7 +152,7 @@ def tools_maindomain(auth, old_domain=None, new_domain=None, dyndns=False): try: with open('/etc/yunohost/installed', 'r') as f: - service_regenconf() + service_regen_conf() except IOError: pass logger.success(m18n.n('maindomain_changed')) @@ -275,7 +275,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False): os.system('update-rc.d yunohost-firewall enable') os.system('service yunohost-firewall start') - service_regenconf(force=True) + service_regen_conf(force=True) logger.success(m18n.n('yunohost_configured')) From 3ed502894c3ba5338f1b8f2565adaebc8e463d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 10 Apr 2016 19:20:40 +0200 Subject: [PATCH 23/86] [fix] Skip hidden and temp files in hook_list --- src/yunohost/hook.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yunohost/hook.py b/src/yunohost/hook.py index 00e06d365..3ee11947a 100644 --- a/src/yunohost/hook.py +++ b/src/yunohost/hook.py @@ -176,6 +176,8 @@ def hook_list(action, list_by='name', show_info=False): def _append_folder(d, folder): # Iterate over and add hook from a folder for f in os.listdir(folder + action): + if f[0] == '.' or f[-1] == '~': + continue path = '%s%s/%s' % (folder, action, f) priority, name = _extract_filename_parts(f) _append_hook(d, priority, name, path) From 2309405e980bf85af932eef7519d57a815c82b52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 17:11:41 +0200 Subject: [PATCH 24/86] [enh] Refactor the conf regen for better conflicts handle It rewrites some parts of the conf regen but try to keep as much as possible the same logic - to prevent a too big refactoring. The main change is that configuration file regen is now handle by service_regen_conf directly. Hooks are now called twice with the following arguments respectively: * `"pre" $force $pending_dir`: it's time for the script to generate and put each configuration file to update/remove into `$pending_dir` with the right directory tree. To remove one, just touch an empty file. * `"post" $force`: this second time, the script may restart services, fix permissions, clean, ... Between this two executions, the service_regen_conf will look for any files under the `$pending_dir` and safely process them - either create, update or remove the proper system configuration. --- data/actionsmap/yunohost.yml | 16 +- locales/en.json | 18 +- src/yunohost/service.py | 388 +++++++++++++++++++++-------------- 3 files changed, 256 insertions(+), 166 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 89bb0b882..83a83ad92 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -958,22 +958,22 @@ service: ### service_regen_conf() regen-conf: - action_help: > - Regenerate the configuration file(s) for a service and compare the result - with the existing configuration file. - Prints the differences between files if any. + action_help: Regenerate the configuration file(s) for a service api: PUT /services/regenconf configuration: lock: false deprecated: - regenconf arguments: - -s: - full: --service - help: Regenerate configuration for a specfic service + names: + help: Services name to regenerate configuration of + nargs: "*" + metavar: NAME -f: 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 ### service_safecopy() diff --git a/locales/en.json b/locales/en.json index 06a91abf0..c33c78c73 100644 --- a/locales/en.json +++ b/locales/en.json @@ -128,12 +128,18 @@ "service_status_failed" : "Unable to determine status of 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_configured": "Configuration successfully generated for service '{service:s}'", - "service_configured_all": "Configuration successfully generated for every services", - "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).", - "no_such_conf_file": "Unable to copy the file {file:s}: the file does not exist", - "service_add_configuration": "Adding the configuration file {file:s}", - "show_diff": "Here are the differences:\n{diff:s}", + "service_regenconf_failed" : "Unable to regenerate the configuration for service(s): {services}", + "service_regenconf_pending_applying" : "Applying pending configuration for service '{service}'...", + "service_conf_file_manually_removed" : "The configuration file '{conf}' has been manually removed and will not be created", + "service_conf_file_manually_modified" : "The configuration file '{conf}' has been manually modified and will not be updated", + "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}'", "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", diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 8552a1b1f..6b3ff335c 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -34,16 +34,14 @@ import difflib import hashlib from moulinette.core import MoulinetteError -from moulinette.utils import log +from moulinette.utils import log, filesystem -template_dir = os.getenv( - 'YUNOHOST_TEMPLATE_DIR', - '/usr/share/yunohost/templates' -) -conf_backup_dir = os.getenv( - 'YUNOHOST_CONF_BACKUP_DIR', - '/home/yunohost.backup/conffiles' -) +from yunohost.hook import hook_list, hook_callback + + +base_conf_path = '/home/yunohost.conf' +backup_conf_dir = os.path.join(base_conf_path, 'backup') +pending_conf_dir = os.path.join(base_conf_path, 'pending') logger = log.getActionLogger('yunohost.service') @@ -273,26 +271,148 @@ def service_log(name, number=50): return result -def service_regen_conf(service=None, force=False): +def service_regen_conf(names=[], force=False): """ - Regenerate the configuration file(s) for a service and compare the result - with the existing configuration file. - Prints the differences between files if any. + Regenerate the configuration file(s) for a service Keyword argument: - service -- Regenerate configuration for a specfic service - force -- Override the current configuration with the newly generated - one, even if it has been modified + names -- Services name to regenerate configuration of + force -- Override all manual modifications in configuration files """ - from yunohost.hook import hook_callback + result = {} - if service is not None: - hook_callback('conf_regen', [service], args=[force]) - logger.success(m18n.n('service_configured', service=service)) - else: - hook_callback('conf_regen', args=[force]) - logger.success(m18n.n('service_configured_all')) + # Clean pending conf directory + shutil.rmtree(pending_conf_dir, ignore_errors=True) + filesystem.mkdir(pending_conf_dir, 0755, True) + + # Execute hooks for pre-regen + 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', 1 if force else 0, 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']))) + + # 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', + 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 + + # 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") + continue + if not current_hash or force: + if force: + logger.debug("> system conf has been manually removed") + conf_status = 'force-created' + else: + logger.debug("> system conf does not exist yet") + conf_status = 'created' + regenerated = _process_regen_conf( + 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") + os.remove(pending_path) + conf_status = 'managed' + regenerated = True + elif force and to_remove: + regenerated = _process_regen_conf(system_path) + conf_status = 'force-removed' + elif force: + regenerated = _process_regen_conf(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 = _process_regen_conf(system_path) + conf_status = 'removed' + elif system_hash != new_hash: + regenerated = _process_regen_conf(system_path, pending_path) + conf_status = 'updated' + else: + logger.debug("> system conf is already up-to-date") + continue + else: + logger.debug("> system conf has been manually modified") + if force: + regenerated = _process_regen_conf(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 + # TODO: Append the diff if --with-diff + conf_result = {'status': conf_status} + if regenerated: + succeed_regen[system_path] = conf_result + conf_hashes[system_path] = new_hash + 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 failed_regen: + logger.error(m18n.n('service_regenconf_failed', services=service)) + else: + logger.success(m18n.n('service_conf_updated', service=service)) + if succeed_regen: + _update_conf_hashes(service, conf_hashes) + + # Append the service results + result[service] = { + 'succeed': succeed_regen, + 'failed': failed_regen + } + + # Execute hooks for post-regen + hook_callback('conf_regen', names, args=['post', 1 if force else 0]) + + return result def _run_service_command(action, service): @@ -402,153 +522,117 @@ def _get_diff(string, filename): except IOError: return [] -def _hash(filename): +def _calculate_hash(path): """ - Calculate a MD5 hash of a file + Calculate the MD5 hash of a file Keyword argument: - filename -- The file to hash + path -- The path to the file """ hasher = hashlib.md5() try: - with open(filename, 'rb') as f: - buf = f.read() - hasher.update(buf) - + with open(path, 'rb') as f: + hasher.update(f.read()) return hasher.hexdigest() except IOError: - return 'no hash yet' + return None -def service_saferemove(service, conf_file, force=False): - """ - Check if the specific file has been modified before removing it. - Backup the file in /home/yunohost.backup +def _get_pending_conf(services=[]): + """Get pending configuration for service(s) - Keyword argument: - service -- Service name of the file to delete - conf_file -- The file to write - force -- Force file deletion + Iterate over the pending configuration directory for given service(s) - or + all if empty - and look for files inside. Each file is considered as a + pending configuration file and therefore must be in the same directory + 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 = {} + if not os.path.isdir(pending_conf_dir): + return result + if not 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 + + +def _get_conf_hashes(service): + """Get the registered conf hashes for a service""" + try: + return _get_services()[service]['conffiles'] + 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() - - if not os.path.exists(conf_file): - try: - del services[service]['conffiles'][conf_file] - except KeyError: pass - 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)) - + service_conf = services.get(service, {}) + service_conf['conffiles'] = hashes + services[service] = service_conf _save_services(services) - return deleted +def _process_regen_conf(system_conf, new_conf=None, save=True): + """Regenerate a given system configuration file -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 + 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. """ - 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 + 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.copy2(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 From 96d4eb33968d9b5d847c1cc10da6f42bc90f501e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 17:30:57 +0200 Subject: [PATCH 25/86] [clean] Move conf_regen hooks to prevent issue while updating them --- data/hooks/{conf_regen => conf_regen-old}/01-yunohost | 0 data/hooks/{conf_regen => conf_regen-old}/02-ssl | 0 data/hooks/{conf_regen => conf_regen-old}/03-ssh | 0 data/hooks/{conf_regen => conf_regen-old}/06-slapd | 0 data/hooks/{conf_regen => conf_regen-old}/09-nslcd | 0 data/hooks/{conf_regen => conf_regen-old}/12-metronome | 0 data/hooks/{conf_regen => conf_regen-old}/15-nginx | 0 data/hooks/{conf_regen => conf_regen-old}/19-postfix | 0 data/hooks/{conf_regen => conf_regen-old}/22-email-legacy | 0 data/hooks/{conf_regen => conf_regen-old}/25-dovecot | 0 data/hooks/{conf_regen => conf_regen-old}/28-rmilter | 0 data/hooks/{conf_regen => conf_regen-old}/31-rspamd | 0 data/hooks/{conf_regen => conf_regen-old}/34-mysql | 0 data/hooks/{conf_regen => conf_regen-old}/37-avahi-daemon | 0 data/hooks/{conf_regen => conf_regen-old}/40-glances | 0 data/hooks/{conf_regen => conf_regen-old}/43-dnsmasq | 0 data/hooks/{conf_regen => conf_regen-old}/46-nsswitch | 0 data/hooks/{conf_regen => conf_regen-old}/52-fail2ban | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename data/hooks/{conf_regen => conf_regen-old}/01-yunohost (100%) rename data/hooks/{conf_regen => conf_regen-old}/02-ssl (100%) rename data/hooks/{conf_regen => conf_regen-old}/03-ssh (100%) rename data/hooks/{conf_regen => conf_regen-old}/06-slapd (100%) rename data/hooks/{conf_regen => conf_regen-old}/09-nslcd (100%) rename data/hooks/{conf_regen => conf_regen-old}/12-metronome (100%) rename data/hooks/{conf_regen => conf_regen-old}/15-nginx (100%) rename data/hooks/{conf_regen => conf_regen-old}/19-postfix (100%) rename data/hooks/{conf_regen => conf_regen-old}/22-email-legacy (100%) rename data/hooks/{conf_regen => conf_regen-old}/25-dovecot (100%) rename data/hooks/{conf_regen => conf_regen-old}/28-rmilter (100%) rename data/hooks/{conf_regen => conf_regen-old}/31-rspamd (100%) rename data/hooks/{conf_regen => conf_regen-old}/34-mysql (100%) rename data/hooks/{conf_regen => conf_regen-old}/37-avahi-daemon (100%) rename data/hooks/{conf_regen => conf_regen-old}/40-glances (100%) rename data/hooks/{conf_regen => conf_regen-old}/43-dnsmasq (100%) rename data/hooks/{conf_regen => conf_regen-old}/46-nsswitch (100%) rename data/hooks/{conf_regen => conf_regen-old}/52-fail2ban (100%) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen-old/01-yunohost similarity index 100% rename from data/hooks/conf_regen/01-yunohost rename to data/hooks/conf_regen-old/01-yunohost diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen-old/02-ssl similarity index 100% rename from data/hooks/conf_regen/02-ssl rename to data/hooks/conf_regen-old/02-ssl diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen-old/03-ssh similarity index 100% rename from data/hooks/conf_regen/03-ssh rename to data/hooks/conf_regen-old/03-ssh diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen-old/06-slapd similarity index 100% rename from data/hooks/conf_regen/06-slapd rename to data/hooks/conf_regen-old/06-slapd diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen-old/09-nslcd similarity index 100% rename from data/hooks/conf_regen/09-nslcd rename to data/hooks/conf_regen-old/09-nslcd diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen-old/12-metronome similarity index 100% rename from data/hooks/conf_regen/12-metronome rename to data/hooks/conf_regen-old/12-metronome diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen-old/15-nginx similarity index 100% rename from data/hooks/conf_regen/15-nginx rename to data/hooks/conf_regen-old/15-nginx diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen-old/19-postfix similarity index 100% rename from data/hooks/conf_regen/19-postfix rename to data/hooks/conf_regen-old/19-postfix diff --git a/data/hooks/conf_regen/22-email-legacy b/data/hooks/conf_regen-old/22-email-legacy similarity index 100% rename from data/hooks/conf_regen/22-email-legacy rename to data/hooks/conf_regen-old/22-email-legacy diff --git a/data/hooks/conf_regen/25-dovecot b/data/hooks/conf_regen-old/25-dovecot similarity index 100% rename from data/hooks/conf_regen/25-dovecot rename to data/hooks/conf_regen-old/25-dovecot diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen-old/28-rmilter similarity index 100% rename from data/hooks/conf_regen/28-rmilter rename to data/hooks/conf_regen-old/28-rmilter diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen-old/31-rspamd similarity index 100% rename from data/hooks/conf_regen/31-rspamd rename to data/hooks/conf_regen-old/31-rspamd diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen-old/34-mysql similarity index 100% rename from data/hooks/conf_regen/34-mysql rename to data/hooks/conf_regen-old/34-mysql diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen-old/37-avahi-daemon similarity index 100% rename from data/hooks/conf_regen/37-avahi-daemon rename to data/hooks/conf_regen-old/37-avahi-daemon diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen-old/40-glances similarity index 100% rename from data/hooks/conf_regen/40-glances rename to data/hooks/conf_regen-old/40-glances diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen-old/43-dnsmasq similarity index 100% rename from data/hooks/conf_regen/43-dnsmasq rename to data/hooks/conf_regen-old/43-dnsmasq diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen-old/46-nsswitch similarity index 100% rename from data/hooks/conf_regen/46-nsswitch rename to data/hooks/conf_regen-old/46-nsswitch diff --git a/data/hooks/conf_regen/52-fail2ban b/data/hooks/conf_regen-old/52-fail2ban similarity index 100% rename from data/hooks/conf_regen/52-fail2ban rename to data/hooks/conf_regen-old/52-fail2ban From eecf03ffbc2d62f1739386b236cd4279a6886cef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 18:50:45 +0200 Subject: [PATCH 26/86] [enh] Allow to show the diff between conf in service_regen_conf --- data/actionsmap/yunohost.yml | 4 +++ src/yunohost/service.py | 64 ++++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 83a83ad92..faeae2367 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -969,6 +969,10 @@ service: help: Services name to regenerate configuration of nargs: "*" metavar: NAME + -d: + full: --with-diff + help: Show differences in case of configuration changes + action: store_true -f: full: --force help: > diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 6b3ff335c..9c5f2e04f 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -30,8 +30,8 @@ import glob import subprocess import errno import shutil -import difflib import hashlib +from difflib import unified_diff from moulinette.core import MoulinetteError from moulinette.utils import log, filesystem @@ -271,12 +271,13 @@ def service_log(name, number=50): return result -def service_regen_conf(names=[], force=False): +def service_regen_conf(names=[], with_diff=False, force=False): """ Regenerate the configuration file(s) for a service Keyword argument: names -- Services name to regenerate configuration of + with_diff -- Show differences in case of configuration changes force -- Override all manual modifications in configuration files """ @@ -316,6 +317,10 @@ def service_regen_conf(names=[], force=False): 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 @@ -384,8 +389,9 @@ def service_regen_conf(names=[], force=False): conf_status = 'modified' # Store the result - # TODO: Append the diff if --with-diff 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 @@ -396,9 +402,7 @@ def service_regen_conf(names=[], force=False): if not succeed_regen and not failed_regen: logger.info(m18n.n('service_conf_up_to_date', service=service)) continue - elif failed_regen: - logger.error(m18n.n('service_regenconf_failed', services=service)) - else: + elif not failed_regen: logger.success(m18n.n('service_conf_updated', service=service)) if succeed_regen: _update_conf_hashes(service, conf_hashes) @@ -500,36 +504,38 @@ def _tail(file, n, offset=None): except IOError: return [] -def _get_diff(string, filename): - """ - Show differences between a string and a file's content +def _get_files_diff(orig_file, new_file, as_string=False, skip_header=True): + """Compare two files and return the differences - Keyword argument: - string -- The string - filename -- The file to compare with + Read and compare two files. The differences are returned either as a delta + in unified diff format or a formatted string if as_string is True. The + header can also be removed if skip_header is True. """ - try: - with open(filename, 'r') as f: - file_lines = f.readlines() + contents = [[], []] + for i, path in enumerate((orig_file, new_file)): + try: + with open(path, 'r') as f: + contents[i] = f.readlines() + except IOError: + pass - string = string + '\n' - new_lines = string.splitlines(True) - if file_lines: - while '\n' == file_lines[-1]: - del file_lines[-1] - return difflib.unified_diff(file_lines, new_lines) - except IOError: return [] + # Compare files and format output + diff = unified_diff(contents[0], contents[1]) + if skip_header: + for i in range(2): + try: + next(diff) + except: + break + if as_string: + result = ''.join(line for line in diff) + return result.rstrip() + return diff def _calculate_hash(path): - """ - Calculate the MD5 hash of a file - - Keyword argument: - path -- The path to the file - - """ + """Calculate the MD5 hash of a file""" hasher = hashlib.md5() try: with open(path, 'rb') as f: From de040283099b5f84fdd9fe401a7f3304a3f4eab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 18:56:48 +0200 Subject: [PATCH 27/86] [fix] Keep only pending conf in conf regen --- src/yunohost/service.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 9c5f2e04f..96a5f05d8 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -333,6 +333,7 @@ def service_regen_conf(names=[], with_diff=False, force=False): 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: @@ -376,6 +377,7 @@ def service_regen_conf(names=[], with_diff=False, force=False): 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") @@ -621,7 +623,7 @@ def _process_regen_conf(system_conf, new_conf=None, save=True): system_dir = os.path.dirname(system_conf) if not os.path.isdir(system_dir): filesystem.mkdir(system_dir, 0755, True) - shutil.copy2(new_conf, system_conf) + shutil.move(new_conf, system_conf) logger.info(m18n.n('service_conf_file_updated', conf=system_conf)) except: From a06a753fb6f70e9f20a36060a99780524eb7cad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 19:03:48 +0200 Subject: [PATCH 28/86] [enh] Allow to list pending conf only in service_regen_conf --- data/actionsmap/yunohost.yml | 4 ++++ src/yunohost/service.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index faeae2367..eb3597078 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -979,6 +979,10 @@ service: Override all manual modifications in configuration files action: store_true + -p: + full: --list-pending + help: List pending configuration files and exit + action: store_true ### service_safecopy() safecopy: diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 96a5f05d8..23336f9b6 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -271,7 +271,8 @@ def service_log(name, number=50): return result -def service_regen_conf(names=[], with_diff=False, force=False): +def service_regen_conf(names=[], with_diff=False, force=False, + list_pending=False): """ Regenerate the configuration file(s) for a service @@ -279,10 +280,15 @@ def service_regen_conf(names=[], with_diff=False, force=False): names -- Services name to regenerate configuration of with_diff -- Show differences in case of configuration changes force -- Override all manual modifications in configuration files + list_pending -- List pending configuration files and exit """ result = {} + # Just return pending conf + if list_pending: + return _get_pending_conf(names) + # Clean pending conf directory shutil.rmtree(pending_conf_dir, ignore_errors=True) filesystem.mkdir(pending_conf_dir, 0755, True) From 9229210538411b6e87bca5dd09686ad9999a3b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 20:42:21 +0200 Subject: [PATCH 29/86] [enh] Also show diff as needed with --list-pending in service_regen_conf --- src/yunohost/service.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 23336f9b6..7795cd453 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -285,9 +285,18 @@ def service_regen_conf(names=[], with_diff=False, force=False, """ result = {} - # Just return pending conf + # Return the list of pending conf if list_pending: - return _get_pending_conf(names) + 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) From 4090630bd936d7c923685615f4618dea487196de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 21:26:13 +0200 Subject: [PATCH 30/86] [enh] Update yunohost, ssl and ssh conf_regen hooks --- data/hooks/conf_regen-old/01-yunohost | 25 ---------- data/hooks/conf_regen-old/02-ssl | 64 ------------------------ data/hooks/conf_regen-old/03-ssh | 30 ----------- data/hooks/conf_regen/01-yunohost | 40 +++++++++++++++ data/hooks/conf_regen/02-ssl | 71 +++++++++++++++++++++++++++ data/hooks/conf_regen/03-ssh | 38 ++++++++++++++ 6 files changed, 149 insertions(+), 119 deletions(-) delete mode 100644 data/hooks/conf_regen-old/01-yunohost delete mode 100644 data/hooks/conf_regen-old/02-ssl delete mode 100644 data/hooks/conf_regen-old/03-ssh create mode 100644 data/hooks/conf_regen/01-yunohost create mode 100644 data/hooks/conf_regen/02-ssl create mode 100644 data/hooks/conf_regen/03-ssh diff --git a/data/hooks/conf_regen-old/01-yunohost b/data/hooks/conf_regen-old/01-yunohost deleted file mode 100644 index d4a823f2e..000000000 --- a/data/hooks/conf_regen-old/01-yunohost +++ /dev/null @@ -1,25 +0,0 @@ -set -e - -force=$1 - -cd /usr/share/yunohost/templates/yunohost - -sudo mkdir -p /etc/yunohost - -if [ ! -f /etc/yunohost/current_host ]; then - echo "yunohost.org" | sudo tee /etc/yunohost/current_host -fi - -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 -fi - -# Allow users to access /media directory -if [ ! -d /etc/skel/media ]; then - mkdir -p /media - ln -s /media /etc/skel/ -fi diff --git a/data/hooks/conf_regen-old/02-ssl b/data/hooks/conf_regen-old/02-ssl deleted file mode 100644 index baa7f13b7..000000000 --- a/data/hooks/conf_regen-old/02-ssl +++ /dev/null @@ -1,64 +0,0 @@ -set -e - -force=$1 - -function safe_copy () { - if [ ! -f /etc/yunohost/installed ]; then - sudo cp $1 $2 - else - if [ $force ]; then - sudo yunohost service safecopy \ - -s ssl $1 $2 --force - else - sudo yunohost service safecopy \ - -s ssl $1 $2 - fi - fi -} - -cd /usr/share/yunohost/templates/ssl -ssl_dir=/usr/share/yunohost/yunohost-config/ssl/yunoCA - -sudo mkdir -p /etc/yunohost/certs/yunohost.org -sudo mkdir -p $ssl_dir/{ca,certs,crl,newcerts} - -safe_copy openssl.cnf $ssl_dir/openssl.cnf - -[ -f $ssl_dir/serial ] \ - || (echo "00" | sudo tee $ssl_dir/serial) - -[ -f $ssl_dir/index.txt ] \ - || sudo touch $ssl_dir/index.txt - -if [ ! -f /etc/yunohost/certs/yunohost.org/ca.pem ]; then - sudo openssl req -x509 -new -config $ssl_dir/openssl.cnf \ - -days 3650 -out $ssl_dir/ca/cacert.pem \ - -keyout $ssl_dir/ca/cakey.pem -nodes -batch - sudo cp $ssl_dir/ca/cacert.pem \ - /etc/yunohost/certs/yunohost.org/ca.pem - sudo ln -sf /etc/yunohost/certs/yunohost.org/ca.pem \ - /etc/ssl/certs/ca-yunohost_crt.pem - sudo update-ca-certificates -fi - -if [ ! -f /etc/yunohost/certs/yunohost.org/crt.pem ]; then - 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 diff --git a/data/hooks/conf_regen-old/03-ssh b/data/hooks/conf_regen-old/03-ssh deleted file mode 100644 index 1487ecdeb..000000000 --- a/data/hooks/conf_regen-old/03-ssh +++ /dev/null @@ -1,30 +0,0 @@ -set -e - -force=$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 - -# Only overwrite SSH configuration on an ISO installation -if [ ! -f /etc/yunohost/from_script ]; then - - # Do not listen to IPv6 if unavailable - if [ ! -f /proc/net/if_inet6 ]; then - sudo sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config - fi - safe_copy sshd_config /etc/ssh/sshd_config - - sudo service ssh restart -fi diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost new file mode 100644 index 000000000..621cb6c40 --- /dev/null +++ b/data/hooks/conf_regen/01-yunohost @@ -0,0 +1,40 @@ +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/yunohost + + [[ -d /etc/yunohost ]] || mkdir -p /etc/yunohost + + # set default current_host + [[ -f /etc/yunohost/current_host ]] \ + || echo "yunohost.org" | sudo tee /etc/yunohost/current_host + + # copy default firewall and services + # TODO: update them as needed with upgrades + [[ -f /etc/yunohost/firewall.yml ]] \ + || sudo cp firewall.yml /etc/yunohost/firewall.yml + [[ -f /etc/yunohost/services.yml ]] \ + || sudo cp services.yml /etc/yunohost/services.yml + + # allow users to access /media directory + [[ -d /etc/skel/media ]] \ + || (mkdir -p /media && ln -s /media /etc/skel/media) +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl new file mode 100644 index 000000000..95578ae90 --- /dev/null +++ b/data/hooks/conf_regen/02-ssl @@ -0,0 +1,71 @@ +set -e + +ssl_dir="/usr/share/yunohost/yunohost-config/ssl/yunoCA" + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/ssl + + install -D openssl.cnf "${pending_dir}/${ssl_dir}/openssl.cnf" +} + +do_post_regen() { + sudo mkdir -p "/etc/yunohost/certs/yunohost.org" + sudo mkdir -p "${ssl_dir}/"{ca,certs,crl,newcerts} + + [[ -f "${ssl_dir}/serial" ]] \ + || (echo "00" | sudo tee "${ssl_dir}/serial") + [[ -f "${ssl_dir}/index.txt" ]] \ + || sudo touch "${ssl_dir}/index.txt" + + if [[ ! -f /etc/yunohost/certs/yunohost.org/ca.pem ]]; then + sudo openssl req -x509 -new -config $ssl_dir/openssl.cnf \ + -days 3650 -out $ssl_dir/ca/cacert.pem \ + -keyout $ssl_dir/ca/cakey.pem -nodes -batch 2>&1 + sudo cp $ssl_dir/ca/cacert.pem \ + /etc/yunohost/certs/yunohost.org/ca.pem + sudo ln -sf /etc/yunohost/certs/yunohost.org/ca.pem \ + /etc/ssl/certs/ca-yunohost_crt.pem + sudo update-ca-certificates + fi + + if [[ ! -f /etc/yunohost/certs/yunohost.org/crt.pem ]]; then + 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 2>&1 + 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 2>&1 + + 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 +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh new file mode 100644 index 000000000..deff76ad5 --- /dev/null +++ b/data/hooks/conf_regen/03-ssh @@ -0,0 +1,38 @@ +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/ssh + + # only overwrite SSH configuration on an ISO installation + 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 + + install -D sshd_config "${pending_conf}/etc/ssh/sshd_config" + fi +} + +do_post_regen() { + [[ -f /etc/yunohost/from_script ]] \ + || sudo service ssh restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 From 386f25b3bf7795138ecb92992b0085f6b769c9c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 21:31:40 +0200 Subject: [PATCH 31/86] [enh] Mark conf_regen hooks as executable interpreted scripts --- data/hooks/conf_regen/01-yunohost | 2 ++ data/hooks/conf_regen/02-ssl | 2 ++ data/hooks/conf_regen/03-ssh | 2 ++ 3 files changed, 6 insertions(+) mode change 100644 => 100755 data/hooks/conf_regen/01-yunohost mode change 100644 => 100755 data/hooks/conf_regen/02-ssl mode change 100644 => 100755 data/hooks/conf_regen/03-ssh diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost old mode 100644 new mode 100755 index 621cb6c40..656663548 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -1,3 +1,5 @@ +#!/bin/bash + set -e do_pre_regen() { diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl old mode 100644 new mode 100755 index 95578ae90..6239b931d --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -1,3 +1,5 @@ +#!/bin/bash + set -e ssl_dir="/usr/share/yunohost/yunohost-config/ssl/yunoCA" diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh old mode 100644 new mode 100755 index deff76ad5..844efef3b --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -1,3 +1,5 @@ +#!/bin/bash + set -e do_pre_regen() { From 50d4eded1800abbe0b429283fbae405f44b96c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 21:34:46 +0200 Subject: [PATCH 32/86] [deb] Add etckeeper package in Recommends (wip #280) --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index b25925fc1..b0585cc27 100644 --- a/debian/control +++ b/debian/control @@ -27,7 +27,7 @@ Depends: ${python:Depends}, ${misc:Depends} , rspamd (>= 1.2.0), rmilter (>=1.7.0), redis-server, opendkim-tools Recommends: yunohost-admin , openssh-server, ntp, inetutils-ping | iputils-ping - , bash-completion, rsyslog + , bash-completion, rsyslog, etckeeper , php5-gd, php5-curl, php-gettext, php5-mcrypt , python-pip , unattended-upgrades From 0f9e777fc603f2d6924b7e79aabee6b3d4adc99a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 22:03:50 +0200 Subject: [PATCH 33/86] [enh] Update slapd conf_regen hook --- data/hooks/conf_regen-old/06-slapd | 71 ------------------------- data/hooks/conf_regen/06-slapd | 84 ++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 71 deletions(-) delete mode 100644 data/hooks/conf_regen-old/06-slapd create mode 100755 data/hooks/conf_regen/06-slapd diff --git a/data/hooks/conf_regen-old/06-slapd b/data/hooks/conf_regen-old/06-slapd deleted file mode 100644 index b5353394f..000000000 --- a/data/hooks/conf_regen-old/06-slapd +++ /dev/null @@ -1,71 +0,0 @@ -set -e - -force=$1 - -function safe_copy () { - if [ ! -f /etc/yunohost/installed ]; then - sudo cp $1 $2 - else - if [[ "$force" == "True" ]]; then - sudo yunohost service safecopy \ - -s slapd $1 $2 --force - else - sudo yunohost service safecopy \ - -s slapd $1 $2 - fi - fi -} - -cd /usr/share/yunohost/templates/slapd - -# Remove legacy configuration file -[ ! -f /etc/yunohost/installed ] \ - || sudo yunohost service saferemove -s slapd \ - /etc/ldap/slapd-yuno.conf - -# Retrieve current backend -backend=$(sudo slapcat -n 0 | sed -n 's/^dn: olcDatabase={1}\(.*\),cn=config$/\1/p') - -# Save current database in case of a backend change -BACKEND_CHANGE=0 -BACKUP_DIR="/var/backups/dc=yunohost,dc=org-${backend}-$(date +%s)" -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 -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 -sudo chown root:openldap /etc/ldap/slapd.conf -sudo chown -R openldap:openldap /etc/ldap/schema/ -sudo chown -R openldap:openldap /etc/ldap/slapd.d/ - -# Check the slapd config file at first -sudo slaptest -Q -u -f /etc/ldap/slapd.conf - -if [[ $BACKEND_CHANGE -eq 1 ]]; then - # Regenerate LDAP config directory and import database as root - # since the admin user may be unavailable - sudo sh -c "rm -Rf /etc/ldap/slapd.d; -mkdir /etc/ldap/slapd.d; -slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d; -chown -R openldap:openldap /etc/ldap/slapd.d; -slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ - -l '${BACKUP_DIR}/dc=yunohost-dc=org.ldif'; -chown -R openldap:openldap /var/lib/ldap" 2>&1 -else - # Regenerate LDAP config directory from slapd.conf - sudo rm -Rf /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 chown -R openldap:openldap /etc/ldap/slapd.d/ -fi - -sudo service slapd force-reload diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd new file mode 100755 index 000000000..a2b0b08c3 --- /dev/null +++ b/data/hooks/conf_regen/06-slapd @@ -0,0 +1,84 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/slapd + + # remove legacy configuration file + [ ! -f /etc/ldap/slapd-yuno.conf ] \ + || touch "${pending_dir}/etc/ldap/slapd-yuno.conf" + + ldap_dir="${pending_dir}/etc/ldap" + install -D ldap.conf "${ldap_dir}/ldap.conf" + install -D slapd.conf "${ldap_dir}/slapd.conf" + + schema_dir="${ldap_dir}/schema" + install -D sudo.schema "${schema_dir}/sudo.schema" + install -D mailserver.schema "${schema_dir}/mailserver.schema" + + install -D slapd.default "${pending_dir}/etc/default/slapd" +} + +do_post_regen() { + # retrieve current backend + #backend=$(sudo slapcat -n 0 | sed -n 's/^dn: olcDatabase={1}\(.*\),cn=config$/\1/p') + 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-${backend}-$(date +%s)" + if [[ -n "$backend" && "$backend" != "mdb" ]]; 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 + + # fix some permissions + sudo chown root:openldap /etc/ldap/slapd.conf + sudo chown -R openldap:openldap /etc/ldap/schema/ + sudo chown -R openldap:openldap /etc/ldap/slapd.d/ + + # check the slapd config file at first + sudo slaptest -Q -u -f /etc/ldap/slapd.conf + + if [[ $backend_change -eq 1 ]]; then + # regenerate LDAP config directory and import database as root + # since the admin user may be unavailable + sudo sh -c "rm -Rf /etc/ldap/slapd.d; + mkdir /etc/ldap/slapd.d; + slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d; + chown -R openldap:openldap /etc/ldap/slapd.d; + slapadd -F /etc/ldap/slapd.d -b dc=yunohost,dc=org \ + -l '${backup_dir}/dc=yunohost-dc=org.ldif'; + chown -R openldap:openldap /var/lib/ldap" 2>&1 + else + # regenerate LDAP config directory from slapd.conf + sudo rm -Rf /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 chown -R openldap:openldap /etc/ldap/slapd.d/ + fi + + sudo service slapd force-reload +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 From 99002b9f42f48367f0b2bbad38f6177af68b8059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 11 Apr 2016 22:04:56 +0200 Subject: [PATCH 34/86] [enh] Update nslcd conf_regen hook and do not edit the file by hand --- data/hooks/conf_regen-old/09-nslcd | 26 ------------------------ data/hooks/conf_regen/09-nslcd | 32 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 26 deletions(-) delete mode 100644 data/hooks/conf_regen-old/09-nslcd create mode 100755 data/hooks/conf_regen/09-nslcd diff --git a/data/hooks/conf_regen-old/09-nslcd b/data/hooks/conf_regen-old/09-nslcd deleted file mode 100644 index de377e997..000000000 --- a/data/hooks/conf_regen-old/09-nslcd +++ /dev/null @@ -1,26 +0,0 @@ -set -e - -force=$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 - -safe_copy nslcd.conf /etc/nslcd.conf - -# Fix: Add a blank line at the end of the file -# to avoid nscld restart failure -echo -e "\n" | sudo tee -a /etc/nslcd.conf - -sudo service nslcd restart diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen/09-nslcd new file mode 100755 index 000000000..e9239ca48 --- /dev/null +++ b/data/hooks/conf_regen/09-nslcd @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/nslcd + + install -D nslcd.conf "${pending_dir}/etc/nslcd.conf" +} + +do_post_regen() { + sudo service nslcd restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 From 2d28006c835a7b524b941ca9b6294fccb1f01f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 12 Apr 2016 21:20:17 +0200 Subject: [PATCH 35/86] [enh] Update metronome conf_regen hook --- data/hooks/conf_regen-old/12-metronome | 66 ----------------------- data/hooks/conf_regen/12-metronome | 74 ++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 66 deletions(-) delete mode 100644 data/hooks/conf_regen-old/12-metronome create mode 100755 data/hooks/conf_regen/12-metronome diff --git a/data/hooks/conf_regen-old/12-metronome b/data/hooks/conf_regen-old/12-metronome deleted file mode 100644 index abcf0d25d..000000000 --- a/data/hooks/conf_regen-old/12-metronome +++ /dev/null @@ -1,66 +0,0 @@ -set -e - -force=$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 - -# Copy configuration files -main_domain=$(cat /etc/yunohost/current_host) -cat metronome.cfg.lua.sed \ - | 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 -sudo mkdir -p /etc/metronome/conf.d - -domain_list=$(sudo yunohost domain list --output-as plain) - -# Copy a configuration file for each YunoHost domain -for domain in $domain_list; do - sanitzed_domain="$(echo $domain | sed 's/\./%2e/g')" - sudo mkdir -p /var/lib/metronome/$sanitzed_domain/pep - - 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 - -# Remove old domains files -for file in /etc/metronome/conf.d/*; do - domain=$(echo $file \ - | sed 's|/etc/metronome/conf.d/||' \ - | sed 's|.cfg.lua||') - sanitzed_domain="$(echo $domain | sed 's/\./%2e/g')" - [[ $domain_list =~ $domain ]] \ - || ([[ $(sudo yunohost service saferemove -s metronome $file | tail -n1) == "True" ]] \ - && sudo rm -rf /var/lib/metronome/$sanitzed_domain) -done - -# Create domain directory -sudo chown -R metronome: /var/lib/metronome/ -sudo chown -R metronome: /etc/metronome/conf.d/ - -# Restart if need be -if [[ "$need_restart" == "True" ]]; then - sudo service metronome restart -else - sudo service metronome reload \ - || sudo service metronome restart -fi diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome new file mode 100755 index 000000000..ff753547b --- /dev/null +++ b/data/hooks/conf_regen/12-metronome @@ -0,0 +1,74 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/metronome + + # create directories for pending conf + metronome_dir="${pending_dir}/etc/metronome" + metronome_conf_dir="${metronome_dir}/conf.d" + mkdir -p "$metronome_dir" "$metronome_conf_dir" + + # retrieve variables + main_domain=$(cat /etc/yunohost/current_host) + domain_list=$(sudo yunohost domain list --output-as plain --quiet) + + # install main conf file + cat metronome.cfg.lua \ + | sed "s/{{ main_domain }}/${main_domain}/g" \ + > "${metronome_dir}/metronome.cfg.lua" + + # add domain conf files + for domain in $domain_list; do + cat domain.cfg.lua.sed \ + | sed "s/{{ domain }}/${domain}/g" \ + > "${metronome_conf_dir}/${domain}.cfg.lua" + done + + # remove old domain conf files + conf_files=$(ls -1 /etc/metronome/conf.d \ + | awk '/^[a-z].*\.cfg\.lua$/ { print $1 }') + for file in $conf_files; do + domain=${file%.cfg.lua} + [[ $domain_list =~ $domain ]] \ + || touch "${metronome_conf_dir}/${file}" + done +} + +do_post_regen() { + # retrieve variables + # TODO: retrieve only changed/new domains + domain_list=$(sudo yunohost domain list --output-as plain --quiet) + + # create metronome directories for domains + for domain in $domain_list; do + sudo mkdir -p "/var/lib/metronome/${domain//./%2e}/pep" + done + + # fix permissions + sudo chown -R metronome: /var/lib/metronome/ + sudo chown -R metronome: /etc/metronome/conf.d/ + + # TODO: only restart if conf changed + sudo service metronome restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 From c09abeaec1db655092b35fdcd6b043d6464c6502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 12 Apr 2016 21:41:52 +0200 Subject: [PATCH 36/86] [enh] Update postfix conf_regen hook and simplify plain conf files copy --- data/hooks/conf_regen-old/19-postfix | 56 ------------------- data/hooks/conf_regen/19-postfix | 53 ++++++++++++++++++ .../postfix/{main.cf.sed => main.cf} | 0 .../postfix/{ => plain}/header_checks | 0 .../postfix/{ => plain}/ldap-accounts.cf | 0 .../postfix/{ => plain}/ldap-aliases.cf | 0 .../postfix/{ => plain}/ldap-domains.cf | 0 data/templates/postfix/{ => plain}/master.cf | 0 .../postfix/{ => plain}/sender_canonical | 0 .../postfix/{ => plain}/smtp_reply_filter | 0 10 files changed, 53 insertions(+), 56 deletions(-) delete mode 100644 data/hooks/conf_regen-old/19-postfix create mode 100755 data/hooks/conf_regen/19-postfix rename data/templates/postfix/{main.cf.sed => main.cf} (100%) rename data/templates/postfix/{ => plain}/header_checks (100%) rename data/templates/postfix/{ => plain}/ldap-accounts.cf (100%) rename data/templates/postfix/{ => plain}/ldap-aliases.cf (100%) rename data/templates/postfix/{ => plain}/ldap-domains.cf (100%) rename data/templates/postfix/{ => plain}/master.cf (100%) rename data/templates/postfix/{ => plain}/sender_canonical (100%) rename data/templates/postfix/{ => plain}/smtp_reply_filter (100%) diff --git a/data/hooks/conf_regen-old/19-postfix b/data/hooks/conf_regen-old/19-postfix deleted file mode 100644 index 1e37e6752..000000000 --- a/data/hooks/conf_regen-old/19-postfix +++ /dev/null @@ -1,56 +0,0 @@ -set -e - -force=$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 - -# Copy plain single configuration files -files="header_checks -ldap-accounts.cf -ldap-aliases.cf -ldap-domains.cf -master.cf -sender_canonical -smtp_reply_filter" - -for file in $files; do - safe_copy $file /etc/postfix/$file -done - -main_domain=$(cat /etc/yunohost/current_host) - -# Replace main domain in the main configuration file -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 - sudo sed -i \ - 's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \ - main.cf - - sudo sed -i \ - 's/inet_interfaces = all/inet_interfaces = all\ninet_protocols = ipv4/' \ - main.cf -fi - -if [[ $(safe_copy main.cf /etc/postfix/main.cf) == "True" ]]; then - sudo service postfix restart -else - sudo service postfix reload \ - || sudo service postfix restart -fi diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix new file mode 100755 index 000000000..2f1ffb283 --- /dev/null +++ b/data/hooks/conf_regen/19-postfix @@ -0,0 +1,53 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/postfix + + postfix_dir="${pending_dir}/etc/postfix" + mkdir -p "$postfix_dir" + + # install plain conf files + cp plain/* "$postfix_dir" + + # prepare main.cf conf file + main_domain=$(cat /etc/yunohost/current_host) + cat main.cf \ + | sed "s/{{ main_domain }}/${main_domain}/g" \ + > "${postfix_dir}/main.cf" + + # adapt it to IPv4-only hosts + if [ ! -f /proc/net/if_inet6 ]; then + sed -i \ + 's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \ + "${postfix_dir}/main.cf" + sed -i \ + 's/inet_interfaces = all/&\ninet_protocols = ipv4/' \ + "${postfix_dir}/main.cf" + fi +} + +do_post_regen() { + # TODO: only restart if conf changed + sudo service postfix restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/postfix/main.cf.sed b/data/templates/postfix/main.cf similarity index 100% rename from data/templates/postfix/main.cf.sed rename to data/templates/postfix/main.cf diff --git a/data/templates/postfix/header_checks b/data/templates/postfix/plain/header_checks similarity index 100% rename from data/templates/postfix/header_checks rename to data/templates/postfix/plain/header_checks diff --git a/data/templates/postfix/ldap-accounts.cf b/data/templates/postfix/plain/ldap-accounts.cf similarity index 100% rename from data/templates/postfix/ldap-accounts.cf rename to data/templates/postfix/plain/ldap-accounts.cf diff --git a/data/templates/postfix/ldap-aliases.cf b/data/templates/postfix/plain/ldap-aliases.cf similarity index 100% rename from data/templates/postfix/ldap-aliases.cf rename to data/templates/postfix/plain/ldap-aliases.cf diff --git a/data/templates/postfix/ldap-domains.cf b/data/templates/postfix/plain/ldap-domains.cf similarity index 100% rename from data/templates/postfix/ldap-domains.cf rename to data/templates/postfix/plain/ldap-domains.cf diff --git a/data/templates/postfix/master.cf b/data/templates/postfix/plain/master.cf similarity index 100% rename from data/templates/postfix/master.cf rename to data/templates/postfix/plain/master.cf diff --git a/data/templates/postfix/sender_canonical b/data/templates/postfix/plain/sender_canonical similarity index 100% rename from data/templates/postfix/sender_canonical rename to data/templates/postfix/plain/sender_canonical diff --git a/data/templates/postfix/smtp_reply_filter b/data/templates/postfix/plain/smtp_reply_filter similarity index 100% rename from data/templates/postfix/smtp_reply_filter rename to data/templates/postfix/plain/smtp_reply_filter From c8255fbc0cad6c97c4d7c8097c0faa14679d326a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Wed, 13 Apr 2016 22:27:43 +0200 Subject: [PATCH 37/86] [enh] Update avahi-daemon, glances and nsswitch conf_regen hooks --- data/hooks/conf_regen-old/37-avahi-daemon | 19 -------------- data/hooks/conf_regen-old/40-glances | 19 -------------- data/hooks/conf_regen-old/46-nsswitch | 19 -------------- data/hooks/conf_regen/37-avahi-daemon | 32 +++++++++++++++++++++++ data/hooks/conf_regen/40-glances | 32 +++++++++++++++++++++++ data/hooks/conf_regen/46-nsswitch | 32 +++++++++++++++++++++++ 6 files changed, 96 insertions(+), 57 deletions(-) delete mode 100644 data/hooks/conf_regen-old/37-avahi-daemon delete mode 100644 data/hooks/conf_regen-old/40-glances delete mode 100644 data/hooks/conf_regen-old/46-nsswitch create mode 100755 data/hooks/conf_regen/37-avahi-daemon create mode 100755 data/hooks/conf_regen/40-glances create mode 100755 data/hooks/conf_regen/46-nsswitch diff --git a/data/hooks/conf_regen-old/37-avahi-daemon b/data/hooks/conf_regen-old/37-avahi-daemon deleted file mode 100644 index 0fed5217c..000000000 --- a/data/hooks/conf_regen-old/37-avahi-daemon +++ /dev/null @@ -1,19 +0,0 @@ -set -e - -force=$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 - -if [[ "$(safe_copy avahi-daemon.conf /etc/avahi/avahi-daemon.conf | tail -n1)" == "True" ]]; then - sudo service avahi-daemon restart -fi diff --git a/data/hooks/conf_regen-old/40-glances b/data/hooks/conf_regen-old/40-glances deleted file mode 100644 index a1bec3dbe..000000000 --- a/data/hooks/conf_regen-old/40-glances +++ /dev/null @@ -1,19 +0,0 @@ -set -e - -force=$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 - -if [[ "$(safe_copy glances.default /etc/default/glances | tail -n1)" == "True" ]]; then - sudo service glances restart -fi diff --git a/data/hooks/conf_regen-old/46-nsswitch b/data/hooks/conf_regen-old/46-nsswitch deleted file mode 100644 index 92f34b249..000000000 --- a/data/hooks/conf_regen-old/46-nsswitch +++ /dev/null @@ -1,19 +0,0 @@ -set -e - -force=$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 - -if [[ "$(safe_copy nsswitch.conf /etc/nsswitch.conf | tail -n1)" == "True" ]]; then - sudo service nscd restart -fi diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon new file mode 100755 index 000000000..bbf290576 --- /dev/null +++ b/data/hooks/conf_regen/37-avahi-daemon @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/avahi-daemon + + install -D avahi-daemon.conf "${pending_dir}/etc/avahi/avahi-daemon.conf" +} + +do_post_regen() { + sudo service avahi-daemon restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen/40-glances new file mode 100755 index 000000000..73558338c --- /dev/null +++ b/data/hooks/conf_regen/40-glances @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/glances + + install -D glances.default "${pending_dir}/etc/default/glances" +} + +do_post_regen() { + sudo service glances restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen/46-nsswitch new file mode 100755 index 000000000..f6ced70f3 --- /dev/null +++ b/data/hooks/conf_regen/46-nsswitch @@ -0,0 +1,32 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/nsswitch + + install -D nsswitch.conf "${pending_dir}/etc/nsswitch.conf" +} + +do_post_regen() { + sudo service nscd restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 From 93fa6b07da2fe712539624cb172d878efda54dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Wed, 13 Apr 2016 22:34:11 +0200 Subject: [PATCH 38/86] =?UTF-8?q?[fix]=C2=A0Simplify=20directory=20creatio?= =?UTF-8?q?n=20in=20metronome=20conf=5Fregen=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/hooks/conf_regen/12-metronome | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index ff753547b..31f925858 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -10,7 +10,7 @@ do_pre_regen() { # create directories for pending conf metronome_dir="${pending_dir}/etc/metronome" metronome_conf_dir="${metronome_dir}/conf.d" - mkdir -p "$metronome_dir" "$metronome_conf_dir" + mkdir -p "$metronome_conf_dir" # retrieve variables main_domain=$(cat /etc/yunohost/current_host) From 3eacbef1448b83886abc749e0c3e0533e266b8bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Wed, 13 Apr 2016 22:35:40 +0200 Subject: [PATCH 39/86] [enh] Update fail2ban conf_regen hook and drop wheezy support --- data/hooks/conf_regen-old/52-fail2ban | 28 -- data/hooks/conf_regen/52-fail2ban | 36 ++ data/templates/fail2ban/jail-wheezy.conf | 346 ------------------ .../fail2ban/{jail-jessie.conf => jail.conf} | 0 4 files changed, 36 insertions(+), 374 deletions(-) delete mode 100644 data/hooks/conf_regen-old/52-fail2ban create mode 100755 data/hooks/conf_regen/52-fail2ban delete mode 100644 data/templates/fail2ban/jail-wheezy.conf rename data/templates/fail2ban/{jail-jessie.conf => jail.conf} (100%) diff --git a/data/hooks/conf_regen-old/52-fail2ban b/data/hooks/conf_regen-old/52-fail2ban deleted file mode 100644 index 9ff6714ad..000000000 --- a/data/hooks/conf_regen-old/52-fail2ban +++ /dev/null @@ -1,28 +0,0 @@ -set -e - -force=$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 - -sudo mkdir -p /etc/fail2ban/filter.d -safe_copy yunohost.conf /etc/fail2ban/filter.d/yunohost.conf - -# Compatibility: change from HDB to MDB on Jessie -version=$(sed 's/\..*//' /etc/debian_version) -[[ "$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 - sudo service fail2ban restart -fi diff --git a/data/hooks/conf_regen/52-fail2ban b/data/hooks/conf_regen/52-fail2ban new file mode 100755 index 000000000..39fd3be21 --- /dev/null +++ b/data/hooks/conf_regen/52-fail2ban @@ -0,0 +1,36 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/fail2ban + + fail2ban_dir="${pending_dir}/etc/fail2ban" + mkdir -p "${fail2ban_dir}/filter.d" + + cp yunohost.conf "${fail2ban_dir}/filter.d/yunohost.conf" + cp jail.conf "${fail2ban_dir}/jail.conf" +} + +do_post_regen() { + sudo service fail2ban restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/fail2ban/jail-wheezy.conf b/data/templates/fail2ban/jail-wheezy.conf deleted file mode 100644 index 8eb0e7a1e..000000000 --- a/data/templates/fail2ban/jail-wheezy.conf +++ /dev/null @@ -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 -# -# $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 diff --git a/data/templates/fail2ban/jail-jessie.conf b/data/templates/fail2ban/jail.conf similarity index 100% rename from data/templates/fail2ban/jail-jessie.conf rename to data/templates/fail2ban/jail.conf From df9094b896d5714ee4fe0862193b614f413454d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 11:39:43 +0200 Subject: [PATCH 40/86] [enh] Update dovecot conf_regen hook --- data/hooks/conf_regen-old/25-dovecot | 51 --------------- data/hooks/conf_regen/25-dovecot | 64 +++++++++++++++++++ .../{dovecot.conf.sed => dovecot.conf} | 0 3 files changed, 64 insertions(+), 51 deletions(-) delete mode 100644 data/hooks/conf_regen-old/25-dovecot create mode 100755 data/hooks/conf_regen/25-dovecot rename data/templates/dovecot/{dovecot.conf.sed => dovecot.conf} (100%) diff --git a/data/hooks/conf_regen-old/25-dovecot b/data/hooks/conf_regen-old/25-dovecot deleted file mode 100644 index c54300e08..000000000 --- a/data/hooks/conf_regen-old/25-dovecot +++ /dev/null @@ -1,51 +0,0 @@ -set -e - -force=$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 - -# Create vmail user -sudo id vmail > /dev/null 2>&1 \ - || sudo adduser --system --ingroup mail --uid 500 vmail - - -# 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 service dovecot restart diff --git a/data/hooks/conf_regen/25-dovecot b/data/hooks/conf_regen/25-dovecot new file mode 100755 index 000000000..190e50ef2 --- /dev/null +++ b/data/hooks/conf_regen/25-dovecot @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/dovecot + + dovecot_dir="${pending_dir}/etc/dovecot" + 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() { + # create vmail user + id vmail > /dev/null 2>&1 \ + || sudo adduser --system --ingroup mail --uid 500 vmail + + # compile sieve script + # TODO: only compile if script changed + sudo sievec /etc/dovecot/global_script/dovecot.sieve + + # fix permissions + sudo chown -R vmail:mail /etc/dovecot/global_script + sudo chmod -R 770 /etc/dovecot/global_script + sudo chmod 660 /etc/dovecot/global_script/dovecot.{sieve,svbin} + + # TODO: only restart if conf changed + sudo service dovecot restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/dovecot/dovecot.conf.sed b/data/templates/dovecot/dovecot.conf similarity index 100% rename from data/templates/dovecot/dovecot.conf.sed rename to data/templates/dovecot/dovecot.conf From 047015ef61887e466cd33d297fcc82bcb2f03fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 14:37:20 +0200 Subject: [PATCH 41/86] [enh] Update rmilter and rspamd conf_regen hooks --- data/hooks/conf_regen-old/28-rmilter | 43 -------------- data/hooks/conf_regen-old/31-rspamd | 33 ----------- data/hooks/conf_regen/28-rmilter | 56 +++++++++++++++++++ data/hooks/conf_regen/31-rspamd | 45 +++++++++++++++ ...{metrics.conf.local => metrics.local.conf} | 0 5 files changed, 101 insertions(+), 76 deletions(-) delete mode 100644 data/hooks/conf_regen-old/28-rmilter delete mode 100644 data/hooks/conf_regen-old/31-rspamd create mode 100755 data/hooks/conf_regen/28-rmilter create mode 100755 data/hooks/conf_regen/31-rspamd rename data/templates/rspamd/{metrics.conf.local => metrics.local.conf} (100%) diff --git a/data/hooks/conf_regen-old/28-rmilter b/data/hooks/conf_regen-old/28-rmilter deleted file mode 100644 index f57427a2c..000000000 --- a/data/hooks/conf_regen-old/28-rmilter +++ /dev/null @@ -1,43 +0,0 @@ -set -e - -force=$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 - -# Copy Rmilter configuration -safe_copy rmilter.conf /etc/rmilter.conf - -# Override socket configuration -safe_copy rmilter.socket /etc/systemd/system/rmilter.socket - -# Create DKIM key for each YunoHost domain -sudo mkdir -p /etc/dkim -domain_list=$(sudo yunohost domain list --output-as plain) - -for domain in $domain_list; do - [ -f /etc/dkim/$domain.mail.key ] \ - || (sudo opendkim-genkey --domain=$domain \ - --selector=mail\ - --directory=/etc/dkim \ - && sudo mv /etc/dkim/mail.private /etc/dkim/$domain.mail.key \ - && 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 - -# Reload systemd daemon, ensure that the socket is listening and stop -# the service. It will be started again by the socket as needed. -sudo systemctl daemon-reload -sudo systemctl start rmilter.socket -sudo systemctl stop rmilter.service 2>&1 || true diff --git a/data/hooks/conf_regen-old/31-rspamd b/data/hooks/conf_regen-old/31-rspamd deleted file mode 100644 index da62e20ea..000000000 --- a/data/hooks/conf_regen-old/31-rspamd +++ /dev/null @@ -1,33 +0,0 @@ -set -e - -force=$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 - -# Create configuration directories -sudo mkdir -p /etc/rspamd/local.d /etc/rspamd/override.d - -# Copy specific configuration to rewrite the defaults -safe_copy metrics.conf.local /etc/rspamd/local.d/metrics.conf - -# Install Rspamd sieve script -safe_copy rspamd.sieve /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 - -# 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 diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter new file mode 100755 index 000000000..75f3d327a --- /dev/null +++ b/data/hooks/conf_regen/28-rmilter @@ -0,0 +1,56 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/rmilter + + install -D rmilter.conf "${pending_dir}/etc/rmilter.conf" + install -D rmilter.socket "${pending_dir}/etc/rmilter.socket" +} + +do_post_regen() { + # retrieve variables + # TODO: retrieve only new domains + domain_list=$(sudo yunohost domain list --output-as plain --quiet) + + # create DKIM key for domains + for domain in $domain_list; do + [ ! -f /etc/dkim/$domain.mail.key ] && { + sudo opendkim-genkey --domain="$domain" \ + --selector=mail --directory=/etc/dkim + sudo mv /etc/dkim/mail.private "/etc/dkim/${domain}.mail.key" + sudo mv /etc/dkim/mail.txt "/etc/dkim/${domain}.mail.txt" + } + done + + # fix DKIM keys permissions + sudo chown _rmilter /etc/dkim/*.mail.key + sudo chmod 400 /etc/dkim/*.mail.key + + # Reload systemd daemon, ensure that the socket is listening and stop + # the service. It will be started again by the socket as needed. + # TODO: only restart if conf changed + sudo systemctl -q daemon-reload + sudo systemctl -q start rmilter.socket + sudo systemctl -q stop rmilter.service 2>&1 || true +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd new file mode 100755 index 000000000..513326f0b --- /dev/null +++ b/data/hooks/conf_regen/31-rspamd @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/rspamd + + install -D metrics.local.conf \ + "${pending_dir}/etc/rspamd/local.d/metrics.conf" + install -D rspamd.sieve \ + "${pending_dir}/etc/dovecot/global_script/rspamd.sieve" +} + +do_post_regen() { + # compile sieve script + # TODO: only compile and restart dovecot if script changed + sudo sievec /etc/dovecot/global_script/dovecot.sieve + # fix permissions and restart dovecot + sudo chown -R vmail:mail /etc/dovecot/global_script + sudo chmod 660 /etc/dovecot/global_script/rspamd.{sieve,svbin} + sudo systemctl restart dovecot + + # TODO: only restart if conf changed + sudo systemctl -q start rspamd.socket + sudo systemctl -q stop rspamd.service 2>&1 || true +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$status'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/rspamd/metrics.conf.local b/data/templates/rspamd/metrics.local.conf similarity index 100% rename from data/templates/rspamd/metrics.conf.local rename to data/templates/rspamd/metrics.local.conf From 8269788172d8a48788803d90130858dcada371b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 14:41:54 +0200 Subject: [PATCH 42/86] [fix] Typo and variable name fixes in conf_regen hooks --- data/hooks/conf_regen/01-yunohost | 4 ++-- data/hooks/conf_regen/02-ssl | 4 ++-- data/hooks/conf_regen/03-ssh | 6 +++--- data/hooks/conf_regen/06-slapd | 4 ++-- data/hooks/conf_regen/09-nslcd | 4 ++-- data/hooks/conf_regen/12-metronome | 4 ++-- data/hooks/conf_regen/19-postfix | 6 +++--- data/hooks/conf_regen/25-dovecot | 4 ++-- data/hooks/conf_regen/28-rmilter | 2 +- data/hooks/conf_regen/31-rspamd | 2 +- data/hooks/conf_regen/37-avahi-daemon | 4 ++-- data/hooks/conf_regen/40-glances | 4 ++-- data/hooks/conf_regen/46-nsswitch | 4 ++-- data/hooks/conf_regen/52-fail2ban | 4 ++-- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 656663548..27268b5a9 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -34,7 +34,7 @@ case "$1" in post) ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index 6239b931d..5d14cb3d1 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e ssl_dir="/usr/share/yunohost/yunohost-config/ssl/yunoCA" @@ -65,7 +65,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 844efef3b..bbf9591e5 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -13,7 +13,7 @@ do_pre_regen() { [[ -f /proc/net/if_inet6 ]] \ || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config - install -D sshd_config "${pending_conf}/etc/ssh/sshd_config" + install -D sshd_config "${pending_dir}/etc/ssh/sshd_config" fi } @@ -32,7 +32,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index a2b0b08c3..778b32eea 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -76,7 +76,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen/09-nslcd index e9239ca48..9ae1a8192 100755 --- a/data/hooks/conf_regen/09-nslcd +++ b/data/hooks/conf_regen/09-nslcd @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -24,7 +24,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index 31f925858..b07a8894f 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -66,7 +66,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 2f1ffb283..df62a0350 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -19,7 +19,7 @@ do_pre_regen() { | sed "s/{{ main_domain }}/${main_domain}/g" \ > "${postfix_dir}/main.cf" - # adapt it to IPv4-only hosts + # adapt it for IPv4-only hosts if [ ! -f /proc/net/if_inet6 ]; then sed -i \ 's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \ @@ -45,7 +45,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/25-dovecot b/data/hooks/conf_regen/25-dovecot index 190e50ef2..0a80dcb13 100755 --- a/data/hooks/conf_regen/25-dovecot +++ b/data/hooks/conf_regen/25-dovecot @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -56,7 +56,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index 75f3d327a..a46284bc8 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -48,7 +48,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index 513326f0b..258535634 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -37,7 +37,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon index bbf290576..6eba8c729 100755 --- a/data/hooks/conf_regen/37-avahi-daemon +++ b/data/hooks/conf_regen/37-avahi-daemon @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -24,7 +24,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen/40-glances index 73558338c..7fe40c2d8 100755 --- a/data/hooks/conf_regen/40-glances +++ b/data/hooks/conf_regen/40-glances @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -24,7 +24,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen/46-nsswitch index f6ced70f3..352346e68 100755 --- a/data/hooks/conf_regen/46-nsswitch +++ b/data/hooks/conf_regen/46-nsswitch @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -24,7 +24,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac diff --git a/data/hooks/conf_regen/52-fail2ban b/data/hooks/conf_regen/52-fail2ban index 39fd3be21..88af82326 100755 --- a/data/hooks/conf_regen/52-fail2ban +++ b/data/hooks/conf_regen/52-fail2ban @@ -1,6 +1,6 @@ #!/bin/bash -set -e +set -e do_pre_regen() { pending_dir=$1 @@ -28,7 +28,7 @@ case "$1" in do_post_regen ;; *) - echo "hook called with unknown argument \`$status'" >&2 + echo "hook called with unknown argument \`$1'" >&2 exit 1 ;; esac From 41f9b2c76cc8bc934130af39f4bf3fb0fe61da77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 15:17:22 +0200 Subject: [PATCH 43/86] [enh] Update dnsmasq conf_regen hook and use loopback address by default --- data/hooks/conf_regen-old/43-dnsmasq | 53 --------------- data/hooks/conf_regen/43-dnsmasq | 64 +++++++++++++++++++ .../dnsmasq/{domain.sed => domain.tpl} | 0 3 files changed, 64 insertions(+), 53 deletions(-) delete mode 100644 data/hooks/conf_regen-old/43-dnsmasq create mode 100755 data/hooks/conf_regen/43-dnsmasq rename data/templates/dnsmasq/{domain.sed => domain.tpl} (100%) diff --git a/data/hooks/conf_regen-old/43-dnsmasq b/data/hooks/conf_regen-old/43-dnsmasq deleted file mode 100644 index 683747adb..000000000 --- a/data/hooks/conf_regen-old/43-dnsmasq +++ /dev/null @@ -1,53 +0,0 @@ -set -e - -force=$1 - -. /usr/share/yunohost/helpers - -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 - -# Get IPv4 address -ip=$(curl -s -4 https://ip.yunohost.org 2>/dev/null || true) -ynh_validate_ip4 $ip || ip='0.0.0.0' - -# Get IPv6 IP address -ipv6=$(curl -s -6 http://ip6.yunohost.org 2>/dev/null || true) -ynh_validate_ip6 $ipv6 || ipv6='' - -sudo mkdir -p /etc/dnsmasq.d - -domain_list=$(sudo yunohost domain list --output-as plain) - -# Copy a configuration file for each YunoHost domain -for domain in $domain_list; do - cat domain.sed \ - | sed "s/{{ domain }}/$domain/g" \ - | sed "s/{{ ip }}/$ip/g" \ - | sudo tee $domain - - if [[ "$ipv6" != "" ]]; then - echo "address=/$domain/$ipv6" | sudo tee -a $domain - fi - - safe_copy $domain /etc/dnsmasq.d/$domain -done - -# Remove old domains files -for file in /etc/dnsmasq.d/*.*; do - domain=$(echo $file | sed 's|/etc/dnsmasq.d/||') - [[ $domain_list =~ $domain ]] \ - || sudo yunohost service saferemove -s dnsmasq $file -done - -sudo service dnsmasq reload \ - || sudo service dnsmasq restart diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq new file mode 100755 index 000000000..100cd4b46 --- /dev/null +++ b/data/hooks/conf_regen/43-dnsmasq @@ -0,0 +1,64 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + # source ip helpers + . /usr/share/yunohost/helpers.d/ip + + cd /usr/share/yunohost/templates/dnsmasq + + # create directory for pending conf + dnsmasq_dir="${pending_dir}/etc/dnsmasq.d" + mkdir -p "$dnsmasq_dir" + + # 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) + ynh_validate_ip6 "$ipv6" || ipv6='' + domain_list=$(sudo yunohost domain list --output-as plain --quiet) + + # add domain conf files + for domain in $domain_list; do + cat domain.tpl \ + | sed "s/{{ domain }}/${domain}/g" \ + | sed "s/{{ ip }}/${ipv4}/g" \ + > "${dnsmasq_dir}/${domain}" + [[ -n $ipv6 ]] \ + && echo "address=/${domain}/${ipv6}" >> "${dnsmasq_dir}/${domain}" + done + + # remove old domain conf files + conf_files=$(ls -1 /etc/dnsmasq.d \ + | awk '/^[^\.]+\.[^\.]+.*$/ { print $1 }') + for domain in $conf_files; do + [[ $domain_list =~ $domain ]] \ + || touch "${dnsmasq_dir}/${domain}" + done +} + +do_post_regen() { + # TODO: only restart if conf changed + sudo service dnsmasq reload \ + || sudo service dnsmasq restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/dnsmasq/domain.sed b/data/templates/dnsmasq/domain.tpl similarity index 100% rename from data/templates/dnsmasq/domain.sed rename to data/templates/dnsmasq/domain.tpl From cdd36570f8434afe1f0c093a639fc0a840b251d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 15:20:05 +0200 Subject: [PATCH 44/86] [fix] Rename metronome template conf files --- data/hooks/conf_regen/12-metronome | 2 +- .../metronome/{domain.cfg.lua.sed => domain.tpl.cfg.lua} | 0 .../metronome/{metronome.cfg.lua.sed => metronome.cfg.lua} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename data/templates/metronome/{domain.cfg.lua.sed => domain.tpl.cfg.lua} (100%) rename data/templates/metronome/{metronome.cfg.lua.sed => metronome.cfg.lua} (100%) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index b07a8894f..2fb03b015 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -23,7 +23,7 @@ do_pre_regen() { # add domain conf files for domain in $domain_list; do - cat domain.cfg.lua.sed \ + cat domain.tpl.cfg.lua \ | sed "s/{{ domain }}/${domain}/g" \ > "${metronome_conf_dir}/${domain}.cfg.lua" done diff --git a/data/templates/metronome/domain.cfg.lua.sed b/data/templates/metronome/domain.tpl.cfg.lua similarity index 100% rename from data/templates/metronome/domain.cfg.lua.sed rename to data/templates/metronome/domain.tpl.cfg.lua diff --git a/data/templates/metronome/metronome.cfg.lua.sed b/data/templates/metronome/metronome.cfg.lua similarity index 100% rename from data/templates/metronome/metronome.cfg.lua.sed rename to data/templates/metronome/metronome.cfg.lua From f019188a9059287e94c45f65685200c31e896dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 16:07:24 +0200 Subject: [PATCH 45/86] [enh] Update mysql conf_regen hook and use helpers for password generation --- data/hooks/conf_regen-old/34-mysql | 35 ------------------------ data/hooks/conf_regen/34-mysql | 43 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 35 deletions(-) delete mode 100644 data/hooks/conf_regen-old/34-mysql create mode 100755 data/hooks/conf_regen/34-mysql diff --git a/data/hooks/conf_regen-old/34-mysql b/data/hooks/conf_regen-old/34-mysql deleted file mode 100644 index 95fc97808..000000000 --- a/data/hooks/conf_regen-old/34-mysql +++ /dev/null @@ -1,35 +0,0 @@ -set -e - -force=$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 - -if [[ "$(safe_copy my.cnf /etc/mysql/my.cnf | tail -n1)" == "True" ]]; then - sudo service mysql restart -fi - -if [ ! -f /etc/yunohost/mysql ]; then - [[ $(/bin/ps aux | grep '[m]ysqld') == "0" ]] \ - && sudo service mysql start - - mysql_password=$(randpass 10 0) - sudo mysqladmin -u root -pyunohost password $mysql_password - echo $mysql_password | sudo tee /etc/yunohost/mysql - sudo chmod 400 /etc/yunohost/mysql -fi diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql new file mode 100755 index 000000000..bed97b497 --- /dev/null +++ b/data/hooks/conf_regen/34-mysql @@ -0,0 +1,43 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/mysql + + install -D my.cnf "${pending_dir}/etc/mysql/my.cnf" +} + +do_post_regen() { + # TODO: only restart if conf changed + sudo service mysql restart + + if [ ! -f /etc/yunohost/mysql ]; then + # source string helpers + . /usr/share/yunohost/helpers.d/string + + mysql_password=$(ynh_string_random 10) + sudo mysqladmin -u root -pyunohost password "$mysql_password" + echo $mysql_password | sudo tee /etc/yunohost/mysql + sudo chmod 400 /etc/yunohost/mysql + fi +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 From d851237dc2eb8ac6fc78efc4b6c4f5f4f6a3fe32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 16:43:13 +0200 Subject: [PATCH 46/86] [enh] Update nginx conf_regen hook and simplify plain conf files copy --- data/hooks/conf_regen-old/15-nginx | 86 ------------------- data/hooks/conf_regen/15-nginx | 79 +++++++++++++++++ data/templates/nginx/{ => plain}/global.conf | 0 data/templates/nginx/{ => plain}/ssowat.conf | 0 .../nginx/{ => plain}/yunohost_admin.conf | 0 .../nginx/{ => plain}/yunohost_admin.conf.inc | 0 .../nginx/{ => plain}/yunohost_api.conf.inc | 0 .../nginx/{ => plain}/yunohost_panel.conf.inc | 0 .../{server.conf.sed => server.tpl.conf} | 0 9 files changed, 79 insertions(+), 86 deletions(-) delete mode 100644 data/hooks/conf_regen-old/15-nginx create mode 100755 data/hooks/conf_regen/15-nginx rename data/templates/nginx/{ => plain}/global.conf (100%) rename data/templates/nginx/{ => plain}/ssowat.conf (100%) rename data/templates/nginx/{ => plain}/yunohost_admin.conf (100%) rename data/templates/nginx/{ => plain}/yunohost_admin.conf.inc (100%) rename data/templates/nginx/{ => plain}/yunohost_api.conf.inc (100%) rename data/templates/nginx/{ => plain}/yunohost_panel.conf.inc (100%) rename data/templates/nginx/{server.conf.sed => server.tpl.conf} (100%) diff --git a/data/hooks/conf_regen-old/15-nginx b/data/hooks/conf_regen-old/15-nginx deleted file mode 100644 index d45c0328e..000000000 --- a/data/hooks/conf_regen-old/15-nginx +++ /dev/null @@ -1,86 +0,0 @@ -set -e - -force=$1 - -function safe_copy () { - 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 -} - -cd /usr/share/yunohost/templates/nginx - -# Copy plain single configuration files -files="ssowat.conf -global.conf -yunohost_admin.conf -yunohost_admin.conf.inc -yunohost_api.conf.inc -yunohost_panel.conf.inc" - -for file in $files; do - safe_copy $file /etc/nginx/conf.d/$file -done - - -if [ -f /etc/yunohost/installed ]; then - - 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) - safe_copy yunohost_local.conf \ - /etc/nginx/conf.d/$main_domain.d/yunohost_local.conf - - - # Remove old domains files - for file in /etc/nginx/conf.d/*.*.conf; do - domain=$(echo $file \ - | sed 's|/etc/nginx/conf.d/||' \ - | sed 's|.conf||') - [[ $domain_list =~ $domain ]] \ - || ([[ $(sudo yunohost service saferemove -s nginx $file) == "True" ]] \ - && (sudo rm -r /etc/nginx/conf.d/$domain.d || true)) - done - -else - [ ! -f /etc/nginx/sites-available/default ] \ - || sudo rm -f /etc/nginx/sites-enabled/default - need_restart=True -fi - -# Restart if need be -if [[ "$need_restart" == "True" ]]; then - sudo service nginx restart -else - sudo service nginx reload \ - || sudo service nginx restart -fi diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx new file mode 100755 index 000000000..c9b0a6baa --- /dev/null +++ b/data/hooks/conf_regen/15-nginx @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e + +do_pre_regen() { + pending_dir=$1 + + cd /usr/share/yunohost/templates/nginx + + nginx_dir="${pending_dir}/etc/nginx" + nginx_conf_dir="${nginx_dir}/conf.d" + mkdir -p "$nginx_conf_dir" + + # install plain conf files + cp plain/* "$nginx_conf_dir" + + # retrieve variables + main_domain=$(cat /etc/yunohost/current_host) + domain_list=$(sudo yunohost domain list --output-as plain --quiet) + + # add domain conf files + for domain in $domain_list; do + domain_conf_dir="${nginx_conf_dir}/${domain}.d" + mkdir -p "$domain_conf_dir" + + # NGINX server configuration + cat server.tpl.conf \ + | sed "s/{{ domain }}/${domain}/g" \ + > "${nginx_conf_dir}/${domain}.conf" + + [[ $main_domain != $domain ]] \ + && touch "${domain_conf_dir}/yunohost_local.conf" \ + || cp yunohost_local.conf "${domain_conf_dir}/yunohost_local.conf" + done + + # remove old domain conf files + conf_files=$(ls -1 /etc/nginx/conf.d \ + | awk '/^[^\.]+\.[^\.]+.*\.conf$/ { print $1 }') + for file in $conf_files; do + 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() { + # retrieve variables + # TODO: retrieve only changed/new domains + 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 + + # TODO: only restart if conf changed + sudo service nginx restart +} + +FORCE=$2 + +case "$1" in + pre) + do_pre_regen $3 + ;; + post) + do_post_regen + ;; + *) + echo "hook called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/data/templates/nginx/global.conf b/data/templates/nginx/plain/global.conf similarity index 100% rename from data/templates/nginx/global.conf rename to data/templates/nginx/plain/global.conf diff --git a/data/templates/nginx/ssowat.conf b/data/templates/nginx/plain/ssowat.conf similarity index 100% rename from data/templates/nginx/ssowat.conf rename to data/templates/nginx/plain/ssowat.conf diff --git a/data/templates/nginx/yunohost_admin.conf b/data/templates/nginx/plain/yunohost_admin.conf similarity index 100% rename from data/templates/nginx/yunohost_admin.conf rename to data/templates/nginx/plain/yunohost_admin.conf diff --git a/data/templates/nginx/yunohost_admin.conf.inc b/data/templates/nginx/plain/yunohost_admin.conf.inc similarity index 100% rename from data/templates/nginx/yunohost_admin.conf.inc rename to data/templates/nginx/plain/yunohost_admin.conf.inc diff --git a/data/templates/nginx/yunohost_api.conf.inc b/data/templates/nginx/plain/yunohost_api.conf.inc similarity index 100% rename from data/templates/nginx/yunohost_api.conf.inc rename to data/templates/nginx/plain/yunohost_api.conf.inc diff --git a/data/templates/nginx/yunohost_panel.conf.inc b/data/templates/nginx/plain/yunohost_panel.conf.inc similarity index 100% rename from data/templates/nginx/yunohost_panel.conf.inc rename to data/templates/nginx/plain/yunohost_panel.conf.inc diff --git a/data/templates/nginx/server.conf.sed b/data/templates/nginx/server.tpl.conf similarity index 100% rename from data/templates/nginx/server.conf.sed rename to data/templates/nginx/server.tpl.conf From bade8c564944befc2d344d862a88d5e58c688094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 16:46:22 +0200 Subject: [PATCH 47/86] [fix] Be less restrictive on domain name in metronome conf_regen hook --- data/hooks/conf_regen/12-metronome | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index 2fb03b015..251b9309d 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -30,7 +30,7 @@ do_pre_regen() { # remove old domain conf files conf_files=$(ls -1 /etc/metronome/conf.d \ - | awk '/^[a-z].*\.cfg\.lua$/ { print $1 }') + | awk '/^[^\.]+\.[^\.]+.*\.cfg\.lua$/ { print $1 }') for file in $conf_files; do domain=${file%.cfg.lua} [[ $domain_list =~ $domain ]] \ From 13a649b6efeef1c50460358e6b50fc0f8a56e17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 17:35:16 +0200 Subject: [PATCH 48/86] [enh] Append coma-separated successful conf changes to post conf_regen --- src/yunohost/service.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 7795cd453..4cd6b5d7a 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -431,7 +431,15 @@ def service_regen_conf(names=[], with_diff=False, force=False, } # Execute hooks for post-regen - hook_callback('conf_regen', names, args=['post', 1 if force else 0]) + def _pre_call(name, priority, path, args): + # append coma-separated successful changes for the service + if name in result and result[name]['succeed']: + args.append(','.join(result[name]['succeed'].keys())) + else: + args.append('') + return args + hook_callback('conf_regen', names, pre_callback=_pre_call, + args=['post', 1 if force else 0]) return result From 0d30ef62770e3d73744f6b29f1e89eb380726411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 17:52:45 +0200 Subject: [PATCH 49/86] [fix] Compare current with new LDAP backend and process as needed in conf_regen --- data/hooks/conf_regen/06-slapd | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 778b32eea..0a8b41a2a 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -23,25 +23,30 @@ do_pre_regen() { } do_post_regen() { - # retrieve current backend - #backend=$(sudo slapcat -n 0 | sed -n 's/^dn: olcDatabase={1}\(.*\),cn=config$/\1/p') - 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-${backend}-$(date +%s)" - if [[ -n "$backend" && "$backend" != "mdb" ]]; 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 + regen_conf_files=$1 # fix some permissions sudo chown root:openldap /etc/ldap/slapd.conf sudo chown -R openldap:openldap /etc/ldap/schema/ sudo chown -R openldap:openldap /etc/ldap/slapd.d/ + [ -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 @@ -73,7 +78,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 From 6c7e202321012525477d8a81d722c7257b67a9c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 18:05:41 +0200 Subject: [PATCH 50/86] [enh] Compile sieve script as needed and fix permissions in dovecot conf_regen --- data/hooks/conf_regen/25-dovecot | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/data/hooks/conf_regen/25-dovecot b/data/hooks/conf_regen/25-dovecot index 0a80dcb13..7dbeb5d68 100755 --- a/data/hooks/conf_regen/25-dovecot +++ b/data/hooks/conf_regen/25-dovecot @@ -29,20 +29,24 @@ do_pre_regen() { } do_post_regen() { + regen_conf_files=$1 + # create vmail user id vmail > /dev/null 2>&1 \ || sudo adduser --system --ingroup mail --uid 500 vmail - # compile sieve script - # TODO: only compile if script changed - sudo sievec /etc/dovecot/global_script/dovecot.sieve - # fix permissions sudo chown -R vmail:mail /etc/dovecot/global_script - sudo chmod -R 770 /etc/dovecot/global_script - sudo chmod 660 /etc/dovecot/global_script/dovecot.{sieve,svbin} + 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 + } - # TODO: only restart if conf changed sudo service dovecot restart } @@ -53,7 +57,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 From b3bb2c512dec5dd12d60d85c6ed63a1af696256b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 18:20:51 +0200 Subject: [PATCH 51/86] [enh] Compile sieve script as needed and fix permissions in rspamd conf_regen --- data/hooks/conf_regen/31-rspamd | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index 258535634..ff9786d2e 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -14,15 +14,19 @@ do_pre_regen() { } do_post_regen() { - # compile sieve script - # TODO: only compile and restart dovecot if script changed - sudo sievec /etc/dovecot/global_script/dovecot.sieve - # fix permissions and restart dovecot - sudo chown -R vmail:mail /etc/dovecot/global_script - sudo chmod 660 /etc/dovecot/global_script/rspamd.{sieve,svbin} - sudo systemctl restart dovecot + regen_conf_files=$1 - # TODO: only restart if conf changed + [ -z "$regen_conf_files" ] && exit 0 + + # compile sieve script + [[ "$regen_conf_files" =~ rspamd\.sieve ]] && { + sudo sievec /etc/dovecot/global_script/rspamd.sieve + sudo chown -R vmail:mail /etc/dovecot/global_script + 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 } @@ -34,7 +38,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 From b4e50f8af6cade4ea00908074e70ccd2249b7410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 18:24:41 +0200 Subject: [PATCH 52/86] [enh] Ensure that mysql is running and restart as needed in conf_regen hook --- data/hooks/conf_regen/34-mysql | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql index bed97b497..6f2f90b43 100755 --- a/data/hooks/conf_regen/34-mysql +++ b/data/hooks/conf_regen/34-mysql @@ -11,18 +11,25 @@ do_pre_regen() { } do_post_regen() { - # TODO: only restart if conf changed - sudo service mysql restart + regen_conf_files=$1 if [ ! -f /etc/yunohost/mysql ]; then # source string helpers . /usr/share/yunohost/helpers.d/string + # ensure that mysql is running + service mysql status >/dev/null 2>&1 \ + || service mysql start + + # generate a new root password mysql_password=$(ynh_string_random 10) sudo mysqladmin -u root -pyunohost password "$mysql_password" echo $mysql_password | sudo tee /etc/yunohost/mysql sudo chmod 400 /etc/yunohost/mysql fi + + [[ -z "$regen_conf_files" ]] \ + || sudo service mysql restart } FORCE=$2 @@ -32,7 +39,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 From 6c3de740aa3d63f9fcf163ce1b3cc7851dabb4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 18:36:20 +0200 Subject: [PATCH 53/86] [enh] Restart services as needed only in conf_regen hooks --- data/hooks/conf_regen/02-ssl | 4 +++- data/hooks/conf_regen/03-ssh | 10 +++++++--- data/hooks/conf_regen/09-nslcd | 7 +++++-- data/hooks/conf_regen/12-metronome | 17 +++++++++-------- data/hooks/conf_regen/15-nginx | 8 +++++--- data/hooks/conf_regen/19-postfix | 8 +++++--- data/hooks/conf_regen/28-rmilter | 23 +++++++++++++++-------- data/hooks/conf_regen/37-avahi-daemon | 7 +++++-- data/hooks/conf_regen/40-glances | 7 +++++-- data/hooks/conf_regen/43-dnsmasq | 7 ++++--- data/hooks/conf_regen/46-nsswitch | 7 +++++-- data/hooks/conf_regen/52-fail2ban | 7 +++++-- 12 files changed, 73 insertions(+), 39 deletions(-) diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index 5d14cb3d1..c58da53b4 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -13,6 +13,8 @@ do_pre_regen() { } do_post_regen() { + regen_conf_files=$1 + sudo mkdir -p "/etc/yunohost/certs/yunohost.org" sudo mkdir -p "${ssl_dir}/"{ca,certs,crl,newcerts} @@ -62,7 +64,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index bbf9591e5..b00e1ac33 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -18,8 +18,12 @@ do_pre_regen() { } do_post_regen() { - [[ -f /etc/yunohost/from_script ]] \ - || sudo service ssh restart + regen_conf_files=$1 + + if [[ ! -f /etc/yunohost/from_script ]]; then + [[ -z "$regen_conf_files" ]] \ + || sudo service ssh restart + fi } FORCE=$2 @@ -29,7 +33,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen/09-nslcd index 9ae1a8192..0cf3d3374 100755 --- a/data/hooks/conf_regen/09-nslcd +++ b/data/hooks/conf_regen/09-nslcd @@ -11,7 +11,10 @@ do_pre_regen() { } do_post_regen() { - sudo service nslcd restart + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ + || sudo service nslcd restart } FORCE=$2 @@ -21,7 +24,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index 251b9309d..767f9db4e 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -39,8 +39,13 @@ do_pre_regen() { } do_post_regen() { + regen_conf_files=$1 + + # fix some permissions + sudo chown -R metronome: /var/lib/metronome/ + sudo chown -R metronome: /etc/metronome/conf.d/ + # retrieve variables - # TODO: retrieve only changed/new domains domain_list=$(sudo yunohost domain list --output-as plain --quiet) # create metronome directories for domains @@ -48,12 +53,8 @@ do_post_regen() { sudo mkdir -p "/var/lib/metronome/${domain//./%2e}/pep" done - # fix permissions - sudo chown -R metronome: /var/lib/metronome/ - sudo chown -R metronome: /etc/metronome/conf.d/ - - # TODO: only restart if conf changed - sudo service metronome restart + [[ -z "$regen_conf_files" ]] \ + || sudo service metronome restart } FORCE=$2 @@ -63,7 +64,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index c9b0a6baa..2d8f73112 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -48,8 +48,11 @@ do_pre_regen() { } do_post_regen() { + regen_conf_files=$1 + + [ -z "$regen_conf_files" ] && exit 0 + # retrieve variables - # TODO: retrieve only changed/new domains domain_list=$(sudo yunohost domain list --output-as plain --quiet) # create NGINX conf directories for domains @@ -57,7 +60,6 @@ do_post_regen() { sudo mkdir -p "/etc/nginx/conf.d/${domain}.d" done - # TODO: only restart if conf changed sudo service nginx restart } @@ -68,7 +70,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index df62a0350..f7b702ae2 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -31,8 +31,10 @@ do_pre_regen() { } do_post_regen() { - # TODO: only restart if conf changed - sudo service postfix restart + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ + || sudo service postfix restart } FORCE=$2 @@ -42,7 +44,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index a46284bc8..0021f2b7e 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -12,16 +12,18 @@ do_pre_regen() { } do_post_regen() { + regen_conf_files=$1 + # retrieve variables - # TODO: retrieve only new domains domain_list=$(sudo yunohost domain list --output-as plain --quiet) # create DKIM key for domains for domain in $domain_list; do - [ ! -f /etc/dkim/$domain.mail.key ] && { + domain_key="/etc/dkim/${domain}.mail.key" + [ ! -f $domain_key ] && { sudo opendkim-genkey --domain="$domain" \ --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" } done @@ -30,10 +32,15 @@ do_post_regen() { sudo chown _rmilter /etc/dkim/*.mail.key sudo chmod 400 /etc/dkim/*.mail.key - # Reload systemd daemon, ensure that the socket is listening and stop - # the service. It will be started again by the socket as needed. - # TODO: only restart if conf changed - sudo systemctl -q daemon-reload + [ -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 } @@ -45,7 +52,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon index 6eba8c729..71e811e3f 100755 --- a/data/hooks/conf_regen/37-avahi-daemon +++ b/data/hooks/conf_regen/37-avahi-daemon @@ -11,7 +11,10 @@ do_pre_regen() { } do_post_regen() { - sudo service avahi-daemon restart + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ + || sudo service avahi-daemon restart } FORCE=$2 @@ -21,7 +24,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen/40-glances index 7fe40c2d8..d21405b3d 100755 --- a/data/hooks/conf_regen/40-glances +++ b/data/hooks/conf_regen/40-glances @@ -11,7 +11,10 @@ do_pre_regen() { } do_post_regen() { - sudo service glances restart + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ + || sudo service glances restart } FORCE=$2 @@ -21,7 +24,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index 100cd4b46..2d4de7b1c 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -41,8 +41,9 @@ do_pre_regen() { } do_post_regen() { - # TODO: only restart if conf changed - sudo service dnsmasq reload \ + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ || sudo service dnsmasq restart } @@ -53,7 +54,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen/46-nsswitch index 352346e68..b8a5b56ed 100755 --- a/data/hooks/conf_regen/46-nsswitch +++ b/data/hooks/conf_regen/46-nsswitch @@ -11,7 +11,10 @@ do_pre_regen() { } do_post_regen() { - sudo service nscd restart + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ + || sudo service nscd restart } FORCE=$2 @@ -21,7 +24,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/52-fail2ban b/data/hooks/conf_regen/52-fail2ban index 88af82326..16bedee0a 100755 --- a/data/hooks/conf_regen/52-fail2ban +++ b/data/hooks/conf_regen/52-fail2ban @@ -15,7 +15,10 @@ do_pre_regen() { } do_post_regen() { - sudo service fail2ban restart + regen_conf_files=$1 + + [[ -z "$regen_conf_files" ]] \ + || sudo service fail2ban restart } FORCE=$2 @@ -25,7 +28,7 @@ case "$1" in do_pre_regen $3 ;; post) - do_post_regen + do_post_regen $3 ;; *) echo "hook called with unknown argument \`$1'" >&2 From e8502f06c41c29aee7724ff19e8ac780cd14956c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 18:48:50 +0200 Subject: [PATCH 54/86] [fix] Remove old service_safecopy/saferemove from the actionsmap --- data/actionsmap/yunohost.yml | 38 ------------------------------------ 1 file changed, 38 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index eb3597078..7d4928b90 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -984,44 +984,6 @@ service: help: List pending configuration files and exit 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: - full: --force - help: Override the current configuration with the newly generated one, even if it has been modified - action: store_true - - ### service_saferemove() - saferemove: - action_help: > - Check if the specific file has been modified before removing it. - Backup the file in /home/yunohost.backup - arguments: - 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 - ############################# # Firewall # ############################# From d24cd494f3dbd12aaae5d77f5dd5608d6c8c6ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 19:32:18 +0200 Subject: [PATCH 55/86] [fix] Update package first install and call some conf_regen hooks with init --- data/hooks/conf_regen/01-yunohost | 19 +++--- data/hooks/conf_regen/02-ssl | 97 ++++++++++++++++++------------- data/hooks/conf_regen/15-nginx | 19 ++++++ debian/postinst | 7 +-- src/yunohost/tools.py | 5 +- 5 files changed, 94 insertions(+), 53 deletions(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 27268b5a9..0d4f4bcec 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -2,8 +2,11 @@ set -e -do_pre_regen() { - pending_dir=$1 +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 @@ -11,14 +14,14 @@ do_pre_regen() { # set default current_host [[ -f /etc/yunohost/current_host ]] \ - || echo "yunohost.org" | sudo tee /etc/yunohost/current_host + || echo "yunohost.org" > /etc/yunohost/current_host # copy default firewall and services # TODO: update them as needed with upgrades [[ -f /etc/yunohost/firewall.yml ]] \ - || sudo cp firewall.yml /etc/yunohost/firewall.yml + || cp firewall.yml /etc/yunohost/firewall.yml [[ -f /etc/yunohost/services.yml ]] \ - || sudo cp services.yml /etc/yunohost/services.yml + || cp services.yml /etc/yunohost/services.yml # allow users to access /media directory [[ -d /etc/skel/media ]] \ @@ -28,10 +31,10 @@ do_pre_regen() { FORCE=$2 case "$1" in - pre) - do_pre_regen $3 + pre|post) ;; - post) + init) + do_init_regen ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index c58da53b4..74600fc89 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -4,6 +4,59 @@ set -e ssl_dir="/usr/share/yunohost/yunohost-config/ssl/yunoCA" +do_init_regen() { + if [[ $EUID -ne 0 ]]; then + echo "You must be root to run this script" 1>&2 + exit 1 + 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 +} + do_pre_regen() { pending_dir=$1 @@ -15,46 +68,7 @@ do_pre_regen() { do_post_regen() { regen_conf_files=$1 - sudo mkdir -p "/etc/yunohost/certs/yunohost.org" - sudo mkdir -p "${ssl_dir}/"{ca,certs,crl,newcerts} - - [[ -f "${ssl_dir}/serial" ]] \ - || (echo "00" | sudo tee "${ssl_dir}/serial") - [[ -f "${ssl_dir}/index.txt" ]] \ - || sudo touch "${ssl_dir}/index.txt" - - if [[ ! -f /etc/yunohost/certs/yunohost.org/ca.pem ]]; then - sudo openssl req -x509 -new -config $ssl_dir/openssl.cnf \ - -days 3650 -out $ssl_dir/ca/cacert.pem \ - -keyout $ssl_dir/ca/cakey.pem -nodes -batch 2>&1 - sudo cp $ssl_dir/ca/cacert.pem \ - /etc/yunohost/certs/yunohost.org/ca.pem - sudo ln -sf /etc/yunohost/certs/yunohost.org/ca.pem \ - /etc/ssl/certs/ca-yunohost_crt.pem - sudo update-ca-certificates - fi - - if [[ ! -f /etc/yunohost/certs/yunohost.org/crt.pem ]]; then - 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 2>&1 - 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 2>&1 - - 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 + # TODO: regenerate certificates if conf changed? } FORCE=$2 @@ -66,6 +80,9 @@ case "$1" in post) do_post_regen $3 ;; + init) + do_init_regen + ;; *) echo "hook called with unknown argument \`$1'" >&2 exit 1 diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index 2d8f73112..29748fa70 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -2,6 +2,15 @@ set -e +do_init_regen() { + if [[ $EUID -ne 0 ]]; then + echo "You must be root to run this script" 1>&2 + exit 1 + fi + + do_pre_regen "" +} + do_pre_regen() { pending_dir=$1 @@ -14,6 +23,13 @@ do_pre_regen() { # install plain conf files cp plain/* "$nginx_conf_dir" + # 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 + # retrieve variables main_domain=$(cat /etc/yunohost/current_host) domain_list=$(sudo yunohost domain list --output-as plain --quiet) @@ -72,6 +88,9 @@ case "$1" in post) do_post_regen $3 ;; + init) + do_init_regen + ;; *) echo "hook called with unknown argument \`$1'" >&2 exit 1 diff --git a/debian/postinst b/debian/postinst index 34f6cd24e..b44530930 100644 --- a/debian/postinst +++ b/debian/postinst @@ -6,10 +6,9 @@ do_configure() { rm -rf /var/cache/moulinette/* if [ ! -f /etc/yunohost/installed ]; then - bash /usr/share/yunohost/hooks/conf_regen/01-yunohost True - bash /usr/share/yunohost/hooks/conf_regen/02-ssl True - bash /usr/share/yunohost/hooks/conf_regen/06-slapd True - bash /usr/share/yunohost/hooks/conf_regen/15-nginx True + bash /usr/share/yunohost/hooks/conf_regen/01-yunohost init + bash /usr/share/yunohost/hooks/conf_regen/02-ssl init + bash /usr/share/yunohost/hooks/conf_regen/15-nginx init else echo "Regenerating configuration, this might take a while..." yunohost service regenconf diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 3fcd3dd48..11af8c2db 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -177,6 +177,9 @@ def tools_postinstall(domain, password, ignore_dyndns=False): else: raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed')) + # Regenerate some services at first + service_regen_conf(['slapd'], force=True) + if len(domain.split('.')) >= 3 and not ignore_dyndns: try: r = requests.get('https://dyndns.yunohost.org/domains') @@ -503,4 +506,4 @@ def tools_diagnosis(auth, private=False): # Domains diagnosis['private']['domains'] = domain_list(auth)['domains'] - return diagnosis \ No newline at end of file + return diagnosis From 43f0fd5dbf91540ebcaa46dca74ae3877a44822a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 20:05:36 +0200 Subject: [PATCH 56/86] [deb] Do not output regen-conf result at postinst --- bin/yunohost | 2 +- debian/postinst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/yunohost b/bin/yunohost index cb663306c..0f2385c10 100755 --- a/bin/yunohost +++ b/bin/yunohost @@ -51,7 +51,7 @@ def _parse_cli_args(): help="Don't use actions map cache", ) parser.add_argument('--output-as', - choices=['json', 'plain'], default=None, + choices=['json', 'plain', 'none'], default=None, help="Output result in another format", ) parser.add_argument('--debug', diff --git a/debian/postinst b/debian/postinst index b44530930..8c4fff665 100644 --- a/debian/postinst +++ b/debian/postinst @@ -11,7 +11,7 @@ do_configure() { bash /usr/share/yunohost/hooks/conf_regen/15-nginx init else echo "Regenerating configuration, this might take a while..." - yunohost service regenconf + yunohost service regenconf --output-as none # restart yunohost-firewall if it's running service yunohost-firewall status >/dev/null \ From 2f1bf014e8c8c0dee99e5c439ea662330ce44e88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 21:29:02 +0200 Subject: [PATCH 57/86] [cli] Deprecate app_initdb action in flavour of helpers --- data/actionsmap/yunohost.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 9105e3daa..6163f58e0 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -516,6 +516,7 @@ app: initdb: action_help: Create database and initialize it with optionnal attached script api: POST /tools/initdb + deprecated: true arguments: user: help: Name of the DB user From c1115fc73605970103a10ebd5dccaa3d98e43413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sat, 16 Apr 2016 21:33:48 +0200 Subject: [PATCH 58/86] [fix] Rename 'deprecated' option to 'deprecated_alias' in actionsmap --- data/actionsmap/yunohost.yml | 2 +- debian/control | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 6163f58e0..6f8dc2747 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -966,7 +966,7 @@ service: api: PUT /services/regenconf configuration: lock: false - deprecated: + deprecated_alias: - regenconf arguments: -s: diff --git a/debian/control b/debian/control index b0585cc27..80f562e76 100644 --- a/debian/control +++ b/debian/control @@ -10,7 +10,7 @@ Homepage: https://yunohost.org/ Package: yunohost Architecture: all Depends: ${python:Depends}, ${misc:Depends} - , moulinette (>= 2.3.5) + , moulinette (>= 2.3.5.1) , python-psutil, python-requests, python-dnspython , python-apt, python-miniupnpc , glances From dcdf85260216c682aaf547356635b6b44c0e3ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 17 Apr 2016 16:59:49 +0200 Subject: [PATCH 59/86] [enh] Update services.yml in yunohost conf_regen and update its content --- data/hooks/conf_regen/01-yunohost | 75 ++++++++++++++++++++++++++-- data/templates/yunohost/services.yml | 11 ++-- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 0d4f4bcec..3583084a1 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -2,6 +2,8 @@ set -e +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 @@ -16,22 +18,85 @@ do_init_regen() { [[ -f /etc/yunohost/current_host ]] \ || echo "yunohost.org" > /etc/yunohost/current_host - # copy default firewall and services - # TODO: update them as needed with upgrades + # 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 - [[ -f /etc/yunohost/services.yml ]] \ - || cp services.yml /etc/yunohost/services.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 + else + sudo cp services.yml /etc/yunohost/services.yml + fi +} + +_update_services() { + sudo python2 - << EOF +import yaml +with open('services.yml') as f: + 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 case "$1" in - pre|post) + pre) + do_pre_regen $3 + ;; + post) ;; init) do_init_regen diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index f8dc324d3..b4e63479b 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -15,8 +15,13 @@ postfix: log: [/var/log/mail.log,/var/log/mail.err] rmilter: status: systemctl status rmilter.socket + log: /var/log/mail.log rspamd: status: systemctl status rspamd.socket + log: /var/log/mail.log +redis-server: + status: service + log: /var/log/redis/redis-server.log mysql: status: service log: [/var/log/mysql.log,/var/log/mysql.err] @@ -39,9 +44,6 @@ yunohost-api: log: /var/log/yunohost/yunohost-api.log yunohost-firewall: status: service -postgrey: - status: service - log: /var/log/mail.log nslcd: status: service log: /var/log/syslog @@ -49,3 +51,6 @@ nsswitch: status: service udisks2: status: service +amavis: null +postgrey: null +spamassassin: null From f96b2afca910bfc688455635ab25a2dd795e657e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 17 Apr 2016 17:03:10 +0200 Subject: [PATCH 60/86] [fix] Remove useless `email_legacy` conf_regen hook The file `/etc/yunohost/ervices.yml` is now updated thanks to 0bacf65, allowing to add new services and remove old ones. Moreover, while migrating to new YunoHost version, the user should execute `apt-get autoremove` to remove old packages such as spamassassin, amavis and postgrey. This sounds more reliable and allows to get rid of this useless hook which would be executed for ever, trying to stop already removed services. --- data/hooks/conf_regen-old/22-email-legacy | 43 ----------------------- 1 file changed, 43 deletions(-) delete mode 100644 data/hooks/conf_regen-old/22-email-legacy diff --git a/data/hooks/conf_regen-old/22-email-legacy b/data/hooks/conf_regen-old/22-email-legacy deleted file mode 100644 index 9c58f9349..000000000 --- a/data/hooks/conf_regen-old/22-email-legacy +++ /dev/null @@ -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 From b4cbe1986558c97e0d4d354abf4b5bbcf3979716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 19 Apr 2016 11:42:56 +0200 Subject: [PATCH 61/86] [enh] Use more precise keys in service_regen_conf output --- src/yunohost/service.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 4cd6b5d7a..ebda88379 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -426,15 +426,15 @@ def service_regen_conf(names=[], with_diff=False, force=False, # Append the service results result[service] = { - 'succeed': succeed_regen, - 'failed': failed_regen + 'applied': succeed_regen, + 'pending': failed_regen } # Execute hooks for post-regen def _pre_call(name, priority, path, args): - # append coma-separated successful changes for the service - if name in result and result[name]['succeed']: - args.append(','.join(result[name]['succeed'].keys())) + # append coma-separated applied changes for the service + if name in result and result[name]['applied']: + args.append(','.join(result[name]['applied'].keys())) else: args.append('') return args From c81393425a85f9ccc85a1a008fe9ef1432e78647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 19 Apr 2016 20:10:02 +0200 Subject: [PATCH 62/86] [fix] Define common hooks arguments once in service_regen_conf --- src/yunohost/service.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index ebda88379..38726425c 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -302,13 +302,17 @@ def service_regen_conf(names=[], with_diff=False, force=False, 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,] + # 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', 1 if force else 0, service_pending_path] + return pre_args + [service_pending_path,] pre_result = hook_callback('conf_regen', names, pre_callback=_pre_call) # Update the services name @@ -431,15 +435,15 @@ def service_regen_conf(names=[], with_diff=False, force=False, } # 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']: - args.append(','.join(result[name]['applied'].keys())) + regen_conf_files = ','.join(result[name]['applied'].keys()) else: - args.append('') - return args - hook_callback('conf_regen', names, pre_callback=_pre_call, - args=['post', 1 if force else 0]) + regen_conf_files = '' + return post_args + [regen_conf_files,] + hook_callback('conf_regen', names, pre_callback=_pre_call) return result From 26adf050428d7debcaa9c7e6e8d69e11397c376b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 19 Apr 2016 20:24:49 +0200 Subject: [PATCH 63/86] [fix] Ensure that pending conf file is deleted after it's applied --- src/yunohost/service.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 38726425c..e82acae6c 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -373,7 +373,6 @@ def service_regen_conf(names=[], with_diff=False, force=False, logger.debug("> system conf is not managed yet") if system_hash == new_hash: logger.debug("> no changes to system conf has been made") - os.remove(pending_path) conf_status = 'managed' regenerated = True elif force and to_remove: @@ -416,6 +415,8 @@ def service_regen_conf(names=[], with_diff=False, force=False, 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 @@ -650,7 +651,7 @@ def _process_regen_conf(system_conf, new_conf=None, save=True): system_dir = os.path.dirname(system_conf) if not os.path.isdir(system_dir): filesystem.mkdir(system_dir, 0755, True) - shutil.move(new_conf, system_conf) + shutil.copy2(new_conf, system_conf) logger.info(m18n.n('service_conf_file_updated', conf=system_conf)) except: From cdf3c13bf5bfd86adc9f5f08cbb57071eec7cef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 19 Apr 2016 20:52:45 +0200 Subject: [PATCH 64/86] [enh] Add a dry-run option for service_regen_conf --- data/actionsmap/yunohost.yml | 4 +++ data/hooks/conf_regen/01-yunohost | 3 ++- data/hooks/conf_regen/02-ssl | 5 ++-- data/hooks/conf_regen/03-ssh | 5 ++-- data/hooks/conf_regen/06-slapd | 5 ++-- data/hooks/conf_regen/09-nslcd | 5 ++-- data/hooks/conf_regen/12-metronome | 5 ++-- data/hooks/conf_regen/15-nginx | 5 ++-- data/hooks/conf_regen/19-postfix | 5 ++-- data/hooks/conf_regen/25-dovecot | 5 ++-- data/hooks/conf_regen/28-rmilter | 5 ++-- data/hooks/conf_regen/31-rspamd | 5 ++-- data/hooks/conf_regen/34-mysql | 5 ++-- data/hooks/conf_regen/37-avahi-daemon | 5 ++-- data/hooks/conf_regen/40-glances | 5 ++-- data/hooks/conf_regen/43-dnsmasq | 5 ++-- data/hooks/conf_regen/46-nsswitch | 5 ++-- data/hooks/conf_regen/52-fail2ban | 5 ++-- locales/en.json | 2 ++ src/yunohost/service.py | 38 ++++++++++++++++++--------- 20 files changed, 82 insertions(+), 45 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 525c0c1da..f51e93bd8 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -980,6 +980,10 @@ service: Override all manual modifications in configuration files action: store_true + -n: + full: --dry-run + help: Show what would have been regenerated + action: store_true -p: full: --list-pending help: List pending configuration files and exit diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 3583084a1..78cb92b1b 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -91,10 +91,11 @@ EOF } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) ;; diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index 74600fc89..758e61b15 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -72,13 +72,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; init) do_init_regen diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index b00e1ac33..b0cac73f7 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -27,13 +27,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 0a8b41a2a..08b3def6e 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -72,13 +72,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen/09-nslcd index 0cf3d3374..d0338ad6c 100755 --- a/data/hooks/conf_regen/09-nslcd +++ b/data/hooks/conf_regen/09-nslcd @@ -18,13 +18,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index 767f9db4e..cabbe8912 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -58,13 +58,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index 29748fa70..8c2fae2db 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -80,13 +80,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; init) do_init_regen diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index f7b702ae2..66a92f9a9 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -38,13 +38,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/25-dovecot b/data/hooks/conf_regen/25-dovecot index 7dbeb5d68..344f43222 100755 --- a/data/hooks/conf_regen/25-dovecot +++ b/data/hooks/conf_regen/25-dovecot @@ -51,13 +51,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index 0021f2b7e..187d893c9 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -46,13 +46,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index ff9786d2e..5e0cb0bd1 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -32,13 +32,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql index 6f2f90b43..c247acbf1 100755 --- a/data/hooks/conf_regen/34-mysql +++ b/data/hooks/conf_regen/34-mysql @@ -33,13 +33,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon index 71e811e3f..8ce180551 100755 --- a/data/hooks/conf_regen/37-avahi-daemon +++ b/data/hooks/conf_regen/37-avahi-daemon @@ -18,13 +18,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen/40-glances index d21405b3d..e610eca2d 100755 --- a/data/hooks/conf_regen/40-glances +++ b/data/hooks/conf_regen/40-glances @@ -18,13 +18,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index 2d4de7b1c..b773cb20a 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -48,13 +48,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen/46-nsswitch index b8a5b56ed..e2865c867 100755 --- a/data/hooks/conf_regen/46-nsswitch +++ b/data/hooks/conf_regen/46-nsswitch @@ -18,13 +18,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/data/hooks/conf_regen/52-fail2ban b/data/hooks/conf_regen/52-fail2ban index 16bedee0a..3b01aead7 100755 --- a/data/hooks/conf_regen/52-fail2ban +++ b/data/hooks/conf_regen/52-fail2ban @@ -22,13 +22,14 @@ do_post_regen() { } FORCE=$2 +DRY_RUN=$3 case "$1" in pre) - do_pre_regen $3 + do_pre_regen $4 ;; post) - do_post_regen $3 + do_post_regen $4 ;; *) echo "hook called with unknown argument \`$1'" >&2 diff --git a/locales/en.json b/locales/en.json index c33c78c73..90f9e46e5 100644 --- a/locales/en.json +++ b/locales/en.json @@ -130,6 +130,7 @@ "service_cmd_exec_failed" : "Unable to execute command '{command:s}'", "service_regenconf_failed" : "Unable to regenerate the configuration for service(s): {services}", "service_regenconf_pending_applying" : "Applying pending configuration for service '{service}'...", + "service_regenconf_dry_pending_applying" : "Checking pending configuration which would have been applied for service '{service}'...", "service_conf_file_manually_removed" : "The configuration file '{conf}' has been manually removed and will not be created", "service_conf_file_manually_modified" : "The configuration file '{conf}' has been manually modified and will not be updated", "service_conf_file_not_managed" : "The configuration file '{conf}' is not managed yet and will not be updated", @@ -140,6 +141,7 @@ "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_ko" : "Outbound mail (SMTP port 25) seems to be blocked by your network", diff --git a/src/yunohost/service.py b/src/yunohost/service.py index e82acae6c..95874f99a 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -271,7 +271,7 @@ def service_log(name, number=50): return result -def service_regen_conf(names=[], with_diff=False, 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 @@ -280,6 +280,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, names -- Services name to regenerate configuration of with_diff -- Show differences in case of configuration changes force -- Override all manual modifications in configuration files + dry_run -- Show what would have been regenerated list_pending -- List pending configuration files and exit """ @@ -303,7 +304,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, filesystem.mkdir(pending_conf_dir, 0755, True) # Format common hooks arguments - common_args = [1 if force else 0,] + common_args = [1 if force else 0, 1 if dry_run else 0] # Execute hooks for pre-regen pre_args = ['pre',] + common_args @@ -322,10 +323,16 @@ def service_regen_conf(names=[], with_diff=False, force=False, 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', - service=service)) + 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 = {} @@ -361,7 +368,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, else: logger.debug("> system conf does not exist yet") conf_status = 'created' - regenerated = _process_regen_conf( + regenerated = _regen( system_path, pending_path, save=False) else: logger.warning(m18n.n( @@ -376,10 +383,10 @@ def service_regen_conf(names=[], with_diff=False, force=False, conf_status = 'managed' regenerated = True elif force and to_remove: - regenerated = _process_regen_conf(system_path) + regenerated = _regen(system_path) conf_status = 'force-removed' elif force: - regenerated = _process_regen_conf(system_path, pending_path) + regenerated = _regen(system_path, pending_path) conf_status = 'force-updated' else: logger.warning(m18n.n('service_conf_file_not_managed', @@ -388,10 +395,10 @@ def service_regen_conf(names=[], with_diff=False, force=False, # -> system conf has not been manually modified elif system_hash == current_hash: if to_remove: - regenerated = _process_regen_conf(system_path) + regenerated = _regen(system_path) conf_status = 'removed' elif system_hash != new_hash: - regenerated = _process_regen_conf(system_path, pending_path) + regenerated = _regen(system_path, pending_path) conf_status = 'updated' else: logger.debug("> system conf is already up-to-date") @@ -400,7 +407,7 @@ def service_regen_conf(names=[], with_diff=False, force=False, else: logger.debug("> system conf has been manually modified") if force: - regenerated = _process_regen_conf(system_path, pending_path) + regenerated = _regen(system_path, pending_path) conf_status = 'force-updated' else: logger.warning(m18n.n( @@ -425,8 +432,11 @@ def service_regen_conf(names=[], with_diff=False, force=False, logger.info(m18n.n('service_conf_up_to_date', service=service)) continue elif not failed_regen: - logger.success(m18n.n('service_conf_updated', service=service)) - if succeed_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 @@ -435,6 +445,10 @@ def service_regen_conf(names=[], with_diff=False, force=False, '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): From 7d2e7cb5e26fc61167b5a19282074955a566633b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Thu, 21 Apr 2016 21:57:35 +0200 Subject: [PATCH 65/86] [fix] Get pending conf for all services if no one is provided --- src/yunohost/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 95874f99a..55047bd4d 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -604,7 +604,7 @@ def _get_pending_conf(services=[]): if not os.path.isdir(pending_conf_dir): return result if not services: - os.listdir(pending_conf_dir) + services = os.listdir(pending_conf_dir) for name in services: service_conf = {} service_pending_path = os.path.join(pending_conf_dir, name) From 053d3efd793018fb67318e3cfe5fdbf6da00e112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Thu, 21 Apr 2016 21:58:55 +0200 Subject: [PATCH 66/86] [deb] Use new regen-conf action name in postinst --- debian/postinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/postinst b/debian/postinst index 8c4fff665..7c730987c 100644 --- a/debian/postinst +++ b/debian/postinst @@ -11,7 +11,7 @@ do_configure() { bash /usr/share/yunohost/hooks/conf_regen/15-nginx init else echo "Regenerating configuration, this might take a while..." - yunohost service regenconf --output-as none + yunohost service regen-conf --output-as none # restart yunohost-firewall if it's running service yunohost-firewall status >/dev/null \ From 0c609f57092c92c2cdd1864101610e4d1931da21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Thu, 21 Apr 2016 22:57:29 +0200 Subject: [PATCH 67/86] [fix] Create /etc/dkim if not exists in rmilter conf_regen hook --- data/hooks/conf_regen/28-rmilter | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index 187d893c9..fbad7c20f 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -17,6 +17,9 @@ do_post_regen() { # retrieve variables domain_list=$(sudo yunohost domain list --output-as plain --quiet) + # create DKIM directory + [[ -f /etc/dkim ]] || sudo mkdir /etc/dkim + # create DKIM key for domains for domain in $domain_list; do domain_key="/etc/dkim/${domain}.mail.key" From 4912a2afae4e25049ead033c03f0f962a80424a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Thu, 21 Apr 2016 22:59:36 +0200 Subject: [PATCH 68/86] [fix] Init LDAP with package installation to fix ynh post-install --- data/hooks/conf_regen/06-slapd | 27 ++++++++++++++++++++++ debian/postinst | 1 + src/yunohost/tools.py | 42 ++++++++++++++++------------------ 3 files changed, 48 insertions(+), 22 deletions(-) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 08b3def6e..6211ebe28 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -2,6 +2,30 @@ set -e +do_init_regen() { + if [[ $EUID -ne 0 ]]; then + echo "You must be root to run this script" 1>&2 + exit 1 + fi + + do_pre_regen "" + + # fix some permissions + chown root:openldap /etc/ldap/slapd.conf + chown -R openldap:openldap /etc/ldap/schema/ + + # check the slapd config file at first + slaptest -Q -u -f /etc/ldap/slapd.conf + + # regenerate LDAP config directory from slapd.conf + rm -Rf /etc/ldap/slapd.d + 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 @@ -81,6 +105,9 @@ case "$1" in post) do_post_regen $4 ;; + init) + do_init_regen + ;; *) echo "hook called with unknown argument \`$1'" >&2 exit 1 diff --git a/debian/postinst b/debian/postinst index 7c730987c..c67d432ab 100644 --- a/debian/postinst +++ b/debian/postinst @@ -8,6 +8,7 @@ do_configure() { if [ ! -f /etc/yunohost/installed ]; then bash /usr/share/yunohost/hooks/conf_regen/01-yunohost init bash /usr/share/yunohost/hooks/conf_regen/02-ssl init + bash /usr/share/yunohost/hooks/conf_regen/06-slapd init bash /usr/share/yunohost/hooks/conf_regen/15-nginx init else echo "Regenerating configuration, this might take a while..." diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index 11af8c2db..f78e32363 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -170,16 +170,10 @@ def tools_postinstall(domain, password, ignore_dyndns=False): """ dyndns = not ignore_dyndns - try: - with open('/etc/yunohost/installed') as f: pass - except IOError: - logger.info(m18n.n('yunohost_installing')) - else: - raise MoulinetteError(errno.EPERM, m18n.n('yunohost_already_installed')) - - # Regenerate some services at first - service_regen_conf(['slapd'], force=True) - + # Do some checks at first + if os.path.isfile('/etc/yunohost/installed'): + raise MoulinetteError(errno.EPERM, + m18n.n('yunohost_already_installed')) if len(domain.split('.')) >= 3 and not ignore_dyndns: try: r = requests.get('https://dyndns.yunohost.org/domains') @@ -190,10 +184,23 @@ def tools_postinstall(domain, password, ignore_dyndns=False): dyndomain = '.'.join(domain.split('.')[1:]) if dyndomain in dyndomains: if requests.get('https://dyndns.yunohost.org/test/%s' % domain).status_code == 200: - dyndns=True + dyndns = True else: 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 folders_to_create = [ @@ -233,6 +240,7 @@ def tools_postinstall(domain, password, ignore_dyndns=False): os.system('chmod 644 /etc/ssowat/conf.json.persistent') # Create SSL CA + service_regen_conf(['ssl'], force=True) ssl_dir = '/usr/share/yunohost/yunohost-config/ssl/yunoCA' command_list = [ 'echo "01" > %s/serial' % ssl_dir, @@ -250,16 +258,6 @@ def tools_postinstall(domain, password, ignore_dyndns=False): raise MoulinetteError(errno.EPERM, 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 tools_maindomain(auth, old_domain='yunohost.org', new_domain=domain, dyndns=dyndns) From 528e39fd846b9d2a3b83b8fe5caf9a8d84b2fb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Tue, 19 Apr 2016 16:08:36 +0200 Subject: [PATCH 69/86] [enh] Add a yunopaste script to paste data to YunoHost Haste server --- bin/yunopaste | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100755 bin/yunopaste diff --git a/bin/yunopaste b/bin/yunopaste new file mode 100755 index 000000000..d52199eba --- /dev/null +++ b/bin/yunopaste @@ -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 "${@}" From 3ecd22c372004f7b783caff0f9c1c090b68325fd Mon Sep 17 00:00:00 2001 From: Moul Date: Fri, 22 Apr 2016 00:04:26 +0200 Subject: [PATCH 70/86] [fix] use '-p' option of mkdir command to check '/etc/dkim' folder exist at its creation. --- data/hooks/conf_regen/28-rmilter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index fbad7c20f..e34ceeeea 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -18,7 +18,7 @@ do_post_regen() { domain_list=$(sudo yunohost domain list --output-as plain --quiet) # create DKIM directory - [[ -f /etc/dkim ]] || sudo mkdir /etc/dkim + sudo mkdir -p /etc/dkim # create DKIM key for domains for domain in $domain_list; do From 13ed77a6ca86f9f495da1ed376e6913e90e671b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 12:18:25 +0200 Subject: [PATCH 71/86] [fix] Set default values for force and dry_run in conf_regen hooks --- data/hooks/conf_regen/01-yunohost | 4 ++-- data/hooks/conf_regen/02-ssl | 4 ++-- data/hooks/conf_regen/03-ssh | 4 ++-- data/hooks/conf_regen/06-slapd | 4 ++-- data/hooks/conf_regen/09-nslcd | 4 ++-- data/hooks/conf_regen/12-metronome | 4 ++-- data/hooks/conf_regen/15-nginx | 4 ++-- data/hooks/conf_regen/19-postfix | 4 ++-- data/hooks/conf_regen/25-dovecot | 4 ++-- data/hooks/conf_regen/28-rmilter | 4 ++-- data/hooks/conf_regen/31-rspamd | 4 ++-- data/hooks/conf_regen/34-mysql | 4 ++-- data/hooks/conf_regen/37-avahi-daemon | 4 ++-- data/hooks/conf_regen/40-glances | 4 ++-- data/hooks/conf_regen/43-dnsmasq | 4 ++-- data/hooks/conf_regen/46-nsswitch | 4 ++-- data/hooks/conf_regen/52-fail2ban | 4 ++-- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/data/hooks/conf_regen/01-yunohost b/data/hooks/conf_regen/01-yunohost index 78cb92b1b..96b62fe67 100755 --- a/data/hooks/conf_regen/01-yunohost +++ b/data/hooks/conf_regen/01-yunohost @@ -90,8 +90,8 @@ if updated: EOF } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index 758e61b15..e3e30cb19 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -71,8 +71,8 @@ do_post_regen() { # TODO: regenerate certificates if conf changed? } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index b0cac73f7..6fb6953ce 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -26,8 +26,8 @@ do_post_regen() { fi } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 6211ebe28..f4bda09da 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -95,8 +95,8 @@ do_post_regen() { sudo service slapd force-reload } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen/09-nslcd index d0338ad6c..a008c4288 100755 --- a/data/hooks/conf_regen/09-nslcd +++ b/data/hooks/conf_regen/09-nslcd @@ -17,8 +17,8 @@ do_post_regen() { || sudo service nslcd restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index cabbe8912..4214722fc 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -57,8 +57,8 @@ do_post_regen() { || sudo service metronome restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index 8c2fae2db..9624878eb 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -79,8 +79,8 @@ do_post_regen() { sudo service nginx restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 66a92f9a9..3cb5cdf50 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -37,8 +37,8 @@ do_post_regen() { || sudo service postfix restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/25-dovecot b/data/hooks/conf_regen/25-dovecot index 344f43222..37552c16b 100755 --- a/data/hooks/conf_regen/25-dovecot +++ b/data/hooks/conf_regen/25-dovecot @@ -50,8 +50,8 @@ do_post_regen() { sudo service dovecot restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index e34ceeeea..9d6df61d7 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -48,8 +48,8 @@ do_post_regen() { sudo systemctl -q stop rmilter.service 2>&1 || true } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index 5e0cb0bd1..1ea777de8 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -31,8 +31,8 @@ do_post_regen() { sudo systemctl -q stop rspamd.service 2>&1 || true } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql index c247acbf1..bd5b5d69d 100755 --- a/data/hooks/conf_regen/34-mysql +++ b/data/hooks/conf_regen/34-mysql @@ -32,8 +32,8 @@ do_post_regen() { || sudo service mysql restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon index 8ce180551..113e9e0c7 100755 --- a/data/hooks/conf_regen/37-avahi-daemon +++ b/data/hooks/conf_regen/37-avahi-daemon @@ -17,8 +17,8 @@ do_post_regen() { || sudo service avahi-daemon restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen/40-glances index e610eca2d..e7d6b07b9 100755 --- a/data/hooks/conf_regen/40-glances +++ b/data/hooks/conf_regen/40-glances @@ -17,8 +17,8 @@ do_post_regen() { || sudo service glances restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index b773cb20a..6947fb634 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -47,8 +47,8 @@ do_post_regen() { || sudo service dnsmasq restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen/46-nsswitch index e2865c867..9cbcae13d 100755 --- a/data/hooks/conf_regen/46-nsswitch +++ b/data/hooks/conf_regen/46-nsswitch @@ -17,8 +17,8 @@ do_post_regen() { || sudo service nscd restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) diff --git a/data/hooks/conf_regen/52-fail2ban b/data/hooks/conf_regen/52-fail2ban index 3b01aead7..1c262078b 100755 --- a/data/hooks/conf_regen/52-fail2ban +++ b/data/hooks/conf_regen/52-fail2ban @@ -21,8 +21,8 @@ do_post_regen() { || sudo service fail2ban restart } -FORCE=$2 -DRY_RUN=$3 +FORCE=${2:-0} +DRY_RUN=${3:-0} case "$1" in pre) From 0065a7512da7a985cc99bc978b457ecf3ec1fe77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 12:21:19 +0200 Subject: [PATCH 72/86] [enh] Force new MySQL password set if it's unknown at regen-conf --- data/hooks/conf_regen/34-mysql | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql index bd5b5d69d..1b84c8aba 100755 --- a/data/hooks/conf_regen/34-mysql +++ b/data/hooks/conf_regen/34-mysql @@ -14,17 +14,39 @@ do_post_regen() { regen_conf_files=$1 if [ ! -f /etc/yunohost/mysql ]; then - # source string helpers . /usr/share/yunohost/helpers.d/string # ensure that mysql is running service mysql status >/dev/null 2>&1 \ || service mysql start - # generate a new root password + # generate and set new root password mysql_password=$(ynh_string_random 10) - sudo mysqladmin -u root -pyunohost password "$mysql_password" - echo $mysql_password | sudo tee /etc/yunohost/mysql + sudo mysqladmin -s -u root -pyunohost password "$mysql_password" || { + if [ $FORCE -eq 1 ]; then + . /usr/share/yunohost/helpers.d/package + + # 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 "MySQL password for root user has been manually changed" \ + "and will not be overridden without --force" >&2 + fi + } + + # store new root password + echo "$mysql_password" | sudo tee /etc/yunohost/mysql sudo chmod 400 /etc/yunohost/mysql fi From 5af988052d14dec6cf6be979d1b4e42249018fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 13:04:51 +0200 Subject: [PATCH 73/86] [fix] Restore MySQL password for root user (bugfix #194) --- data/hooks/backup/11-conf_ynh_mysql | 2 +- data/hooks/restore/11-conf_ynh_mysql | 39 +++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/data/hooks/backup/11-conf_ynh_mysql b/data/hooks/backup/11-conf_ynh_mysql index 5789901f1..435ba2807 100644 --- a/data/hooks/backup/11-conf_ynh_mysql +++ b/data/hooks/backup/11-conf_ynh_mysql @@ -1,4 +1,4 @@ backup_dir="$1/conf/ynh/mysql" sudo mkdir -p $backup_dir -sudo cp -a /etc/yunohost/mysql $backup_dir/ +sudo cp -a /etc/yunohost/mysql "${backup_dir}/root_pwd" diff --git a/data/hooks/restore/11-conf_ynh_mysql b/data/hooks/restore/11-conf_ynh_mysql index e4d71b369..8d6dc53dc 100644 --- a/data/hooks/restore/11-conf_ynh_mysql +++ b/data/hooks/restore/11-conf_ynh_mysql @@ -1,6 +1,37 @@ backup_dir="$1/conf/ynh/mysql" -sudo service mysql restart -sudo cp -a $backup_dir/mysql /etc/yunohost/mysql -mysqlpwd=$(sudo cat /etc/yunohost/mysql) -sudo mysqladmin flush-privileges -p"$mysqlpwd" +# ensure that mysql is running +service mysql status >/dev/null 2>&1 \ + || service mysql start + +# 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 + + # 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 From 689b390342afc32fd436d091252d15a3f71a49ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 14:54:41 +0200 Subject: [PATCH 74/86] [fix] Restore current_host and use only one backup path for it --- data/hooks/backup/40-conf_ynh_currenthost | 7 ++----- data/hooks/restore/40-conf_ynh_currenthost | 2 ++ src/yunohost/backup.py | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/data/hooks/backup/40-conf_ynh_currenthost b/data/hooks/backup/40-conf_ynh_currenthost index a91a50001..af054cad4 100644 --- a/data/hooks/backup/40-conf_ynh_currenthost +++ b/data/hooks/backup/40-conf_ynh_currenthost @@ -1,7 +1,4 @@ -backup_dir="$1/conf/ynh/" -backup_dir_legacy="$1/yunohost/" +backup_dir="$1/conf/ynh" 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_legacy +sudo cp -a /etc/yunohost/current_host "${backup_dir}/current_host" diff --git a/data/hooks/restore/40-conf_ynh_currenthost b/data/hooks/restore/40-conf_ynh_currenthost index 8b1378917..a0bdf94d3 100644 --- a/data/hooks/restore/40-conf_ynh_currenthost +++ b/data/hooks/restore/40-conf_ynh_currenthost @@ -1 +1,3 @@ +backup_dir="$1/conf/ynh" +sudo cp -a "${backup_dir}/current_host" /etc/yunohost/current_host diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 94b15ff42..430ecd570 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -375,11 +375,11 @@ def backup_restore(auth, name, hooks=[], ignore_hooks=False, else: # Retrieve the domain from the backup 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() except IOError: - logger.debug("unable to retrieve domain from " - "'%s/yunohost/current_host'", tmp_dir, exc_info=1) + logger.debug("unable to retrieve current_host from the backup", + exc_info=1) raise MoulinetteError(errno.EIO, m18n.n('backup_invalid_archive')) logger.debug("executing the post-install...") From 31c8aabe861f53bb1098ec94de7c2606fbbe80c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 15:17:56 +0200 Subject: [PATCH 75/86] [fix] Set proper mode when using install in conf_regen hooks --- data/hooks/conf_regen/02-ssl | 2 +- data/hooks/conf_regen/03-ssh | 2 +- data/hooks/conf_regen/06-slapd | 13 +++++++------ data/hooks/conf_regen/09-nslcd | 2 +- data/hooks/conf_regen/28-rmilter | 4 ++-- data/hooks/conf_regen/31-rspamd | 4 ++-- data/hooks/conf_regen/34-mysql | 2 +- data/hooks/conf_regen/37-avahi-daemon | 3 ++- data/hooks/conf_regen/40-glances | 2 +- data/hooks/conf_regen/46-nsswitch | 2 +- 10 files changed, 19 insertions(+), 17 deletions(-) diff --git a/data/hooks/conf_regen/02-ssl b/data/hooks/conf_regen/02-ssl index e3e30cb19..60acdbbbb 100755 --- a/data/hooks/conf_regen/02-ssl +++ b/data/hooks/conf_regen/02-ssl @@ -62,7 +62,7 @@ do_pre_regen() { cd /usr/share/yunohost/templates/ssl - install -D openssl.cnf "${pending_dir}/${ssl_dir}/openssl.cnf" + install -D -m 644 openssl.cnf "${pending_dir}/${ssl_dir}/openssl.cnf" } do_post_regen() { diff --git a/data/hooks/conf_regen/03-ssh b/data/hooks/conf_regen/03-ssh index 6fb6953ce..a469b7a66 100755 --- a/data/hooks/conf_regen/03-ssh +++ b/data/hooks/conf_regen/03-ssh @@ -13,7 +13,7 @@ do_pre_regen() { [[ -f /proc/net/if_inet6 ]] \ || sed -i "s/ListenAddress ::/#ListenAddress ::/g" sshd_config - install -D sshd_config "${pending_dir}/etc/ssh/sshd_config" + install -D -m 644 sshd_config "${pending_dir}/etc/ssh/sshd_config" fi } diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index f4bda09da..4316de5e4 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -35,15 +35,16 @@ do_pre_regen() { [ ! -f /etc/ldap/slapd-yuno.conf ] \ || touch "${pending_dir}/etc/ldap/slapd-yuno.conf" + # create needed directories ldap_dir="${pending_dir}/etc/ldap" - install -D ldap.conf "${ldap_dir}/ldap.conf" - install -D slapd.conf "${ldap_dir}/slapd.conf" - schema_dir="${ldap_dir}/schema" - install -D sudo.schema "${schema_dir}/sudo.schema" - install -D mailserver.schema "${schema_dir}/mailserver.schema" + mkdir -p "$ldap_dir" "$schema_dir" - install -D slapd.default "${pending_dir}/etc/default/slapd" + # copy configuration files + cp -a ldap.conf slapd.conf "$ldap_dir" + cp -a sudo.schema mailserver.schema "$schema_dir" + + install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd" } do_post_regen() { diff --git a/data/hooks/conf_regen/09-nslcd b/data/hooks/conf_regen/09-nslcd index a008c4288..5071ac1fd 100755 --- a/data/hooks/conf_regen/09-nslcd +++ b/data/hooks/conf_regen/09-nslcd @@ -7,7 +7,7 @@ do_pre_regen() { cd /usr/share/yunohost/templates/nslcd - install -D nslcd.conf "${pending_dir}/etc/nslcd.conf" + install -D -m 644 nslcd.conf "${pending_dir}/etc/nslcd.conf" } do_post_regen() { diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index 9d6df61d7..bc225d530 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -7,8 +7,8 @@ do_pre_regen() { cd /usr/share/yunohost/templates/rmilter - install -D rmilter.conf "${pending_dir}/etc/rmilter.conf" - install -D rmilter.socket "${pending_dir}/etc/rmilter.socket" + install -D -m 644 rmilter.conf "${pending_dir}/etc/rmilter.conf" + install -D -m 644 rmilter.socket "${pending_dir}/etc/rmilter.socket" } do_post_regen() { diff --git a/data/hooks/conf_regen/31-rspamd b/data/hooks/conf_regen/31-rspamd index 1ea777de8..327bedef1 100755 --- a/data/hooks/conf_regen/31-rspamd +++ b/data/hooks/conf_regen/31-rspamd @@ -7,9 +7,9 @@ do_pre_regen() { cd /usr/share/yunohost/templates/rspamd - install -D metrics.local.conf \ + install -D -m 644 metrics.local.conf \ "${pending_dir}/etc/rspamd/local.d/metrics.conf" - install -D rspamd.sieve \ + install -D -m 644 rspamd.sieve \ "${pending_dir}/etc/dovecot/global_script/rspamd.sieve" } diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql index 1b84c8aba..7905035fb 100755 --- a/data/hooks/conf_regen/34-mysql +++ b/data/hooks/conf_regen/34-mysql @@ -7,7 +7,7 @@ do_pre_regen() { cd /usr/share/yunohost/templates/mysql - install -D my.cnf "${pending_dir}/etc/mysql/my.cnf" + install -D -m 644 my.cnf "${pending_dir}/etc/mysql/my.cnf" } do_post_regen() { diff --git a/data/hooks/conf_regen/37-avahi-daemon b/data/hooks/conf_regen/37-avahi-daemon index 113e9e0c7..655a2e054 100755 --- a/data/hooks/conf_regen/37-avahi-daemon +++ b/data/hooks/conf_regen/37-avahi-daemon @@ -7,7 +7,8 @@ do_pre_regen() { cd /usr/share/yunohost/templates/avahi-daemon - install -D avahi-daemon.conf "${pending_dir}/etc/avahi/avahi-daemon.conf" + install -D -m 644 avahi-daemon.conf \ + "${pending_dir}/etc/avahi/avahi-daemon.conf" } do_post_regen() { diff --git a/data/hooks/conf_regen/40-glances b/data/hooks/conf_regen/40-glances index e7d6b07b9..a19d35d56 100755 --- a/data/hooks/conf_regen/40-glances +++ b/data/hooks/conf_regen/40-glances @@ -7,7 +7,7 @@ do_pre_regen() { cd /usr/share/yunohost/templates/glances - install -D glances.default "${pending_dir}/etc/default/glances" + install -D -m 644 glances.default "${pending_dir}/etc/default/glances" } do_post_regen() { diff --git a/data/hooks/conf_regen/46-nsswitch b/data/hooks/conf_regen/46-nsswitch index 9cbcae13d..db3a2199a 100755 --- a/data/hooks/conf_regen/46-nsswitch +++ b/data/hooks/conf_regen/46-nsswitch @@ -7,7 +7,7 @@ do_pre_regen() { cd /usr/share/yunohost/templates/nsswitch - install -D nsswitch.conf "${pending_dir}/etc/nsswitch.conf" + install -D -m 644 nsswitch.conf "${pending_dir}/etc/nsswitch.conf" } do_post_regen() { From a3b6a737092ea102953b5b8a9dd8b85a384bb7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 15:22:43 +0200 Subject: [PATCH 76/86] [fix] Correct rmilter.socket installation path in conf_regen hook --- data/hooks/conf_regen/28-rmilter | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/28-rmilter b/data/hooks/conf_regen/28-rmilter index bc225d530..05f921e09 100755 --- a/data/hooks/conf_regen/28-rmilter +++ b/data/hooks/conf_regen/28-rmilter @@ -7,8 +7,10 @@ do_pre_regen() { cd /usr/share/yunohost/templates/rmilter - install -D -m 644 rmilter.conf "${pending_dir}/etc/rmilter.conf" - install -D -m 644 rmilter.socket "${pending_dir}/etc/rmilter.socket" + install -D -m 644 rmilter.conf \ + "${pending_dir}/etc/rmilter.conf" + install -D -m 644 rmilter.socket \ + "${pending_dir}/etc/systemd/system/rmilter.socket" } do_post_regen() { From e0ea752f6b6e406124d6f0bdf7a5bc9352c6c843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Fri, 22 Apr 2016 15:30:35 +0200 Subject: [PATCH 77/86] [fix] Copy data without stat when processing regen conf file --- src/yunohost/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 55047bd4d..8d77a486e 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -665,7 +665,7 @@ def _process_regen_conf(system_conf, new_conf=None, save=True): system_dir = os.path.dirname(system_conf) if not os.path.isdir(system_dir): filesystem.mkdir(system_dir, 0755, True) - shutil.copy2(new_conf, system_conf) + shutil.copyfile(new_conf, system_conf) logger.info(m18n.n('service_conf_file_updated', conf=system_conf)) except: From 55f4ff8d69e70c12cc50734dab2d1ca7d263428b Mon Sep 17 00:00:00 2001 From: Moul Date: Sun, 24 Apr 2016 00:21:26 +0200 Subject: [PATCH 78/86] [fix] domains creation: regen_conf() were called with an old name of variable. --- src/yunohost/domain.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index ec003a79b..7862be2fc 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -157,10 +157,10 @@ def domain_add(auth, domain, dyndns=False): try: with open('/etc/yunohost/installed', 'r') as f: - service_regen_conf(service='nginx') - service_regen_conf(service='metronome') - service_regen_conf(service='dnsmasq') - service_regen_conf(service='rmilter') + service_regen_conf(names='nginx') + service_regen_conf(names='metronome') + service_regen_conf(names='dnsmasq') + service_regen_conf(names='rmilter') os.system('yunohost app ssowatconf > /dev/null 2>&1') except IOError: pass except: @@ -206,9 +206,9 @@ def domain_remove(auth, domain, force=False): else: raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) - service_regen_conf(service='nginx') - service_regen_conf(service='metronome') - service_regen_conf(service='dnsmasq') + service_regen_conf(names='nginx') + service_regen_conf(names='metronome') + service_regen_conf(names='dnsmasq') os.system('yunohost app ssowatconf > /dev/null 2>&1') hook_callback('post_domain_remove', args=[domain]) From 55debefd56ccf73f214459824cd000a8d0a1de7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 24 Apr 2016 22:15:29 +0200 Subject: [PATCH 79/86] [fix] Call regen-conf only once passing a list in domain_add/remove --- src/yunohost/domain.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 7862be2fc..b41891fb9 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -37,6 +37,8 @@ from urllib import urlopen from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger +from yunohost.service import service_regen_conf + logger = getActionLogger('yunohost.domain') @@ -78,7 +80,6 @@ def domain_add(auth, domain, dyndns=False): dyndns -- Subscribe to DynDNS """ - from yunohost.service import service_regen_conf from yunohost.hook import hook_callback attr_dict = { 'objectClass' : ['mailDomain', 'top'] } @@ -157,10 +158,8 @@ def domain_add(auth, domain, dyndns=False): try: with open('/etc/yunohost/installed', 'r') as f: - service_regen_conf(names='nginx') - service_regen_conf(names='metronome') - service_regen_conf(names='dnsmasq') - service_regen_conf(names='rmilter') + service_regen_conf(names=[ + 'nginx', 'metronome', 'dnsmasq', 'rmilter']) os.system('yunohost app ssowatconf > /dev/null 2>&1') except IOError: pass except: @@ -183,7 +182,6 @@ def domain_remove(auth, domain, force=False): force -- Force the domain removal """ - from yunohost.service import service_regen_conf from yunohost.hook import hook_callback if not force and domain not in domain_list(auth)['domains']: @@ -206,9 +204,7 @@ def domain_remove(auth, domain, force=False): else: raise MoulinetteError(errno.EIO, m18n.n('domain_deletion_failed')) - service_regen_conf(names='nginx') - service_regen_conf(names='metronome') - service_regen_conf(names='dnsmasq') + service_regen_conf(names=['nginx', 'metronome', 'dnsmasq']) os.system('yunohost app ssowatconf > /dev/null 2>&1') hook_callback('post_domain_remove', args=[domain]) From afda4a9c900f522fd1516d3fb1a5641280036f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Sun, 24 Apr 2016 23:03:18 +0200 Subject: [PATCH 80/86] [enh] Implement the intersection of package version Specifier class --- src/yunohost/utils/packages.py | 99 ++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index edade553f..82ebed243 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -98,14 +98,19 @@ class Specifier(object): } def __init__(self, spec): - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + if isinstance(spec, basestring): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - self._spec = ( - match.group("relation").strip(), - match.group("version").strip(), - ) + self._spec = ( + match.group("relation").strip(), + match.group("version").strip(), + ) + elif isinstance(spec, self.__class__): + self._spec = spec._spec + else: + return NotImplemented def __repr__(self): return "".format(str(self)) @@ -138,6 +143,9 @@ class Specifier(object): return self._spec != other._spec + def __and__(self, other): + return self.intersection(other) + def _get_relation(self, op): return getattr(self, "_compare_{0}".format(self._relations[op])) @@ -167,6 +175,49 @@ class Specifier(object): def __contains__(self, 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 contains(self, item): return self._get_relation(self.relation)(item, self.version) @@ -181,7 +232,9 @@ class SpecifierSet(object): """ 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() for specifier in specifiers: @@ -198,15 +251,8 @@ class SpecifierSet(object): def __hash__(self): return hash(self._specs) - def __and__(self, other): - if isinstance(other, basestring): - other = SpecifierSet(other) - elif not isinstance(other, SpecifierSet): - return NotImplemented - - specifier = SpecifierSet() - specifier._specs = frozenset(self._specs | other._specs) - return specifiers + def __or__(self, other): + return self.union(other) def __eq__(self, other): if isinstance(other, basestring): @@ -237,6 +283,25 @@ class SpecifierSet(object): def __contains__(self, item): return self.contains(item) + def union(self, other): + """Make the union of two specifiers set + + 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): return all( s.contains(item) From 3e1f9d223bcc592b8cc7043812a02d272a27912b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 25 Apr 2016 11:13:01 +0200 Subject: [PATCH 81/86] [doc] Add documentation to contains methods of Specifier/SpecifierSet --- src/yunohost/utils/packages.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index 82ebed243..43d2f2927 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -219,6 +219,15 @@ class Specifier(object): return SpecifierSet(result if result is not None else '') 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) @@ -303,6 +312,15 @@ class SpecifierSet(object): return specifiers 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( s.contains(item) for s in self._specs From d280a08cee1bae5ff731f9cea2db5202fc119e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 25 Apr 2016 11:18:08 +0200 Subject: [PATCH 82/86] [enh] Implement the union of package version Specifier class --- src/yunohost/utils/packages.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index 43d2f2927..3f168926c 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -146,6 +146,9 @@ class Specifier(object): def __and__(self, other): return self.intersection(other) + def __or__(self, other): + return self.union(other) + def _get_relation(self, op): return getattr(self, "_compare_{0}".format(self._relations[op])) @@ -218,6 +221,26 @@ class Specifier(object): 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): """Check if the specifier contains an other From 86e10064e03320a20297e9aeac116abfc69ca7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 25 Apr 2016 11:55:49 +0200 Subject: [PATCH 83/86] [fix] Correct argument index in __repr__ of utils.packages classes --- src/yunohost/utils/packages.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index 3f168926c..9b39ea133 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -113,7 +113,7 @@ class Specifier(object): return NotImplemented def __repr__(self): - return "".format(str(self)) + return "".format(str(self)) def __str__(self): return "{0}{1}".format(*self._spec) @@ -275,7 +275,7 @@ class SpecifierSet(object): self._specs = frozenset(parsed) def __repr__(self): - return "".format(str(self)) + return "".format(str(self)) def __str__(self): return ",".join(sorted(str(s) for s in self._specs)) From 7a87fa7ebc4a056c36bd0748d6a16b1c3b87e474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 25 Apr 2016 12:17:10 +0200 Subject: [PATCH 84/86] [enh] Implement the intersection of package version SpecifierSet class --- src/yunohost/utils/packages.py | 39 +++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index 9b39ea133..5be2103e5 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -283,6 +283,9 @@ class SpecifierSet(object): def __hash__(self): return hash(self._specs) + def __and__(self, other): + return self.intersection(other) + def __or__(self, other): return self.union(other) @@ -315,8 +318,42 @@ class SpecifierSet(object): def __contains__(self, 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 set + """Make the union of two specifiers sets Return a new `SpecifierSet` with version specifiers from the set and the other. From aca251e98173d042e254f09d53c985a5fb39fb18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 25 Apr 2016 13:58:17 +0200 Subject: [PATCH 85/86] [enh] Prevent non-updated multi-instances apps installation (close #126) --- locales/en.json | 2 ++ src/yunohost/app.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 90f9e46e5..9290b7c24 100644 --- a/locales/en.json +++ b/locales/en.json @@ -34,6 +34,8 @@ "app_extraction_failed" : "Unable to extract installation files", "app_install_files_invalid" : "Invalid installation files", "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_invalid" : "Invalid value for argument '{name:s}': {error:s}", "app_argument_required" : "Argument '{name:s}' is required", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 032f7fb4a..f6a099bd1 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1385,12 +1385,24 @@ def _encode_string(value): def _check_manifest_requirements(manifest): """Check if required packages are met from the manifest""" requirements = manifest.get('requirements', dict()) + # FIXME: Deprecate min_version key if 'min_version' in manifest: requirements['yunohost'] = '>> {0}'.format(manifest['min_version']) logger.debug("the manifest key 'min_version' is deprecated, " "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 logger.info(m18n.n('app_requirements_checking')) From 1430fa133a163d1e6d953f013db9df0d1a589a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Lebleu?= Date: Mon, 25 Apr 2016 15:26:11 +0200 Subject: [PATCH 86/86] [doc] Be more verbose when reset the MySQL root password --- data/hooks/conf_regen/34-mysql | 13 +++++++++++-- data/hooks/restore/11-conf_ynh_mysql | 5 +++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/34-mysql b/data/hooks/conf_regen/34-mysql index 7905035fb..84f473d04 100755 --- a/data/hooks/conf_regen/34-mysql +++ b/data/hooks/conf_regen/34-mysql @@ -26,6 +26,11 @@ do_post_regen() { 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" \ @@ -40,8 +45,12 @@ EOF # reconfigure Debian package sudo dpkg-reconfigure -freadline -u "$mysql_pkg" 2>&1 else - echo "MySQL password for root user has been manually changed" \ - "and will not be overridden without --force" >&2 + 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 } diff --git a/data/hooks/restore/11-conf_ynh_mysql b/data/hooks/restore/11-conf_ynh_mysql index 8d6dc53dc..b2f8c8e31 100644 --- a/data/hooks/restore/11-conf_ynh_mysql +++ b/data/hooks/restore/11-conf_ynh_mysql @@ -14,6 +14,11 @@ new_pwd=$(sudo cat "${backup_dir}/root_pwd" || sudo cat "${backup_dir}/mysql") 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" \