CSa32/CS.S
changeset 0 5c129dd80d4f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CSa32/CS.S	Thu Nov 21 14:55:10 2019 +0100
@@ -0,0 +1,556 @@
+	.include "DS.S"
+#-----------------------------------------------
+#	S T A T I C   V A R I A B L E S
+#-----------------------------------------------
+	.data
+	.global C.csP
+C.csP:	.int	0				# -->CS
+	.text
+#-----------------------------------------------
+#	M A I N   R O U T I N E
+#-----------------------------------------------
+# takes values from ENV, establishes task for comm nodes and waits for their completion
+	ARGS
+	DS	prgP				# -->progname
+# returns	nothing
+	PROLOC
+	DL	this, CSL			# CS - top level attr vector
+	DL	deP				# -->Debug
+	DL	status				# status returned from wait
+	DL	pid				# PID returned from wait
+	DL	bad				# cummulative subtask rc
+	DL	s, 256				# string buf
+	EPILOC
+#-----------------------------------------------
+	.global _start
+_start:
+	PROLOG
+	lea	this(bp), b			# -->CS
+	mov	b, C.csP			# save -->CS
+	lea	C.debug(b), a			# -->Debug
+	mov	a, deP(bp)
+# initialize static debug vars
+	push	prgP(bp)			# argv[0] -->prgname
+	call	D.init				# initialize static debug vars
+	push	D.prgNameP
+# indetify itself
+	DEBID	"client/server demo"
+	movl	$1, C.debMaxLev(b)		# default debug level
+# initialize top level ctrl values vector
+	ShareA					# allocate shared structure
+	mov	C.csP, b
+	mov	a, C.shP(b)			# save -->shared struct in vector
+	movl	$0, S.msgs(a)			# init msg cntr
+	movl	$0, S.conns(a)			# init connection cntr
+	push	$1				# initial semaphore value
+	push	$1				# semaphore shared between processes
+	lea	S.counter_sem(a), c
+	push	c
+#	call	sem_init			# initialize shared counters semaphore
+#	cmp	$0, a
+#	jz	0f
+#	SYSERR	"sem_init of shared counters semaphore"
+#0:
+	SYS	sem_init
+# set default values
+	mov	C.csP, b
+	movl	$3, C.ttl(b)			# default TTL
+	movl	$11000, C.rp0(b)		# default bind port for the 1. node in ring
+	movl	$12000, C.mp0(b)		# default bind port for the 1. node in mash
+	movl	$0, C.rn(b)			# default # of nodes in ring
+	movl	$0, C.mn(b)			# default # of nodes in mash
+	movl	$0, C.ssl(b)			# default ssl switch - 0=noSSL
+	movl	$77, C.connTh(b)		# conn retries threshold
+	mov	$0f, a
+	mov	a, C.txtP(b)			# default payload text
+	jmp	1f
+0:	.ascii "bla bla\0"
+1:
+# get control values from ENV
+# get max debug level
+	GETINTENV "DEB"
+	mov	C.csP, b
+	mov	a, C.debMaxLev(b)
+	cmp	$-1, a				# debug level -1 means search subroutines
+	jne	0f
+	call	D.subr
+	jmp	C.ret
+0:
+# get message payload text
+	push	$0f
+	call	getenv
+	cmp	$0, a
+	jz	1f
+	mov	C.csP, b
+	mov	a, C.txtP(b)
+	jmp	1f
+0:	.asciz "T"
+1:
+# get TTL
+	GETINTENV "TTL"
+	jz	0f
+	mov	a, C.ttl(b)
+0:
+# get # of nodes in each topology
+	GETINTENV "I"
+	jz	0f
+	mov	a, C.rn(b)
+	mov	a, C.mn(b)
+0:
+# get # of nodes in ring
+	GETINTENV "RN"
+	jz	0f
+	mov	a, C.rn(b)
+0:
+# get # of nodes in mash
+	GETINTENV "MN"
+	jz	0f
+	mov	a, C.mn(b)
+0:
+	add	C.rn(b), a
+	cmp	$3, C.ssl(b)
+	jne	1f
+	add	a, a				# double # of nodes when both SSL and nonSSL
+1:	mov	C.shP(b), c			# -->shared counters
+	mov	a, S.act(c)			# save # of active nodes
+# get first ring node's bind port#
+	GETINTENV "RP0"
+	jz	0f
+	mov	a, C.rp0(b)
+0:
+# get first mash node's bind port#
+	GETINTENV "MP0"
+	jz	0f
+	mov	a, C.mp0(b)
+0:
+# get random() seed
+	GETINTENV "RS"
+	jz	0f
+	push	a
+	call	srandom
+0:
+# get pacing interval (real num in seconds)
+	movl	$0, C.pace.tv_sec(b)
+	movl	$0, C.pace.tv_nsec(b)
+	movl	$0, C.pacing(b)
+	push	$0f
+	call	getenv
+	jmp	1f
+0:	.asciz "P"
+1:
+	test	a, a				# env P set ?
+	jz	3f				# no
+	push	a
+	call	atof				# convert to double
+	fstl	(sp)				# tempor save
+	mov	C.csP, b
+	fisttpl	C.pace.tv_sec(b)		# truncated integral part = secs
+	fldl	(sp)
+	fisubl	C.pace.tv_sec(b)		# fraction part
+	fimull	1f				# * 10^9 = nanosecs
+	fistl	C.pace.tv_nsec(b)
+	jmp	2f
+0:	.double	0
+1:	.int	1000000000			# 10^9
+2:
+	cmp	$0, C.pace.tv_sec(b)
+	jnz	0f
+	cmp	$0, C.pace.tv_nsec(b)
+	jz	3f
+0:	movl	$1, C.pacing(b)
+3:
+# get ssl switch value and save it as mask
+# switch: 0 = noSSL, 1 = SSL, 2 = both
+# mask: 01B=noSSL, 10B=SSL, 11B=both
+	GETINTENV "SSL"				# returned zero means SSL=0 or SSL by default 0
+	inc	a				# change switch to mask
+	mov	a, C.ssl(b)
+	cmp	$1, a
+	je	C.cont				# no SSL
+	cmp	$2, a
+	je	0f				# only SSL
+	mov	C.shP(b), a			# -->shared mem
+	shll	$1, S.act(a)			# double # of active nodes when running both SSL and nonSSL
+# get SSL CA cert dir path
+0:
+	push	$0f
+	call	getenv
+	jmp	1f
+0:	.asciz "CAP"
+1:
+	test	a, a
+	jnz	1f
+	mov	$0f, a
+	jmp	1f
+0:	.asciz	"/home/local/etc/ssl/certs/"
+1:
+	mov	C.csP, b
+	mov	a, C.caPathP(b)			# save -->SSL CA certs path
+# get SSL dir path
+	push	$0f
+	call	getenv
+	jmp	1f
+0:	.asciz "CEP"
+1:
+	test	a, a
+	jz	0f				# ceP not set, try to determine
+	mov	C.csP, b
+	mov	a, C.cePathP(b)			# save -->SSL dir path
+	jmp	1f
+# determine home path (needed to locate SSL keys & certificates)
+0:
+	push	prgP(bp)			# -->first parm - progname w/ path
+	call	dirname				# get prog dirname
+	push	a
+	call	strlen				# dirname length
+	lea	1f-0f(a), a			# + suffix length
+	sub	a, sp				# allocate space for cePath
+	mov	C.csP, b
+	mov	sp, C.cePathP(b)		# save -->SSL path
+	push	prgP(bp)
+	call	dirname				# get home dirname
+	push	a				# -->home dirname
+	mov	C.csP, b
+	push	C.cePathP(b)			# -->SSL path
+	call	strcpy				# copy dirname to SSL path
+	call	strlen				# end of dirname
+	push	$0f				# -->suffix
+	mov	C.csP, b
+	mov	C.cePathP(b), c
+	lea	(c, a), a			# -->end of dirname
+	push	a
+	call	strcpy				# copy suffix to SSL path
+	jmp	1f
+0:	.asciz	"/../CS/"
+1:
+# testing sandbox
+	mov	C.csP, b
+	pushl	C.debMaxLev(b)
+	cmp	$9, C.debMaxLev(b)
+	jne	C.cont
+
+	LOG	9, "debug=%u, testing...", 1
+	DebugA
+	mov	a, deP(bp)
+	DEBID	"TEST"
+	LOG	9, "progress"
+
+	mov	C.csP, b
+	mov	C.shP(b), a
+	push	S.act(a)
+	LOG	9, "shared act=%u"
+
+	jmp	C.ret
+# normal execution
+C.cont:
+	push	C.debMaxLev(b)
+	push	C.ssl(b)
+	push	C.pace.tv_nsec(b)
+	push	C.pace.tv_sec(b)
+	push	C.ttl(b)
+	push	C.rn(b)
+	push	C.mn(b)
+	push	D.prgNameP
+	LOG	1, "pgm=%s, mash nodes=%d, ring nodes=%d, ttl=%d, pacing=%ld.%09ld, SSL mask=0x%02x, debug=%u", 8
+	testl	$2, C.ssl(b)
+	jz	0f
+	push	C.caPathP(b)
+	push	C.cePathP(b)
+	LOG	1, "SSL path=%s, SSL CA path=%s"
+0:
+# create constellation processes RING/MASH, nonSSL/SSL
+	mov	$0, c				# iter counter
+	mov	C.ssl(b), d			# SSL mask (01b = noSSL, 10b = SSL, 11b = both)
+# iterate on SSL variants
+C.iterateOnSsl:
+	test	$1, d				# check lowest bit of mask
+	jz	C.nextSsslVar			# next SSL variant
+	pusha
+# create RING
+	SYS	fork
+	jnz	1f				# parent
+	popa
+	pushl	$Cn.ring			# RING topology
+	push	c				# use iter ctr as SSL switch
+	call	Constellation			# create RING constellation
+1:	push	a
+	LOG	5, "RING started in process %d"
+	lea 	4(sp), sp
+# create MASH
+	SYS	fork
+	jnz	1f				# parent
+	popa
+	pushl	$Cn.mash			# MASH topology
+	push	c				# use iter ctr as SSL switch
+	call	Constellation			# create MASH constellation
+1:	push	a
+	LOG	5, "MASH started in process %d"
+	lea 	4(sp), sp
+
+	popa
+C.nextSsslVar:
+	shr	$1, d				# shift to test next SSL bit
+	inc	c				# incr ctr
+	cmp	$2, c
+	jl	C.iterateOnSsl
+# wait for constellation processes completion
+	LOG	5, "waiting for constellations to terminate"
+	movl	$0, bad(bp)			# clear cummulative rc
+C.iterateOnWait:
+	lea	status(bp), a
+	push	a				# -->return status of task
+	call	wait
+	mov	a, pid(bp)			# save pid
+	cmp	$-1, a				# a task ended?
+	jne	0f				# yes
+	call	__errno_location
+	cmp	$10, (a)			# error == ECHILD ?
+	je	C.ret				# yes, no other subtasks
+	SYSERR	"wait"
+0:
+#	push	status(bp)			# status of task
+#	push	a				# pid
+#	LOG	5, "status returned from task %u: 0x%08x"
+	mov	status(bp), d
+	test	$0x7f, d
+	jnz	1f				# task killed, ABEND
+	and	$0xff00, d			# task exited, extract rc
+	shr	$8, d
+	or	d, bad(bp)			# accumulate rc
+	push	d				# task rc
+	push	pid(bp)				# task pid
+	LOG	5, "constellation task %u ended with exit(%d)"
+	jmp	C.iterateOnWait			# wait for other tasks
+1:	push	d
+	push	pid(bp)
+	LOG	5, "constellation task %u killed, status=0x%x", 2
+2:
+# ABEND
+	LOG	0, "ABEND, kill all tasks"
+	pushl	$15				# SIGTERM
+	pushl	$0				# all tasks
+#	call	kill
+	SYS	kill
+	pushl	$1
+	call	exit
+# normal end
+C.ret:	movl	C.csP, b			# -->CS vector
+	movl	C.shP(b), b			# -->Share
+	pushl	S.conns(b)			# no. of connections made
+	pushl	S.msgs(b)			# no. of messages sent
+	LOG	1, "END, forwards=%d, connections=%d", 2
+	push	bad(bp)
+	call	exit
+#-----------------------------------------------
+#	 C O N S T E L L A T I O N   O P E R A T I O N S
+#-----------------------------------------------
+	ARGS
+	DS	ssl				# ssl switch
+	DS	topo				# topology
+# returns:	nothing
+	PROLOC
+	DL	this, ConstellationL		# this Constellation instance
+	DL	thisP				# -->this Constellation
+	DL	deP				# -->Debug
+	DL	last				# last node#
+	DL	pid				# pid returned from wait
+	DL	stat				# stat returned from wait
+	DL	bad				# "some node BAD" exit indicator
+	DL	catched				# count of returned node tasks
+	DL	killed				# count of killed node tasks
+	EPILOC
+#-----------------------------------------------
+	.global Constellation
+Constellation:
+	PROLOG
+	lea	this(bp), b			# -->this Constellation
+	mov	b, thisP(bp)			# save -->this Constellation
+# set debid
+	lea	Cn.debug(b), a			# -->Debug
+	mov	a, deP(bp)			# save -->Debug locally
+	mov	C.csP, c			# -->CS
+	cmp	$Cn.ring, topo(bp)		# ring topology ?
+	je	0f
+	mov	C.mn(c), a			# num. of nodes
+	mov	a, Cn.nodes(b)
+	mov	C.mp0(c), a			# port # of fist node
+	mov	a, Cn.first(b)
+	movl	$Cn.mash, Cn.topo(b)
+	push	$8f
+	jmp	1f
+0:	mov	C.rn(c), a			# num. of nodes
+	mov	a, Cn.nodes(b)
+	mov	C.rp0(c), a			# port # of fist node
+	mov	a, Cn.first(b)
+	movl	$Cn.ring, Cn.topo(b)
+	push	$7f
+1:	cmp	$0, ssl(bp)			# SSL ?
+	jz	2f				# no
+	movl	$1, Cn.ssl(b)
+	addl	$500, Cn.first(b)		# first SSL port #
+	push	$6f
+	jmp	9f
+2:	movl	$0, Cn.ssl(b)
+	push	$5f
+	jmp	9f
+5:	.ascii	"non\0"
+6:	.ascii	"\0"
+7:	.ascii	"RING\0"
+8:	.ascii	"MASH\0"
+9:	DEBID	"%sSSL %s", 2
+# check # of nodes
+	movL	$0, bad(bp)
+	cmp	$1, Cn.nodes(b)			# num of nodes
+	jl	Cn.ret			# < 1 ? nothing to do
+	jg	0f
+	LOG	0, "1 node configuration not implemented yet"
+	jmp	Cn.ret
+0:	LOG	5, "initializing..."
+# determine divisor for random next node choise
+	mov     $1, a
+        shl     $31, a
+        not     a             			# MAX_INT
+        xor     d, d
+        divl    Cn.nodes(b)
+        mov     a, Cn.div(b)			# save divisor (MAX_INT / nodes)
+# allocate "forward" indicator shared by nodes in constellation
+	push	$0
+	push	$-1
+	push	$0x21				# PROT_READ | PROT_WRITE
+	push	$0x03				# MAP_SHARED | MAP_ANONYMOUS
+	push	$4
+	push	$0
+#	call	mmap
+#	cmp	$-1, a
+#	jne	0f
+#	SYSERR	"mmap"
+#0:	mov	thisP(bp), b
+	SYS	mmap
+	mov	a, Cn.forwP(b)			# save -->forw
+	movl	$1, (a)				# enable forwarding
+	push	Cn.nodes(b)
+	LOG	1, "%u nodes starting..."
+# start processes for all nodes in constellation
+	mov	Cn.first(b), d			# first node#
+	mov	d, c
+	add	Cn.nodes(b), c			# last node + 1
+Cn.iterateOnFork:
+	pusha
+#	call	fork
+	SYS	fork
+	cmp	$0, a
+	jnz	1f				# parent
+	popa
+	push	d				# node's port#
+	push	b				# -->Cnstlln
+	call	Node
+1:	mov	a, pid(bp)
+	popa
+	push	pid(bp)				# nodes's pid
+	push	d				# node's port#
+	LOG	4, "node %u established in process %u", 2
+	inc	d
+	cmp	d, c				# last node ?
+	jg	Cn.iterateOnFork		# no, continue forking
+# wait for completion of node processes
+	LOG	2, "all nodes established, waiting for them to terminate..."
+	movl	$0, bad(bp)			# accumulated return status of node tasks
+	movl	$0, killed(bp)			# num. of killed node tasks
+	movl	$0, catched(bp)			# num. of returned node tasks
+	lea	-12(sp), sp			# prepare space for loop
+Cn.iterateOnWait:
+	cmp	$0, bad(bp)			# constellation status still OK ?
+	je	1f				# yes
+	mov	Cn.forwP(b), a			# -->forwarding switch
+	movl	$0, (a)				# disable forwarding between nodes
+1:
+	lea	stat(bp), c
+	mov	c, (sp)				# -->return status of task
+	call	wait
+	mov	thisP(bp), b
+	cmp	$-1, a				# normal return from wait?
+	jne	0f				# yes
+	call	__errno_location
+	cmp	$10, (a)			# error == ECHILD ?
+	je	Cn.allFinished			# yes, no subtasks
+	SYSERR	"wait"
+0:
+	incl	catched(bp)
+	mov	a, pid(bp)			# save subtask's pid
+	mov	a, (sp)
+	mov	stat(bp), c			# subtask return status
+	mov	c, 4(sp)
+	test	$0x7f, c			# subtask ended by exit ?
+	jnz	2f				# no, killed
+	and	$0xff00, c			# extract subtask rc
+	jz	1f				# rc = 0
+	movl	$1, bad(bp)			# non zero rc, turn on BAD switch
+1:	shr	$8, c
+	mov	c, 4(sp)			# rc
+	mov	a, (sp)				# pid
+	LOG	4, "node process %u ended by exit(%d)"
+	jmp	Cn.iterateOnWait		# continue waiting for other subtasks
+2:	movl	$1, bad(bp)			# subtask killed, turn on BAD switch
+	incl	killed(bp)			# counter of killed
+	mov	c, 4(sp)			# status
+	mov	a, (sp)				# pid
+	LOG	4, "node process %u killed, status=0x%x"
+	jmp	Cn.iterateOnWait		# continue waiting for other subtasks
+
+# opers of all nodes finished
+Cn.allFinished:
+	push	killed(bp)
+	push	catched(bp)
+	mov	thisP(bp), b
+	push	Cn.nodes(b)
+	cmp	$0, bad(bp)			# all nodes ended OK ?
+	je	0f				# yes
+	push	$7f
+	jmp	9f
+0:	push	$8f
+	jmp	9f
+7:	.ascii	"with ERROR\0"
+8:	.ascii	"OK\0"
+9:	LOG	1, "ENDED %s, %u spawned, %u catched, %u killed"
+Cn.ret:
+	push	bad(bp)
+	call	exit
+#-----------------------------------------------
+#	G E T   I N T   V A L U E S   F R O M   E N V
+#-----------------------------------------------
+	ARGS
+	DS	key				# -->env key string
+# returns	int value or 0 if not found
+	PROLOC
+	EPILOC
+#-----------------------------------------------
+C.getArg:
+	PROLOG
+	pushl	key(bp)				# -->env key string
+	call	getenv				# get value
+	test	a, a
+	jz	0f				# not found in ENV
+	push	a
+	call	atoi				# convert to int
+0:
+	EPILOG_R
+#-----------------------------------------------
+#	A B N O R M A L   E N D
+#-----------------------------------------------
+	ARGS
+	DS	deP				# -->Debug
+	PROLOC
+	EPILOC
+#-----------------------------------------------
+	.globl	C.abend
+C.abend:
+	PROLOG
+	LOG	0, "ABEND"
+	push	$15				# SIGTERM
+	push	$0				# kill all
+#	call	kill
+	SYS	kill
+	push	$1
+	call	exit
+#-----------------------------------------------
+	.end