parsav  Check-in [87731d4007]

Overview
Comment:add ipc backbone
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 87731d4007d725922fe0a577842d943573ecfbecc36a385055c3d6b62c1c2061
User & Date: lexi on 2020-12-29 14:35:10
Other Links: manifest | tags
Context
2020-12-29
14:35
check in missing file check-in: 5a4f99fb55 user: lexi tags: trunk
14:35
add ipc backbone check-in: 87731d4007 user: lexi tags: trunk
00:57
add privilege control verbs check-in: a64461061f user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [f0f9593494] to [0f1425913d].

215
216
217
218
219
220
221








222
223
224
225
226
227
228
...
876
877
878
879
880
881
882
883
884
885
886
887
888

889
890












891
892
893
894
895
896
			insert into parsav_auth (uid, name, kind, cred) values (
				$1::bigint,
				(select handle from parsav_actors where id = $1::bigint),
				'pw-sha256', $2::bytea
			)
		]]
	};









	post_create = {
		params = {uint64, rawstring, rawstring, rawstring}, sql = [[
			insert into parsav_posts (
				author, subject, acl, body,
				posted, discovered,
				circles, mentions
................................................................................

	auth_create_pw = [terra(
		src: &lib.store.source,
		uid: uint64,
		reset: bool,
		pw: lib.mem.ptr(int8)
	): {}
		-- TODO impl reset support
		var hash: uint8[lib.crypt.algsz.sha256]
		if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(lib.crypt.alg.sha256.id),
			[&uint8](pw.ptr), pw.ct, &hash[0]) ~= 0 then
			lib.bail('cannot hash password')
		end

		queries.auth_create_pw.exec(src, uid, [lib.mem.ptr(uint8)] {ptr = &hash[0], ct = [hash.type.N]})
	end];













	actor_auth_register_uid = nil; -- not necessary for view-based auth

}

return b







>
>
>
>
>
>
>
>







 







<





>


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






215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
...
884
885
886
887
888
889
890

891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
			insert into parsav_auth (uid, name, kind, cred) values (
				$1::bigint,
				(select handle from parsav_actors where id = $1::bigint),
				'pw-sha256', $2::bytea
			)
		]]
	};

	auth_purge_type = {
		params = {rawstring, uint64, rawstring}, cmd = true, sql = [[
			delete from parsav_auth where
				((uid = 0 and name = $1::text) or uid = $2::bigint) and
				kind like $3::text
		]]
	};

	post_create = {
		params = {uint64, rawstring, rawstring, rawstring}, sql = [[
			insert into parsav_posts (
				author, subject, acl, body,
				posted, discovered,
				circles, mentions
................................................................................

	auth_create_pw = [terra(
		src: &lib.store.source,
		uid: uint64,
		reset: bool,
		pw: lib.mem.ptr(int8)
	): {}

		var hash: uint8[lib.crypt.algsz.sha256]
		if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(lib.crypt.alg.sha256.id),
			[&uint8](pw.ptr), pw.ct, &hash[0]) ~= 0 then
			lib.bail('cannot hash password')
		end
		if reset then queries.auth_purge_type.exec(src, nil, uid, 'pw-%') end
		queries.auth_create_pw.exec(src, uid, [lib.mem.ptr(uint8)] {ptr = &hash[0], ct = [hash.type.N]})
	end];

	auth_purge_pw = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
		queries.auth_purge_type.exec(src, handle, uid, 'pw-%')
	end];

	auth_purge_otp = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
		queries.auth_purge_type.exec(src, handle, uid, 'otp-%')
	end];

	auth_purge_trust = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
		queries.auth_purge_type.exec(src, handle, uid, 'trust')
	end];

	actor_auth_register_uid = nil; -- not necessary for view-based auth

}

return b

Modified cmdparse.t from [bfedd61eec] to [49c267b075].

61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
88
89
90
91
92
93
94

95
96
97
98
99
100
101
		end
		terra options:free() self.arglist:free() end
		options.methods.parse = terra([self], [argc], [argv])
			[init]
			var parseopts = true
			var [optstack] = 0
			var [subcmd] = [ opts.subcmd or 0 ]
			self.arglist = lib.mem.heapa(rawstring, argc)
			var finalargc = 0
			for [idx]=1,argc do
				var [arg] = argv[idx]
				if optstack > 0 then optstack = optstack - 1 goto [skip] end
				if arg[0] == @'-' and parseopts then
					if arg[1] == @'-' then -- long option
						if arg[2] == 0 then -- last option
................................................................................
						subcmd = subcmd - 1
						if subcmd == 0 then parseopts = false end
					end
				end
				::[skip]::
			end
			[verifiers]

			if finalargc == 0 then self.arglist:free()
							  else self.arglist:resize(finalargc) end
		end
		options.helptxt = { opts = helpstr, flags = flagstr }
	end
	return options
end







|







 







>







