xref: /illumos-gate/usr/src/lib/libadm/common/pkginfo.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
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 /*LINTLIBRARY*/
32 
33 /*  5-20-92   added newroot functions  */
34 
35 #include <stdio.h>
36 #include <limits.h>
37 #include <stdarg.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <dirent.h>
45 #include <pkginfo.h>
46 #include <pkgstrct.h>
47 #include <pkglocs.h>
48 #include <errno.h>
49 #include "libadm.h"
50 
51 static void	initpkg(struct pkginfo *);
52 static int	rdconfig(struct pkginfo *, char *, char *);
53 static int	ckinfo(char *, char *, char *);
54 static int	ckinst(char *, char *, char *, char *, char *);
55 static int	verscmp(char *, char *);
56 static int	archcmp(char *, char *);
57 static int	compver(char *, char *);
58 
59 /*
60  * Globals:
61  *	pkgdir - specifies the directory where information about packages
62  *	    resides, i.e. the pkginfo file is located in a subdirectory
63  *
64  * Caveats:
65  *	The structure provided via "info" will contain malloc'd information;
66  *	    this will be free'd upon the next call to pkginfo with this
67  *	    same structure.  Application calls must make sure this structure
68  *	    is null on the first call, or else we'll free static memory areas
69  *	If the "pkg" argument is a wildcard specification, the next found
70  *	    instance available which matches the request will be returned
71  *	If the "pkg" argument is a NULL pointer, the structure pointed to
72  *	    via "info" will have its elements deallocated and all files
73  *	    associated with this routine will be closed
74  *
75  * Return codes:
76  *	A non-zero exit code indicates error with "errno" appropriately set:
77  *	    EINVAL - invalid argument
78  *	    ESRCH - there are no more instances of this package around
79  *	    EACCESS - unable to access files which should have been there
80  */
81 
82 /*VARARGS*/
83 int
84 pkginfo(struct pkginfo *info, char *pkginst, ...)
85 {
86 	char	*ckarch, *ckvers;
87 	int	check;
88 	va_list ap;
89 
90 	va_start(ap, pkginst);
91 	if (info == NULL) {
92 		errno = EINVAL;
93 		return (-1);
94 	}
95 	if (pkginst == NULL) {
96 		info->pkginst = NULL;
97 		(void) fpkginfo(info, NULL);
98 		(void) fpkginst(NULL);
99 		return (0);
100 	}
101 	ckarch = va_arg(ap, char *);
102 	ckvers = va_arg(ap, char *);
103 	va_end(ap);
104 
105 	check = 0;
106 	if (pkgnmchk(pkginst, "all", 1)) {
107 		/* wild card specification */
108 		pkginst = fpkginst(pkginst, ckarch, ckvers);
109 		if (pkginst == NULL)
110 			return (-1);
111 	} else {
112 		/* request to check indicated instance */
113 		if (ckarch || ckvers)
114 			check++;
115 	}
116 
117 	info->pkginst = NULL;
118 	if (fpkginfo(info, pkginst))
119 		return (-1);
120 
121 	if (check) {
122 		/*
123 		 * verify that the provided instance matches
124 		 * any arch & vers specs that were provided
125 		 */
126 		if (ckinst(pkginst, info->arch, info->version, ckarch,
127 		    ckvers)) {
128 			errno = ESRCH;
129 			return (-1);
130 		}
131 	}
132 	return (0);
133 }
134 /*ARGSUSED*/
135 
136 int
137 fpkginfo(struct pkginfo *info, char *pkginst)
138 {
139 
140 	if (info == NULL) {
141 		errno = EINVAL;
142 		return (-1);
143 	}
144 
145 	initpkg(info);
146 
147 	if (pkginst == NULL)
148 		return (0);
149 	else if (pkgnmchk(pkginst, "all", 1)) {
150 		errno = EINVAL; /* not an instance identifier */
151 		return (-1);
152 	}
153 	if (pkgdir == NULL)
154 		pkgdir = get_PKGLOC();
155 
156 	if (rdconfig(info, pkginst, NULL)) {
157 		initpkg(info);
158 		return (-1);
159 	}
160 	return (0);
161 }
162 
163 static void
164 initpkg(struct pkginfo *info)
165 {
166 	/* free previously allocated space */
167 	if (info->pkginst) {
168 		free(info->pkginst);
169 		if (info->arch)
170 			free(info->arch);
171 		if (info->version)
172 			free(info->version);
173 		if (info->basedir)
174 			free(info->basedir);
175 		if (info->name)
176 			free(info->name);
177 		if (info->vendor)
178 			free(info->vendor);
179 		if (info->catg)
180 			free(info->catg);
181 	}
182 
183 	info->pkginst = NULL;
184 	info->arch = info->version = NULL;
185 	info->basedir = info->name = NULL;
186 	info->vendor = info->catg = NULL;
187 	info->status = PI_UNKNOWN;
188 }
189 
190 static int
191 rdconfig(struct pkginfo *info, char *pkginst, char *ckvers)
192 {
193 	FILE	*fp;
194 	char	temp[256];
195 	char	*value, *pt, *copy, **memloc;
196 	int	count;
197 
198 	if ((fp = pkginfopen(pkgdir, pkginst)) == NULL) {
199 		errno = EACCES;
200 		return (-1);
201 	}
202 
203 	*temp = '\0';
204 	count = 0;
205 	while (value = fpkgparam(fp, temp)) {
206 		if (strcmp(temp, "ARCH") == 0 ||
207 		    strcmp(temp, "CATEGORY") == 0) {
208 			/* remove all whitespace from value */
209 			pt = copy = value;
210 			while (*pt) {
211 				if (!isspace((unsigned char)*pt))
212 					*copy++ = *pt;
213 				pt++;
214 			}
215 			*copy = '\0';
216 		}
217 		count++;
218 		memloc = NULL;
219 		if (strcmp(temp, "NAME") == 0)
220 			memloc = &info->name;
221 		else if (strcmp(temp, "VERSION") == 0)
222 			memloc = &info->version;
223 		else if (strcmp(temp, "ARCH") == 0)
224 			memloc = &info->arch;
225 		else if (strcmp(temp, "VENDOR") == 0)
226 			memloc = &info->vendor;
227 		else if (strcmp(temp, "BASEDIR") == 0)
228 			memloc = &info->basedir;
229 		else if (strcmp(temp, "CATEGORY") == 0)
230 			memloc = &info->catg;
231 
232 		temp[0] = '\0';
233 		if (memloc == NULL)
234 			continue; /* not a parameter we're looking for */
235 
236 		*memloc = strdup(value);
237 		if (!*memloc) {
238 			(void) fclose(fp);
239 			errno = ENOMEM;
240 			return (-1); /* malloc from strdup failed */
241 		}
242 	}
243 	(void) fclose(fp);
244 
245 	if (!count) {
246 		errno = ESRCH;
247 		return (-1);
248 	}
249 
250 	info->status = (strcmp(pkgdir, get_PKGLOC()) ? PI_SPOOLED :
251 	    PI_INSTALLED);
252 
253 	if (info->status == PI_INSTALLED) {
254 		(void) snprintf(temp, sizeof (temp),
255 		    "%s/%s/!I-Lock!", pkgdir, pkginst);
256 		if (access(temp, 0) == 0)
257 			info->status = PI_PARTIAL;
258 		else {
259 			(void) snprintf(temp, sizeof (temp),
260 			    "%s/%s/!R-Lock!", pkgdir, pkginst);
261 			if (access(temp, 0) == 0)
262 				info->status = PI_PARTIAL;
263 		}
264 	}
265 	info->pkginst = strdup(pkginst);
266 	return (0);
267 }
268 
269 static int
270 ckinst(char *pkginst, char *pkgarch, char *pkgvers, char *ckarch, char *ckvers)
271 {
272 	if (ckarch && archcmp(ckarch, pkgarch))
273 		return (-1);
274 	if (ckvers) {
275 		/* Check for exact version match */
276 		if (verscmp(ckvers, pkgvers)) {
277 			/* Check for compatable version */
278 			if (compver(pkginst, ckvers))
279 				return (-1);
280 		}
281 	}
282 	return (0);
283 }
284 
285 /*VARARGS*/
286 char *
287 fpkginst(char *pkg, ...)
288 {
289 	static char pkginst[PKGSIZ+1];
290 	static DIR *pdirfp;
291 	struct dirent64 *dp;
292 	char	*ckarch, *ckvers;
293 	va_list	ap;
294 
295 	va_start(ap, pkg);
296 
297 	if (pkg == NULL) {
298 		/* request to close or rewind the file */
299 		if (pdirfp) {
300 			(void) closedir(pdirfp);
301 			pdirfp = NULL;
302 		}
303 		return (NULL);
304 	}
305 
306 	ckarch = va_arg(ap, char *);
307 	ckvers = va_arg(ap, char *);
308 	va_end(ap);
309 
310 	if (!pkgdir)
311 		pkgdir = get_PKGLOC();
312 
313 	if (!pdirfp && ((pdirfp = opendir(pkgdir)) == NULL)) {
314 		errno = EACCES;
315 		return (NULL);
316 	}
317 
318 	while ((dp = readdir64(pdirfp)) != NULL) {
319 		if (dp->d_name[0] == '.')
320 			continue;
321 
322 		if (pkgnmchk(dp->d_name, pkg, 0))
323 			continue; /* ignore invalid SVR4 package names */
324 
325 		if (ckinfo(dp->d_name, ckarch, ckvers))
326 			continue;
327 
328 		/*
329 		 * Leave directory open in case user requests another
330 		 * instance.
331 		 */
332 		(void) strcpy(pkginst, dp->d_name);
333 		return (pkginst);
334 	}
335 
336 	errno = ESRCH;
337 	/* close any file we might have open */
338 	(void) closedir(pdirfp);
339 	pdirfp = NULL;
340 	return (NULL);
341 }
342 
343 static int
344 verscmp(char *request, char *actual)
345 {
346 	/* eat leading white space */
347 	while (isspace((unsigned char)*actual))
348 		actual++;
349 	while (isspace((unsigned char)*request))
350 		request++;
351 
352 	while (*request || *actual) {
353 		/*
354 		 * Once the pointers don't match, return an error condition.
355 		 */
356 
357 		if (*request++ != *actual++)
358 			return (-1);
359 
360 		/* eat white space if any in both the strings */
361 		if (isspace((unsigned char)*request)) {
362 			if (*actual && !isspace((unsigned char)*actual))
363 				return (-1);
364 			while (isspace((unsigned char)*request))
365 				request++;
366 			while (isspace((unsigned char)*actual))
367 				actual++;
368 		}
369 	}
370 
371 	return (0);
372 
373 }
374 
375 static int
376 compver(char *pkginst, char *version)
377 {
378 	FILE *fp;
379 	char temp[256];
380 
381 	(void) snprintf(temp, sizeof (temp),
382 	    "%s/%s/install/compver", get_PKGLOC(), pkginst);
383 	if ((fp = fopen(temp, "r")) == NULL)
384 		return (-1);
385 
386 	while (fgets(temp, 256, fp)) {
387 		if (*temp == '#')
388 			continue;
389 		if (verscmp(temp, version) == 0) {
390 			(void) fclose(fp);
391 			return (0);
392 		}
393 	}
394 	(void) fclose(fp);
395 	return (-1);
396 }
397 
398 static int
399 archcmp(char *arch, char *archlist)
400 {
401 	char *pt;
402 
403 	if (arch == NULL)
404 		return (0);
405 
406 	/* arch and archlist must not contain whitespace! */
407 
408 	while (*archlist) {
409 		for (pt = arch; *pt && (*pt == *archlist); )
410 			pt++, archlist++;
411 		if (!*pt && (!*archlist || (*archlist == ',')))
412 			return (0);
413 		while (*archlist) {
414 			if (*archlist++ == ',')
415 				break;
416 		}
417 	}
418 	return (-1);
419 }
420 
421 static int
422 ckinfo(char *inst, char *arch, char *vers)
423 {
424 	FILE	*fp;
425 	char	temp[128];
426 	char	file[PATH_MAX];
427 	char	*pt, *copy, *value, *myarch, *myvers;
428 	int	errflg;
429 
430 	(void) snprintf(file, sizeof (file), "%s/%s/pkginfo", pkgdir, inst);
431 	if ((fp = fopen(file, "r")) == NULL)
432 		return (1);
433 
434 	if ((arch == NULL) && (vers == NULL)) {
435 		(void) fclose(fp);
436 		return (0);
437 	}
438 	temp[0] = '\0';
439 	myarch = myvers = NULL;
440 	while (value = fpkgparam(fp, temp)) {
441 		if (strcmp(temp, "ARCH") == 0) {
442 			/* remove all whitespace from value */
443 			pt = copy = value;
444 			while (*pt) {
445 				if (!isspace((unsigned char)*pt))
446 					*copy++ = *pt;
447 				pt++;
448 			}
449 			*copy = '\0';
450 			myarch = value;
451 			if (myvers)
452 				break;
453 		} else if (strcmp(temp, "VERSION") == 0) {
454 			myvers = value;
455 			if (myarch)
456 				break;
457 		} else
458 			free(value);
459 		temp[0] = '\0';
460 	}
461 	(void) fclose(fp);
462 	errflg = 0;
463 
464 	if (ckinst(inst, myarch, myvers, arch, vers))
465 		errflg++;
466 
467 	if (myarch)
468 		free(myarch);
469 	if (myvers)
470 		free(myvers);
471 
472 	return (errflg);
473 }
474