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
pkginfo(struct pkginfo * info,char * pkginst,...)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
fpkginfo(struct pkginfo * info,char * pkginst)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
initpkg(struct pkginfo * info)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
rdconfig(struct pkginfo * info,char * pkginst,char * ckvers)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
svr4info(struct pkginfo * info,char * pkginst,char * ckvers)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
ckinst(char * pkginst,char * pkgarch,char * pkgvers,char * ckarch,char * ckvers)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 *
fpkginst(char * pkg,...)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 *
svr4inst(char * pkg)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
verscmp(char * request,char * actual)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
compver(char * pkginst,char * version)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
archcmp(char * arch,char * archlist)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
ckinfo(char * inst,char * arch,char * vers)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