61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
		end
		terra options:free() self.arglist:free() end
		options.methods.parse = terra([self], [argc], [argv])
			[init]
			var parseopts = true
			var [optstack] = 0
			var [subcmd] = [ opts.subcmd or 0 ]
			self.arglist = lib.mem.heapa(rawstring, argc + 1)
			var finalargc = 0
			for [idx]=1,argc do
				var [arg] = argv[idx]
				if optstack > 0 then optstack = optstack - 1 goto [skip] end
				if arg[0] == @'-' and parseopts then
					if arg[1] == @'-' then -- long option
						if arg[2] == 0 then -- last option
................................................................................
						subcmd = subcmd - 1
						if subcmd == 0 then parseopts = false end
					end
				end
				::[skip]::
			end
			[verifiers]
			self.arglist.ptr[finalargc] = nil -- for lazy-ass argv compat
			if finalargc == 0 then self.arglist:free()
							  else self.arglist:resize(finalargc) end
		end
		options.helptxt = { opts = helpstr, flags = flagstr }
	end
	return options
end

Modified config.lua from [0c09bec0e6] to [931922a3e1].

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
65
66
67
68
69
70
71

72
73
74
75
76
77
78
	tgthf     = u.tobool(default('parsav_arch_armhf',true)); 
	doc       = {
		online  = u.tobool(default('parsav_online_documentation',true)); 
		offline = u.tobool(default('parsav_offline_documentation',true)); 
	};
	outform   = default('parsav_emit_type', 'o');
	endian    = default('parsav_arch_endian', 'little');
	prefix    = default('parsav_install_prefix', './');
	build     = {
		id = u.rndstr(6);
		release = u.ingest('release');
		when = os.date();
	};
	feat = {};
	debug = u.tobool(default('parsav_enable_debug',true)); 
................................................................................
	}):gsub("^'(.*)'$", '%1')
end
conf.os    = default('parsav_host_os', default_os)
conf.tgtos = default('parsav_target_os', default_os)
conf.posix = posixes[conf.os]
conf.exe   = u.tobool(default('parsav_link',not conf.tgttrip)) -- turn off for partial builds
conf.prefix_conf = default('parsav_install_prefix_cfg', conf.prefix)

conf.prefix_static = default('parsav_install_prefix_static', nil)
conf.build.origin = coalesce(
	os.getenv('parsav_builder'),
	string.format('%s@%s', coalesce (
		os.getenv('USER'),
		u.exec{'whoami'}
	), u.exec{'hostname'}) -- whoami and hostname are present on both windows & unix







|







 







>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	tgthf     = u.tobool(default('parsav_arch_armhf',true)); 
	doc       = {
		online  = u.tobool(default('parsav_online_documentation',true)); 
		offline = u.tobool(default('parsav_offline_documentation',true)); 
	};
	outform   = default('parsav_emit_type', 'o');
	endian    = default('parsav_arch_endian', 'little');
	prefix    = default('parsav_install_prefix', '.');
	build     = {
		id = u.rndstr(6);
		release = u.ingest('release');
		when = os.date();
	};
	feat = {};
	debug = u.tobool(default('parsav_enable_debug',true)); 
................................................................................
	}):gsub("^'(.*)'$", '%1')
