Overview
| Comment: | enable remote control of running instances |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
f8816b0ab5148803709d93515359d376 |
| User & Date: | lexi on 2020-12-29 15:48:34 |
| Other Links: | manifest | tags |
Context
|
2020-12-30
| ||
| 00:43 | continued iteration check-in: 0324d62546 user: lexi tags: trunk | |
|
2020-12-29
| ||
| 15:48 | enable remote control of running instances check-in: f8816b0ab5 user: lexi tags: trunk | |
| 14:35 | check in missing file check-in: 5a4f99fb55 user: lexi tags: trunk | |
Changes
Modified ipc.t from [5ea8386d8f] to [ff639e2a51].
195 195 196 196 cmd.cmd = m.cmd.none 197 197 end 198 198 199 199 terra m.emperor:mallack() return lib.mem.heapa(m.ack, self:countpeers()) end 200 200 terra m.emperor.methods.decree :: { 201 201 &m.emperor, uint64, rawstring, m.cmd.t, uint64, &m.ack 202 -} -> {} 202 +} -> bool 203 203 terra m.emperor:decree( 204 204 tgtclid: uint64, tgtname: rawstring, 205 205 cmd: m.cmd.t, operand: uint64, 206 206 result: &m.ack 207 -): {} 207 +): bool 208 208 if self.client then lib.bail('client attempted to issue IPC decree') end 209 209 var dem = m.demand { 210 210 cmd = cmd; 211 211 operand = operand; 212 212 empq = self.msqid; -- register to receive replies 213 213 } 214 214 var npeers = self:countpeers() 215 - if npeers == 0 then lib.bail('no processes connected to control bus') end 215 + if npeers == 0 then lib.warn('no processes connected to control bus') return false end 216 216 if tgtclid == 0 and tgtname == nil then 217 217 lib.dbg('sending to all instances, waiting for edict to become writable') 218 218 self.edict:sem(0) -- wait for all locks on edict to resolve 219 219 lib.dbg('locking edict') 220 220 self.edict:sem(npeers) -- place a read lock for each peer 221 221 self.edict.demand = dem 222 222 lib.dbg('sending edict') ................................................................................ 246 246 found = true 247 247 tgt = acks(i).cliq 248 248 end 249 249 end 250 250 acks:free() 251 251 if not found then 252 252 lib.warn('no such instance is currently online and responding to IPC calls') 253 + return false 253 254 else 254 255 lib.dbg('located instance, sending command') 255 256 if mq.msgsnd(tgt, &dem, sizeof(m.demand), 0) == -1 then 256 257 lib.bail('could not send command to target process') 257 258 end 258 259 while true do 259 260 var ack: m.ack ................................................................................ 264 265 lib.dbg('got response, writing out and returning') 265 266 @result = ack 266 267 break 267 268 else lib.warn('got spurious response, ignoring') end 268 269 end 269 270 end 270 271 end 272 + return true 271 273 end 272 274 273 275 terra m.demand:ack(emp: &m.emperor, a: &m.ack) 274 276 a.clid = emp.clid 275 277 a.cliq = emp.msqid 276 278 mq.msgsnd(self.empq, a, sizeof(m.ack), 0) 277 279 end
Modified mem.t from [a177326f1c] to [1f9397ac82].
60 60 t.methods = { 61 61 free = terra(self: &t): bool 62 62 [recurse and quote 63 63 self.ptr:free() 64 64 end or {}] 65 65 if self.ct > 0 then 66 66 m.heapf(self.ptr) 67 + self.ptr = nil 67 68 self.ct = 0 68 69 return true 69 70 end 70 71 return false 71 72 end; 72 73 init = terra(self: &t, newct: intptr): bool 73 74 if newct == 0 then self.ct = 0 self.ptr = nil return false end
Modified mgtool.t from [c954b701da] to [c623f2c8c5].
4 4 local ctloptions = lib.cmdparse({ 5 5 version = {'V', 'display information about the binary build and exit'}; 6 6 verbose = {'v', 'increase logging verbosity', inc=1}; 7 7 quiet = {'q', 'do not print to standard out'}; 8 8 help = {'h', 'display this list'}; 9 9 backend_file = {'B', 'init from specified backend file', consume=1}; 10 10 backend = {'b', 'operate on only the selected backend'}; 11 - instance = {'i', 'specify the instance to control by name', consume=1}; 12 - all = {'A', 'affect all running instances'}; 11 + instance_id = {'i', 'specify the instance to control by name', consume=1}; 12 + instance_serial = {'I', 'specify the instance to control by serial', consume=1}; 13 + all = {'A', 'affect all running instances/backends'}; 13 14 }, { subcmd = 1 }) 14 15 15 16 local pbasic = lib.cmdparse { 16 17 help = {'h', 'display this list'} 17 18 } 18 19 local subcmds = { 19 20 } 20 21 21 22 local ctlcmds = { 22 23 { 'start', 'start a new instance of the server' }; 23 24 { 'stop', 'stop a running instance' }; 25 + { 'ls', 'list all running instances' }; 24 26 { 'attach', 'capture log output from a running instance' }; 25 27 { 'db', 'set up and manage the database' }; 26 28 { 'user', 'manage users, privileges, and credentials'}; 27 29 { 'mkroot <handle>', 'establish a new root user with the given handle' }; 28 30 { '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)' }; 29 31 { 'actor <xid> create', 'instantiate a new actor' }; 30 32 { 'actor <xid> bestow <epithet>', 'bestow an epithet upon an actor' }; ................................................................................ 45 47 v[2] 46 48 ) 47 49 end 48 50 return str 49 51 end 50 52 51 53 local struct idelegate { 54 + emperor: &lib.ipc.emperor 55 + 52 56 all: bool 53 57 src: &lib.store.source 54 58 srv: &lib.srv.overlord 59 + 60 + sid: uint64 61 + iname: rawstring 55 62 } 56 63 idelegate.metamethods.__methodmissing = macro(function(meth, self, ...) 57 64 local expr = {...} 58 65 local rt 59 66 for _,f in pairs(lib.store.backend.entries) do 60 67 local fn = f.field or f[1] 61 68 local ft = f.type or f[2] ................................................................................ 67 74 if self.all 68 75 then r=self.srv:[meth]([expr]) 69 76 elseif self.src ~= nil then r=self.src:[meth]([expr]) 70 77 else lib.bail('no data source specified') 71 78 end 72 79 in r end 73 80 end) 81 + 82 +terra idelegate:ipc_send(cmd: lib.ipc.cmd.t, operand: uint64) 83 + var emp = self.emperor 84 + var acks: lib.mem.ptr(lib.ipc.ack) 85 + if self.sid == 0 and self.iname == nil then 86 + if not self.all and emp:countpeers() > 1 then 87 + lib.bail('either specify the instance to control or pass --all to control all instances') 88 + end 89 + acks = emp:mallack() 90 + emp:decree(0,nil, cmd, operand, &acks(0)) -- TODO targeting 91 + else 92 + acks = lib.mem.heapa(lib.ipc.ack, 1) 93 + if not emp:decree(self.sid, self.iname, cmd, operand, &acks(0)) then 94 + acks:free() 95 + end 96 + end 97 + return acks 98 +end 74 99 75 100 local terra gensec(sdest: rawstring) 76 101 var dest = [&uint8](sdest) 77 102 lib.crypt.spray(dest,64) 78 103 for i=0,64 do dest[i] = dest[i] % (0x7e - 0x20) + 0x20 end 79 104 dest[64] = 0 80 105 end ................................................................................ 92 117 else tmppw[i] = tmppw[i] + 0x30 end 93 118 end 94 119 lib.dbg('assigning temporary password') 95 120 dlg:auth_create_pw(uid, reset, pstr { 96 121 ptr = [rawstring](tmppw), ct = 32 97 122 }) 98 123 end 124 + 125 +local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring) 126 + var decbuf: int8[21] 127 + for i=0,acks.ct do 128 + var num = lib.math.decstr(acks(i).clid, &decbuf[20]) 129 + if acks(i).success then 130 + lib.report('instance #',num,' reports successful ',rep) 131 + else 132 + lib.report('instance #',num,' reports failed ',rep) 133 + end 134 + end 135 +end 99 136 100 137 local emp = lib.ipc.global_emperor 101 138 local terra entry_mgtool(argc: int, argv: &rawstring): int 102 139 if argc < 1 then lib.bail('bad invocation!') end 103 140 104 141 lib.noise.init(2) 105 142 [lib.init] 106 143 107 144 var srv: lib.srv.overlord 108 - var dlg = idelegate { srv = &srv, src = nil } 145 + var dlg = idelegate { 146 + emperor = &emp, srv = &srv; 147 + src = nil, sid = 0, iname = nil, all = false; 148 + } 109 149 110 150 var mode: ctloptions 111 151 mode:parse(argc,argv) defer mode:free() 112 152 if mode.version then version() return 0 end 113 153 if mode.help then 114 154 [ lib.emit(false, 1, 'usage: ', `argv[0], ' ', ctloptions.helptxt.flags, ' <cmd> [<args>…]', ctloptions.helptxt.opts, cmdhelp(ctlcmds)) ] 115 155 return 0 ................................................................................ 118 158 var cnf: rawstring 119 159 if mode.backend_file ~= nil 120 160 then cnf = @mode.backend_file 121 161 else cnf = lib.proc.getenv('parsav_backend_file') 122 162 end 123 163 if cnf == nil then cnf = [config.prefix_conf .. "/backend.conf"] end 124 164 if mode.all then dlg.all = true else 125 - -- iterate through and pick the right backend 165 + -- iterate through and pick the right backend, if one is indicated 166 + end 167 + 168 + if mode.instance_id ~= nil and mode.instance_serial ~= nil then 169 + lib.bail('conflicting flags passed') 170 + end 171 + 172 + if mode.instance_id ~= nil then 173 + dlg.iname = @mode.instance_id 174 + elseif mode.instance_serial ~= nil then 175 + -- decode serial 126 176 end 127 177 128 178 if mode.arglist.ct == 0 then lib.bail('no command') return 1 end 129 179 130 180 if lib.str.cmp(mode.arglist(0),'start') ~= 0 then 131 181 -- hack to save us some pain around forking 132 182 emp = lib.ipc.emperor.mk(false) 133 183 end 134 184 defer emp:release() 135 185 136 186 if lib.str.cmp(mode.arglist(0),'attach') == 0 then 137 187 elseif lib.str.cmp(mode.arglist(0),'start') == 0 then 138 - mode.arglist(0) = "-"; 139 - var chargv = mode.arglist.ptr 188 + var smode: lib.cmdparse { 189 + help = {'h', 'display this list'}; 190 + log = {'l', 'send server\'s logging output to a file', consume=1}; 191 + pid = {'p', 'report PID of launched process'}; 192 + instance_serial = {'s', 'report the instance serial of the launched process'}; 193 + keep_attached = {'k', 'don\'t detach from the calling terminal until a successful startup (or ever, if passed twice)', inc=1}; 194 + } 195 + smode:parse(mode.arglist.ct,mode.arglist.ptr) 196 + if smode.help then 197 + [ lib.emit(false, 1, 'usage: ', `argv[0], ' start ', smode.type.helptxt.flags, ' [<instance args>…] [-- <instance flags>…]', smode.type.helptxt.opts) ] 198 + return 1 199 + end 140 200 var lsr = lib.ipc.listener.mk() 141 201 var chpid = lib.proc.fork() 142 202 if chpid == 0 then 143 203 lsr:release() 144 - --lib.proc.daemonize(1,0) 145 - lib.io.close(0) lib.io.close(1) lib.io.close(2) 146 - lib.proc.exec([config.prefix_bin .. '/parsavd'], chargv) 147 - lib.proc.execp([config.prefix_bin .. '/parsavd'], chargv) 204 + var chargv = lib.mem.heapa(rawstring, smode.arglist.ct + 2) 205 + chargv(0) = '-' 206 + for i = 1, chargv.ct - 1 do 207 + chargv(i) = smode.arglist(i-1) 208 + end 209 + chargv(chargv.ct-1) = nil 210 + if smode.keep_attached == 0 then 211 + lib.io.close(0) lib.io.close(1) lib.io.close(2) 212 + end 213 + lib.proc.exec([config.prefix_bin .. '/parsavd'], chargv.ptr) 214 + lib.proc.execp([config.prefix_bin .. '/parsavd'], chargv.ptr) 148 215 lib.ipc.notify_parent(lib.ipc.signals.state_fail_find) 149 216 lib.bail('cannot find parsav program') 217 + -- chargv:free() 150 218 else 151 219 lib.report('starting parsav daemon') 152 220 while true do 153 221 var sig = lsr:block() 154 222 if sig.system then lib.dbg('got system signal') end 155 223 if sig.from == chpid then 156 224 if sig.system and sig.sig == lib.ipc.signals.sys_child then ................................................................................ 166 234 else lib.warn('got unrecognized signal, ignoring') 167 235 end 168 236 end 169 237 end 170 238 lsr:release() -- just because i feel distinctly uncomfortable leaving it out 171 239 end 172 240 elseif lib.str.cmp(mode.arglist(0),'stop') == 0 then 173 - var acks = emp:mallack() 174 - emp:decree(0,nil, lib.ipc.cmd.stop, 0, &acks(0)) -- TODO targeting 175 - for i=0,acks.ct do 176 - if acks(i).success then 177 - lib.io.fmt('instance %llu successfully stepped down\n', acks(i).clid) 178 - else 179 - lib.io.fmt('instance %llu reports failure to halt\n', acks(i).clid) 180 - end 241 + if mode.arglist.ct ~= 1 then goto cmderr end 242 + var acks = dlg:ipc_send(lib.ipc.cmd.stop, 0) 243 + if acks:ref() then 244 + ipc_report(acks, 'step-down') 245 + acks:free() 181 246 end 182 - acks:free() 247 + elseif lib.str.cmp(mode.arglist(0),'ls') == 0 then 248 + if mode.arglist.ct ~= 1 then goto cmderr end 249 + if dlg.sid == 0 and dlg.iname == nil then dlg.all = true end 250 + var acks = dlg:ipc_send(lib.ipc.cmd.enumerate, 0) 251 + if acks:ref() then 252 + for i=0,acks.ct do 253 + var decbuf: int8[21] 254 + var num = lib.math.decstr(acks(i).clid, &decbuf[20]) 255 + [ lib.emit(true,1, '\27[1m(', `num, ')\27[m ',`&acks(i).iname[0],' \27[1;32mactive\27[m') ] 256 + end 257 + acks:free() 258 + else lib.bail('no active instances') end 183 259 else 184 260 if lib.str.cmp(mode.arglist(0),'db') == 0 then 185 261 var dbmode: pbasic dbmode:parse(mode.arglist.ct, &mode.arglist(0)) 186 262 if dbmode.help then 187 263 [ lib.emit(false, 1, 'usage: ', `argv[0], ' db ', dbmode.type.helptxt.flags, ' <cmd> [<args>…]', dbmode.type.helptxt.opts, cmdhelp { 188 264 { 'db init <domain>', 'initialize backend databases (or a single specified database) with the necessary schema and structures for the given FQDN' }; 189 265 { 'db vacuum', 'delete old remote content from the database' };
Modified parsav.t from [1565f096a3] to [b85f6f7647].
429 429 quiet = {'q', 'do not print to standard out'}; 430 430 help = {'h', 'display this list'}; 431 431 backend_file = {'B', 'init from specified backend file', consume=1}; 432 432 static_dir = {'S', 'directory with overrides for static content', consume=1}; 433 433 builtin_data = {'D', 'do not load static content overrides at runtime under any circumstances'}; 434 434 instance = {'i', 'set an instance name to make it easier to control multiple daemons', consume = 1}; 435 435 no_ipc = {'I', 'disable IPC'}; 436 + chroot = {'C', 'chroot to the specified directory after starting and connecting to backends', consume=1}; 437 + no_lockdown = {'L', 'don\'t drop privileges or apply other security measures that could cause compatibility or communication problems'} 436 438 } 437 439 438 440 439 441 local static_setup = quote end 440 442 local mapin = quote end 441 443 local odir = symbol(rawstring) 442 444 local pathbuf = symbol(lib.str.acc)
Modified srv.t from [7c204f248d] to [7727a773dd].
619 619 lib.net.mg_mgr_init(&self.webmgr) 620 620 self.webcon = lib.net.mg_http_listen(&self.webmgr, bind, handle.http, self) 621 621 622 622 if dbbind.ptr ~= nil then dbbind:free() end 623 623 end 624 624 625 625 terra srv:poll() 626 - lib.net.mg_mgr_poll(&self.webmgr,1000) 626 + lib.net.mg_mgr_poll(&self.webmgr,300) 627 627 end 628 628 629 629 terra srv:shutdown() 630 630 lib.net.mg_mgr_free(&self.webmgr) 631 631 for i=0,self.sources.ct do var src = self.sources.ptr + i 632 632 lib.report('closing data source ', src.id.ptr, '(', src.backend.id, ')') 633 633 src:close()