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 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
31 #include <sys/types.h>
43 #define HDR_FSUSAGE "#name remote_name writeable bfree bused ifree iused"
45 #define ERR_NOCREAT "cannot create %s."
46 #define ERR_NOOPEN "cannot open %s."
47 #define ERR_NOWRITE "cannot write to %s."
48 #define ERR_NOREAD "cannot read from %s."
49 #define ERR_FSFAIL "cannot construct filesystem table entry."
50 #define ERR_BADTYPE "cannot record %s dryrun from %s continuation file."
51 #define ERR_NOCONT "cannot install from continue file due to error " \
54 #define ISUMASC_SUFFIX ".isum.asc"
55 #define FSASC_SUFFIX ".fs.asc"
56 #define IPOASC_SUFFIX ".ipo.asc"
57 #define IBIN_SUFFIX ".inst.bin"
59 #define MALCOUNT 5 /* package entries to allocate in a block */
60 #define PKGNAMESIZE 32 /* package entries to allocate in a block */
62 extern struct cfextra
**extlist
;
65 static struct cfextra
**extptr
;
66 static int dryrun_mode
= 0;
67 static int continue_mode
= 0;
68 static int this_exitcode
= 0;
70 /* The dryrun and continuation filenames */
71 static char *dryrun_sumasc
;
72 static char *dryrun_fsasc
;
73 static char *dryrun_poasc
;
74 static char *dryrun_bin
;
75 static char *continue_bin
;
77 /* These tell us if the actual files are initialized yet. */
78 static int dryrun_initialized
= 0;
79 static int continue_initialized
= 0;
81 static int this_type
; /* type of transaction from main.c */
83 static int pkg_handle
= -1;
86 /* Their associated file pointers */
92 char pkginst
[PKGNAMESIZE
+ 2];
93 struct dr_pkg_entry
*next
;
96 static struct drinfo
{
97 unsigned partial_set
:1; /* 1 if a partial installation was detected. */
98 unsigned partial
:1; /* 1 if a partial installation was detected. */
99 unsigned runlevel_set
:1;
100 unsigned runlevel
:1; /* 1 if runlevel test returned an error. */
101 unsigned pkgfiles_set
:1;
103 unsigned depend_set
:1;
105 unsigned space_set
:1;
107 unsigned conflict_set
:1;
109 unsigned setuid_set
:1;
113 unsigned pkgdirs_set
:1;
115 unsigned reqexit_set
:1;
116 unsigned checkexit_set
:1;
118 int type
; /* type of operation */
119 int reqexit
; /* request script exit code */
120 int checkexit
; /* checkinstall script exit code */
121 int exitcode
; /* overall program exit code. */
123 struct dr_pkg_entry
*packages
; /* pointer to the list of packages */
125 int total_ext_recs
; /* total extlist entries */
126 int total_fs_recs
; /* total fs_tab entries */
127 int total_pkgs
; /* total number of dryrun packages */
128 int do_not_continue
; /* error stacking is likely */
131 static char *exitmsg
; /* the last meaningful message printed */
134 * In the event that live continue (continue from a dryrun source only)
135 * becomes a feature, it will be necessary to keep track of those events such
136 * as multiply edited files and files dependent upon multiple class action
137 * scripts that will lead to "tolerance stacking". Calling this function
138 * states that we've lost the level of precision necessary for a live
142 set_continue_not_ok(void)
144 dr_info
.do_not_continue
= 1;
150 return (!dr_info
.do_not_continue
);
154 wr_OK(FILE *fp
, char *parameter
, int set
, int value
)
156 (void) fprintf(fp
, "%s=%s\n", parameter
,
157 (set
? (value
? "OK" : "NOT_OK") : "NOT_TESTED"));
161 add_pkg_to_list(char *pkgname
)
163 struct dr_pkg_entry
**pkg_entry
;
165 if (pkg_handle
== -1) {
166 if ((pkg_handle
= bl_create(MALCOUNT
,
167 sizeof (struct dr_pkg_entry
), "package dryrun")) == -1)
171 pkg_entry
= &(dr_info
.packages
);
173 while (*pkg_entry
!= NULL
)
174 pkg_entry
= &((*pkg_entry
)->next
);
176 /* LINTED pointer cast may result in improper alignment */
177 *pkg_entry
= (struct dr_pkg_entry
*)bl_next_avail(pkg_handle
);
178 dr_info
.total_pkgs
++;
180 (void) snprintf((*pkg_entry
)->pkginst
, PKGNAMESIZE
, "%s%s",
181 (pkgname
? pkgname
: ""), ((this_exitcode
== 0) ? "" : "-"));
185 write_pkglist_ascii(void)
187 struct dr_pkg_entry
*pkg_entry
;
189 (void) fprintf(fp_dra
, "PKG_LIST=\"");
191 pkg_entry
= dr_info
.packages
;
193 (void) fprintf(fp_dra
, " %s", pkg_entry
->pkginst
);
194 pkg_entry
= pkg_entry
->next
;
197 (void) fprintf(fp_dra
, "\"\n");
201 write_string(int fd
, char *string
)
206 string_size
= strlen(string
) + 1;
210 if (write(fd
, &string_size
, sizeof (string_size
)) == -1) {
211 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
215 if (string_size
> 0) {
216 if (write(fd
, string
, string_size
) == -1) {
217 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
226 read_string(int fd
, char *buffer
)
230 if (read(fd
, &(string_size
), sizeof (string_size
)) == -1) {
231 progerr(gettext(ERR_NOREAD
), continue_bin
);
235 if (string_size
!= 0) {
236 if (read(fd
, buffer
, string_size
) == -1) {
237 progerr(gettext(ERR_NOREAD
), continue_bin
);
251 char *fs_mntpt
, *src_name
;
253 if ((fp_dra
= fopen(dryrun_sumasc
, "wb")) == NULL
) {
254 progerr(gettext(ERR_NOOPEN
), dryrun_sumasc
);
258 (void) fprintf(fp_dra
, "DR_TYPE=%s\n", (dr_info
.type
== REMOVE_TYPE
?
259 "REMOVE" : "INSTALL"));
261 (void) fprintf(fp_dra
, "PKG_INSTALL_ROOT=%s\n", (((get_inst_root()) &&
262 (strcmp(get_inst_root(), "/") != 0)) ?
263 get_inst_root() : ""));
265 write_pkglist_ascii();
267 wr_OK(fp_dra
, "CONTINUE", 1, !(dr_info
.do_not_continue
));
269 wr_OK(fp_dra
, "PARTIAL", dr_info
.partial_set
, dr_info
.partial
);
271 wr_OK(fp_dra
, "RUNLEVEL", dr_info
.runlevel_set
, dr_info
.runlevel
);
273 (void) fprintf(fp_dra
, "REQUESTEXITCODE=%d\n", dr_info
.reqexit
);
275 (void) fprintf(fp_dra
, "CHECKINSTALLEXITCODE=%d\n", dr_info
.checkexit
);
277 wr_OK(fp_dra
, "PKGFILES", dr_info
.pkgfiles_set
, dr_info
.pkgfiles
);
279 wr_OK(fp_dra
, "DEPEND", dr_info
.depend_set
, dr_info
.depend
);
281 wr_OK(fp_dra
, "SPACE", dr_info
.space_set
, dr_info
.space
);
283 wr_OK(fp_dra
, "CONFLICT", dr_info
.conflict_set
, dr_info
.conflict
);
285 wr_OK(fp_dra
, "SETUID", dr_info
.setuid_set
, dr_info
.setuid
);
287 wr_OK(fp_dra
, "PRIV", dr_info
.priv_set
, dr_info
.priv
);
289 wr_OK(fp_dra
, "PKGDIRS", dr_info
.pkgdirs_set
, dr_info
.pkgdirs
);
291 (void) fprintf(fp_dra
, "EXITCODE=%d\n", dr_info
.exitcode
);
293 (void) fprintf(fp_dra
, "ERRORMSG=%s\n", (exitmsg
? exitmsg
: "NONE"));
295 (void) fclose(fp_dra
);
297 if ((fp_dra
= fopen(dryrun_fsasc
, "wb")) == NULL
) {
298 progerr(gettext(ERR_NOOPEN
), dryrun_fsasc
);
302 (void) fprintf(fp_dra
, "%s\nFSUSAGE=\\\n\"\\\n", HDR_FSUSAGE
);
304 for (n
= 0; fs_mntpt
= get_fs_name_n(n
); n
++) {
306 bfree
= get_blk_free_n(n
);
307 bused
= get_blk_used_n(n
);
309 if (bfree
|| bused
) {
310 (void) fprintf(fp_dra
, "%s %s %s %d %d %lu %lu \\\n",
312 ((src_name
= get_source_name_n(n
)) ?
314 (is_fs_writeable_n(n
) ? "TRUE" : "FALSE"),
318 get_inode_used_n(n
));
322 dr_info
.total_fs_recs
= n
;
324 (void) fprintf(fp_dra
, "\"\n");
326 (void) fclose(fp_dra
);
328 if ((fp_dra
= fopen(dryrun_poasc
, "wb")) == NULL
) {
329 progerr(gettext(ERR_NOOPEN
), dryrun_poasc
);
333 dr_info
.total_ext_recs
= 0;
335 (void) fprintf(fp_dra
, "WOULD_INSTALL=\\\n\"\\\n");
337 for (n
= 0; extptr
&& extptr
[n
]; n
++) {
339 * Write it out if it's a successful change or it is from the
340 * prior dryrun file (meaning it was a change back then).
342 if ((this_exitcode
== 0 &&
343 (extptr
[n
]->mstat
.contchg
|| extptr
[n
]->mstat
.attrchg
)) ||
344 extptr
[n
]->mstat
.preloaded
) {
345 (void) fprintf(fp_dra
, "%c %s \\\n",
346 extptr
[n
]->cf_ent
.ftype
,
347 extptr
[n
]->client_path
);
349 /* Count it, if it's going into the dryrun file. */
350 if (extptr
[n
]->cf_ent
.ftype
!= 'i')
351 dr_info
.total_ext_recs
++;
355 (void) fprintf(fp_dra
, "\"\n");
357 (void) fclose(fp_dra
);
361 * This writes out a dryrun file.
366 struct fstable
*fs_entry
;
367 struct pinfo
*pkginfo
;
368 struct dr_pkg_entry
*pkg_entry
;
370 int fsentry_size
= sizeof (struct fstable
);
371 int extentry_size
= sizeof (struct cfextra
);
372 int pinfoentry_size
= sizeof (struct pinfo
);
374 if ((fd_drb
= open(dryrun_bin
,
375 O_RDWR
| O_APPEND
| O_TRUNC
)) == -1) {
376 progerr(gettext(ERR_NOOPEN
), dryrun_bin
);
380 /* Write the dryrun info table. */
381 if (write(fd_drb
, &dr_info
, sizeof (struct drinfo
)) == -1) {
382 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
386 /* Write out the package instance list. */
387 pkg_entry
= dr_info
.packages
;
389 if (write(fd_drb
, pkg_entry
->pkginst
, PKGNAMESIZE
) == -1) {
390 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
393 pkg_entry
= pkg_entry
->next
;
396 /* Write out the fstable records. */
397 for (n
= 0; n
< dr_info
.total_fs_recs
; n
++) {
398 fs_entry
= get_fs_entry(n
);
400 if (write(fd_drb
, fs_entry
, fsentry_size
) == -1) {
401 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
405 if (!write_string(fd_drb
, fs_entry
->name
))
408 if (!write_string(fd_drb
, fs_entry
->fstype
))
411 if (!write_string(fd_drb
, fs_entry
->remote_name
))
415 /* Write out the package objects and their attributes. */
416 for (n
= 0; extptr
&& extptr
[n
]; n
++) {
417 /* Don't save metafiles. */
418 if (extptr
[n
]->cf_ent
.ftype
== 'i')
422 * If it's a new package object (not left over from the
423 * continuation file) and it indicates no changes to the
424 * system, skip it. Only files that will change the system
427 if (extptr
[n
]->mstat
.preloaded
== 0 &&
428 !(this_exitcode
== 0 &&
429 (extptr
[n
]->mstat
.contchg
|| extptr
[n
]->mstat
.attrchg
)))
432 if (write(fd_drb
, extptr
[n
], extentry_size
) == -1) {
433 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
437 if (!write_string(fd_drb
, extptr
[n
]->cf_ent
.path
))
440 if (!write_string(fd_drb
, extptr
[n
]->cf_ent
.ainfo
.local
))
443 extptr
[n
]->cf_ent
.pinfo
= eptstat(&(extptr
[n
]->cf_ent
),
444 pkginst
, CONFIRM_CONT
);
447 * Now all of the entries about the various packages that own
450 pkginfo
= extptr
[n
]->cf_ent
.pinfo
;
453 if (write(fd_drb
, pkginfo
,
454 pinfoentry_size
) == -1) {
455 progerr(gettext(ERR_NOWRITE
), dryrun_bin
);
458 pkginfo
= pkginfo
->next
; /* May be several */
462 (void) close(fd_drb
);
468 if (dr_info
.partial
!= 0)
469 dr_info
.partial_set
= 0;
471 if (dr_info
.runlevel
!= 0)
472 dr_info
.runlevel_set
= 0;
474 if (dr_info
.pkgfiles
!= 0)
475 dr_info
.pkgfiles_set
= 0;
477 if (dr_info
.depend
!= 0)
478 dr_info
.depend_set
= 0;
480 if (dr_info
.space
!= 0)
481 dr_info
.space_set
= 0;
483 if (dr_info
.conflict
!= 0)
484 dr_info
.conflict_set
= 0;
486 if (dr_info
.setuid
!= 0)
487 dr_info
.setuid_set
= 0;
489 if (dr_info
.priv
!= 0)
490 dr_info
.priv_set
= 0;
492 if (dr_info
.pkgdirs
!= 0)
493 dr_info
.pkgdirs_set
= 0;
495 if (dr_info
.reqexit
== 0)
496 dr_info
.reqexit_set
= 0;
498 if (dr_info
.checkexit
== 0)
499 dr_info
.checkexit_set
= 0;
501 dr_info
.packages
= NULL
;
502 tot_pkgs
= dr_info
.total_pkgs
;
503 dr_info
.total_pkgs
= 0;
507 * This function reads in the various continuation file data in order to seed
508 * the internal data structures.
511 read_continue_bin(void)
514 int fsentry_size
= sizeof (struct fstable
);
515 int extentry_size
= sizeof (struct cfextra
);
516 int pinfoentry_size
= sizeof (struct pinfo
);
519 if (!init_pkgobjspace())
522 if ((fd_cnb
= open(continue_bin
, O_RDONLY
)) == -1) {
523 progerr(gettext(ERR_NOOPEN
), continue_bin
);
527 /* Read the dryrun info structure. */
528 if (read(fd_cnb
, &dr_info
, sizeof (struct drinfo
)) == -1) {
529 progerr(gettext(ERR_NOREAD
), continue_bin
);
535 if (this_type
!= dr_info
.type
) {
536 progerr(gettext(ERR_BADTYPE
),
537 (this_type
== REMOVE_TYPE
) ?
538 "a remove" : "an install",
539 (dr_info
.type
== REMOVE_TYPE
) ?
540 "a remove" : "an install");
544 /* Read in the dryrun package records. */
545 for (n
= 0; n
< tot_pkgs
; n
++) {
546 char pkg_name
[PKGNAMESIZE
];
548 if (read(fd_cnb
, &pkg_name
, PKGNAMESIZE
) == -1) {
549 progerr(gettext(ERR_NOREAD
), continue_bin
);
553 add_pkg_to_list(pkg_name
);
556 /* Read in the fstable records. */
557 for (n
= 0; n
< dr_info
.total_fs_recs
; n
++) {
558 struct fstable fs_entry
;
559 char name
[PATH_MAX
], remote_name
[PATH_MAX
];
562 if (read(fd_cnb
, &fs_entry
, fsentry_size
) == -1) {
563 progerr(gettext(ERR_NOREAD
), continue_bin
);
567 if (read_string(fd_cnb
, &name
[0]) == NULL
)
570 if (read_string(fd_cnb
, &fstype
[0]) == NULL
)
573 if (read_string(fd_cnb
, &remote_name
[0]) == NULL
)
576 if (load_fsentry(&fs_entry
, name
, fstype
, remote_name
)) {
577 progerr(gettext(ERR_FSFAIL
));
582 /* Read in the package objects and their attributes. */
583 for (n
= 0; n
< dr_info
.total_ext_recs
; n
++) {
584 struct cfextra ext_entry
;
585 struct pinfo pinfo_area
, *pinfo_ptr
;
586 char path
[PATH_MAX
], local
[PATH_MAX
], *local_ptr
;
588 if (read(fd_cnb
, &ext_entry
, extentry_size
) == -1) {
589 progerr(gettext(ERR_NOREAD
), continue_bin
);
594 * If the previous dryrun replaced a directory with a
595 * non-directory and we're going into *another* dryrun, we're
596 * stacking errors and continuation should not be permitted.
598 if (ext_entry
.mstat
.dir2nondir
&& dryrun_mode
)
599 dr_info
.do_not_continue
= 1;
602 * Since we just read this from a continuation file; it is,
603 * by definition, preloaded.
605 ext_entry
.mstat
.preloaded
= 1;
607 if (read_string(fd_cnb
, &path
[0]) == NULL
)
610 local_ptr
= read_string(fd_cnb
, &local
[0]);
612 ext_entry
.cf_ent
.pinfo
= NULL
;
615 * Now all of the entries about the various packages that own
619 if (read(fd_cnb
, &pinfo_area
, pinfoentry_size
) == -1) {
620 progerr(gettext(ERR_NOREAD
), continue_bin
);
625 pinfo_ptr
= eptstat(&(ext_entry
.cf_ent
),
626 pinfo_area
.pkg
, CONFIRM_CONT
);
628 if (pinfo_ptr
->next
) {
629 pinfo_ptr
= pinfo_ptr
->next
;
636 seed_pkgobjmap(&ext_entry
, path
, local_ptr
);
639 (void) close(fd_cnb
);
642 * Return as reading is done, so pkginstall doesn't
643 * read the same info from the system.
652 return (dryrun_mode
);
656 set_dryrun_mode(void)
662 in_continue_mode(void)
664 return (continue_mode
);
668 set_continue_mode(void)
674 * Initialize a dryrun file by assigning it a name and creating it
678 init_drfile(char **targ_ptr
, char *path
)
683 *targ_ptr
= strdup(path
);
684 targ_file
= *targ_ptr
;
686 if (access(targ_file
, W_OK
) == 0) {
687 (void) unlink(targ_file
);
690 n
= open(targ_file
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_EXCL
, 0644);
692 progerr(gettext(ERR_NOCREAT
), targ_file
);
702 * Initialize all required dryrun files and see that the target directory is
703 * present. If all goes well, we're in dryrun mode. If it doesn't, we're not.
706 init_dryrunfile(char *dr_dir
)
708 char temp_path
[PATH_MAX
];
709 char *dot_pos
= (temp_path
+strlen(dr_dir
)+7);
711 /* First create or confirm the directory. */
712 if (isdir(dr_dir
) != 0) {
713 (void) mkpath(dr_dir
);
716 (void) snprintf(temp_path
, sizeof (temp_path
), "%s/dryrun", dr_dir
);
718 (void) strcpy(dot_pos
, ISUMASC_SUFFIX
);
720 if (!init_drfile(&dryrun_sumasc
, temp_path
))
723 (void) strcpy(dot_pos
, FSASC_SUFFIX
);
725 if (!init_drfile(&dryrun_fsasc
, temp_path
))
728 (void) strcpy(dot_pos
, IPOASC_SUFFIX
);
730 if (!init_drfile(&dryrun_poasc
, temp_path
))
733 (void) strcpy(dot_pos
, IBIN_SUFFIX
);
735 if (!init_drfile(&dryrun_bin
, temp_path
))
738 dryrun_initialized
= 1;
742 init_contfile(char *cn_dir
)
744 char temp_path
[PATH_MAX
];
746 /* First confirm the directory. */
747 if (isdir(cn_dir
) != 0)
748 return; /* no continuation directory */
750 (void) snprintf(temp_path
, sizeof (temp_path
),
751 "%s/dryrun%s", cn_dir
, IBIN_SUFFIX
);
752 continue_bin
= strdup(temp_path
);
754 if (access(continue_bin
, W_OK
) != 0) {
759 continue_initialized
= 1;
763 set_dr_exitmsg(char *value
)
769 set_dr_info(int type
, int value
)
773 if (dr_info
.partial_set
== 0) {
774 dr_info
.partial_set
= 1;
775 dr_info
.partial
= (value
? 1 : 0);
780 if (dr_info
.runlevel_set
== 0) {
781 dr_info
.runlevel_set
= 1;
782 dr_info
.runlevel
= (value
? 1 : 0);
787 if (dr_info
.pkgfiles_set
== 0) {
788 dr_info
.pkgfiles_set
= 1;
789 dr_info
.pkgfiles
= (value
? 1 : 0);
794 if (dr_info
.depend_set
== 0) {
795 dr_info
.depend_set
= 1;
796 dr_info
.depend
= (value
? 1 : 0);
801 if (dr_info
.space_set
== 0) {
802 dr_info
.space_set
= 1;
803 dr_info
.space
= (value
? 1 : 0);
808 if (dr_info
.conflict_set
== 0) {
809 dr_info
.conflict_set
= 1;
810 dr_info
.conflict
= (value
? 1 : 0);
815 if (dr_info
.setuid_set
== 0) {
816 dr_info
.setuid_set
= 1;
817 dr_info
.setuid
= (value
? 1 : 0);
822 if (dr_info
.priv_set
== 0) {
823 dr_info
.priv_set
= 1;
824 dr_info
.priv
= (value
? 1 : 0);
830 if (dr_info
.pkgdirs_set
== 0) {
831 dr_info
.pkgdirs_set
= 1;
832 dr_info
.pkgdirs
= (value
? 1 : 0);
837 case REQUESTEXITCODE
:
838 if (dr_info
.reqexit_set
== 0) {
839 dr_info
.reqexit_set
= 1;
840 dr_info
.reqexit
= value
;
846 if (dr_info
.checkexit_set
== 0) {
847 dr_info
.checkexit_set
= 1;
848 dr_info
.checkexit
= value
;
854 if (dr_info
.exitcode
== 0) {
855 dr_info
.exitcode
= value
;
858 this_exitcode
= value
;
862 /* default to install if the value is kookie. */
864 if (value
== REMOVE_TYPE
)
865 this_type
= REMOVE_TYPE
;
867 this_type
= INSTALL_TYPE
;
874 write_dryrun_file(struct cfextra
**extlist
)
878 if (dryrun_initialized
) {
879 dr_info
.type
= this_type
;
881 add_pkg_to_list(pkginst
);
882 write_dryrun_ascii();
895 * Name: read_continuation
896 * Description: If continuation is initialised, reads the
897 * continuation binary file. The path for the
898 * same is freed, if set, as this is the last
900 * Sets: Error condition, through the pointer passed
902 * Returns: B_TRUE - if the continuation binary file
903 * from previous dryrun is read successfully.
904 * B_FALSE - if either continuation is not initialised
905 * or read was not successful.
908 read_continuation(int *error
)
910 boolean_t ret
= B_FALSE
;
912 if (continue_initialized
) {
913 if (!read_continue_bin()) {