end
conf.os    = default('parsav_host_os', default_os)
conf.tgtos = default('parsav_target_os', default_os)
conf.posix = posixes[conf.os]
conf.exe   = u.tobool(default('parsav_link',not conf.tgttrip)) -- turn off for partial builds
conf.prefix_conf = default('parsav_install_prefix_cfg', conf.prefix)
conf.prefix_bin = default('parsav_install_prefix_cfg', conf.prefix)
conf.prefix_static = default('parsav_install_prefix_static', nil)
conf.build.origin = coalesce(
	os.getenv('parsav_builder'),
	string.format('%s@%s', coalesce (
		os.getenv('USER'),
		u.exec{'whoami'}
	), u.exec{'hostname'}) -- whoami and hostname are present on both windows & unix

Modified crypt.t from [9b6529621c] to [f5b057e4fa].

58
59
60
61
62
63
64


65
66
67
68
69
70
71
		for i=0,sz do dest[i] = [uint8](rnd()) end
		return sz
	end
end

m.random = macro(function(typ, from, to)
	local ty = typ:astype()


	return quote
		var v: ty
		m.spray([&uint8](&v), sizeof(ty))
		v = v % (to - from) + from -- only works with unsigned!!
	in v end
end)








>
>







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
		for i=0,sz do dest[i] = [uint8](rnd()) end
		return sz
	end
end

m.random = macro(function(typ, from, to)
	local ty = typ:astype()
	from = from or 0
	to = to or ty:max()
	return quote
		var v: ty
		m.spray([&uint8](&v), sizeof(ty))
		v = v % (to - from) + from -- only works with unsigned!!
	in v end
end)

Modified mgtool.t from [54eca1a845] to [c954b701da].

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

52
53
54




55


56
57
58
59
60
61
62
...
100
101
102
103
104
105
106

107
108
109
110
111
112
113
114
115
116
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
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
182
183
184
185
186
187
188
189






190
191
192
193
194
195
196
...
245
246
247
248
249
250
251
252








253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
...
285
286
287
288
289
290
291



292
293
294
295
296
297









298
299
300
301
302
303
304
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 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' };
	{ 'db extract (<artifact>|<post>/<attachment number>)', 'extracts an attachment artifact from the database and prints it to standard out' };
	{ 'db excise <artifact>', 'extracts an attachment artifact from the database and prints it to standard out' };
	{ 'db obliterate', 'completely purge all parsav-related content and structure from the database, destroying all user content (requires confirmation)' };
	{ 'db insert', 'reads a file from standard in and inserts it into the attachment database, printing the resulting ID' };
	{ 'mkroot <handle>', 'establish a new root user with the given handle' };
	{ 'user <handle> auth <type> new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' };
	{ 'user <handle> auth <type> reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' };
	{ 'user <handle> auth (<type>|all) purge', 'delete all credentials that would allow this user to log in (where possible)' };
	{ 'user <handle> (grant|revoke) (<priv>|all)', 'grant or revoke a specific power to or from a user' };
	{ 'user <handle> emasculate', 'strip all administrative powers from a user' };
	{ 'user <handle> suspend [<timespec>]', '(e.g. \27[1muser jokester suspend 5d 6h 7m 3s\27[m to suspend "jokester" for five days, six hours, seven minutes, and three seconds) suspend a user'};
	{ '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' };
	{ 'conf set <setting> <value>', 'add or a change a server configuration parameter to the database' };
	{ 'conf get <setting>', 'report the value of a server setting' };
	{ 'conf reset <setting>', 'reset a server setting to its default value' };
	{ 'conf refresh', 'instruct an instance to refresh its configuration cache' };
	{ 'conf chsec', 'reset the server secret, invalidating all authentication cookies' };
	{ 'serv dl', 'initiate an update cycle over foreign actors' };
	{ 'tl', 'print the current local timeline to standard out' };
	{ 'be pgsql setup-auth (managed|unmanaged)', '(PGSQL backends) select the authentication strategy to use' };
}

local ctlcmdhelp = 'commands:\n'

for _, v in ipairs(ctlcmds) do
	ctlcmdhelp = ctlcmdhelp .. string.format (
		'    \27[1m%s\27[m: %s\n', v[1]:gsub('(<%w+>)','\27[36m%1\27[;1m'), v[2]




	)


end

local struct idelegate {
	all: bool
	src: &lib.store.source
	srv: &lib.srv.overlord
}
................................................................................
	end
	lib.dbg('assigning temporary password')
	dlg:auth_create_pw(uid, reset, pstr {
		ptr = [rawstring](tmppw), ct = 32
	})
end


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, ctlcmdhelp) ]
		return 0
	end

	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 = "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),'attach') == 0 then
	elseif lib.str.cmp(mode.arglist(0),'start') == 0 then


































	elseif lib.str.cmp(mode.arglist(0),'stop') == 0 then










	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) ]







				return 1
			end
			if dbmode.arglist.ct < 1 then goto cmderr end

			srv:setup(cnf) 
			if lib.str.cmp(dbmode.arglist(0),'init') == 0 and dbmode.arglist.ct == 2 then
				lib.report('initializing new database structure for domain ', dbmode.arglist(1))
................................................................................
				var confirmstrs = array(
					'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'eta', 'nu', 'kappa'
				)
				var cfmstr: int8[64] cfmstr[0] = 0
				var tdx = lib.osclock.time(nil) / 60
				for i=0,3 do
					if i ~= 0 then lib.str.cat(&cfmstr[0], '-') end
					lib.str.cat(&cfmstr[0], confirmstrs[(tdx + 49*i) % [confirmstrs.type.N]])
				end

				if dbmode.arglist.ct == 1 then
					lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
				elseif dbmode.arglist.ct == 2 then
					if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then
						lib.warn('completely obliterating all data!')
