Progress
This commit is contained in:
parent
3c91e5d74b
commit
b8ba97742d
|
@ -0,0 +1,19 @@
|
||||||
|
; Sending <run-oneshot SOMECOMMAND RESTARTPOLICY> causes the command to be run.
|
||||||
|
;
|
||||||
|
?? <run-oneshot ?argv ?restartPolicy> [
|
||||||
|
let ?id = timestamp
|
||||||
|
let ?facet = facet
|
||||||
|
let ?d = <temporary-exec $id $argv>
|
||||||
|
<run-service <daemon $d>>
|
||||||
|
<daemon $d {
|
||||||
|
argv: $argv,
|
||||||
|
readyOnStart: #f,
|
||||||
|
restart: $restartPolicy,
|
||||||
|
}>
|
||||||
|
? <service-state <daemon $d> complete> [$facet ! stop]
|
||||||
|
? <service-state <daemon $d> failed> [$facet ! stop]
|
||||||
|
]
|
||||||
|
|
||||||
|
; If the restart policy is not specified, it is defaulted to `on-error`.
|
||||||
|
;
|
||||||
|
?? <run-oneshot ?argv> ! <run-oneshot $argv on-error>
|
|
@ -1,2 +1,7 @@
|
||||||
let ?ds = dataspace
|
let ?ds = dataspace
|
||||||
<machine-dataspace $ds>
|
<machine-dataspace $ds>
|
||||||
|
|
||||||
|
$ds ? ?r [
|
||||||
|
$log ! <log "-" { line: "machine" |+++|: $r }>
|
||||||
|
?- $log ! <log "-" { line: "machine" |---|: $r }>
|
||||||
|
]
|
||||||
|
|
|
@ -20,30 +20,6 @@
|
||||||
<daemon <udhcpc $ifname> ["udhcpc" "-i" $ifname "-fR" "-s" "/usr/lib/synit/udhcpc.script"]>
|
<daemon <udhcpc $ifname> ["udhcpc" "-i" $ifname "-fR" "-s" "/usr/lib/synit/udhcpc.script"]>
|
||||||
]
|
]
|
||||||
|
|
||||||
? <run-service <daemon <wpa_supplicant ?ifname>>> [
|
|
||||||
<daemon <wpa_supplicant $ifname> [
|
|
||||||
"wpa_supplicant" "-Dnl80211,wext" "-C/run/wpa_supplicant" "-i" $ifname
|
|
||||||
]>
|
|
||||||
]
|
|
||||||
|
|
||||||
?? <run-oneshot ?argv> ! <run-oneshot $argv on-error>
|
|
||||||
|
|
||||||
?? <run-oneshot ?argv ?restartPolicy> [
|
|
||||||
let ?id = timestamp
|
|
||||||
let ?facet = facet
|
|
||||||
let ?d = <temporary-exec $id $argv>
|
|
||||||
<run-service <daemon $d>>
|
|
||||||
<daemon $d {
|
|
||||||
argv: $argv,
|
|
||||||
readyOnStart: #f,
|
|
||||||
restart: $restartPolicy,
|
|
||||||
}>
|
|
||||||
? <service-state <daemon $d> complete> [$facet ! stop]
|
|
||||||
? <service-state <daemon $d> failed> [$facet ! stop]
|
|
||||||
]
|
|
||||||
|
|
||||||
;---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
<daemon interface-monitor {
|
<daemon interface-monitor {
|
||||||
argv: "/usr/lib/synit/interface-monitor"
|
argv: "/usr/lib/synit/interface-monitor"
|
||||||
protocol: application/syndicate
|
protocol: application/syndicate
|
||||||
|
@ -56,22 +32,10 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
$machine ? ?r [
|
|
||||||
$log ! <log "-" { line: "machine" |+++|: $r }>
|
|
||||||
?- $log ! <log "-" { line: "machine" |---|: $r }>
|
|
||||||
]
|
|
||||||
|
|
||||||
$machine ? <interface ?ifname _ normal _ _ carrier _> [
|
$machine ? <interface ?ifname _ normal _ _ carrier _> [
|
||||||
$config <configure-interface $ifname <dhcp>>
|
$config <configure-interface $ifname <dhcp>>
|
||||||
]
|
]
|
||||||
|
|
||||||
$machine ? <interface ?ifname _ wireless _ _ _ _> [
|
|
||||||
$config <run-service <daemon <wpa_supplicant $ifname>>>
|
|
||||||
]
|
|
||||||
$machine ? <interface ?ifname _ wireless up up carrier _> [
|
|
||||||
$config <configure-interface $ifname <dhcp>>
|
|
||||||
]
|
|
||||||
|
|
||||||
$machine ? <route ?af default _ _ _ _> [
|
$machine ? <route ?af default _ _ _ _> [
|
||||||
$config <default-route $af>
|
$config <default-route $af>
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
let ?settingsDir = "/etc/syndicate/user-settings"
|
||||||
|
|
||||||
|
let ?settings = <* $config [
|
||||||
|
<rewrite ?item <user-setting $item>>
|
||||||
|
]>
|
||||||
|
|
||||||
|
<require-service <config-watcher $settingsDir { config: $settings }>>
|
||||||
|
|
||||||
|
<require-service <daemon user-settings-daemon>>
|
||||||
|
<daemon user-settings-daemon {
|
||||||
|
argv: "/usr/lib/synit/user-settings-daemon"
|
||||||
|
protocol: application/syndicate
|
||||||
|
}>
|
||||||
|
|
||||||
|
? <service-object <daemon user-settings-daemon> ?cap> [
|
||||||
|
$cap {
|
||||||
|
config: $config
|
||||||
|
settingsDir: $settingsDir
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,34 @@
|
||||||
|
? <machine-dataspace ?machine> [
|
||||||
|
$machine ? <interface ?ifname _ wireless _ _ _ _> [
|
||||||
|
$config [
|
||||||
|
<require-service <daemon <wpa_supplicant $ifname>>>
|
||||||
|
<depends-on <daemon <wifi-daemon $ifname>> <service-state <daemon <wpa_supplicant $ifname>> up>>
|
||||||
|
<require-service <daemon <wifi-daemon $ifname>>>
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
$machine ? <interface ?ifname _ wireless up up carrier _> [
|
||||||
|
$config <configure-interface $ifname <dhcp>>
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
? <run-service <daemon <wifi-daemon ?ifname>>> [
|
||||||
|
<daemon <wifi-daemon $ifname> {
|
||||||
|
argv: "/usr/lib/synit/wifi-daemon"
|
||||||
|
protocol: application/syndicate
|
||||||
|
}>
|
||||||
|
? <machine-dataspace ?machine> [
|
||||||
|
? <service-object <daemon <wifi-daemon $ifname>> ?cap> [
|
||||||
|
$cap {
|
||||||
|
machine: $machine
|
||||||
|
ifname: $ifname
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
? <run-service <daemon <wpa_supplicant ?ifname>>> [
|
||||||
|
<daemon <wpa_supplicant $ifname> [
|
||||||
|
"wpa_supplicant" "-Dnl80211,wext" "-C/run/wpa_supplicant" "-i" $ifname
|
||||||
|
]>
|
||||||
|
]
|
|
@ -20,6 +20,7 @@ mount -t cgroup2 none /sys/fs/cgroup
|
||||||
|
|
||||||
mount -o rw,remount /
|
mount -o rw,remount /
|
||||||
|
|
||||||
|
mkdir -p /etc/syndicate/user-settings
|
||||||
mkdir -p /run/etc/syndicate/core
|
mkdir -p /run/etc/syndicate/core
|
||||||
mkdir -p /usr/local/etc/syndicate/core
|
mkdir -p /usr/local/etc/syndicate/core
|
||||||
mkdir -p /run/etc/syndicate/services
|
mkdir -p /run/etc/syndicate/services
|
||||||
|
|
|
@ -12,7 +12,10 @@ import threading
|
||||||
import pyroute2
|
import pyroute2
|
||||||
from pr2modules.iwutil import IW
|
from pr2modules.iwutil import IW
|
||||||
|
|
||||||
schemas = preserves.schema.load_schema_file('/usr/share/synit/schemas/schema-bundle.prb')
|
try:
|
||||||
|
schemas = preserves.schema.load_schema_file('/usr/share/synit/schemas/schema-bundle.prb')
|
||||||
|
except:
|
||||||
|
schemas = preserves.schema.load_schema_file('/home/tonyg/src/syndicate-system/protocols/schema-bundle.bin')
|
||||||
network = schemas.network
|
network = schemas.network
|
||||||
|
|
||||||
class LenientFormatter(Formatter):
|
class LenientFormatter(Formatter):
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import preserves.schema
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
from syndicate import patterns as P, relay, turn, dataspace, Symbol, canonicalize, stringify
|
||||||
|
from syndicate.actor import find_loop
|
||||||
|
from syndicate.during import During
|
||||||
|
|
||||||
|
try:
|
||||||
|
schemas = preserves.schema.load_schema_file('/usr/share/synit/schemas/schema-bundle.prb')
|
||||||
|
except:
|
||||||
|
schemas = preserves.schema.load_schema_file('/home/tonyg/src/syndicate-system/protocols/schema-bundle.bin')
|
||||||
|
userSettings = schemas.userSettings
|
||||||
|
|
||||||
|
def digest(item):
|
||||||
|
return hashlib.sha1(canonicalize(item)).hexdigest()
|
||||||
|
|
||||||
|
@relay.service(name='user_settings_daemon', debug=False)
|
||||||
|
@During().add_handler
|
||||||
|
def main(args):
|
||||||
|
config = args[Symbol('config')].embeddedValue
|
||||||
|
settingsDir = args[Symbol('settingsDir')]
|
||||||
|
|
||||||
|
def assert_item(item):
|
||||||
|
filename = os.path.join(settingsDir, digest(item) + '.pr')
|
||||||
|
turn.log.info(f'Asserting: {item} --> {filename}')
|
||||||
|
with open(filename, 'wt') as f:
|
||||||
|
f.write(stringify(item, indent=2) + '\n')
|
||||||
|
|
||||||
|
def retract_item(item):
|
||||||
|
filename = os.path.join(settingsDir, digest(item) + '.pr')
|
||||||
|
turn.log.info(f'Retracting: {item} --> {filename}')
|
||||||
|
try:
|
||||||
|
os.unlink(filename)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataspace.during(config, P.bind(P.quote(userSettings.Command(P.u_, P.u_))))
|
||||||
|
def handle_command(c):
|
||||||
|
c = userSettings.Command.try_decode(c)
|
||||||
|
if c is None: return
|
||||||
|
c.action._accept({
|
||||||
|
'assert': assert_item,
|
||||||
|
'retract': retract_item,
|
||||||
|
})
|
||||||
|
if c.reply.VARIANT.name == 'replyWanted':
|
||||||
|
turn.publish(c.reply.value, userSettings.CommandReply())
|
|
@ -0,0 +1,155 @@
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import atexit
|
||||||
|
import preserves.schema
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
|
||||||
|
from syndicate import patterns as P, relay, turn, dataspace, Symbol
|
||||||
|
from syndicate.actor import find_loop
|
||||||
|
from syndicate.during import During
|
||||||
|
|
||||||
|
try:
|
||||||
|
schemas = preserves.schema.load_schema_file('/usr/share/synit/schemas/schema-bundle.prb')
|
||||||
|
except:
|
||||||
|
schemas = preserves.schema.load_schema_file('/home/tonyg/src/syndicate-system/protocols/schema-bundle.bin')
|
||||||
|
network = schemas.network
|
||||||
|
|
||||||
|
server_socket_dir = '/run/wpa_supplicant'
|
||||||
|
|
||||||
|
class WPASupplicantClient:
|
||||||
|
# https://w1.fi/wpa_supplicant/devel/ctrl_iface_page.html
|
||||||
|
#
|
||||||
|
# According to the code (!) any response starting with '<' is an event, not a reply.
|
||||||
|
|
||||||
|
def __init__(self, interface, log):
|
||||||
|
self.interface = interface
|
||||||
|
self.log = log
|
||||||
|
self.server_socket_path = os.path.join(server_socket_dir, interface)
|
||||||
|
self.client_socket_path = f'/tmp/wpa_cli_py_{interface}_{os.getpid()}'
|
||||||
|
|
||||||
|
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||||
|
self.sock.bind(self.client_socket_path)
|
||||||
|
self.sock.connect(self.server_socket_path)
|
||||||
|
|
||||||
|
self.send('ATTACH')
|
||||||
|
answer = self.recv(timeout_sec=3.0)
|
||||||
|
if answer != 'OK':
|
||||||
|
raise AssertionError(f'wpa_supplicant replied {repr(answer)} to ATTACH')
|
||||||
|
|
||||||
|
self.cleanup = lambda: self.close()
|
||||||
|
atexit.register(self.cleanup)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.cleanup:
|
||||||
|
atexit.unregister(self.cleanup)
|
||||||
|
self.cleanup = None
|
||||||
|
self.send('DETACH')
|
||||||
|
self.sock.close()
|
||||||
|
os.unlink(self.client_socket_path)
|
||||||
|
|
||||||
|
def send(self, command):
|
||||||
|
self.sock.send(command.encode('utf-8'))
|
||||||
|
|
||||||
|
def recv(self, timeout_sec=None):
|
||||||
|
self.sock.settimeout(timeout_sec)
|
||||||
|
try:
|
||||||
|
reply = self.sock.recv(65536)
|
||||||
|
self.log.info(f'Raw input: {repr(reply)}')
|
||||||
|
return reply.strip().decode('utf-8')
|
||||||
|
except TimeoutError:
|
||||||
|
return None
|
||||||
|
finally:
|
||||||
|
self.sock.settimeout(None)
|
||||||
|
|
||||||
|
def gather_events(facet, callback, client, loop):
|
||||||
|
facet.log.debug('Background socket read thread started')
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
facet.log.debug('waiting for event...')
|
||||||
|
e = client.recv()
|
||||||
|
# OMG python's incredibly broken mutable-variable-closures bit me AGAIN. I've had
|
||||||
|
# to add a layer of gratuitous rebinding of `e` plus the `lambda:` below to make it
|
||||||
|
# properly close over the current binding of `e`.
|
||||||
|
def handler_for_specific_e(e):
|
||||||
|
return lambda: callback(e)
|
||||||
|
turn.external(facet, handler_for_specific_e(e), loop=loop)
|
||||||
|
except Exception as e:
|
||||||
|
facet.log.debug(e)
|
||||||
|
finally:
|
||||||
|
facet.log.debug('Background socket read thread terminated')
|
||||||
|
|
||||||
|
@relay.service(name='wifi_daemon', debug=False)
|
||||||
|
@During().add_handler
|
||||||
|
def main(args):
|
||||||
|
machine_ds = args[Symbol('machine')].embeddedValue
|
||||||
|
ifname = args[Symbol('ifname')]
|
||||||
|
|
||||||
|
client = WPASupplicantClient(ifname, turn.log)
|
||||||
|
commands = []
|
||||||
|
active_network_id = None
|
||||||
|
|
||||||
|
def send_next_command():
|
||||||
|
while len(commands) > 0:
|
||||||
|
turn.log.info(f'Sending {commands[0][0]}')
|
||||||
|
client.send(commands[0][0])
|
||||||
|
if commands[0][1] is None:
|
||||||
|
commands.pop(0)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
def send_command(c, handler = lambda reply: None):
|
||||||
|
turn.log.info(f'Scheduling {c}')
|
||||||
|
commands.append((c, handler))
|
||||||
|
if len(commands) == 1:
|
||||||
|
send_next_command()
|
||||||
|
|
||||||
|
def cleanout_networks(nets):
|
||||||
|
for line in nets.split('\n'):
|
||||||
|
if line.startswith('network id'):
|
||||||
|
continue
|
||||||
|
turn.log.info(f'Cleaning out old network: {repr(line)}')
|
||||||
|
send_command('REMOVE_NETWORK ' + line.split('\t')[0])
|
||||||
|
send_command('ADD_NETWORK', remember_network_id)
|
||||||
|
|
||||||
|
def remember_network_id(netid):
|
||||||
|
nonlocal active_network_id
|
||||||
|
active_network_id = netid
|
||||||
|
turn.log.info(f'Network ID: {active_network_id}')
|
||||||
|
def handle_scan_failure(reply):
|
||||||
|
if reply == 'FAIL':
|
||||||
|
send_command('TERMINATE')
|
||||||
|
send_command('SCAN', handle_scan_failure)
|
||||||
|
|
||||||
|
send_command('LEVEL 3')
|
||||||
|
send_command('LIST_NETWORKS', cleanout_networks)
|
||||||
|
|
||||||
|
def handle_event(e):
|
||||||
|
if e[0] == '<':
|
||||||
|
# It's an event
|
||||||
|
m = re.match(r'<(\d+)>(.*)', e)
|
||||||
|
if m:
|
||||||
|
(_level, message) = m.groups()
|
||||||
|
if message == 'CTRL-EVENT-SCAN-RESULTS':
|
||||||
|
send_command('SCAN_RESULTS')
|
||||||
|
turn.log.info(f'Level {_level}: {message}')
|
||||||
|
else:
|
||||||
|
turn.log.info(f'Unusual event: {e}')
|
||||||
|
else:
|
||||||
|
if len(commands) > 0:
|
||||||
|
turn.log.info(f'REPLY: {e}')
|
||||||
|
commands[0][1](e)
|
||||||
|
commands.pop(0)
|
||||||
|
send_next_command()
|
||||||
|
else:
|
||||||
|
turn.log.warning(f'Unexpected reply: {e}')
|
||||||
|
|
||||||
|
facet = turn.active_facet()
|
||||||
|
loop = find_loop()
|
||||||
|
turn.log.info('Starting background read thread')
|
||||||
|
threading.Thread(
|
||||||
|
name='background-wpa_supplicant-read-thread',
|
||||||
|
target=lambda: gather_events(facet, handle_event, client, loop)).start()
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python'))
|
||||||
|
import synit.daemon.user_settings_daemon
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'python'))
|
||||||
|
import synit.daemon.wifi_daemon
|
Loading…
Reference in New Issue