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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28/* All Rights Reserved */
29
30
31
32#include <stdio.h>
33#include <ctype.h>
34#include <string.h>
35#include <signal.h>
36#include <errno.h>
37#include <stdlib.h>
38#include <valtools.h>
39#include "pkginfo.h"
40#include "pkglib.h"
41#include "pkglibmsgs.h"
42#include "pkgstrct.h"
43#include "pkglocale.h"
44
45extern char	*pkgdir; 		/* WHERE? */
46
47/* libadm.a */
48extern CKMENU	*allocmenu(char *label, int attr);
49extern int	ckitem(CKMENU *menup, char *item[], short max, char *defstr,
50				char *error, char *help, char *prompt);
51extern int	pkgnmchk(register char *pkg, register char *spec,
52				int presvr4flg);
53extern int	fpkginfo(struct pkginfo *info, char *pkginst);
54extern char	*fpkginst(char *pkg, ...);
55extern int	setinvis(CKMENU *menup, char *choice);
56extern int	setitem(CKMENU *menup, char *choice);
57
58#define	CMDSIZ			512
59#define	LSIZE			256
60#define	MAXSIZE			128
61#define	MALLOCSIZ		128
62#define	MAX_CAT_ARGS	64
63#define	MAX_CAT_LEN		16
64
65static int	cont_in_list = 0;	/* live continuation */
66static char	cont_keyword[PKGSIZ+1];	/* the continuation keyword */
67
68/*
69 * Allocate memory for the next package name. This function attempts the
70 * allocation and if that succeeds, returns a pointer to the new memory
71 * location and increments "n". Otherwise, it returens NULL and n is
72 * unchanged.
73 */
74static char **
75next_n(int *n, char **nwpkg)
76{
77	int loc_n = *n;
78
79	if ((++loc_n % MALLOCSIZ) == 0) {
80		nwpkg = (char **)realloc(nwpkg,
81			(loc_n+MALLOCSIZ) * sizeof (char **));
82		if (nwpkg == NULL) {
83			progerr(pkg_gt(ERR_MEMORY), errno);
84			errno = ENOMEM;
85			return (NULL);
86		}
87	}
88
89	*n = loc_n;
90	return (nwpkg);
91}
92
93/*
94 * This informs gpkglist() to put a keyword at the head of the pkglist. This
95 * was originally intended for live continue, but it may have other
96 * applications as well.
97 */
98void
99pkglist_cont(char *keyword)
100{
101	cont_in_list = 1;
102	(void) strncpy(cont_keyword, keyword, PKGSIZ);
103}
104
105/*
106 * This function constructs the list of packages that the user wants managed.
107 * It may be a list on the command line, it may be some or all of the
108 * packages in a directory or it may be a continuation from a previous
109 * dryrun. It may also be a list of pkgs gathered from the CATEGORY parameter
110 * in a spooled or installed pkginfo file.
111 */
112char **
113gpkglist(char *dir, char **pkg, char **catg)
114{
115	struct _choice_ *chp;
116	struct pkginfo info;
117	char	*inst;
118	CKMENU	*menup;
119	char	temp[LSIZE];
120	char	*savedir, **nwpkg;
121	int	i, n;
122
123	savedir = pkgdir;
124	pkgdir = dir;
125
126	info.pkginst = NULL; /* initialize for memory handling */
127	if (pkginfo(&info, "all", NULL, NULL)) {
128		errno = ENOPKG; /* contains no valid packages */
129		pkgdir = savedir;
130		return (NULL);
131	}
132
133	/*
134	 * If no explicit list was provided and this is not a continuation
135	 * (implying a certain level of direction on the caller's part)
136	 * present a menu of available packages for installation.
137	 */
138	if (pkg[0] == NULL && !cont_in_list) {
139		menup = allocmenu(pkg_gt(HEADER), CKALPHA);
140		if (setinvis(menup, "all")) {
141			errno = EFAULT;
142			return (NULL);
143		}
144		do {
145			/* bug id 1087404 */
146			if (!info.pkginst || !info.name || !info.arch ||
147			    !info.version)
148				continue;
149			(void) snprintf(temp, sizeof (temp),
150			    "%s %s\n(%s) %s", info.pkginst,
151			    info.name, info.arch, info.version);
152			if (setitem(menup, temp)) {
153				errno = EFAULT;
154				return (NULL);
155			}
156		} while (pkginfo(&info, "all", NULL, NULL) == 0);
157		/* clear memory usage by pkginfo */
158		(void) pkginfo(&info, NULL, NULL, NULL);
159		pkgdir = savedir; 	/* restore pkgdir to orig value */
160
161		nwpkg = (char **)calloc(MALLOCSIZ, sizeof (char **));
162		n = ckitem(menup, nwpkg, MALLOCSIZ, "all", NULL,
163		    pkg_gt(HELP), pkg_gt(PROMPT));
164		if (n) {
165			free(nwpkg);
166			errno = ((n == 3) ? EINTR : EFAULT);
167			pkgdir = savedir;
168			return (NULL);
169		}
170		if (strcmp(nwpkg[0], "all") == 0) {
171			chp = menup->choice;
172			for (n = 0; chp; /* void */) {
173				nwpkg[n] = strdup(chp->token);
174				nwpkg = next_n(&n, nwpkg);
175				chp = chp->next;
176				nwpkg[n] = NULL;
177			}
178		} else {
179			for (n = 0; nwpkg[n]; n++)
180				nwpkg[n] = strdup(nwpkg[n]);
181		}
182		(void) setitem(menup, NULL); /* free resources */
183		free(menup);
184		pkgdir = savedir;
185		return (nwpkg);
186	}
187
188	/* clear memory usage by pkginfo */
189	(void) pkginfo(&info, NULL, NULL, NULL);
190
191	nwpkg = (char **)calloc(MALLOCSIZ, sizeof (char **));
192
193	/*
194	 * pkg array contains the instance identifiers to
195	 * be selected, or possibly wildcard definitions
196	 */
197	i = n = 0;
198	do {
199		if (cont_in_list) {	/* This is a live continuation. */
200			nwpkg[n] = strdup(cont_keyword);
201			nwpkg = next_n(&n, nwpkg);
202			nwpkg[n] = NULL;
203			cont_in_list = 0;	/* handled */
204
205			if (pkg[0] == NULL) {	/* It's just a continuation. */
206				break;
207			}
208		} else if (pkgnmchk(pkg[i], "all", 1)) {
209			/* wildcard specification */
210			(void) fpkginst(NULL);
211			inst = fpkginst(pkg[i], NULL, NULL);
212			if (inst == NULL) {
213				progerr(pkg_gt(ERR_NOPKG), pkg[i]);
214				free(nwpkg);
215				nwpkg = NULL;
216				errno = ESRCH;
217				break;
218			}
219			do {
220				if (catg != NULL) {
221					pkginfo(&info, inst, NULL, NULL);
222					if (!is_same_CATEGORY(catg,
223							info.catg))
224						continue;
225				}
226				nwpkg[n] = strdup(inst);
227				nwpkg = next_n(&n, nwpkg);
228				nwpkg[n] = NULL;
229			} while (inst = fpkginst(pkg[i], NULL, NULL));
230		} else {
231			if (fpkginfo(&info, pkg[i])) {
232				progerr(pkg_gt(ERR_NOPKG), pkg[i]);
233				free(nwpkg);
234				nwpkg = NULL;
235				errno = ESRCH;
236				break;
237			}
238			nwpkg[n] = strdup(pkg[i]);
239			nwpkg = next_n(&n, nwpkg);
240			nwpkg[n] = NULL;
241		}
242	} while (pkg[++i]);
243
244	(void) fpkginst(NULL);
245	(void) fpkginfo(&info, NULL);
246	pkgdir = savedir; 	/* restore pkgdir to orig value */
247
248	if (catg != NULL) {
249		if (nwpkg[0] == NULL) {
250
251			/*
252			 * No pkgs in the spooled directory matched the
253			 * category specified by the user.
254			 */
255
256			free(nwpkg);
257			return (NULL);
258		}
259	}
260	return (nwpkg);
261}
262
263/*
264 * Check category passed in on the command line to see if it is valid.
265 *
266 * returns 0 if the category is valid
267 * returns 1 if the category is invalid
268 */
269
270int
271is_not_valid_category(char **category, char *progname)
272{
273	if (strcasecmp(progname, "pkgrm") == 0) {
274		if (is_same_CATEGORY(category, "system"))
275			return (1);
276	}
277
278	return (0);
279}
280
281/*
282 * Check category length
283 *
284 * returns 0 if the category length is valid
285 * returns 1 if a category has length > 16 chars as defined by the SVr4 ABI
286 */
287
288int
289is_not_valid_length(char **category)
290{
291	int i;
292
293	for (i = 0; category[i] != NULL; i++) {
294		if (strlen(category[i]) > MAX_CAT_LEN)
295			return (1);
296	}
297
298	return (0);
299}
300
301/*
302 * Check category passed in on the command line against the CATEGORY in the
303 * spooled or installed packages pkginfo file.
304 *
305 * returns 0 if categories match
306 * returns 1 if categories don't match
307 */
308
309int
310is_same_CATEGORY(char **category, char *persistent_category)
311{
312	int i, j, n = 0;
313	char *pers_catg, **pers_catgs;
314
315	pers_catg = strdup(persistent_category);
316
317	pers_catgs = (char **)calloc(MAX_CAT_LEN, sizeof (char **));
318
319	pers_catgs[n++] = strtok(pers_catg, " \t\n, ");
320	while (pers_catgs[n] = strtok(NULL, " \t\n, "))
321		n++;
322
323	for (i = 0; category[i] != NULL; i++) {
324		for (j = 0; j < n; j++) {
325			if (strcasecmp(category[i], pers_catgs[j]) == 0) {
326				return (1);
327			}
328		}
329	}
330
331	return (0);
332}
333
334/*
335 * Given a string of categories, construct a null-terminated array of
336 * categories.
337 *
338 * returns the array of categories or NULL
339 */
340
341char **
342get_categories(char *catg_arg)
343{
344	int n = 0;
345	char *tmp_catg;
346	char **catgs;
347
348	tmp_catg = strdup(catg_arg);
349
350	catgs = (char **)calloc(MAX_CAT_LEN, sizeof (char **));
351
352	catgs[n++] = strtok(tmp_catg, " \t\n, ");
353	while (catgs[n] = strtok(NULL, " \t\n, "))
354		n++;
355
356	if (*catgs == NULL)
357		return (NULL);
358	else
359		return (catgs);
360}
361