parsav  Diff

Differences From Artifact [c954b701da]:

To Artifact [c623f2c8c5]:


4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
..
45
46
47
48
49
50
51


52
53
54



55
56
57
58
59
60
61
..
67
68
69
70
71
72
73


















74
75
76
77
78
79
80
..
92
93
94
95
96
97
98












99
100
101
102
103
104
105
106
107
108



109
110
111
112
113
114
115
...
118
119
120
121
122
123
124
125










126
127
128
129
130
131
132
133
134
135
136
137
138






139




140
141
142
143
144






145

146
147
148
149

150
151
152
153
154
155
156
...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180









181
182

183
184
185
186
187
188
189
local ctloptions = lib.cmdparse({
	version = {'V', 'display information about the binary build and exit'};
	verbose = {'v', 'increase logging verbosity', inc=1};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'};
	backend_file = {'B', 'init from specified backend file', consume=1};
	backend = {'b', 'operate on only the selected backend'};
	instance = {'i', 'specify the instance to control by name', consume=1};

	all = {'A', 'affect all running instances'};
}, { subcmd = 1 })

local pbasic = lib.cmdparse {
	help = {'h', 'display this list'}
}
local subcmds = {
}

local ctlcmds = {
	{ 'start', 'start a new instance of the server' };
	{ 'stop', 'stop a running instance' };

	{ 'attach', 'capture log output from a running instance' };
	{ 'db', 'set up and manage the database' };
	{ 'user', 'manage users, privileges, and credentials'};
	{ 'mkroot <handle>', 'establish a new root user with the given handle' };
	{ 'actor <xid> purge-all', 'remove all traces of a user from the database (except local user credentials -- use \27[1mauth all purge\27[m to prevent a user from accessing the instance)' };
	{ 'actor <xid> create', 'instantiate a new actor' };
	{ 'actor <xid> bestow <epithet>', 'bestow an epithet upon an actor' };
................................................................................
			v[2]
		)
	end
	return str
end

local struct idelegate {


	all: bool
	src: &lib.store.source
	srv: &lib.srv.overlord



}
idelegate.metamethods.__methodmissing = macro(function(meth, self, ...)
	local expr = {...}
	local rt
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
................................................................................
		if self.all
			then r=self.srv:[meth]([expr])
			elseif self.src ~= nil then r=self.src:[meth]([expr])
			else lib.bail('no data source specified')
		end
	in r end
end)



















local terra gensec(sdest: rawstring)
	var dest = [&uint8](sdest)
	lib.crypt.spray(dest,64)
	for i=0,64 do dest[i] = dest[i] % (0x7e - 0x20) + 0x20 end
	dest[64] = 0
end
................................................................................
		else tmppw[i] = tmppw[i] + 0x30 end
	end
	lib.dbg('assigning temporary password')
	dlg:auth_create_pw(uid, reset, pstr {
		ptr = [rawstring](tmppw), ct = 32
	})
end













