From 31b66afc19df71b1cb58b7c2875b4254bb5a0132 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 26 Jul 2023 19:44:07 +0200 Subject: [PATCH 001/111] Bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index c609848..f2ecc47 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,7 @@ "en": "Create and manager wifi networks, share Internet access and use YunoHost apps accross wifi", "fr": "Créer et configurer des réseaux wifi, partager l'accès a Internet et utiliser les applications YunoHost via wifi" }, - "version": "2.0~ynh3", + "version": "2.0~ynh4", "url": "https://github.com/labriqueinternet/hotspot_ynh", "license": "AGPL-3.0", "maintainer": { From 46401d395cc4c6935771e036acc8cccb8e0d6b96 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Wed, 26 Jul 2023 17:44:11 +0000 Subject: [PATCH 002/111] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c945fcb..f70b8eb 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.0~ynh3 +**Shipped version:** 2.0~ynh4 ## Screenshots diff --git a/README_fr.md b/README_fr.md index 27a6f13..88c5f61 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.0~ynh3 +**Version incluse :** 2.0~ynh4 ## Captures d’écran From 0782d830f513f6e37f7b88e812bd11300de8174e Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 15 Aug 2023 11:13:57 +0200 Subject: [PATCH 003/111] fix ssid with space character --- scripts/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/config b/scripts/config index 2ab89f4..78fa748 100644 --- a/scripts/config +++ b/scripts/config @@ -186,7 +186,7 @@ set__array_settings() { then value="" fi - local values="$(ynh_app_setting_get $app $short_setting | awk 'BEGIN{FS=OFS="|"} {$'$index'="'${!1}'"}'1)" + local values="$(ynh_app_setting_get $app $short_setting | awk "BEGIN{FS=OFS=\"|\"} {\$${index}=\"${value}\"}"1)" ynh_app_setting_set --app=$app --key=$short_setting --value="$values" ynh_print_info --message="Configuration key '$short_setting' edited in app settings" } From 6ecbe7480de53b5c2c509c1d291c5c65bc954d47 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Aug 2023 13:28:17 +0200 Subject: [PATCH 004/111] Delete legacy screenshot --- screenshot.png | Bin 87091 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 screenshot.png diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index d8b4cdf102f2e23f1f2a13d823f9fcade695f157..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87091 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 From 178da8da7bde815e724b9da4b1bcbfe3a766065f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 16 Aug 2023 13:31:52 +0200 Subject: [PATCH 005/111] Delete legacy travis.yml --- .travis.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3303710..0000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -language: php -before_script: - - git clone --depth 1 git://github.com/YunoHost/package_linter ../package_linter && cd ../package_linter - - mv ../hotspot_ynh hotspot_ynh -script: - - ./package_linter.py hotspot_ynh -notifications: - email: false - irc: - on_success: always - on_failure: always - channels: - - "irc.geeknode.org#labriqueinter.net-dev" From 643d8eaf52c6c54f4bc98c9f1bec7de41bf97213 Mon Sep 17 00:00:00 2001 From: HgO Date: Wed, 16 Aug 2023 20:23:08 +0200 Subject: [PATCH 006/111] fix dhcp cleanup --- conf/ynh-hotspot | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 546d283..f6ba832 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -158,7 +158,7 @@ start_dhcpd() { sed "s|__IP4_DNS__|${ip4_dns[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf sed "s|__IP4_NAT_PREFIX__|${ip4_nat_prefix[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf - dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf -p0 + dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv4-ssid${i}.pid fi # Run DHCPv6 server @@ -171,7 +171,7 @@ start_dhcpd() { sed "s|__IP6_DNS__|${ip6_dns[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf sed "s|__IP6_NET__|${ip6_net[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf - dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf -p0 + dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv6-ssid${i}.pid fi } @@ -249,13 +249,13 @@ stop_dhcpd() { if is_dhcpd6_running ${i}; then echo "hotspot${i}: Stop the NDP and DHCPv6 server (dnsmasq)" kill $(ps aux | grep 'dhcpdv6-ssid' | grep -v grep | awk '{ print $2 }') - rm -f /etc/dnsmasq.d/dhcpdv6-ssid*.conf + rm -f /etc/dnsmasq.dhcpd/dhcpdv6-ssid*.conf fi if is_dhcpd4_running ${i}; then echo "hotspot${i}: Stop the DHCPv4 server (dnsmasq)" kill $(ps aux | grep 'dhcpdv4-ssid' | grep -v grep | awk '{ print $2 }') - rm -f /etc/dnsmasq.d/dhcpdv4-ssid*.conf + rm -f /etc/dnsmasq.dhcpd/dhcpdv4-ssid*.conf fi } From c1246b49632d35e8dfb0b1c618399f151896d89f Mon Sep 17 00:00:00 2001 From: HgO Date: Wed, 16 Aug 2023 20:35:58 +0200 Subject: [PATCH 007/111] make use of pid files --- conf/ynh-hotspot | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index f6ba832..d3c732b 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -73,13 +73,13 @@ is_forwarding_set() { is_dhcpd6_running() { local i=${1} - ps aux | grep "dhcpdv6-ssid${i}" | grep -qv grep + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid" ]] } is_dhcpd4_running() { local i=${1} - ps aux | grep "dhcpdv4-ssid${i}" | grep -qv grep + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid" ]] } is_hostapd_running() { @@ -248,14 +248,14 @@ stop_dhcpd() { if is_dhcpd6_running ${i}; then echo "hotspot${i}: Stop the NDP and DHCPv6 server (dnsmasq)" - kill $(ps aux | grep 'dhcpdv6-ssid' | grep -v grep | awk '{ print $2 }') - rm -f /etc/dnsmasq.dhcpd/dhcpdv6-ssid*.conf + kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid) + rm -f /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf fi if is_dhcpd4_running ${i}; then echo "hotspot${i}: Stop the DHCPv4 server (dnsmasq)" - kill $(ps aux | grep 'dhcpdv4-ssid' | grep -v grep | awk '{ print $2 }') - rm -f /etc/dnsmasq.dhcpd/dhcpdv4-ssid*.conf + kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid) + rm -f /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf fi } From 2d8b5867942f224453b012fe518e5684c3d22313 Mon Sep 17 00:00:00 2001 From: HgO Date: Wed, 16 Aug 2023 20:39:02 +0200 Subject: [PATCH 008/111] remove openvpn coupling --- conf/openvpn_90-hotspot | 3 +++ conf/ynh-hotspot | 15 --------------- scripts/backup | 3 +++ scripts/install | 8 ++++++++ scripts/remove | 3 +++ scripts/restore | 3 +++ scripts/upgrade | 7 +++++++ 7 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 conf/openvpn_90-hotspot diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot new file mode 100644 index 0000000..5964ef2 --- /dev/null +++ b/conf/openvpn_90-hotspot @@ -0,0 +1,3 @@ +#!/bin/bash + +systemctl restart ynh-hotspot diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 546d283..26fa6d9 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -20,10 +20,6 @@ # Functions ## State functions -has_vpnclient_app() { - [ -e /tmp/.ynh-vpnclient-started ] -} - has_ip6delegatedprefix() { local i=${1} @@ -353,12 +349,6 @@ if [ "$1" != restart ]; then old_gateway_interface=$(ynh_setting_get hotspot gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') - # Switch the NAT interface if there is a VPN - ip link show dev tun0 &>/dev/null - if [ "$?" -eq 0 ]; then - new_gateway_interface=tun0 - fi - echo "OK" fi @@ -459,11 +449,6 @@ stop) echo "Stop hostapd" stop_hostapd fi - - # Fix configuration - if has_vpnclient_app; then - ynh-vpnclient start - fi ;; restart) $0 stop diff --git a/scripts/backup b/scripts/backup index 8284b04..8634184 100644 --- a/scripts/backup +++ b/scripts/backup @@ -50,6 +50,9 @@ ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" ynh_backup --src_path="/usr/local/bin/$service_name" +ynh_backup --src_path="/etc/openvpn/scripts/route-up.d/90-hotspot" +ynh_backup --src_path="/etc/openvpn/scripts/route-down.d/90-hotspot" + ynh_backup --src_path="/etc/init.d/hostapd" #================================================= diff --git a/scripts/install b/scripts/install index 8032f2a..e82036b 100644 --- a/scripts/install +++ b/scripts/install @@ -165,6 +165,14 @@ install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq # Copy init script install -o root -g root -m 0755 ../conf/$service_name /usr/local/bin/ +# Copy openvpn scripts +mkdir -pm 0755 /etc/openvpn/scripts +mkdir -pm 0755 /etc/openvpn/scripts/route-up.d +mkdir -pm 0755 /etc/openvpn/scripts/route-down.d +install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-up.d/90-hotspot +install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-down.d/90-hotspot + + #================================================= # CONFIGURE HOSTAPD #================================================= diff --git a/scripts/remove b/scripts/remove index 4ccfd27..ae210dd 100644 --- a/scripts/remove +++ b/scripts/remove @@ -54,6 +54,9 @@ ynh_remove_app_dependencies #================================================= ynh_script_progression --message="Removing app main directory..." +ynh_secure_remove --file="/etc/openvpn/scripts/route-up.d/90-hotspot" +ynh_secure_remove --file="/etc/openvpn/scripts/route-down.d/90-hotspot" + # Remove the app directory securely ynh_secure_remove --file="/usr/local/bin/$service_name" diff --git a/scripts/restore b/scripts/restore index fb047d1..ee9580e 100644 --- a/scripts/restore +++ b/scripts/restore @@ -70,6 +70,9 @@ ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" ynh_restore_file --origin_path="/usr/local/bin/$service_name" +ynh_restore_file --origin_path="/etc/openvpn/scripts/route-up.d/90-hotspot" +ynh_restore_file --origin_path="/etc/openvpn/scripts/route-down.d/90-hotspot" + ynh_restore_file --origin_path="/etc/init.d/hostapd" #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 519721e..72a98b4 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -141,6 +141,13 @@ install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq # Copy init script install -o root -g root -m 0755 ../conf/$service_name /usr/local/bin/ +# Copy openvpn scripts +mkdir -pm 0755 /etc/openvpn/scripts +mkdir -pm 0755 /etc/openvpn/scripts/route-up.d +mkdir -pm 0755 /etc/openvpn/scripts/route-down.d +install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-up.d/90-hotspot +install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-down.d/90-hotspot + #================================================= # SETUP SYSTEMD #================================================= From dceb955917ed04c7b7a0aa641417b2978478e9ef Mon Sep 17 00:00:00 2001 From: HgO Date: Wed, 16 Aug 2023 21:04:42 +0200 Subject: [PATCH 009/111] ensure process from pid is running and remove pid file --- conf/ynh-hotspot | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index d3c732b..8883f2f 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -73,13 +73,13 @@ is_forwarding_set() { is_dhcpd6_running() { local i=${1} - [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid" ]] + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid") > /dev/null } is_dhcpd4_running() { local i=${1} - [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid" ]] + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid") > /dev/null } is_hostapd_running() { @@ -249,12 +249,14 @@ stop_dhcpd() { if is_dhcpd6_running ${i}; then echo "hotspot${i}: Stop the NDP and DHCPv6 server (dnsmasq)" kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid) + rm -f /run/dnsmasq/dnsmasq-dhcpdv6-ssid${1}.pid rm -f /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf fi if is_dhcpd4_running ${i}; then echo "hotspot${i}: Stop the DHCPv4 server (dnsmasq)" kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid) + rm -f /run/dnsmasq/dnsmasq-dhcpdv4-ssid${1}.pid rm -f /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf fi } From f02097b99ed93a2bf2912cd92635c9667c37fcdc Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 15:22:11 +0200 Subject: [PATCH 010/111] remove multissid --- conf/hostapd.accesspoint.conf | 7 - conf/hostapd.base.conf | 8 +- conf/ynh-hotspot | 432 ++++++++++++---------------------- config_panel.toml | 168 ++----------- scripts/config | 88 ++----- scripts/install | 1 - 6 files changed, 191 insertions(+), 513 deletions(-) delete mode 100644 conf/hostapd.accesspoint.conf diff --git a/conf/hostapd.accesspoint.conf b/conf/hostapd.accesspoint.conf deleted file mode 100644 index a726b1e..0000000 --- a/conf/hostapd.accesspoint.conf +++ /dev/null @@ -1,7 +0,0 @@ -__BSS_COMMENT__bss=__WIFI_INTERFACE__ -ssid=__WIFI_SSID__ -__SEC_COMMENT__wpa=2 -__SEC_COMMENT__wpa_passphrase=__WIFI_PASSPHRASE__ -__SEC_COMMENT__wpa_key_mgmt=WPA-PSK -__SEC_COMMENT__wpa_pairwise=TKIP -__SEC_COMMENT__rsn_pairwise=CCMP diff --git a/conf/hostapd.base.conf b/conf/hostapd.base.conf index 8ea3d7d..64e59eb 100644 --- a/conf/hostapd.base.conf +++ b/conf/hostapd.base.conf @@ -1,8 +1,12 @@ interface=__WIFI_DEVICE__ hw_mode=g -__N_COMMENT__ieee80211n=1 -__N_COMMENT__wmm_enabled=1 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 channel=__WIFI_CHANNEL__ +ssid=__WIFI_SSID__ +__SEC_COMMENT__wpa=2 +__SEC_COMMENT__wpa_passphrase=__WIFI_PASSPHRASE__ +__SEC_COMMENT__wpa_key_mgmt=WPA-PSK +__SEC_COMMENT__wpa_pairwise=TKIP +__SEC_COMMENT__rsn_pairwise=CCMP diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 95eaff3..aaf0b96 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -17,65 +17,49 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +source /usr/share/yunohost/helpers + # Functions ## State functions has_ip6delegatedprefix() { - local i=${1} - - [[ -n "${ip6_net[${i}]}" ]] && [[ "${ip6_net[${i}]}" != "none" ]] + [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]] } ip6addrfromdelegatedprefix() { - local i=${1} - - echo "${ip6_net[${i}]}${i}001" + echo "${ip6_net}1" } is_nat_set() { local gateway_interface=${1} - iptables -w -nvt nat -L POSTROUTING | grep MASQUERADE | grep -q "${gateway_interface}" } is_ip4nataddr_set() { - local i=${1} - local dev=$(devfromid "${i}") - - ip address show dev "${dev}" 2>/dev/null | grep -q "${ip4_nat_prefix[${i}]}.1/24" + ip address show dev "${wifi_device}" 2>/dev/null | grep -q "${ip4_nat_prefix}.1/24" } is_ip6addr_set() { - local i=${1} - local dev=$(devfromid "${i}") - - ip address show dev "${dev}" 2>/dev/null | grep -q "$(ip6addrfromdelegatedprefix $i)/64" + ip address show dev "${wifi_device}" 2>/dev/null | grep -q "$(ip6addrfromdelegatedprefix)/64" } is_ip6firewall_set() { - local i=${1} - local dev=$(devfromid "${i}") - - ip6tables -w -nvL FORWARD | grep DROP | grep -q "${dev}" + ip6tables -w -nvL FORWARD | grep DROP | grep -q "${wifi_device}" } is_forwarding_set() { local ip6=$(sysctl net.ipv6.conf.all.forwarding | awk '{ print $NF; }') local ip4=$(sysctl net.ipv4.conf.all.forwarding | awk '{ print $NF; }') - [ "${ip6}" -eq 1 ] && [ "${ip4}" -eq 1 ] + [[ "${ip6}" -eq 1 ]] && [[ "${ip4}" -eq 1 ]] } is_dhcpd6_running() { - local i=${1} - - [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid") > /dev/null + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid") > /dev/null } is_dhcpd4_running() { - local i=${1} - - [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid") > /dev/null + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid") > /dev/null } is_hostapd_running() { @@ -83,18 +67,39 @@ is_hostapd_running() { } is_running() { - for i in $(seq 0 $((${multissid} - 1))); do - (has_ip6delegatedprefix ${i} && is_ip6addr_set ${i} \ - && ([ "${ip6_firewall[${i}]}" -eq 1 ] && is_ip6firewall_set ${i} || [ "${ip6_firewall[${i}]}" -eq 0 ]) \ - && is_dhcpd6_running ${i} || ! has_ip6delegatedprefix ${i}) \ - && is_ip4nataddr_set ${i} && is_dhcpd4_running ${i} - - if [ ! $? -eq 0 ]; then + if has_ip6delegatedprefix; then + if ! is_ip6addr_set; then return 1 fi - done + if [[ "${ip6_firewall}" -eq 1 ]] && ! is_ip6firewall_set; then + return 1 + fi + if ! is_dhcpd6_running; then + return 1 + fi + fi - is_hostapd_running && is_forwarding_set && ([ -z "${new_gateway_interface}" ] || is_nat_set "${new_gateway_interface}") + if ! is_ip4nataddr_set; then + return 1 + fi + + if ! is_dhcpd4_running; then + return 1 + fi + + if ! is_hostapd_running; then + return 1 + fi + + if ! is_forwarding_set; then + return 1 + fi + + if [[ -n ${new_gateway_interface} ]] && ! is_nat_set "${new_gateway_interface}"; then + return 1 + fi + + return 0 } ## Setters @@ -106,33 +111,26 @@ set_nat() { } set_ipaddr() { - local i=${1} - local dev=$(devfromid "${i}") - - if ! is_ip4nataddr_set ${i}; then - echo "hotspot${i}: Set IPv4 NAT address" - ip address add "${ip4_nat_prefix[${i}]}.1/24" dev "${dev}" + if ! is_ip4nataddr_set; then + echo "hotspot ${wifi_device}: Set IPv4 NAT address" + ip address add "${ip4_nat_prefix}.1/24" dev "${wifi_device}" fi - if has_ip6delegatedprefix ${i} && ! is_ip6addr_set ${i}; then - echo "hotspot${i}: Set IPv6 address" - ip address delete "$(ip6addrfromdelegatedprefix $i)/64" dev tun0 &>/dev/null - ip address add "$(ip6addrfromdelegatedprefix $i)/64" dev "${dev}" + if has_ip6delegatedprefix && ! is_ip6addr_set; then + echo "hotspot ${wifi_device}: Set IPv6 address" + ip address delete "$(ip6addrfromdelegatedprefix)/64" dev tun0 &>/dev/null + ip address add "$(ip6addrfromdelegatedprefix)/64" dev "${wifi_device}" fi } set_ipfirewall() { - local i=${1} - local dev=$(devfromid "${i}") - # Set ipv6 firewalling - if has_ip6delegatedprefix ${i} && [ "${ip6_firewall[${i}]}" -eq 1 ] && ! is_ip6firewall_set ${i}; then - echo "hotspot${i}: Set IPv6 firewalling" - ip6tables -w -A FORWARD -i "${dev}" -j ACCEPT - ip6tables -w -A FORWARD -o "${dev}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT - ip6tables -w -A FORWARD -o "${dev}" -j DROP + if has_ip6delegatedprefix && [[ "${ip6_firewall}" -eq 1 ]] && ! is_ip6firewall_set; then + echo "hotspot ${wifi_device}: Set IPv6 firewalling" + ip6tables -w -A FORWARD -i "${wifi_device}" -j ACCEPT + ip6tables -w -A FORWARD -o "${wifi_device}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + ip6tables -w -A FORWARD -o "${wifi_device}" -j DROP fi - } set_forwarding() { @@ -141,62 +139,22 @@ set_forwarding() { } start_dhcpd() { - local i=${1} - local dev=$(devfromid "${i}") - # Run DHCPv4 server - if ! is_dhcpd4_running ${i}; then - echo "hotspot${i}: Start the DHCPv4 server (dnsmasq)" - - cp /etc/dnsmasq.dhcpd/dhcpdv4{.conf.tpl,-ssid${i}.conf} - - sed "s|__WIFI_DEVICE__|${dev}|g" -i /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf - sed "s|__IP4_DNS__|${ip4_dns[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf - sed "s|__IP4_NAT_PREFIX__|${ip4_nat_prefix[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf - - dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv4-ssid${i}.pid + if ! is_dhcpd4_running; then + echo "hotspot ${wifi_device}: Start the DHCPv4 server (dnsmasq)" + dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv4-ssid-${wifi_device}.pid fi # Run DHCPv6 server - if has_ip6delegatedprefix ${i} && ! is_dhcpd6_running ${i}; then - echo "hotspot${i}: Start the NDP and DHCPv6 server (dnsmasq)" - - cp /etc/dnsmasq.dhcpd/dhcpdv6{.conf.tpl,-ssid${i}.conf} - - sed "s|__WIFI_DEVICE__|${dev}|g" -i /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf - sed "s|__IP6_DNS__|${ip6_dns[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf - sed "s|__IP6_NET__|${ip6_net[${i}]}|g" -i /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf - - dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv6-ssid${i}.pid + if has_ip6delegatedprefix && ! is_dhcpd6_running; then + echo "hotspot ${wifi_device}: Start the NDP and DHCPv6 server (dnsmasq)" + dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv6-ssid-${wifi_device}.pid fi } configure_hostapd() { - local ethaddr=$(ip link show dev "${wifi_device}" | grep link/ether | awk -F: '{ printf "02:%s:%s:%s:%s:00", $2, $3, $4, $5 }') ip link set addr "${ethaddr}" dev "${wifi_device}" - - cp /etc/hostapd/hostapd.base.conf /etc/hostapd/hostapd.conf - sed "s|__WIFI_DEVICE__|${wifi_device}|g" -i /etc/hostapd/hostapd.conf - sed "s|__WIFI_CHANNEL__|${wifi_channel}|g" -i /etc/hostapd/hostapd.conf - sed "s|__N_COMMENT__||g" -i /etc/hostapd/hostapd.conf - - for i in $(seq 0 $((${multissid} - 1))); do - - [ "${wifi_secure[${i}]}" -eq 1 ] && local sec_comment="" || local sec_comment="#" - [ "${i}" -eq 0 ] && local bss_comment="#" || local bss_comment="" - - cp /etc/hostapd/hostapd.accesspoint.conf /etc/hostapd/hostapd.conf.tmp - - sed "s|__WIFI_INTERFACE__|hotspot${i}|g" -i /etc/hostapd/hostapd.conf.tmp - sed "s|__WIFI_SSID__|${wifi_ssid[${i}]}|g" -i /etc/hostapd/hostapd.conf.tmp - sed "s|__WIFI_PASSPHRASE__|${wifi_passphrase[${i}]}|g" -i /etc/hostapd/hostapd.conf.tmp - sed "s|__SEC_COMMENT__|${sec_comment}|g" -i /etc/hostapd/hostapd.conf.tmp - sed "s|__BSS_COMMENT__|${bss_comment}|g" -i /etc/hostapd/hostapd.conf.tmp - - cat /etc/hostapd/hostapd.conf.tmp >>/etc/hostapd/hostapd.conf - rm /etc/hostapd/hostapd.conf.tmp - done } ## Unsetters @@ -208,29 +166,23 @@ unset_nat() { } unset_ipaddr() { - local i=${1} - local dev=$(devfromid "${i}") - - if is_ip4nataddr_set ${i}; then - echo "hotspot${i}: Unset IPv4 NAT address" - ip address delete "${ip4_nat_prefix[${i}]}.1/24" dev "${dev}" + if is_ip4nataddr_set; then + echo "hotspot ${wifi_device}: Unset IPv4 NAT address" + ip address delete "${ip4_nat_prefix}.1/24" dev "${wifi_device}" fi - if has_ip6delegatedprefix ${i} && is_ip6addr_set ${i}; then - echo "hotspot${i}: Unset IPv6 address" - ip address delete "$(ip6addrfromdelegatedprefix $i)/64" dev "${dev}" + if has_ip6delegatedprefix && is_ip6addr_set; then + echo "hotspot ${wifi_device}: Unset IPv6 address" + ip address delete "$(ip6addrfromdelegatedprefix)/64" dev "${wifi_device}" fi } unset_ipfirewall() { - local i=${1} - local dev=$(devfromid "${i}") - - if has_ip6delegatedprefix ${i} && [ "${ip6_firewall[${i}]}" -eq 1 ] && is_ip6firewall_set ${i}; then - echo "hotspot${i}: Unset IPv6 firewalling" - ip6tables -w -D FORWARD -i "${dev}" -j ACCEPT - ip6tables -w -D FORWARD -o "${dev}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT - ip6tables -w -D FORWARD -o "${dev}" -j DROP + if has_ip6delegatedprefix && [[ "${ip6_firewall}" -eq 1 ]] && is_ip6firewall_set; then + echo "hotspot ${wifi_device}: Unset IPv6 firewalling" + ip6tables -w -D FORWARD -i "${wifi_device}" -j ACCEPT + ip6tables -w -D FORWARD -o "${wifi_device}" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + ip6tables -w -D FORWARD -o "${wifi_device}" -j DROP fi } @@ -240,81 +192,23 @@ unset_forwarding() { } stop_dhcpd() { - local i=${1} - - if is_dhcpd6_running ${i}; then - echo "hotspot${i}: Stop the NDP and DHCPv6 server (dnsmasq)" - kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-ssid${i}.pid) - rm -f /run/dnsmasq/dnsmasq-dhcpdv6-ssid${1}.pid - rm -f /etc/dnsmasq.dhcpd/dhcpdv6-ssid${i}.conf + if is_dhcpd6_running; then + echo "hotspot ${wifi_device}: Stop the NDP and DHCPv6 server (dnsmasq)" + kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid) + rm -f /run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid fi - if is_dhcpd4_running ${i}; then - echo "hotspot${i}: Stop the DHCPv4 server (dnsmasq)" - kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-ssid${i}.pid) - rm -f /run/dnsmasq/dnsmasq-dhcpdv4-ssid${1}.pid - rm -f /etc/dnsmasq.dhcpd/dhcpdv4-ssid${i}.conf + if is_dhcpd4_running; then + echo "hotspot ${wifi_device}: Stop the DHCPv4 server (dnsmasq)" + kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid) + rm -f /run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid fi } -stop_dhcpd4() { - : -} - stop_hostapd() { systemctl stop hostapd } -## Tools - -ynh_setting_get() { - - APP="$1" KEY="$2" python3 - </dev/null; do - sleep 1 - if [ ${i} -gt 20 ]; then - echo "Failed to see hotspot interface showing up in 'ip a'" - stop_hostapd - exit 1 - fi - i=$(($i + 1)) - done - fi fi - # For each registred ssid - for i in $(seq 0 $((${multissid} - 1))); do - set_ipaddr ${i} - set_ipfirewall ${i} - start_dhcpd ${i} - done + set_ipaddr + set_ipfirewall + start_dhcpd # Update dynamic settings - ynh_setting_set hotspot gateway_interface "${new_gateway_interface}" + ynh_app_setting_set hotspot gateway_interface "${new_gateway_interface}" ;; stop) echo "[hotspot] Stopping..." rm -f /tmp/.ynh-hotspot-started - if [ -n "${old_gateway_interface}" ] && is_nat_set "${old_gateway_interface}"; then + if [[ -n "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then echo "Unset NAT" unset_nat "${old_gateway_interface}" fi @@ -441,11 +313,9 @@ stop) echo "Unset forwarding" unset_forwarding - for i in $(seq 0 $((${multissid} - 1))); do - unset_ipaddr ${i} - unset_ipfirewall ${i} - stop_dhcpd ${i} - done + unset_ipaddr + unset_ipfirewall + stop_dhcpd if is_hostapd_running; then echo "Stop hostapd" @@ -459,12 +329,12 @@ restart) status) exitcode=0 - if [ "${service_enabled}" != "enabled" ]; then + if [[ "${service_enabled}" != "enabled" ]]; then echo "[FAIL] Hotspot Service disabled" exit 1 fi - if [ -z "${wifi_device}" ]; then + if [[ -z "${wifi_device}" ]]; then echo "[FAIL] No wifi device selected. Make sure your wifi antenna is plugged-in / available and select it in the Hotspot admin" exit 1 fi @@ -474,7 +344,7 @@ status) if is_nat_set "${new_gateway_interface}"; then echo "[ OK ] IPv4 NAT set" else - if [ -z "${new_gateway_interface}" ]; then + if [[ -z "${new_gateway_interface}" ]]; then echo "[INFO] No IPv4 NAT set (no internet interface)" else echo "[FAIL] No IPv4 NAT set" @@ -496,53 +366,51 @@ status) exitcode=1 fi - for i in $(seq 0 $((${multissid} - 1))); do - if has_ip6delegatedprefix ${i}; then - echo "[INFO] hotspot${i}: IPv6 delegated prefix found" - echo "[INFO] hotspot${i}: IPv6 address computed from the delegated prefix: $(ip6addrfromdelegatedprefix $i)" + if has_ip6delegatedprefix; then + echo "[INFO] hotspot ${wifi_device}: IPv6 delegated prefix found" + echo "[INFO] hotspot ${wifi_device}: IPv6 address computed from the delegated prefix: $(ip6addrfromdelegatedprefix)" - if is_ip6addr_set ${i}; then - echo "[ OK ] hotspot${i}: IPv6 address set" - else - echo "[FAIL] hotspot${i}: No IPv6 address set" - exitcode=1 - fi - - if is_ip6firewall_set ${i}; then - echo "[ OK ] hotspot${i}: IPv6 firewalling set" - else - if [ "${ip6_firewall[${i}]}" -eq 1 ]; then - echo "[FAIL] hotspot${i}: No IPv6 firewalling set" - else - echo "[INFO] hotspot${i}: No IPv6 firewalling set" - fi - exitcode=1 - fi - - if is_dhcpd6_running ${i}; then - echo "[ OK ] hotspot${i}: NDP and DHCPv6 server (dnsmasq) are running" - else - echo "[FAIL] hotspot${i}: NDP and DHCPv6 server (dnsmasq) are not running" - exitcode=1 - fi + if is_ip6addr_set; then + echo "[ OK ] hotspot ${wifi_device}: IPv6 address set" else - echo "[INFO] hotspot${i}: No IPv6 delegated prefix found" - fi - - if is_dhcpd4_running ${i}; then - echo "[ OK ] hotspot${i}: DHCPv4 server (dnsmasq) is running" - else - echo "[FAIL] hotspot${i}: DHCPv4 (dnsmasq) is not running" + echo "[FAIL] hotspot ${wifi_device}: No IPv6 address set" exitcode=1 fi - if is_ip4nataddr_set ${i}; then - echo "[ OK ] hotspot${i}: IPv4 NAT address set" + if is_ip6firewall_set; then + echo "[ OK ] hotspot ${wifi_device}: IPv6 firewalling set" else - echo "[FAIL] hotspot${i}: No IPv4 NAT address set" + if [[ "${ip6_firewall}" -eq 1 ]]; then + echo "[FAIL] hotspot ${wifi_device}: No IPv6 firewalling set" + else + echo "[INFO] hotspot ${wifi_device}: No IPv6 firewalling set" + fi exitcode=1 fi - done + + if is_dhcpd6_running; then + echo "[ OK ] hotspot ${wifi_device}: NDP and DHCPv6 server (dnsmasq) are running" + else + echo "[FAIL] hotspot ${wifi_device}: NDP and DHCPv6 server (dnsmasq) are not running" + exitcode=1 + fi + else + echo "[INFO] hotspot ${wifi_device}: No IPv6 delegated prefix found" + fi + + if is_dhcpd4_running; then + echo "[ OK ] hotspot ${wifi_device}: DHCPv4 server (dnsmasq) is running" + else + echo "[FAIL] hotspot ${zifi_device}: DHCPv4 (dnsmasq) is not running" + exitcode=1 + fi + + if is_ip4nataddr_set; then + echo "[ OK ] hotspot ${wifi_device}: IPv4 NAT address set" + else + echo "[FAIL] hotspot ${wifi_device}: No IPv4 NAT address set" + exitcode=1 + fi exit ${exitcode} ;; diff --git a/config_panel.toml b/config_panel.toml index 7b11364..71a9384 100644 --- a/config_panel.toml +++ b/config_panel.toml @@ -19,7 +19,7 @@ name = "Configuration" visible = "no_antenna" [main.service.status] - ask = "The status of your VPN is unknown." + ask = "The status of your Hotspot is unknown." type = "alert" style = "info" visible = "! no_antenna" @@ -52,194 +52,58 @@ name = "Configuration" visible = "! no_antenna" help = "Changing the channel may help with signal strength depending on neighbour WiFis" - [main.service.multissid] - ask = "Number of hotspots to broadcast" - type = "select" - choices.1 = "1" - choices.2 = "2" - choices.3 = "3" - visible = "! no_antenna" - - [main.hotspot1] - name = "Hotspot 1" + [main.hotspot] + name = "Hotspot" optional = false visible = "! no_antenna" - [main.hotspot1.wifi_ssid__1] + [main.hotspot.wifi_ssid] ask = "Name (SSID)" type = "string" - bind = "array_settings()" pattern.regexp = '^[\w \-]{1,32}$' pattern.error = "SSID in this app are limited to letter, number space, dash and underscores." - [main.hotspot1.wifi_secure__1] + [main.hotspot.wifi_secure] ask = "Secure" type = "boolean" - bind = "array_settings()" - [main.hotspot1.wifi_passphrase__1] + [main.hotspot.wifi_passphrase] ask = "Password (WPA2)" type = "string" - bind = "array_settings()" redact = true optional = true - visible = "wifi_secure__1" + visible = "wifi_secure" pattern.regexp = '^[a-zA-Z0-9]{8,63}$' pattern.error = "Only printable alphanumeric characters are permitted in your password. Maximal size 63 chars" - [main.hotspot1.advanced__1] + [main.hotspot.advanced] ask = "Advanced settings" type = "boolean" - bind = "array_settings()" - [main.hotspot1.ip4_nat_prefix__1] + [main.hotspot.ip4_nat_prefix] ask = "IPv4 NAT prefix (/24)" type = "string" - bind = "array_settings()" - visible = "advanced__1" + visible = "advanced" pattern.regexp = '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$' pattern.error = "Please provide a private /24 range in the format xxx.xxx.xxx" - [main.hotspot1.ip6_net__1] + [main.hotspot.ip6_net] ask = "IPv6 delegated prefix" type = "string" - bind = "array_settings()" optional = true - visible = "advanced__1" + visible = "advanced" pattern.regexp = '^[0-9a-fA-F:]+$' pattern.error = "Please provide a valid IPv6 Prefix" - [main.hotspot1.ip6_firewall__1] + [main.hotspot.ip6_firewall] ask = "IPv6 firewall" type = "boolean" - bind = "array_settings()" - visible = "advanced__1" + visible = "advanced" - [main.hotspot1.dns__1] + [main.hotspot.dns] ask = "DNS resolvers" type = "tags" - bind = "array_settings()" - visible = "advanced__1" - pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' - pattern.error = "Not an ip" - - [main.hotspot2] - name = "Hotspot 2" - visible = "! no_antenna && multissid >= 2" - - [main.hotspot2.wifi_ssid__2] - ask = "Name (SSID)" - type = "string" - bind = "array_settings()" - pattern.regexp = '^[\w \-]{1,32}$' - pattern.error = "SSID in this app are limited to letter, number space, dash and underscores." - - [main.hotspot2.wifi_secure__2] - ask = "Secure" - type = "boolean" - bind = "array_settings()" - - [main.hotspot2.wifi_passphrase__2] - ask = "Password (WPA2)" - type = "string" - bind = "array_settings()" - redact = true - visible = "wifi_secure__2" - pattern.regexp = '^[a-zA-Z0-9]{8,63}$' - pattern.error = "Only printable alphanumeric characters are permitted in your password. Maximal size 63 chars" - - [main.hotspot2.advanced__2] - ask = "Advanced settings" - type = "boolean" - bind = "array_settings()" - - [main.hotspot2.ip4_nat_prefix__2] - ask = "IPv4 NAT prefix (/24)" - type = "string" - bind = "array_settings()" - visible = "advanced__2" - pattern.regexp = '^[0-9.]{7,15}$' - pattern.error = "Please provide a valid IP" - - [main.hotspot2.ip6_net__2] - ask = "IPv6 delegated prefix" - type = "string" - bind = "array_settings()" - visible = "advanced__2" - pattern.regexp = '^[0-9a-fA-F:]+$' - pattern.error = "Please provide a valid IPv6 Prefix" - - [main.hotspot2.ip6_firewall__2] - ask = "IPv6 firewall" - type = "boolean" - bind = "array_settings()" - visible = "advanced__2" - - [main.hotspot2.dns__2] - ask = "DNS resolvers" - type = "tags" - bind = "array_settings()" - visible = "advanced__2" - pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' - pattern.error = "Not an ip" - - [main.hotspot3] - name = "Hotspot 3" - visible = "! no_antenna && multissid >= 3" - - [main.hotspot3.wifi_ssid__3] - ask = "Name (SSID)" - type = "string" - bind = "array_settings()" - pattern.regexp = '^[\w \-]{1,32}$' - pattern.error = "SSID in this app are limited to letter, number space, dash and underscores." - - [main.hotspot3.wifi_secure__3] - ask = "Secure" - type = "boolean" - bind = "array_settings()" - - [main.hotspot3.wifi_passphrase__3] - ask = "Password (WPA2)" - type = "string" - bind = "array_settings()" - redact = true - visible = "wifi_secure__3" - pattern.regexp = '^[a-zA-Z0-9]{8,63}$' - pattern.error = "Only printable alphanumeric characters are permitted in your password. Maximal size 63 chars" - - [main.hotspot3.advanced__3] - ask = "Advanced settings" - type = "boolean" - bind = "array_settings()" - - [main.hotspot3.ip4_nat_prefix__3] - ask = "IPv4 NAT prefix (/24)" - type = "string" - bind = "array_settings()" - visible = "advanced__3" - pattern.regexp = '^[0-9.]{7,15}$' - pattern.error = "Please provide a valid IP" - - [main.hotspot3.ip6_net__3] - ask = "IPv6 delegated prefix" - type = "string" - bind = "array_settings()" - visible = "advanced__3" - pattern.regexp = '^[0-9a-fA-F:]+$' - pattern.error = "Please provide a valid IPv6 Prefix" - - [main.hotspot3.ip6_firewall__3] - ask = "IPv6 firewall" - type = "boolean" - bind = "array_settings()" - visible = "advanced__3" - - [main.hotspot3.dns__3] - ask = "DNS resolvers" - type = "tags" - bind = "array_settings()" - visible = "advanced__3" + visible = "advanced" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" diff --git a/scripts/config b/scripts/config index 78fa748..e3fb11d 100644 --- a/scripts/config +++ b/scripts/config @@ -92,112 +92,49 @@ EOF echo "value: '$(ynh_app_setting_get $app wifi_device)'" } -get__array_settings() { - local short_setting="${1%%__*}" - local index="${1#*__}" - IFS='|' read -a values <<< "$(ynh_app_setting_get $app $short_setting)" - echo "value: \"${values[$(($index - 1))]:-}\"" -} - #================================================= # SPECIFIC VALIDATORS FOR TOML SHORT KEYS #================================================= -is_unique() { - local short_setting="$1" - local short_setting__1="$1__1" - local short_setting__2="$1__2" - local short_setting__3="$1__3" - if [[ "${!short_setting__1}" == "${!short_setting__2}" ]] - then - return 1 - elif [ "$multissid" -ge "3" ] && [[ "${!short_setting__1}" == "${!short_setting__3}" ]] - then - return 1 - elif [ "$multissid" -ge "3" ] && [[ "${!short_setting__2}" == "${!short_setting__3}" ]] - then - return 1 - fi - return 0 -} + validate__wifi_ssid() { - local wifi_ssid_var="wifi_ssid__$1" - if [ "$multissid" -ge "$1" ] && [[ -z "${!wifi_ssid_var}" ]] + if [[ -z "${wifi_ssid}" ]] then echo 'SSID required' fi - if ! is_unique wifi_ssid - then - echo 'All Wifi names must be unique' - fi } validate__wifi_passphrase() { - local wifi_secure_var="wifi_secure__$1" - local wifi_passphrase_var="wifi_passphrase__$1" - if [ "$multissid" -ge "$1" ] && [[ "${!wifi_secure_var}" == "1" ]] && [[ -z "${!wifi_passphrase_var}" ]] + if [[ "${wifi_secure}" == "1" ]] && [[ -z "${wifi_passphrase}" ]] then echo 'In WPA2 secure mode, you need to provide a passphrase' fi } validate__ip4_nat_prefix() { - local ip4_nat_prefix_var="ip4_nat_prefix__$1" - if [ "$multissid" -ge "$1" ] && [[ -z "${!ip4_nat_prefix_var}" ]] + if [[ -z "${ip4_nat_prefix}" ]] then echo 'Private IPv4 nat prefix required' fi - if ! is_unique ip4_nat_prefix - then - echo 'All IPv4 prefix must be unique' - fi } validate__dns() { - local dns_var="dns__$1" - local ip6_net_var="dns__$1" - if [ "$multissid" -ge "$1" ] && ! echo "${!dns_var}" | grep -q "\." + if ! echo "${dns}" | grep -q "\." then echo 'IPv4 DNS required' fi - if [ "$multissid" -ge "$1" ] && [[ -n "${!ip6_net_var}" ]] && ! echo "${!dns_var}" | grep -q ":" + if [[ -n "${ip6_net}" ]] && ! echo "${dns}" | grep -q ":" then echo 'IPv6 DNS required' fi } -validate__array_settings() { - local short_setting="${1%%__*}" - local index="${1#*__}" - if type -t validate__$short_setting | grep -q '^function$' 2>/dev/null; - then - validate__$short_setting $index - fi -} - #================================================= # SPECIFIC SETTERS FOR TOML SHORT KEYS #================================================= -set__array_settings() { - local short_setting="${1%%__*}" - local index="${1#*__}" - local type="${types[$1]}" - local value="${!1}" - if [[ "$type" == "string" ]] && [ "$multissid" -lt "$index" ] - then - value="" - fi - local values="$(ynh_app_setting_get $app $short_setting | awk "BEGIN{FS=OFS=\"|\"} {\$${index}=\"${value}\"}"1)" - ynh_app_setting_set --app=$app --key=$short_setting --value="$values" - ynh_print_info --message="Configuration key '$short_setting' edited in app settings" -} - #================================================= # OVERWRITING VALIDATE STEP #================================================= -ynh_app_config_validate() { - _ynh_app_config_validate -} #================================================= # OVERWRITING APPLY STEP @@ -209,7 +146,20 @@ ynh_app_config_apply() { /usr/local/bin/ynh-hotspot stop _ynh_app_config_apply + + if [ "${wifi_secure}" -eq 1 ]; then + local sec_comment="" + else + local sec_comment="#" + fi + + ynh_add_config --template="/etc/hostapd/hostapd.base.conf" --destination="/etc/hostapd/hostapd-${wifi_device}.conf" + ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination"/etc/dnsmasq.dhcpdv4-ssid-${wifi_device}.conf" + if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then + ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" --destination"/etc/dnsmasq.dhcpdv6-ssid-${wifi_device}.conf" + fi + # Start vpn client ynh_print_info --message="Starting hotspot service if needed" /usr/local/bin/ynh-hotspot start diff --git a/scripts/install b/scripts/install index e82036b..090700b 100644 --- a/scripts/install +++ b/scripts/install @@ -132,7 +132,6 @@ fi hot_reload_usb_wifi_cards wifi_device=$(iw_devices | awk -F\| '{ print $1 }') -ynh_app_setting_set --app=$app --key=multissid --value=1 ynh_app_setting_set --app=$app --key=ssid_nb --value=1 ynh_app_setting_set --app=$app --key=wifi_ssid --value="${wifi_ssid}" ynh_app_setting_set --app=$app --key=wifi_secure --value=1 From 1a4030a094057a570a190262c96e49366e2e162a Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 15:38:01 +0200 Subject: [PATCH 011/111] fix syntax error --- scripts/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/config b/scripts/config index e3fb11d..8655dfd 100644 --- a/scripts/config +++ b/scripts/config @@ -154,10 +154,10 @@ ynh_app_config_apply() { fi ynh_add_config --template="/etc/hostapd/hostapd.base.conf" --destination="/etc/hostapd/hostapd-${wifi_device}.conf" - ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination"/etc/dnsmasq.dhcpdv4-ssid-${wifi_device}.conf" + ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.dhcpdv4-ssid-${wifi_device}.conf" if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" --destination"/etc/dnsmasq.dhcpdv6-ssid-${wifi_device}.conf" + ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.dhcpdv6-ssid-${wifi_device}.conf" fi # Start vpn client From c05667cdbb8515813c9a05dd701c1c152fa777f0 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 16:20:20 +0200 Subject: [PATCH 012/111] fix dns config --- conf/ynh-hotspot | 15 ++------------- config_panel.toml | 1 + scripts/config | 18 ++++++++++++++++++ scripts/install | 3 ++- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index aaf0b96..d375730 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -223,21 +223,10 @@ if [ "$1" != restart ]; then wifi_secure=$(ynh_app_setting_get hotspot wifi_secure) wifi_passphrase=$(ynh_app_setting_get hotspot wifi_passphrase) ip6_firewall=$(ynh_app_setting_get hotspot ip6_firewall) + ip6_dns=$(ynh_app_setting_get hotspot ip6_dns) ip6_net=$(ynh_app_setting_get hotspot ip6_net) - dns=$(ynh_app_setting_get hotspot dns) + ip4_dns=$(ynh_app_setting_get hotspot ip4_dns) ip4_nat_prefix=$(ynh_app_setting_get hotspot ip4_nat_prefix) - ip6_dns="" - ip4_dns="" - for ip in $(echo "${dns}" | tr ',' ' '); do - if [[ "$ip" == *":"* ]]; then - ip6_dns+="[$ip]," - else - ip4_dns+="$ip," - fi - done - # Remove trailing , - ip6_dns="${ip6_dns%%,}" - ip4_dns="${ip4_dns%%,}" old_gateway_interface=$(ynh_app_setting_get hotspot gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') diff --git a/config_panel.toml b/config_panel.toml index 71a9384..ff286d0 100644 --- a/config_panel.toml +++ b/config_panel.toml @@ -103,6 +103,7 @@ name = "Configuration" [main.hotspot.dns] ask = "DNS resolvers" type = "tags" + bind = "null" visible = "advanced" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" diff --git a/scripts/config b/scripts/config index 8655dfd..c683ae8 100644 --- a/scripts/config +++ b/scripts/config @@ -132,6 +132,24 @@ validate__dns() { # SPECIFIC SETTERS FOR TOML SHORT KEYS #================================================= +set__dns() { + ip6_dns="" + ip4_dns="" + for ip in $(echo "${dns}" | tr ',' ' '); do + if [[ "$ip" == *":"* ]]; then + ip6_dns+="[$ip]," + else + ip4_dns+="$ip," + fi + done + # Remove trailing , + ip6_dns="${ip6_dns%%,}" + ip4_dns="${ip4_dns%%,}" + + ynh_app_setting_set $app ip6_dns "${ip6_dns}" + ynh_app_setting_set $app ip4_dns "${ip4_dns}" +} + #================================================= # OVERWRITING VALIDATE STEP #================================================= diff --git a/scripts/install b/scripts/install index 090700b..4fa79f4 100644 --- a/scripts/install +++ b/scripts/install @@ -139,8 +139,9 @@ ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase} ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" ynh_app_setting_set --app=$app --key=wifi_channel --value=6 ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 +ynh_app_setting_set --app=$app --key=ip6_dns --value="" ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" -ynh_app_setting_set --app=$app --key=dns --value="10.0.242.1" +ynh_app_setting_set --app=$app --key=ip4_dns --value="10.0.242.1" ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value=10.0.242 if [[ -z $wifi_device ]]; then From 4e55c614627edc2139193b20651db4479a57ba5e Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 16:31:09 +0200 Subject: [PATCH 013/111] fix dns config getter --- scripts/config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/config b/scripts/config index c683ae8..61919a0 100644 --- a/scripts/config +++ b/scripts/config @@ -92,6 +92,14 @@ EOF echo "value: '$(ynh_app_setting_get $app wifi_device)'" } +get__dns() { + ip6_dns=$(ynh_app_setting_get $app ip6_dns | tr -d '[' | tr -d ']') + ip4_dns=$(ynh_app_setting_get $app ip4_dns) + + echo "value: ${ip4_dns},${ip6_dns}" +} + + #================================================= # SPECIFIC VALIDATORS FOR TOML SHORT KEYS #================================================= From 982f4ebe3e3521eaa8a8f84f702fdc196128aada Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 16:36:39 +0200 Subject: [PATCH 014/111] fix dnsmasq config --- scripts/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/config b/scripts/config index 61919a0..11e4f3d 100644 --- a/scripts/config +++ b/scripts/config @@ -180,10 +180,10 @@ ynh_app_config_apply() { fi ynh_add_config --template="/etc/hostapd/hostapd.base.conf" --destination="/etc/hostapd/hostapd-${wifi_device}.conf" - ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.dhcpdv4-ssid-${wifi_device}.conf" + ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.dhcpdv6-ssid-${wifi_device}.conf" + ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" fi # Start vpn client From 64075f2dd99547277a94da6677fbd5e06bb48f6e Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 17:14:13 +0200 Subject: [PATCH 015/111] handle new dns settings --- scripts/upgrade | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/scripts/upgrade b/scripts/upgrade index 72a98b4..a27b9b0 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -72,6 +72,31 @@ if [ -z $service_name ]; then ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi +dns=$(ynh_app_setting_get $app dns) +if [[ -n "${dns}" ]]; then + ip6_dns="" + ip4_dns="" + for ip in $(echo "${dns}" | tr ',' ' '); do + if [[ "$ip" == *":"* ]]; then + ip6_dns+="[$ip]," + else + ip4_dns+="$ip," + fi + done + # Remove trailing , + ip6_dns="${ip6_dns%%,}" + ip4_dns="${ip4_dns%%,}" + + if [[ -z "$(ynh_app_setting_get --app=$app --key=ip6_dns)" ]]; then + ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" + if + if [[ -z "$(ynh_app_setting_get --app=$app --key=ip4_dns)" ]]; then + ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" + if + + ynh_app_setting_delete $app dns +fi + # Old stuff prior to 2.x ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) From 5b4c94c042a105e4b3945df9dc2adc4fdfdd5848 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 18:02:20 +0200 Subject: [PATCH 016/111] convert multissid settings --- scripts/install | 1 - scripts/upgrade | 25 ++++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/scripts/install b/scripts/install index 4fa79f4..b3fa5fb 100644 --- a/scripts/install +++ b/scripts/install @@ -132,7 +132,6 @@ fi hot_reload_usb_wifi_cards wifi_device=$(iw_devices | awk -F\| '{ print $1 }') -ynh_app_setting_set --app=$app --key=ssid_nb --value=1 ynh_app_setting_set --app=$app --key=wifi_ssid --value="${wifi_ssid}" ynh_app_setting_set --app=$app --key=wifi_secure --value=1 ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase}" diff --git a/scripts/upgrade b/scripts/upgrade index a27b9b0..93e6690 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -72,7 +72,26 @@ if [ -z $service_name ]; then ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi -dns=$(ynh_app_setting_get $app dns) +multissid=$(ynh_app_setting_get --app=$app --key=multissid) +if [[ -n ${multissid} ]] && [[ ${multissid} -gt 1 ]]; then + wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid | cut -d'|' -f 1) + wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure | cut -d'|' -f 1) + wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase | cut -d'|' -f 1) + ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix | cut -d'|' -f 1) + ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net | cut -d'|' -f 1) + ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall | cut -d'|' -f 1) + dns=$(ynh_app_setting_get --app=$app --key=dns | cut -d'|' -f 1) + + ynh_app_setting_set --app=$app --key=wifi_ssid --value="${wifi_ssid}" + ynh_app_setting_set --app=$app --key=wifi_secure --value="${wifi_secure}" + ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase}" + ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}" + ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" + ynh_app_setting_set --app=$app --key=ip6_firewall --value="${ip6_firewall}" +else + dns=$(ynh_app_setting_get --app=$app --key=dns) +fi + if [[ -n "${dns}" ]]; then ip6_dns="" ip4_dns="" @@ -97,6 +116,10 @@ if [[ -n "${dns}" ]]; then ynh_app_setting_delete $app dns fi +if [[ -n ${multissid} ]]; then + ynh_app_setting_delete --app=$app --key=multissid +fi + # Old stuff prior to 2.x ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) From c077514062276b317ac80b6bc751c6c67ca0d6f5 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 18:15:58 +0200 Subject: [PATCH 017/111] typo --- scripts/upgrade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/upgrade b/scripts/upgrade index 93e6690..a636f93 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -111,7 +111,7 @@ if [[ -n "${dns}" ]]; then if if [[ -z "$(ynh_app_setting_get --app=$app --key=ip4_dns)" ]]; then ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" - if + fi ynh_app_setting_delete $app dns fi From 186ec93570df979d0348208ccfa4d6cd4324cc80 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 20 Aug 2023 18:35:02 +0200 Subject: [PATCH 018/111] typo bis --- scripts/upgrade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/upgrade b/scripts/upgrade index a636f93..c6878d4 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -108,7 +108,7 @@ if [[ -n "${dns}" ]]; then if [[ -z "$(ynh_app_setting_get --app=$app --key=ip6_dns)" ]]; then ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" - if + fi if [[ -z "$(ynh_app_setting_get --app=$app --key=ip4_dns)" ]]; then ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" fi From 40534c2ac4b36be88ae1b30148e17aaf55a9d5c1 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 10:51:37 +0200 Subject: [PATCH 019/111] prefill dns settings from nat and ipv6 prefix --- scripts/config | 21 ++++++++++++++++++--- scripts/install | 7 ++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/scripts/config b/scripts/config index 11e4f3d..9c73781 100644 --- a/scripts/config +++ b/scripts/config @@ -93,13 +93,20 @@ EOF } get__dns() { - ip6_dns=$(ynh_app_setting_get $app ip6_dns | tr -d '[' | tr -d ']') + ip6_dns=$(ynh_app_setting_get $app ip6_dns | tr -d '[]') ip4_dns=$(ynh_app_setting_get $app ip4_dns) - + + if [[ -n ${ip6_net} ]] && [[ -z ${ip6_dns} ]]; then + ip6_dns="${ip6_net}1" + fi + + if [[ -n ${ip4_nat_prefix} ]] && [[ -z ${ip4_dns} ]]; then + ip4_dns="${ip4_nat_prefix}.1" + fi + echo "value: ${ip4_dns},${ip6_dns}" } - #================================================= # SPECIFIC VALIDATORS FOR TOML SHORT KEYS #================================================= @@ -154,6 +161,14 @@ set__dns() { ip6_dns="${ip6_dns%%,}" ip4_dns="${ip4_dns%%,}" + if [[ -n ${ip6_net} ]] && [[ -z ${ip6_dns} ]]; then + ip6_dns="${ip6_net}1" + fi + + if [[ -n ${ip4_nat_prefix} ]] && [[ -z ${ip4_dns} ]]; then + ip4_dns="${ip4_nat_prefix}.1" + fi + ynh_app_setting_set $app ip6_dns "${ip6_dns}" ynh_app_setting_set $app ip4_dns "${ip4_dns}" } diff --git a/scripts/install b/scripts/install index b3fa5fb..8bc4568 100644 --- a/scripts/install +++ b/scripts/install @@ -117,14 +117,15 @@ ynh_system_user_create --username=$app #================================================= ynh_script_progression --message="Configuring hotspot..." +ip6_net="" +ip6_dns="" if [[ ! -v ip6_net ]]; then # if ip6_net not set - ip6_net="" - if [[ -e /tmp/.ynh-vpnclient-started ]]; then vpnclient_ip6_net=$(ynh_app_setting_get vpnclient ip6_net 2>&1) if [[ $vpnclient_ip6_net =~ :: ]]; then ip6_net=${vpnclient_ip6_net} + ip6_dns="${ip6_net}1" fi fi fi @@ -138,7 +139,7 @@ ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase} ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" ynh_app_setting_set --app=$app --key=wifi_channel --value=6 ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 -ynh_app_setting_set --app=$app --key=ip6_dns --value="" +ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" ynh_app_setting_set --app=$app --key=ip4_dns --value="10.0.242.1" ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value=10.0.242 From ee1f3cd176d49e4fcf2ff18908d8679a870fa532 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 10:52:04 +0200 Subject: [PATCH 020/111] fix restore --- scripts/backup | 5 +---- scripts/restore | 6 ++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/scripts/backup b/scripts/backup index 8634184..793ab93 100644 --- a/scripts/backup +++ b/scripts/backup @@ -40,10 +40,7 @@ ynh_print_info --message="Declaring files to be backed up..." # BACKUP THE APP MAIN DIR #================================================= -for FILE in $(ls /etc/hostapd/hostapd.*.conf 2>/dev/null) -do - ynh_backup --src_path="$FILE" -done +ynh_backup --src_path="/etc/hostapd/hostapd.conf" ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" diff --git a/scripts/restore b/scripts/restore index ee9580e..4c4ff7e 100644 --- a/scripts/restore +++ b/scripts/restore @@ -61,10 +61,8 @@ else pkg_dependencies="$pkg_dependencies $free_firmware_packages" fi -for FILE in $(ls /etc/hostapd/hostapd.conf{.tpl?,} 2>/dev/null) -do - ynh_restore_file --origin_path="$FILE" -done +ynh_restore_file --origin_path="/etc/hostapd/hostapd.conf" + ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" From 676e6d973399f1242491d3bc5462692f574df4f2 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 11:04:39 +0200 Subject: [PATCH 021/111] backup, restore & remove config files --- scripts/backup | 5 +++++ scripts/remove | 11 +++++++---- scripts/restore | 5 +++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/scripts/backup b/scripts/backup index 793ab93..6dfa463 100644 --- a/scripts/backup +++ b/scripts/backup @@ -30,6 +30,7 @@ app=$YNH_APP_INSTANCE_NAME final_path=$(ynh_app_setting_get --app=$app --key=final_path) firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) +wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # DECLARE DATA AND CONF FILES TO BACKUP @@ -41,9 +42,13 @@ ynh_print_info --message="Declaring files to be backed up..." #================================================= ynh_backup --src_path="/etc/hostapd/hostapd.conf" +ynh_backup --src_path="/etc/hostapd/hostapd-${wifi_device}.conf" ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" +ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" + ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" +ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" ynh_backup --src_path="/usr/local/bin/$service_name" diff --git a/scripts/remove b/scripts/remove index ae210dd..6510749 100644 --- a/scripts/remove +++ b/scripts/remove @@ -18,6 +18,7 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) +wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # STANDARD REMOVE @@ -67,11 +68,13 @@ done # Remove confs ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" +ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" + ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" -for FILE in $(ls /etc/hostapd/hostapd.*.conf 2>/dev/null) -do - ynh_secure_remove --file="$FILE" -done +ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" + +ynh_secure_remove --file="/etc/hostapd/hostapd.conf" +ynh_secure_remove --file="/etc/hostapd/hostapd-${wifi_device}.conf" #================================================= # CLOSE A PORT diff --git a/scripts/restore b/scripts/restore index 4c4ff7e..b4f5fe8 100644 --- a/scripts/restore +++ b/scripts/restore @@ -29,6 +29,7 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) +wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # CHECK IF THE APP CAN BE RESTORED @@ -62,9 +63,13 @@ else fi ynh_restore_file --origin_path="/etc/hostapd/hostapd.conf" +ynh_restore_file --origin_path="/etc/hostapd/hostapd-${wifi_device}.conf" ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" +ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" + ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" +ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" ynh_restore_file --origin_path="/usr/local/bin/$service_name" From d6b5dc62c53936a932aa27418130c43f23ba2e54 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 16:17:22 +0200 Subject: [PATCH 022/111] custom systemd config for hostapd to handle multiple instances --- conf/{hostapd.base.conf => hostapd.conf.tpl} | 0 conf/systemd_hostapd.service | 21 ++++++++++++++++++++ conf/ynh-hotspot | 6 +++--- scripts/backup | 2 +- scripts/config | 7 ++++++- scripts/install | 10 +++------- scripts/remove | 2 +- scripts/restore | 2 +- scripts/upgrade | 11 ++++++++++ 9 files changed, 47 insertions(+), 14 deletions(-) rename conf/{hostapd.base.conf => hostapd.conf.tpl} (100%) create mode 100644 conf/systemd_hostapd.service diff --git a/conf/hostapd.base.conf b/conf/hostapd.conf.tpl similarity index 100% rename from conf/hostapd.base.conf rename to conf/hostapd.conf.tpl diff --git a/conf/systemd_hostapd.service b/conf/systemd_hostapd.service new file mode 100644 index 0000000..c2b3146 --- /dev/null +++ b/conf/systemd_hostapd.service @@ -0,0 +1,21 @@ +[Unit] +Documentation=man:systemd-sysv-generator(8) +SourcePath=/etc/init.d/hostapd +Description=LSB: Advanced IEEE 802.11 management daemon +After=remote-fs.target +After=network-online.target +Wants=network-online.target + +[Service] +Type=forking +Restart=no +TimeoutSec=5min +IgnoreSIGPIPE=no +KillMode=process +GuessMainPID=no +RemainAfterExit=yes +SuccessExitStatus=5 6 +Environment=DAEMON_CONF=/etc/hostapd/hostapd-%i.conf +ExecStart=/etc/init.d/hostapd start +ExecStop=/etc/init.d/hostapd stop +ExecReload=/etc/init.d/hostapd reload diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index d375730..adf5194 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -63,7 +63,7 @@ is_dhcpd4_running() { } is_hostapd_running() { - systemctl is-active hostapd &>/dev/null + systemctl is-active "hostapd@${wifi_device}" &>/dev/null } is_running() { @@ -206,7 +206,7 @@ stop_dhcpd() { } stop_hostapd() { - systemctl stop hostapd + systemctl stop "hostapd@${wifi_device}" } if [ "$1" != restart ]; then @@ -275,7 +275,7 @@ start) configure_hostapd echo "Starting hostapd..." - if ! systemctl start hostapd; then + if ! systemctl start "hostapd@${wifi_device}"; then journalctl -u hostapd -n 100 --no-hostname --no-pager exit 1 fi diff --git a/scripts/backup b/scripts/backup index 6dfa463..ba2defc 100644 --- a/scripts/backup +++ b/scripts/backup @@ -41,7 +41,7 @@ ynh_print_info --message="Declaring files to be backed up..." # BACKUP THE APP MAIN DIR #================================================= -ynh_backup --src_path="/etc/hostapd/hostapd.conf" +ynh_backup --src_path="/etc/hostapd/hostapd.conf.tpl" ynh_backup --src_path="/etc/hostapd/hostapd-${wifi_device}.conf" ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" diff --git a/scripts/config b/scripts/config index 9c73781..34f5794 100644 --- a/scripts/config +++ b/scripts/config @@ -181,6 +181,7 @@ set__dns() { # OVERWRITING APPLY STEP #================================================= ynh_app_config_apply() { + old_wifi_device=$(ynh_app_setting_get $app wifi_device) # Stop vpn client ynh_print_info --message="Stopping hotspot in order to edit files" @@ -194,7 +195,11 @@ ynh_app_config_apply() { local sec_comment="#" fi - ynh_add_config --template="/etc/hostapd/hostapd.base.conf" --destination="/etc/hostapd/hostapd-${wifi_device}.conf" + ynh_secure_remove --file="/etc/hostapd/hostapd-${old_wifi_device}.conf" + ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${old_wifi_device}.conf" + ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${old_wifi_device}.conf" + + ynh_add_config --template="/etc/hostapd/hostapd.conf.tpl" --destination="/etc/hostapd/hostapd-${wifi_device}.conf" ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then diff --git a/scripts/install b/scripts/install index 8bc4568..1e9504c 100644 --- a/scripts/install +++ b/scripts/install @@ -158,7 +158,7 @@ ynh_script_progression --message="Copying configuration files..." mkdir -pm 0755 /etc/dnsmasq.dhcpd/ chown root: /etc/dnsmasq.dhcpd/ -install -b -o root -g root -m 0644 ../conf/hostapd.*.conf /etc/hostapd/ +install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/hostapd.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl @@ -179,14 +179,8 @@ install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/ ynh_script_progression --message="Configuring hostapd..." ## hostapd -ynh_replace_string --match_string="^DAEMON_CONF=$" --replace_string="&/etc/hostapd/hostapd.conf" --target_file=/etc/init.d/hostapd ynh_store_file_checksum --file="/etc/init.d/hostapd" -# We also need to put this in /etc/default/hostapd because on some setup -# like RPi, the version of hostapd is different and /etc/init.d/hostapd -# isnt used ... instead the service is "pure systemd" ... -echo "DAEMON_CONF=/etc/hostapd/hostapd.conf" > /etc/default/hostapd - # Set default inits # The boot order of these services are important, so they are disabled by default # and the ynh-hotspot service handles them. @@ -201,6 +195,8 @@ ynh_script_progression --message="Configuring a systemd service..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name +# Create custom systemd config for hostapd to handle multiple wifi devices +ynh_add_systemd_config --service="hostapd@.service" --template="../conf/systemd_hostapd.service" #================================================= # INTEGRATE SERVICE IN YUNOHOST diff --git a/scripts/remove b/scripts/remove index 6510749..408f31a 100644 --- a/scripts/remove +++ b/scripts/remove @@ -73,7 +73,7 @@ ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" -ynh_secure_remove --file="/etc/hostapd/hostapd.conf" +ynh_secure_remove --file="/etc/hostapd/hostapd.conf.tpl" ynh_secure_remove --file="/etc/hostapd/hostapd-${wifi_device}.conf" #================================================= diff --git a/scripts/restore b/scripts/restore index b4f5fe8..e4d982b 100644 --- a/scripts/restore +++ b/scripts/restore @@ -62,7 +62,7 @@ else pkg_dependencies="$pkg_dependencies $free_firmware_packages" fi -ynh_restore_file --origin_path="/etc/hostapd/hostapd.conf" +ynh_restore_file --origin_path="/etc/hostapd/hostapd.conf.tpl" ynh_restore_file --origin_path="/etc/hostapd/hostapd-${wifi_device}.conf" ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" diff --git a/scripts/upgrade b/scripts/upgrade index c6878d4..9ef90ed 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -120,6 +120,15 @@ if [[ -n ${multissid} ]]; then ynh_app_setting_delete --app=$app --key=multissid fi +ynh_secure_remove --file="/etc/hostapd/hostapd.conf" +ynh_secure_remove --file="/etc/hostapd/hostapd.base.conf" +ynh_secure_remove --file="/etc/hostapd/hostapd.accesspoint.conf" + +for FILE in $(ls /etc/dnsmasq.dhcpd/dhcpdv{4,6}-ssid{0..3}.conf 2>/dev/null); do + ynh_secure_remove --file="$FILE" + ynh_secure_remove --file="$FILE" +done + # Old stuff prior to 2.x ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) @@ -203,6 +212,8 @@ ynh_script_progression --message="Upgrading systemd configuration..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name +# Create custom systemd config for hostapd to handle multiple wifi devices +ynh_add_systemd_config --service="hostapd@.service" --template="../conf/systemd_hostapd.service" #================================================= # GENERIC FINALIZATION From 538826b32e88ab3f448e0d8f5df6e7e957dd0e12 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 18:43:20 +0200 Subject: [PATCH 023/111] enable multi instance --- conf/openvpn_90-hotspot | 2 +- conf/systemd.service | 4 +-- conf/systemd_hostapd.service | 15 ++-------- conf/ynh-hotspot | 55 +++++++++++++++++++++--------------- manifest.json | 2 +- scripts/backup | 20 ++++++------- scripts/config | 18 ++++-------- scripts/install | 42 ++++++++++++++++----------- scripts/remove | 29 +++++++++---------- scripts/restore | 23 +++++++-------- scripts/upgrade | 30 +++++++++++--------- 11 files changed, 119 insertions(+), 121 deletions(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index 5964ef2..98bb699 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -1,3 +1,3 @@ #!/bin/bash -systemctl restart ynh-hotspot +systemctl restart __SERVICE_NAME__ diff --git a/conf/systemd.service b/conf/systemd.service index 6268e69..1537aca 100644 --- a/conf/systemd.service +++ b/conf/systemd.service @@ -6,8 +6,8 @@ After=network.target [Service] Type=oneshot User=root -ExecStart=/usr/local/bin/ynh-hotspot start -ExecStop=/usr/local/bin/ynh-hotspot stop +ExecStart=/usr/local/bin/__SERVICE_NAME__ start +ExecStop=/usr/local/bin/__SERVICE_NAME__ stop RemainAfterExit=yes [Install] diff --git a/conf/systemd_hostapd.service b/conf/systemd_hostapd.service index c2b3146..14ad3eb 100644 --- a/conf/systemd_hostapd.service +++ b/conf/systemd_hostapd.service @@ -1,21 +1,12 @@ [Unit] -Documentation=man:systemd-sysv-generator(8) -SourcePath=/etc/init.d/hostapd Description=LSB: Advanced IEEE 802.11 management daemon After=remote-fs.target After=network-online.target Wants=network-online.target [Service] -Type=forking +Type=simple Restart=no TimeoutSec=5min -IgnoreSIGPIPE=no -KillMode=process -GuessMainPID=no -RemainAfterExit=yes -SuccessExitStatus=5 6 -Environment=DAEMON_CONF=/etc/hostapd/hostapd-%i.conf -ExecStart=/etc/init.d/hostapd start -ExecStop=/etc/init.d/hostapd stop -ExecReload=/etc/init.d/hostapd reload +ExecStart=/usr/sbin/hostapd /etc/hostapd/__APP__/hostapd.conf +ExecReload=/bin/kill -HEP $MAINPID diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index adf5194..5f3428f 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -63,7 +63,13 @@ is_dhcpd4_running() { } is_hostapd_running() { - systemctl is-active "hostapd@${wifi_device}" &>/dev/null + systemctl is-active "hostapd@${app}" &>/dev/null +} + +is_other_hostapd_running() { + other_hostapd_services=$(systemctl list-units --state=running hostapd@*.service | grep -v "^hostapd@$app.service") + + [[ -n "${other_hostapd_service}" ]] } is_running() { @@ -206,7 +212,7 @@ stop_dhcpd() { } stop_hostapd() { - systemctl stop "hostapd@${wifi_device}" + systemctl stop "hostapd@${app}" } if [ "$1" != restart ]; then @@ -215,20 +221,21 @@ if [ "$1" != restart ]; then echo -n "Retrieving Yunohost settings... " - service_enabled=$(systemctl is-enabled ynh-hotspot) - wifi_device=$(ynh_app_setting_get hotspot wifi_device) - wifi_channel=$(ynh_app_setting_get hotspot wifi_channel) + app=__APP__ + service_enabled=$(ynh_app_setting_get --app=$app --key=service_enabled) + wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) + wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel) - wifi_ssid=$(ynh_app_setting_get hotspot wifi_ssid) - wifi_secure=$(ynh_app_setting_get hotspot wifi_secure) - wifi_passphrase=$(ynh_app_setting_get hotspot wifi_passphrase) - ip6_firewall=$(ynh_app_setting_get hotspot ip6_firewall) - ip6_dns=$(ynh_app_setting_get hotspot ip6_dns) - ip6_net=$(ynh_app_setting_get hotspot ip6_net) - ip4_dns=$(ynh_app_setting_get hotspot ip4_dns) - ip4_nat_prefix=$(ynh_app_setting_get hotspot ip4_nat_prefix) + wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) + wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) + wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) + ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall) + ip6_dns=$(ynh_app_setting_get --app=$app --key=ip6_dns) + ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) + ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) + ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) - old_gateway_interface=$(ynh_app_setting_get hotspot gateway_interface) + old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') echo "OK" @@ -252,7 +259,7 @@ start) fi echo "[hotspot] Starting..." - touch /tmp/.ynh-hotspot-started + touch /tmp/.${service_name}-started # Check old state of the ipv4 NAT settings if [[ -n "${old_gateway_interface}" ]] && [[ "${new_gateway_interface}" != "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then @@ -275,7 +282,7 @@ start) configure_hostapd echo "Starting hostapd..." - if ! systemctl start "hostapd@${wifi_device}"; then + if ! systemctl start "hostapd@${app}"; then journalctl -u hostapd -n 100 --no-hostname --no-pager exit 1 fi @@ -292,16 +299,18 @@ start) ;; stop) echo "[hotspot] Stopping..." - rm -f /tmp/.ynh-hotspot-started + rm -f /tmp/.${service_name}-started - if [[ -n "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then - echo "Unset NAT" - unset_nat "${old_gateway_interface}" + if ! is_other_hostapd_running; then + if [[ -n "${old_gateway_interface}" ]] && is_nat_set "${old_gateway_interface}"; then + echo "Unset NAT" + unset_nat "${old_gateway_interface}" + fi + + echo "Unset forwarding" + unset_forwarding fi - echo "Unset forwarding" - unset_forwarding - unset_ipaddr unset_ipfirewall stop_dhcpd diff --git a/manifest.json b/manifest.json index f2ecc47..efcda4f 100644 --- a/manifest.json +++ b/manifest.json @@ -21,7 +21,7 @@ "requirements": { "yunohost": ">= 4.3.2" }, - "multi_instance": false, + "multi_instance": true, "services": [], "arguments": { "install" : [ diff --git a/scripts/backup b/scripts/backup index ba2defc..cbf88e1 100644 --- a/scripts/backup +++ b/scripts/backup @@ -30,7 +30,6 @@ app=$YNH_APP_INSTANCE_NAME final_path=$(ynh_app_setting_get --app=$app --key=final_path) firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) -wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # DECLARE DATA AND CONF FILES TO BACKUP @@ -41,21 +40,19 @@ ynh_print_info --message="Declaring files to be backed up..." # BACKUP THE APP MAIN DIR #================================================= -ynh_backup --src_path="/etc/hostapd/hostapd.conf.tpl" -ynh_backup --src_path="/etc/hostapd/hostapd-${wifi_device}.conf" +ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf.tpl" +ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf" -ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" -ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" +ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" +ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf" -ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" -ynh_backup --src_path="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" +ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" +ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf" ynh_backup --src_path="/usr/local/bin/$service_name" -ynh_backup --src_path="/etc/openvpn/scripts/route-up.d/90-hotspot" -ynh_backup --src_path="/etc/openvpn/scripts/route-down.d/90-hotspot" - -ynh_backup --src_path="/etc/init.d/hostapd" +ynh_backup --src_path="/etc/openvpn/scripts/route-up.d/90-$service_name" +ynh_backup --src_path="/etc/openvpn/scripts/route-down.d/90-$service_name" #================================================= # SPECIFIC BACKUP @@ -64,6 +61,7 @@ ynh_backup --src_path="/etc/init.d/hostapd" #================================================= ynh_backup --src_path="/etc/systemd/system/$service_name.service" +ynh_backup --src_path="/etc/systemd/system/hostapd@$app.service" #================================================= # END OF SCRIPT diff --git a/scripts/config b/scripts/config index 34f5794..4c7c40c 100644 --- a/scripts/config +++ b/scripts/config @@ -181,11 +181,9 @@ set__dns() { # OVERWRITING APPLY STEP #================================================= ynh_app_config_apply() { - old_wifi_device=$(ynh_app_setting_get $app wifi_device) - # Stop vpn client ynh_print_info --message="Stopping hotspot in order to edit files" - /usr/local/bin/ynh-hotspot stop + /usr/local/bin/${service_name} stop _ynh_app_config_apply @@ -195,20 +193,16 @@ ynh_app_config_apply() { local sec_comment="#" fi - ynh_secure_remove --file="/etc/hostapd/hostapd-${old_wifi_device}.conf" - ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${old_wifi_device}.conf" - ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${old_wifi_device}.conf" - - ynh_add_config --template="/etc/hostapd/hostapd.conf.tpl" --destination="/etc/hostapd/hostapd-${wifi_device}.conf" - ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" + ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" fi - # Start vpn client + # Start hotspot ynh_print_info --message="Starting hotspot service if needed" - /usr/local/bin/ynh-hotspot start + /usr/local/bin/${service_name} start } diff --git a/scripts/install b/scripts/install index 1e9504c..725312f 100644 --- a/scripts/install +++ b/scripts/install @@ -30,7 +30,7 @@ firmware_nonfree=$YNH_APP_ARG_FIRMWARE_NONFREE app=$YNH_APP_INSTANCE_NAME # the service name must match the service template files -service_name='ynh-hotspot' +service_name=ynh-$app #================================================= # CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS @@ -130,6 +130,13 @@ if [[ ! -v ip6_net ]]; then # if ip6_net not set fi fi +ip4_nat_prefix_index=${app##*__} +if [[ "${ip4_nat_prefix_index}" == "${app}" ]]; then + ip4_nat_prefix_index=0 +fi +ip4_nat_prefix="10.${ip4_nat_prefix_index}.242" +ip4_dns="${ip4_nat_prefix}.1" + hot_reload_usb_wifi_cards wifi_device=$(iw_devices | awk -F\| '{ print $1 }') @@ -141,8 +148,8 @@ ynh_app_setting_set --app=$app --key=wifi_channel --value=6 ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" -ynh_app_setting_set --app=$app --key=ip4_dns --value="10.0.242.1" -ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value=10.0.242 +ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" +ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}" if [[ -z $wifi_device ]]; then ynh_app_setting_set --app=$app --key=service_enabled --value=0 @@ -155,32 +162,33 @@ fi #================================================= ynh_script_progression --message="Copying configuration files..." -mkdir -pm 0755 /etc/dnsmasq.dhcpd/ -chown root: /etc/dnsmasq.dhcpd/ +mkdir -pm 0755 /etc/hostapd/$app/ +chown root: /etc/hostapd/$app/ -install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/hostapd.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl +mkdir -pm 0755 /etc/dnsmasq.$app/ +chown root: /etc/dnsmasq.$app/ + +install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl +install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl +install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl # Copy init script -install -o root -g root -m 0755 ../conf/$service_name /usr/local/bin/ +ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name" # Copy openvpn scripts mkdir -pm 0755 /etc/openvpn/scripts mkdir -pm 0755 /etc/openvpn/scripts/route-up.d mkdir -pm 0755 /etc/openvpn/scripts/route-down.d -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-up.d/90-hotspot -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-down.d/90-hotspot - +ynh_add_config --template="../conf/openvpn_90-hotspot" --destination="/etc/openvpn/scripts/route-up.d/90-$service_name" +ynh_add_config --template="../conf/openvpn_90-hotspot" --destination="/etc/openvpn/scripts/route-down.d/90-$service_name" +chmod 0755 "/etc/openvpn/scripts/route-up.d/90-${service_name}" +chmod 0755 "/etc/openvpn/scripts/route-down.d/90-${service_name}" #================================================= # CONFIGURE HOSTAPD #================================================= ynh_script_progression --message="Configuring hostapd..." -## hostapd -ynh_store_file_checksum --file="/etc/init.d/hostapd" - # Set default inits # The boot order of these services are important, so they are disabled by default # and the ynh-hotspot service handles them. @@ -196,14 +204,14 @@ ynh_script_progression --message="Configuring a systemd service..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name # Create custom systemd config for hostapd to handle multiple wifi devices -ynh_add_systemd_config --service="hostapd@.service" --template="../conf/systemd_hostapd.service" +ynh_add_systemd_config --service="hostapd@$app.service" --template="../conf/systemd_hostapd.service" #================================================= # INTEGRATE SERVICE IN YUNOHOST #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" #================================================= # START SYSTEMD SERVICE diff --git a/scripts/remove b/scripts/remove index 408f31a..d3acaa9 100644 --- a/scripts/remove +++ b/scripts/remove @@ -18,7 +18,6 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) -wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # STANDARD REMOVE @@ -27,12 +26,9 @@ wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # Remove the service from the list of services known by Yunohost (added from `yunohost service add`) -if yunohost service status $service_name >/dev/null 2>&1 -then - ynh_script_progression --message="Removing $app service" - yunohost service stop $service_name - yunohost service remove $service_name -fi +ynh_script_progression --message="Removing $app service" +yunohost service stop $service_name +yunohost service remove $service_name #================================================= # STOP AND REMOVE SERVICE @@ -41,6 +37,7 @@ ynh_script_progression --message="Stopping and removing the systemd service..." # Remove the dedicated systemd config ynh_remove_systemd_config --service=$service_name +ynh_remove_systemd_config --service="hostapd@$app" #================================================= # REMOVE DEPENDENCIES @@ -55,26 +52,26 @@ ynh_remove_app_dependencies #================================================= ynh_script_progression --message="Removing app main directory..." -ynh_secure_remove --file="/etc/openvpn/scripts/route-up.d/90-hotspot" -ynh_secure_remove --file="/etc/openvpn/scripts/route-down.d/90-hotspot" +ynh_secure_remove --file="/etc/openvpn/scripts/route-up.d/90-${service_name}" +ynh_secure_remove --file="/etc/openvpn/scripts/route-down.d/90-${service_name}" # Remove the app directory securely ynh_secure_remove --file="/usr/local/bin/$service_name" -for FILE in $(ls /tmp/.ynh-hotspot-* 2>/dev/null) +for FILE in $(ls /tmp/.${service_name}-* 2>/dev/null) do ynh_secure_remove --file="$FILE" done # Remove confs -ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" -ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" +ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" +ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv6.conf" -ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" -ynh_secure_remove --file="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" +ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" +ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv4.conf" -ynh_secure_remove --file="/etc/hostapd/hostapd.conf.tpl" -ynh_secure_remove --file="/etc/hostapd/hostapd-${wifi_device}.conf" +ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf.tpl" +ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf" #================================================= # CLOSE A PORT diff --git a/scripts/restore b/scripts/restore index e4d982b..0fc6027 100644 --- a/scripts/restore +++ b/scripts/restore @@ -29,7 +29,6 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) -wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # CHECK IF THE APP CAN BE RESTORED @@ -62,21 +61,19 @@ else pkg_dependencies="$pkg_dependencies $free_firmware_packages" fi -ynh_restore_file --origin_path="/etc/hostapd/hostapd.conf.tpl" -ynh_restore_file --origin_path="/etc/hostapd/hostapd-${wifi_device}.conf" +ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf.tpl" +ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory -ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl" -ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf" +ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" +ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory -ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl" -ynh_restore_file --origin_path="/etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf" +ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" +ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv4.conf" --not_mandatory ynh_restore_file --origin_path="/usr/local/bin/$service_name" -ynh_restore_file --origin_path="/etc/openvpn/scripts/route-up.d/90-hotspot" -ynh_restore_file --origin_path="/etc/openvpn/scripts/route-down.d/90-hotspot" - -ynh_restore_file --origin_path="/etc/init.d/hostapd" +ynh_restore_file --origin_path="/etc/openvpn/scripts/route-up.d/90-${service_name}" +ynh_restore_file --origin_path="/etc/openvpn/scripts/route-down.d/90-${service_name}" #================================================= # SPECIFIC RESTORATION @@ -94,14 +91,14 @@ ynh_install_app_dependencies $pkg_dependencies ynh_script_progression --message="Restoring the systemd configuration..." ynh_restore_file --origin_path="/etc/systemd/system/$service_name.service" -systemctl enable $service_name.service --quiet +ynh_restore_file --origin_path="/etc/systemd/system/hostapd@$app.service" #================================================= # INTEGRATE SERVICE IN YUNOHOST #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" #================================================= # START SYSTEMD SERVICE diff --git a/scripts/upgrade b/scripts/upgrade index 9ef90ed..6a9ce48 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -68,7 +68,7 @@ elif [ $firmware_nonfree = "no" ]; then fi if [ -z $service_name ]; then - service_name="ynh-hotspot" + service_name="ynh-$app" ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi @@ -124,8 +124,7 @@ ynh_secure_remove --file="/etc/hostapd/hostapd.conf" ynh_secure_remove --file="/etc/hostapd/hostapd.base.conf" ynh_secure_remove --file="/etc/hostapd/hostapd.accesspoint.conf" -for FILE in $(ls /etc/dnsmasq.dhcpd/dhcpdv{4,6}-ssid{0..3}.conf 2>/dev/null); do - ynh_secure_remove --file="$FILE" +for FILE in $(ls /etc/dnsmasq.dhcpd/*.conf 2>/dev/null); do ynh_secure_remove --file="$FILE" done @@ -188,22 +187,27 @@ ynh_install_app_dependencies $pkg_dependencies #================================================= ynh_script_progression --message="Copying configuration..." -mkdir -pm 0755 /etc/dnsmasq.dhcpd/ -chown root: /etc/dnsmasq.dhcpd/ +mkdir -pm 0755 /etc/hostapd/$app/ +chown root: /etc/hostapd/$app/ -install -b -o root -g root -m 0644 ../conf/hostapd.*.conf /etc/hostapd/ -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.dhcpd/dhcpdv6.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.dhcpd/dhcpdv4.conf.tpl +mkdir -pm 0755 /etc/dnsmasq.$app/ +chown root: /etc/dnsmasq.$app/ + +install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl +install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl +install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl # Copy init script -install -o root -g root -m 0755 ../conf/$service_name /usr/local/bin/ +ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name" # Copy openvpn scripts mkdir -pm 0755 /etc/openvpn/scripts mkdir -pm 0755 /etc/openvpn/scripts/route-up.d mkdir -pm 0755 /etc/openvpn/scripts/route-down.d -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-up.d/90-hotspot -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-down.d/90-hotspot +ynh_add_config --template="../conf/openvpn_90-hotspot" --destination="/etc/openvpn/scripts/route-up.d/90-$service_name" +ynh_add_config --template="../conf/openvpn_90-hotspot" --destination="/etc/openvpn/scripts/route-down.d/90-$service_name" +chmod 0755 "/etc/openvpn/scripts/route-up.d/90-${service_name}" +chmod 0755 "/etc/openvpn/scripts/route-down.d/90-${service_name}" #================================================= # SETUP SYSTEMD @@ -213,7 +217,7 @@ ynh_script_progression --message="Upgrading systemd configuration..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name # Create custom systemd config for hostapd to handle multiple wifi devices -ynh_add_systemd_config --service="hostapd@.service" --template="../conf/systemd_hostapd.service" +ynh_add_systemd_config --service="hostapd@$app.service" --template="../conf/systemd_hostapd.service" #================================================= # GENERIC FINALIZATION @@ -222,7 +226,7 @@ ynh_add_systemd_config --service="hostapd@.service" --template="../conf/systemd_ #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" #================================================= # START SYSTEMD SERVICE From 35eb80ef4ae55475ed5aeeb4c0a9da6053c30c64 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 19:09:11 +0200 Subject: [PATCH 024/111] remove .service prefix --- scripts/install | 2 +- scripts/upgrade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/install b/scripts/install index 725312f..8ad7e66 100644 --- a/scripts/install +++ b/scripts/install @@ -204,7 +204,7 @@ ynh_script_progression --message="Configuring a systemd service..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name # Create custom systemd config for hostapd to handle multiple wifi devices -ynh_add_systemd_config --service="hostapd@$app.service" --template="../conf/systemd_hostapd.service" +ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_hostapd.service" #================================================= # INTEGRATE SERVICE IN YUNOHOST diff --git a/scripts/upgrade b/scripts/upgrade index 6a9ce48..5989d23 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -217,7 +217,7 @@ ynh_script_progression --message="Upgrading systemd configuration..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name # Create custom systemd config for hostapd to handle multiple wifi devices -ynh_add_systemd_config --service="hostapd@$app.service" --template="../conf/systemd_hostapd.service" +ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_hostapd.service" #================================================= # GENERIC FINALIZATION From 8f6da7c103113b5c0395ed154a6316b70e4f1f5b Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 19:16:02 +0200 Subject: [PATCH 025/111] fix cleanup --- scripts/remove | 10 ++-------- scripts/upgrade | 5 +---- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/scripts/remove b/scripts/remove index d3acaa9..e7a0a0f 100644 --- a/scripts/remove +++ b/scripts/remove @@ -64,14 +64,8 @@ do done # Remove confs -ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" -ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv6.conf" - -ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" -ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv4.conf" - -ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf.tpl" -ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf" +ynh_secure_remove --file="/etc/dnsmasq.$app/" +ynh_secure_remove --file="/etc/hostapd/$app/" #================================================= # CLOSE A PORT diff --git a/scripts/upgrade b/scripts/upgrade index 5989d23..de98521 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -123,10 +123,7 @@ fi ynh_secure_remove --file="/etc/hostapd/hostapd.conf" ynh_secure_remove --file="/etc/hostapd/hostapd.base.conf" ynh_secure_remove --file="/etc/hostapd/hostapd.accesspoint.conf" - -for FILE in $(ls /etc/dnsmasq.dhcpd/*.conf 2>/dev/null); do - ynh_secure_remove --file="$FILE" -done +ynh_secure_remove --file="/etc/dnsmasq.dhcpd/" # Old stuff prior to 2.x From ee19798159db5a1e702ee8141e9c96ee0410642a Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 19:19:24 +0200 Subject: [PATCH 026/111] fix exec permissions --- scripts/install | 1 + scripts/upgrade | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/install b/scripts/install index 8ad7e66..88fe8de 100644 --- a/scripts/install +++ b/scripts/install @@ -174,6 +174,7 @@ install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq # Copy init script ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name" +chmod 0755 "/usr/local/bin/$service_name" # Copy openvpn scripts mkdir -pm 0755 /etc/openvpn/scripts diff --git a/scripts/upgrade b/scripts/upgrade index de98521..24e0880 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -196,6 +196,7 @@ install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq # Copy init script ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name" +chmod 0755 "/usr/local/bin/$service_name" # Copy openvpn scripts mkdir -pm 0755 /etc/openvpn/scripts From 4bbf247394b2d8e885c4ecc2a15d7154285608b4 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 19:27:40 +0200 Subject: [PATCH 027/111] fix service enabled --- conf/ynh-hotspot | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 5f3428f..ed49234 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -248,8 +248,8 @@ start) if is_running; then echo "Already started" exit 0 - elif [[ "${service_enabled}" != "enabled" ]]; then - echo "Not starting because hotspod service is disabled" + elif [[ "${service_enabled}" -eq 0 ]]; then + echo "Not starting because hotspot service is disabled" exit 1 fi @@ -327,7 +327,7 @@ restart) status) exitcode=0 - if [[ "${service_enabled}" != "enabled" ]]; then + if [[ "${service_enabled}" -eq 0 ]]; then echo "[FAIL] Hotspot Service disabled" exit 1 fi From 5d8ea57e2a90699ab17adf7517b16d27ba471b41 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 19:35:38 +0200 Subject: [PATCH 028/111] fix start dhcp --- conf/ynh-hotspot | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index ed49234..a98d557 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -55,11 +55,11 @@ is_forwarding_set() { } is_dhcpd6_running() { - [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid") > /dev/null + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv6-$app.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv6-$app.pid") > /dev/null } is_dhcpd4_running() { - [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid") > /dev/null + [[ -e "/run/dnsmasq/dnsmasq-dhcpdv4-$app.pid" ]] && ps -p $(cat "/run/dnsmasq/dnsmasq-dhcpdv4-$app.pid") > /dev/null } is_hostapd_running() { @@ -148,13 +148,13 @@ start_dhcpd() { # Run DHCPv4 server if ! is_dhcpd4_running; then echo "hotspot ${wifi_device}: Start the DHCPv4 server (dnsmasq)" - dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv4-ssid-${wifi_device}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv4-ssid-${wifi_device}.pid + dnsmasq -C /etc/dnsmasq.$app/dhcpdv4.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv4-$app.pid fi # Run DHCPv6 server if has_ip6delegatedprefix && ! is_dhcpd6_running; then echo "hotspot ${wifi_device}: Start the NDP and DHCPv6 server (dnsmasq)" - dnsmasq -C /etc/dnsmasq.dhcpd/dhcpdv6-ssid-${wifi_device}.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv6-ssid-${wifi_device}.pid + dnsmasq -C /etc/dnsmasq.$app/dhcpdv6.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv6-$app.pid fi } @@ -200,14 +200,14 @@ unset_forwarding() { stop_dhcpd() { if is_dhcpd6_running; then echo "hotspot ${wifi_device}: Stop the NDP and DHCPv6 server (dnsmasq)" - kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid) - rm -f /run/dnsmasq/dnsmasq-dhcpdv6-ssid-${wifi_device}.pid + kill $(cat /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid) + rm -f /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid fi if is_dhcpd4_running; then echo "hotspot ${wifi_device}: Stop the DHCPv4 server (dnsmasq)" - kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid) - rm -f /run/dnsmasq/dnsmasq-dhcpdv4-ssid-${wifi_device}.pid + kill $(cat /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid) + rm -f /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid fi } From eaf7b994b03d91655f0f91b3fdd4a35a44e216ca Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 21:59:27 +0200 Subject: [PATCH 029/111] fix unbound var --- scripts/config | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/config b/scripts/config index 4c7c40c..1c26524 100644 --- a/scripts/config +++ b/scripts/config @@ -93,8 +93,10 @@ EOF } get__dns() { - ip6_dns=$(ynh_app_setting_get $app ip6_dns | tr -d '[]') - ip4_dns=$(ynh_app_setting_get $app ip4_dns) + ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) + ip6_dns=$(ynh_app_setting_get --app=$app --key=ip6_dns | tr -d '[]') + ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) + ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) if [[ -n ${ip6_net} ]] && [[ -z ${ip6_dns} ]]; then ip6_dns="${ip6_net}1" From da0c4095a8a119a00e7262eaccc123738b229254 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 22:07:05 +0200 Subject: [PATCH 030/111] run config after install --- scripts/install | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/install b/scripts/install index 88fe8de..468f5dc 100644 --- a/scripts/install +++ b/scripts/install @@ -223,7 +223,8 @@ ynh_script_progression --message="Starting a systemd service..." if [[ $wifi_device == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else - ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" + ynh_app_config_run $1 + # ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" fi #================================================= From 9b6ba3ec5a195cfa938dd1a954ea10cf30ed6329 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 22:16:26 +0200 Subject: [PATCH 031/111] fix config run --- scripts/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install b/scripts/install index 468f5dc..f39d45b 100644 --- a/scripts/install +++ b/scripts/install @@ -223,7 +223,7 @@ ynh_script_progression --message="Starting a systemd service..." if [[ $wifi_device == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else - ynh_app_config_run $1 + ynh_app_config_run apply # ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" fi From baf3dac35f6286469032e0fc671a72c00672f250 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 22:40:58 +0200 Subject: [PATCH 032/111] apply config during install and upgrade --- scripts/install | 24 ++++++++++++++++++++---- scripts/upgrade | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/scripts/install b/scripts/install index f39d45b..6be322a 100644 --- a/scripts/install +++ b/scripts/install @@ -139,12 +139,14 @@ ip4_dns="${ip4_nat_prefix}.1" hot_reload_usb_wifi_cards wifi_device=$(iw_devices | awk -F\| '{ print $1 }') +wifi_secure=1 +wifi_channel=6 ynh_app_setting_set --app=$app --key=wifi_ssid --value="${wifi_ssid}" -ynh_app_setting_set --app=$app --key=wifi_secure --value=1 +ynh_app_setting_set --app=$app --key=wifi_secure --value="${wifi_secure}" ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase}" ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" -ynh_app_setting_set --app=$app --key=wifi_channel --value=6 +ynh_app_setting_set --app=$app --key=wifi_channel --value="${wifi_channel}" ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" @@ -197,6 +199,21 @@ systemctl disable hostapd --quiet 2>&1 systemctl stop hostapd 2>&1 systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd is masked after install ... +if [[ -n ${wifi_device} ]]; then + if [ "${wifi_secure}" -eq 1 ]; then + local sec_comment="" + else + local sec_comment="#" + fi + + ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" + + if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" + fi +fi + #================================================= # SETUP SYSTEMD #================================================= @@ -223,8 +240,7 @@ ynh_script_progression --message="Starting a systemd service..." if [[ $wifi_device == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else - ynh_app_config_run apply - # ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" + ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" fi #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 24e0880..464a152 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -72,15 +72,24 @@ if [ -z $service_name ]; then ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi +wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) +wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) +wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) +wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel) +ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) +ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) +ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall) +dns=$(ynh_app_setting_get --app=$app --key=dns) + multissid=$(ynh_app_setting_get --app=$app --key=multissid) if [[ -n ${multissid} ]] && [[ ${multissid} -gt 1 ]]; then - wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid | cut -d'|' -f 1) - wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure | cut -d'|' -f 1) - wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase | cut -d'|' -f 1) - ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix | cut -d'|' -f 1) - ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net | cut -d'|' -f 1) - ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall | cut -d'|' -f 1) - dns=$(ynh_app_setting_get --app=$app --key=dns | cut -d'|' -f 1) + wifi_ssid=$(cut -d'|' -f 1 <<< ${wifi_ssid}) + wifi_secure=$(cut -d'|' -f 1 <<< ${wifi_secure}) + wifi_passphrase=$(cut -d'|' -f 1 <<< ${wifi_passphrase}) + ip4_nat_prefix=$(cut -d'|' -f 1 <<< ${ip4_nat_prefix}) + ip6_net=$(cut -d'|' -f 1 <<< ${ip6_net}) + ip6_firewall=$(cut -d'|' -f 1 <<< ${ip6_firewall}) + dns=$(cut -d'|' -f 1 <<< ${dns}) ynh_app_setting_set --app=$app --key=wifi_ssid --value="${wifi_ssid}" ynh_app_setting_set --app=$app --key=wifi_secure --value="${wifi_secure}" @@ -88,8 +97,6 @@ if [[ -n ${multissid} ]] && [[ ${multissid} -gt 1 ]]; then ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}" ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" ynh_app_setting_set --app=$app --key=ip6_firewall --value="${ip6_firewall}" -else - dns=$(ynh_app_setting_get --app=$app --key=dns) fi if [[ -n "${dns}" ]]; then @@ -114,6 +121,9 @@ if [[ -n "${dns}" ]]; then fi ynh_app_setting_delete $app dns +else + ip6_dns=$(ynh_app_setting_get --app=$app --key=ip6_dns) + ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) fi if [[ -n ${multissid} ]]; then @@ -194,6 +204,21 @@ install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/ho install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl +if [[ -n ${wifi_device} ]]; then + if [ "${wifi_secure}" -eq 1 ]; then + local sec_comment="" + else + local sec_comment="#" + fi + + ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" + + if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" + fi +fi + # Copy init script ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name" chmod 0755 "/usr/local/bin/$service_name" From 5b2143d814d41ee111a18bdd8570ac3be84e6526 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 22:46:38 +0200 Subject: [PATCH 033/111] stupid bash syntax --- scripts/install | 4 ++-- scripts/upgrade | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/install b/scripts/install index 6be322a..0eea9a7 100644 --- a/scripts/install +++ b/scripts/install @@ -201,9 +201,9 @@ systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd if [[ -n ${wifi_device} ]]; then if [ "${wifi_secure}" -eq 1 ]; then - local sec_comment="" + sec_comment="" else - local sec_comment="#" + sec_comment="#" fi ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" diff --git a/scripts/upgrade b/scripts/upgrade index 464a152..e656456 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -206,9 +206,9 @@ install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq if [[ -n ${wifi_device} ]]; then if [ "${wifi_secure}" -eq 1 ]; then - local sec_comment="" + sec_comment="" else - local sec_comment="#" + sec_comment="#" fi ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" From 3e0b11f919777965b6e2a56f2fce58d863c443bc Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 22:55:37 +0200 Subject: [PATCH 034/111] fix backup config --- scripts/backup | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/backup b/scripts/backup index cbf88e1..1362ed7 100644 --- a/scripts/backup +++ b/scripts/backup @@ -41,13 +41,13 @@ ynh_print_info --message="Declaring files to be backed up..." #================================================= ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf.tpl" -ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf" +ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" -ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf" +ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" -ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf" +ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf" --not_mandatory ynh_backup --src_path="/usr/local/bin/$service_name" From b840bac031bd2e60140753d59bd69486748c9fb0 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 23:12:27 +0200 Subject: [PATCH 035/111] delete legacy files only when multissid detected --- scripts/upgrade | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/upgrade b/scripts/upgrade index e656456..c8b1a63 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -128,12 +128,12 @@ fi if [[ -n ${multissid} ]]; then ynh_app_setting_delete --app=$app --key=multissid -fi -ynh_secure_remove --file="/etc/hostapd/hostapd.conf" -ynh_secure_remove --file="/etc/hostapd/hostapd.base.conf" -ynh_secure_remove --file="/etc/hostapd/hostapd.accesspoint.conf" -ynh_secure_remove --file="/etc/dnsmasq.dhcpd/" + ynh_secure_remove --file="/etc/hostapd/hostapd.conf" + ynh_secure_remove --file="/etc/hostapd/hostapd.base.conf" + ynh_secure_remove --file="/etc/hostapd/hostapd.accesspoint.conf" + ynh_secure_remove --file="/etc/dnsmasq.dhcpd/" +fi # Old stuff prior to 2.x From 5e89bf7d917b24bc25062cea4299d996489e7fe5 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 23:14:42 +0200 Subject: [PATCH 036/111] scan wifi devices earlier --- scripts/upgrade | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/upgrade b/scripts/upgrade index c8b1a63..670e77c 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -194,6 +194,9 @@ ynh_install_app_dependencies $pkg_dependencies #================================================= ynh_script_progression --message="Copying configuration..." +hot_reload_usb_wifi_cards +wifi_device=$(iw_devices | awk -F\| '{ print $1 }') + mkdir -pm 0755 /etc/hostapd/$app/ chown root: /etc/hostapd/$app/ @@ -256,9 +259,6 @@ yunohost service add $service_name --description "Creates a Wi-Fi access point" #================================================= ynh_script_progression --message="Starting the hotspot service..." -hot_reload_usb_wifi_cards -wifi_device=$(iw_devices | awk -F\| '{ print $1 }') - if [[ -z $wifi_device ]]; then ynh_app_setting_set --app=$app --key=service_enabled --value=0 wifi_device="" From 60e2cdd1d76cec00b31484af6f046f418512d515 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 21 Aug 2023 23:24:01 +0200 Subject: [PATCH 037/111] fix check hostapd status --- scripts/config | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/config b/scripts/config index 1c26524..7cdcbe5 100644 --- a/scripts/config +++ b/scripts/config @@ -37,9 +37,9 @@ get__no_antenna() { get__status() { local service_enabled=$(ynh_app_setting_get $app service_enabled) - if systemctl is-active hostapd -q + if systemctl is-active hostapd@$app -q then - if [ $service_enabled -eq 1 ] + if [[ $service_enabled -eq 1 ]] then cat << EOF style: success @@ -54,7 +54,7 @@ ask: en: Your Hotspot is running, but it shouldn't ! EOF fi - elif [ $service_enabled -eq 1 ] + elif [[ $service_enabled -eq 1 ]] then cat << EOF style: danger @@ -62,7 +62,7 @@ ask: en: |- Your Hotspot is down ! Here are errors logged in the last 5 minutes \`\`\` -$(journalctl -u hostapd -n10 -o cat | sed 's/^/ /g') +$(journalctl -u hostapd@$app -n10 -o cat | sed 's/^/ /g') \`\`\` EOF else From a4a99e70f689360a2ad778dfecf127172c00827d Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 22 Aug 2023 09:28:36 +0200 Subject: [PATCH 038/111] fix config panel --- scripts/config | 2 ++ scripts/install | 1 + 2 files changed, 3 insertions(+) diff --git a/scripts/config b/scripts/config index 7cdcbe5..6ce9c2f 100644 --- a/scripts/config +++ b/scripts/config @@ -183,6 +183,8 @@ set__dns() { # OVERWRITING APPLY STEP #================================================= ynh_app_config_apply() { + service_name=$(ynh_app_setting_get --app=$app --key=service_name) + # Stop vpn client ynh_print_info --message="Stopping hotspot in order to edit files" /usr/local/bin/${service_name} stop diff --git a/scripts/install b/scripts/install index 0eea9a7..276885f 100644 --- a/scripts/install +++ b/scripts/install @@ -147,6 +147,7 @@ ynh_app_setting_set --app=$app --key=wifi_secure --value="${wifi_secure}" ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase}" ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" ynh_app_setting_set --app=$app --key=wifi_channel --value="${wifi_channel}" +ynh_app_setting_set --app=$app --key=advanced --value=0 ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" From 69478cd6e261ea6c9f864e0eff4c7d24a0e1062a Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 22 Aug 2023 14:51:13 +0200 Subject: [PATCH 039/111] create functions for configuring hostapd and dhcp --- scripts/_common.sh | 20 ++++++++++++++++++++ scripts/config | 14 ++------------ scripts/install | 14 ++------------ scripts/upgrade | 14 ++------------ 4 files changed, 26 insertions(+), 36 deletions(-) diff --git a/scripts/_common.sh b/scripts/_common.sh index 0687a14..a39ce75 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -48,6 +48,26 @@ function hot_reload_usb_wifi_cards() done } +function configure_hostapd() +{ + if [[ "${wifi_secure}" -eq 1 ]]; then + sec_comment="" + else + sec_comment="#" + fi + + ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" +} + +function configure_dhcp() +{ + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" + + if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" + fi +} + #================================================= # EXPERIMENTAL HELPERS #================================================= diff --git a/scripts/config b/scripts/config index 6ce9c2f..b0013ee 100644 --- a/scripts/config +++ b/scripts/config @@ -191,18 +191,8 @@ ynh_app_config_apply() { _ynh_app_config_apply - if [ "${wifi_secure}" -eq 1 ]; then - local sec_comment="" - else - local sec_comment="#" - fi - - ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" - - if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" - fi + configure_hostapd + configure_dhcp # Start hotspot ynh_print_info --message="Starting hotspot service if needed" diff --git a/scripts/install b/scripts/install index 276885f..eec1e7a 100644 --- a/scripts/install +++ b/scripts/install @@ -201,18 +201,8 @@ systemctl stop hostapd 2>&1 systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd is masked after install ... if [[ -n ${wifi_device} ]]; then - if [ "${wifi_secure}" -eq 1 ]; then - sec_comment="" - else - sec_comment="#" - fi - - ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" - - if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" - fi + configure_hostapd + configure_dhcp fi #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 670e77c..6ae4e2a 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -208,18 +208,8 @@ install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl if [[ -n ${wifi_device} ]]; then - if [ "${wifi_secure}" -eq 1 ]; then - sec_comment="" - else - sec_comment="#" - fi - - ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" - - if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" - fi + configure_hostapd + configure_dhcp fi # Copy init script From 2a1aa439544fedc38394afc1eb9f078b1b71691f Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 22 Aug 2023 15:09:30 +0200 Subject: [PATCH 040/111] setup advanced setting --- scripts/upgrade | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/scripts/upgrade b/scripts/upgrade index 6ae4e2a..78fc062 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -19,6 +19,17 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) +wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) +wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) +wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) +wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel) +advanced=$(ynh_app_setting_get --app=$app --key=advanced) +ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) +ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) +ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall) +dns=$(ynh_app_setting_get --app=$app --key=dns) +multissid=$(ynh_app_setting_get --app=$app --key=multissid) + #================================================= # CHECK VERSION #================================================= @@ -72,20 +83,11 @@ if [ -z $service_name ]; then ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi -wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) -wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) -wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) -wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel) -ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) -ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) -ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall) -dns=$(ynh_app_setting_get --app=$app --key=dns) - -multissid=$(ynh_app_setting_get --app=$app --key=multissid) if [[ -n ${multissid} ]] && [[ ${multissid} -gt 1 ]]; then wifi_ssid=$(cut -d'|' -f 1 <<< ${wifi_ssid}) wifi_secure=$(cut -d'|' -f 1 <<< ${wifi_secure}) wifi_passphrase=$(cut -d'|' -f 1 <<< ${wifi_passphrase}) + advanced=$(cut -d'|' -f 1 <<< ${advanced}) ip4_nat_prefix=$(cut -d'|' -f 1 <<< ${ip4_nat_prefix}) ip6_net=$(cut -d'|' -f 1 <<< ${ip6_net}) ip6_firewall=$(cut -d'|' -f 1 <<< ${ip6_firewall}) @@ -135,6 +137,10 @@ if [[ -n ${multissid} ]]; then ynh_secure_remove --file="/etc/dnsmasq.dhcpd/" fi +if [[ -z ${advanced} ]]; then + ynh_app_setting_set --app=$app --key=adanced --value=0 +fi + # Old stuff prior to 2.x ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) From ee04b0449bc7700eaa99dfcd8229f82dd0cc7f4d Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 22 Aug 2023 15:26:24 +0200 Subject: [PATCH 041/111] fix firewall port warning --- scripts/install | 2 +- scripts/restore | 2 +- scripts/upgrade | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/install b/scripts/install index eec1e7a..d89e519 100644 --- a/scripts/install +++ b/scripts/install @@ -220,7 +220,7 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --needs_exposed_ports 547 67 #================================================= # START SYSTEMD SERVICE diff --git a/scripts/restore b/scripts/restore index 0fc6027..a892475 100644 --- a/scripts/restore +++ b/scripts/restore @@ -98,7 +98,7 @@ ynh_restore_file --origin_path="/etc/systemd/system/hostapd@$app.service" #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --needs_exposed_ports 547 67 #================================================= # START SYSTEMD SERVICE diff --git a/scripts/upgrade b/scripts/upgrade index 78fc062..47c6ebe 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -248,7 +248,7 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --needs_exposed_ports 547 67 #================================================= # START SYSTEMD SERVICE From 7803699e667d654bc9979b8f83a59fcdc9a2eee0 Mon Sep 17 00:00:00 2001 From: HgO Date: Wed, 23 Aug 2023 10:24:43 +0200 Subject: [PATCH 042/111] display unused wifi devices --- scripts/_common.sh | 22 +++++++++++++++++++++- scripts/config | 7 ++++--- scripts/install | 2 +- scripts/restore | 6 +++++- scripts/upgrade | 6 +++++- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/scripts/_common.sh b/scripts/_common.sh index a39ce75..b6e0acb 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -14,7 +14,27 @@ free_firmware_packages="firmware-ath9k-htc" function iw_devices() { - echo -n $(/sbin/iw dev | grep Interface | grep -v 'mon\.' | grep -v hotspot | awk '{ print $NF }') | tr ' ' '|' + /sbin/iw dev | grep Interface | grep -v 'mon\.' | grep -v hotspot | awk '{ print $NF }' +} + +function used_iw_devices() +{ + local app_shortname="${app%%__*}" + local hotspot_apps=$(yunohost app list --output-as json | jq -r .apps[].id | grep -F $app_shortname) + # Remove this app from hotspot apps list + local other_hotspot_apps=$(grep -F -x -v $app <<< ${hotspot_apps}) + for hotspot_app in ${other_hotspot_apps}; do + hotspot_wifi_device=$(ynh_app_setting_get --app=$hotspot_app --key=wifi_device) + if [[ -n ${hotspot_wifi_device} ]]; then + echo "${hotspot_wifi_device}" + fi + done +} + +function unused_iw_devices() +{ + # Only prints devices that are not in the list of used devices + iw_devices | grep -F -v -f <(used_iw_devices) } function check_armbian_nonfree_conflict() diff --git a/scripts/config b/scripts/config index b0013ee..7ad0dc5 100644 --- a/scripts/config +++ b/scripts/config @@ -27,7 +27,7 @@ final_path=$(ynh_app_setting_get $app final_path) #================================================= get__no_antenna() { - if [[ $(iw_devices) == "" ]] + if [[ $(unused_iw_devices) == "" ]] then echo "value: true" else @@ -76,14 +76,15 @@ EOF } get__wifi_device() { - if [[ $(iw_devices) == "" ]] + local unused_wifi_devices=$(unused_iw_devices) + if [[ -z ${unused_wifi_devices} ]] then echo "choices: []" else cat << EOF choices: EOF - for device in $(iw_devices | sed "s/|/ /g") + for device in $unused_wifi_devices do echo " $device: $device" done diff --git a/scripts/install b/scripts/install index d89e519..8643f91 100644 --- a/scripts/install +++ b/scripts/install @@ -138,7 +138,7 @@ ip4_nat_prefix="10.${ip4_nat_prefix_index}.242" ip4_dns="${ip4_nat_prefix}.1" hot_reload_usb_wifi_cards -wifi_device=$(iw_devices | awk -F\| '{ print $1 }') +wifi_device=$(unused_iw_devices | head -n 1) wifi_secure=1 wifi_channel=6 diff --git a/scripts/restore b/scripts/restore index a892475..0f9f615 100644 --- a/scripts/restore +++ b/scripts/restore @@ -29,6 +29,7 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) +wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= # CHECK IF THE APP CAN BE RESTORED @@ -106,7 +107,10 @@ yunohost service add $service_name --description "Creates a Wi-Fi access point" ynh_script_progression --message="Starting a systemd service..." hot_reload_usb_wifi_cards -wifi_device=$(iw_devices | awk -F\| '{ print $1 }') +if [[ -z $wifi_device ]] || ! grep -q -F $wifi_device <(unused_iw_devices); then + wifi_device=$(unused_iw_devices | head -n 1) + ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" +fi if [[ -z $wifi_device ]]; then ynh_app_setting_set --app=$app --key=service_enabled --value=0 diff --git a/scripts/upgrade b/scripts/upgrade index 47c6ebe..bfad83b 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -19,6 +19,7 @@ app=$YNH_APP_INSTANCE_NAME firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) service_name=$(ynh_app_setting_get --app=$app --key=service_name) +wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) @@ -201,7 +202,10 @@ ynh_install_app_dependencies $pkg_dependencies ynh_script_progression --message="Copying configuration..." hot_reload_usb_wifi_cards -wifi_device=$(iw_devices | awk -F\| '{ print $1 }') +if [[ -z $wifi_device ]] || ! grep -q -F $wifi_device <(unused_iw_devices); then + wifi_device=$(unused_iw_devices | head -n 1) + ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" +fi mkdir -pm 0755 /etc/hostapd/$app/ chown root: /etc/hostapd/$app/ From 20cd43340cd438a3f334bab861b371beca8ad589 Mon Sep 17 00:00:00 2001 From: HgO Date: Wed, 23 Aug 2023 15:46:15 +0200 Subject: [PATCH 043/111] Revert "fix firewall port warning" This reverts commit ee04b0449bc7700eaa99dfcd8229f82dd0cc7f4d. --- scripts/install | 2 +- scripts/restore | 2 +- scripts/upgrade | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/install b/scripts/install index 8643f91..fb494bd 100644 --- a/scripts/install +++ b/scripts/install @@ -220,7 +220,7 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --needs_exposed_ports 547 67 +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" #================================================= # START SYSTEMD SERVICE diff --git a/scripts/restore b/scripts/restore index 0f9f615..6bce614 100644 --- a/scripts/restore +++ b/scripts/restore @@ -99,7 +99,7 @@ ynh_restore_file --origin_path="/etc/systemd/system/hostapd@$app.service" #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --needs_exposed_ports 547 67 +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" #================================================= # START SYSTEMD SERVICE diff --git a/scripts/upgrade b/scripts/upgrade index bfad83b..cbd43ad 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -252,7 +252,7 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --needs_exposed_ports 547 67 +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" #================================================= # START SYSTEMD SERVICE From 1d5d14162c680a0979169c2f6fb9a89eec9b927e Mon Sep 17 00:00:00 2001 From: HgO Date: Thu, 24 Aug 2023 08:45:34 +0200 Subject: [PATCH 044/111] typo --- scripts/upgrade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/upgrade b/scripts/upgrade index cbd43ad..076da9d 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -139,7 +139,7 @@ if [[ -n ${multissid} ]]; then fi if [[ -z ${advanced} ]]; then - ynh_app_setting_set --app=$app --key=adanced --value=0 + ynh_app_setting_set --app=$app --key=advanced --value=0 fi # Old stuff prior to 2.x From fbf6e7acc12b6a6068000e9ba67d33f6f65e17b7 Mon Sep 17 00:00:00 2001 From: HgO Date: Thu, 24 Aug 2023 09:32:03 +0200 Subject: [PATCH 045/111] enable multi instance check --- check_process | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/check_process b/check_process index aafd852..950622f 100644 --- a/check_process +++ b/check_process @@ -13,7 +13,7 @@ upgrade=1 upgrade=1 from_commit=539a1f26c30ba850455c63746d50ce3d8f33b119 backup_restore=1 - multi_instance=0 + multi_instance=1 change_url=0 ;;; Upgrade options ; commit=539a1f26c30ba850455c63746d50ce3d8f33b119 From 2f0dfecca5e918597a7ab5326f1f439366dbe30b Mon Sep 17 00:00:00 2001 From: HgO Date: Thu, 24 Aug 2023 09:56:08 +0200 Subject: [PATCH 046/111] don't guess ipv6 prefix from vpnclient app --- scripts/install | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/scripts/install b/scripts/install index e82036b..a98772b 100644 --- a/scripts/install +++ b/scripts/install @@ -117,18 +117,6 @@ ynh_system_user_create --username=$app #================================================= ynh_script_progression --message="Configuring hotspot..." -if [[ ! -v ip6_net ]]; then # if ip6_net not set - ip6_net="" - - if [[ -e /tmp/.ynh-vpnclient-started ]]; then - vpnclient_ip6_net=$(ynh_app_setting_get vpnclient ip6_net 2>&1) - - if [[ $vpnclient_ip6_net =~ :: ]]; then - ip6_net=${vpnclient_ip6_net} - fi - fi -fi - hot_reload_usb_wifi_cards wifi_device=$(iw_devices | awk -F\| '{ print $1 }') From c39e31a92cc7f962906236b50f2fb85401c338af Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 17 Sep 2023 21:57:39 +0200 Subject: [PATCH 047/111] Code review / add quotes around suspicious vars that may be empty --- scripts/_common.sh | 2 +- scripts/config | 16 ++++++++-------- scripts/install | 2 +- scripts/restore | 2 +- scripts/upgrade | 12 ++++++------ 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/scripts/_common.sh b/scripts/_common.sh index b6e0acb..94a730a 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -25,7 +25,7 @@ function used_iw_devices() local other_hotspot_apps=$(grep -F -x -v $app <<< ${hotspot_apps}) for hotspot_app in ${other_hotspot_apps}; do hotspot_wifi_device=$(ynh_app_setting_get --app=$hotspot_app --key=wifi_device) - if [[ -n ${hotspot_wifi_device} ]]; then + if [[ -n "${hotspot_wifi_device}" ]]; then echo "${hotspot_wifi_device}" fi done diff --git a/scripts/config b/scripts/config index 7ad0dc5..b7c9748 100644 --- a/scripts/config +++ b/scripts/config @@ -27,7 +27,7 @@ final_path=$(ynh_app_setting_get $app final_path) #================================================= get__no_antenna() { - if [[ $(unused_iw_devices) == "" ]] + if [[ "$(unused_iw_devices)" == "" ]] then echo "value: true" else @@ -39,7 +39,7 @@ get__status() { local service_enabled=$(ynh_app_setting_get $app service_enabled) if systemctl is-active hostapd@$app -q then - if [[ $service_enabled -eq 1 ]] + if [[ "$service_enabled" -eq 1 ]] then cat << EOF style: success @@ -54,7 +54,7 @@ ask: en: Your Hotspot is running, but it shouldn't ! EOF fi - elif [[ $service_enabled -eq 1 ]] + elif [[ "$service_enabled" -eq 1 ]] then cat << EOF style: danger @@ -77,7 +77,7 @@ EOF get__wifi_device() { local unused_wifi_devices=$(unused_iw_devices) - if [[ -z ${unused_wifi_devices} ]] + if [[ -z "${unused_wifi_devices}" ]] then echo "choices: []" else @@ -99,11 +99,11 @@ get__dns() { ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) - if [[ -n ${ip6_net} ]] && [[ -z ${ip6_dns} ]]; then + if [[ -n "${ip6_net}" ]] && [[ -z "${ip6_dns}" ]]; then ip6_dns="${ip6_net}1" fi - if [[ -n ${ip4_nat_prefix} ]] && [[ -z ${ip4_dns} ]]; then + if [[ -n "${ip4_nat_prefix}" ]] && [[ -z "${ip4_dns}" ]]; then ip4_dns="${ip4_nat_prefix}.1" fi @@ -164,11 +164,11 @@ set__dns() { ip6_dns="${ip6_dns%%,}" ip4_dns="${ip4_dns%%,}" - if [[ -n ${ip6_net} ]] && [[ -z ${ip6_dns} ]]; then + if [[ -n "${ip6_net}" ]] && [[ -z "${ip6_dns}" ]]; then ip6_dns="${ip6_net}1" fi - if [[ -n ${ip4_nat_prefix} ]] && [[ -z ${ip4_dns} ]]; then + if [[ -n "${ip4_nat_prefix}" ]] && [[ -z "${ip4_dns}" ]]; then ip4_dns="${ip4_nat_prefix}.1" fi diff --git a/scripts/install b/scripts/install index fb494bd..a8ebb60 100644 --- a/scripts/install +++ b/scripts/install @@ -200,7 +200,7 @@ systemctl disable hostapd --quiet 2>&1 systemctl stop hostapd 2>&1 systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd is masked after install ... -if [[ -n ${wifi_device} ]]; then +if [[ -n "${wifi_device}" ]]; then configure_hostapd configure_dhcp fi diff --git a/scripts/restore b/scripts/restore index 6bce614..43d912c 100644 --- a/scripts/restore +++ b/scripts/restore @@ -107,7 +107,7 @@ yunohost service add $service_name --description "Creates a Wi-Fi access point" ynh_script_progression --message="Starting a systemd service..." hot_reload_usb_wifi_cards -if [[ -z $wifi_device ]] || ! grep -q -F $wifi_device <(unused_iw_devices); then +if [[ -z "$wifi_device" ]] || ! grep -q -F "$wifi_device" <(unused_iw_devices); then wifi_device=$(unused_iw_devices | head -n 1) ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" fi diff --git a/scripts/upgrade b/scripts/upgrade index 076da9d..ce8da75 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -84,7 +84,7 @@ if [ -z $service_name ]; then ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi -if [[ -n ${multissid} ]] && [[ ${multissid} -gt 1 ]]; then +if [[ -n "${multissid}" ]] && [[ "${multissid}" -gt 1 ]]; then wifi_ssid=$(cut -d'|' -f 1 <<< ${wifi_ssid}) wifi_secure=$(cut -d'|' -f 1 <<< ${wifi_secure}) wifi_passphrase=$(cut -d'|' -f 1 <<< ${wifi_passphrase}) @@ -129,7 +129,7 @@ else ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) fi -if [[ -n ${multissid} ]]; then +if [[ -n "${multissid}" ]]; then ynh_app_setting_delete --app=$app --key=multissid ynh_secure_remove --file="/etc/hostapd/hostapd.conf" @@ -138,7 +138,7 @@ if [[ -n ${multissid} ]]; then ynh_secure_remove --file="/etc/dnsmasq.dhcpd/" fi -if [[ -z ${advanced} ]]; then +if [[ -z "${advanced}" ]]; then ynh_app_setting_set --app=$app --key=advanced --value=0 fi @@ -202,8 +202,8 @@ ynh_install_app_dependencies $pkg_dependencies ynh_script_progression --message="Copying configuration..." hot_reload_usb_wifi_cards -if [[ -z $wifi_device ]] || ! grep -q -F $wifi_device <(unused_iw_devices); then - wifi_device=$(unused_iw_devices | head -n 1) +if [[ -z "$wifi_device" ]] || ! grep -q -F "$wifi_device" <(unused_iw_devices); then + wifi_device="$(unused_iw_devices | head -n 1)" ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" fi @@ -217,7 +217,7 @@ install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/ho install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl -if [[ -n ${wifi_device} ]]; then +if [[ -n "${wifi_device}" ]]; then configure_hostapd configure_dhcp fi From 6a3cc410e94412c4f9da1302acfd989b6bbed96b Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 17 Sep 2023 22:07:49 +0200 Subject: [PATCH 048/111] bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index efcda4f..8dff521 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,7 @@ "en": "Create and manager wifi networks, share Internet access and use YunoHost apps accross wifi", "fr": "Créer et configurer des réseaux wifi, partager l'accès a Internet et utiliser les applications YunoHost via wifi" }, - "version": "2.0~ynh4", + "version": "2.1~ynh1", "url": "https://github.com/labriqueinternet/hotspot_ynh", "license": "AGPL-3.0", "maintainer": { From fffdf4c5b7b48575cc6613512cf90712ce7ab4ef Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sun, 17 Sep 2023 20:07:52 +0000 Subject: [PATCH 049/111] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f70b8eb..ee3b6d4 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.0~ynh4 +**Shipped version:** 2.1~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index 88c5f61..d19c85b 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.0~ynh4 +**Version incluse :** 2.1~ynh1 ## Captures d’écran From ac6cff8a93ff3d515181e7b3265e2fb47ef07679 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 18 Sep 2023 09:39:50 +0200 Subject: [PATCH 050/111] init ip6_net and ip6_dns vars --- scripts/install | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/install b/scripts/install index 924b7ee..a94c79a 100644 --- a/scripts/install +++ b/scripts/install @@ -136,6 +136,8 @@ ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" ynh_app_setting_set --app=$app --key=wifi_channel --value="${wifi_channel}" ynh_app_setting_set --app=$app --key=advanced --value=0 ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 +ynh_app_setting_set --app=$app --key=ip6_net --value="" +ynh_app_setting_set --app=$app --key=ip6_dns --value="" ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}" From 52057765510fa0b4e1cd98a3797a43d83191d780 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 18 Sep 2023 10:48:18 +0200 Subject: [PATCH 051/111] declare empty vars for ip6_net and ip6_dns --- scripts/install | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/install b/scripts/install index a94c79a..08e09ee 100644 --- a/scripts/install +++ b/scripts/install @@ -117,6 +117,9 @@ ynh_system_user_create --username=$app #================================================= ynh_script_progression --message="Configuring hotspot..." +ip6_net="" +ip6_dns="" + ip4_nat_prefix_index=${app##*__} if [[ "${ip4_nat_prefix_index}" == "${app}" ]]; then ip4_nat_prefix_index=0 @@ -136,8 +139,8 @@ ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" ynh_app_setting_set --app=$app --key=wifi_channel --value="${wifi_channel}" ynh_app_setting_set --app=$app --key=advanced --value=0 ynh_app_setting_set --app=$app --key=ip6_firewall --value=1 -ynh_app_setting_set --app=$app --key=ip6_net --value="" -ynh_app_setting_set --app=$app --key=ip6_dns --value="" +ynh_app_setting_set --app=$app --key=ip6_net --value="${ip6_net}" +ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}" From 2b60fee9f25d2b74ae72b0c24f3b3bd1be8627ab Mon Sep 17 00:00:00 2001 From: HgO Date: Fri, 22 Sep 2023 17:33:14 +0200 Subject: [PATCH 052/111] configure dnsmasq to enable dns resolver on wifi interface --- conf/dnsmasq.conf.tpl | 4 ++++ scripts/_common.sh | 5 +++++ scripts/backup | 3 +++ scripts/config | 1 + scripts/install | 2 ++ scripts/remove | 2 ++ scripts/restore | 3 +++ scripts/upgrade | 2 ++ 8 files changed, 22 insertions(+) create mode 100644 conf/dnsmasq.conf.tpl diff --git a/conf/dnsmasq.conf.tpl b/conf/dnsmasq.conf.tpl new file mode 100644 index 0000000..74b638a --- /dev/null +++ b/conf/dnsmasq.conf.tpl @@ -0,0 +1,4 @@ +# Wifi Hotspot app for YunoHost + +# Enable DNS +interface=__WIFI_DEVICE__ diff --git a/scripts/_common.sh b/scripts/_common.sh index 94a730a..485fed0 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -79,6 +79,11 @@ function configure_hostapd() ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" } +function configure_dnsmasq() +{ + ynh_add_config --template="/etc/dnsmasq.$app/dnsmasq.conf.tpl" --destination="/etc/dnsmasq.d/$app.conf" +} + function configure_dhcp() { ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" diff --git a/scripts/backup b/scripts/backup index 1362ed7..de71cba 100644 --- a/scripts/backup +++ b/scripts/backup @@ -43,6 +43,9 @@ ynh_print_info --message="Declaring files to be backed up..." ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf.tpl" ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory +ynh_backup --src_path="/etc/dnsmasq.$app/dnsmasq.conf.tpl" +ynh_backup --src_path="/etc/dnsmasq.d/$app.conf" --not_mandatory + ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory diff --git a/scripts/config b/scripts/config index b7c9748..b493dbf 100644 --- a/scripts/config +++ b/scripts/config @@ -193,6 +193,7 @@ ynh_app_config_apply() { _ynh_app_config_apply configure_hostapd + configure_dnsmasq configure_dhcp # Start hotspot diff --git a/scripts/install b/scripts/install index 08e09ee..5dd508d 100644 --- a/scripts/install +++ b/scripts/install @@ -162,6 +162,7 @@ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl +install -b -o root -g root -m 0644 ../conf/dnsmasq.conf.tpl /etc/dnsmasq.$app/dnsmasq.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl @@ -200,6 +201,7 @@ systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd if [[ -n "${wifi_device}" ]]; then configure_hostapd + configure_dnsmasq configure_dhcp fi diff --git a/scripts/remove b/scripts/remove index e7a0a0f..c59fe8e 100644 --- a/scripts/remove +++ b/scripts/remove @@ -64,6 +64,8 @@ do done # Remove confs +ynh_secure_remove --file="/etc/dnsmasq.d/$app.conf" + ynh_secure_remove --file="/etc/dnsmasq.$app/" ynh_secure_remove --file="/etc/hostapd/$app/" diff --git a/scripts/restore b/scripts/restore index 43d912c..afd0c60 100644 --- a/scripts/restore +++ b/scripts/restore @@ -65,6 +65,9 @@ fi ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf.tpl" ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory +ynh_restore_file --origin_path="/etc/dnsmasq.$app/dnsmasq.conf.tpl" +ynh_restore_file --origin_path="/etc/dnsmasq.d/$app.conf" --not_mandatory + ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory diff --git a/scripts/upgrade b/scripts/upgrade index 0810b57..dfa7c6d 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -214,11 +214,13 @@ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl +install -b -o root -g root -m 0644 ../conf/dnsmasq.conf.tpl /etc/dnsmasq.$app/dnsmasq.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl if [[ -n "${wifi_device}" ]]; then configure_hostapd + configure_dnsmasq configure_dhcp fi From fe15d2e5755b4da2a6d28e4ccd73ccf8a815ace7 Mon Sep 17 00:00:00 2001 From: HgO Date: Fri, 22 Sep 2023 17:41:44 +0200 Subject: [PATCH 053/111] restart dnsmasq to apply config change --- conf/dnsmasq.conf.tpl | 2 +- scripts/_common.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/dnsmasq.conf.tpl b/conf/dnsmasq.conf.tpl index 74b638a..7af2560 100644 --- a/conf/dnsmasq.conf.tpl +++ b/conf/dnsmasq.conf.tpl @@ -1,4 +1,4 @@ # Wifi Hotspot app for YunoHost -# Enable DNS +# Enable DNS resolution on wifi interface interface=__WIFI_DEVICE__ diff --git a/scripts/_common.sh b/scripts/_common.sh index 485fed0..a7f046f 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -82,6 +82,7 @@ function configure_hostapd() function configure_dnsmasq() { ynh_add_config --template="/etc/dnsmasq.$app/dnsmasq.conf.tpl" --destination="/etc/dnsmasq.d/$app.conf" + systemctl restart dnsmasq } function configure_dhcp() From 5d7a9ca16e48745239823f983fb4fda3b5d972f6 Mon Sep 17 00:00:00 2001 From: HgO Date: Fri, 22 Sep 2023 17:44:27 +0200 Subject: [PATCH 054/111] restart dnsmasq to apply config change --- scripts/remove | 1 + scripts/restore | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/remove b/scripts/remove index c59fe8e..e3f3ea5 100644 --- a/scripts/remove +++ b/scripts/remove @@ -65,6 +65,7 @@ done # Remove confs ynh_secure_remove --file="/etc/dnsmasq.d/$app.conf" +systemctl restart dnsmasq ynh_secure_remove --file="/etc/dnsmasq.$app/" ynh_secure_remove --file="/etc/hostapd/$app/" diff --git a/scripts/restore b/scripts/restore index afd0c60..af9c431 100644 --- a/scripts/restore +++ b/scripts/restore @@ -67,6 +67,7 @@ ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory ynh_restore_file --origin_path="/etc/dnsmasq.$app/dnsmasq.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.d/$app.conf" --not_mandatory +systemctl restart dnsmasq ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory From 92876e023e9366d3b2ded7d88dba92cb2fc77a10 Mon Sep 17 00:00:00 2001 From: HgO Date: Fri, 22 Sep 2023 18:12:20 +0200 Subject: [PATCH 055/111] add checks before closing dhcp ports --- scripts/_common.sh | 14 +++++++++----- scripts/remove | 22 ++++++++++------------ scripts/restore | 9 +++++++++ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/scripts/_common.sh b/scripts/_common.sh index a7f046f..13ceb53 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -12,6 +12,14 @@ free_firmware_packages="firmware-ath9k-htc" # PERSONAL HELPERS #================================================= +function other_hotspot_apps() +{ + local app_shortname="${app%%__*}" + local hotspot_apps=$(yunohost app list --output-as json | jq -r .apps[].id | grep -F $app_shortname) + # Remove this app from hotspot apps list + grep -F -x -v $app <<< ${hotspot_apps} +} + function iw_devices() { /sbin/iw dev | grep Interface | grep -v 'mon\.' | grep -v hotspot | awk '{ print $NF }' @@ -19,11 +27,7 @@ function iw_devices() function used_iw_devices() { - local app_shortname="${app%%__*}" - local hotspot_apps=$(yunohost app list --output-as json | jq -r .apps[].id | grep -F $app_shortname) - # Remove this app from hotspot apps list - local other_hotspot_apps=$(grep -F -x -v $app <<< ${hotspot_apps}) - for hotspot_app in ${other_hotspot_apps}; do + for hotspot_app in $(other_hotspot_apps); do hotspot_wifi_device=$(ynh_app_setting_get --app=$hotspot_app --key=wifi_device) if [[ -n "${hotspot_wifi_device}" ]]; then echo "${hotspot_wifi_device}" diff --git a/scripts/remove b/scripts/remove index e3f3ea5..7b3d158 100644 --- a/scripts/remove +++ b/scripts/remove @@ -58,8 +58,7 @@ ynh_secure_remove --file="/etc/openvpn/scripts/route-down.d/90-${service_name}" # Remove the app directory securely ynh_secure_remove --file="/usr/local/bin/$service_name" -for FILE in $(ls /tmp/.${service_name}-* 2>/dev/null) -do +for FILE in $(ls /tmp/.${service_name}-* 2>/dev/null); do ynh_secure_remove --file="$FILE" done @@ -74,17 +73,16 @@ ynh_secure_remove --file="/etc/hostapd/$app/" # CLOSE A PORT #================================================= -if yunohost firewall list | grep -q "\- 547$" -then - ynh_script_progression --message="Closing port 547" - ynh_exec_warn_less yunohost firewall disallow TCP 547 -fi +if [[ -z "$(other_hotspot_apps)" ]]; then + if yunohost firewall list | grep -q "\- 547$"; then + ynh_script_progression --message="Closing port 547" + ynh_exec_warn_less yunohost firewall disallow TCP 547 + fi - -if yunohost firewall list | grep -q "\- 67$" -then - ynh_script_progression --message="Closing port 67" - ynh_exec_warn_less yunohost firewall disallow TCP 67 + if yunohost firewall list | grep -q "\- 67$"; then + ynh_script_progression --message="Closing port 67" + ynh_exec_warn_less yunohost firewall disallow TCP 67 + fi fi #================================================= diff --git a/scripts/restore b/scripts/restore index af9c431..679bb30 100644 --- a/scripts/restore +++ b/scripts/restore @@ -36,6 +36,15 @@ wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) #================================================= ynh_script_progression --message="Validating restoration parameters..." +#================================================= +# FIND AND OPEN A PORT +#================================================= +ynh_script_progression --message="Configuring firewall..." + +# Update firewall for DHCP +ynh_exec_warn_less yunohost firewall allow --no-upnp --ipv6 UDP 547 +ynh_exec_warn_less yunohost firewall allow --no-upnp UDP 67 + # Meh idk where to put this ... On RPi, by default wlan is blocked if test -e /usr/sbin/rfkill && rfkill | grep wlan | grep -q -w 'blocked' then From e3a4f9e34ad88162b2e784e9949d95cd279a91b9 Mon Sep 17 00:00:00 2001 From: HgO Date: Fri, 22 Sep 2023 18:23:16 +0200 Subject: [PATCH 056/111] remove duplicate config --- scripts/install | 8 -------- scripts/upgrade | 7 ------- 2 files changed, 15 deletions(-) diff --git a/scripts/install b/scripts/install index 5dd508d..5f746d4 100644 --- a/scripts/install +++ b/scripts/install @@ -179,14 +179,6 @@ ynh_add_config --template="../conf/openvpn_90-hotspot" --destination="/etc/openv chmod 0755 "/etc/openvpn/scripts/route-up.d/90-${service_name}" chmod 0755 "/etc/openvpn/scripts/route-down.d/90-${service_name}" -# Copy openvpn scripts -mkdir -pm 0755 /etc/openvpn/scripts -mkdir -pm 0755 /etc/openvpn/scripts/route-up.d -mkdir -pm 0755 /etc/openvpn/scripts/route-down.d -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-up.d/90-hotspot -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-down.d/90-hotspot - - #================================================= # CONFIGURE HOSTAPD #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index dfa7c6d..1d56a39 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -237,13 +237,6 @@ ynh_add_config --template="../conf/openvpn_90-hotspot" --destination="/etc/openv chmod 0755 "/etc/openvpn/scripts/route-up.d/90-${service_name}" chmod 0755 "/etc/openvpn/scripts/route-down.d/90-${service_name}" -# Copy openvpn scripts -mkdir -pm 0755 /etc/openvpn/scripts -mkdir -pm 0755 /etc/openvpn/scripts/route-up.d -mkdir -pm 0755 /etc/openvpn/scripts/route-down.d -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-up.d/90-hotspot -install -o root -g root -m 0755 ../conf/openvpn_90-hotspot /etc/openvpn/scripts/route-down.d/90-hotspot - #================================================= # SETUP SYSTEMD #================================================= From 15ef276917a38bed5db6622569a62ea8b3f6394a Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 24 Sep 2023 11:34:54 +0200 Subject: [PATCH 057/111] fix unbound dns variables + check if hotspot is enabled before starting it + cleanup stuff when hotspot is disabled --- config_panel.toml | 1 - scripts/config | 46 +++++++++++++++++++++++++++++++++------------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/config_panel.toml b/config_panel.toml index ff286d0..71a9384 100644 --- a/config_panel.toml +++ b/config_panel.toml @@ -103,7 +103,6 @@ name = "Configuration" [main.hotspot.dns] ask = "DNS resolvers" type = "tags" - bind = "null" visible = "advanced" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" diff --git a/scripts/config b/scripts/config index b493dbf..bff0cab 100644 --- a/scripts/config +++ b/scripts/config @@ -136,21 +136,24 @@ validate__ip4_nat_prefix() { } validate__dns() { - if ! echo "${dns}" | grep -q "\." + if [[ -z "$ip4_dns" ]] then echo 'IPv4 DNS required' fi - if [[ -n "${ip6_net}" ]] && ! echo "${dns}" | grep -q ":" + if [[ -n "${ip6_net}" ]] && [[ -z "$ip6_dns" ]] then echo 'IPv6 DNS required' fi } -#================================================= -# SPECIFIC SETTERS FOR TOML SHORT KEYS -#================================================= +ynh_app_config_validate() { + if [[ "${advanced}" -eq 0 ]]; then + # When we aren't in advanced mode, these variables must be manually declared + dns="${old[dns]}" + ip6_net="${old[ip6_net]}" + ip4_nat_prefix="${old[ip4_nat_prefix]}" + fi -set__dns() { ip6_dns="" ip4_dns="" for ip in $(echo "${dns}" | tr ',' ' '); do @@ -171,7 +174,15 @@ set__dns() { if [[ -n "${ip4_nat_prefix}" ]] && [[ -z "${ip4_dns}" ]]; then ip4_dns="${ip4_nat_prefix}.1" fi - + + _ynh_app_config_validate +} + +#================================================= +# SPECIFIC SETTERS FOR TOML SHORT KEYS +#================================================= + +set__dns() { ynh_app_setting_set $app ip6_dns "${ip6_dns}" ynh_app_setting_set $app ip4_dns "${ip4_dns}" } @@ -192,14 +203,23 @@ ynh_app_config_apply() { _ynh_app_config_apply - configure_hostapd - configure_dnsmasq - configure_dhcp + if [[ "${service_enabled}" -eq 1 ]]; then + configure_hostapd + configure_dnsmasq + configure_dhcp - # Start hotspot - ynh_print_info --message="Starting hotspot service if needed" - /usr/local/bin/${service_name} start + # Start hotspot + ynh_print_info --message="Starting hotspot service if needed" + /usr/local/bin/${service_name} start + else + ynh_print_info --message="Cleanup hotspot config files" + ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf" + ynh_secure_remove --file="/etc/dnsmasq.d/$app.conf" + ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv4.conf" + ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv6.conf" + systemctl restart dnsmasq + fi } ynh_app_config_run $1 From d7bd4c15710cb7bc846ad170f304539d7a87d80b Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 24 Sep 2023 19:41:52 +0200 Subject: [PATCH 058/111] bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 8dff521..8fc2251 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,7 @@ "en": "Create and manager wifi networks, share Internet access and use YunoHost apps accross wifi", "fr": "Créer et configurer des réseaux wifi, partager l'accès a Internet et utiliser les applications YunoHost via wifi" }, - "version": "2.1~ynh1", + "version": "2.1.1~ynh1", "url": "https://github.com/labriqueinternet/hotspot_ynh", "license": "AGPL-3.0", "maintainer": { From 3366dbbbec59ee937b3e815e48fc676df07e9c8d Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sun, 24 Sep 2023 17:42:00 +0000 Subject: [PATCH 059/111] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee3b6d4..7cd872e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.1~ynh1 +**Shipped version:** 2.1.1~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index d19c85b..8706e5d 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.1~ynh1 +**Version incluse :** 2.1.1~ynh1 ## Captures d’écran From 0dcddf281c1a933f9356554916098aead3dae4c3 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 24 Sep 2023 20:13:54 +0200 Subject: [PATCH 060/111] fix typo in dnsmasq pid name --- conf/ynh-hotspot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index a98d557..0133dee 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -148,13 +148,13 @@ start_dhcpd() { # Run DHCPv4 server if ! is_dhcpd4_running; then echo "hotspot ${wifi_device}: Start the DHCPv4 server (dnsmasq)" - dnsmasq -C /etc/dnsmasq.$app/dhcpdv4.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv4-$app.pid + dnsmasq -C /etc/dnsmasq.$app/dhcpdv4.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpdv4-$app.pid fi # Run DHCPv6 server if has_ip6delegatedprefix && ! is_dhcpd6_running; then echo "hotspot ${wifi_device}: Start the NDP and DHCPv6 server (dnsmasq)" - dnsmasq -C /etc/dnsmasq.$app/dhcpdv6.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpv6-$app.pid + dnsmasq -C /etc/dnsmasq.$app/dhcpdv6.conf -p0 -x /run/dnsmasq/dnsmasq-dhcpdv6-$app.pid fi } From ddc23c17b55d8bb7961e54548b569803cd05d199 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 24 Sep 2023 20:14:16 +0200 Subject: [PATCH 061/111] stop / start hotspot through systemd --- scripts/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/config b/scripts/config index bff0cab..ff02211 100644 --- a/scripts/config +++ b/scripts/config @@ -199,7 +199,7 @@ ynh_app_config_apply() { # Stop vpn client ynh_print_info --message="Stopping hotspot in order to edit files" - /usr/local/bin/${service_name} stop + systemctl stop ${service_name} _ynh_app_config_apply @@ -210,7 +210,7 @@ ynh_app_config_apply() { # Start hotspot ynh_print_info --message="Starting hotspot service if needed" - /usr/local/bin/${service_name} start + systemctl start ${service_name} else ynh_print_info --message="Cleanup hotspot config files" ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf" From 7e404e0365687019c7bb032fafe2528ca43c9992 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Thu, 28 Sep 2023 18:00:31 +0000 Subject: [PATCH 062/111] Auto-update README --- README.md | 1 - README_fr.md | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index 7cd872e..7ce4f72 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources * Official app website: -* YunoHost documentation for this app: * Report a bug: ## Developer info diff --git a/README_fr.md b/README_fr.md index 8706e5d..d60ae7e 100644 --- a/README_fr.md +++ b/README_fr.md @@ -29,7 +29,6 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources * Site officiel de l’app : -* Documentation YunoHost pour cette app : * Signaler un bug : ## Informations pour les développeurs From f55f11762529a81093853ad46d98bc3e4f88bef8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 18:26:41 +0200 Subject: [PATCH 063/111] Fix indent in _common.sh --- scripts/_common.sh | 90 +++++++++++++++++++++------------------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/scripts/_common.sh b/scripts/_common.sh index 13ceb53..6ca2179 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -14,31 +14,31 @@ free_firmware_packages="firmware-ath9k-htc" function other_hotspot_apps() { - local app_shortname="${app%%__*}" - local hotspot_apps=$(yunohost app list --output-as json | jq -r .apps[].id | grep -F $app_shortname) - # Remove this app from hotspot apps list - grep -F -x -v $app <<< ${hotspot_apps} + local app_shortname="${app%%__*}" + local hotspot_apps=$(yunohost app list --output-as json | jq -r .apps[].id | grep -F $app_shortname) + # Remove this app from hotspot apps list + grep -F -x -v $app <<< ${hotspot_apps} } function iw_devices() { - /sbin/iw dev | grep Interface | grep -v 'mon\.' | grep -v hotspot | awk '{ print $NF }' + /sbin/iw dev | grep Interface | grep -v 'mon\.' | grep -v hotspot | awk '{ print $NF }' } function used_iw_devices() { - for hotspot_app in $(other_hotspot_apps); do - hotspot_wifi_device=$(ynh_app_setting_get --app=$hotspot_app --key=wifi_device) - if [[ -n "${hotspot_wifi_device}" ]]; then - echo "${hotspot_wifi_device}" - fi - done + for hotspot_app in $(other_hotspot_apps); do + hotspot_wifi_device=$(ynh_app_setting_get --app=$hotspot_app --key=wifi_device) + if [[ -n "${hotspot_wifi_device}" ]]; then + echo "${hotspot_wifi_device}" + fi + done } function unused_iw_devices() { - # Only prints devices that are not in the list of used devices - iw_devices | grep -F -v -f <(used_iw_devices) + # Only prints devices that are not in the list of used devices + iw_devices | grep -F -v -f <(used_iw_devices) } function check_armbian_nonfree_conflict() @@ -47,10 +47,10 @@ function check_armbian_nonfree_conflict() # If we're on armbian, force $firmware_nonfree # because armbian-firmware conflicts with firmware-misc-nonfree package if dpkg --list | grep -q armbian-firmware; then - echo "You are running Armbian and firmware-misc-nonfree are known to conflict with armbian-firwmare. " >&2 - echo "The package firmware-misc-nonfree is a dependency of firmware-ralink, so firmware-ralink will NOT be installed" >&2 - echo "You can manually install firmware-ralink with 'apt -o Dpkg::Options::=\"--force-overwrite\" firmware-ralink'" >&2 - nonfree_firmware_packages=$(echo $nonfree_firmware_packages | sed 's/ firmware-ralink//') + echo "You are running Armbian and firmware-misc-nonfree are known to conflict with armbian-firwmare. " >&2 + echo "The package firmware-misc-nonfree is a dependency of firmware-ralink, so firmware-ralink will NOT be installed" >&2 + echo "You can manually install firmware-ralink with 'apt -o Dpkg::Options::=\"--force-overwrite\" firmware-ralink'" >&2 + nonfree_firmware_packages=$(echo $nonfree_firmware_packages | sed 's/ firmware-ralink//') fi } @@ -58,50 +58,42 @@ function check_armbian_nonfree_conflict() function hot_reload_usb_wifi_cards() { - modulesList="acx-mac80211 ar5523 ar9170usb at76c50x-usb at76_usb ath9k_htc carl9170 orinoco_usb p54usb prism2_usb r8712u r8192s_usb r8192u_usb rndis_wlan rt2500usb rt2800usb rt2870sta rt73usb rtl8187 rtl8192cu usb8xxx vt6656_stage zd1201 zd1211rw" - modprobe --quiet --remove $modulesList || true - possibleUsbDevicesNeedingReload=$(dmesg | grep -Pio '(?<=usb )[0-9-]+(?=:.*firmware)' | sort | uniq) - for usbPath in $possibleUsbDevicesNeedingReload; do - if [[ -f "/sys/bus/usb/devices/$usbPath/authorized" ]]; then - echo "Try to reload driver for usb $usbPath" >&2 - echo 0 > /sys/bus/usb/devices/$usbPath/authorized - echo 1 > /sys/bus/usb/devices/$usbPath/authorized - # Wait for driver reloading - sleep 2 - fi - done + modulesList="acx-mac80211 ar5523 ar9170usb at76c50x-usb at76_usb ath9k_htc carl9170 orinoco_usb p54usb prism2_usb r8712u r8192s_usb r8192u_usb rndis_wlan rt2500usb rt2800usb rt2870sta rt73usb rtl8187 rtl8192cu usb8xxx vt6656_stage zd1201 zd1211rw" + modprobe --quiet --remove $modulesList || true + possibleUsbDevicesNeedingReload=$(dmesg | grep -Pio '(?<=usb )[0-9-]+(?=:.*firmware)' | sort | uniq) + for usbPath in $possibleUsbDevicesNeedingReload; do + if [[ -f "/sys/bus/usb/devices/$usbPath/authorized" ]]; then + echo "Try to reload driver for usb $usbPath" >&2 + echo 0 > /sys/bus/usb/devices/$usbPath/authorized + echo 1 > /sys/bus/usb/devices/$usbPath/authorized + # Wait for driver reloading + sleep 2 + fi + done } function configure_hostapd() { - if [[ "${wifi_secure}" -eq 1 ]]; then - sec_comment="" - else - sec_comment="#" - fi + if [[ "${wifi_secure}" -eq 1 ]]; then + sec_comment="" + else + sec_comment="#" + fi - ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" + ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" } function configure_dnsmasq() { - ynh_add_config --template="/etc/dnsmasq.$app/dnsmasq.conf.tpl" --destination="/etc/dnsmasq.d/$app.conf" - systemctl restart dnsmasq + ynh_add_config --template="/etc/dnsmasq.$app/dnsmasq.conf.tpl" --destination="/etc/dnsmasq.d/$app.conf" + systemctl restart dnsmasq } function configure_dhcp() { - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" - if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" - fi + if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then + ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" + fi } - -#================================================= -# EXPERIMENTAL HELPERS -#================================================= - -#================================================= -# FUTURE OFFICIAL HELPERS -#================================================= From f40082a94c707f8ca1ddb08ee6c969d96b7885a3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 18:44:05 +0200 Subject: [PATCH 064/111] yolo: we shouldn't need to add an app-specific conf for dnsmasq only to add 'interface=wlanX', it's already handled by the core, we just need to regen-conf dnsmasq when we start/stop the hotspot ? --- conf/dnsmasq.conf.tpl | 4 ---- conf/ynh-hotspot | 6 ++++++ scripts/_common.sh | 6 ------ scripts/backup | 1 - scripts/config | 1 - scripts/install | 6 ++---- scripts/restore | 4 ---- scripts/upgrade | 6 ++---- 8 files changed, 10 insertions(+), 24 deletions(-) delete mode 100644 conf/dnsmasq.conf.tpl diff --git a/conf/dnsmasq.conf.tpl b/conf/dnsmasq.conf.tpl deleted file mode 100644 index 7af2560..0000000 --- a/conf/dnsmasq.conf.tpl +++ /dev/null @@ -1,4 +0,0 @@ -# Wifi Hotspot app for YunoHost - -# Enable DNS resolution on wifi interface -interface=__WIFI_DEVICE__ diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 0133dee..f190a78 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -296,6 +296,9 @@ start) # Update dynamic settings ynh_app_setting_set hotspot gateway_interface "${new_gateway_interface}" + + # Regen-conf dnsmasq to enable dns resolution on dnsmasq for the new interface + yunohost tools regen-conf dnsmasq ;; stop) echo "[hotspot] Stopping..." @@ -319,6 +322,9 @@ stop) echo "Stop hostapd" stop_hostapd fi + + # Regen-conf dnsmasq to disable dns resolution on dnsmasq for the previous interface + yunohost tools regen-conf dnsmasq ;; restart) $0 stop diff --git a/scripts/_common.sh b/scripts/_common.sh index 6ca2179..8d1c20d 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -83,12 +83,6 @@ function configure_hostapd() ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" } -function configure_dnsmasq() -{ - ynh_add_config --template="/etc/dnsmasq.$app/dnsmasq.conf.tpl" --destination="/etc/dnsmasq.d/$app.conf" - systemctl restart dnsmasq -} - function configure_dhcp() { ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" diff --git a/scripts/backup b/scripts/backup index de71cba..a3dbd5e 100644 --- a/scripts/backup +++ b/scripts/backup @@ -43,7 +43,6 @@ ynh_print_info --message="Declaring files to be backed up..." ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf.tpl" ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory -ynh_backup --src_path="/etc/dnsmasq.$app/dnsmasq.conf.tpl" ynh_backup --src_path="/etc/dnsmasq.d/$app.conf" --not_mandatory ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" diff --git a/scripts/config b/scripts/config index ff02211..1283d6b 100644 --- a/scripts/config +++ b/scripts/config @@ -205,7 +205,6 @@ ynh_app_config_apply() { if [[ "${service_enabled}" -eq 1 ]]; then configure_hostapd - configure_dnsmasq configure_dhcp # Start hotspot diff --git a/scripts/install b/scripts/install index 5f746d4..98588b2 100644 --- a/scripts/install +++ b/scripts/install @@ -162,7 +162,6 @@ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq.conf.tpl /etc/dnsmasq.$app/dnsmasq.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl @@ -192,9 +191,8 @@ systemctl stop hostapd 2>&1 systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd is masked after install ... if [[ -n "${wifi_device}" ]]; then - configure_hostapd - configure_dnsmasq - configure_dhcp + configure_hostapd + configure_dhcp fi #================================================= diff --git a/scripts/restore b/scripts/restore index 679bb30..7bfd49f 100644 --- a/scripts/restore +++ b/scripts/restore @@ -74,10 +74,6 @@ fi ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf.tpl" ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory -ynh_restore_file --origin_path="/etc/dnsmasq.$app/dnsmasq.conf.tpl" -ynh_restore_file --origin_path="/etc/dnsmasq.d/$app.conf" --not_mandatory -systemctl restart dnsmasq - ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory diff --git a/scripts/upgrade b/scripts/upgrade index 1d56a39..e3fdd59 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -214,14 +214,12 @@ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq.conf.tpl /etc/dnsmasq.$app/dnsmasq.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl if [[ -n "${wifi_device}" ]]; then - configure_hostapd - configure_dnsmasq - configure_dhcp + configure_hostapd + configure_dhcp fi # Copy init script From bdd92243419bc45116a1d9d4eb995c129fb391e2 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Thu, 5 Oct 2023 16:45:40 +0000 Subject: [PATCH 065/111] Auto-update README --- README.md | 1 - README_fr.md | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index 7cd872e..7ce4f72 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources * Official app website: -* YunoHost documentation for this app: * Report a bug: ## Developer info diff --git a/README_fr.md b/README_fr.md index 8706e5d..d60ae7e 100644 --- a/README_fr.md +++ b/README_fr.md @@ -29,7 +29,6 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources * Site officiel de l’app : -* Documentation YunoHost pour cette app : * Signaler un bug : ## Informations pour les développeurs From 3a72871641a734870511629057f30cf1174574bd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 18:49:17 +0200 Subject: [PATCH 066/111] follow-up of previous commit : we probably need yunohost's lock to be able to trigger the regenconf from inside the service script --- scripts/install | 2 +- scripts/restore | 2 +- scripts/upgrade | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/install b/scripts/install index 98588b2..8dfe28a 100644 --- a/scripts/install +++ b/scripts/install @@ -210,7 +210,7 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock #================================================= # START SYSTEMD SERVICE diff --git a/scripts/restore b/scripts/restore index 7bfd49f..807a336 100644 --- a/scripts/restore +++ b/scripts/restore @@ -108,7 +108,7 @@ ynh_restore_file --origin_path="/etc/systemd/system/hostapd@$app.service" #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock #================================================= # START SYSTEMD SERVICE diff --git a/scripts/upgrade b/scripts/upgrade index e3fdd59..8826961 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -252,7 +252,7 @@ ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_host #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" +yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock #================================================= # START SYSTEMD SERVICE From b80ee8b347bd0f723e7aa63574e4d30618cb605f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 19:00:56 +0200 Subject: [PATCH 067/111] Cleanup the whole stuff with .tpl file ... we don't need to copy them to intermediate locations, nor to backup/restore them... --- conf/{dnsmasq_dhcpdv4.conf.tpl => dnsmasq_dhcpdv4.conf} | 0 conf/{dnsmasq_dhcpdv6.conf.tpl => dnsmasq_dhcpdv6.conf} | 0 conf/{hostapd.conf.tpl => hostapd.conf} | 0 scripts/_common.sh | 6 +++--- scripts/backup | 7 ------- scripts/install | 4 ---- scripts/restore | 5 ----- scripts/upgrade | 4 ---- 8 files changed, 3 insertions(+), 23 deletions(-) rename conf/{dnsmasq_dhcpdv4.conf.tpl => dnsmasq_dhcpdv4.conf} (100%) rename conf/{dnsmasq_dhcpdv6.conf.tpl => dnsmasq_dhcpdv6.conf} (100%) rename conf/{hostapd.conf.tpl => hostapd.conf} (100%) diff --git a/conf/dnsmasq_dhcpdv4.conf.tpl b/conf/dnsmasq_dhcpdv4.conf similarity index 100% rename from conf/dnsmasq_dhcpdv4.conf.tpl rename to conf/dnsmasq_dhcpdv4.conf diff --git a/conf/dnsmasq_dhcpdv6.conf.tpl b/conf/dnsmasq_dhcpdv6.conf similarity index 100% rename from conf/dnsmasq_dhcpdv6.conf.tpl rename to conf/dnsmasq_dhcpdv6.conf diff --git a/conf/hostapd.conf.tpl b/conf/hostapd.conf similarity index 100% rename from conf/hostapd.conf.tpl rename to conf/hostapd.conf diff --git a/scripts/_common.sh b/scripts/_common.sh index 8d1c20d..c67cc0e 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -80,14 +80,14 @@ function configure_hostapd() sec_comment="#" fi - ynh_add_config --template="/etc/hostapd/$app/hostapd.conf.tpl" --destination="/etc/hostapd/$app/hostapd.conf" + ynh_add_config --template="../conf/hostapd.conf" --destination="/etc/hostapd/$app/hostapd.conf" } function configure_dhcp() { - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" + ynh_add_config --template="../conf/dhcpdv4.conf" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" + ynh_add_config --template="../conf/dhcpdv6.conf" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" fi } diff --git a/scripts/backup b/scripts/backup index a3dbd5e..ce94889 100644 --- a/scripts/backup +++ b/scripts/backup @@ -40,15 +40,8 @@ ynh_print_info --message="Declaring files to be backed up..." # BACKUP THE APP MAIN DIR #================================================= -ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf.tpl" ynh_backup --src_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory - -ynh_backup --src_path="/etc/dnsmasq.d/$app.conf" --not_mandatory - -ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory - -ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" ynh_backup --src_path="/etc/dnsmasq.$app/dhcpdv4.conf" --not_mandatory ynh_backup --src_path="/usr/local/bin/$service_name" diff --git a/scripts/install b/scripts/install index 8dfe28a..ff45235 100644 --- a/scripts/install +++ b/scripts/install @@ -161,10 +161,6 @@ chown root: /etc/hostapd/$app/ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ -install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl - # Copy init script ynh_add_config --template="../conf/ynh-hotspot" --destination="/usr/local/bin/$service_name" chmod 0755 "/usr/local/bin/$service_name" diff --git a/scripts/restore b/scripts/restore index 807a336..8510bc7 100644 --- a/scripts/restore +++ b/scripts/restore @@ -71,13 +71,8 @@ else pkg_dependencies="$pkg_dependencies $free_firmware_packages" fi -ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf.tpl" ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory - -ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory - -ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv4.conf.tpl" ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv4.conf" --not_mandatory ynh_restore_file --origin_path="/usr/local/bin/$service_name" diff --git a/scripts/upgrade b/scripts/upgrade index 8826961..a64ab4d 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -213,10 +213,6 @@ chown root: /etc/hostapd/$app/ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ -install -b -o root -g root -m 0644 ../conf/hostapd.conf.tpl /etc/hostapd/$app/hostapd.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv6.conf.tpl /etc/dnsmasq.$app/dhcpdv6.conf.tpl -install -b -o root -g root -m 0644 ../conf/dnsmasq_dhcpdv4.conf.tpl /etc/dnsmasq.$app/dhcpdv4.conf.tpl - if [[ -n "${wifi_device}" ]]; then configure_hostapd configure_dhcp From 3e2bd872ce3b16e0c71c6d31c36eef0468da30eb Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 19:05:13 +0200 Subject: [PATCH 068/111] Cleanup/propagate stuff to config panel controller as well --- scripts/config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/config b/scripts/config index 1283d6b..ad92a84 100644 --- a/scripts/config +++ b/scripts/config @@ -213,12 +213,13 @@ ynh_app_config_apply() { else ynh_print_info --message="Cleanup hotspot config files" ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf" - ynh_secure_remove --file="/etc/dnsmasq.d/$app.conf" ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv4.conf" ynh_secure_remove --file="/etc/dnsmasq.$app/dhcpdv6.conf" systemctl restart dnsmasq fi + + yunohost tools regen-conf dnsmasq } ynh_app_config_run $1 From 8449fc5ac8d0d7b998626e1b5d24b85b04532088 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 19:08:51 +0200 Subject: [PATCH 069/111] Delete boring legacy /etc/dnsmasq.d/$app.conf if it exists --- scripts/upgrade | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/upgrade b/scripts/upgrade index a64ab4d..42929db 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -70,6 +70,9 @@ if [ -d /var/www/wifiadmin/ ]; then ynh_secure_remove /var/www/wifiadmin/ fi +if [ -e /etc/dnsmasq.d/$app.conf ]; then + ynh_secure_remove /etc/dnsmasq.d/$app.conf +fi if [ $firmware_nonfree = "yes" ]; then firmware_nonfree=1 From cf03447866ec0c575ee1e2e305d43d3a5bdcf900 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 19:27:55 +0200 Subject: [PATCH 070/111] Oopsies --- scripts/_common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/_common.sh b/scripts/_common.sh index c67cc0e..942715a 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -85,9 +85,9 @@ function configure_hostapd() function configure_dhcp() { - ynh_add_config --template="../conf/dhcpdv4.conf" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" + ynh_add_config --template="../conf/dnsmasq_dhcpdv4.conf" --destination="/etc/dnsmasq.$app/dhcpdv4.conf" if [[ -n "${ip6_net}" ]] && [[ "${ip6_net}" != "none" ]]; then - ynh_add_config --template="../conf/dhcpdv6.conf" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" + ynh_add_config --template="../conf/dnsmasq_dhcpdv6.conf" --destination="/etc/dnsmasq.$app/dhcpdv6.conf" fi } From 1d9844f997d30a4b05c7e8916e3bb0c829c01dd3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 5 Oct 2023 20:34:32 +0200 Subject: [PATCH 071/111] Use 'yunohost service start' instead of 'ynh_systemd_action' such that yunohost properly gives the lock to the 'yunohost tools regen-conf' inside the service --- scripts/install | 2 +- scripts/restore | 2 +- scripts/upgrade | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/install b/scripts/install index ff45235..13640b1 100644 --- a/scripts/install +++ b/scripts/install @@ -217,7 +217,7 @@ ynh_script_progression --message="Starting a systemd service..." if [[ $wifi_device == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else - ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" + yunohost service start $service_name fi #================================================= diff --git a/scripts/restore b/scripts/restore index 8510bc7..8232152 100644 --- a/scripts/restore +++ b/scripts/restore @@ -126,7 +126,7 @@ fi if [[ $wifi_device == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else - ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" + yunohost service start $service_name fi #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 42929db..3d3ecbd 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -58,8 +58,7 @@ ynh_abort_if_errors # STOP SYSTEMD SERVICE #================================================= ynh_script_progression --message="Stopping the hotspot service ... (this may take some time)" - -ynh_systemd_action --service_name=$service_name --action="stop" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" +yunohost service stop $service_name #================================================= # ENSURE DOWNWARD COMPATIBILITY @@ -269,7 +268,7 @@ fi if [[ $wifi_device == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else - ynh_systemd_action --service_name=$service_name --action="start" --log_path=systemd #--line_match="Started YunoHost Wifi Hotspot" + yunohost service start $service_name fi #================================================= From 8c3f2e721cedf3709105a81e671642542c18559c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin <4533074+alexAubin@users.noreply.github.com> Date: Thu, 5 Oct 2023 22:48:05 +0200 Subject: [PATCH 072/111] Bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 8fc2251..369f0f2 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,7 @@ "en": "Create and manager wifi networks, share Internet access and use YunoHost apps accross wifi", "fr": "Créer et configurer des réseaux wifi, partager l'accès a Internet et utiliser les applications YunoHost via wifi" }, - "version": "2.1.1~ynh1", + "version": "2.2.0~ynh1", "url": "https://github.com/labriqueinternet/hotspot_ynh", "license": "AGPL-3.0", "maintainer": { From 07ade30f32c2d6d16f993769b1fda60acba46ffa Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Thu, 5 Oct 2023 20:48:09 +0000 Subject: [PATCH 073/111] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7ce4f72..d13490b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.1.1~ynh1 +**Shipped version:** 2.2.0~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index d60ae7e..af0814d 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.1.1~ynh1 +**Version incluse :** 2.2.0~ynh1 ## Captures d’écran From ddb932408407c36342e6fdb01beb4d51a21dd845 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 6 Oct 2023 18:36:24 +0200 Subject: [PATCH 074/111] Yolo packaging v2 --- doc/PRE_INSTALL.md | 1 + doc/PRE_INSTALL_fr.md | 1 + manifest.json | 66 ------------------------ manifest.toml | 73 ++++++++++++++++++++++++++ scripts/_common.sh | 26 ---------- scripts/backup | 27 ---------- scripts/config | 6 --- scripts/install | 83 ++++-------------------------- scripts/remove | 44 +--------------- scripts/restore | 71 +++----------------------- scripts/upgrade | 116 ++++++++---------------------------------- 11 files changed, 111 insertions(+), 403 deletions(-) create mode 100644 doc/PRE_INSTALL.md create mode 100644 doc/PRE_INSTALL_fr.md delete mode 100644 manifest.json create mode 100644 manifest.toml diff --git a/doc/PRE_INSTALL.md b/doc/PRE_INSTALL.md new file mode 100644 index 0000000..1b77a3b --- /dev/null +++ b/doc/PRE_INSTALL.md @@ -0,0 +1 @@ +After the installation, you will be able to configure the application from YunoHost's webadmin in Applications > Hotspot > Configuration. diff --git a/doc/PRE_INSTALL_fr.md b/doc/PRE_INSTALL_fr.md new file mode 100644 index 0000000..e210e68 --- /dev/null +++ b/doc/PRE_INSTALL_fr.md @@ -0,0 +1 @@ +Après l'application, vous pourrez configurer l'application depuis la webadmin de YunoHost dans Applications > Hotspot > Configuration. diff --git a/manifest.json b/manifest.json deleted file mode 100644 index 369f0f2..0000000 --- a/manifest.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "Wifi Hotspot", - "id": "hotspot", - "packaging_format": 1, - "description": { - "en": "Create and manager wifi networks, share Internet access and use YunoHost apps accross wifi", - "fr": "Créer et configurer des réseaux wifi, partager l'accès a Internet et utiliser les applications YunoHost via wifi" - }, - "version": "2.2.0~ynh1", - "url": "https://github.com/labriqueinternet/hotspot_ynh", - "license": "AGPL-3.0", - "maintainer": { - "name": "Julien Vaubourg", - "email": "julien@vaubourg.com", - "url": "http://julien.vaubourg.com" - }, - "upstream": { - "license": "AGPL-3.0", - "website": "https://internetcu.be/" - }, - "requirements": { - "yunohost": ">= 4.3.2" - }, - "multi_instance": true, - "services": [], - "arguments": { - "install" : [ - { - "name": "disclaimer", - "type": "display_text", - "style": "info", - "ask": { - "en": "After installation, you will be able to configure the application from YunoHost's webadmin in Applications > Hotspot > Configuration.", - "fr": "Après l'application, vous pourrez configurer l'application depuis la webadmin de YunoHost dans Applications > Hotspot > Configuration." - } - }, - { - "name": "wifi_ssid", - "type": "string", - "ask": { - "en": "Choose a wifi name (SSID)", - "fr": "Choisissez un nom pour le wifi (SSID)" - }, - "example": "myNeutralNetwork", - "default": "myNeutralNetwork" - }, - { - "name": "wifi_passphrase", - "type": "password", - "ask": { - "en": "Choose a wifi password (at least 8 characters for WPA2)", - "fr": "Choisissez un mot de passe wifi (au minimum 8 caractères pour le WPA2)" - } - }, - { - "name": "firmware_nonfree", - "type": "boolean", - "ask": { - "en": "Install non-free WiFi firmwares? (Only needed if you're using a proprietary WiFi antenna/dongle)", - "fr": "Installer des firmwares WiFi non-libres ? (Nécessaire seulement si vous utilisez une antenne/clé WiFi propriétaire)" - }, - "default": false - } - ] - } -} diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..95d1d72 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,73 @@ +#:schema https://raw.githubusercontent.com/YunoHost/apps/master/schemas/manifest.v2.schema.json + +packaging_format = 2 + +id = "hotspot" +name = "Wifi Hotspot" +description.en = "Create and configure a WiFi hotspot" +description.fr = "Créez et gérez un point d'accès WiFi" + +version = "2.3.0~ynh1" + +maintainers = [] + +[upstream] +license = "AGPL-3.0" +website = "https://internetcu.be/" + +[integration] +yunohost = ">= 11.2" +architectures = "all" +multi_instance = true +ldap = "not_relevant" +sso = "not_relevant" +disk = "50M" +ram.build = "50M" +ram.runtime = "50M" + +[install] + [install.wifi_ssid] + ask.en = "Choose a wifi name (SSID)" + ask.fr = "Choisissez un nom pour le wifi (SSID)" + type = "string" + example = "myNeutralNetwork" + default = "myNeutralNetwork" + + [install.wifi_passphrase] + ask.en = "Choose a wifi password (at least 8 characters for WPA2)" + ask.fr = "Choisissez un mot de passe wifi (au minimum 8 caractères pour le WPA2)" + type = "password" + + [install.firmware_nonfree] + ask.en = "Install non-free WiFi firmwares? (Only needed if you're using a proprietary WiFi antenna/dongle)" + ask.fr = "Installer des firmwares WiFi non-libres ? (Nécessaire seulement si vous utilisez une antenne/clé WiFi propriétaire)" + type = "boolean" + default = false + +[resources] + [resources.system_user] + + [resources.apt] + packages = "sipcalc, hostapd, iw, kmod" + +# ========================================= +# FIXME FIXME FIXME FIXME FIXME FIXME FIXME +# Gotta find a way to conditionally install the non-free packages +# So far the "packages_from_raw_bash" thing doesnt allow conditional packages from extra repo (here, the non-free component..) +# FIXME FIXME FIXME FIXME FIXME FIXME FIXME +# ========================================= +# +# # Packaged USB Wireless Device firmwares +# # Based on https://wiki.debian.org/WiFi#USB_Devices +# if [[ $firmware_nonfree -eq 1 ]]; then +# # FIXME : if armbian-firmware is detected, we should remove ra-link.... +# #if dpkg --list | grep -q armbian-firmware; then +# # echo "You are running Armbian and firmware-misc-nonfree are known to conflict with armbian-firwmare. " >&2 +# # echo "The package firmware-misc-nonfree is a dependency of firmware-ralink, so firmware-ralink will NOT be installed" >&2 +# # echo "You can manually install firmware-ralink with 'apt -o Dpkg::Options::=\"--force-overwrite\" firmware-ralink'" >&2 +# # nonfree_firmware_packages=$(echo $nonfree_firmware_packages | sed 's/ firmware-ralink//') +# #fi +# echo "firmware-atheros firmware-realtek firmware-ralink firmware-libertas atmel-firmware firmware-zd1211" +# else +# echo "firmware-ath9k-htc" +# fi diff --git a/scripts/_common.sh b/scripts/_common.sh index 942715a..6d440ed 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -1,17 +1,5 @@ #!/bin/bash -#================================================= -# COMMON VARIABLES -#================================================= - -pkg_dependencies="sipcalc hostapd iw kmod" -nonfree_firmware_packages="firmware-atheros firmware-realtek firmware-ralink firmware-libertas atmel-firmware firmware-zd1211" -free_firmware_packages="firmware-ath9k-htc" - -#================================================= -# PERSONAL HELPERS -#================================================= - function other_hotspot_apps() { local app_shortname="${app%%__*}" @@ -41,20 +29,6 @@ function unused_iw_devices() iw_devices | grep -F -v -f <(used_iw_devices) } -function check_armbian_nonfree_conflict() -{ - - # If we're on armbian, force $firmware_nonfree - # because armbian-firmware conflicts with firmware-misc-nonfree package - if dpkg --list | grep -q armbian-firmware; then - echo "You are running Armbian and firmware-misc-nonfree are known to conflict with armbian-firwmare. " >&2 - echo "The package firmware-misc-nonfree is a dependency of firmware-ralink, so firmware-ralink will NOT be installed" >&2 - echo "You can manually install firmware-ralink with 'apt -o Dpkg::Options::=\"--force-overwrite\" firmware-ralink'" >&2 - nonfree_firmware_packages=$(echo $nonfree_firmware_packages | sed 's/ firmware-ralink//') - fi - -} - function hot_reload_usb_wifi_cards() { diff --git a/scripts/backup b/scripts/backup index ce94889..d01e26d 100644 --- a/scripts/backup +++ b/scripts/backup @@ -10,27 +10,6 @@ source ../settings/scripts/_common.sh source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - ynh_clean_check_starting -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# LOAD SETTINGS -#================================================= -ynh_print_info --message="Loading installation settings..." - -app=$YNH_APP_INSTANCE_NAME - -final_path=$(ynh_app_setting_get --app=$app --key=final_path) -firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) -service_name=$(ynh_app_setting_get --app=$app --key=service_name) - #================================================= # DECLARE DATA AND CONF FILES TO BACKUP #================================================= @@ -49,12 +28,6 @@ ynh_backup --src_path="/usr/local/bin/$service_name" ynh_backup --src_path="/etc/openvpn/scripts/route-up.d/90-$service_name" ynh_backup --src_path="/etc/openvpn/scripts/route-down.d/90-$service_name" -#================================================= -# SPECIFIC BACKUP -#================================================= -# BACKUP SYSTEMD -#================================================= - ynh_backup --src_path="/etc/systemd/system/$service_name.service" ynh_backup --src_path="/etc/systemd/system/hostapd@$app.service" diff --git a/scripts/config b/scripts/config index ad92a84..c353476 100644 --- a/scripts/config +++ b/scripts/config @@ -16,12 +16,6 @@ source /usr/share/yunohost/helpers # Exit if an error occurs during the execution of the script ynh_abort_if_errors -#================================================= -# RETRIEVE ARGUMENTS -#================================================= - -final_path=$(ynh_app_setting_get $app final_path) - #================================================= # SPECIFIC GETTERS FOR TOML SHORT KEY #================================================= diff --git a/scripts/install b/scripts/install index 13640b1..b3335cf 100644 --- a/scripts/install +++ b/scripts/install @@ -9,32 +9,14 @@ source _common.sh source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - ynh_clean_check_starting -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# RETRIEVE ARGUMENTS FROM THE MANIFEST -#================================================= - -wifi_ssid=$YNH_APP_ARG_WIFI_SSID -wifi_passphrase=$YNH_APP_ARG_WIFI_PASSPHRASE -firmware_nonfree=$YNH_APP_ARG_FIRMWARE_NONFREE - -app=$YNH_APP_INSTANCE_NAME - -# the service name must match the service template files service_name=ynh-$app +ynh_app_setting_set --app=$app --key=service_name --value=$service_name #================================================= # CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS #================================================= + +# FIXME : we could probably implement all these checks in manifest.toml directly ? ynh_script_progression --message="Validating installation parameters..." # Check arguments @@ -45,7 +27,7 @@ fi # Check passphrase length wifi_passphrase_length="$(wc -c <<< "${wifi_passphrase}")" if [[ $wifi_passphrase_length -lt 8 ]] || [[ $wifi_passphrase_length -gt 63 ]]; then - ynh_die --message="Your password must from 8 to 63 characters (WPA2 passphrase)" + ynh_die --message="Your password must have between 8 and 63 characters (WPA2 passphrase)" fi # Check no special characters are present in the passphrase @@ -53,16 +35,6 @@ if [[ $wifi_passphrase =~ [^[:print:]] ]]; then ynh_die --message="Only printable ASCII characters are permitted in your password (WPA2 passphrase)" fi -#================================================= -# STORE SETTINGS FROM MANIFEST -#================================================= -ynh_script_progression --message="Storing installation settings..." - -ynh_app_setting_set --app=$app --key=wifi_ssid --value="$wifi_ssid" -ynh_app_setting_set --app=$app --key=wifi_passphrase --value="$wifi_passphrase" -ynh_app_setting_set --app=$app --key=firmware_nonfree --value="$firmware_nonfree" -ynh_app_setting_set --app=$app --key=service_name --value=$service_name - #================================================= # STANDARD MODIFICATIONS #================================================= @@ -71,6 +43,7 @@ ynh_app_setting_set --app=$app --key=service_name --value=$service_name ynh_script_progression --message="Configuring firewall..." # Update firewall for DHCP +# FIXME : move to manifest.toml ynh_exec_warn_less yunohost firewall allow --no-upnp --ipv6 UDP 547 ynh_exec_warn_less yunohost firewall allow --no-upnp UDP 67 @@ -81,37 +54,6 @@ then /usr/sbin/rfkill unblock wlan fi -#================================================= -# INSTALL NONFREE FIRWARE IF REQUESTED -#================================================= -ynh_script_progression --message="Installing firmware..." - -export DEBIAN_FRONTEND=noninteractive - -# Packaged USB Wireless Device firmwares -# Based on https://wiki.debian.org/WiFi#USB_Devices -if [[ $firmware_nonfree -eq 1 ]]; then - check_armbian_nonfree_conflict - ynh_install_extra_app_dependencies --repo="deb http://deb.debian.org/debian $(ynh_get_debian_release) non-free" --package="$nonfree_firmware_packages" -else - pkg_dependencies="$pkg_dependencies $free_firmware_packages" -fi - -#================================================= -# INSTALL DEPENDENCIES -#================================================= -ynh_script_progression --message="Installing dependencies..." - -ynh_install_app_dependencies $pkg_dependencies - -#================================================= -# CREATE DEDICATED USER -#================================================= -ynh_script_progression --message="Configuring system user..." - -# Create a system user -ynh_system_user_create --username=$app - #================================================= # SPECIFIC SETTINGS #================================================= @@ -132,9 +74,7 @@ wifi_device=$(unused_iw_devices | head -n 1) wifi_secure=1 wifi_channel=6 -ynh_app_setting_set --app=$app --key=wifi_ssid --value="${wifi_ssid}" ynh_app_setting_set --app=$app --key=wifi_secure --value="${wifi_secure}" -ynh_app_setting_set --app=$app --key=wifi_passphrase --value="${wifi_passphrase}" ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" ynh_app_setting_set --app=$app --key=wifi_channel --value="${wifi_channel}" ynh_app_setting_set --app=$app --key=advanced --value=0 @@ -144,7 +84,7 @@ ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" ynh_app_setting_set --app=$app --key=ip4_nat_prefix --value="${ip4_nat_prefix}" -if [[ -z $wifi_device ]]; then +if [[ -z "$wifi_device" ]]; then ynh_app_setting_set --app=$app --key=service_enabled --value=0 else ynh_app_setting_set --app=$app --key=service_enabled --value=1 @@ -194,28 +134,23 @@ fi #================================================= # SETUP SYSTEMD #================================================= -ynh_script_progression --message="Configuring a systemd service..." +ynh_script_progression --message="Configuring systemd service..." # Create a dedicated systemd config ynh_add_systemd_config --service=$service_name # Create custom systemd config for hostapd to handle multiple wifi devices ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_hostapd.service" -#================================================= -# INTEGRATE SERVICE IN YUNOHOST -#================================================= -ynh_script_progression --message="Integrating service in YunoHost..." - yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock #================================================= # START SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Starting a systemd service..." +ynh_script_progression --message="Starting the hotspot..." # Start a systemd service if device is present if [[ $wifi_device == "" ]]; then - echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 + echo "WARNING: Wifi Hotspot is not started because no wifi device was found (check the Hotspot configuration in the webadmin > Applications > Hotspot > the config panel)" >&2 else yunohost service start $service_name fi diff --git a/scripts/remove b/scripts/remove index 7b3d158..edaa762 100644 --- a/scripts/remove +++ b/scripts/remove @@ -9,16 +9,6 @@ source _common.sh source /usr/share/yunohost/helpers -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." - -app=$YNH_APP_INSTANCE_NAME - -firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) -service_name=$(ynh_app_setting_get --app=$app --key=service_name) - #================================================= # STANDARD REMOVE #================================================= @@ -30,32 +20,16 @@ ynh_script_progression --message="Removing $app service" yunohost service stop $service_name yunohost service remove $service_name -#================================================= -# STOP AND REMOVE SERVICE -#================================================= -ynh_script_progression --message="Stopping and removing the systemd service..." - -# Remove the dedicated systemd config ynh_remove_systemd_config --service=$service_name ynh_remove_systemd_config --service="hostapd@$app" -#================================================= -# REMOVE DEPENDENCIES -#================================================= -ynh_script_progression --message="Removing dependencies..." - -# Remove metapackage and its dependencies -ynh_remove_app_dependencies - #================================================= # REMOVE APP MAIN DIR #================================================= -ynh_script_progression --message="Removing app main directory..." +ynh_script_progression --message="Removing $app configurations..." ynh_secure_remove --file="/etc/openvpn/scripts/route-up.d/90-${service_name}" ynh_secure_remove --file="/etc/openvpn/scripts/route-down.d/90-${service_name}" - -# Remove the app directory securely ynh_secure_remove --file="/usr/local/bin/$service_name" for FILE in $(ls /tmp/.${service_name}-* 2>/dev/null); do @@ -84,19 +58,3 @@ if [[ -z "$(other_hotspot_apps)" ]]; then ynh_exec_warn_less yunohost firewall disallow TCP 67 fi fi - -#================================================= -# GENERIC FINALIZATION -#================================================= -# REMOVE DEDICATED USER -#================================================= -ynh_script_progression --message="Removing the dedicated system user..." - -# Delete a system user -ynh_system_user_delete --username=$app - -#================================================= -# END OF SCRIPT -#================================================= - -ynh_script_progression --message="Removal of $app completed" diff --git a/scripts/restore b/scripts/restore index 8232152..4d93dce 100644 --- a/scripts/restore +++ b/scripts/restore @@ -10,32 +10,6 @@ source ../settings/scripts/_common.sh source /usr/share/yunohost/helpers -#================================================= -# MANAGE SCRIPT FAILURE -#================================================= - -ynh_clean_setup () { - ynh_clean_check_starting -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." - -app=$YNH_APP_INSTANCE_NAME - -firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) -service_name=$(ynh_app_setting_get --app=$app --key=service_name) -wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) - -#================================================= -# CHECK IF THE APP CAN BE RESTORED -#================================================= -ynh_script_progression --message="Validating restoration parameters..." - #================================================= # FIND AND OPEN A PORT #================================================= @@ -55,21 +29,8 @@ fi #================================================= # STANDARD RESTORATION STEPS #================================================= -#================================================= -# RECREATE THE DEDICATED USER -#================================================= -ynh_script_progression --message="Recreating the dedicated system user..." -# Create the dedicated user (if not existing) -ynh_system_user_create --username=$app - - -if [[ $firmware_nonfree -eq 1 ]]; then - check_armbian_nonfree_conflict - ynh_install_extra_app_dependencies --repo="deb http://deb.debian.org/debian $(ynh_get_debian_release) non-free" --package="$nonfree_firmware_packages" -else - pkg_dependencies="$pkg_dependencies $free_firmware_packages" -fi +ynh_script_progression --message="Restoring configurations ..." ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory @@ -80,50 +41,30 @@ ynh_restore_file --origin_path="/usr/local/bin/$service_name" ynh_restore_file --origin_path="/etc/openvpn/scripts/route-up.d/90-${service_name}" ynh_restore_file --origin_path="/etc/openvpn/scripts/route-down.d/90-${service_name}" -#================================================= -# SPECIFIC RESTORATION -#================================================= -# REINSTALL DEPENDENCIES -#================================================= -ynh_script_progression --message="Reinstalling dependencies..." - -# Define and install dependencies -ynh_install_app_dependencies $pkg_dependencies - -#================================================= -# RESTORE SYSTEMD -#================================================= -ynh_script_progression --message="Restoring the systemd configuration..." - ynh_restore_file --origin_path="/etc/systemd/system/$service_name.service" ynh_restore_file --origin_path="/etc/systemd/system/hostapd@$app.service" -#================================================= -# INTEGRATE SERVICE IN YUNOHOST -#================================================= -ynh_script_progression --message="Integrating service in YunoHost..." - -yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock +yunohost service add "$service_name" --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock #================================================= # START SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Starting a systemd service..." +ynh_script_progression --message="Starting the hotspot service..." hot_reload_usb_wifi_cards -if [[ -z "$wifi_device" ]] || ! grep -q -F "$wifi_device" <(unused_iw_devices); then +if [[ -z "${wifi_device:-}" ]] || ! grep -q -F "$wifi_device" <(unused_iw_devices); then wifi_device=$(unused_iw_devices | head -n 1) ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" fi -if [[ -z $wifi_device ]]; then +if [[ -z "${wifi_device:-}" ]]; then ynh_app_setting_set --app=$app --key=service_enabled --value=0 else ynh_app_setting_set --app=$app --key=service_enabled --value=1 fi # Start a systemd service if device is present -if [[ $wifi_device == "" ]]; then +if [[ "${wifi_device:-}" == "" ]]; then echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 else yunohost service start $service_name diff --git a/scripts/upgrade b/scripts/upgrade index 3d3ecbd..7b14ae9 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -9,49 +9,8 @@ source _common.sh source /usr/share/yunohost/helpers -#================================================= -# LOAD SETTINGS -#================================================= -ynh_script_progression --message="Loading installation settings..." - -app=$YNH_APP_INSTANCE_NAME - -firmware_nonfree=$(ynh_app_setting_get --app=$app --key=firmware_nonfree) -service_name=$(ynh_app_setting_get --app=$app --key=service_name) - -wifi_device=$(ynh_app_setting_get --app=$app --key=wifi_device) -wifi_ssid=$(ynh_app_setting_get --app=$app --key=wifi_ssid) -wifi_secure=$(ynh_app_setting_get --app=$app --key=wifi_secure) -wifi_passphrase=$(ynh_app_setting_get --app=$app --key=wifi_passphrase) -wifi_channel=$(ynh_app_setting_get --app=$app --key=wifi_channel) -advanced=$(ynh_app_setting_get --app=$app --key=advanced) -ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) -ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) -ip6_firewall=$(ynh_app_setting_get --app=$app --key=ip6_firewall) -dns=$(ynh_app_setting_get --app=$app --key=dns) -multissid=$(ynh_app_setting_get --app=$app --key=multissid) - -#================================================= -# CHECK VERSION -#================================================= -ynh_script_progression --message="Checking version..." - upgrade_type=$(ynh_check_app_version_changed) -#================================================= -# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP -#================================================= -ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." - -# Backup the current version of the app -ynh_backup_before_upgrade -ynh_clean_setup () { - # Restore it if the upgrade fails - ynh_restore_upgradebackup -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors - #================================================= # STANDARD UPGRADE STEPS #================================================= @@ -73,20 +32,20 @@ if [ -e /etc/dnsmasq.d/$app.conf ]; then ynh_secure_remove /etc/dnsmasq.d/$app.conf fi -if [ $firmware_nonfree = "yes" ]; then +if [ ${firmware_nonfree:-} = "yes" ]; then firmware_nonfree=1 ynh_app_setting_set --app=$app --key=firmware_nonfree --value=$firmware_nonfree -elif [ $firmware_nonfree = "no" ]; then +elif [ ${firmware_nonfree:-} = "no" ]; then firmware_nonfree=0 ynh_app_setting_set --app=$app --key=firmware_nonfree --value=$firmware_nonfree fi -if [ -z $service_name ]; then +if [ -z ${service_name:-} ]; then service_name="ynh-$app" ynh_app_setting_set --app=$app --key=service_name --value=$service_name fi -if [[ -n "${multissid}" ]] && [[ "${multissid}" -gt 1 ]]; then +if [[ -n "${multissid:-}" ]] && [[ "${multissid}" -gt 1 ]]; then wifi_ssid=$(cut -d'|' -f 1 <<< ${wifi_ssid}) wifi_secure=$(cut -d'|' -f 1 <<< ${wifi_secure}) wifi_passphrase=$(cut -d'|' -f 1 <<< ${wifi_passphrase}) @@ -104,7 +63,7 @@ if [[ -n "${multissid}" ]] && [[ "${multissid}" -gt 1 ]]; then ynh_app_setting_set --app=$app --key=ip6_firewall --value="${ip6_firewall}" fi -if [[ -n "${dns}" ]]; then +if [[ -n "${dns:-}" ]]; then ip6_dns="" ip4_dns="" for ip in $(echo "${dns}" | tr ',' ' '); do @@ -118,20 +77,17 @@ if [[ -n "${dns}" ]]; then ip6_dns="${ip6_dns%%,}" ip4_dns="${ip4_dns%%,}" - if [[ -z "$(ynh_app_setting_get --app=$app --key=ip6_dns)" ]]; then + if [[ -z "$(ynh_app_setting_get --app=$app --key=ip6_dns)" ]]; then ynh_app_setting_set --app=$app --key=ip6_dns --value="${ip6_dns}" fi - if [[ -z "$(ynh_app_setting_get --app=$app --key=ip4_dns)" ]]; then + if [[ -z "$(ynh_app_setting_get --app=$app --key=ip4_dns)" ]]; then ynh_app_setting_set --app=$app --key=ip4_dns --value="${ip4_dns}" fi ynh_app_setting_delete $app dns -else - ip6_dns=$(ynh_app_setting_get --app=$app --key=ip6_dns) - ip4_dns=$(ynh_app_setting_get --app=$app --key=ip4_dns) fi -if [[ -n "${multissid}" ]]; then +if [[ -n "${multissid:-}" ]]; then ynh_app_setting_delete --app=$app --key=multissid ynh_secure_remove --file="/etc/hostapd/hostapd.conf" @@ -140,14 +96,13 @@ if [[ -n "${multissid}" ]]; then ynh_secure_remove --file="/etc/dnsmasq.dhcpd/" fi -if [[ -z "${advanced}" ]]; then +if [[ -z "${advanced:-}" ]]; then ynh_app_setting_set --app=$app --key=advanced --value=0 fi # Old stuff prior to 2.x -ip6_net=$(ynh_app_setting_get --app=$app --key=ip6_net) -if [ "$ip6_net" == "none" ] +if [ "${ip6_net:-}" == "none" ] then ip6_net="" ynh_app_setting_set --app=$app --key=ip6_net --value="$ip6_net" @@ -166,36 +121,14 @@ if [ -d /var/www/$app ]; then ynh_secure_remove /var/www/$app fi -[ -z "$(ynh_app_setting_get $app domain)" ] || ynh_app_setting_delete $app domain -[ -z "$(ynh_app_setting_get $app path)" ] || ynh_app_setting_delete $app path -[ -z "$(ynh_app_setting_get $app final_path)" ] || ynh_app_setting_delete $app final_path +[ -z "${domain:-}" ] || ynh_app_setting_delete $app domain +[ -z "${path:-}" ] || ynh_app_setting_delete $app path +[ -z "${install_dir:-}" ] || ynh_app_setting_delete $app install_dir if [ -e "/etc/sudoers.d/${app}_ynh" ]; then ynh_secure_remove "/etc/sudoers.d/${app}_ynh" fi -#================================================= -# CREATE DEDICATED USER -#================================================= -ynh_script_progression --message="Making sure dedicated system user exists..." - -# Create a dedicated user (if not existing) -ynh_system_user_create --username=$app - -#================================================= -# UPGRADE DEPENDENCIES -#================================================= -ynh_script_progression --message="Upgrading dependencies..." - -if [[ $firmware_nonfree -eq 1 ]]; then - check_armbian_nonfree_conflict - ynh_install_extra_app_dependencies --repo="deb http://deb.debian.org/debian $(ynh_get_debian_release) non-free" --package="$nonfree_firmware_packages" -else - pkg_dependencies="$pkg_dependencies $free_firmware_packages" -fi - -ynh_install_app_dependencies $pkg_dependencies - #================================================= # SPECIFIC UPGRADE #================================================= @@ -204,6 +137,7 @@ ynh_install_app_dependencies $pkg_dependencies ynh_script_progression --message="Copying configuration..." hot_reload_usb_wifi_cards + if [[ -z "$wifi_device" ]] || ! grep -q -F "$wifi_device" <(unused_iw_devices); then wifi_device="$(unused_iw_devices | head -n 1)" ynh_app_setting_set --app=$app --key=wifi_device --value="${wifi_device}" @@ -215,9 +149,13 @@ chown root: /etc/hostapd/$app/ mkdir -pm 0755 /etc/dnsmasq.$app/ chown root: /etc/dnsmasq.$app/ -if [[ -n "${wifi_device}" ]]; then +if [[ -n "${wifi_device:-}" ]]; then configure_hostapd configure_dhcp + ynh_app_setting_set --app=$app --key=service_enabled --value=1 +else + ynh_app_setting_set --app=$app --key=service_enabled --value=0 + wifi_device="" fi # Copy init script @@ -243,13 +181,6 @@ ynh_add_systemd_config --service=$service_name # Create custom systemd config for hostapd to handle multiple wifi devices ynh_add_systemd_config --service="hostapd@$app" --template="../conf/systemd_hostapd.service" -#================================================= -# GENERIC FINALIZATION -#================================================= -# INTEGRATE SERVICE IN YUNOHOST -#================================================= -ynh_script_progression --message="Integrating service in YunoHost..." - yunohost service add $service_name --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock #================================================= @@ -257,16 +188,9 @@ yunohost service add $service_name --description "Creates a Wi-Fi access point" #================================================= ynh_script_progression --message="Starting the hotspot service..." -if [[ -z $wifi_device ]]; then - ynh_app_setting_set --app=$app --key=service_enabled --value=0 - wifi_device="" -else - ynh_app_setting_set --app=$app --key=service_enabled --value=1 -fi - # Start a systemd service if device is present if [[ $wifi_device == "" ]]; then - echo "WARNING: Wifi Hotspot is not started because no wifi device was found (please, check the web admin)" >&2 + echo "WARNING: Wifi Hotspot is not started because no wifi device was found (check the Hotspot configuration in the webadmin > Applications > Hotspot > the config panel)" >&2 else yunohost service start $service_name fi From 3849c69b41e3f27ecc28ada498e6e81480fc5a3d Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 6 Oct 2023 16:36:36 +0000 Subject: [PATCH 075/111] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d13490b..98af644 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.2.0~ynh1 +**Shipped version:** 2.3.0~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index af0814d..fbfc54b 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.2.0~ynh1 +**Version incluse :** 2.3.0~ynh1 ## Captures d’écran From 7127e3b3a1ad693cb5f1d66be9e936c8d5d640d4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 8 Oct 2023 13:05:20 +0200 Subject: [PATCH 076/111] Fix start/restart of the service during config panel operation, we have to use 'yunohost service' otherwise the lock is not transferred --- scripts/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/config b/scripts/config index ad92a84..a0a5fc8 100644 --- a/scripts/config +++ b/scripts/config @@ -199,7 +199,7 @@ ynh_app_config_apply() { # Stop vpn client ynh_print_info --message="Stopping hotspot in order to edit files" - systemctl stop ${service_name} + yunohost service stop $service_name _ynh_app_config_apply @@ -209,7 +209,7 @@ ynh_app_config_apply() { # Start hotspot ynh_print_info --message="Starting hotspot service if needed" - systemctl start ${service_name} + yunohost service start $service_name else ynh_print_info --message="Cleanup hotspot config files" ynh_secure_remove --file="/etc/hostapd/$app/hostapd.conf" From 638b4674412de6f53baa38d75aaee564fc702baf Mon Sep 17 00:00:00 2001 From: HgO Date: Sat, 14 Oct 2023 10:52:27 +0200 Subject: [PATCH 077/111] allow empty dns field to generate default local dns --- config_panel.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config_panel.toml b/config_panel.toml index 71a9384..b03b3bd 100644 --- a/config_panel.toml +++ b/config_panel.toml @@ -103,6 +103,7 @@ name = "Configuration" [main.hotspot.dns] ask = "DNS resolvers" type = "tags" + optional = true visible = "advanced" pattern.regexp = '^([0-9.]{7,15}|[0-9a-fA-F:]+)$' pattern.error = "Not an ip" From 29d48e1f0a94b5c8c348317d5b24ae0b1773fc88 Mon Sep 17 00:00:00 2001 From: HgO Date: Sat, 14 Oct 2023 10:55:37 +0200 Subject: [PATCH 078/111] bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 369f0f2..0c165f2 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,7 @@ "en": "Create and manager wifi networks, share Internet access and use YunoHost apps accross wifi", "fr": "Créer et configurer des réseaux wifi, partager l'accès a Internet et utiliser les applications YunoHost via wifi" }, - "version": "2.2.0~ynh1", + "version": "2.2.1~ynh1", "url": "https://github.com/labriqueinternet/hotspot_ynh", "license": "AGPL-3.0", "maintainer": { From ba8c5906c1db68a60d060732d64b1c242a3eac7a Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sat, 14 Oct 2023 11:48:37 +0000 Subject: [PATCH 079/111] Auto-update README --- README.md | 2 +- README_fr.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d13490b..8744250 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.2.0~ynh1 +**Shipped version:** 2.2.1~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index af0814d..628c909 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.2.0~ynh1 +**Version incluse :** 2.2.1~ynh1 ## Captures d’écran From fc97720e2cc75e187e1c6c579a193537c3c58154 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Tue, 31 Oct 2023 14:15:23 +0000 Subject: [PATCH 080/111] Auto-update README --- README.md | 1 + README_fr.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 8744250..678fee1 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources * Official app website: +* YunoHost Store: * Report a bug: ## Developer info diff --git a/README_fr.md b/README_fr.md index 628c909..4c4da81 100644 --- a/README_fr.md +++ b/README_fr.md @@ -29,6 +29,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources * Site officiel de l’app : +* YunoHost Store: * Signaler un bug : ## Informations pour les développeurs From b5cd8bd1a332b3afb22b660e6810ae46bd8c44ce Mon Sep 17 00:00:00 2001 From: HgO Date: Sat, 18 Nov 2023 19:18:15 +0100 Subject: [PATCH 081/111] openvpn client reload only nat rules instead of whole hotspot --- conf/openvpn_90-hotspot | 28 +++++++++++++++++++++++++++- conf/ynh-hotspot | 6 +++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index ce09c25..33fe318 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -1,3 +1,29 @@ #!/bin/bash -systemctl restart __SERVICE_NAME__ \ No newline at end of file +source /usr/share/yunohost/helpers + +is_nat_set() { + local gateway_interface=${1} + iptables -w -nvt nat -L POSTROUTING | grep MASQUERADE | grep -q "${gateway_interface}" +} + +unset_nat() { + local gateway_interface=${1} + iptables -w -t nat -D POSTROUTING -o "${gateway_interface}" -j MASQUERADE +} + +set_nat() { + local gateway_interface=${1} + iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE +} + +old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) +new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') + +if [[ -n "$old_gateway_interface" ]] && [[ "$old_gateway_interface" != "$new_gateway_interface" ]] && is_nat_set "$old_gateway_interface"; then + unset_nat "${old_gateway_interface}" +fi + +set_nat "${new_gateway_interface}" + +ynh_app_setting_set --app=$app --key=gateway_interface --value="${new_gateway_interface}" diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index f190a78..e9460df 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -258,7 +258,7 @@ start) exit 1 fi - echo "[hotspot] Starting..." + echo "[$app] Starting..." touch /tmp/.${service_name}-started # Check old state of the ipv4 NAT settings @@ -295,13 +295,13 @@ start) start_dhcpd # Update dynamic settings - ynh_app_setting_set hotspot gateway_interface "${new_gateway_interface}" + ynh_app_setting_set --app=$app --key=gateway_interface --value="${new_gateway_interface}" # Regen-conf dnsmasq to enable dns resolution on dnsmasq for the new interface yunohost tools regen-conf dnsmasq ;; stop) - echo "[hotspot] Stopping..." + echo "[$app] Stopping..." rm -f /tmp/.${service_name}-started if ! is_other_hostapd_running; then From 31bf8493bdd53a42f60e60784cead3029eda20a1 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sat, 18 Nov 2023 18:18:23 +0000 Subject: [PATCH 082/111] Auto-update README --- README.md | 3 ++- README_fr.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d13490b..678fee1 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.2.0~ynh1 +**Shipped version:** 2.2.1~ynh1 ## Screenshots @@ -29,6 +29,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources * Official app website: +* YunoHost Store: * Report a bug: ## Developer info diff --git a/README_fr.md b/README_fr.md index af0814d..4c4da81 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.2.0~ynh1 +**Version incluse :** 2.2.1~ynh1 ## Captures d’écran @@ -29,6 +29,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources * Site officiel de l’app : +* YunoHost Store: * Signaler un bug : ## Informations pour les développeurs From 689f00983cdb90202e6fd12b7fa0079ee5231f45 Mon Sep 17 00:00:00 2001 From: HgO Date: Sat, 18 Nov 2023 21:42:48 +0100 Subject: [PATCH 083/111] check if hotspot is enabled before applying nat rules --- conf/openvpn_90-hotspot | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index 33fe318..2808892 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -17,13 +17,15 @@ set_nat() { iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE } -old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) -new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') +if systemctl is-active __SERVICE_NAME__; then + old_gateway_interface=$(yunohost app setting --app=__APP__ --key=gateway_interface) + new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') -if [[ -n "$old_gateway_interface" ]] && [[ "$old_gateway_interface" != "$new_gateway_interface" ]] && is_nat_set "$old_gateway_interface"; then - unset_nat "${old_gateway_interface}" + if [[ -n "$old_gateway_interface" ]] && [[ "$old_gateway_interface" != "$new_gateway_interface" ]] && is_nat_set "$old_gateway_interface"; then + unset_nat "${old_gateway_interface}" + fi + + set_nat "${new_gateway_interface}" + + ynh_app_setting_set --app=__APP__ --key=gateway_interface --value="${new_gateway_interface}" fi - -set_nat "${new_gateway_interface}" - -ynh_app_setting_set --app=$app --key=gateway_interface --value="${new_gateway_interface}" From 6ef195c8674defc30bc57bd5a87f893b68717b7b Mon Sep 17 00:00:00 2001 From: HgO Date: Sat, 18 Nov 2023 21:45:01 +0100 Subject: [PATCH 084/111] check if nat not already set before applying nat rule --- conf/openvpn_90-hotspot | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index 2808892..f4d5fc5 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -25,7 +25,9 @@ if systemctl is-active __SERVICE_NAME__; then unset_nat "${old_gateway_interface}" fi - set_nat "${new_gateway_interface}" + if [[ -n "$new_gateway_interface" ]] && ! is_nat_set $new_gateway_interface; then + set_nat "${new_gateway_interface}" + fi ynh_app_setting_set --app=__APP__ --key=gateway_interface --value="${new_gateway_interface}" fi From 0642570974957773e0caed392419da29fc14c224 Mon Sep 17 00:00:00 2001 From: HgO Date: Sun, 19 Nov 2023 15:33:13 +0100 Subject: [PATCH 085/111] use yunohost helper instead of cli --- conf/openvpn_90-hotspot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index f4d5fc5..940148d 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -18,7 +18,7 @@ set_nat() { } if systemctl is-active __SERVICE_NAME__; then - old_gateway_interface=$(yunohost app setting --app=__APP__ --key=gateway_interface) + old_gateway_interface=$(ynh_app_setting_get --app=__APP__ --key=gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') if [[ -n "$old_gateway_interface" ]] && [[ "$old_gateway_interface" != "$new_gateway_interface" ]] && is_nat_set "$old_gateway_interface"; then From 9195d3d64d273a41f7425f4f461aa2c838c262ac Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 20 Nov 2023 11:54:47 +0100 Subject: [PATCH 086/111] export PATH variable and remove yunohost helpers --- conf/openvpn_90-hotspot | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index 940148d..547a630 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -1,6 +1,6 @@ #!/bin/bash -source /usr/share/yunohost/helpers +export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" is_nat_set() { local gateway_interface=${1} @@ -17,8 +17,8 @@ set_nat() { iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE } -if systemctl is-active __SERVICE_NAME__; then - old_gateway_interface=$(ynh_app_setting_get --app=__APP__ --key=gateway_interface) +if systemctl -q is-active __SERVICE_NAME__; then + old_gateway_interface=$(yunohost app setting __APP__ gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') if [[ -n "$old_gateway_interface" ]] && [[ "$old_gateway_interface" != "$new_gateway_interface" ]] && is_nat_set "$old_gateway_interface"; then @@ -29,5 +29,5 @@ if systemctl is-active __SERVICE_NAME__; then set_nat "${new_gateway_interface}" fi - ynh_app_setting_set --app=__APP__ --key=gateway_interface --value="${new_gateway_interface}" + yunohost app setting __APP__ gateway_interface --value "${new_gateway_interface}" fi From a4fe4d040633c78fa8f7c8c4776c05851f845062 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 20 Nov 2023 17:22:17 +0100 Subject: [PATCH 087/111] remove export PATH variable as it will be provided by run-parts script --- conf/openvpn_90-hotspot | 2 -- 1 file changed, 2 deletions(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index 547a630..1fbc11c 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -1,7 +1,5 @@ #!/bin/bash -export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" - is_nat_set() { local gateway_interface=${1} iptables -w -nvt nat -L POSTROUTING | grep MASQUERADE | grep -q "${gateway_interface}" From f53168da30c6e23669336a4e7ff42475ee660404 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin <4533074+alexAubin@users.noreply.github.com> Date: Mon, 27 Nov 2023 14:45:26 +0100 Subject: [PATCH 088/111] manifest: implement conditional inclusion of non-free firmwares --- manifest.toml | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/manifest.toml b/manifest.toml index 95d1d72..27f4a7b 100644 --- a/manifest.toml +++ b/manifest.toml @@ -49,25 +49,22 @@ ram.runtime = "50M" [resources.apt] packages = "sipcalc, hostapd, iw, kmod" + packages_from_raw_bash = """ + # Free firmwares + [[ "$firmware_nonfree" -eq 0 ]] && echo "firmware-ath9k-htc" || true + """ -# ========================================= -# FIXME FIXME FIXME FIXME FIXME FIXME FIXME -# Gotta find a way to conditionally install the non-free packages -# So far the "packages_from_raw_bash" thing doesnt allow conditional packages from extra repo (here, the non-free component..) -# FIXME FIXME FIXME FIXME FIXME FIXME FIXME -# ========================================= -# -# # Packaged USB Wireless Device firmwares -# # Based on https://wiki.debian.org/WiFi#USB_Devices -# if [[ $firmware_nonfree -eq 1 ]]; then -# # FIXME : if armbian-firmware is detected, we should remove ra-link.... -# #if dpkg --list | grep -q armbian-firmware; then -# # echo "You are running Armbian and firmware-misc-nonfree are known to conflict with armbian-firwmare. " >&2 -# # echo "The package firmware-misc-nonfree is a dependency of firmware-ralink, so firmware-ralink will NOT be installed" >&2 -# # echo "You can manually install firmware-ralink with 'apt -o Dpkg::Options::=\"--force-overwrite\" firmware-ralink'" >&2 -# # nonfree_firmware_packages=$(echo $nonfree_firmware_packages | sed 's/ firmware-ralink//') -# #fi -# echo "firmware-atheros firmware-realtek firmware-ralink firmware-libertas atmel-firmware firmware-zd1211" -# else -# echo "firmware-ath9k-htc" -# fi + extras.nonfree.repo = "deb http://deb.debian.org/debian bullseye non-free" + extras.nonfree.key = "https://ftp-master.debian.org/keys/archive-key-11.asc" + extras.nonfree.packages_from_raw_bash = """ + # Proprietary USB Wireless Device firmwares, based on https://wiki.debian.org/WiFi#USB_Devices + if [[ "$firmware_nonfree" -eq 1 ]]; then + # if armbian-firmware is detected, we dont include ra-link which is known to conflict.... + if dpkg --list | grep -q armbian-firmware; then + echo "firmware-atheros firmware-realtek firmware-libertas atmel-firmware firmware-zd1211" + fi + echo "firmware-atheros firmware-realtek firmware-ralink firmware-libertas atmel-firmware firmware-zd1211" + else + echo "" + fi + """ From d528bdcf6b3f6124e674bf94560cc3ca5b4414e2 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Mon, 27 Nov 2023 13:45:35 +0000 Subject: [PATCH 089/111] Auto-update README --- README.md | 1 + README_fr.md | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 98af644..6df4e15 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources * Official app website: +* YunoHost Store: * Report a bug: ## Developer info diff --git a/README_fr.md b/README_fr.md index fbfc54b..2830785 100644 --- a/README_fr.md +++ b/README_fr.md @@ -29,6 +29,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources * Site officiel de l’app : +* YunoHost Store: * Signaler un bug : ## Informations pour les développeurs From 24067faa53c53e6f4aca8d7531d890f6423bcd08 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Nov 2023 22:52:24 +0100 Subject: [PATCH 090/111] Fix stupid toml multiline string parsing issue... --- manifest.toml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/manifest.toml b/manifest.toml index 27f4a7b..db31176 100644 --- a/manifest.toml +++ b/manifest.toml @@ -49,10 +49,7 @@ ram.runtime = "50M" [resources.apt] packages = "sipcalc, hostapd, iw, kmod" - packages_from_raw_bash = """ - # Free firmwares - [[ "$firmware_nonfree" -eq 0 ]] && echo "firmware-ath9k-htc" || true - """ + packages_from_raw_bash = '[[ "$firmware_nonfree" -eq 0 ]] && echo "firmware-ath9k-htc" || true' extras.nonfree.repo = "deb http://deb.debian.org/debian bullseye non-free" extras.nonfree.key = "https://ftp-master.debian.org/keys/archive-key-11.asc" @@ -62,9 +59,9 @@ ram.runtime = "50M" # if armbian-firmware is detected, we dont include ra-link which is known to conflict.... if dpkg --list | grep -q armbian-firmware; then echo "firmware-atheros firmware-realtek firmware-libertas atmel-firmware firmware-zd1211" + else + echo "firmware-atheros firmware-realtek firmware-ralink firmware-libertas atmel-firmware firmware-zd1211" fi - echo "firmware-atheros firmware-realtek firmware-ralink firmware-libertas atmel-firmware firmware-zd1211" else - echo "" - fi - """ + echo " " + fi""" From 93f99733f520133012b43b558c6e35be0fffbde2 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Nov 2023 23:01:30 +0100 Subject: [PATCH 091/111] =?UTF-8?q?Fix=20stupid=20toml=20multiline=20strin?= =?UTF-8?q?g=20parsing=20issue...=20=C2=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manifest.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/manifest.toml b/manifest.toml index db31176..ddbac22 100644 --- a/manifest.toml +++ b/manifest.toml @@ -51,9 +51,10 @@ ram.runtime = "50M" packages = "sipcalc, hostapd, iw, kmod" packages_from_raw_bash = '[[ "$firmware_nonfree" -eq 0 ]] && echo "firmware-ath9k-htc" || true' - extras.nonfree.repo = "deb http://deb.debian.org/debian bullseye non-free" - extras.nonfree.key = "https://ftp-master.debian.org/keys/archive-key-11.asc" - extras.nonfree.packages_from_raw_bash = """ + [resources.apt.extras.nonfree] + repo = "deb http://deb.debian.org/debian bullseye non-free" + key = "https://ftp-master.debian.org/keys/archive-key-11.asc" + packages_from_raw_bash = """ # Proprietary USB Wireless Device firmwares, based on https://wiki.debian.org/WiFi#USB_Devices if [[ "$firmware_nonfree" -eq 1 ]]; then # if armbian-firmware is detected, we dont include ra-link which is known to conflict.... From 366beb69b3b2c6ee4c213743a19aec710e560b20 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Nov 2023 23:10:12 +0100 Subject: [PATCH 092/111] wifi_passphrase must be saved explicitly --- scripts/install | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/install b/scripts/install index b3335cf..9ee1bab 100644 --- a/scripts/install +++ b/scripts/install @@ -90,6 +90,10 @@ else ynh_app_setting_set --app=$app --key=service_enabled --value=1 fi +# We must explicitly save the wifi passphrase despite being in the install question +# because password-type questions are not saved automatically +ynh_app_setting_set --app=$app --key=wifi_passphrase --value="$wifi_passphrase" + #================================================= # COPY CONFIGS #================================================= From 750f3ac66a18868512c58fece1d261fe418722da Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Mon, 27 Nov 2023 22:17:15 +0000 Subject: [PATCH 093/111] Auto-update README --- README_fr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_fr.md b/README_fr.md index 3354c9a..2830785 100644 --- a/README_fr.md +++ b/README_fr.md @@ -44,4 +44,4 @@ ou sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug ``` -**Plus d’infos sur le packaging d’applications :** +**Plus d’infos sur le packaging d’applications :** \ No newline at end of file From 60f3bced6535c8d88c220118a80e333485037e8e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Nov 2023 23:33:44 +0100 Subject: [PATCH 094/111] Replace check_process with tests.toml --- check_process | 20 -------------------- tests.toml | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 20 deletions(-) delete mode 100644 check_process create mode 100644 tests.toml diff --git a/check_process b/check_process deleted file mode 100644 index 950622f..0000000 --- a/check_process +++ /dev/null @@ -1,20 +0,0 @@ -;; Test complet - ; Manifest - wifi_ssid="myNeutralNetwork" - wifi_passphrase="VhegT8oev0jZI" - firmware_nonfree="no" - ; Checks - pkg_linter=1 - setup_sub_dir=0 - setup_root=0 - setup_nourl=1 - setup_private=0 - setup_public=0 - upgrade=1 - upgrade=1 from_commit=539a1f26c30ba850455c63746d50ce3d8f33b119 - backup_restore=1 - multi_instance=1 - change_url=0 -;;; Upgrade options - ; commit=539a1f26c30ba850455c63746d50ce3d8f33b119 - name=Pre-2.0 diff --git a/tests.toml b/tests.toml new file mode 100644 index 0000000..419bdc4 --- /dev/null +++ b/tests.toml @@ -0,0 +1,14 @@ +test_format = 1.0 + +[default] + + args.wifi_passphrase = "!abcDEF01234" + args.firmware_nonfree = "no" + + test_upgrade_from.539a1f26.name = "Pre 2.0" + +[with_nonfree] + + args.wifi_passphrase = "!abcDEF01234" + args.firmware_nonfree = "yes" + only = ["install.nourl"] From 7b4254baa0a0f9ec3630beebb2ca4d3f418f20d6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 27 Nov 2023 23:47:57 +0100 Subject: [PATCH 095/111] For some reason, gotta specify a default domain arg when testing upgrade from old version --- tests.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests.toml b/tests.toml index 419bdc4..98907d8 100644 --- a/tests.toml +++ b/tests.toml @@ -6,6 +6,7 @@ test_format = 1.0 args.firmware_nonfree = "no" test_upgrade_from.539a1f26.name = "Pre 2.0" + test_upgrade_from.539a1f26.args.domain = "domain.tld" [with_nonfree] From 5f7551ed1c727ae98ecac23e8dafe52758ea4c64 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 28 Nov 2023 00:02:23 +0100 Subject: [PATCH 096/111] Moar args to expicitly define for tests --- tests.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests.toml b/tests.toml index 98907d8..f8716c5 100644 --- a/tests.toml +++ b/tests.toml @@ -7,6 +7,7 @@ test_format = 1.0 test_upgrade_from.539a1f26.name = "Pre 2.0" test_upgrade_from.539a1f26.args.domain = "domain.tld" + test_upgrade_from.539a1f26.args.wifi_passphrase = "!abcDEF01234" [with_nonfree] From 53d1708056dc114b74c915c026a9f0e931274692 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin <4533074+alexAubin@users.noreply.github.com> Date: Tue, 28 Nov 2023 03:04:14 +0100 Subject: [PATCH 097/111] Update manifest.toml : bump yunohos requirement to 11.2.7 --- manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.toml b/manifest.toml index ddbac22..a54c1b6 100644 --- a/manifest.toml +++ b/manifest.toml @@ -16,7 +16,7 @@ license = "AGPL-3.0" website = "https://internetcu.be/" [integration] -yunohost = ">= 11.2" +yunohost = ">= 11.2.7" architectures = "all" multi_instance = true ldap = "not_relevant" From 80e29842d97b9e5f1b5bff664720c4f5159323f7 Mon Sep 17 00:00:00 2001 From: HgO Date: Mon, 18 Dec 2023 14:33:53 +0100 Subject: [PATCH 098/111] check if ynh-hotspot is activating --- conf/openvpn_90-hotspot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index 1fbc11c..bb1f580 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -15,7 +15,8 @@ set_nat() { iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE } -if systemctl -q is-active __SERVICE_NAME__; then +ynh_hotspot_state=$(systemctl -q is-active __SERVICE_NAME__) +if [[ "${ynh_hotspot_state}" == "active" || "${ynh_hotspot_state}" == "activating" ]]; then old_gateway_interface=$(yunohost app setting __APP__ gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') From d16e8f5503f2525a54165d5edf7408e20ca30fba Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 19 Dec 2023 10:42:31 +0100 Subject: [PATCH 099/111] obviously it won't work in quiet mode... --- conf/openvpn_90-hotspot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/openvpn_90-hotspot b/conf/openvpn_90-hotspot index bb1f580..1fc4dd0 100644 --- a/conf/openvpn_90-hotspot +++ b/conf/openvpn_90-hotspot @@ -15,7 +15,7 @@ set_nat() { iptables -w -t nat -A POSTROUTING -o "${gateway_interface}" -j MASQUERADE } -ynh_hotspot_state=$(systemctl -q is-active __SERVICE_NAME__) +ynh_hotspot_state=$(systemctl is-active __SERVICE_NAME__) if [[ "${ynh_hotspot_state}" == "active" || "${ynh_hotspot_state}" == "activating" ]]; then old_gateway_interface=$(yunohost app setting __APP__ gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') From 2d38e95ea3a05b5a0d5b3d692f43132c72626bc6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 31 Dec 2023 17:32:37 +0100 Subject: [PATCH 100/111] Disable the classic hostapd service because we're using hostapd@$app instead, otherwise it'll flood logs x_x --- scripts/install | 6 ++---- scripts/restore | 8 ++++++++ scripts/upgrade | 8 ++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/scripts/install b/scripts/install index 9ee1bab..451d9ad 100644 --- a/scripts/install +++ b/scripts/install @@ -123,12 +123,10 @@ chmod 0755 "/etc/openvpn/scripts/route-down.d/90-${service_name}" #================================================= ynh_script_progression --message="Configuring hostapd..." -# Set default inits -# The boot order of these services are important, so they are disabled by default -# and the ynh-hotspot service handles them. +# Disable hostapd, we'll use hostapd@$app instead (for multissid support etc) systemctl disable hostapd --quiet 2>&1 systemctl stop hostapd 2>&1 -systemctl unmask hostapd 2>&1 # On some system e.g. RPi, for some reason hostapd is masked after install ... +systemctl mask hostapd 2>&1 if [[ -n "${wifi_device}" ]]; then configure_hostapd diff --git a/scripts/restore b/scripts/restore index 4d93dce..9cd18cb 100644 --- a/scripts/restore +++ b/scripts/restore @@ -10,6 +10,14 @@ source ../settings/scripts/_common.sh source /usr/share/yunohost/helpers +if systemctl -q is-enabled hostapd +then + # Disable hostapd, we'll use hostapd@$app instead (for multissid support etc) + systemctl disable hostapd --quiet 2>&1 + systemctl stop hostapd 2>&1 + systemctl mask hostapd 2>&1 +fi + #================================================= # FIND AND OPEN A PORT #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 7b14ae9..fb92cc1 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -129,6 +129,14 @@ if [ -e "/etc/sudoers.d/${app}_ynh" ]; then ynh_secure_remove "/etc/sudoers.d/${app}_ynh" fi +if systemctl -q is-enabled hostapd +then + # Disable hostapd, we'll use hostapd@$app instead (for multissid support etc) + systemctl disable hostapd --quiet 2>&1 + systemctl stop hostapd 2>&1 + systemctl mask hostapd 2>&1 +fi + #================================================= # SPECIFIC UPGRADE #================================================= From ecf91a8ecd049cb36fa0a0676934e7ec7d3f9ba6 Mon Sep 17 00:00:00 2001 From: HgO Date: Tue, 2 Jan 2024 10:59:14 +0100 Subject: [PATCH 101/111] restore files with magic --- scripts/restore | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/scripts/restore b/scripts/restore index 9cd18cb..6a60840 100644 --- a/scripts/restore +++ b/scripts/restore @@ -40,17 +40,7 @@ fi ynh_script_progression --message="Restoring configurations ..." -ynh_restore_file --origin_path="/etc/hostapd/$app/hostapd.conf" --not_mandatory -ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv6.conf" --not_mandatory -ynh_restore_file --origin_path="/etc/dnsmasq.$app/dhcpdv4.conf" --not_mandatory - -ynh_restore_file --origin_path="/usr/local/bin/$service_name" - -ynh_restore_file --origin_path="/etc/openvpn/scripts/route-up.d/90-${service_name}" -ynh_restore_file --origin_path="/etc/openvpn/scripts/route-down.d/90-${service_name}" - -ynh_restore_file --origin_path="/etc/systemd/system/$service_name.service" -ynh_restore_file --origin_path="/etc/systemd/system/hostapd@$app.service" +ynh_restore yunohost service add "$service_name" --description "Creates a Wi-Fi access point" --test_status "systemctl is-active hostapd@$app" --need_lock From e725d58f7bfdc815c0a3f37cb286f64b32fdc05a Mon Sep 17 00:00:00 2001 From: Lab-8916100448256 <38373466+Lab-8916100448256@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:14:50 +0100 Subject: [PATCH 102/111] Fix issue of gateway interface detection in the case of VPN usage --- conf/ynh-hotspot | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index e9460df..928b54b 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -236,8 +236,7 @@ if [ "$1" != restart ]; then ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) - new_gateway_interface=$(ip route get 1.2.3.4 | awk '{ print $5; }') - + new_gateway_interface=$(ip route get 1.2.3.4 | awk '$2 ~ /^dev$/ { print $3; } $4 ~ /^dev$/ { print $5; }') echo "OK" fi From f21df258cc0d94ee3f9886b2a44d97784e39db9a Mon Sep 17 00:00:00 2001 From: Lab-8916100448256 <38373466+Lab-8916100448256@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:34:34 +0100 Subject: [PATCH 103/111] put back the empty line I accidently removed --- conf/ynh-hotspot | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 928b54b..37e5783 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -237,6 +237,7 @@ if [ "$1" != restart ]; then old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) new_gateway_interface=$(ip route get 1.2.3.4 | awk '$2 ~ /^dev$/ { print $3; } $4 ~ /^dev$/ { print $5; }') + echo "OK" fi From f93ffd83c377d4df532e59f402a0f233c2b1f350 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin <4533074+alexAubin@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:12:41 +0100 Subject: [PATCH 104/111] Add comment to explain the origin of the awk syntax --- conf/ynh-hotspot | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/ynh-hotspot b/conf/ynh-hotspot index 37e5783..d55456f 100644 --- a/conf/ynh-hotspot +++ b/conf/ynh-hotspot @@ -236,6 +236,10 @@ if [ "$1" != restart ]; then ip4_nat_prefix=$(ynh_app_setting_get --app=$app --key=ip4_nat_prefix) old_gateway_interface=$(ynh_app_setting_get --app=$app --key=gateway_interface) + + # The awk syntax is to accomodate to the fact that the ip route output may look like: + # 1.2.3.4 via 192.168.1.254 dev end0 src 192.168.1.35 uid 0 + # 1.2.3.4 dev vpn_iloth table 51820 src 5.6.7.8 uid 0 new_gateway_interface=$(ip route get 1.2.3.4 | awk '$2 ~ /^dev$/ { print $3; } $4 ~ /^dev$/ { print $5; }') echo "OK" From 81a315df665248e6a47b62d998cb59d54f2074a4 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Tue, 19 Mar 2024 17:12:53 +0000 Subject: [PATCH 105/111] Auto-update README --- README.md | 11 +++++------ README_fr.md | 13 ++++++------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6df4e15..999e435 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ @@ -19,7 +19,6 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in * Broadcast a Wi-Fi access point from your self-hosted server * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi - **Shipped version:** 2.3.0~ynh1 ## Screenshots @@ -28,9 +27,9 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources -* Official app website: -* YunoHost Store: -* Report a bug: +- Official app website: +- YunoHost Store: +- Report a bug: ## Developer info @@ -38,7 +37,7 @@ Please send your pull request to the [testing branch](https://github.com/YunoHos To try the testing branch, please proceed like that. -``` bash +```bash sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug or sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug diff --git a/README_fr.md b/README_fr.md index 2830785..9769056 100644 --- a/README_fr.md +++ b/README_fr.md @@ -1,5 +1,5 @@ @@ -19,7 +19,6 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * Diffusez un point d'accès Wi-Fi depuis votre serveur auto-hébergé * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN - **Version incluse :** 2.3.0~ynh1 ## Captures d’écran @@ -28,9 +27,9 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources -* Site officiel de l’app : -* YunoHost Store: -* Signaler un bug : +- Site officiel de l’app : +- YunoHost Store : +- Signaler un bug : ## Informations pour les développeurs @@ -38,10 +37,10 @@ Merci de faire vos pull request sur la [branche testing](https://github.com/Yuno Pour essayer la branche testing, procédez comme suit. -``` bash +```bash sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug ou sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug ``` -**Plus d’infos sur le packaging d’applications :** \ No newline at end of file +**Plus d’infos sur le packaging d’applications :** From f860c3061f7c6162d2b56b58eb1c7a2e54ca65c6 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 29 Mar 2024 07:06:56 +0100 Subject: [PATCH 106/111] Auto-update README --- ALL_README.md | 6 ++++++ README.md | 20 ++++++++++---------- README_fr.md | 26 +++++++++++++------------- README_gl.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README_it.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 ALL_README.md create mode 100644 README_gl.md create mode 100644 README_it.md diff --git a/ALL_README.md b/ALL_README.md new file mode 100644 index 0000000..3d6c579 --- /dev/null +++ b/ALL_README.md @@ -0,0 +1,6 @@ +# All available README files by language + +- [Read the README in English](README.md) +- [Lire le README en français](README_fr.md) +- [Le o README en galego](README_gl.md) +- [Leggi il “README” in italiano](README_it.md) diff --git a/README.md b/README.md index 6df4e15..b9392d2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ @@ -9,10 +9,10 @@ It shall NOT be edited by hand. [![Install Wifi Hotspot with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) -*[Lire ce readme en français.](./README_fr.md)* +*[Read this README is other languages.](./ALL_README.md)* -> *This package allows you to install Wifi Hotspot quickly and simply on a YunoHost server. -If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/install) to learn how to install it.* +> *This package allows you to install Wifi Hotspot quickly and simply on a YunoHost server.* +> *If you don't have YunoHost, please consult [the guide](https://yunohost.org/install) to learn how to install it.* ## Overview @@ -28,17 +28,17 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Documentation and resources -* Official app website: -* YunoHost Store: -* Report a bug: +- Official app website: +- YunoHost Store: +- Report a bug: ## Developer info -Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). +Please send your pull request to the [`testing` branch](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). -To try the testing branch, please proceed like that. +To try the `testing` branch, please proceed like that: -``` bash +```bash sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug or sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug diff --git a/README_fr.md b/README_fr.md index 2830785..099c93e 100644 --- a/README_fr.md +++ b/README_fr.md @@ -1,6 +1,6 @@ # Wifi Hotspot pour YunoHost @@ -9,10 +9,10 @@ It shall NOT be edited by hand. [![Installer Wifi Hotspot avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) -*[Read this readme in english.](./README.md)* +*[Lire le README dans d'autres langues.](./ALL_README.md)* -> *Ce package vous permet d’installer Wifi Hotspot rapidement et simplement sur un serveur YunoHost. -Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour savoir comment l’installer et en profiter.* +> *Ce package vous permet d’installer Wifi Hotspot rapidement et simplement sur un serveur YunoHost.* +> *Si vous n’avez pas YunoHost, consultez [ce guide](https://yunohost.org/install) pour savoir comment l’installer et en profiter.* ## Vue d’ensemble @@ -20,7 +20,7 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.3.0~ynh1 +**Version incluse :** 2.3.0~ynh1 ## Captures d’écran @@ -28,20 +28,20 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources -* Site officiel de l’app : -* YunoHost Store: -* Signaler un bug : +- Site officiel de l’app : +- YunoHost Store : +- Signaler un bug : ## Informations pour les développeurs -Merci de faire vos pull request sur la [branche testing](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). +Merci de faire vos pull request sur la [branche `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). -Pour essayer la branche testing, procédez comme suit. +Pour essayer la branche `testing`, procédez comme suit : -``` bash +```bash sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug ou sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug ``` -**Plus d’infos sur le packaging d’applications :** \ No newline at end of file +**Plus d’infos sur le packaging d’applications :** diff --git a/README_gl.md b/README_gl.md new file mode 100644 index 0000000..7fb47d0 --- /dev/null +++ b/README_gl.md @@ -0,0 +1,47 @@ + + +# Wifi Hotspot para YunoHost + +[![Nivel de integración](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![Estado de funcionamento](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![Estado de mantemento](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![Instalar Wifi Hotspot con YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[Le este README en outros idiomas.](./ALL_README.md)* + +> *Este paquete permíteche instalar Wifi Hotspot de xeito rápido e doado nun servidor YunoHost.* +> *Se non usas YunoHost, le a [documentación](https://yunohost.org/install) para saber como instalalo.* + +## Vista xeral + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**Versión proporcionada:** 2.3.0~ynh1 + +## Capturas de pantalla + +![Captura de pantalla de Wifi Hotspot](./doc/screenshots/hotspot.png) + +## Documentación e recursos + +- Web oficial da app: +- Tenda YunoHost: +- Informar dun problema: + +## Info de desenvolvemento + +Envía a túa colaboración á [rama `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). + +Para probar a rama `testing`, procede deste xeito: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +ou +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**Máis info sobre o empaquetado da app:** diff --git a/README_it.md b/README_it.md new file mode 100644 index 0000000..3f8deb4 --- /dev/null +++ b/README_it.md @@ -0,0 +1,47 @@ + + +# Wifi Hotspot per YunoHost + +[![Livello di integrazione](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![Stato di funzionamento](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![Stato di manutenzione](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![Installa Wifi Hotspot con YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[Leggi questo README in altre lingue.](./ALL_README.md)* + +> *Questo pacchetto ti permette di installare Wifi Hotspot su un server YunoHost in modo semplice e veloce.* +> *Se non hai YunoHost, consulta [la guida](https://yunohost.org/install) per imparare a installarlo.* + +## Panoramica + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**Versione pubblicata:** 2.3.0~ynh1 + +## Screenshot + +![Screenshot di Wifi Hotspot](./doc/screenshots/hotspot.png) + +## Documentazione e risorse + +- Sito web ufficiale dell’app: +- Store di YunoHost: +- Segnala un problema: + +## Informazioni per sviluppatori + +Si prega di inviare la tua pull request alla [branch di `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). + +Per provare la branch di `testing`, si prega di procedere in questo modo: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +o +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**Maggiori informazioni riguardo il pacchetto di quest’app:** From 026ed2c0422e815ec2694fe99519983f12ead1cd Mon Sep 17 00:00:00 2001 From: Alexandre Aubin <4533074+alexAubin@users.noreply.github.com> Date: Fri, 29 Mar 2024 22:13:55 +0100 Subject: [PATCH 107/111] Update manifest.toml: we do need a "permission" block for hotspot otherwise there's no permission at all and the label can't be stored? --- manifest.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/manifest.toml b/manifest.toml index a54c1b6..066af81 100644 --- a/manifest.toml +++ b/manifest.toml @@ -47,6 +47,8 @@ ram.runtime = "50M" [resources] [resources.system_user] + [resources.permissions] + [resources.apt] packages = "sipcalc, hostapd, iw, kmod" packages_from_raw_bash = '[[ "$firmware_nonfree" -eq 0 ]] && echo "firmware-ath9k-htc" || true' From f5c02db268b47acbe7c68c2edd9b87ffc9cb0245 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 29 Mar 2024 21:13:59 +0000 Subject: [PATCH 108/111] Auto-update READMEs --- ALL_README.md | 6 ++++++ README.md | 13 +++++++------ README_fr.md | 25 +++++++++++++------------ README_gl.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README_it.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 ALL_README.md create mode 100644 README_gl.md create mode 100644 README_it.md diff --git a/ALL_README.md b/ALL_README.md new file mode 100644 index 0000000..3d6c579 --- /dev/null +++ b/ALL_README.md @@ -0,0 +1,6 @@ +# All available README files by language + +- [Read the README in English](README.md) +- [Lire le README en français](README_fr.md) +- [Le o README en galego](README_gl.md) +- [Leggi il “README” in italiano](README_it.md) diff --git a/README.md b/README.md index 999e435..b9392d2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ @@ -9,16 +9,17 @@ It shall NOT be edited by hand. [![Install Wifi Hotspot with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) -*[Lire ce readme en français.](./README_fr.md)* +*[Read this README is other languages.](./ALL_README.md)* -> *This package allows you to install Wifi Hotspot quickly and simply on a YunoHost server. -If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/install) to learn how to install it.* +> *This package allows you to install Wifi Hotspot quickly and simply on a YunoHost server.* +> *If you don't have YunoHost, please consult [the guide](https://yunohost.org/install) to learn how to install it.* ## Overview * Broadcast a Wi-Fi access point from your self-hosted server * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + **Shipped version:** 2.3.0~ynh1 ## Screenshots @@ -33,9 +34,9 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in ## Developer info -Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). +Please send your pull request to the [`testing` branch](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). -To try the testing branch, please proceed like that. +To try the `testing` branch, please proceed like that: ```bash sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug diff --git a/README_fr.md b/README_fr.md index 9769056..099c93e 100644 --- a/README_fr.md +++ b/README_fr.md @@ -1,6 +1,6 @@ # Wifi Hotspot pour YunoHost @@ -9,17 +9,18 @@ It shall NOT be edited by hand. [![Installer Wifi Hotspot avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) -*[Read this readme in english.](./README.md)* +*[Lire le README dans d'autres langues.](./ALL_README.md)* -> *Ce package vous permet d’installer Wifi Hotspot rapidement et simplement sur un serveur YunoHost. -Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour savoir comment l’installer et en profiter.* +> *Ce package vous permet d’installer Wifi Hotspot rapidement et simplement sur un serveur YunoHost.* +> *Si vous n’avez pas YunoHost, consultez [ce guide](https://yunohost.org/install) pour savoir comment l’installer et en profiter.* ## Vue d’ensemble * Diffusez un point d'accès Wi-Fi depuis votre serveur auto-hébergé * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.3.0~ynh1 + +**Version incluse :** 2.3.0~ynh1 ## Captures d’écran @@ -27,15 +28,15 @@ Si vous n’avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) po ## Documentations et ressources -- Site officiel de l’app : -- YunoHost Store : -- Signaler un bug : +- Site officiel de l’app : +- YunoHost Store : +- Signaler un bug : ## Informations pour les développeurs -Merci de faire vos pull request sur la [branche testing](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). +Merci de faire vos pull request sur la [branche `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). -Pour essayer la branche testing, procédez comme suit. +Pour essayer la branche `testing`, procédez comme suit : ```bash sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug @@ -43,4 +44,4 @@ ou sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug ``` -**Plus d’infos sur le packaging d’applications :** +**Plus d’infos sur le packaging d’applications :** diff --git a/README_gl.md b/README_gl.md new file mode 100644 index 0000000..7fb47d0 --- /dev/null +++ b/README_gl.md @@ -0,0 +1,47 @@ + + +# Wifi Hotspot para YunoHost + +[![Nivel de integración](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![Estado de funcionamento](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![Estado de mantemento](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![Instalar Wifi Hotspot con YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[Le este README en outros idiomas.](./ALL_README.md)* + +> *Este paquete permíteche instalar Wifi Hotspot de xeito rápido e doado nun servidor YunoHost.* +> *Se non usas YunoHost, le a [documentación](https://yunohost.org/install) para saber como instalalo.* + +## Vista xeral + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**Versión proporcionada:** 2.3.0~ynh1 + +## Capturas de pantalla + +![Captura de pantalla de Wifi Hotspot](./doc/screenshots/hotspot.png) + +## Documentación e recursos + +- Web oficial da app: +- Tenda YunoHost: +- Informar dun problema: + +## Info de desenvolvemento + +Envía a túa colaboración á [rama `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). + +Para probar a rama `testing`, procede deste xeito: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +ou +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**Máis info sobre o empaquetado da app:** diff --git a/README_it.md b/README_it.md new file mode 100644 index 0000000..3f8deb4 --- /dev/null +++ b/README_it.md @@ -0,0 +1,47 @@ + + +# Wifi Hotspot per YunoHost + +[![Livello di integrazione](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![Stato di funzionamento](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![Stato di manutenzione](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![Installa Wifi Hotspot con YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[Leggi questo README in altre lingue.](./ALL_README.md)* + +> *Questo pacchetto ti permette di installare Wifi Hotspot su un server YunoHost in modo semplice e veloce.* +> *Se non hai YunoHost, consulta [la guida](https://yunohost.org/install) per imparare a installarlo.* + +## Panoramica + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**Versione pubblicata:** 2.3.0~ynh1 + +## Screenshot + +![Screenshot di Wifi Hotspot](./doc/screenshots/hotspot.png) + +## Documentazione e risorse + +- Sito web ufficiale dell’app: +- Store di YunoHost: +- Segnala un problema: + +## Informazioni per sviluppatori + +Si prega di inviare la tua pull request alla [branch di `testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). + +Per provare la branch di `testing`, si prega di procedere in questo modo: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +o +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**Maggiori informazioni riguardo il pacchetto di quest’app:** From 156587f20cf999d1518a638cce4db2ac74431821 Mon Sep 17 00:00:00 2001 From: HgO Date: Fri, 29 Mar 2024 22:30:11 +0100 Subject: [PATCH 109/111] bump version --- manifest.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.toml b/manifest.toml index 066af81..678f54f 100644 --- a/manifest.toml +++ b/manifest.toml @@ -7,7 +7,7 @@ name = "Wifi Hotspot" description.en = "Create and configure a WiFi hotspot" description.fr = "Créez et gérez un point d'accès WiFi" -version = "2.3.0~ynh1" +version = "2.3.1~ynh1" maintainers = [] From 9b1dba03ea596a4f47d4078a9661d98193d03f2c Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Fri, 29 Mar 2024 21:30:21 +0000 Subject: [PATCH 110/111] Auto-update READMEs --- README.md | 2 +- README_fr.md | 2 +- README_gl.md | 2 +- README_it.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b9392d2..c91d410 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ It shall NOT be edited by hand. * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Shipped version:** 2.3.0~ynh1 +**Shipped version:** 2.3.1~ynh1 ## Screenshots diff --git a/README_fr.md b/README_fr.md index 099c93e..b7b7eae 100644 --- a/README_fr.md +++ b/README_fr.md @@ -20,7 +20,7 @@ Il NE doit PAS être modifié à la main. * À combiner avec l'[app VPN Client](https://github.com/labriqueinternet/vpnclient_ynh) pour obtenir un accès internet aumatiquement protégé par votre VPN -**Version incluse :** 2.3.0~ynh1 +**Version incluse :** 2.3.1~ynh1 ## Captures d’écran diff --git a/README_gl.md b/README_gl.md index 7fb47d0..8689b94 100644 --- a/README_gl.md +++ b/README_gl.md @@ -20,7 +20,7 @@ NON debe editarse manualmente. * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Versión proporcionada:** 2.3.0~ynh1 +**Versión proporcionada:** 2.3.1~ynh1 ## Capturas de pantalla diff --git a/README_it.md b/README_it.md index 3f8deb4..9c4dee7 100644 --- a/README_it.md +++ b/README_it.md @@ -20,7 +20,7 @@ NON DEVE essere modificato manualmente. * Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi -**Versione pubblicata:** 2.3.0~ynh1 +**Versione pubblicata:** 2.3.1~ynh1 ## Screenshot From 023e82ef7fc2f7e2cdf502209d0e88b2a2054b27 Mon Sep 17 00:00:00 2001 From: yunohost-bot Date: Sat, 22 Jun 2024 22:42:54 +0000 Subject: [PATCH 111/111] Auto-update READMEs --- ALL_README.md | 4 +++- README.md | 2 +- README_es.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README_eu.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ README_zh_Hans.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 README_es.md create mode 100644 README_eu.md create mode 100644 README_zh_Hans.md diff --git a/ALL_README.md b/ALL_README.md index 3d6c579..152f2e7 100644 --- a/ALL_README.md +++ b/ALL_README.md @@ -1,6 +1,8 @@ # All available README files by language - [Read the README in English](README.md) +- [Lea el README en español](README_es.md) +- [Irakurri README euskaraz](README_eu.md) - [Lire le README en français](README_fr.md) - [Le o README en galego](README_gl.md) -- [Leggi il “README” in italiano](README_it.md) +- [阅读中文(简体)的 README](README_zh_Hans.md) diff --git a/README.md b/README.md index c91d410..9b1e364 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ It shall NOT be edited by hand. [![Install Wifi Hotspot with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) -*[Read this README is other languages.](./ALL_README.md)* +*[Read this README in other languages.](./ALL_README.md)* > *This package allows you to install Wifi Hotspot quickly and simply on a YunoHost server.* > *If you don't have YunoHost, please consult [the guide](https://yunohost.org/install) to learn how to install it.* diff --git a/README_es.md b/README_es.md new file mode 100644 index 0000000..f4260a6 --- /dev/null +++ b/README_es.md @@ -0,0 +1,47 @@ + + +# Wifi Hotspot para Yunohost + +[![Nivel de integración](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![Estado funcional](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![Estado En Mantención](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![Instalar Wifi Hotspot con Yunhost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[Leer este README en otros idiomas.](./ALL_README.md)* + +> *Este paquete le permite instalarWifi Hotspot rapidamente y simplement en un servidor YunoHost.* +> *Si no tiene YunoHost, visita [the guide](https://yunohost.org/install) para aprender como instalarla.* + +## Descripción general + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**Versión actual:** 2.3.1~ynh1 + +## Capturas + +![Captura de Wifi Hotspot](./doc/screenshots/hotspot.png) + +## Documentaciones y recursos + +- Sitio web oficial: +- Catálogo YunoHost: +- Reportar un error: + +## Información para desarrolladores + +Por favor enviar sus correcciones a la [`branch testing`](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing + +Para probar la rama `testing`, sigue asÍ: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +o +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**Mas informaciones sobre el empaquetado de aplicaciones:** diff --git a/README_eu.md b/README_eu.md new file mode 100644 index 0000000..107765e --- /dev/null +++ b/README_eu.md @@ -0,0 +1,47 @@ + + +# Wifi Hotspot YunoHost-erako + +[![Integrazio maila](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![Funtzionamendu egoera](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![Mantentze egoera](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![Instalatu Wifi Hotspot YunoHost-ekin](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[Irakurri README hau beste hizkuntzatan.](./ALL_README.md)* + +> *Pakete honek Wifi Hotspot YunoHost zerbitzari batean azkar eta zailtasunik gabe instalatzea ahalbidetzen dizu.* +> *YunoHost ez baduzu, kontsultatu [gida](https://yunohost.org/install) nola instalatu ikasteko.* + +## Aurreikuspena + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**Paketatutako bertsioa:** 2.3.1~ynh1 + +## Pantaila-argazkiak + +![Wifi Hotspot(r)en pantaila-argazkia](./doc/screenshots/hotspot.png) + +## Dokumentazioa eta baliabideak + +- Aplikazioaren webgune ofiziala: +- YunoHost Denda: +- Eman errore baten berri: + +## Garatzaileentzako informazioa + +Bidali `pull request`a [`testing` abarrera](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing). + +`testing` abarra probatzeko, ondorengoa egin: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +edo +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**Informazio gehiago aplikazioaren paketatzeari buruz:** diff --git a/README_zh_Hans.md b/README_zh_Hans.md new file mode 100644 index 0000000..2c93b7d --- /dev/null +++ b/README_zh_Hans.md @@ -0,0 +1,47 @@ + + +# YunoHost 上的 Wifi Hotspot + +[![集成程度](https://dash.yunohost.org/integration/hotspot.svg)](https://dash.yunohost.org/appci/app/hotspot) ![工作状态](https://ci-apps.yunohost.org/ci/badges/hotspot.status.svg) ![维护状态](https://ci-apps.yunohost.org/ci/badges/hotspot.maintain.svg) + +[![使用 YunoHost 安装 Wifi Hotspot](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=hotspot) + +*[阅读此 README 的其它语言版本。](./ALL_README.md)* + +> *通过此软件包,您可以在 YunoHost 服务器上快速、简单地安装 Wifi Hotspot。* +> *如果您还没有 YunoHost,请参阅[指南](https://yunohost.org/install)了解如何安装它。* + +## 概况 + +* Broadcast a Wi-Fi access point from your self-hosted server +* Combine with the [VPN Client app](https://github.com/labriqueinternet/vpnclient_ynh) to obtain a VPN-protected WiFi + + +**分发版本:** 2.3.1~ynh1 + +## 截图 + +![Wifi Hotspot 的截图](./doc/screenshots/hotspot.png) + +## 文档与资源 + +- 官方应用网站: +- YunoHost 商店: +- 报告 bug: + +## 开发者信息 + +请向 [`testing` 分支](https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing) 发送拉取请求。 + +如要尝试 `testing` 分支,请这样操作: + +```bash +sudo yunohost app install https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +或 +sudo yunohost app upgrade hotspot -u https://github.com/YunoHost-Apps/hotspot_ynh/tree/testing --debug +``` + +**有关应用打包的更多信息:**