mirror of
https://review.haiku-os.org/haiku
synced 2024-11-23 07:18:40 +01:00
bootstrap image: Add remote command execution script
In order to automate the complete bootstrap build process we need a mechanism to control the second phase which builds the final packages on the booted bootstrap Haiku. To avoid additional dependencies (buildbot slave, ssh, rsh,...) we'd have to cross-build, there's now a pair of simple python scripts that allows executing commands on a remote machine. The server script (bootstrap_daemon.py) is added to the bootstrap image and started automatically during the boot.
This commit is contained in:
parent
c00e34900a
commit
96a321df07
@ -14,3 +14,8 @@ AddFilesToHaikuImage home haikuports
|
||||
local formatVersionsFile = <haikuports>FormatVersions ;
|
||||
SEARCH on $(formatVersionsFile) = $(HAIKU_PORTS) ;
|
||||
AddFilesToHaikuImage home haikuports : $(formatVersionsFile) ;
|
||||
|
||||
# bootstrap daemon
|
||||
local bootstrapDaemon = <haiku-image>bootstrap_daemon.py ;
|
||||
SEARCH on $(bootstrapDaemon) = [ FDirName $(HAIKU_TOP) build scripts ] ;
|
||||
AddFilesToHaikuImage home config settings boot launch : $(bootstrapDaemon) ;
|
||||
|
85
build/scripts/bootstrap_client.py
Executable file
85
build/scripts/bootstrap_client.py
Executable file
@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Usage: bootstrap_client.py <address>[:port] <command> ...
|
||||
#
|
||||
# <command> and the following arguments are concatenated (separated by a space)
|
||||
# and passed to a shell on the server.
|
||||
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import sys
|
||||
|
||||
|
||||
port = 4242
|
||||
bufferSize = 4 * 1024
|
||||
|
||||
# interpret command line args
|
||||
if len(sys.argv) < 3:
|
||||
sys.exit('Usage: ' + sys.argv[0] + ' <address>[:<port>] <command>')
|
||||
|
||||
address = sys.argv[1]
|
||||
portIndex = address.find(':')
|
||||
if portIndex >= 0:
|
||||
port = int(address[portIndex + 1:])
|
||||
address = address[:portIndex]
|
||||
|
||||
commandToRun = " ".join(sys.argv[2:])
|
||||
|
||||
# create sockets and connect to server
|
||||
controlConnection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
stdioConnection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
stderrConnection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
try:
|
||||
controlConnection.connect((address, port))
|
||||
stdioConnection.connect((address, port))
|
||||
stderrConnection.connect((address, port))
|
||||
except socket.error, msg:
|
||||
sys.exit('Failed to connect to %s port %d: %s' % (address, port, msg[1]))
|
||||
|
||||
# send command length and command
|
||||
controlConnection.send("%08d" % len(commandToRun))
|
||||
controlConnection.send(commandToRun)
|
||||
|
||||
# I/O loop. We quit when all sockets have been closed.
|
||||
exitCode = ''
|
||||
connections = [controlConnection, stdioConnection, stderrConnection, sys.stdin]
|
||||
|
||||
while connections and (len(connections) > 1 or not sys.stdin in connections):
|
||||
(readable, writable, exceptions) = select.select(connections, [],
|
||||
connections)
|
||||
|
||||
if sys.stdin in readable:
|
||||
data = sys.stdin.readline(bufferSize)
|
||||
if data:
|
||||
stdioConnection.send(data)
|
||||
else:
|
||||
connections.remove(sys.stdin)
|
||||
stdioConnection.shutdown(socket.SHUT_WR)
|
||||
|
||||
if stdioConnection in readable:
|
||||
data = stdioConnection.recv(bufferSize)
|
||||
if data:
|
||||
sys.stdout.write(data)
|
||||
else:
|
||||
connections.remove(stdioConnection)
|
||||
|
||||
if stderrConnection in readable:
|
||||
data = stderrConnection.recv(bufferSize)
|
||||
if data:
|
||||
sys.stderr.write(data)
|
||||
else:
|
||||
connections.remove(stderrConnection)
|
||||
|
||||
if controlConnection in readable:
|
||||
data = controlConnection.recv(bufferSize)
|
||||
if data:
|
||||
exitCode += data
|
||||
else:
|
||||
connections.remove(controlConnection)
|
||||
|
||||
# either an exit code has been sent or we consider this an error
|
||||
if exitCode:
|
||||
sys.exit(int(exitCode))
|
||||
sys.exit(1)
|
61
build/scripts/bootstrap_daemon.py
Executable file
61
build/scripts/bootstrap_daemon.py
Executable file
@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
address = '0.0.0.0'
|
||||
port = 4242
|
||||
|
||||
|
||||
def receiveExactly(connection, size):
|
||||
data = '';
|
||||
while size > 0:
|
||||
dataReceived = connection.recv(size)
|
||||
if not dataReceived:
|
||||
raise EOFError()
|
||||
data += dataReceived
|
||||
size -= len(dataReceived)
|
||||
return data
|
||||
|
||||
|
||||
def handleConnection(listenerSocket):
|
||||
(controlConnection, controlAddress) = listenerSocket.accept()
|
||||
(stdioConnection, stdioAddress) = listenerSocket.accept()
|
||||
(stderrConnection, stderrAddress) = listenerSocket.accept()
|
||||
|
||||
print 'accepted client connections'
|
||||
|
||||
try:
|
||||
commandLength = receiveExactly(controlConnection, 8)
|
||||
commandToRun = receiveExactly(controlConnection, int(commandLength))
|
||||
|
||||
print 'received command: ' + commandToRun
|
||||
|
||||
exitCode = subprocess.call(commandToRun, stdin=stdioConnection,
|
||||
stdout=stdioConnection, stderr=stderrConnection, shell=True)
|
||||
|
||||
controlConnection.send(str(exitCode))
|
||||
finally:
|
||||
controlConnection.close()
|
||||
stdioConnection.close()
|
||||
stderrConnection.close()
|
||||
|
||||
print 'client connections closed'
|
||||
|
||||
|
||||
listenerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
listenerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
|
||||
try:
|
||||
listenerSocket.bind((address, port))
|
||||
except socket.error, msg:
|
||||
sys.exit('Failed to bind to %s port %d: %s' % (address, port, msg[1]))
|
||||
|
||||
listenerSocket.listen(3)
|
||||
|
||||
print 'started listening on adddress %s port %s' % (address, port)
|
||||
|
||||
while True:
|
||||
handleConnection(listenerSocket)
|
Loading…
Reference in New Issue
Block a user