xref: /illumos-gate/usr/src/contrib/ast/src/lib/libdll/dllscan.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1997-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * Glenn Fowler
23  * AT&T Research
24  */
25 
26 #define _DLLINFO_PRIVATE_ \
27 	char*	sib[3]; \
28 	char	sibbuf[64]; \
29 	char	envbuf[64];
30 
31 #define _DLLSCAN_PRIVATE_ \
32 	Dllent_t	entry; \
33 	Uniq_t*		uniq; \
34 	int		flags; \
35 	Vmalloc_t*	vm; \
36 	Dt_t*		dict; \
37 	Dtdisc_t	disc; \
38 	FTS*		fts; \
39 	FTSENT*		ent; \
40 	Sfio_t*		tmp; \
41 	char**		sb; \
42 	char**		sp; \
43 	char*		pb; \
44 	char*		pp; \
45 	char*		pe; \
46 	int		off; \
47 	int		prelen; \
48 	int		suflen; \
49 	char**		lib; \
50 	char		nam[64]; \
51 	char		pat[64]; \
52 	char		buf[64];
53 
54 #define DLL_MATCH_DONE		0x8000
55 #define DLL_MATCH_NAME		0x4000
56 #define DLL_MATCH_VERSION	0x2000
57 
58 #include <ast.h>
59 #include <cdt.h>
60 #include <ctype.h>
61 #include <error.h>
62 #include <fts.h>
63 #include <vmalloc.h>
64 
65 typedef struct Uniq_s
66 {
67 	Dtlink_t	link;
68 	char		name[1];
69 } Uniq_t;
70 
71 #include <dlldefs.h>
72 
73 static char		bin[] = "bin";
74 static char		lib[] = "lib";
75 
76 /*
77  * we need a sibling dir in PATH to search for dlls
78  * the confstr LIBPATH provides the local info
79  *
80  *	<sibling-dir>[:<env-var>[:<host-pattern>]][,...]
81  *
82  * if <host-pattern> is present then it must match confstr HOSTTYPE
83  */
84 
85 Dllinfo_t*
dllinfo(void)86 dllinfo(void)
87 {
88 	register char*		s;
89 	register char*		h;
90 	char*			d;
91 	char*			v;
92 	char*			p;
93 	int			dn;
94 	int			vn;
95 	int			pn;
96 	char			pat[256];
97 
98 	static Dllinfo_t	info;
99 
100 	if (!info.sibling)
101 	{
102 		info.sibling = info.sib;
103 		if (*(s = astconf("LIBPATH", NiL, NiL)))
104 		{
105 			while (*s == ':' || *s == ',')
106 				s++;
107 			if (*s)
108 			{
109 				h = 0;
110 				for (;;)
111 				{
112 					for (d = s; *s && *s != ':' && *s != ','; s++);
113 					if (!(dn = s - d))
114 						d = 0;
115 					if (*s == ':')
116 					{
117 						for (v = ++s; *s && *s != ':' && *s != ','; s++);
118 						if (!(vn = s - v))
119 							v = 0;
120 						if (*s == ':')
121 						{
122 							for (p = ++s; *s && *s != ':' && *s != ','; s++);
123 							if (!(pn = s - p))
124 								p = 0;
125 						}
126 						else
127 							p = 0;
128 					}
129 					else
130 					{
131 						v = 0;
132 						p = 0;
133 					}
134 					while (*s && *s++ != ',');
135 					if (!*s || !p || !h && !*(h = astconf("HOSTTYPE", NiL, NiL)))
136 						break;
137 					if (pn >= sizeof(pat))
138 						pn = sizeof(pat) - 1;
139 					memcpy(pat, p, pn);
140 					pat[pn] = 0;
141 					if (strmatch(h, pat))
142 						break;
143 				}
144 				if (d && dn < sizeof(info.sibbuf))
145 				{
146 					memcpy(info.sibbuf, d, dn);
147 					info.sibling[0] = info.sibbuf;
148 				}
149 				if (v && vn < sizeof(info.envbuf))
150 				{
151 					memcpy(info.envbuf, v, vn);
152 					info.env = info.envbuf;
153 				}
154 			}
155 		}
156 		if (!info.sibling[0] || streq(info.sibling[0], bin))
157 			info.sibling[0] = bin;
158 		if (!streq(info.sibling[0], lib))
159 			info.sibling[1] = lib;
160 		if (!info.env)
161 			info.env = "LD_LIBRARY_PATH";
162 		info.prefix = astconf("LIBPREFIX", NiL, NiL);
163 		info.suffix = astconf("LIBSUFFIX", NiL, NiL);
164 		if (streq(info.suffix, ".dll"))
165 			info.flags |= DLL_INFO_PREVER;
166 		else
167 			info.flags |= DLL_INFO_DOTVER;
168 	}
169 	return &info;
170 }
171 
172 /*
173  * fts version sort order
174  * higher versions appear first
175  */
176 
177 static int
vercmp(FTSENT * const * ap,FTSENT * const * bp)178 vercmp(FTSENT* const* ap, FTSENT* const* bp)
179 {
180 	register unsigned char*	a = (unsigned char*)(*ap)->fts_name;
181 	register unsigned char*	b = (unsigned char*)(*bp)->fts_name;
182 	register int		n;
183 	register int		m;
184 	char*			e;
185 
186 	for (;;)
187 	{
188 		if (isdigit(*a) && isdigit(*b))
189 		{
190 			m = strtol((char*)a, &e, 10);
191 			a = (unsigned char*)e;
192 			n = strtol((char*)b, &e, 10);
193 			b = (unsigned char*)e;
194 			if (n -= m)
195 				return n;
196 		}
197 		if (n = *a - *b)
198 			return n;
199 		if (!*a++)
200 			return *b ? 0 : -1;
201 		if (!*b++)
202 			return 1;
203 	}
204 	/*NOTREACHED*/
205 }
206 
207 /*
208  * open a scan stream
209  */
210 
211 Dllscan_t*
dllsopen(const char * lib,const char * name,const char * version)212 dllsopen(const char* lib, const char* name, const char* version)
213 {
214 	register char*	s;
215 	register char*	t;
216 	Dllscan_t*	scan;
217 	Dllinfo_t*	info;
218 	Vmalloc_t*	vm;
219 	int		i;
220 	int		j;
221 	int		k;
222 	char		buf[32];
223 
224 	if (!(vm = vmopen(Vmdcheap, Vmlast, 0)))
225 		return 0;
226 	if (lib && *lib && (*lib != '-' || *(lib + 1)))
227 	{
228 		/*
229 		 * grab the local part of the library id
230 		 */
231 
232 		if (s = strrchr(lib, ':'))
233 			lib = (const char*)(s + 1);
234 		i = 2 * sizeof(char**) + strlen(lib) + 5;
235 	}
236 	else
237 	{
238 		lib = 0;
239 		i = 0;
240 	}
241 	if (version && (!*version || *version == '-' && !*(version + 1)))
242 		version = 0;
243 	if (!(scan = vmnewof(vm, 0, Dllscan_t, 1, i)) || !(scan->tmp = sfstropen()))
244 	{
245 		vmclose(vm);
246 		return 0;
247 	}
248 	scan->vm = vm;
249 	info = dllinfo();
250 	scan->flags = info->flags;
251 	if (lib)
252 	{
253 		scan->lib = (char**)(scan + 1);
254 		s = *scan->lib = (char*)(scan->lib + 2);
255 		sfsprintf(s, i, "lib/%s", lib);
256 		if (!version && streq(info->suffix, ".dylib"))
257 			version = "0.0";
258 	}
259 	if (!name || !*name || *name == '-' && !*(name + 1))
260 	{
261 		name = (const char*)"?*";
262 		scan->flags |= DLL_MATCH_NAME;
263 	}
264 	else if (t = strrchr(name, '/'))
265 	{
266 		if (!(scan->pb = vmnewof(vm, 0, char, t - (char*)name, 2)))
267 			goto bad;
268 		memcpy(scan->pb, name, t - (char*)name);
269 		name = (const char*)(t + 1);
270 	}
271 	if (name)
272 	{
273 		i = strlen(name);
274 		j = strlen(info->prefix);
275 		if (!j || i > j && strneq(name, info->prefix, j))
276 		{
277 			k = strlen(info->suffix);
278 			if (i > k && streq(name + i - k, info->suffix))
279 			{
280 				i -= j + k;
281 				if (!(t = vmnewof(vm, 0, char, i, 1)))
282 					goto bad;
283 				memcpy(t, name + j, i);
284 				t[i] = 0;
285 				name = (const char*)t;
286 			}
287 		}
288 		if (!version)
289 			for (t = (char*)name; *t; t++)
290 				if ((*t == '-' || *t == '.' || *t == '?') && isdigit(*(t + 1)))
291 				{
292 					if (*t != '-')
293 						scan->flags |= DLL_MATCH_VERSION;
294 					version = t + 1;
295 					if (!(s = vmnewof(vm, 0, char, t - (char*)name, 1)))
296 						goto bad;
297 					memcpy(s, name, t - (char*)name);
298 					name = (const char*)s;
299 					break;
300 				}
301 	}
302 	if (!version)
303 	{
304 		scan->flags |= DLL_MATCH_VERSION;
305 		sfsprintf(scan->nam, sizeof(scan->nam), "%s%s%s", info->prefix, name, info->suffix);
306 	}
307 	else if (scan->flags & DLL_INFO_PREVER)
308 	{
309 		sfprintf(scan->tmp, "%s%s", info->prefix, name);
310 		for (s = (char*)version; *s; s++)
311 			if (isdigit(*s))
312 				sfputc(scan->tmp, *s);
313 		sfprintf(scan->tmp, "%s", info->suffix);
314 		if (!(s = sfstruse(scan->tmp)))
315 			goto bad;
316 		sfsprintf(scan->nam, sizeof(scan->nam), "%s", s);
317 	}
318 	else
319 		sfsprintf(scan->nam, sizeof(scan->nam), "%s%s%s.%s", info->prefix, name, info->suffix, version);
320 	if (scan->flags & (DLL_MATCH_NAME|DLL_MATCH_VERSION))
321 	{
322 		if (scan->flags & DLL_INFO_PREVER)
323 		{
324 			if (!version)
325 				version = "*([0-9_])";
326 			else
327 			{
328 				t = buf;
329 				for (s = (char*)version; *s; s++)
330 					if (isdigit(*s) && t < &buf[sizeof(buf)-1])
331 						*t++ = *s;
332 				*t = 0;
333 				version = (const char*)buf;
334 			}
335 			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s%s%s", info->prefix, name, version, info->suffix);
336 		}
337 		else if (version)
338 			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s@(%s([-.])%s%s|%s.%s)", info->prefix, name, strchr(version, '.') ? "@" : "?", version, info->suffix, info->suffix, version);
339 		else
340 		{
341 			version = "*([0-9.])";
342 			sfsprintf(scan->pat, sizeof(scan->pat), "%s%s@(?([-.])%s%s|%s%s)", info->prefix, name, version, info->suffix, info->suffix, version);
343 		}
344 	}
345 	scan->sp = scan->sb = (scan->lib ? scan->lib : info->sibling);
346 	scan->prelen = strlen(info->prefix);
347 	scan->suflen = strlen(info->suffix);
348 	return scan;
349  bad:
350 	dllsclose(scan);
351 	return 0;
352 }
353 
354 /*
355  * close a scan stream
356  */
357 
358 int
dllsclose(Dllscan_t * scan)359 dllsclose(Dllscan_t* scan)
360 {
361 	if (!scan)
362 		return -1;
363 	if (scan->fts)
364 		fts_close(scan->fts);
365 	if (scan->dict)
366 		dtclose(scan->dict);
367 	if (scan->tmp)
368 		sfclose(scan->tmp);
369 	if (scan->vm)
370 		vmclose(scan->vm);
371 	return 0;
372 }
373 
374 /*
375  * return the next scan stream entry
376  */
377 
378 Dllent_t*
dllsread(register Dllscan_t * scan)379 dllsread(register Dllscan_t* scan)
380 {
381 	register char*		p;
382 	register char*		b;
383 	register char*		t;
384 	register Uniq_t*	u;
385 	register int		n;
386 	register int		m;
387 
388 	if (scan->flags & DLL_MATCH_DONE)
389 		return 0;
390  again:
391 	do
392 	{
393 		while (!scan->ent || !(scan->ent = scan->ent->fts_link))
394 		{
395 			if (scan->fts)
396 			{
397 				fts_close(scan->fts);
398 				scan->fts = 0;
399 			}
400 			if (!scan->pb)
401 				scan->pb = pathbin();
402 			else if (!*scan->sp)
403 			{
404 				scan->sp = scan->sb;
405 				if (!*scan->pe++)
406 					return 0;
407 				scan->pb = scan->pe;
408 			}
409 			for (p = scan->pp = scan->pb; *p && *p != ':'; p++)
410 				if (*p == '/')
411 					scan->pp = p;
412 			scan->pe = p;
413 			if (*scan->sp == bin)
414 				scan->off = sfprintf(scan->tmp, "%-.*s", scan->pe - scan->pb, scan->pb);
415 			else
416 				scan->off = sfprintf(scan->tmp, "%-.*s/%s", scan->pp - scan->pb, scan->pb, *scan->sp);
417 			scan->sp++;
418 			if (!(scan->flags & DLL_MATCH_NAME))
419 			{
420 				sfprintf(scan->tmp, "/%s", scan->nam);
421 				if (!(p = sfstruse(scan->tmp)))
422 					return 0;
423 				if (!eaccess(p, R_OK))
424 				{
425 					b = scan->nam;
426 					goto found;
427 				}
428 				if (errno != ENOENT)
429 					continue;
430 			}
431 			if (scan->flags & (DLL_MATCH_NAME|DLL_MATCH_VERSION))
432 			{
433 				sfstrseek(scan->tmp, scan->off, SEEK_SET);
434 				if (!(t = sfstruse(scan->tmp)))
435 					return 0;
436 				if ((scan->fts = fts_open((char**)t, FTS_LOGICAL|FTS_NOPOSTORDER|FTS_ONEPATH, vercmp)) && (scan->ent = fts_read(scan->fts)) && (scan->ent = fts_children(scan->fts, FTS_NOSTAT)))
437 					break;
438 			}
439 		}
440 	} while (!strmatch(scan->ent->fts_name, scan->pat));
441 	b = scan->ent->fts_name;
442 	sfstrseek(scan->tmp, scan->off, SEEK_SET);
443 	sfprintf(scan->tmp, "/%s", b);
444 	if (!(p = sfstruse(scan->tmp)))
445 		return 0;
446  found:
447 	b = scan->buf + sfsprintf(scan->buf, sizeof(scan->buf), "%s", b + scan->prelen);
448 	if (!(scan->flags & DLL_INFO_PREVER))
449 		while (b > scan->buf)
450 		{
451 			if (!isdigit(*(b - 1)) && *(b - 1) != '.')
452 				break;
453 			b--;
454 		}
455 	b -= scan->suflen;
456 	if (b > (scan->buf + 2) && (*(b - 1) == 'g' || *(b - 1) == 'O') && *(b - 2) == '-')
457 		b -= 2;
458 	n = m = 0;
459 	for (t = b; t > scan->buf; t--)
460 		if (isdigit(*(t - 1)))
461 			n = 1;
462 		else if (*(t - 1) != m)
463 		{
464 			if (*(t - 1) == '.' || *(t - 1) == '-' || *(t - 1) == '_')
465 			{
466 				n = 1;
467 				if (m)
468 				{
469 					m = -1;
470 					t--;
471 					break;
472 				}
473 				m = *(t - 1);
474 			}
475 			else
476 				break;
477 		}
478 	if (n)
479 	{
480 		if (isdigit(t[0]) && isdigit(t[1]) && !isdigit(t[2]))
481 			n = (t[0] - '0') * 10 + (t[1] - '0');
482 		else if (isdigit(t[1]) && isdigit(t[2]) && !isdigit(t[3]))
483 			n = (t[1] - '0') * 10 + (t[2] - '0');
484 		else
485 			n = 0;
486 		if (n && !(n & (n - 1)))
487 		{
488 			if (!isdigit(t[0]))
489 				t++;
490 			m = *(t += 2);
491 		}
492 		if (m || (scan->flags & DLL_INFO_PREVER))
493 			b = t;
494 	}
495 	*b = 0;
496 	if (!*(b = scan->buf))
497 		goto again;
498 	if (scan->uniq)
499 	{
500 		if (!scan->dict)
501 		{
502 			scan->disc.key = offsetof(Uniq_t, name);
503 			scan->disc.size = 0;
504 			scan->disc.link = offsetof(Uniq_t, link);
505 			if (!(scan->dict = dtopen(&scan->disc, Dtset)))
506 				return 0;
507 			dtinsert(scan->dict, scan->uniq);
508 		}
509 		if (dtmatch(scan->dict, b))
510 			goto again;
511 		if (!(u = vmnewof(scan->vm, 0, Uniq_t, 1, strlen(b))))
512 			return 0;
513 		strcpy(u->name, b);
514 		dtinsert(scan->dict, u);
515 	}
516 	else if (!(scan->flags & DLL_MATCH_NAME))
517 		scan->flags |= DLL_MATCH_DONE;
518 	else if (!(scan->uniq = vmnewof(scan->vm, 0, Uniq_t, 1, strlen(b))))
519 		return 0;
520 	else
521 		strcpy(scan->uniq->name, b);
522 	scan->entry.name = b;
523 	scan->entry.path = p;
524 	errorf("dll", NiL, -1, "dllsread: %s bound to %s", b, p);
525 	return &scan->entry;
526 }
527