parsav  Diff

Differences From Artifact [c954b701da]:

To Artifact [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' };