local emp = lib.ipc.global_emperor
local terra entry_mgtool(argc: int, argv: &rawstring): int
	if argc < 1 then lib.bail('bad invocation!') end

	lib.noise.init(2)
	[lib.init]

	var srv: lib.srv.overlord
	var dlg = idelegate { srv = &srv, src = nil }




	var mode: ctloptions
	mode:parse(argc,argv) defer mode:free()
	if mode.version then version() return 0 end
	if mode.help then
		[ lib.emit(false, 1, 'usage: ', `argv[0], ' ', ctloptions.helptxt.flags, ' <cmd> [<args>…]', ctloptions.helptxt.opts, cmdhelp(ctlcmds)) ]
		return 0
................................................................................
	var cnf: rawstring
	if mode.backend_file ~= nil
		then cnf = @mode.backend_file
		else cnf = lib.proc.getenv('parsav_backend_file')
	end
	if cnf == nil then cnf = [config.prefix_conf .. "/backend.conf"] end
	if mode.all then dlg.all = true else
		-- iterate through and pick the right backend










	end

	if mode.arglist.ct == 0 then lib.bail('no command') return 1 end

	if lib.str.cmp(mode.arglist(0),'start') ~= 0 then
	-- hack to save us some pain around forking
		emp = lib.ipc.emperor.mk(false)
	end
	defer emp:release()

	if lib.str.cmp(mode.arglist(0),'attach') == 0 then
	elseif lib.str.cmp(mode.arglist(0),'start') == 0 then
		mode.arglist(0) = "-";






		var chargv = mode.arglist.ptr




		var lsr = lib.ipc.listener.mk()
		var chpid = lib.proc.fork()
		if chpid == 0 then
			lsr:release()
			--lib.proc.daemonize(1,0)






			lib.io.close(0) lib.io.close(1) lib.io.close(2)

			lib.proc.exec([config.prefix_bin .. '/parsavd'], chargv)
			lib.proc.execp([config.prefix_bin .. '/parsavd'], chargv)
			lib.ipc.notify_parent(lib.ipc.signals.state_fail_find)
			lib.bail('cannot find parsav program')

		else
			lib.report('starting parsav daemon')
			while true do
				var sig = lsr:block()
				if sig.system then lib.dbg('got system signal') end
				if sig.from == chpid then
					if sig.system and sig.sig == lib.ipc.signals.sys_child then 
................................................................................
					else lib.warn('got unrecognized signal, ignoring')
					end
				end
			end
			lsr:release() -- just because i feel distinctly uncomfortable leaving it out
		end
	elseif lib.str.cmp(mode.arglist(0),'stop') == 0 then
		var acks = emp:mallack()
		emp:decree(0,nil, lib.ipc.cmd.stop, 0, &acks(0)) -- TODO targeting
		for i=0,acks.ct do
			if acks(i).success then
				lib.io.fmt('instance %llu successfully stepped down\n', acks(i).clid)
			else
				lib.io.fmt('instance %llu reports failure to halt\n', acks(i).clid)
			end









		end
		acks:free()

	else
		if lib.str.cmp(mode.arglist(0),'db') == 0 then
			var dbmode: pbasic dbmode:parse(mode.arglist.ct, &mode.arglist(0))
			if dbmode.help then
				[ lib.emit(false, 1, 'usage: ', `argv[0], ' db ', dbmode.type.helptxt.flags, ' <cmd> [<args>…]', dbmode.type.helptxt.opts, cmdhelp {
					{ 'db init <domain>', 'initialize backend databases (or a single specified database) with the necessary schema and structures for the given FQDN' };
					{ 'db vacuum', 'delete old remote content from the database' };







|
>
|











>







 







>
>



>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>









|
>
>
>







 







|
>
>
>
>
>
>
>
>
>
>












|
>
>
>
>
>
>
|
>
>
>
>




|
>
>
>
>
>
>
|
>
|
|


>







 







|
|
|
|
|
<
<
|
>
>
>
>
>
>
>
>
>
|
|
>







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
..
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
234
235
236
237
238
239
240
241
242
243
244
245


246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
local ctloptions = lib.cmdparse({
	version = {'V', 'display information about the binary build and exit'};
	verbose = {'v', 'increase logging verbosity', inc=1};
	quiet = {'q', 'do not print to standard out'};
	help = {'h', 'display this list'};
	backend_file = {'B', 'init from specified backend file', consume=1};
	backend = {'b', 'operate on only the selected backend'};
	instance_id = {'i', 'specify the instance to control by name', consume=1};
	instance_serial = {'I', 'specify the instance to control by serial', consume=1};
	all = {'A', 'affect all running instances/backends'};
}, { subcmd = 1 })

local pbasic = lib.cmdparse {
	help = {'h', 'display this list'}
}
local subcmds = {
}

local ctlcmds = {
	{ 'start', 'start a new instance of the server' };
	{ 'stop', 'stop a running instance' };
	{ 'ls', 'list all running instances' };
	{ 'attach', 'capture log output from a running instance' };
	{ 'db', 'set up and manage the database' };
	{ 'user', 'manage users, privileges, and credentials'};
	{ 'mkroot <handle>', 'establish a new root user with the given handle' };
	{ 'actor <xid> purge-all', 'remove all traces of a user from the database (except local user credentials -- use \27[1mauth all purge\27[m to prevent a user from accessing the instance)' };
	{ 'actor <xid> create', 'instantiate a new actor' };
	{ 'actor <xid> bestow <epithet>', 'bestow an epithet upon an actor' };
................................................................................
			v[2]
		)
	end
	return str
end

local struct idelegate {
	emperor: &lib.ipc.emperor

	all: bool
	src: &lib.store.source
	srv: &lib.srv.overlord

	sid: uint64
	iname: rawstring
}
idelegate.metamethods.__methodmissing = macro(function(meth, self, ...)
	local expr = {...}
	local rt
	for _,f in pairs(lib.store.backend.entries) do
		local fn = f.field or f[1]
		local ft = f.type or f[2]
................................................................................
		if self.all
			then r=self.srv:[meth]([expr])
			elseif self.src ~= nil then r=self.src:[meth]([expr])
			else lib.bail('no data source specified')
		end
	in r end
end)

terra idelegate:ipc_send(cmd: lib.ipc.cmd.t, operand: uint64)
	var emp = self.emperor
	var acks: lib.mem.ptr(lib.ipc.ack)
	if self.sid == 0 and self.iname == nil then
		if not self.all and emp:countpeers() > 1 then
			lib.bail('either specify the instance to control or pass --all to control all instances')
		end
		acks = emp:mallack()
		emp:decree(0,nil, cmd, operand, &acks(0)) -- TODO targeting
	else
		acks = lib.mem.heapa(lib.ipc.ack, 1)
		if not emp:decree(self.sid, self.iname, cmd, operand, &acks(0)) then
			acks:free()
		end
	end
	return acks
end

local terra gensec(sdest: rawstring)
	var dest = [&uint8](sdest)
	lib.crypt.spray(dest,64)
	for i=0,64 do dest[i] = dest[i] % (0x7e - 0x20) + 0x20 end
	dest[64] = 0
end
................................................................................
		else tmppw[i] = tmppw[i] + 0x30 end
	end
	lib.dbg('assigning temporary password')
	dlg:auth_create_pw(uid, reset, pstr {
		ptr = [rawstring](tmppw), ct = 32
	})
end

local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring)
	var decbuf: int8[21]
	for i=0,acks.ct do
		var num = lib.math.decstr(acks(i).clid, &decbuf[20])
		if acks(i).success then
			lib.report('instance #',num,' reports successful ',rep)
		else
			lib.report('instance #',num,' reports failed ',rep)
		end
	end
end

local emp = lib.ipc.global_emperor
local terra entry_mgtool(argc: int, argv: &rawstring): int
	if argc < 1 then lib.bail('bad invocation!') end

	lib.noise.init(2)
	[lib.init]

	var srv: lib.srv.overlord
	var dlg = idelegate {
		emperor = &emp, srv = &srv;
		src = nil, sid = 0, iname = nil, all = false;
	}

	var mode: ctloptions
	mode:parse(argc,argv) defer mode:free()
	if mode.version then version() return 0 end
	if mode.help then
		[ lib.emit(false, 1, 'usage: ', `argv[0], ' ', ctloptions.helptxt.flags, ' <cmd> [<args>…]', ctloptions.helptxt.opts, cmdhelp(ctlcmds)) ]
		return 0
................................................................................
	var cnf: rawstring
	if mode.backend_file ~= nil
		then cnf = @mode.backend_file
		else cnf = lib.proc.getenv('parsav_backend_file')
	end
	if cnf == nil then cnf = [config.prefix_conf .. "/backend.conf"] end
	if mode.all then dlg.all = true else
		-- iterate through and pick the right backend, if one is indicated 
	end
	
	if mode.instance_id ~= nil and mode.instance_serial ~= nil then
		lib.bail('conflicting flags passed')
	end

	if mode.instance_id ~= nil then
		dlg.iname = @mode.instance_id
	elseif mode.instance_serial ~= nil then
		-- decode serial
	end

	if mode.arglist.ct == 0 then lib.bail('no command') return 1 end

	if lib.str.cmp(mode.arglist(0),'start') ~= 0 then
	-- hack to save us some pain around forking
		emp = lib.ipc.emperor.mk(false)
	end
	defer emp:release()

	if lib.str.cmp(mode.arglist(0),'attach') == 0 then
	elseif lib.str.cmp(mode.arglist(0),'start') == 0 then
		var smode: lib.cmdparse {
			help = {'h', 'display this list'};
			log = {'l', 'send server\'s logging output to a file', consume=1};
			pid = {'p', 'report PID of launched process'};
			instance_serial = {'s', 'report the instance serial of the launched process'};
			keep_attached = {'k', 'don\'t detach from the calling terminal until a successful startup (or ever, if passed twice)', inc=1};
		}
		smode:parse(mode.arglist.ct,mode.arglist.ptr)
		if smode.help then
			[ lib.emit(false, 1, 'usage: ', `argv[0], ' start ', smode.type.helptxt.flags, ' [<instance args>…] [-- <instance flags>…]', smode.type.helptxt.opts) ]
			return 1
		end
		var lsr = lib.ipc.listener.mk()
		var chpid = lib.proc.fork()
		if chpid == 0 then
			lsr:release()
			var chargv = lib.mem.heapa(rawstring, smode.arglist.ct + 2)
			chargv(0) = '-'
			for i = 1, chargv.ct - 1 do
				chargv(i) = smode.arglist(i-1)
			end
			chargv(chargv.ct-1) = nil
			if smode.keep_attached == 0 then
				lib.io.close(0) lib.io.close(1) lib.io.close(2)
			end
			lib.proc.exec([config.prefix_bin .. '/parsavd'], chargv.ptr)
			lib.proc.execp([config.prefix_bin .. '/parsavd'], chargv.ptr)
			lib.ipc.notify_parent(lib.ipc.signals.state_fail_find)
			lib.bail('cannot find parsav program')
			-- chargv:free()
		else
			lib.report('starting parsav daemon')
			while true do
				var sig = lsr:block()
				if sig.system then lib.dbg('got system signal') end
				if sig.from == chpid then
					if sig.system and sig.sig == lib.ipc.signals.sys_child then 
................................................................................
					else lib.warn('got unrecognized signal, ignoring')
					end
				end
			end
			lsr:release() -- just because i feel distinctly uncomfortable leaving it out
		end
	elseif lib.str.cmp(mode.arglist(0),'stop') == 0 then
		if mode.arglist.ct ~= 1 then goto cmderr end
		var acks = dlg:ipc_send(lib.ipc.cmd.stop, 0)
		if acks:ref() then
			ipc_report(acks, 'step-down')
			acks:free()


		end
	elseif lib.str.cmp(mode.arglist(0),'ls') == 0 then
		if mode.arglist.ct ~= 1 then goto cmderr end
		if dlg.sid == 0 and dlg.iname == nil then dlg.all = true end
		var acks = dlg:ipc_send(lib.ipc.cmd.enumerate, 0)
		if acks:ref() then
			for i=0,acks.ct do
				var decbuf: int8[21]
				var num = lib.math.decstr(acks(i).clid, &decbuf[20])
				[ lib.emit(true,1, '\27[1m(', `num, ')\27[m ',`&acks(i).iname[0],' \27[1;32mactive\27[m') ]
			end
			acks:free()
		else lib.bail('no active instances') end
	else
		if lib.str.cmp(mode.arglist(0),'db') == 0 then
			var dbmode: pbasic dbmode:parse(mode.arglist.ct, &mode.arglist(0))
			if dbmode.help then
				[ lib.emit(false, 1, 'usage: ', `argv[0], ' db ', dbmode.type.helptxt.flags, ' <cmd> [<args>…]', dbmode.type.helptxt.opts, cmdhelp {
					{ 'db init <domain>', 'initialize backend databases (or a single specified database) with the necessary schema and structures for the given FQDN' };
					{ 'db vacuum', 'delete old remote content from the database' };