................................................................................
			srv:conprep(lib.store.prepmode.conf)
			var cfmode: lib.cmdparse {
				help = {'h','display this list'};
				no_notify = {'n', "don't instruct the server to refresh its configuration cache after making changes; useful for \"transactional\" configuration changes."};
			}
			cfmode:parse(mode.arglist.ct, &mode.arglist(0))
			if cfmode.help then
				[ lib.emit(false, 1, 'usage: ', `argv[0], ' conf ', cfmode.type.helptxt.flags, ' <cmd> [<args>…]', cfmode.type.helptxt.opts) ]






				return 1
			end
			if cfmode.arglist.ct < 1 then goto cmderr end

			if cfmode.arglist.ct == 1 then
				if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
					var sec: int8[65] gensec(&sec[0])
................................................................................
						pwset(dlg, &tmppw, ruid, false)
						lib.report('temporary root pw: ', {&tmppw[0], 32})
					end
				else goto cmderr end
			elseif lib.str.cmp(mode.arglist(0),'user') == 0 then
				var umode: pbasic umode:parse(mode.arglist.ct, &mode.arglist(0))
				if umode.help then
					[ lib.emit(false, 1, 'usage: ', `argv[0], ' user ', umode.type.helptxt.flags, ' <handle> <cmd> [<args>…]', umode.type.helptxt.opts) ]








					return 1
				end
				if umode.arglist.ct >= 3 then
					var grant = lib.str.cmp(umode.arglist(1),'grant') == 0
					var handle = umode.arglist(0)
					var usr = dlg:actor_fetch_xid(pstr {ptr=handle, ct=lib.str.sz(handle)})
					if not usr then lib.bail('unknown handle') end
					if grant or lib.str.cmp(umode.arglist(1),'revoke') == 0 then

						var newprivs = usr.ptr.rights.powers
						var map = array([lib.store.privmap])
						if umode.arglist.ct == 3 and lib.str.cmp(umode.arglist(2),'all') == 0 then
							if grant
								then newprivs:fill()
								else newprivs:clear()
							end
................................................................................
						end

						usr.ptr.rights.powers = newprivs
						dlg:actor_save_privs(usr.ptr)
					elseif lib.str.cmp(umode.arglist(1),'auth') == 0 and umode.arglist.ct == 4 then
						var reset = lib.str.cmp(umode.arglist(3),'reset') == 0
						if reset or lib.str.cmp(umode.arglist(3),'new') == 0 then



							if lib.str.cmp(umode.arglist(2),'pw') == 0 then
								var tmppw: int8[33]
								pwset(dlg, &tmppw, usr.ptr.id, reset)
								lib.report('new temporary password for ',usr.ptr.handle,': ', {&tmppw[0], 32})
							else lib.bail('unknown credential type') end
						elseif lib.str.cmp(umode.arglist(3),'purge') == 0 then









						else goto cmderr end
					else goto cmderr end
				else goto cmderr end
			elseif lib.str.cmp(mode.arglist(0),'actor') == 0 then
			elseif lib.str.cmp(mode.arglist(0),'tl') == 0 then
			elseif lib.str.cmp(mode.arglist(0),'serv') == 0 then
			else goto cmderr end







|
|
<
<
<
<

<
<
<
<
<
<



|
<
<
<
<





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







 







>



|









|


>





|





>
>
>
>
>
>
>


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

>
>
>
>
>
>
>
>
>
>




|
>
>
>
>
>
>
>







 







|







 







|
>
>
>
>
>
>







 







|
>
>
>
>
>
>
>
>






<

>







 







>
>
>






>
>
>
>
>
>
>
>
>







18
19
20
21
22
23
24
25
26




27






28
29
30
31




32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
156
157
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
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333
334
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
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' };
	{ 'conf', 'manage the server configuration'};




	{ 'serv dl', 'initiate an update cycle over foreign actors' };
	{ 'tl', 'print the current local timeline to standard out' };
	{ 'be pgsql setup-auth (managed|unmanaged)', '(PGSQL backends) select the authentication strategy to use' };
}

local cmdhelp = function(tbl)
	local str = '\ncommands:\n'
	for _, v in ipairs(tbl) do
		str = str .. string.format (
			'    \27[1m%s\27[m: %s\n',
			v[1]
				:gsub('([%(%)|%[%]])', '\27[34m%1\27[;1m')
				:gsub('(<.->)','\27[36m%1\27[;1m'),
			v[2]
		)
	end
	return str
end

local struct idelegate {
	all: bool
	src: &lib.store.source
	srv: &lib.srv.overlord
}
................................................................................
	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
	end
	if mode.quiet then lib.noise.level = 0 end
	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 
						lib.warn('parsavd failed to start')
						return 0
					elseif sig.sig == lib.ipc.signals.notify_state_change and
					       sig.event == lib.ipc.signals.state_success then
						lib.report('parsavd successfully started')
						return 0
					elseif sig.sig == lib.ipc.signals.notify_state_change and
					       sig.event == lib.ipc.signals.state_fail_find then
						lib.bail('parsavd could not be found')
					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' };
					{ 'db extract (<artifact>|<post>/<attachment number>)', 'extracts an attachment artifact from the database and prints it to standard out' };
					{ 'db excise (<artifact>|<post>/<attachment number>)', 'removes an undesirable artifact from the database' };
					{ 'db obliterate [<confirmation code>]', 'completely purge all parsav-related content and structure from the database, destroying all user content (requires confirmation)' };
					{ 'db insert', 'reads a file from standard in and inserts it into the attachment database, printing the resulting ID' };
				}) ]
				return 1
			end
			if dbmode.arglist.ct < 1 then goto cmderr end

			srv:setup(cnf) 
			if lib.str.cmp(dbmode.arglist(0),'init') == 0 and dbmode.arglist.ct == 2 then
				lib.report('initializing new database structure for domain ', dbmode.arglist(1))
