diff --git a/README.md b/README.md index 880e8302c..c97228dd4 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ We'd be curious to hear about how you use this with your own Pi-powered access p - [Prerequisites](#prerequisites) - [Quick installer](#quick-installer) - [Manual installation](#manual-installation) + - [Optional reset button](#optional-reset-button) - [Optional services](#optional-services) - [How to contribute](#how-to-contribute) - [License](#license) @@ -114,6 +115,61 @@ sudo reboot The default username is 'admin' and the default password is 'secret'. +## Optional reset button + +#### About +The RaspAP software monitors a GPIO pin which can be used to activate several reset functions. When held for different lengths of time, this one pin can activate reboot, power down and factory reset functions. You can connect a push button to this GPIO pin to make these functions available to the user. RaspAP also drives a GPIO output pin to indicate which function will be activated. You can wire an LED to this pin so that you can clearly see which function will be activated. The LED is not necessary to use the reset functions but it is nice to have. + +This functionality is included in RaspAP. All you need to do is add the button and the LED. + +#### Reset functions + +The following functions can all be accessed by using the reset button + +- Reboot. The Pi will power down and power back up again. +- Shutdown. The Pi will shut down. You need to cycle power to re-start the Pi. +- Factory reset. See [below](#factory-reset-function) for details about how this works. + +#### Function selection + +To activate the reset functions, hold the reset button down. Different hold times select the reset function that will be performed. +If you have connected an LED, the LED flash timing shows which of the reset functions will be activated when the button is released. + +The reset function hold times and the LED flash codes are: + +Reset function | Button held for | Recommended hold time | LED flash rate +-------------- | --------------- | --------------------- | --------------- +Device reboot | Between 1sec and 6 sec | 3 seconds | 1 per second (slow) +Device shutdown | Between 6 sec and 15 sec | 8 seconds | 2 per second (medium) +Factory reset | Longer than 15 seconds | 20 seconds | 5 per second (fast) + +If the button has not been pressed, the LED will simply be ON. This indicates that RaspAP is operating normally. + +Video showing how to use the button to activate the reset modes +https://youtu.be/7N_r_Cffa58 + +#### Factory reset function + +The factory reset function restores RaspAP settings to a pre-defined state. Without changing any settings, the factory reset restores the default RaspAP settings, as would be used for a new installation. + +You can configure the factory reset function to use settings that you have saved. The System -> Defaults menu has functions to set this up. There is a control that will save the current RaspAP configuration. You can also select whether the user saved settings or the RaspAP default settings will be used upon factory reset. + +#### Adding the button and the LED + +The button must be connected so that it shorts the pin GPIO 21 to ground. There is a ground pin directly adjacent to GPIO21 that will be convenient for most applications. Any normally open button will work. +The LED is connected from pin GPIO 20 to ground via a resistor. Using a resistor is **critical**. Leaving it out will definitely fry the GPIO pin and if you're unlucky it will take out the whole processor too. 330 ohms is a safe value to choose for the resistor. + +Fritzing diagram showing which Pi pins to use and which way around the LED needs to be. +![](https://i.imgur.com/m7pT25C.png) + +Photo of a button and LED installed on a Raspberry Pi Zero W. +![](https://i.imgur.com/X95yb6S.png) + +Reference GPIO pinouts +![](https://pinout.xyz/resources/raspberry-pi-pinout.png) +Image courtesy of [pinout.xyz](https://pinout.xyz/) + + ## Optional services OpenVPN and TOR are two additional services that run perfectly well on the RPi, and are a nice way to extend the usefulness of your WiFi router. I've started on interfaces to administer these services. Not everyone will need them, so for the moment they are disabled by default. You can enable them by changing these options in `index.php`: diff --git a/includes/configure_client.php b/includes/configure_client.php index 77c50b1b8..6b0c1889b 100755 --- a/includes/configure_client.php +++ b/includes/configure_client.php @@ -163,70 +163,77 @@ function DisplayWPAConfig(){

showMessages(); ?>

-

Client settings for interface

-
- Rescan -
-
- - - - - - - - - - - - - $network) { ?> - - - - - - - - - - - - - - - +
+ +
Channel
+ + + + X + +
+ + +
Security
+
+ +
Passphrase
+ + --- + + + +
+ +
+ + /> + + /> + + /> +
+ + + + + + + -
SSIDChannelSecurityPassphrase
- - - - - - - - - - X - - - - - --- - - -
+

Interface

+ +
+ Rescan +
+ + + + + + + $network) { ?> + +
+
+
+ + + +

+
+ +
Status
- /> - - /> + + + + - /> -
-
+
- + diff --git a/includes/dashboard.php b/includes/dashboard.php index fa5820dc4..ca8a52381 100755 --- a/includes/dashboard.php +++ b/includes/dashboard.php @@ -51,26 +51,37 @@ function DisplayDashboard(){ $strFrequency = $result[1]; if(strpos( $strWlan0, "UP" ) !== false) { - $status->addMessage('Interface is up', 'success'); $wlan0up = true; - } else { - $status->addMessage('Interface is down', 'warning'); } - if( isset($_POST['ifdown_wlan0']) ) { - exec( 'ifconfig ' . RASPI_WIFI_CLIENT_INTERFACE . ' | grep -i running | wc -l',$test ); - if($test[0] == 1) { + if( $wlan0up ) { + if( isset($_POST['ifdown_wlan0']) ) { exec( 'sudo ip link set ' . RASPI_WIFI_CLIENT_INTERFACE . ' down',$return ); + $status->addMessage('Stopping the interface. Wait for the page to refresh', 'warning'); + ?> + + addMessage('Interface is already up', 'warning'); } else { - echo 'Interface already down'; + $status->addMessage('Interface is up', 'success'); } - } elseif( isset($_POST['ifup_wlan0']) ) { - exec( 'ifconfig ' . RASPI_WIFI_CLIENT_INTERFACE . ' | grep -i running | wc -l',$test ); - if($test[0] == 0) { + } else { + if( isset($_POST['ifup_wlan0']) ) { exec( 'sudo ip link set ' . RASPI_WIFI_CLIENT_INTERFACE . ' up',$return ); exec( 'sudo ip -s a f label ' . RASPI_WIFI_CLIENT_INTERFACE,$return); + $status->addMessage('Starting the interface. Wait for the page to refresh', 'warning'); + ?> + + addMessage('Interface is already down', 'warning'); } else { - echo 'Interface already up'; + $status->addMessage('Interface is down', 'warning'); } } ?> @@ -131,7 +142,7 @@ function DisplayDashboard(){ echo ''; } ?> - + Refresh diff --git a/includes/system.php b/includes/system.php index 8f8c29e38..faa462a6f 100755 --- a/includes/system.php +++ b/includes/system.php @@ -46,7 +46,7 @@ function RPiVersion() { * */ function DisplaySystem(){ - + $status = new StatusMessages(); // hostname exec("hostname -f", $hostarray); $hostname = $hostarray[0]; @@ -80,6 +80,64 @@ function DisplaySystem(){ elseif ($cpuload > 75) { $cpuload_status = "warning"; } elseif ($cpuload > 0) { $cpuload_status = "success"; } + + # Check for RasAP defaults or user saved defaults upon system factory reset + if ( ! $arrDefaultsConf = parse_ini_file('/etc/raspap/hostapd/reset.ini')) { + $status->addMessage('Could not read the configuration file', 'warning'); + } + + # Write preference for RaspAP defaults to reset.ini + if( isset($_POST['select_raspap_defaults']) ) { + if (CSRFValidate()) { + $arrDefaultsConf["user_reset_files"] = "0"; + if ( write_php_ini($arrDefaultsConf,'/etc/raspap/hostapd/reset.ini')) { + $status->addMessage('Successfully saved preference for RaspAP defaults', 'success'); + } else { + $status->addMessage('Unable to save configuration preferences', 'danger'); + } + } else { + $status->addMessage('Unable to save configuration preferences', 'danger'); + error_log('CSRF violation'); + } + } + + # Write preference for user-saved defaults to reset.ini + if( isset($_POST['select_user_defaults']) ) { + if (CSRFValidate()) { + $arrDefaultsConf["user_reset_files"] = "1"; + if ( write_php_ini($arrDefaultsConf,'/etc/raspap/hostapd/reset.ini')) { + $status->addMessage('Successfully saved preference for user-saved defaults', 'success'); + } else { + $status->addMessage('Unable to save configuration preferences', 'danger'); + } + } else { + error_log('CSRF violation'); + } + } + + # Copy current RaspAP settings into user preference files + if( isset($_POST['save_user_settings']) ) { + if (CSRFValidate()) { + SaveUserSettings($status); + $arrDefaultsConf["user_files_saved"] = "1"; + write_php_ini($arrDefaultsConf,'/etc/raspap/hostapd/reset.ini'); + } else { + error_log('CSRF violation'); + } + } + + # Use values from reset.ini for correct display of buttons on "defaults" tab + if ( $arrDefaultsConf['user_reset_files'] == "0") { + $raspapDefaults = " active"; + $userDefaults = ""; + } else { + $raspapDefaults = ""; + $userDefaults = " active"; + } + if ( $arrDefaultsConf['user_files_saved'] == "0") { + $disableUserSettings = ' disabled="disabled"'; + } + ?>
@@ -97,13 +155,14 @@ function DisplaySystem(){ $result = shell_exec("sudo /sbin/shutdown -h now"); } ?> - +

showMessages(); ?>

@@ -140,12 +199,28 @@ function DisplaySystem(){
+
+
+
+

Source for reset data

+
Settings that will be written in if a factory reset is performed
+
+ + name="select_user_defaults" value="User settings" /> +
+
+

Save user settings

+
Save current settings as user defaults
+
+ +
+
+
+
- -
@@ -162,4 +237,124 @@ function DisplaySystem(){ addMessage('Unable to save WiFi hotspot configuration', 'danger'); + $fail = True; + } else { + $status->addMessage('Successfully saved WiFi hotspot configuration', 'success'); + } + + # DHCP server + exec( 'cp /etc/dnsmasq.conf config/user_dnsmasq.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to save DHCP server configuration', 'danger'); + $fail = True; + } else { + $status->addMessage('Successfully saved DHCP server configuration', 'success'); + } + + # DHCP client + exec( 'cp /etc/dhcpcd.conf config/user_dhcpcd.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to save Networking configuration', 'danger'); + $fail = True; + } else { + $status->addMessage('Successfully saved Networking configuration', 'success'); + } + + # Update wifi client configuration + if (file_exists('/etc/wpa_supplicant/wpa_supplicant.conf')) { + exec( 'sudo cat /etc/wpa_supplicant/wpa_supplicant.conf > config/user_wpa_supplicant.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to save WiFi client configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully saved WiFi client configuration', 'success'); + } + } else { + if (file_exists('config/user_wpa_supplicant.conf')) { + exec( 'rm config/user_wpa_supplicant.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to remove old WiFi client configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully removed old WiFi client configuration', 'success'); + } + } + } + + # Update wlan0 wifi client configuration + if (file_exists('/etc/wpa_supplicant/wpa_supplicant-wlan0.conf')) { + exec( 'sudo cat /etc/wpa_supplicant/wpa_supplicant-wlan0.conf > config/user_wpa_supplicant-wlan0.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to save wlan0 WiFi client configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully saved wlan0 WiFi client configuration', 'success'); + } + } else { + if (file_exists('config/user_wpa_supplicant-wlan0.conf')) { + exec( 'rm config/user_wpa_supplicant-wlan0.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to remove old wlan0 WiFi client configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully removed old wlan0 WiFi client configuration', 'success'); + } + } + } + + # Update wlan1 wifi client configuration + if (file_exists('/etc/wpa_supplicant/wpa_supplicant-wlan1.conf')) { + exec( 'sudo cat /etc/wpa_supplicant/wpa_supplicant-wlan1.conf > config/user_wpa_supplicant-wlan1.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to save wlan1 WiFi client configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully saved wlan1 WiFi client configuration', 'success'); + } + } else { + if (file_exists('config/user_wpa_supplicant-wlan1.conf')) { + exec( 'rm config/user_wpa_supplicant-wlan1.conf', $output, $return ); + if ($return) { + $status->addMessage('Unable to remove old wlan1 WiFi client configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully removed old wlan1 WiFi client configuration', 'success'); + } + } + } + + # Update RaspAP authentication configuration + if (file_exists('/etc/raspap/raspap.auth')) { + exec( 'cp /etc/raspap/raspap.auth config/user_raspap.auth', $output, $return ); + if ($return) { + $status->addMessage('Unable to save RaspAP authentication configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully saved RaspAP authentication configuration', 'success'); + } + } else { + if (file_exists('config/user_raspap.auth')) { + exec( 'rm config/user_raspap.auth', $output, $return ); + if ($return) { + $status->addMessage('Unable to remove old RaspAP authentication configuration', 'warning'); + $fail = True; + } else { + $status->addMessage('Successfully removed old RaspAP authentication configuration', 'success'); + } + } + } + + return $fail; +} + ?> diff --git a/installers/button.py b/installers/button.py new file mode 100644 index 000000000..600d740e1 --- /dev/null +++ b/installers/button.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +from gpiozero import Button, LED +from signal import pause +import os, sys + +# Define the location of the script to restore default configuration +defaultsscriptlocation = "/etc/raspap/hostapd/reset.sh" +scriptlocation = sys.argv[1] if len(sys.argv) >= 2 else defaultsscriptlocation + +buttonGPIO = 21 # Pushbutton is connected to GPIO 21 (pin 40) +ledGPIO = 20 # LED is connected to GPIO 20 (pin 38) + +restarttime = 1 # restart if held for greater than restarttime seconds +offtime = 6 # shut down if held for greater than offtime seconds +defaultstime = 15 # reset RaspAP to default config if held for greater than defaultstime seconds + +restartready = False # Flag for reset time exceeded +shutdownready = False # Flag for shutdown time exceeded +defaultsready = False # Flag for defaults reset flag exceeded + +def when_held(): + global restartready + global shutdownready + global defaultsready + + # find how long the button has been held + held_time = button.held_time + restarttime + + # blink rate will increase the longer we hold + # Set flags for the action needed when the button is released + if held_time > restarttime and held_time < offtime and not restartready: + led.blink(on_time=0.5, off_time=0.5) + restartready = True + shutdownready = False + defaultsready = False + print ("Restart time reached") + + + if held_time > offtime and held_time < defaultstime and not shutdownready: + led.blink(on_time=0.25, off_time=0.25) + restartready = False + shutdownready = True + defaultsready = False + print ("Shutdown time reached") + + + if held_time > defaultstime and not defaultsready: + led.blink(on_time=0.1, off_time=0.1) + restartready = False + shutdownready = False + defaultsready = True + print ("Restore defaults time reached") + + +def when_released(): + global restartready + global shutdownready + global defaultsready + + led.on() + + if restartready: + print ("System restarting") + os.system("sudo reboot") + + if shutdownready: + print ("System powering down") + os.system("sudo poweroff") + + if defaultsready: + print ("System restoring RaspAP defaults") + os.system("sudo bash " + scriptlocation) + os.system("sudo reboot") + + # Clear flags if the button was released early + print ("Button released before any action was needed") + restartready = False + shutdownready = False + defaultsready = False + + +led = LED(ledGPIO) +led.on() + +button = Button(buttonGPIO, hold_time=restarttime, hold_repeat=True) +button.when_held = when_held +button.when_released = when_released + +print ("Waiting for a button press") + +pause() \ No newline at end of file diff --git a/installers/common.sh b/installers/common.sh index 2bf9152f8..5330f79f9 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -111,10 +111,10 @@ function create_logging_scripts() { sudo mv $webroot_dir/installers/*log.sh $raspap_dir/hostapd || install_error "Unable to move logging scripts" } -# Generate logging enable/disable files for hostapd -function create_logging_scripts() { - sudo mkdir /etc/raspap/hostapd - sudo mv /var/www/html/installers/*log.sh /etc/raspap/hostapd +# Generate configuration reset files for raspap +function create_reset_scripts() { + sudo mv /var/www/html/installers/reset.sh /etc/raspap/hostapd + sudo mv /var/www/html/installers/button.py /etc/raspap/hostapd } # Fetches latest files from github to webroot @@ -174,39 +174,61 @@ function move_config_file() { install_log "Moving configuration file to '$raspap_dir'" sudo mv "$webroot_dir"/raspap.php "$raspap_dir" || install_error "Unable to move files to '$raspap_dir'" +} + +# Set up configuration for the reset function +function configuration_for_reset() { + install_log "Setting up configuration for the reset function" + sudo echo "webroot_dir = \"$webroot_dir\"" >> /tmp/reset.ini || install_error "Unable to write to reset configuration file" + sudo echo "user_reset_files = 0" >> /tmp/reset.ini || install_error "Unable to write to reset configuration file" + sudo echo "user_files_saved = 0" >> /tmp/reset.ini || install_error "Unable to write to reset configuration file" + sudo mv /tmp/reset.ini /etc/raspap/hostapd/ || install_error "Unable to move files to '$raspap_dir'" +} + +# Set permissions for all RaspAP directories and folders +function set_permissions() { sudo chown -R $raspap_user:$raspap_user "$raspap_dir" || install_error "Unable to change file ownership for '$raspap_dir'" } + # Set up default configuration function default_configuration() { install_log "Setting up hostapd" if [ -f /etc/default/hostapd ]; then sudo mv /etc/default/hostapd /tmp/default_hostapd.old || install_error "Unable to remove old /etc/default/hostapd file" fi - sudo mv $webroot_dir/config/default_hostapd /etc/default/hostapd || install_error "Unable to move hostapd defaults file" - sudo mv $webroot_dir/config/hostapd.conf /etc/hostapd/hostapd.conf || install_error "Unable to move hostapd configuration file" - sudo mv $webroot_dir/config/dnsmasq.conf /etc/dnsmasq.conf || install_error "Unable to move dnsmasq configuration file" - sudo mv $webroot_dir/config/dhcpcd.conf /etc/dhcpcd.conf || install_error "Unable to move dhcpcd configuration file" + sudo cp $webroot_dir/config/default_hostapd /etc/default/hostapd || install_error "Unable to copy hostapd defaults file" + sudo cp $webroot_dir/config/hostapd.conf /etc/hostapd/hostapd.conf || install_error "Unable to copy hostapd configuration file" + sudo cp $webroot_dir/config/dnsmasq.conf /etc/dnsmasq.conf || install_error "Unable to copy dnsmasq configuration file" + sudo cp $webroot_dir/config/dhcpcd.conf /etc/dhcpcd.conf || install_error "Unable to copy dhcpcd configuration file" + if [ -f /etc/wpa_supplicant/wpa_supplicant.conf ]; then + sudo cp /etc/wpa_supplicant/wpa_supplicant.conf $webroot_dir/config/wpa_supplicant.conf || install_error "Unable to copy original wpa_supplicant configuration" + fi + if [ -f /etc/wpa_supplicant/wpa_supplicant_wlan0.conf ]; then + sudo cp /etc/wpa_supplicant/wpa_supplicant_wlan0.conf $webroot_dir/config/wpa_supplicant_wlan0.conf || install_error "Unable to copy original wpa_supplicant_wlan0 configuration" + fi + if [ -f /etc/wpa_supplicant/wpa_supplicant_wlan1.conf ]; then + sudo cp /etc/wpa_supplicant/wpa_supplicant_wlan1.conf $webroot_dir/config/wpa_supplicant_wlan1.conf || install_error "Unable to copy original wpa_supplicant_wlan1 configuration" + fi # Generate required lines for Rasp AP to place into rc.local file. # #RASPAP is for removal script lines=( - 'echo 1 > \/proc\/sys\/net\/ipv4\/ip_forward #RASPAP' + 'echo 1 > /proc/sys/net/ipv4/ip_forward #RASPAP' 'iptables -t nat -A POSTROUTING -j MASQUERADE #RASPAP' - + "python3 $raspap_dir/hostapd/button.py \& #RASPAP" ) for line in "${lines[@]}"; do if grep "$line" /etc/rc.local > /dev/null; then echo "$line: Line already added" else - sudo sed -i "s/^exit 0$/$line\nexit 0/" /etc/rc.local + sudo sed -i "s~^exit 0$~$line\nexit 0~" /etc/rc.local echo "Adding line $line" fi done } - # Add a single entry to the sudoers file function sudo_add() { sudo bash -c "echo \"www-data ALL=(ALL) NOPASSWD:$1\" | (EDITOR=\"tee -a\" visudo)" \ @@ -291,8 +313,12 @@ function install_raspap() { download_latest_files change_file_ownership create_logging_scripts + create_reset_scripts move_config_file + configuration_for_reset + set_permissions default_configuration + sudo_add patch_system_files install_complete } diff --git a/installers/reset.sh b/installers/reset.sh new file mode 100755 index 000000000..208cbd843 --- /dev/null +++ b/installers/reset.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +function reset_default_configuration() { + + webroot_dir=$(sudo cat /etc/raspap/hostapd/reset.ini | grep --only-matching --perl-regexp "(?<=webroot_dir = \")\S+(?=\")") + user_reset_files=$(sudo cat /etc/raspap/hostapd/reset.ini | grep --only-matching --perl-regexp "(?<=user_reset_files = ).") + + if [ "$user_reset_files" == "0" ]; then + echo Restoring RaspAP defaults + sudo cp $webroot_dir/config/hostapd.conf /etc/hostapd/hostapd.conf + sudo cp $webroot_dir/config/dnsmasq.conf /etc/dnsmasq.conf + sudo cp $webroot_dir/config/dhcpcd.conf /etc/dhcpcd.conf + sudo cp $webroot_dir/config/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf + sudo cp $webroot_dir/config/wpa_supplicant_wlan0.conf /etc/wpa_supplicant/wpa_supplicant_wlan0.conf + sudo cp $webroot_dir/config/wpa_supplicant_wlan1.conf /etc/wpa_supplicant/wpa_supplicant_wlan1.conf + sudo rm /etc/raspap/raspap.auth + else + echo Restoring user defaults + sudo cp $webroot_dir/config/user_hostapd.conf /etc/hostapd/hostapd.conf + sudo cp $webroot_dir/config/user_dnsmasq.conf /etc/dnsmasq.conf + sudo cp $webroot_dir/config/user_dhcpcd.conf /etc/dhcpcd.conf + sudo cp $webroot_dir/config/user_wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf + sudo cp $webroot_dir/config/user_wpa_supplicant_wlan0.conf /etc/wpa_supplicant/wpa_supplicant_wlan0.conf + sudo cp $webroot_dir/config/user_wpa_supplicant_wlan1.conf /etc/wpa_supplicant/wpa_supplicant_wlan1.conf + sudo rm /etc/raspap/raspap.auth + sudo cp $webroot_dir/config/user_raspap.auth /etc/raspap/raspap.auth + fi +} + +reset_default_configuration