Index: backend/pgsql.t ================================================================== --- backend/pgsql.t +++ backend/pgsql.t @@ -217,10 +217,18 @@ (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, @@ -878,19 +886,31 @@ 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 + 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 Index: cmdparse.t ================================================================== --- cmdparse.t +++ cmdparse.t @@ -63,11 +63,11 @@ 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) + 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 @@ -90,12 +90,13 @@ 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 Index: config.lua ================================================================== --- config.lua +++ config.lua @@ -35,11 +35,11 @@ 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', './'); + prefix = default('parsav_install_prefix', '.'); build = { id = u.rndstr(6); release = u.ingest('release'); when = os.date(); }; @@ -67,10 +67,11 @@ 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'), Index: crypt.t ================================================================== --- crypt.t +++ crypt.t @@ -60,10 +60,12 @@ 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 Index: mgtool.t ================================================================== --- mgtool.t +++ mgtool.t @@ -20,41 +20,34 @@ 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 ', '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 (|/)', 'extracts an attachment artifact from the database and prints it to standard out' }; - { 'db excise ', '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' }; + { 'db', 'set up and manage the database' }; + { 'user', 'manage users, privileges, and credentials'}; { 'mkroot ', 'establish a new root user with the given handle' }; - { 'user auth new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' }; - { 'user auth reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' }; - { 'user auth (|all) purge', 'delete all credentials that would allow this user to log in (where possible)' }; - { 'user (grant|revoke) (|all)', 'grant or revoke a specific power to or from a user' }; - { 'user emasculate', 'strip all administrative powers from a user' }; - { 'user suspend []', '(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 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 create', 'instantiate a new actor' }; { 'actor bestow ', 'bestow an epithet upon an actor' }; - { 'conf set ', 'add or a change a server configuration parameter to the database' }; - { 'conf get ', 'report the value of a server setting' }; - { 'conf reset ', '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' }; + { '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 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] - ) +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 @@ -102,45 +95,105 @@ 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.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, ' […]', ctloptions.helptxt.opts, ctlcmdhelp) ] + [ lib.emit(false, 1, 'usage: ', `argv[0], ' ', ctloptions.helptxt.flags, ' […]', 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 = "backend.conf" 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, ' […]', dbmode.type.helptxt.opts) ] + [ lib.emit(false, 1, 'usage: ', `argv[0], ' db ', dbmode.type.helptxt.flags, ' […]', dbmode.type.helptxt.opts, cmdhelp { + { 'db init ', '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 (|/)', 'extracts an attachment artifact from the database and prints it to standard out' }; + { 'db excise (|/)', 'removes an undesirable artifact from the database' }; + { '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' }; + }) ] return 1 end if dbmode.arglist.ct < 1 then goto cmderr end srv:setup(cnf) @@ -159,11 +212,11 @@ ) 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]]) + 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 @@ -184,11 +237,17 @@ 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, ' […]', cfmode.type.helptxt.opts) ] + [ lib.emit(false, 1, 'usage: ', `argv[0], ' conf ', cfmode.type.helptxt.flags, ' […]', cfmode.type.helptxt.opts, cmdhelp { + { 'conf set ', 'add or a change a server configuration parameter to the database' }; + { 'conf get ', 'report the value of a server setting' }; + { 'conf reset ', '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 @@ -247,19 +306,27 @@ 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, ' […]', umode.type.helptxt.opts) ] + [ lib.emit(false, 1, 'usage: ', `argv[0], ' user ', umode.type.helptxt.flags, ' […]', umode.type.helptxt.opts, cmdhelp { + { 'user auth new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' }; + { 'user auth reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' }; + { 'user auth (|all) purge', 'delete all credentials that would allow this user to log in (where possible)' }; + { 'user (grant|revoke) (|all)', 'grant or revoke a specific power to or from a user' }; + { 'user emasculate', 'strip all administrative powers from a user' }; + { 'user forgive', 'restore all default powers to a user' }; + { 'user suspend []', '(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 not usr then lib.bail('unknown handle') end 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() @@ -287,16 +354,28 @@ 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 Index: parsav.t ================================================================== --- parsav.t +++ parsav.t @@ -126,16 +126,21 @@ [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) }; @@ -150,26 +155,29 @@ 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 + +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 - lastnoisetime + var diff = now - lib.noise.lasttime if diff > 30 then -- print cur time - lastnoisetime = now + 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 @@ -184,23 +192,27 @@ 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), ...) + 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 noise >= level then timehdr(); [q] end end + 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, noise_header('31','fatal'), ...) + local q = lib.emit(true, 2, lib.noise.header('31','fatal'), ...) return quote - timehdr(); [q] + 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 { @@ -334,11 +346,11 @@ 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'; + 'mem', 'math', 'str', 'file', 'crypt', 'ipc'; 'http', 'html', 'session', 'tpl', 'store'; 'smackdown'; -- md-alike parser } @@ -395,21 +407,21 @@ 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 +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 - noise = n[0] - 0x30 + lib.noise.level = n[0] - 0x30 return end end - noise = default_level + lib.noise.level = default_level end lib.load{'mgtool'} local options = lib.cmdparse { version = {'V', 'display information about the binary build and exit'}; @@ -418,10 +430,11 @@ 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 @@ -463,15 +476,19 @@ 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.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 @@ -482,30 +499,54 @@ if mode.version then version() return 0 end if mode.help then [ lib.emit(true, 1, 'usage: ',`argv[0],' ', options.helptxt.flags, ' […]', 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 + 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 true do + 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 Index: store.t ================================================================== --- store.t +++ store.t @@ -268,10 +268,13 @@ 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)