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
28 #include <stdio.h>
29 #include <limits.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <utmpx.h>
36 #include <dirent.h>
37 #include <sys/types.h>
38 #include <locale.h>
39 #include <libintl.h>
40 #include <pkgstrct.h>
41 #include <pkglocs.h>
42 #include <assert.h>
43 #include <pkglib.h>
44 #include <install.h>
45 #include <libinst.h>
46 #include <libadm.h>
47 #include <messages.h>
48 #include <instzones_api.h>
49
50 extern int npkgs; /* the number of packages yet to be installed */
51
52 /*
53 * ckquit is a global that controls 'ckyorn' (defined in libadm)
54 * If ckquit is non-zero, then "quit" is allowed as an answer when
55 * ckyorn is called. If is it zero, then "quit" is not an allowed answer.
56 */
57 extern int ckquit;
58
59 extern struct admin adm;
60
61 /*
62 * each one of these represents a single kind of dependency check
63 */
64
65 static depckError_t er_depsonme = {0, (depckErrorRecord_t *)NULL};
66 static depckError_t er_prenci = {0, (depckErrorRecord_t *)NULL};
67 static depckError_t er_prereq = {0, (depckErrorRecord_t *)NULL};
68 static depckError_t er_rckdepend = {0, (depckErrorRecord_t *)NULL};
69 static depckError_t er_rckpriv = {0, (depckErrorRecord_t *)NULL};
70 static depckError_t er_rckrunlevel = {0, (depckErrorRecord_t *)NULL};
71 static depckError_t er_runlevel = {0, (depckErrorRecord_t *)NULL};
72
73 /*
74 * each one of these represents a localized message for a single kind
75 * of dependency check
76 */
77
78 static char *IMSG_PKGRMCHK_CKRUNLVL = (char *)NULL;
79 static char *IMSG_PKGRMCHK_DEPEND = (char *)NULL;
80 static char *IMSG_PKGRMCHK_DEPSONME = (char *)NULL;
81 static char *IMSG_PKGRMCHK_PRENCI = (char *)NULL;
82 static char *IMSG_PKGRMCHK_PREREQ = (char *)NULL;
83 static char *IMSG_PKGRMCHK_PRIV = (char *)NULL;
84 static char *IMSG_PKGRMCHK_RUNLEVEL = (char *)NULL;
85
86 /*
87 * each one of these represents a function to handle a single kind of
88 * dependency check
89 */
90
91 static int rckdepend(char *a_msg, char *a_pkg);
92 static int rckdepsonme(char *a_msg, char *a_pkg);
93 static int rckprenci(char *a_msg, char *a_pkg);
94 static int rckprereq(char *a_msg, char *a_pkg);
95 static int rckpriv(char *a_msg, char *a_pkg);
96 static int rckrunlevel(char *a_msg, char *a_pkg);
97
98 static depckl_t DEPCKL[] = {
99 /*
100 * Message hierarchy:
101 * -- runlevel=%s
102 * --- rckrunlevel=%d
103 * --- rckpriv=%d ****
104 * -- incompat=%s
105 * -- prerequisite-incomplete=%s
106 * -- dependonme=%s
107 * -- dependsonme=%s:%s
108 * -- prerequisite-installed=%s
109 * ---rckdepend=%d ****
110 */
111
112 /* name, ignore_values, err_msg, depcklFunc, recrd */
113 /*
114 * package and zone information is collected in the "record" object for
115 * each occurance - then a message is constructed for each zone that
116 * reported the condition - the message includes that portion of the
117 * check past the "=" - then the specified "depcklFunc" is called to
118 * process each message.
119 * Message format:
120 * %s %s <%s> %s <%s>
121 * Message arguments:
122 * value, "package", package-name, "zone/zones", zone-name
123 */
124
125 { "dependsonme=", NULL, &IMSG_PKGRMCHK_DEPSONME,
126 &rckdepsonme, &er_depsonme
127 },
128 { "dependonme=", NULL, &IMSG_PKGRMCHK_DEPSONME,
129 &rckdepsonme, &er_depsonme
130 },
131 { "prerequisite-incomplete=", NULL, &IMSG_PKGRMCHK_PRENCI,
132 &rckprenci, &er_prenci
133 },
134 { "prerequisite-installed=", NULL, &IMSG_PKGRMCHK_PREREQ,
135 &rckprereq, &er_prereq
136 },
137 { "runlevel=", NULL, &IMSG_PKGRMCHK_RUNLEVEL,
138 NULL, &er_runlevel
139 },
140
141 /*
142 * these checks are ignored if they return one of the listed values
143 * if they do NOT return one of the listed values, then the package
144 * and zone information is collected in the "record" object for each
145 * occurance - then a single unified message is constructed for all
146 * zones that report the same condition; then the specified "depcklFunc"
147 * is called to process the resulting combined message.
148 * Message format:
149 * %s <%s> %s <%s>
150 * Message arguments:
151 * "package", package-name, "zone/zones", zone-name(s)
152 */
153
154 { "rckdepend=", "0", &IMSG_PKGRMCHK_DEPEND,
155 &rckdepend, &er_rckdepend
156 },
157 { "rckpriv=", "0", &IMSG_PKGRMCHK_PRIV,
158 &rckpriv, &er_rckpriv
159 },
160 { "rckrunlevel=", "0", &IMSG_PKGRMCHK_CKRUNLVL,
161 &rckrunlevel, &er_rckrunlevel
162 },
163
164 /*
165 * same as above BUT no check to ignore is done; message always reported
166 */
167
168 { NULL, NULL, NULL,
169 NULL, NULL
170 }
171 };
172
173 /*
174 * Name: preremove_verify
175 * Description: verify results of preremoval dependency checking
176 * Arguments: a_pkglist - pointer to array of strings representing the names
177 * of all the packages that have been checked
178 * a_zlst - list of zones that dependencies were checked on
179 * a_zoneTempDir - pointer to string representing the path where
180 * the files containing the preremoval dependency
181 * check data are located
182 * Returns: int
183 * == 0 - continue processing
184 * != 0 - do not continue processing
185 */
186
187 int
preremove_verify(char ** a_pkglist,zoneList_t a_zlst,char * a_zoneTempDir)188 preremove_verify(char **a_pkglist, zoneList_t a_zlst, char *a_zoneTempDir)
189 {
190 char *pkginst;
191 int i;
192 int savenpkgs = npkgs;
193
194 /*
195 * entry assertions
196 */
197
198 assert(a_pkglist != (char **)NULL);
199 assert(a_zlst != (zoneList_t)NULL);
200 assert(a_zoneTempDir != (char *)NULL);
201
202 /*
203 * entry debugging info
204 */
205
206 echoDebug(DBG_PRERVFY_ENTRY);
207
208 /*
209 * localize messages
210 */
211
212 IMSG_PKGRMCHK_DEPSONME = MSG_PKGRMCHK_DEPSONME;
213 IMSG_PKGRMCHK_PRENCI = MSG_PKGRMCHK_PRENCI;
214 IMSG_PKGRMCHK_PREREQ = MSG_PKGRMCHK_PREREQ;
215 IMSG_PKGRMCHK_RUNLEVEL = MSG_PKGRMCHK_RUNLEVEL;
216 IMSG_PKGRMCHK_DEPEND = MSG_PKGRMCHK_DEPEND;
217 IMSG_PKGRMCHK_PRIV = MSG_PKGRMCHK_PRIV;
218 IMSG_PKGRMCHK_CKRUNLVL = MSG_PKGRMCHK_CKRUNLVL;
219
220 /*
221 * outer loop - process each package first
222 */
223
224 for (i = 0; (pkginst = a_pkglist[i]) != NULL; i++) {
225
226 char *zoneName;
227 int zoneIndex;
228
229 /*
230 * inner loop - for each package process each zone second
231 */
232
233 if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
234 continue;
235 }
236
237 for (zoneIndex = 0;
238 (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
239 (char *)NULL; zoneIndex++) {
240
241 FILE *fp;
242 char line[PATH_MAX+1];
243 char preremovecheckPath[PATH_MAX+1];
244 int len;
245
246 /* skip the zone if it is NOT bootable */
247
248 if (z_zlist_is_zone_runnable(a_zlst,
249 zoneIndex) == B_FALSE) {
250 continue;
251 }
252
253 /* create path to this packages preremove check data */
254
255 len = snprintf(preremovecheckPath,
256 sizeof (preremovecheckPath),
257 "%s/%s.%s.preremovecheck.txt",
258 a_zoneTempDir, pkginst,
259 z_zlist_get_scratch(a_zlst, zoneIndex));
260
261 if (len > sizeof (preremovecheckPath)) {
262 progerr(ERR_CREATE_PATH_3, a_zoneTempDir,
263 pkginst, zoneName);
264 continue;
265 }
266
267 /* error if preremove check data path is not a file */
268
269 if (isfile((char *)NULL, preremovecheckPath) != 0) {
270 echoDebug(DBG_PRERVFY_NOFILE, pkginst, zoneName,
271 preremovecheckPath, strerror(errno));
272 progerr(ERR_PRERVFY_NOFILE, pkginst, zoneName);
273 continue;
274 }
275
276 /* open the preremove check data file */
277
278 fp = fopen(preremovecheckPath, "r");
279 if (fp == (FILE *)NULL) {
280 progerr(ERR_PRERVFY_OPEN_FILE,
281 preremovecheckPath, pkginst, zoneName,
282 strerror(errno));
283 continue;
284 }
285
286 /* read and process each preremove check data line */
287
288 while (fgets(line, sizeof (line), fp) != (char *)NULL) {
289 int len;
290 int j;
291
292 /* remove all new-lines from end of line */
293
294 len = strlen(line);
295 while ((len > 0) && (line[len-1] == '\n')) {
296 line[--len] = '\0';
297 }
298
299 /* ignore comment lines */
300
301 if (line[0] == '#') {
302 continue;
303 }
304
305 /* ignore empty lines */
306
307 if (line[0] == '\0') {
308 continue;
309 }
310
311 /* scan dependency list for this item */
312
313 for (j = 0;
314 DEPCKL[j].name != (char *)NULL; j++) {
315 len = strlen(DEPCKL[j].name);
316
317 if (strncmp(line, DEPCKL[j].name,
318 len) == 0) {
319 break;
320 }
321 }
322
323 echoDebug(DBG_PRERVFY_SCAN, line, pkginst,
324 zoneName);
325
326 /* ignore line if not found */
327
328 if (DEPCKL[j].name == (char *)NULL) {
329 progerr(ERR_PRERVFY_UNKNOWN_LINE, line,
330 pkginst, zoneName);
331 continue;
332 }
333
334 if ((DEPCKL[j].ignore_values != (char *)NULL) &&
335 (*(DEPCKL[j].ignore_values) != '\0') &&
336 (strchr(DEPCKL[j].ignore_values,
337 line[len]) != (char *)NULL)) {
338 continue;
339 }
340 /* found match - record this dependency issue */
341
342 depchkRecordError(DEPCKL[j].record, pkginst,
343 zoneName, &line[len]);
344 }
345
346 /* close preremove check data file */
347
348 (void) fclose(fp);
349 }
350 }
351
352 /*
353 * all dependency issues have been recorded; report results
354 */
355
356 i = depchkReportErrors(DEPCKL);
357
358 /* restore "npkgs" */
359
360 npkgs = savenpkgs;
361
362 /* return continue/dont dontinue results */
363
364 return (i);
365 }
366
367 /*
368 * Name: getyorn
369 * Description: Deliver dependency check reason; ask question; return response
370 * Arguments: a_msg - pointer to string representing the message to output
371 * such as 'The package <..> contains <...>'
372 * a_pkg - pointer to string representing the package for which
373 * the question is being asked
374 * a_nocheck - should the message be output?
375 * == 0 - do not output the message
376 * != 0 - output the message
377 * a_quit - should the question NOT be asked?
378 * == 0 - ask the question
379 * != 0 - do not ask the question - return "no"
380 * a_helpMsg - pointer to string representing help message to be
381 * made available if the question is asked
382 * == NULL - no help message is available
383 * a_adminMsg - pointer to string representing the dependency check
384 * failure 'reason' - such as "Privilege checking failed."
385 * == NULL - no failure reason is available
386 * Returns: int - results of question/response actions
387 * 0 - success
388 * 1 - end of file
389 * 2 - undefined error
390 * 3 - answer was not "y"/was "q"
391 * 4 - quit action taken
392 * 5 - interactive mode required
393 */
394
395 static int
getyorn(char * a_msg,char * a_pkg,int a_nocheck,int a_quit,char * a_helpMsg,char * a_adminMsg)396 getyorn(char *a_msg, char *a_pkg, int a_nocheck, int a_quit,
397 char *a_helpMsg, char *a_adminMsg)
398 {
399 char ans[MAX_INPUT];
400 char ask_cont[MSG_MAX];
401 int n;
402 int saveCkquit;
403
404 /*
405 * entry assertions
406 */
407
408 assert(a_pkg != (char *)NULL);
409 assert(*a_pkg != '\0');
410
411 /*
412 * entry debugging info
413 */
414
415 echoDebug(DBG_PRERVFY_GETYORN_ARGS, a_pkg, a_nocheck, a_quit, a_msg,
416 a_adminMsg ? a_adminMsg : "");
417
418 /* return success (0) if "nocheck" is non-zero */
419
420 if (a_nocheck != 0) {
421 echoDebug(DBG_PRERVFY_GETYORN_NOCHECK, a_pkg);
422 return (0);
423 }
424
425 /* output reason for this particular failure */
426
427 if ((a_msg != (char *)NULL) && (*a_msg != '\0')) {
428 ptext(stderr, "%s", a_msg);
429 }
430
431 /* return "4 (administration)" if "quit" is non-zero */
432
433 if (a_quit != 0) {
434 /* output failure "admin reason" if available */
435 if ((a_adminMsg != (char *)NULL) && (*a_adminMsg != '\0')) {
436 ptext(stderr, a_adminMsg);
437 }
438 echoDebug(DBG_PRERVFY_GETYORN_QUIT, a_pkg);
439 return (4);
440 }
441
442 /* return "5 (administration interaction required)" if -n */
443
444 if (echoGetFlag() == B_FALSE) {
445 ptext(stderr, MSG_PRERVFY_GETYORN_SUSP, a_pkg);
446 echoDebug(DBG_PRERVFY_GETYORN_QUIT_USER, a_pkg);
447 return (5);
448 }
449
450 /* prepare question to ask "continue with removal of pkg <xxx>?" */
451
452 (void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_PKGRMCHK_CONT),
453 a_pkg);
454
455 /* ask question */
456
457 saveCkquit = ckquit;
458 ckquit = 0;
459
460 n = ckyorn(ans, NULL, NULL, a_helpMsg, ask_cont);
461
462 ckquit = saveCkquit;
463
464 if (n != 0) {
465 ptext(stderr, MSG_PRERVFY_GETYORN_TERM, a_pkg);
466 echoDebug(DBG_PRERVFY_GETYORN_CKYORN, a_pkg, n);
467 return (n);
468 }
469
470 /* return "3 (interruption) if not "y" or "Y" */
471
472 if (strchr("yY", *ans) == NULL) {
473 ptext(stderr, MSG_PRERVFY_GETYORN_TERM_USER, a_pkg);
474 echoDebug(DBG_PRERVFY_GETYORN_NOT_Y, a_pkg, ans);
475 return (3);
476 }
477
478 /* return "0 - success" */
479
480 echoDebug(DBG_PRERVFY_GETYORN_SUCCESS, a_pkg);
481
482 return (0);
483 }
484
485 /*
486 * Trigger: dependsonme=<<package>>
487 * Sequence: - one or more: dependsonme=<<package>>
488 * - one: rckdepend=<<n>>
489 * Actions: Output message if "rdepend!=nocheck"
490 * Return 0
491 * Terminate when 'rckdepend' processed
492 */
493
494 static int
rckdepsonme(char * a_msg,char * a_pkg)495 rckdepsonme(char *a_msg, char *a_pkg)
496 {
497 echoDebug(DBG_PRERVFY_RCKDEPSONME, a_pkg, a_msg);
498
499 if (!(ADM(rdepend, "nocheck"))) {
500 ptext(stderr, "%s", a_msg);
501 }
502
503 return (0);
504 }
505
506 /*
507 * Trigger: prerequisite-incomplete=<<package>>
508 * Sequence: - one or more: prerequisite-incomplete=<<package>>
509 * - one: rckdepend=<<n>>
510 * Actions: Output message if "rdepend!=nocheck"
511 * Return 0
512 * Terminate when 'rckdepend' processed
513 */
514
515 static int
rckprenci(char * a_msg,char * a_pkg)516 rckprenci(char *a_msg, char *a_pkg)
517 {
518 echoDebug(DBG_PRERVFY_RCKPRENCI, a_pkg, a_msg);
519
520 if (!(ADM(rdepend, "nocheck"))) {
521 ptext(stderr, "%s", a_msg);
522 }
523
524 return (0);
525 }
526
527 /*
528 * Trigger: prerequisite-installed=<<package>>
529 * Sequence: - one or more: prerequisite-installed=<<package>>
530 * - one: rckdepend=<<n>>
531 * Actions: Output message if "rdepend!=nocheck"
532 * Return 0
533 * Terminate when 'rckdepend' processed
534 */
535
536 static int
rckprereq(char * a_msg,char * a_pkg)537 rckprereq(char *a_msg, char *a_pkg)
538 {
539 echoDebug(DBG_PRERVFY_RCKPREREQ, a_pkg, a_msg);
540
541 if (!(ADM(rdepend, "nocheck"))) {
542 ptext(stderr, "%s", a_msg);
543 }
544
545 return (0);
546 }
547
548 /*
549 * Return value: int
550 * 0 - success
551 * 1 - end of file
552 * 2 - undefined error
553 * 3 - answer was not "y"/was "q"
554 * 4 - quit action taken
555 * 5 - interactive mode required
556 * 99 - fatal error
557 */
558
559 static int
rckrunlevel(char * a_msg,char * a_pkg)560 rckrunlevel(char *a_msg, char *a_pkg)
561 {
562 echoDebug(DBG_PRERVFY_RCKRUNLEVEL, a_pkg, a_msg);
563 /*
564 * For now, we are ignoring runlevel removal issues within
565 * non-global zones. This is questionable, but the RSTATES
566 * feature is rarely used and known uses within Solaris are
567 * effectively no-ops as of this time
568 */
569 return (0);
570 }
571
572 /*
573 * Trigger: rckdepend=<<n>>
574 * Sequence: - one or more of:
575 * -- incompat=<<package>>
576 * -- prerequisite-incomplete=<<package>>
577 * -- prerequisite-installed=<<package>>
578 * -- dependson=<<package>>
579 * -- dependsonme=<<package>>
580 * - one: ckpdepend=<<n>>
581 * Actions: process according to settings
582 * Return value: int
583 * 0 - success
584 * 1 - end of file
585 * 2 - undefined error
586 * 3 - answer was not "y"/was "q"
587 * 4 - quit action taken
588 * 5 - interactive mode required
589 */
590
591 static int
rckdepend(char * a_msg,char * a_pkg)592 rckdepend(char *a_msg, char *a_pkg)
593 {
594 echoDebug(DBG_PRERVFY_RCKDEPEND, a_pkg, a_msg);
595
596 return (getyorn(a_msg, a_pkg, ADM(rdepend, "nocheck"),
597 ADM(rdepend, "quit"), HLP_PKGRMCHK_DEPEND,
598 ERR_PKGRMCHK_DEPFAILED));
599 }
600
601 /*
602 * Trigger: rckpriv=<<n>>
603 * Sequence: - one: rckpriv=<<n>>
604 * Actions: process according to settings
605 * Return value: int
606 * 0 - success
607 * 1 - end of file
608 * 2 - undefined error
609 * 3 - answer was not "y"/was "q"
610 * 4 - quit action taken
611 * 5 - interactive mode required
612 */
613
614 static int
rckpriv(char * a_msg,char * a_pkg)615 rckpriv(char *a_msg, char *a_pkg)
616 {
617 echoDebug(DBG_PRERVFY_RCKPRIV, a_pkg, a_msg);
618
619 return (getyorn(a_msg, a_pkg, ADM(action, "nocheck"),
620 ADM(action, "quit"), HLP_PKGRMCHK_PRIV,
621 ERR_PKGRMCHK_PRIVFAILED));
622 }
623