................................................................................
				var confirmstrs = array(
					'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'eta', 'nu', 'kappa'
				)
				var cfmstr: int8[64] cfmstr[0] = 0
				var tdx = lib.osclock.time(nil) / 60
				for i=0,3 do
					if i ~= 0 then lib.str.cat(&cfmstr[0], '-') end
					lib.str.cat(&cfmstr[0], confirmstrs[(tdx ^ (173*i)) % [confirmstrs.type.N]])
				end

				if dbmode.arglist.ct == 1 then
					lib.bail('you are attempting to completely obliterate all data! make sure you have selected your target correctly. if you really want to do this, pass the confirmation string ', &cfmstr[0])
				elseif dbmode.arglist.ct == 2 then
					if lib.str.cmp(dbmode.arglist(1), cfmstr) == 0 then
						lib.warn('completely obliterating all data!')
................................................................................
			srv:conprep(lib.store.prepmode.conf)
			var cfmode: lib.cmdparse {
				help = {'h','display this list'};
				no_notify = {'n', "don't instruct the server to refresh its configuration cache after making changes; useful for \"transactional\" configuration changes."};
			}
			cfmode:parse(mode.arglist.ct, &mode.arglist(0))
			if cfmode.help then
				[ lib.emit(false, 1, 'usage: ', `argv[0], ' conf ', cfmode.type.helptxt.flags, ' <cmd> [<args>…]', cfmode.type.helptxt.opts, cmdhelp {
					{ 'conf set <setting> <value>', 'add or a change a server configuration parameter to the database' };
					{ 'conf get <setting>', 'report the value of a server setting' };
					{ 'conf reset <setting>', 'reset a server setting to its default value' };
					{ 'conf refresh', 'instruct an instance to refresh its configuration cache' };
					{ 'conf chsec', 'reset the server secret, invalidating all authentication cookies' };
				}) ]
				return 1
			end
			if cfmode.arglist.ct < 1 then goto cmderr end

			if cfmode.arglist.ct == 1 then
				if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
					var sec: int8[65] gensec(&sec[0])
................................................................................
						pwset(dlg, &tmppw, ruid, false)
						lib.report('temporary root pw: ', {&tmppw[0], 32})
					end
				else goto cmderr end
			elseif lib.str.cmp(mode.arglist(0),'user') == 0 then
				var umode: pbasic umode:parse(mode.arglist.ct, &mode.arglist(0))
				if umode.help then
					[ lib.emit(false, 1, 'usage: ', `argv[0], ' user ', umode.type.helptxt.flags, ' <handle> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
						{ 'user <handle> auth <type> new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' };
						{ 'user <handle> auth <type> reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' };
						{ 'user <handle> auth (<type>|all) purge', 'delete all credentials that would allow this user to log in (where possible)' };
						{ 'user <handle> (grant|revoke) (<priv>|all)', 'grant or revoke a specific power to or from a user' };
						{ 'user <handle> emasculate', 'strip all administrative powers from a user' };
						{ 'user <handle> forgive', 'restore all default powers to a user' };
						{ 'user <handle> suspend [<timespec>]', '(e.g. \27[1muser jokester suspend 5d 6h 7m 3s\27[m to suspend "jokester" for five days, six hours, seven minutes, and three seconds) suspend a user'};
					}) ]
					return 1
				end
				if umode.arglist.ct >= 3 then
					var grant = lib.str.cmp(umode.arglist(1),'grant') == 0
					var handle = umode.arglist(0)
					var usr = dlg:actor_fetch_xid(pstr {ptr=handle, ct=lib.str.sz(handle)})

					if grant or lib.str.cmp(umode.arglist(1),'revoke') == 0 then
						if not usr then lib.bail('unknown handle') end
						var newprivs = usr.ptr.rights.powers
						var map = array([lib.store.privmap])
						if umode.arglist.ct == 3 and lib.str.cmp(umode.arglist(2),'all') == 0 then
							if grant
								then newprivs:fill()
								else newprivs:clear()
							end
................................................................................
						end

						usr.ptr.rights.powers = newprivs
						dlg:actor_save_privs(usr.ptr)
					elseif lib.str.cmp(umode.arglist(1),'auth') == 0 and umode.arglist.ct == 4 then
						var reset = lib.str.cmp(umode.arglist(3),'reset') == 0
						if reset or lib.str.cmp(umode.arglist(3),'new') == 0 then
							-- FIXME enable resetting pws for users who have
							-- not logged in yet
							if not usr then lib.bail('unknown handle') end
							if lib.str.cmp(umode.arglist(2),'pw') == 0 then
								var tmppw: int8[33]
								pwset(dlg, &tmppw, usr.ptr.id, reset)
								lib.report('new temporary password for ',usr.ptr.handle,': ', {&tmppw[0], 32})
							else lib.bail('unknown credential type') end
						elseif lib.str.cmp(umode.arglist(3),'purge') == 0 then
							var uid: uint64 = 0
							if usr:ref() then uid = usr(0).id end
							if lib.str.cmp(umode.arglist(2),'pw') == 0 then
								dlg:auth_purge_pw(uid, handle)
							elseif lib.str.cmp(umode.arglist(2),'otp') == 0 then
								dlg:auth_purge_otp(uid, handle)
							elseif lib.str.cmp(umode.arglist(2),'trust') == 0 then
								dlg:auth_purge_trust(uid, handle)
							else lib.bail('unknown credential type') end
						else goto cmderr end
					else goto cmderr end
				else goto cmderr end
			elseif lib.str.cmp(mode.arglist(0),'actor') == 0 then
			elseif lib.str.cmp(mode.arglist(0),'tl') == 0 then
			elseif lib.str.cmp(mode.arglist(0),'serv') == 0 then
			else goto cmderr end

