mirror of
https://git.disroot.org/pranav/pybatmesh.git
synced 2024-12-29 11:30:28 +05:30
271 lines
9.2 KiB
Python
271 lines
9.2 KiB
Python
# This file is part of naxalnet.
|
|
# Copyright (C) 2021 The naxalnet Authors
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
scripts.py
|
|
----------
|
|
|
|
The functions in this file is used for reading configs, args
|
|
and doing the things this program is supposed to do.
|
|
This file is named scripts.py because the original developer
|
|
of this program could not think of a better name that suits this file.
|
|
If you want to hack naxalnet, this is the right place to start.
|
|
When run from the commandline, the function main() is called.
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
from dasbus.error import DBusError
|
|
from systemd.daemon import notify
|
|
from naxalnet import __version__
|
|
from naxalnet.default import REPORT_BUG_INFO, MESH_GLOB, TMP_NET_GLOB
|
|
from naxalnet.log import logger
|
|
from naxalnet.iwd import Adapter, Device, IWD
|
|
from naxalnet.config import args
|
|
from naxalnet.daemon import Daemon
|
|
from naxalnet.network import NetworkD, NetworkLoop
|
|
|
|
|
|
def get_sorted_glob(directory: str, glob: str) -> list:
|
|
"""return sorted list of filenames matching glob"""
|
|
path = Path(directory)
|
|
glob_list = path.glob(glob)
|
|
sorted_list = []
|
|
for i in glob_list:
|
|
# g is a list of PosixPath objects.
|
|
# So we add their absolute path as str.
|
|
sorted_list.append(str(i))
|
|
# sorted_list is not sorted, so we sort them here
|
|
sorted_list.sort()
|
|
return sorted_list
|
|
|
|
|
|
def any_interface_is_routable():
|
|
"""returns true if any of the interfaces is routable"""
|
|
networkd = NetworkLoop(runtime_dir=args.networkd_runtime_dir)
|
|
|
|
# First, add the temporary configs to networkd.
|
|
for i in get_sorted_glob(args.networkd_config_dir, TMP_NET_GLOB):
|
|
logger.debug("Adding temporary config %s", i)
|
|
networkd.add_config(i)
|
|
|
|
# timeout = 10 seconds
|
|
routable = networkd.wait_until_routable(10 * 1000)
|
|
networkd.remove_all_configs()
|
|
|
|
return routable
|
|
|
|
|
|
def setup_mesh(gateway_mode: str = "off"):
|
|
"""
|
|
configure networkd to setup the mesh
|
|
|
|
gateway_mode can be client, server, or off
|
|
"""
|
|
try:
|
|
notify("STATUS=Configuring the network...")
|
|
logger.info("Copying network config files")
|
|
|
|
networkd = NetworkD(runtime_dir=args.networkd_runtime_dir)
|
|
networkd.set_vars(
|
|
batdev=args.batman_device,
|
|
bridgedev=args.bridge_device,
|
|
gateway_mode=gateway_mode,
|
|
)
|
|
|
|
# Fix for issue #19. There should be a switch to disable this
|
|
# humanitarian intervention. We don't want to adopt the U.S.
|
|
# foreign policy here.
|
|
networkd.disable_config("80-wifi-adhoc.network")
|
|
|
|
for i in get_sorted_glob(args.networkd_config_dir, MESH_GLOB):
|
|
logger.debug("Adding network config %s", i)
|
|
networkd.add_config(i)
|
|
except PermissionError:
|
|
logger.exception(
|
|
"A PermissionError occured while copying files. Make sure you are root."
|
|
)
|
|
logger.error(REPORT_BUG_INFO)
|
|
sys.exit(3)
|
|
except:
|
|
logger.exception("An unknown error occured while copying files")
|
|
logger.error(REPORT_BUG_INFO)
|
|
sys.exit(3)
|
|
|
|
|
|
def setup_devices():
|
|
"""
|
|
Setup wifi interfaces using iwd
|
|
This function should be called every time an interface
|
|
is connected or removed.
|
|
"""
|
|
try:
|
|
notify("STATUS=Setting up mesh...")
|
|
iwd = IWD()
|
|
devices = iwd.get_devices()
|
|
adhoc_devices = []
|
|
ap_devices = []
|
|
|
|
# Find devices supporting ad-hoc and ap
|
|
for i in devices:
|
|
# For each device, check if its adapter supports
|
|
# ad-hoc or ap. Many adapters will support both,
|
|
# so we will prioritise ad-hoc over ap.
|
|
device = Device(i)
|
|
logger.debug("Found device %s", device.name)
|
|
adapter = Adapter(device.adapter)
|
|
if adapter.supports_mode("ad-hoc"):
|
|
logger.debug("The device %s can be used for ad-hoc", device.name)
|
|
adhoc_devices.append(i)
|
|
if adapter.supports_mode("ap"):
|
|
logger.debug("The device %s can be used for ap", device.name)
|
|
ap_devices.append(i)
|
|
|
|
if len(adhoc_devices) != 0:
|
|
# Start ad-hoc on first device supporting ad-hoc
|
|
adhoc_device = Device(adhoc_devices.pop())
|
|
# The same device is likely to have ap support too.
|
|
# But we can't start ad-hoc and ap on the same interface.
|
|
# So we will remove adhoc_device from ap_devices if it exists there
|
|
if adhoc_device.name in ap_devices:
|
|
ap_devices.remove(adhoc_device.name)
|
|
|
|
# Turn on adapter if it is off
|
|
# See issue #9
|
|
adhoc_adapter = Adapter(adhoc_device.adapter)
|
|
if not adhoc_adapter.is_powered_on():
|
|
logger.debug("Adapter %s is off. Turning on", adhoc_adapter.name)
|
|
adhoc_adapter.power_on()
|
|
|
|
logger.info("Starting mesh on %s", adhoc_device.name)
|
|
adhoc_device.start_adhoc_open(args.adhoc_name)
|
|
|
|
# Start Access point if ap_device is not empty,
|
|
# ie, we have more devices
|
|
if len(ap_devices) != 0:
|
|
ap_device = Device(ap_devices.pop())
|
|
logger.info("Starting WiFi Access Point on %s", ap_device.name)
|
|
logger.info("Use naxalnet --print-wifi to get password")
|
|
# Turn on adapter if it is off
|
|
# See issue #9
|
|
ap_adapter = Adapter(ap_device.adapter)
|
|
if not ap_adapter.is_powered_on():
|
|
logger.debug("Adapter %s is off. Turning on", ap_adapter.name)
|
|
ap_adapter.power_on()
|
|
ap_device.start_ap(args.ap_ssid, args.ap_passwd)
|
|
else:
|
|
logger.warning("Not setting up WiFi AP.")
|
|
else:
|
|
logger.warning(
|
|
"No device found to setup mesh. Make sure a WiFi adapter is connected"
|
|
)
|
|
|
|
except DBusError:
|
|
logger.exception("Error while communicating with iwd")
|
|
logger.error(REPORT_BUG_INFO)
|
|
sys.exit(4)
|
|
except:
|
|
logger.exception("An unknown error occured while setting up the mesh")
|
|
logger.error(REPORT_BUG_INFO)
|
|
sys.exit(4)
|
|
|
|
|
|
def cleanup():
|
|
"""
|
|
Remove all network config, poweroff used wireless devices and
|
|
exit with 0.
|
|
"""
|
|
networkd = NetworkD(runtime_dir=args.networkd_runtime_dir)
|
|
logger.info("Exiting gracefully")
|
|
networkd.remove_all_configs()
|
|
for i in IWD().get_devices():
|
|
logger.debug("Turning off %s", i)
|
|
device = Device(i)
|
|
# device.set_mode("station")
|
|
device.power_off()
|
|
logger.debug("Deleting interface %s", args.batman_device)
|
|
networkd.delete_interface(args.batman_device)
|
|
logger.debug("Deleting interface %s", args.bridge_device)
|
|
networkd.delete_interface(args.bridge_device)
|
|
|
|
|
|
def print_wifi():
|
|
"""
|
|
Prints the name and password of the adhoc, and ap
|
|
from the arguments
|
|
"""
|
|
print("Mesh name:", args.adhoc_name)
|
|
print("SSID:", args.ap_ssid)
|
|
print("Password:", args.ap_passwd)
|
|
|
|
|
|
def print_version():
|
|
"""Just does what the name suggests"""
|
|
print(__version__)
|
|
|
|
|
|
def main():
|
|
"""
|
|
This is where the magic happens!
|
|
This function is run every time you
|
|
execute naxalnet from the commandline
|
|
"""
|
|
|
|
if args.print_wifi:
|
|
print_wifi()
|
|
sys.exit(0)
|
|
elif args.version:
|
|
print_version()
|
|
sys.exit(0)
|
|
|
|
# Notify systemd that naxalnet is ready.
|
|
# see man:sd_notify(3)
|
|
notify("READY=1")
|
|
|
|
# Gateway mode comes in handy when many nodes have a DHCP server and
|
|
# you want to prevent conflicts. It defaults to "auto" in naxalnet.
|
|
# https://www.open-mesh.org/projects/batman-adv/wiki/Gateways
|
|
if args.gateway_mode == "auto":
|
|
logger.info("Checking for internet connection")
|
|
notify("STATUS=Checking for internet")
|
|
# If any interface is routable, set gateway mode to server
|
|
if any_interface_is_routable():
|
|
gateway_mode = "server"
|
|
else:
|
|
gateway_mode = "client"
|
|
logger.info("gateway_mode set to %s", gateway_mode)
|
|
elif args.gateway_mode in ["server", "client", "off"]:
|
|
gateway_mode = args.gateway_mode
|
|
else:
|
|
logger.error("gateway-mode has an illegal value")
|
|
sys.exit(5)
|
|
|
|
try:
|
|
setup_devices()
|
|
setup_mesh(gateway_mode=gateway_mode)
|
|
|
|
# Start the daemon so that setup_devices() is called every
|
|
# time a device is connected or removed.
|
|
daemon = Daemon()
|
|
daemon.add_callback(setup_devices)
|
|
|
|
notify("STATUS=Waiting for changes")
|
|
daemon.start()
|
|
# systemd uses SIGINT to kill this program
|
|
except KeyboardInterrupt:
|
|
cleanup()
|