xref: /illumos-gate/usr/src/lib/libadm/common/pkginfo.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.2 */
32 /*LINTLIBRARY*/
33 
34 /*  5-20-92   added newroot functions  */
35 
36 #include <stdio.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <dirent.h>
46 #include <pkginfo.h>
47 #include <pkgstrct.h>
48 #include <pkglocs.h>
49 #include <errno.h>
50 #include "libadm.h"
51 
52 static void	initpkg(struct pkginfo *);
53 static char	*svr4inst(char *);
54 static int	rdconfig(struct pkginfo *, char *, char *);
55 static int	svr4info(struct pkginfo *, char *, char *);
56 static int	ckinfo(char *, char *, char *);
57 static int	ckinst(char *, char *, char *, char *, char *);
58 static int	verscmp(char *, char *);
59 static int	archcmp(char *, char *);
60 static int	compver(char *, char *);
61 
62 /*
63  * Globals:
64  *	pkgdir - specifies the directory where information about packages
65  *	    resides, i.e. the pkginfo file is located in a subdirectory
66  *
67  * Caveats:
68  *	The structure provided via "info" will contain malloc'd information;
69  *	    this will be free'd upon the next call to pkginfo with this
70  *	    same structure.  Application calls must make sure this structure
71  *	    is null on the first call, or else we'll free static memory areas
72  *	If the "pkg" argument is a wildcard specification, the next found
73  *	    instance available which matches the request will be returned
74  *	If the "pkg" argument is a NULL pointer, the structure pointed to
75  *	    via "info" will have its elements deallocated and all files
76  *	    associated with this routine will be closed
77  *
78  * Return codes:
79  *	A non-zero exit code indicates error with "errno" appropriately set:
80  *	    EINVAL - invalid argument
81  *	    ESRCH - there are no more instances of this package around
82  *	    EACCESS - unable to access files which should have been there
83  */
84 
85 /*VARARGS*/
86 int
87 pkginfo(struct pkginfo *info, char *pkginst, ...)
88 {
89 	char	*ckarch, *ckvers;
90 	int	check;
91 	va_list ap;
92 
93 	va_start(ap, pkginst);
94 	if (info == NULL) {
95 		errno = EINVAL;
96 		return (-1);
97 	}
98 	if (pkginst == NULL) {
99 		info->pkginst = NULL;
100 		(void) fpkginfo(info, NULL);
101 		(void) fpkginst(NULL);
102 		return (0);
103 	}
104 	ckarch = va_arg(ap, char *);
105 	ckvers = va_arg(ap, char *);
106 	va_end(ap);
107 
108 	check = 0;
109 	if (pkgnmchk(pkginst, "all", 1)) {
110 		/* wild card specification */
111 		pkginst = fpkginst(pkginst, ckarch, ckvers);
112 		if (pkginst == NULL)
113 			return (-1);
114 	} else {
115 		/* request to check indicated instance */
116 		if (ckarch || ckvers)
117 			check++;
118 	}
119 
120 	info->pkginst = NULL;
121 	if (fpkginfo(info, pkginst))
122 		return (-1);
123 
124 	if (check) {
125 		/*
126 		 * verify that the provided instance matches
127 		 * any arch & vers specs that were provided
128 		 */
129 		if (ckinst(pkginst, info->arch, info->version, ckarch,
130 		    ckvers)) {
131 			errno = ESRCH;
132 			return (-1);
133 		}
134 	}
135 	return (0);
136 }
137 /*ARGSUSED*/
138 
139 int
140 fpkginfo(struct pkginfo *info, char *pkginst)
141 {
142 
143 	if (info == NULL) {
144 		errno = EINVAL;
145 		return (-1);
146 	}
147 
148 	initpkg(info);
149 
150 	if (pkginst == NULL)
151 		return (0);
152 	else if (pkgnmchk(pkginst, "all", 1)) {
153 		errno = EINVAL; /* not an instance identifier */
154 		return (-1);
155 	}
156 	if (pkgdir == NULL)
157 		pkgdir = get_PKGLOC();
158 
159 	if (rdconfig(info, pkginst, NULL)) {
160 		initpkg(info);
161 		return (-1);
162 	}
163 	return (0);
164 }
165 
166 static void
167 initpkg(struct pkginfo *info)
168 {
169 	/* free previously allocated space */
170 	if (info->pkginst) {
171 		free(info->pkginst);
172 		if (info->arch)
173 			free(info->arch);
174 		if (info->version)
175 			free(info->version);
176 		if (info->basedir)
177 			free(info->basedir);
178 		if (info->name)
179 			free(info->name);
180 		if (info->vendor)
181 			free(info->vendor);
182 		if (info->catg)
183 			free(info->catg);
184 	}
185 
186 	info->pkginst = NULL;
187 	info->arch = info->version = NULL;
188 	info->basedir = info->name = NULL;
189 	info->vendor = info->catg = NULL;
190 	info->status = PI_UNKNOWN;
191 }
192 
193 static int
194 rdconfig(struct pkginfo *info, char *pkginst, char *ckvers)
195 {
196 	FILE	*fp;
197 	char	temp[256];
198 	char	*value, *pt, *copy, **memloc;
199 	int	count;
200 
201 	if ((fp = pkginfopen(pkgdir, pkginst)) == NULL) {
202 		if ((errno == ENOENT) && strcmp(pkgdir, get_PKGLOC()) == 0)
203 			return (svr4info(info, pkginst, ckvers));
204 
205 		errno = EACCES;
206 		return (-1);
207 	}
208 
209 	*temp = '\0';
210 	count = 0;
211 	while (value = fpkgparam(fp, temp)) {
212 		if (strcmp(temp, "ARCH") == 0 ||
213 		    strcmp(temp, "CATEGORY") == 0) {
214 			/* remove all whitespace from value */
215 			pt = copy = value;
216 			while (*pt) {
217 				if (!isspace((unsigned char)*pt))
218 					*copy++ = *pt;
219 				pt++;
220 			}
221 			*copy = '\0';
222 		}
223 		count++;
224 		memloc = NULL;
225 		if (strcmp(temp, "NAME") == 0)
226 			memloc = &info->name;
227 		else if (strcmp(temp, "VERSION") == 0)
228 			memloc = &info->version;
229 		else if (strcmp(temp, "ARCH") == 0)
230 			memloc = &info->arch;
231 		else if (strcmp(temp, "VENDOR") == 0)
232 			memloc = &info->vendor;
233 		else if (strcmp(temp, "BASEDIR") == 0)
234 			memloc = &info->basedir;
235 		else if (strcmp(temp, "CATEGORY") == 0)
236 			memloc = &info->catg;
237 
238 		temp[0] = '\0';
239 		if (memloc == NULL)
240 			continue; /* not a parameter we're looking for */
241 
242 		*memloc = strdup(value);
243 		if (!*memloc) {
244 			(void) fclose(fp);
245 			errno = ENOMEM;
246 			return (-1); /* malloc from strdup failed */
247 		}
248 	}
249 	(void) fclose(fp);
250 
251 	if (!count) {
252 		errno = ESRCH;
253 		return (-1);
254 	}
255 
256 	info->status = (strcmp(pkgdir, get_PKGLOC()) ? PI_SPOOLED :
257 	    PI_INSTALLED);
258 
259 	if (info->status == PI_INSTALLED) {
260 		(void) sprintf(temp, "%s/%s/!I-Lock!", pkgdir, pkginst);
261 		if (access(temp, 0) == 0)
262 			info->status = PI_PARTIAL;
263 		else {
264 			(void) sprintf(temp, "%s/%s/!R-Lock!", pkgdir, pkginst);
265 			if (access(temp, 0) == 0)
266 				info->status = PI_PARTIAL;
267 		}
268 	}
269 	info->pkginst = strdup(pkginst);
270 	return (0);
271 }
272 
273 static int
274 svr4info(struct pkginfo *info, char *pkginst, char *ckvers)
275 {
276 	static DIR *pdirfp;
277 	struct stat64 status;
278 	FILE *fp;
279 	char *pt, path[128], line[128];
280 	char	temp[PKGSIZ+1];
281 
282 	if (strcmp(pkginst, "all")) {
283 		if (pdirfp) {
284 			(void) closedir(pdirfp);
285 			pdirfp = NULL;
286 		}
287 		/* determine pkginst - remove '.*' extension, if any */
288 		(void) strncpy(temp, pkginst, PKGSIZ);
289 		if (((pt = strchr(temp, '.')) != NULL) && strcmp(pt, ".*") == 0)
290 			*pt = '\0';
291 	}
292 
293 	/* look in /usr/options direcotry for 'name' file */
294 	(void) sprintf(path, "%s/%s.name", get_PKGOLD(), temp);
295 	if (lstat64(path, &status)) {
296 		errno = (errno == ENOENT) ? ESRCH : EACCES;
297 		return (-1);
298 	}
299 	if ((status.st_mode & S_IFMT) != S_IFREG) {
300 		errno = ESRCH;
301 		return (-1);
302 	}
303 	if ((fp = fopen(path, "r")) == NULL) {
304 		errno = (errno == ENOENT) ? ESRCH : EACCES;
305 		return (-1);
306 	}
307 
308 	/* /usr/options/xxx.name exists */
309 	(void) fgets(line, 128, fp);
310 	(void) fclose(fp);
311 	if (pt = strchr(line, '\n'))
312 		*pt = '\0'; /* remove trailing newline */
313 	if (pt = strchr(line, ':'))
314 		*pt++ = '\0'; /* assumed version specification */
315 
316 	if (info) {
317 		info->name = strdup(line);
318 		info->pkginst = strdup(temp);
319 		if (!info->name || !info->pkginst) {
320 			errno = ENOMEM;
321 			return (-1);
322 		}
323 		info->status = PI_PRESVR4;
324 		info->version = NULL;
325 	}
326 
327 	if (pt) {
328 		/* eat leading space off of version spec */
329 		while (isspace((unsigned char)*pt))
330 			pt++;
331 	}
332 	if (ckvers && verscmp(ckvers, pt)) {
333 		errno = ESRCH;
334 		return (-1);
335 	}
336 	if (info && *pt)
337 		info->version = strdup(pt);
338 	return (0);
339 }
340 
341 static int
342 ckinst(char *pkginst, char *pkgarch, char *pkgvers, char *ckarch, char *ckvers)
343 {
344 	if (ckarch && archcmp(ckarch, pkgarch))
345 		return (-1);
346 	if (ckvers) {
347 		/* Check for exact version match */
348 		if (verscmp(ckvers, pkgvers)) {
349 			/* Check for compatable version */
350 			if (compver(pkginst, ckvers))
351 				return (-1);
352 		}
353 	}
354 	return (0);
355 }
356 
357 /*VARARGS*/
358 char *
359 fpkginst(char *pkg, ...)
360 {
361 	static char pkginst[PKGSIZ+1];
362 	static DIR *pdirfp;
363 	struct dirent64 *dp;
364 	char	*pt, *ckarch, *ckvers;
365 	va_list	ap;
366 
367 	va_start(ap, pkg);
368 
369 	if (pkg == NULL) {
370 		/* request to close or rewind the file */
371 		if (pdirfp) {
372 			(void) closedir(pdirfp);
373 			pdirfp = NULL;
374 		}
375 		(void) svr4inst(NULL); /* close any files used here */
376 		return (NULL);
377 	}
378 
379 	ckarch = va_arg(ap, char *);
380 	ckvers = va_arg(ap, char *);
381 	va_end(ap);
382 
383 	if (!pkgdir)
384 		pkgdir = get_PKGLOC();
385 
386 	if (!pdirfp && ((pdirfp = opendir(pkgdir)) == NULL)) {
387 		errno = EACCES;
388 		return (NULL);
389 	}
390 
391 	while ((dp = readdir64(pdirfp)) != NULL) {
392 		if (dp->d_name[0] == '.')
393 			continue;
394 
395 		if (pkgnmchk(dp->d_name, pkg, 0))
396 			continue; /* ignore invalid SVR4 package names */
397 
398 		if (ckinfo(dp->d_name, ckarch, ckvers))
399 			continue;
400 
401 		/*
402 		 * Leave directory open in case user requests another
403 		 * instance.
404 		 */
405 		(void) strcpy(pkginst, dp->d_name);
406 		return (pkginst);
407 	}
408 
409 	/*
410 	 * If we are searching the directory which contains info about
411 	 * installed packages, check the pre-svr4 directory for an instance
412 	 * and be sure it matches any version specification provided to us
413 	 */
414 	if (strcmp(pkgdir, get_PKGLOC()) == 0 && (ckarch == NULL)) {
415 		/* search for pre-SVR4 instance */
416 		if (pt = svr4inst(pkg))
417 			return (pt);
418 	}
419 	errno = ESRCH;
420 	/* close any file we might have open */
421 	(void) closedir(pdirfp);
422 	pdirfp = NULL;
423 	return (NULL);
424 }
425 /*ARGSUSED*/
426 
427 static char *
428 svr4inst(char *pkg)
429 {
430 	static char pkginst[PKGSIZ];
431 	static DIR *pdirfp;
432 	struct dirent64 *dp;
433 	struct stat64	status;	/* file status buffer */
434 	char	*pt;
435 	char	path[PATH_MAX];
436 
437 	if (pkg == NULL) {
438 		if (pdirfp) {
439 			(void) closedir(pdirfp);
440 			pdirfp = NULL;
441 		}
442 		return (NULL);
443 	}
444 
445 	if (!pdirfp && ((pdirfp = opendir(get_PKGOLD())) == NULL))
446 		return (NULL);
447 
448 	while ((dp = readdir64(pdirfp)) != NULL) {
449 		if (dp->d_name[0] == '.')
450 			continue;
451 		pt = strchr(dp->d_name, '.');
452 		if (pt && strcmp(pt, ".name") == 0) {
453 			/* the pkgnmchk function works on .name extensions */
454 			if (pkgnmchk(dp->d_name, pkg, 1))
455 				continue;
456 			(void) sprintf(path, "%s/%s", get_PKGOLD(), dp->d_name);
457 			if (lstat64(path, &status))
458 				continue;
459 			if ((status.st_mode & S_IFMT) != S_IFREG)
460 				continue;
461 			*pt = '\0';
462 			(void) strcpy(pkginst, dp->d_name);
463 			return (pkginst);
464 		}
465 	}
466 	(void) closedir(pdirfp);
467 	pdirfp = NULL;
468 	return (NULL);
469 }
470 
471 static int
472 verscmp(char *request, char *actual)
473 {
474 	/* eat leading white space */
475 	while (isspace((unsigned char)*actual))
476 		actual++;
477 	while (isspace((unsigned char)*request))
478 		request++;
479 
480 	while (*request || *actual) {
481 		/*
482 		 * Once the pointers don't match, return an error condition.
483 		 */
484 
485 		if (*request++ != *actual++)
486 			return (-1);
487 
488 		/* eat white space if any in both the strings */
489 		if (isspace((unsigned char)*request)) {
490 			if (*actual && !isspace((unsigned char)*actual))
491 				return (-1);
492 			while (isspace((unsigned char)*request))
493 				request++;
494 			while (isspace((unsigned char)*actual))
495 				actual++;
496 		}
497 	}
498 
499 	return (0);
500 
501 }
502 
503 static int
504 compver(char *pkginst, char *version)
505 {
506 	FILE *fp;
507 	char temp[256];
508 
509 	(void) sprintf(temp, "%s/%s/install/compver", get_PKGLOC(), pkginst);
510 	if ((fp = fopen(temp, "r")) == NULL)
511 		return (-1);
512 
513 	while (fgets(temp, 256, fp)) {
514 		if (*temp == '#')
515 			continue;
516 		if (verscmp(temp, version) == 0) {
517 			(void) fclose(fp);
518 			return (0);
519 		}
520 	}
521 	(void) fclose(fp);
522 	return (-1);
523 }
524 
525 static int
526 archcmp(char *arch, char *archlist)
527 {
528 	char *pt;
529 
530 	if (arch == NULL)
531 		return (0);
532 
533 	/* arch and archlist must not contain whitespace! */
534 
535 	while (*archlist) {
536 		for (pt = arch; *pt && (*pt == *archlist); )
537 			pt++, archlist++;
538 		if (!*pt && (!*archlist || (*archlist == ',')))
539 			return (0);
540 		while (*archlist) {
541 			if (*archlist++ == ',')
542 				break;
543 		}
544 	}
545 	return (-1);
546 }
547 
548 static int
549 ckinfo(char *inst, char *arch, char *vers)
550 {
551 	FILE	*fp;
552 	char	temp[128];
553 	char	file[PATH_MAX];
554 	char	*pt, *copy, *value, *myarch, *myvers;
555 	int	errflg;
556 
557 	(void) sprintf(file, "%s/%s/pkginfo", pkgdir, inst);
558 	if ((fp = fopen(file, "r")) == NULL)
559 		return (1);
560 
561 	if ((arch == NULL) && (vers == NULL)) {
562 		(void) fclose(fp);
563 		return (0);
564 	}
565 	temp[0] = '\0';
566 	myarch = myvers = NULL;
567 	while (value = fpkgparam(fp, temp)) {
568 		if (strcmp(temp, "ARCH") == 0) {
569 			/* remove all whitespace from value */
570 			pt = copy = value;
571 			while (*pt) {
572 				if (!isspace((unsigned char)*pt))
573 					*copy++ = *pt;
574 				pt++;
575 			}
576 			*copy = '\0';
577 			myarch = value;
578 			if (myvers)
579 				break;
580 		} else if (strcmp(temp, "VERSION") == 0) {
581 			myvers = value;
582 			if (myarch)
583 				break;
584 		} else
585 			free(value);
586 		temp[0] = '\0';
587 	}
588 	(void) fclose(fp);
589 	errflg = 0;
590 
591 	if (ckinst(inst, myarch, myvers, arch, vers))
592 		errflg++;
593 
594 	if (myarch)
595 		free(myarch);
596 	if (myvers)
597 		free(myvers);
598 
599 	return (errflg);
600 }
601