Modified parsav.t from [d1470e4b10] to [1565f096a3].

124
125
126
127
128
129
130


131
132


133
134
135
136

137
138
139
140
141
142
143
...
148
149
150
151
152
153
154



155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
...
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
...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
...
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422

423
424
425
426
427
428
429
...
461
462
463
464
465
466
467




468
469
470
471
472
473
474
475
476
477
478
479
...
480
481
482
483
484
485
486

487
488
489
490
491
492
493
494




495
496

497
498
499

500



















501
502
503
504
505
506
507
508
509
510
511
512
513
		local q = quote
			var [val]
			[exp]
		in val end
		return q
	end);
	proc = {


		exit = terralib.externfunction('exit', int -> {});
		getenv = terralib.externfunction('getenv', rawstring -> rawstring);


	};
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);

		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};
	str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
	copy = function(tbl)
		local new = {}
................................................................................
	osclock = terralib.includec 'time.h';
}
if config.posix then
	lib.uio = terralib.includec 'sys/uio.h';
	lib.emit = lib.emitv -- use more efficient call where available
else lib.emit = lib.emit_unitary end




local starttime = global(lib.osclock.time_t)
local lastnoisetime = global(lib.osclock.time_t)
local noise = global(uint8,1)
local noise_header = function(code,txt,mod)
	if mod then
		return string.format('\27[%s;1m(%s %s)\27[m ', code,mod,txt)
	else
		return string.format('\27[%s;1m(%s)\27[m ', code,txt)
	end
end


local terra timehdr()
	var now = lib.osclock.time(nil)
	var diff = now - lastnoisetime
	if diff > 30 then -- print cur time
		lastnoisetime = now
		var curtime: int8[26]
		lib.osclock.ctime_r(&now, &curtime[0])
		for i=0,26 do if curtime[i] == @'\n' then curtime[i] = 0 break end end -- :/
		[ lib.emit(false, 2, '\27[1m[', `&curtime[0], ']\27[;36m\n +00 ') ]
	else -- print time since last msg
		var dfs = arrayof(int8, 0x30 + diff/10, 0x30 + diff%10, 0x20, 0)
		[ lib.emit(false, 2, ' \27[36m+', `&dfs[0]) ]
................................................................................
	if level >= 3 and config.debug == false then
		return macro(function(...) return {} end)
	end
	return macro(function(...)
		local fn = (...).filename
		local ln = tostring((...).linenumber)
		local dbgtag = string.format('\27[35m · \27[34m%s:\27[1m%s\27[m\n', fn,ln)
		local q = lib.emit(level < 3 and true or dbgtag, 2, noise_header(code,n), ...)
		return quote
		--lib.io.fmt(['attempting to emit at ' .. fn..':'..ln.. '\n'])
		if noise >= level then timehdr(); [q] end end
	end);
end

lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(true, 2, noise_header('31','fatal'), ...)
	return quote

		timehdr(); [q]


		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
		ok: bool
		union {
................................................................................
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')

lib.load {
	'mem',  'math', 'str', 'file', 'crypt';
	'http', 'html', 'session', 'tpl', 'store';

	'smackdown'; -- md-alike parser
}

local be = {}
for _, b in pairs(config.backends) do
................................................................................
}

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end

terra lib.noise_init(default_level: uint)
	starttime = lib.osclock.time(nil)
	lastnoisetime = 0
	var n = lib.proc.getenv('parsav_noise')
	if n ~= nil then
		if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
			noise = n[0] - 0x30
			return
		end
	end
	noise = default_level
end
lib.load{'mgtool'}

local options = 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};
	static_dir = {'S', 'directory with overrides for static content', consume=1};
	builtin_data = {'D', 'do not load static content overrides at runtime under any circumstances'};
	instance = {'i', 'set an instance name to make it easier to control multiple daemons', consume = 1};

}


local static_setup = quote end
local mapin = quote end
local odir = symbol(rawstring)
local pathbuf = symbol(lib.str.acc)
................................................................................
		end or quote return end
	] end

	var [pathbuf] defer pathbuf:free()
	pathbuf:compose(odir,'/')
	[mapin]
end





