Differences From
Artifact [c954b701da]:
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' };