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.
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.
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]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
31 * This module contains code required to remove special contents from
32 * the contents file when a pkgrm is done on a system upgraded to use
45 #include <sys/types.h>
51 /* This specifies the maximum length of a contents file line read in. */
54 #define SPECIAL_MALLOC "unable to maintain package contents text due to "\
55 "insufficient memory."
56 #define SPECIAL_ACCESS "unable to maintain package contents text due to "\
58 #define SPECIAL_INPUT "unable to maintain package contents text: alternate "\
64 * This function is used by qsort to sort an array of special contents
65 * rule strings. This array must be sorted to facilitate efficient
66 * rule processing. See qsort(3c) regarding qsort compare functions.
69 strcompare(const void *pv1
, const void *pv2
)
71 char **ppc1
= (char **) pv1
;
72 char **ppc2
= (char **) pv2
;
73 int i
= strcmp(*ppc1
, *ppc2
);
84 * This function determines whether a file name (pc) matches a rule
85 * from the special contents file (pcrule). We assume that neither
86 * string is ever NULL.
88 * Return: 1 on match, 0 on no match.
92 match(const char *pc
, char *pcrule
)
94 int n
= strlen(pcrule
);
96 if (pcrule
[n
- 1] == '*') {
102 if (fnmatch(pc
, pcrule
, FNM_PATHNAME
) == 0 ||
103 fnmatch(pc
, pcrule
, 0) == 0)
107 j
= strncmp(pc
, pcrule
, n
- 1);
116 * search_special_contents
118 * This function assumes that a series of calls will be made requesting
119 * whether a given path matches the special contents rules or not. We
122 * a) the special_contents array is sorted
123 * b) the calls will be made with paths in a sorted order
125 * Given that, we can keep track of where the last search ended and
126 * begin the new search at that point. This reduces the cost of a
127 * special contents matching search to O(n) from O(n^2).
129 * ppcSC A pointer to an array of special contents obtained via
130 * get_special_contents().
131 * path A path: determine whether it matches the special
132 * contents rules or not.
133 * piX The position in the special_contents array we have already
134 * arrived at through searching. This must be initialized to
135 * zero before initiating a series of search_special_contents
141 * char **ppSC = NULL;
142 * if (get_special_contents(NULL, &ppcSC, &max) != 0) exit(1);
143 * for (j = 0; paths != NULL && paths[j] != NULL; j++) {
144 * if (search_special_contents(ppcSC, path[j], &i)) {
145 * do_something_with_special_path(path[j]);
150 * Return: 1 if there is a match, 0 otherwise.
151 * Side effects: The value of *piX will be set between calls to this
152 * function. To make this function thread safe, use search arrays.
153 * Also: Nonmatching entries are eliminated, set to NULL.
156 search_special_contents(char **ppcSC
, const char *pcpath
, int *piX
, int max
)
159 if (ppcSC
== NULL
|| *piX
== max
)
165 if (ppcSC
[*piX
] == NULL
) {
170 j
= strlen(ppcSC
[*piX
]);
171 k
= strcmp(pcpath
, ppcSC
[*piX
]);
172 wild
= (ppcSC
[*piX
][j
- 1] == '*');
175 * Depending on whether the path string compared with the
176 * rule, we take different actions. If the path is less
177 * than the rule, we keep the rule. If the path equals
178 * the rule, we advance the rule (as long as the rule is
179 * not a wild card). If the path is greater than the rule,
180 * we have to advance the rule list until we are less or equal
181 * again. This way we only have to make one pass through the
182 * rules, as we make one pass through the path strings. We
183 * assume that the rules and the path strings are sorted.
190 if (match(pcpath
, ppcSC
[*piX
]))
196 int x
= match(pcpath
, ppcSC
[*piX
]);
197 if (wild
== 0) (*piX
)++;
202 if (match(pcpath
, ppcSC
[*piX
]))
206 * As pcpath > ppcSC[*piX] we have passed up this
207 * rule - it cannot apply. Therefore, we do not
208 * need to retain it. Removing the rule will make
209 * subsequent searching more efficient.
221 * get_special_contents
223 * Retrieves the special contents file entries, if they exist. These
224 * are sorted. We do not assume the special_contents file is in sorted
227 * pcroot The root of the install database. If NULL assume '/'.
228 * pppcSC A pointer to a char **. This pointer will be set to
229 * point at NULL if there is no special_contents file or
230 * to a sorted array of strings, NULL terminated, otherwise.
231 * piMax The # of entries in the special contents result.
233 * Returns: 0 on no error, nonzero on error.
234 * Side effects: the pppcSC pointer is set to point at a newly
235 * allocated array of pointers to strings.. The caller must
236 * free this buffer. The value of *piMax is set to the # of
240 get_special_contents(const char *pcroot
, char ***pppcSC
, int *piMax
)
246 char *pc
= "var/sadm/install/special_contents";
250 /* Initialize the return values. */
254 if (pcroot
== NULL
) {
258 if (pcroot
[strlen(pcroot
) - 1] == '/') {
259 if (snprintf(path
, PATH_MAX
, "%s%s", pcroot
, pc
) >= PATH_MAX
) {
260 progerr(gettext(SPECIAL_INPUT
));
264 if (snprintf(path
, PATH_MAX
, "%s/%s", pcroot
, pc
)
266 progerr(gettext(SPECIAL_INPUT
));
273 if (e
!= 0 && errno
== ENOENT
)
274 return (0); /* No special contents file. Do nothing. */
276 if (access(path
, R_OK
) != 0 || (fp
= fopen(path
, "r")) == NULL
) {
277 /* Could not open special contents which exists */
278 progerr(gettext(SPECIAL_ACCESS
));
282 for (i
= 0; fgets(line
, 2048, fp
) != NULL
; i
++);
284 if ((ppc
= (char **) calloc(i
+ 1, sizeof (char *))) == NULL
) {
285 progerr(gettext(SPECIAL_MALLOC
));
289 for (i
= 0; fgets(line
, 2048, fp
) != NULL
; ) {
291 if (line
[0] == '#' || line
[0] == ' ' || line
[0] == '\n' ||
292 line
[0] == '\t' || line
[0] == '\r')
295 if (line
[n
- 1] == '\n')
297 ppc
[i
++] = strdup(line
);
300 qsort(ppc
, i
, sizeof (char *), strcompare
);
308 * free_special_contents
310 * This function frees special_contents which have been allocated using
311 * get_special_contents.
313 * pppcSC A pointer to a buffer allocated using get_special_contents.
314 * max The number of entries allocated.
317 * Side effects: Frees memory allocated using get_special_contents and
318 * sets the pointer passed in to NULL.
321 free_special_contents(char ***pppcSC
, int max
)
329 for (i
= 0; ppc
!= NULL
&& i
< max
; i
++)
342 * Return the first field of a string delimited by a space.
344 * pcline A line from the contents file.
346 * Return: NULL if an error. Otherwise a string allocated by this
347 * function. The caller must free the string.
348 * Side effects: none.
351 get_path(const char *pcline
)
353 int i
= strcspn(pcline
, " ");
355 if (i
<= 1 || (pc
= (char *) calloc(i
+ 1, 1)) == NULL
)
357 (void) memcpy(pc
, pcline
, i
);
362 * generate_special_contents_rules
364 * This procedure will generate an array of integers which will be a mask
365 * to apply to the ppcfextra array. If set to 1, then the content must be
366 * added to the contents file. Otherwise it will not be: The old contents
367 * file will be used for this path value, if one even exists.
369 * ient The number of ppcfextra contents installed.
370 * ppcfent The contents installed.
371 * ppcSC The rules (special contents)
372 * max The number of special contents rules.
373 * ppiIndex The array of integer values, determining whether
374 * individual ppcfextra items match special contents rules.
375 * This array will be created and set in this function and
378 * Return: 0 success, nonzero failure
379 * Side effects: allocates an array of integers that the caller must free.
382 generate_special_contents_rules(int ient
, struct cfent
**ppcfent
,
383 char **ppcSC
, int max
, int **ppiIndex
)
386 int *pi
= (int *) calloc(ient
, sizeof (int));
388 progerr(gettext(SPECIAL_MALLOC
));
393 * For each entry in ppcfextra, check if it matches a rule.
394 * If it does not, set the entry in the index to -1.
396 for (i
= 0, j
= 0; i
< ient
&& j
< max
; i
++) {
397 if (search_special_contents(ppcSC
, ppcfent
[i
]->path
,
407 * In case we ran out of rules before contents, we will not use
408 * those contents. Make sure these contents are set to 0 and
409 * will not be copied from the ppcfent array into the contents
412 for (i
= i
; i
< ient
; i
++)
423 * Compare a path to a cfent. It will match either if the path is
424 * equal to the cfent path, or if the cfent is a symbolic or link
425 * and *that* matches.
428 * pent a contents entry
430 * Returns: as per strcmp
431 * Side effects: none.
434 pathcmp(const char *pc
, const struct cfent
*pent
)
437 if ((pent
->ftype
== 's' || pent
->ftype
== 'l') &&
440 if ((p
= strstr(pc
, "=")) == NULL
) {
442 i
= strcmp(pc
, pent
->path
);
444 /* A path without additional chars strcmp's to less */
449 /* Break the link path into two pieces. */
452 /* Compare the first piece. */
453 i
= strcmp(pc
, pent
->path
);
455 /* If equal we must compare the second piece. */
458 i
= strcmp(q
, pent
->ainfo
.local
);
461 /* Restore the link path. */
465 i
= strcmp(pc
, pent
->path
);
472 * -----------------------------------------------------------------------
473 * Externally visible function.
477 * special_contents_remove
479 * Given a set of entries to remove and an alternate root, this function
480 * will do everything required to ensure that the entries are removed
481 * from the contents file if they are listed in the special_contents
482 * file. The contents file will get changed only in the case that the
483 * entire operation has succeeded.
485 * ient The number of entries.
486 * ppcfent The entries to remove.
487 * pcroot The alternate install root. Could be NULL. In this
488 * case, assume root is '/'
490 * Result: 0 on success, nonzero on failure. If an error occurs, an
491 * error string will get output to standard error alerting the user.
492 * Side effects: The contents file may change as a result of this call,
493 * such that lines in the in the file will be changed or removed.
494 * If the call fails, a t.contents file may be left behind. This
495 * temporary file should be removed subsequently.
498 special_contents_remove(int ient
, struct cfent
**ppcfent
, const char *pcroot
)
500 int result
= 0; /* Assume we will succeed. Return result. */
501 char **ppcSC
= NULL
; /* The special contents rules, sorted. */
502 int i
, j
; /* Indexes into contents & special contents */
503 FILE *fpi
= NULL
, /* Input of contents file */
504 *fpo
= NULL
; /* Output to temp contents file */
505 char cpath
[PATH_MAX
], /* Contents file path */
506 tcpath
[PATH_MAX
]; /* Temp contents file path */
507 const char *pccontents
= "var/sadm/install/contents";
508 const char *pctcontents
= "var/sadm/install/t.contents";
509 char line
[LINESZ
]; /* Reads in and writes out contents lines. */
510 time_t t
; /* Used to create a timestamp comment. */
511 int max
; /* Max number of special contents entries. */
512 int *piIndex
; /* An index to ppcfents to remove from cfile */
514 cpath
[0] = tcpath
[0] = '\0';
516 if (ient
== 0 || ppcfent
== NULL
|| ppcfent
[0] == NULL
) {
520 if ((get_special_contents(pcroot
, &ppcSC
, &max
)) != 0) {
525 /* Check if there are no special contents actions to take. */
530 if (pcroot
== NULL
) pcroot
= "/";
531 if (pcroot
[strlen(pcroot
) - 1] == '/') {
532 if (snprintf(cpath
, PATH_MAX
, "%s%s", pcroot
, pccontents
)
534 snprintf(tcpath
, PATH_MAX
, "%s%s", pcroot
, pctcontents
)
536 progerr(gettext(SPECIAL_INPUT
));
541 if (snprintf(cpath
, PATH_MAX
, "%s/%s", pcroot
, pccontents
)
543 snprintf(tcpath
, PATH_MAX
, "%s/%s", pcroot
, pctcontents
)
545 progerr(gettext(SPECIAL_INPUT
));
551 /* Open the temporary contents file to write, contents to read. */
552 if (access(cpath
, F_OK
| R_OK
) != 0) {
554 * This is not a problem since no contents means nothing
555 * to remove due to special contents rules.
558 cpath
[0] = '\0'; /* This signals omission of 'rename cleanup' */
562 if (access(cpath
, W_OK
) != 0) {
563 /* can't write contents file, something is wrong. */
564 progerr(gettext(SPECIAL_ACCESS
));
570 if ((fpi
= fopen(cpath
, "r")) == NULL
) {
571 /* Given the access test above, this should not happen. */
572 progerr(gettext(SPECIAL_ACCESS
));
577 if ((fpo
= fopen(tcpath
, "w")) == NULL
) {
578 /* open t.contents failed */
579 progerr(gettext(SPECIAL_ACCESS
));
584 if (generate_special_contents_rules(ient
, ppcfent
, ppcSC
, max
, &piIndex
)
591 * Copy contents to t.contents unless there is an entry in
592 * the ppcfent array which corresponds to an index set to 1.
594 * These items are the removed package contents which matche an
595 * entry in ppcSC (the special_contents rules).
597 * Since both the contents and rules are sorted, we can
598 * make a single efficient pass.
600 (void) memset(line
, 0, LINESZ
);
602 for (i
= 0, j
= 0; fgets(line
, LINESZ
, fpi
) != NULL
; ) {
607 * Note: This could be done better: We should figure out
608 * which are the last 2 lines and only trim those off.
609 * This will suffice to do this and will only be done as
610 * part of special_contents handling.
613 continue; /* Do not copy the final 2 comment lines */
615 pcpath
= get_path(line
);
617 if (pcpath
!= NULL
&& i
< ient
) {
619 while (piIndex
[i
] == 0)
623 k
= pathcmp(pcpath
, ppcfent
[i
]);
625 if (k
< 0 || i
>= ient
) {
626 /* Just copy contents -> t.contents */
629 /* We have a match. Do not copy the content. */
632 (void) memset(line
, 0, LINESZ
);
634 } else while (i
< ient
) {
637 * This is a complex case: The content
638 * entry is further along alphabetically
639 * than the rule. Skip over all rules which
640 * apply until we come to a rule which is
641 * greater than the current entry, or equal
642 * to it. If equal, do not copy, otherwise
645 if (piIndex
[i
] == 0) {
648 } else if ((k
= pathcmp(pcpath
, ppcfent
[i
]))
653 (void) memset(line
, 0, LINESZ
);
657 /* path < rule, end special case */
663 * Avoid copying the old content when path == rule
664 * This occurs when the complex case ends on a match.
670 if (fprintf(fpo
, "%s", line
) < 0) {
671 /* Failing to write output would be catastrophic. */
672 progerr(gettext(SPECIAL_ACCESS
));
676 (void) memset(line
, 0, LINESZ
);
680 (void) fprintf(fpo
, "# Last modified by pkgremove\n");
681 (void) fprintf(fpo
, "# %s", ctime(&t
));
684 free_special_contents(&ppcSC
, max
);
693 if (tcpath
[0] != '\0' && cpath
[0] != '\0' &&
694 rename(tcpath
, cpath
) != 0) {
695 progerr(gettext(SPECIAL_ACCESS
));
699 if (tcpath
[0] != '\0' && remove(tcpath
) != 0) {
701 * Do not output a diagnostic message. This condition
702 * occurs only when we are unable to clean up after
703 * a failure. A temporary file will linger.