local terra entry_daemon(argc: int, argv: &rawstring): int
	if argc < 1 then lib.bail('bad invocation!') end

	lib.noise_init(1)
	[lib.init]

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)
	var srv: lib.srv.overlord

	do var mode: options
................................................................................
		mode:parse(argc,argv) defer mode:free()
		static_init(&mode)
		if mode.version then version() return 0 end
		if mode.help then
			[ lib.emit(true, 1, 'usage: ',`argv[0],' ', options.helptxt.flags, ' [<args>…]', options.helptxt.opts) ]
			return 0
		end

		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

		srv:setup(cnf)




		srv:start(lib.trn(mode.instance ~= nil, @mode.instance, nil))
	end


	lib.report('listening for requests')
	while true do

		srv:poll()



















	end
	srv:shutdown()

	return 0
end


local bflag = function(long,short)
	if short and util.has(buildopts, short) then return true end
	if long and util.has(buildopts, long) then return true end
	return false
end








>
>


>
>




>







 







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


|

|







 







|


|


>




|

>
|
>
>







 







|







 







|
|
|



|



|












>







 







>
>
>
>




|







 







>





|


>
>
>
>


>


<
>

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





<







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
...
153
154
155
156
157
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
...
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
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
...
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547

548
549
550
551
552
553
554
		local q = quote
			var [val]
			[exp]
		in val end
		return q
	end);
	proc = {
		fork = terralib.externfunction('fork', {} -> int);
		daemonize = terralib.externfunction('daemon', {int,int} -> {});
		exit = terralib.externfunction('exit', int -> {});
		getenv = terralib.externfunction('getenv', rawstring -> rawstring);
		exec = terralib.externfunction('execv', {rawstring,&rawstring} -> int);
		execp = terralib.externfunction('execvp', {rawstring,&rawstring} -> int);
	};
	io = {
		send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
		recv = terralib.externfunction('read',  {int, rawstring, intptr} -> ptrdiff);
		close = terralib.externfunction('close', {int} -> int);
		say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
		fmt = terralib.externfunction('printf',
			terralib.types.funcpointer({rawstring},{int},true));
	};
	str = { sz = terralib.externfunction('strlen', rawstring -> intptr) };
	copy = function(tbl)
		local new = {}
................................................................................
	osclock = terralib.includec 'time.h';
}
if config.posix then
	lib.uio = terralib.includec 'sys/uio.h';
	lib.emit = lib.emitv -- use more efficient call where available
else lib.emit = lib.emit_unitary end


lib.noise = {
	level = global(uint8,1);
	starttime = global(lib.osclock.time_t);
	lasttime = global(lib.osclock.time_t);

	header = function(code,txt,mod)
		if mod then
			return string.format('\27[%s;1m(%s %s)\27[m ', code,mod,txt)
		else
			return string.format('\27[%s;1m(%s)\27[m ', code,txt)
		end
	end;
}

local terra timehdr()
	var now = lib.osclock.time(nil)
	var diff = now - lib.noise.lasttime
	if diff > 30 then -- print cur time
		lib.noise.lasttime = now
		var curtime: int8[26]
		lib.osclock.ctime_r(&now, &curtime[0])
		for i=0,26 do if curtime[i] == @'\n' then curtime[i] = 0 break end end -- :/
		[ lib.emit(false, 2, '\27[1m[', `&curtime[0], ']\27[;36m\n +00 ') ]
	else -- print time since last msg
		var dfs = arrayof(int8, 0x30 + diff/10, 0x30 + diff%10, 0x20, 0)
		[ lib.emit(false, 2, ' \27[36m+', `&dfs[0]) ]
................................................................................
	if level >= 3 and config.debug == false then
		return macro(function(...) return {} end)
	end
	return macro(function(...)
		local fn = (...).filename
		local ln = tostring((...).linenumber)
		local dbgtag = string.format('\27[35m · \27[34m%s:\27[1m%s\27[m\n', fn,ln)
		local q = lib.emit(level < 3 and true or dbgtag, 2, lib.noise.header(code,n), ...)
		return quote
		--lib.io.fmt(['attempting to emit at ' .. fn..':'..ln.. '\n'])
		if lib.noise.level >= level then timehdr(); [q] end end
	end);
end
local terra final_cleanup :: {} -> {}
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
	local q = lib.emit(true, 2, lib.noise.header('31','fatal'), ...)
	return quote
		if lib.noise.level ~= 0 then
			timehdr(); [q]
		end
		final_cleanup()
		lib.proc.exit(1)
	end
end);
lib.stat = terralib.memoize(function(ty)
	local n = struct {
		ok: bool
		union {
................................................................................
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')

lib.load {
	'mem', 'math', 'str', 'file', 'crypt', 'ipc';
	'http', 'html', 'session', 'tpl', 'store';

	'smackdown'; -- md-alike parser
}

local be = {}
for _, b in pairs(config.backends) do
................................................................................
}

do
	local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
	terra version() lib.io.send(1, p, [#p]) end
end

terra lib.noise.init(default_level: uint)
	lib.noise.starttime = lib.osclock.time(nil)
	lib.noise.lasttime = 0
	var n = lib.proc.getenv('parsav_noise')
	if n ~= nil then
		if n[0] >= 0x30 and n[0] <= 0x39 and n[1] == 0 then
			lib.noise.level = n[0] - 0x30
			return
		end
	end
	lib.noise.level = default_level
end
lib.load{'mgtool'}

local options = 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};
	static_dir = {'S', 'directory with overrides for static content', consume=1};
	builtin_data = {'D', 'do not load static content overrides at runtime under any circumstances'};
	instance = {'i', 'set an instance name to make it easier to control multiple daemons', consume = 1};
	no_ipc = {'I', 'disable IPC'};
}


local static_setup = quote end
local mapin = quote end
local odir = symbol(rawstring)
local pathbuf = symbol(lib.str.acc)
................................................................................
		end or quote return end
	] end

	var [pathbuf] defer pathbuf:free()
	pathbuf:compose(odir,'/')
	[mapin]
end

terra final_cleanup()
	lib.ipc.global_emperor:release()
end

local terra entry_daemon(argc: int, argv: &rawstring): int
	if argc < 1 then lib.bail('bad invocation!') end

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

	-- shut mongoose the fuck up
	lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil)
	var srv: lib.srv.overlord

	do var mode: options
................................................................................
		mode:parse(argc,argv) defer mode:free()
		static_init(&mode)
		if mode.version then version() return 0 end
		if mode.help then
			[ lib.emit(true, 1, 'usage: ',`argv[0],' ', options.helptxt.flags, ' [<args>…]', options.helptxt.opts) ]
			return 0
		end
		if mode.quiet then lib.noise.level = 0 end
		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

		srv:setup(cnf)
		if argv[0][0] == @'-' and argv[0][1] == 0 then
			lib.ipc.notify_parent(lib.ipc.signals.state_success)
			argv[0] = 'parsav service daemon'
		end
		srv:start(lib.trn(mode.instance ~= nil, @mode.instance, nil))
	end
	lib.ipc.global_emperor = lib.ipc.emperor.mk(true) defer lib.ipc.global_emperor:release()

	lib.report('listening for requests')

	while lib.ipc.proc_active do
		srv:poll()
		while true do
			var d: lib.ipc.demand
			lib.ipc.global_emperor:poll(&d)
			var a = lib.ipc.ack {success = true}
			if d.cmd == lib.ipc.cmd.none then break
			elseif d.cmd == lib.ipc.cmd.stop then
				lib.ipc.proc_active = false
			elseif d.cmd == lib.ipc.cmd.enumerate then
				if srv.id ~= nil then
					lib.str.ncpy(&a.iname[0], srv.id, [(`a.iname).tree.type.N])
				else a.iname[0] = 0 end
			elseif d.cmd == lib.ipc.cmd.chnoise then
				lib.noise.level = d.operand
			elseif d.cmd == lib.ipc.cmd.cfgrefresh then
				srv.cfg:free()
				srv.cfg:load()
			end
			d:ack(&lib.ipc.global_emperor, &a)
		end
	end
	srv:shutdown()

	return 0
end


local bflag = function(long,short)
	if short and util.has(buildopts, short) then return true end
	if long and util.has(buildopts, long) then return true end
	return false
end

Modified store.t from [3a79c99b1d] to [763fd9ba8a].

266
267
268
269
270
271
272



273
274
275
276
277
278
279
	actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
	actor_conf_int: cnf(intptr, lib.stat(intptr))

	auth_create_pw: {&m.source, uint64, bool, lib.mem.ptr(int8)} -> {}
		-- uid: uint64
		-- reset: bool (delete other passwords?)
		-- pw: pstring




	post_save: {&m.source, &m.post} -> {}
	post_create: {&m.source, &m.post} -> uint64
	post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
	convo_fetch_xid: {&m.source,rawstring} -> lib.mem.ptr(m.post)
	convo_fetch_uid: {&m.source,uint64} -> lib.mem.ptr(m.post)








>
>
>







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
	actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
	actor_conf_int: cnf(intptr, lib.stat(intptr))

	auth_create_pw: {&m.source, uint64, bool, lib.mem.ptr(int8)} -> {}
		-- uid: uint64
		-- reset: bool (delete other passwords?)
		-- pw: pstring
	auth_purge_pw: {&m.source, uint64, rawstring} -> {}
	auth_purge_otp: {&m.source, uint64, rawstring} -> {}
	auth_purge_trust: {&m.source, uint64, rawstring} -> {}

	post_save: {&m.source, &m.post} -> {}
	post_create: {&m.source, &m.post} -> uint64
	post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
	convo_fetch_xid: {&m.source,rawstring} -> lib.mem.ptr(m.post)
	convo_fetch_uid: {&m.source,uint64} -> lib.mem.ptr(m.post)