1 /*******************************************************************/
2 /*  slibtool: a skinny libtool implementation, written in C        */
3 /*  Copyright (C) 2016--2018  Z. Gilboa                            */
4 /*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
5 /*******************************************************************/
6 
7 #include <fcntl.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdbool.h>
11 #include <fcntl.h>
12 #include <errno.h>
13 #include <sys/stat.h>
14 
15 #define ARGV_DRIVER
16 
17 #include <slibtool/slibtool.h>
18 #include "slibtool_driver_impl.h"
19 #include "slibtool_install_impl.h"
20 #include "slibtool_mapfile_impl.h"
21 #include "slibtool_readlink_impl.h"
22 #include "slibtool_spawn_impl.h"
23 #include "slibtool_symlink_impl.h"
24 #include "slibtool_errinfo_impl.h"
25 #include "argv/argv.h"
26 
slbt_install_usage(int fdout,const char * program,const char * arg,const struct argv_option ** optv,struct argv_meta * meta,int noclr)27 static int slbt_install_usage(
28 	int				fdout,
29 	const char *			program,
30 	const char *			arg,
31 	const struct argv_option **	optv,
32 	struct argv_meta *		meta,
33 	int				noclr)
34 {
35 	char header[512];
36 
37 	snprintf(header,sizeof(header),
38 		"Usage: %s --mode=install <install> [options] [SOURCE]... DEST\n"
39 		"Options:\n",
40 		program);
41 
42 	switch (noclr) {
43 		case 0:
44 			argv_usage(fdout,header,optv,arg);
45 			break;
46 
47 		default:
48 			argv_usage_plain(fdout,header,optv,arg);
49 			break;
50 	}
51 
52 	argv_free(meta);
53 
54 	return SLBT_USAGE;
55 }
56 
slbt_exec_install_fail(struct slbt_exec_ctx * actx,struct argv_meta * meta,int ret)57 static int slbt_exec_install_fail(
58 	struct slbt_exec_ctx *	actx,
59 	struct argv_meta *	meta,
60 	int			ret)
61 {
62 	argv_free(meta);
63 	slbt_free_exec_ctx(actx);
64 	return ret;
65 }
66 
slbt_exec_install_init_dstdir(const struct slbt_driver_ctx * dctx,struct argv_entry * dest,struct argv_entry * last,char * dstdir)67 static int slbt_exec_install_init_dstdir(
68 	const struct slbt_driver_ctx *	dctx,
69 	struct argv_entry *	dest,
70 	struct argv_entry *	last,
71 	char *			dstdir)
72 {
73 	int		fdcwd;
74 	struct stat	st;
75 	char *		slash;
76 	size_t		len;
77 
78 	/* fdcwd */
79 	fdcwd = slbt_driver_fdcwd(dctx);
80 
81 	/* last */
82 	if (dest)
83 		last = dest;
84 
85 	/* dstdir: initial string */
86 	if ((size_t)snprintf(dstdir,PATH_MAX,"%s",
87 			last->arg) >= PATH_MAX)
88 		return SLBT_BUFFER_ERROR(dctx);
89 
90 	/* dstdir might end with a slash */
91 	len = strlen(dstdir);
92 
93 	if (dstdir[--len] == '/')
94 		dstdir[len] = 0;
95 
96 	/* -t DSTDIR? */
97 	if (dest)
98 		return 0;
99 
100 	/* is DEST a directory? */
101 	if (!fstatat(fdcwd,dstdir,&st,0))
102 		if (S_ISDIR(st.st_mode))
103 			return 0;
104 
105 	/* remove last path component */
106 	if ((slash = strrchr(dstdir,'/')))
107 		*slash = 0;
108 
109 	return 0;
110 }
111 
slbt_exec_install_import_libraries(const struct slbt_driver_ctx * dctx,struct slbt_exec_ctx * ectx,char * srcdso,char * dstdir)112 static int slbt_exec_install_import_libraries(
113 	const struct slbt_driver_ctx *	dctx,
114 	struct slbt_exec_ctx *		ectx,
115 	char *				srcdso,
116 	char *				dstdir)
117 {
118 	char *	slash;
119 	char *	dot;
120 	char *	mark;
121 	char	srcbuf [PATH_MAX];
122 	char	implib [PATH_MAX];
123 	char	hostlnk[PATH_MAX];
124 	char	major  [128];
125 	char	minor  [128];
126 	char	rev    [128];
127 
128 	/* .libs/libfoo.so.x.y.z */
129 	if ((size_t)snprintf(srcbuf,sizeof(srcbuf),"%s",
130 			srcdso) >= sizeof(srcbuf))
131 		return SLBT_BUFFER_ERROR(dctx);
132 
133 	/* (dso is under .libs) */
134 	if (!(slash = strrchr(srcbuf,'/')))
135 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
136 
137 	/* libfoo.so.x.y.z */
138 	if ((size_t)snprintf(implib,sizeof(implib),"%s",
139 			++slash) >= sizeof(implib)
140 				    - strlen(dctx->cctx->settings.impsuffix))
141 		return SLBT_BUFFER_ERROR(dctx);
142 
143 	/* guard against an infinitely long version */
144 	mark = srcbuf + strlen(srcbuf);
145 
146 	if (dctx->cctx->asettings.osdfussix[0]) {
147 		if (!(dot = strrchr(srcbuf,'.')))
148 			return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
149 
150 		*dot = 0;
151 	}
152 
153 	/* rev */
154 	if (!(dot = strrchr(srcbuf,'.')))
155 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
156 	else if ((size_t)(mark - dot) > sizeof(rev))
157 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_REV);
158 	else {
159 		strcpy(rev,dot);
160 		*dot = 0;
161 	}
162 
163 	/* minor */
164 	if (!(dot = strrchr(srcbuf,'.')))
165 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
166 	else if ((size_t)(mark - dot) > sizeof(minor))
167 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_REV);
168 	else {
169 		strcpy(minor,dot);
170 		*dot = 0;
171 	}
172 
173 	/* major */
174 	if (!(dot = strrchr(srcbuf,'.')))
175 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
176 	else if ((size_t)(mark - dot) > sizeof(major))
177 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_REV);
178 	else {
179 		strcpy(major,dot);
180 		*dot = 0;
181 	}
182 
183 	if (!dctx->cctx->asettings.osdfussix[0])
184 		if (!(dot = strrchr(srcbuf,'.')))
185 			return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
186 
187 	/* .libs/libfoo.x.y.z.lib.a */
188 	sprintf(dot,"%s%s%s%s",
189 		major,minor,rev,
190 		dctx->cctx->asettings.impsuffix);
191 
192 	/* copy: .libs/libfoo.x.y.z.lib.a --> dstdir */
193 	if (slbt_copy_file(dctx,ectx,srcbuf,dstdir))
194 		return SLBT_NESTED_ERROR(dctx);
195 
196 	/* .libs/libfoo.x.lib.a */
197 	sprintf(dot,"%s%s",
198 		major,
199 		dctx->cctx->asettings.impsuffix);
200 
201 	/* copy: .libs/libfoo.x.lib.a --> dstdir */
202 	if (slbt_copy_file(dctx,ectx,srcbuf,dstdir))
203 		return SLBT_NESTED_ERROR(dctx);
204 
205 	/* /dstdir/libfoo.lib.a */
206 	strcpy(implib,slash);
207 	strcpy(dot,dctx->cctx->asettings.impsuffix);
208 
209 	if ((size_t)snprintf(hostlnk,sizeof(hostlnk),"%s/%s",
210 			dstdir,slash) >= sizeof(hostlnk))
211 		return SLBT_BUFFER_ERROR(dctx);
212 
213 	if (slbt_create_symlink(
214 			dctx,ectx,
215 			implib,
216 			hostlnk,
217 			SLBT_SYMLINK_DEFAULT))
218 		return SLBT_NESTED_ERROR(dctx);
219 
220 	return 0;
221 }
222 
slbt_exec_install_library_wrapper(const struct slbt_driver_ctx * dctx,struct slbt_exec_ctx * ectx,struct argv_entry * entry,char * dstdir)223 static int slbt_exec_install_library_wrapper(
224 	const struct slbt_driver_ctx *	dctx,
225 	struct slbt_exec_ctx *		ectx,
226 	struct argv_entry *		entry,
227 	char *				dstdir)
228 {
229 	int			fdcwd;
230 	int			fddst;
231 	size_t			buflen;
232 	const char *		base;
233 	char *			srcline;
234 	char *			dstline;
235 	char			clainame[PATH_MAX];
236 	char			instname[PATH_MAX];
237 	char			cfgbuf  [PATH_MAX];
238 	struct slbt_map_info *	mapinfo;
239 
240 	/* base libfoo.la */
241 	if ((base = strrchr(entry->arg,'/')))
242 		base++;
243 	else
244 		base = entry->arg;
245 
246 	/* /dstdir/libfoo.la */
247 	if ((size_t)snprintf(instname,sizeof(instname),"%s/%s",
248 			dstdir,base) >= sizeof(instname))
249 		return SLBT_BUFFER_ERROR(dctx);
250 
251 	/* libfoo.la.slibtool.install */
252 	if ((size_t)snprintf(clainame,sizeof(clainame),"%s.slibtool.install",
253 			entry->arg) >= sizeof(clainame))
254 		return SLBT_BUFFER_ERROR(dctx);
255 
256 	/* fdcwd */
257 	fdcwd = slbt_driver_fdcwd(dctx);
258 
259 	/* fddst (libfoo.la.slibtool.install, build directory) */
260 	if ((fddst = openat(fdcwd,clainame,O_RDWR|O_CREAT|O_TRUNC,0644)) < 0)
261 		return SLBT_SYSTEM_ERROR(dctx,clainame);
262 
263 	/* mapinfo (libfoo.la, build directory) */
264 	if (!(mapinfo = slbt_map_file(fdcwd,entry->arg,SLBT_MAP_INPUT))) {
265 		close(fddst);
266 		return SLBT_SYSTEM_ERROR(dctx,entry->arg);
267 	}
268 
269 	/* srcline */
270 	if (mapinfo->size < sizeof(cfgbuf)) {
271 		buflen  = sizeof(cfgbuf);
272 		srcline = cfgbuf;
273 	} else {
274 		buflen  = mapinfo->size;
275 		srcline = malloc(++buflen);
276 	}
277 
278 	if (!srcline) {
279 		close(fddst);
280 		slbt_unmap_file(mapinfo);
281 		return SLBT_SYSTEM_ERROR(dctx,0);
282 	}
283 
284 	/* copy config, installed=no --> installed=yes */
285 	while (mapinfo->mark < mapinfo->cap) {
286 		if (slbt_mapped_readline(dctx,mapinfo,srcline,buflen) < 0) {
287 			close(fddst);
288 			slbt_unmap_file(mapinfo);
289 			return SLBT_NESTED_ERROR(dctx);
290 		}
291 
292 		dstline = strcmp(srcline,"installed=no\n")
293 			? srcline
294 			: "installed=yes\n";
295 
296 		if (slbt_dprintf(fddst,"%s",dstline) < 0) {
297 			close(fddst);
298 			slbt_unmap_file(mapinfo);
299 			return SLBT_SYSTEM_ERROR(dctx,0);
300 		}
301 	}
302 
303 	if (srcline != cfgbuf)
304 		free(srcline);
305 
306 	/* close, unmap */
307 	close(fddst);
308 	slbt_unmap_file(mapinfo);
309 
310 	/* cp libfoo.la.slibtool.instal /dstdir/libfoo.la */
311 	if (slbt_copy_file(dctx,ectx,clainame,instname))
312 		return SLBT_NESTED_ERROR(dctx);
313 
314 	return 0;
315 }
316 
slbt_exec_install_entry(const struct slbt_driver_ctx * dctx,struct slbt_exec_ctx * ectx,struct argv_entry * entry,struct argv_entry * last,struct argv_entry * dest,char * dstdir,char ** src,char ** dst)317 static int slbt_exec_install_entry(
318 	const struct slbt_driver_ctx *	dctx,
319 	struct slbt_exec_ctx *		ectx,
320 	struct argv_entry *		entry,
321 	struct argv_entry *		last,
322 	struct argv_entry *		dest,
323 	char *				dstdir,
324 	char **				src,
325 	char **				dst)
326 {
327 	int		ret;
328 	int		fdcwd;
329 	const char *	base;
330 	char *		dot;
331 	char *		host;
332 	char *		mark;
333 	char *		slash;
334 	char *		suffix;
335 	char *		dsosuffix;
336 	char		sobuf   [64];
337 	char		target  [PATH_MAX];
338 	char		srcfile [PATH_MAX];
339 	char		dstfile [PATH_MAX];
340 	char		slnkname[PATH_MAX];
341 	char		dlnkname[PATH_MAX];
342 	char		hosttag [PATH_MAX];
343 	char		lasource[PATH_MAX - 8];
344 	bool		fexe = false;
345 	bool		fpe;
346 	bool		frelease;
347 	bool		fdualver;
348 	bool		fstatic;
349 	bool		farchive;
350 	size_t		slen;
351 	size_t		dlen;
352 	struct stat	st;
353 
354 	/* executable wrapper? */
355 	base = (slash = strrchr(entry->arg,'/'))
356 		? ++slash : entry->arg;
357 
358 	strcpy(slnkname,entry->arg);
359 	mark = &slnkname[base - entry->arg];
360 	slen = sizeof(slnkname) - (mark - slnkname);
361 
362 	if ((size_t)snprintf(mark,slen,
363 			".libs/%s.exe.wrapper",
364 			base) >= slen)
365 		return SLBT_BUFFER_ERROR(dctx);
366 
367 	/* fdcwd */
368 	fdcwd = slbt_driver_fdcwd(dctx);
369 
370 	/* fexe */
371 	fexe = !fstatat(fdcwd,slnkname,&st,0);
372 
373 	/* argument suffix */
374 	dot  = strrchr(entry->arg,'.');
375 
376 	/* .lai --> .la */
377 	if (!fexe && dot && !strcmp(dot,".lai"))
378 		dot[3] = 0;
379 
380 	/* srcfile */
381 	if (strlen(entry->arg) + strlen(".libs/") >= (PATH_MAX-1))
382 		return SLBT_BUFFER_ERROR(dctx);
383 
384 	strcpy(lasource,entry->arg);
385 
386 	if ((slash = strrchr(lasource,'/'))) {
387 		*slash++ = 0;
388 		sprintf(srcfile,"%s/.libs/%s",lasource,slash);
389 	} else {
390 		sprintf(srcfile,".libs/%s",lasource);
391 	}
392 
393 	/* executable? ordinary file? */
394 	if (fexe || !dot || strcmp(dot,".la")) {
395 		*src = fexe ? srcfile : (char *)entry->arg;
396 		*dst = dest ? 0 : (char *)last->arg;
397 
398 		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
399 			if (slbt_output_install(dctx,ectx))
400 				return SLBT_NESTED_ERROR(dctx);
401 
402 		return (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
403 			? SLBT_SPAWN_ERROR(dctx) : 0;
404 	}
405 
406 	/* -shrext, dsosuffix */
407 	strcpy(sobuf,dctx->cctx->settings.dsosuffix);
408 	dsosuffix = sobuf;
409 
410 	if ((size_t)snprintf(slnkname,sizeof(slnkname),"%s.shrext",
411 			srcfile) >= sizeof(slnkname))
412 		return SLBT_BUFFER_ERROR(dctx);
413 
414 	if (!fstatat(fdcwd,slnkname,&st,0)) {
415 		if (slbt_readlinkat(fdcwd,slnkname,target,sizeof(target)) < 0)
416 			return SLBT_SYSTEM_ERROR(dctx,slnkname);
417 
418 		if (strncmp(lasource,target,(slen = strlen(lasource))))
419 			return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
420 
421 		if (strncmp(&target[slen],".shrext",7))
422 			return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
423 
424 		strcpy(sobuf,&target[slen+7]);
425 	}
426 
427 	/* legabits? */
428 	if (dctx->cctx->drvflags & SLBT_DRIVER_LEGABITS)
429 		if (slbt_exec_install_library_wrapper(dctx,ectx,entry,dstdir))
430 			return SLBT_NESTED_ERROR(dctx);
431 
432 	/* *dst: consider: cp libfoo.la /dest/dir/libfoo.la */
433 	if ((*dst = dest ? 0 : (char *)last->arg))
434 		if ((dot = strrchr(last->arg,'.')))
435 			if (!(strcmp(dot,".la")))
436 				*dst = dstdir;
437 
438 	/* libfoo.a */
439 	dot = strrchr(srcfile,'.');
440 	strcpy(dot,dctx->cctx->settings.arsuffix);
441 
442 	/* dot/suffix */
443 	strcpy(slnkname,srcfile);
444 	dot = strrchr(slnkname,'.');
445 
446 	/* .libs/libfoo.so.def.host */
447 	slen  = sizeof(slnkname);
448 	slen -= (dot - slnkname);
449 
450 	/* detect -release, exclusively or alongside -version-info */
451 	frelease = false;
452 	fdualver = false;
453 	fstatic  = false;
454 	fpe      = false;
455 
456 	/* static library only? */
457 	sprintf(dot,"%s",dsosuffix);
458 	fstatic = slbt_symlink_is_a_placeholder(fdcwd,slnkname);
459 
460 	/* libfoo.a --> libfoo.so.release */
461 	if (!fstatic) {
462 		sprintf(dot,"%s.release",dsosuffix);
463 		frelease = !fstatat(fdcwd,slnkname,&st,0);
464 	}
465 
466 	/* libfoo.a --> libfoo.so.dualver */
467 	if (!fstatic && !frelease) {
468 		sprintf(dot,"%s.dualver",dsosuffix);
469 		fdualver = !fstatat(fdcwd,slnkname,&st,0);
470 	}
471 
472 	/* libfoo.so.def.{flavor} */
473 	if (fstatic) {
474 		(void)0;
475 
476 	} else if (frelease || fdualver) {
477 		strcpy(dlnkname,slnkname);
478 		slash = strrchr(dlnkname,'/');
479 
480 		dlen  = sizeof(dlnkname);
481 		dlen -= (++slash - dlnkname);
482 		dlen -= 9;
483 
484 		if (slbt_readlinkat(fdcwd,slnkname,slash,dlen))
485 			return SLBT_SYSTEM_ERROR(dctx,slnkname);
486 
487 		if (fdualver) {
488 			/* remove .patch */
489 			if (!(mark = strrchr(dlnkname,'.')))
490 				return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
491 
492 			*mark = 0;
493 
494 			/* remove .minor */
495 			if (!(mark = strrchr(dlnkname,'.')))
496 				return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
497 
498 			*mark = 0;
499 
500 			/* remove .major */
501 			if (!(mark = strrchr(dlnkname,'.')))
502 				return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
503 		} else {
504 			mark = slash += strlen(slash);
505 		}
506 
507 		strcpy(mark,".def.host");
508 
509 		if (slbt_readlinkat(fdcwd,dlnkname,hosttag,sizeof(hosttag)))
510 			return SLBT_SYSTEM_ERROR(dctx,slnkname);
511 	} else {
512 		if ((size_t)snprintf(dot,slen,"%s.def.host",dsosuffix) >= slen)
513 			return SLBT_BUFFER_ERROR(dctx);
514 
515 		if (slbt_readlinkat(fdcwd,frelease ? dlnkname : slnkname,hosttag,sizeof(hosttag)))
516 			return SLBT_SYSTEM_ERROR(dctx,slnkname);
517 	}
518 
519 	/* host/flabor */
520 	if (fstatic) {
521 		(void)0;
522 
523 	} else if (!(host = strrchr(hosttag,'.'))) {
524 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
525 	} else {
526 		host++;
527 	}
528 
529 	/* symlink-based alternate host */
530 	if (!fstatic) {
531 		if (slbt_set_alternate_host(dctx,host,host))
532 			return SLBT_NESTED_ERROR(dctx);
533 
534 		fpe = !strcmp(dctx->cctx->asettings.imagefmt,"pe");
535 	}
536 
537 	/* libfoo.a --> libfoo.so */
538 	strcpy(dot,dsosuffix);
539 
540 	/* libfoo.a installation */
541 	if (!(dctx->cctx->drvflags & SLBT_DRIVER_DISABLE_STATIC))
542 		farchive = true;
543 	else if (fstatic)
544 		farchive = true;
545 	else
546 		farchive = false;
547 
548 	if (farchive)
549 		if (slbt_copy_file(dctx,ectx,
550 				srcfile,
551 				dest ? (char *)dest->arg : *dst))
552 			return SLBT_NESTED_ERROR(dctx);
553 
554 	/* basename */
555 	if ((base = strrchr(slnkname,'/')))
556 		base++;
557 	else
558 		base = slnkname;
559 
560 	/* source (build) symlink target */
561 	if (slbt_readlinkat(fdcwd,slnkname,target,sizeof(target)) < 0) {
562 		/* -all-static? */
563 		if (fstatic)
564 			return 0;
565 
566 		/* -avoid-version? */
567 		if (fstatat(fdcwd,slnkname,&st,0))
568 			return SLBT_SYSTEM_ERROR(dctx,slnkname);
569 
570 		/* dstfile */
571 		if ((size_t)snprintf(dstfile,sizeof(dstfile),"%s/%s",
572 				dstdir,base) >= sizeof(dstfile))
573 			return SLBT_BUFFER_ERROR(dctx);
574 
575 		/* single spawn, no symlinks */
576 		*src = slnkname;
577 		*dst = dest ? 0 : dstfile;
578 
579 		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
580 			if (slbt_output_install(dctx,ectx))
581 				return SLBT_NESTED_ERROR(dctx);
582 
583 		if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
584 			return SLBT_SPAWN_ERROR(dctx);
585 
586 		return 0;
587 	}
588 
589 	/* srcfile: .libs/libfoo.so.x.y.z */
590 	slash = strrchr(srcfile,'/');
591 	strcpy(++slash,target);
592 
593 	/* dstfile */
594 	if (!dest)
595 		if ((size_t)snprintf(dstfile,sizeof(dstfile),"%s/%s",
596 				dstdir,target) >= sizeof(dstfile))
597 			return SLBT_BUFFER_ERROR(dctx);
598 
599 	/* spawn */
600 	*src = srcfile;
601 	*dst = dest ? 0 : dstfile;
602 
603 	if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
604 		if (slbt_output_install(dctx,ectx))
605 			return SLBT_NESTED_ERROR(dctx);
606 
607 	if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
608 		return SLBT_SPAWN_ERROR(dctx);
609 
610 	/* destination symlink: dstdir/libfoo.so */
611 	if ((size_t)snprintf(dlnkname,sizeof(dlnkname),"%s/%s",
612 			dstdir,base) >= sizeof(dlnkname))
613 		return SLBT_BUFFER_ERROR(dctx);
614 
615 	/* create symlink: libfoo.so --> libfoo.so.x.y.z */
616 	if (slbt_create_symlink(
617 			dctx,ectx,
618 			target,dlnkname,
619 			SLBT_SYMLINK_DEFAULT))
620 		return SLBT_NESTED_ERROR(dctx);
621 
622 	if (frelease)
623 		return 0;
624 
625 	/* libfoo.so.x --> libfoo.so.x.y.z */
626 	strcpy(slnkname,target);
627 
628 	if ((suffix = strrchr(slnkname,'.')))
629 		*suffix++ = 0;
630 	else
631 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
632 
633 	if ((dot = strrchr(slnkname,'.')))
634 		*dot++ = 0;
635 	else
636 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
637 
638 	if ((*dot < '0') || (*dot > '9'))
639 		return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
640 
641 	/* libfoo.x.y.z.so? */
642 	if ((suffix[0] < '0') || (suffix[0] > '9')) {
643 		if ((dot = strrchr(slnkname,'.')))
644 			dot++;
645 		else
646 			return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
647 
648 		if ((*dot < '0') || (*dot > '9'))
649 			return SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FLOW);
650 
651 		for (; *suffix; )
652 			*dot++ = *suffix++;
653 
654 		*dot++ = 0;
655 	}
656 
657 	/* destination symlink: dstdir/libfoo.so.x */
658 	if ((size_t)snprintf(dlnkname,sizeof(dlnkname),"%s/%s",
659 			dstdir,slnkname) >= sizeof(dlnkname))
660 		return SLBT_BUFFER_ERROR(dctx);
661 
662 	if (fpe) {
663 		/* copy: .libs/libfoo.so.x.y.z --> libfoo.so.x */
664 		if (slbt_copy_file(
665 				dctx,ectx,
666 				srcfile,
667 				dlnkname))
668 			return SLBT_NESTED_ERROR(dctx);
669 
670 		/* import libraries */
671 		if (slbt_exec_install_import_libraries(
672 				dctx,ectx,
673 				srcfile,
674 				dstdir))
675 			return SLBT_NESTED_ERROR(dctx);
676 	} else {
677 		/* create symlink: libfoo.so.x --> libfoo.so.x.y.z */
678 		if (slbt_create_symlink(
679 				dctx,ectx,
680 				target,dlnkname,
681 				SLBT_SYMLINK_DEFAULT))
682 			return SLBT_NESTED_ERROR(dctx);
683 	}
684 
685 	return 0;
686 }
687 
slbt_exec_install(const struct slbt_driver_ctx * dctx,struct slbt_exec_ctx * ectx)688 int slbt_exec_install(
689 	const struct slbt_driver_ctx *	dctx,
690 	struct slbt_exec_ctx *		ectx)
691 {
692 	int				ret;
693 	int				fdout;
694 	char **				argv;
695 	char **				iargv;
696 	char **				src;
697 	char **				dst;
698 	char *				slash;
699 	char *				optsh;
700 	char *				script;
701 	char *				shtool;
702 	struct slbt_exec_ctx *		actx;
703 	struct argv_meta *		meta;
704 	struct argv_entry *		entry;
705 	struct argv_entry *		copy;
706 	struct argv_entry *		dest;
707 	struct argv_entry *		last;
708 	const struct argv_option *	optv[SLBT_OPTV_ELEMENTS];
709 	char				dstdir[PATH_MAX];
710 
711 	/* dry run */
712 	if (dctx->cctx->drvflags & SLBT_DRIVER_DRY_RUN)
713 		return 0;
714 
715 	/* context */
716 	if (ectx)
717 		actx = 0;
718 	else if ((ret = slbt_get_exec_ctx(dctx,&ectx)))
719 		return ret;
720 	else
721 		actx = ectx;
722 
723 	/* initial state, install mode skin */
724 	slbt_reset_arguments(ectx);
725 	slbt_disable_placeholders(ectx);
726 	iargv = ectx->cargv;
727 	fdout = slbt_driver_fdout(dctx);
728 	optsh = 0;
729 	script = 0;
730 
731 	/* work around non-conforming uses of --mode=install */
732 	if (iargv[1] && (slash = strrchr(iargv[1],'/'))) {
733 		if (!strcmp(++slash,"install-sh")) {
734 			optsh  = *iargv++;
735 			script = *iargv;
736 		}
737 	} else {
738 		slash  = strrchr(iargv[0],'/');
739 		shtool = slash ? ++slash : iargv[0];
740 		shtool = strcmp(shtool,"shtool") ? 0 : shtool;
741 
742 		if (shtool && iargv[1] && !strcmp(iargv[1],"install")) {
743 			iargv++;
744 		} else if (shtool) {
745 			return slbt_install_usage(
746 				fdout,
747 				dctx->program,
748 				0,optv,0,
749 				dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER);
750 		}
751 	}
752 
753 	/* missing arguments? */
754 	argv_optv_init(slbt_install_options,optv);
755 
756 	if (!iargv[1] && (dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_USAGE))
757 		return slbt_install_usage(
758 			fdout,
759 			dctx->program,
760 			0,optv,0,
761 			dctx->cctx->drvflags & SLBT_DRIVER_ANNOTATE_NEVER);
762 
763 	/* <install> argv meta */
764 	if (!(meta = argv_get(
765 			iargv,optv,
766 			dctx->cctx->drvflags & SLBT_DRIVER_VERBOSITY_ERRORS
767 				? ARGV_VERBOSITY_ERRORS
768 				: ARGV_VERBOSITY_NONE,
769 			fdout)))
770 		return slbt_exec_install_fail(
771 			actx,meta,
772 			SLBT_CUSTOM_ERROR(dctx,SLBT_ERR_INSTALL_FAIL));
773 
774 	/* dest, alternate argument vector options */
775 	argv = ectx->altv;
776 	copy = meta->entries;
777 	dest = 0;
778 	last = 0;
779 
780 	if (optsh)
781 		*argv++ = script;
782 
783 	*argv++ = iargv[0];
784 
785 	for (entry=meta->entries; entry->fopt || entry->arg; entry++) {
786 		if (entry->fopt) {
787 			switch (entry->tag) {
788 				case TAG_INSTALL_SYSROOT:
789 					break;
790 
791 				case TAG_INSTALL_COPY:
792 					*argv++ = "-c";
793 					copy = entry;
794 					break;
795 
796 				case TAG_INSTALL_FORCE:
797 					*argv++ = "-f";
798 					break;
799 
800 				case TAG_INSTALL_MKDIR:
801 					*argv++ = "-d";
802 					copy = 0;
803 					break;
804 
805 				case TAG_INSTALL_TARGET_MKDIR:
806 					*argv++ = "-D";
807 					copy = 0;
808 					break;
809 
810 				case TAG_INSTALL_STRIP:
811 					*argv++ = "-s";
812 					break;
813 
814 				case TAG_INSTALL_PRESERVE:
815 					*argv++ = "-p";
816 					break;
817 
818 				case TAG_INSTALL_USER:
819 					*argv++ = "-o";
820 					break;
821 
822 				case TAG_INSTALL_GROUP:
823 					*argv++ = "-g";
824 					break;
825 
826 				case TAG_INSTALL_MODE:
827 					*argv++ = "-m";
828 					break;
829 
830 				case TAG_INSTALL_DSTDIR:
831 					*argv++ = "-t";
832 					dest = entry;
833 					break;
834 			}
835 
836 			if (entry->tag == TAG_INSTALL_SYSROOT) {
837 				(void)0;
838 
839 			} else if (entry->fval) {
840 				*argv++ = (char *)entry->arg;
841 			}
842 		} else
843 			last = entry;
844 	}
845 
846 	/* install */
847 	if (copy) {
848 		/* using alternate argument vector */
849 		if (optsh)
850 			ectx->altv[0] = optsh;
851 
852 		ectx->argv    = ectx->altv;
853 		ectx->program = ectx->altv[0];
854 
855 		/* marks */
856 		src = argv++;
857 		dst = argv++;
858 
859 		/* dstdir */
860 		if (slbt_exec_install_init_dstdir(dctx,dest,last,dstdir))
861 			return slbt_exec_install_fail(
862 				actx,meta,
863 				SLBT_NESTED_ERROR(dctx));
864 
865 		/* install entries one at a time */
866 		for (entry=meta->entries; entry->fopt || entry->arg; entry++)
867 			if (!entry->fopt && (dest || (entry != last)))
868 				if (slbt_exec_install_entry(
869 						dctx,ectx,
870 						entry,last,
871 						dest,dstdir,
872 						src,dst))
873 					return slbt_exec_install_fail(
874 						actx,meta,
875 						SLBT_NESTED_ERROR(dctx));
876 	} else {
877 		/* using original argument vector */
878 		ectx->argv    = ectx->cargv;
879 		ectx->program = ectx->cargv[0];
880 
881 		/* spawn */
882 		if (!(dctx->cctx->drvflags & SLBT_DRIVER_SILENT))
883 			if (slbt_output_install(dctx,ectx))
884 				return SLBT_NESTED_ERROR(dctx);
885 
886 		if (((ret = slbt_spawn(ectx,true)) < 0) || ectx->exitcode)
887 			return slbt_exec_install_fail(
888 				actx,meta,
889 				SLBT_SPAWN_ERROR(dctx));
890 	}
891 
892 	argv_free(meta);
893 	slbt_free_exec_ctx(actx);
894 
895 	return 0;
896 }
897