xref: /inferno-os/appl/cmd/install/inst.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Inst;
2
3include "sys.m";
4	sys: Sys;
5	Dir, sprint, fprint: import sys;
6include "draw.m";
7include "bufio.m";
8	bufio: Bufio;
9	Iobuf: import bufio;
10include "string.m";
11	str: String;
12include "arg.m";
13	arg: Arg;
14include "keyring.m";
15	keyring : Keyring;
16include "arch.m";
17	arch : Arch;
18include "wrap.m";
19	wrap : Wrap;
20
21Inst: module
22{
23	init:	fn(nil: ref Draw->Context, nil: list of string);
24};
25
26LEN: con Sys->ATOMICIO;
27
28tflag := 0;
29uflag := 0;
30hflag := 0;
31vflag := 0;
32fflag := 1;
33stderr: ref Sys->FD;
34bout: ref Iobuf;
35argv0 := "inst";
36oldw, w : ref Wrap->Wrapped;
37root := "/";
38force := 0;
39stoponerr := 1;
40
41# membogus(argv: list of string)
42# {
43#
44# }
45
46init(nil: ref Draw->Context, args: list of string)
47{
48	sys = load Sys Sys->PATH;
49	stderr = sys->fildes(2);
50	bufio = load Bufio Bufio->PATH;
51	if(bufio == nil)
52		error(sys->sprint("cannot load %s: %r\n", Bufio->PATH));
53
54	str = load String String->PATH;
55	if(str == nil)
56		error(sys->sprint("cannot load %s: %r\n", String->PATH));
57
58	arg = load Arg Arg->PATH;
59	if(arg == nil)
60		error(sys->sprint("cannot load %s: %r\n", Arg->PATH));
61	keyring = load Keyring Keyring->PATH;
62	if(keyring == nil)
63		error(sys->sprint("cannot load %s: %r\n", Keyring->PATH));
64	arch = load Arch Arch->PATH;
65	if(arch == nil)
66		error(sys->sprint("cannot load %s: %r\n", Arch->PATH));
67	arch->init(bufio);
68	wrap = load Wrap Wrap->PATH;
69	if(wrap == nil)
70		error(sys->sprint("cannot load %s: %r\n", Wrap->PATH));
71	wrap->init(bufio);
72	arg->init(args);
73	while((c := arg->opt()) != 0)
74		case c {
75		'f' =>
76			fflag = 0;
77		'h' =>
78			hflag = 1;
79			bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
80			if(bout == nil)
81				error(sys->sprint("can't access standard output: %r"));
82		't' =>
83			tflag = 1;
84		'u' =>
85			uflag = 1;
86		'v' =>
87			vflag = 1;
88		'r' =>
89			root = arg->arg();
90			if (root == nil)
91				fatal("root missing");
92		'F' =>
93			force = 1;
94		'c' =>
95			stoponerr = 0;
96		* =>
97			usage();
98		}
99	args = arg->argv();
100	if (args == nil)
101		usage();
102	ar := arch->openarch(hd args);
103	if(ar == nil || ar.b == nil)
104		error(sys->sprint("can't access %s: %r", hd args));
105	w = wrap->openwraphdr(hd args, root, nil, 0);
106	if (w == nil)
107		fatal("no such package found");
108	if(w.nu != 1)
109		fatal("strange package: more than one piece");
110	if (force == 0)
111		oldw = wrap->openwrap(w.name, root, 0);
112	if (force == 0 && w.u[0].utime && (oldw == nil || oldw.tfull < w.u[0].utime)){
113		tfull: int;
114		if(oldw == nil)
115			tfull = -1;
116		else
117			tfull = oldw.tfull;
118		fatal(sys->sprint("need %s version of %s already installed (pkg %d)", wrap->now2string(w.u[0].utime, 0), w.name, tfull));
119	}
120	args = tl args;
121	digest := array[Keyring->MD5dlen] of byte;
122	digest0 := array[Keyring->MD5dlen] of byte;
123	digest1 := array[Keyring->MD5dlen] of byte;
124
125	while ((a := arch->gethdr(ar)) != nil) {
126		why := "";
127		docopy := 0;
128		if(force)
129			docopy = 1;
130		else if(a.d.mode & Sys->DMDIR)
131			docopy = 1;
132		else if(wrap->md5file(root+a.name, digest) < 0)
133			docopy = 1;
134		else{
135			wrap->md5filea(root+a.name, digest1);
136			(ok, t) := wrap->getfileinfo(oldw, a.name, digest, nil, digest1);
137			if (ok >= 0) {
138				if(t > w.u[0].time){
139					docopy = 0;
140					why = "version from newer package exists";
141				}
142				else
143					docopy = 1;
144			}
145			else {
146				(ok, t) = wrap->getfileinfo(oldw, a.name, nil, nil, nil);
147				if(ok >= 0){
148					docopy = 0;
149					why = "locally modified";
150				}
151				else{
152					docopy = 0;
153					why = "locally created";
154				}
155			}
156		}
157		if(!docopy){
158			wrap->md5sum(ar.b, digest0, int a.d.length);
159			if(wrap->memcmp(digest, digest0, Keyring->MD5dlen))
160				skipfile(a.name, why);
161			continue;
162		}
163		if(args != nil){
164			if(!selected(a.name, args)){
165				arch->drain(ar, int a.d.length);
166				continue;
167			}
168			if (!hflag)
169				mkdirs(root, a.name);
170		}
171		name := pathcat(root, a.name);
172		if(hflag){
173			bout.puts(sys->sprint("%s %uo %s %s %ud %d\n",
174				name, a.d.mode, a.d.uid, a.d.gid, a.d.mtime, int a.d.length));
175			arch->drain(ar, int a.d.length);
176			continue;
177		}
178		if(a.d.mode & Sys->DMDIR)
179			mkdir(name, a.d);
180		else
181			extract(ar, name, a.d);
182	}
183	arch->closearch(ar);
184	if(ar.err == nil){
185		# fprint(stderr, "done\n");
186		quit(nil);
187	}
188	else {
189		fprint(stderr, "%s\n", ar.err);
190		quit("eof");
191	}
192}
193
194skipfile(f : string, why : string)
195{
196	sys->fprint(stderr, "skipping %s: %s\n", f, why);
197}
198
199skiprmfile(f: string, why: string)
200{
201	sys->fprint(stderr, "not removing %s: %s\n", f, why);
202}
203
204doremove(s : string)
205{
206	p := pathcat(root, s);
207	digest := array[Keyring->MD5dlen] of { * => byte 0 };
208	digest1 := array[Keyring->MD5dlen] of { * => byte 0 };
209	if(wrap->md5file(p, digest) < 0)
210		;
211	else{
212		wrap->md5filea(p, digest1);
213		(ok, nil) := wrap->getfileinfo(oldw, s, digest, nil, digest1);
214		if(force == 0 && ok < 0)
215			skiprmfile(p, "locally modified");
216		else{
217			if (vflag)
218				sys->print("rm %s\n", p);
219			remove(p);
220		}
221	}
222}
223
224quit(s: string)
225{
226	if (s == nil) {
227		p := w.u[0].dir + "/remove";
228		if ((b := bufio->open(p, Bufio->OREAD)) != nil) {
229			while ((t := b.gets('\n')) != nil) {
230				lt := len t;
231				if (t[lt-1] == '\n')
232					t = t[0:lt-1];
233				doremove(t);
234			}
235		}
236	}
237	if(bout != nil)
238		bout.flush();
239	if(wrap != nil)
240		wrap->end();
241	if(s != nil)
242		raise "fail: "+s;
243	else
244		fprint(stderr, "done\n");
245	exit;
246}
247
248fileprefix(prefix, s: string): int
249{
250	n := len prefix;
251	m := len s;
252	if(n > m || !str->prefix(prefix, s))
253		return 0;
254	if(m > n && s[n] != '/')
255		return 0;
256	return 1;
257}
258
259selected(s: string, args: list of string): int
260{
261	for(; args != nil; args = tl args)
262		if(fileprefix(hd args, s))
263			return 1;
264	return 0;
265}
266
267mkdirs(basedir, name: string)
268{
269	(nil, names) := sys->tokenize(name, "/");
270	while(names != nil) {
271		create(basedir, Sys->OREAD, 8r775|Sys->DMDIR);
272		if(tl names == nil)
273			break;
274		basedir = basedir + "/" + hd names;
275		names = tl names;
276	}
277}
278
279mkdir(name: string, dir : ref Sys->Dir)
280{
281	d: Dir;
282	i: int;
283
284	if(vflag) {
285		MTPT : con "/n/remote";
286		s := name;
287		if (len name >= len MTPT && name[0:len MTPT] == MTPT)
288			s = name[len MTPT:];
289		sys->print("installing directory %s\n", s);
290	}
291	fd := create(name, Sys->OREAD, dir.mode);
292	if(fd == nil) {
293		err := sys->sprint("%r");
294		(i, d) = sys->stat(name);
295		if(i < 0 || !(d.mode & Sys->DMDIR)){
296			werr(sys->sprint("can't make directory %s: %s", name, err));
297			return;
298		}
299	}
300	else {
301		(i, d) = sys->fstat(fd);
302		if(i < 0)
303			warn(sys->sprint("can't stat %s: %r", name));
304		fd = nil;
305	}
306	d = sys->nulldir;
307	(nil, p) := str->splitr(name, "/");
308	if(p == nil)
309		p = name;
310	d.name = p;
311	d.mode = dir.mode;
312	if(tflag || uflag)
313		d.mtime = dir.mtime;
314	if(uflag){
315		d.uid = dir.uid;
316		d.gid = dir.gid;
317	}
318	fd = nil;
319	if(sys->wstat(name, d) < 0){
320		e := sys->sprint("%r");
321		if(wstat(name, d) < 0)
322			warn(sys->sprint("can't set modes for %s: %s", name, e));
323	}
324	if(uflag){
325		(i, d) = sys->stat(name);
326		if(i < 0)
327			warn(sys->sprint("can't reread modes for %s: %r", name));
328		if(dir.uid != d.uid)
329			warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid));
330		if(dir.gid != d.gid)
331			warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid));
332	}
333}
334
335extract(ar : ref Arch->Archive, name: string, dir : ref Sys->Dir)
336{
337	sfd := create(name, Sys->OWRITE, dir.mode);
338	if(sfd == nil) {
339		if(!fflag || remove(name) == -1 ||
340		    (sfd = create(name, Sys->OWRITE, dir.mode)) == nil) {
341			werr(sys->sprint("can't make file %s: %r", name));
342			arch->drain(ar, int dir.length);
343			return;
344		}
345	}
346	b := bufio->fopen(sfd, Bufio->OWRITE);
347	if (b == nil) {
348		warn(sys->sprint("can't open file %s for bufio : %r", name));
349		arch->drain(ar, int dir.length);
350		return;
351	}
352	err := arch->getfile(ar, b, int dir.length);
353	if (err != nil) {
354		if (len err >= 9 && err[0:9] == "premature")
355			fatal(err);
356		else
357			warn(err);
358	}
359	(i, d) := sys->fstat(b.fd);
360	if(i < 0)
361		warn(sys->sprint("can't stat %s: %r", name));
362	d = sys->nulldir;
363	(nil, p) := str->splitr(name, "/");
364	if(p == nil)
365		p = name;
366	d.name = p;
367	d.mode = dir.mode;
368	if(tflag || uflag)
369		d.mtime = dir.mtime;
370	if(uflag){
371		d.uid = dir.uid;
372		d.gid = dir.gid;
373	}
374	if(b.flush() == Bufio->ERROR)
375		werr(sys->sprint("error writing %s: %r", name));
376	b.close();
377	sfd = nil;
378	if(sys->wstat(name, d) < 0){
379		e := sys->sprint("%r");
380		if(wstat(name, d) < 0)
381			warn(sys->sprint("can't set modes for %s: %s", name, e));
382	}
383	if(uflag){
384		(i, d) = sys->stat(name);
385		if(i < 0)
386			warn(sys->sprint("can't reread modes for %s: %r", name));
387		if(d.uid != dir.uid)
388			warn(sys->sprint("%s: uid mismatch %s %s", name, dir.uid, d.uid));
389		if(d.gid != dir.gid)
390			warn(sys->sprint("%s: gid mismatch %s %s", name, dir.gid, d.gid));
391	}
392}
393
394error(s: string)
395{
396	fprint(stderr, "%s: %s\n", argv0, s);
397	quit("error");
398}
399
400werr(s: string)
401{
402	fprint(stderr, "%s: %s\n", argv0, s);
403	if(stoponerr)
404		quit("werr");
405}
406
407warn(s: string)
408{
409	fprint(stderr, "%s: %s\n", argv0, s);
410}
411
412usage()
413{
414	fprint(stderr, "Usage: inst [-h] [-u] [-v] [-f] [-c] [-F] [-r dest-root] [file ...]\n");
415	raise "fail: usage";
416}
417
418fatal(s : string)
419{
420	sys->fprint(stderr, "inst: %s\n", s);
421	if(wrap != nil)
422		wrap->end();
423	exit;
424}
425
426parent(name : string) : string
427{
428	slash := -1;
429	for (i := 0; i < len name; i++)
430		if (name[i] == '/')
431			slash = i;
432	if (slash > 0)
433		return name[0:slash];
434	return "/";
435}
436
437create(name : string, rw : int, mode : int) : ref Sys->FD
438{
439	fd := sys->create(name, rw, mode);
440	if (fd == nil) {
441		p := parent(name);
442		(ok, d) := sys->stat(p);
443		if (ok < 0)
444			return nil;
445		omode := d.mode;
446		d = sys->nulldir;
447		d.mode = omode | 8r222;	# ensure parent is writable
448		sys->wstat(p, d);
449		fd = sys->create(name, rw, mode);
450		d.mode = omode;
451		sys->wstat(p, d);
452	}
453	return fd;
454}
455
456remove(name : string) : int
457{
458	if (sys->remove(name) < 0) {
459		(ok, d) := sys->stat(name);
460		if (ok < 0)
461			return -1;
462		omode := d.mode;
463		d.mode |= 8r222;
464		sys->wstat(name, d);
465		if (sys->remove(name) >= 0)
466			return 0;
467		d.mode = omode;
468		sys->wstat(name, d);
469		return -1;
470	}
471	return 0;
472}
473
474wstat(name : string, d : Dir) : int
475{
476	(ok, dir) := sys->stat(name);
477	if (ok < 0)
478		return -1;
479	omode := dir.mode;
480	dir.mode |= 8r222;
481	sys->wstat(name, dir);
482	if (sys->wstat(name, d) >= 0)
483		return 0;
484	dir.mode = omode;
485	sys->wstat(name, dir);
486	return -1;
487}
488
489pathcat(s : string, t : string) : string
490{
491	if (s == nil) return t;
492	if (t == nil) return s;
493	slashs := s[len s - 1] == '/';
494	slasht := t[0] == '/';
495	if (slashs && slasht)
496		return s + t[1:];
497	if (!slashs && !slasht)
498		return s + "/" + t;
499	return s + t;
500}
501