diff -Nur --exclude '.*' portage-2.0.51.20.orig/bin/ebuild.sh portage-2.0.51.20.installwatch/bin/ebuild.sh --- portage-2.0.51.20.orig/bin/ebuild.sh 2005-04-20 08:19:03.000000000 -0700 +++ portage-2.0.51.20.installwatch/bin/ebuild.sh 2005-04-28 04:32:29.000000000 -0700 @@ -1119,12 +1119,25 @@ trap SIGINT SIGQUIT } +iw_begin() { + export SANDBOX_ON="1" + export SANDBOX_WRITE="/" + export SANDBOX_DEBUG="1" + export SANDBOX_DEBUG_LOG="$INSTALLWATCHFILE" +} + +iw_end() { + export SANDBOX_ON="0" + unset SANDBOX_DEBUG SANDBOX_DEBUG_LOG INSTALLWATCHFILE +} + dyn_preinst() { # set IMAGE depending if this is a binary or compile merge [ "${EMERGE_FROM}" == "binary" ] && IMAGE=${PKG_TMPDIR}/${PF}/bin \ || IMAGE=${D} - + [ -n "$INSTALLWATCHFILE" ] && iw_begin pkg_preinst + [ -n "$INSTALLWATCHFILE" ] && iw_end # hopefully this will someday allow us to get rid of the no* feature flags # we don't want globbing for initial expansion, but afterwards, we do @@ -1749,6 +1762,7 @@ ;; prerm|postrm|postinst|config) export SANDBOX_ON="0" + [ -n "$INSTALLWATCHFILE" ] && iw_begin if [ "$PORTAGE_DEBUG" != "1" ]; then pkg_${myarg} #Allow non-zero return codes since they can be caused by && @@ -1758,6 +1772,7 @@ #Allow non-zero return codes since they can be caused by && set +x fi + [ -n "$INSTALLWATCHFILE" ] && iw_end ;; unpack|compile|test|clean|install) if [ "${SANDBOX_DISABLED="0"}" == "0" ]; then diff -Nur --exclude '.*' portage-2.0.51.20.orig/pym/portage_installwatch.py portage-2.0.51.20.installwatch/pym/portage_installwatch.py --- portage-2.0.51.20.orig/pym/portage_installwatch.py 1969-12-31 16:00:00.000000000 -0800 +++ portage-2.0.51.20.installwatch/pym/portage_installwatch.py 2005-04-28 17:57:03.000000000 -0700 @@ -0,0 +1,126 @@ +import errno +import os +import stat +import portage_checksum + +def file_text_description(fullpath, root, delim): + """Create a string similar to portage CONTENTS format""" + mystat=os.lstat(fullpath) + mymode=mystat[stat.ST_MODE] + mymtime=mystat[stat.ST_MTIME] + + path=fullpath + if root!="/" and path[0:len(root)] == root: + path=path[len(root)-1:] + + if stat.S_ISLNK(mymode): + myto=os.readlink(fullpath) + return "sym"+delim+path+delim+"->"+delim+myto+delim+str(mymtime) + elif stat.S_ISDIR(mymode): + return "dir"+delim+path + elif stat.S_ISREG(mymode): + mymd5=portage_checksum.perform_md5(fullpath,calc_prelink=1) + return "obj"+delim+path+delim+mymd5+delim+str(mymtime) + elif stat.S_ISFIFO(mymode): + return "fif"+delim+path + else: + return "unk"+delim+path + +def process_iw_log(sandbox_debug_file, output_file, root, delim): + """Convert sandbox_debug_file to Portage CONTENTS format. + The output_file can be the same file as the input sandbox_debug_file + The output_file will be automatically removed if there is no output + Returns 0 on success, 1 if the input file does not exist or there is no output""" + try: + infile=open(sandbox_debug_file,"r") + except EnvironmentError, e: + if e.errno == errno.ENOENT: + return 1 + else: + raise e + possible_file_creation=[ + "open_wr", + "creat", + "creat64", + "mkdir", + "mknod", + "mkfifo", + "link", + "symlink", + "rename" + ] + + unique_files=[] + try: + for line in infile: + for prefix in possible_file_creation: + if line.startswith(prefix): + fullpath=line[11:-1] + if fullpath not in unique_files: + unique_files.append(fullpath) + break + except StopIteration, si: + pass + infile.close() + + unique_files.sort() + outfile=open(output_file,"w") + ignore_list=["/dev/null","/dev/tty"] + for fullpath in unique_files: + if fullpath not in ignore_list: + try: + outfile.write(file_text_description(fullpath, root, delim)+"\n") + except EnvironmentError, e: + if e.errno == errno.ENOENT: + # These files don't necessarily exist + pass + else: + raise e + outfile.flush() + outfile.close() + + if os.path.getsize(output_file)==0: + os.unlink(output_file); + return 1 + + return 0 + +def read_contents(contents_file_path): + """Read a portage CONTENTS file and return a list of files""" + files=[] + myc=open(contents_file_path,"r") + mylines=myc.readlines() + myc.close() + for line in mylines: + mydat = line.split() + files.append(mydat[1]) + return files + +def remove_duplicates(primary, secondary): + """remove lines from secondary which alread occur in primary + The secondary will be automatically removed if it contains no unique files + Return 0 upon success or 1 if secodary does not exist""" + infile=None + try: + infile=open(secondary,"r") + except EnvironmentError, e: + if e.errno == errno.ENOENT: + return 1 + else: + raise e + secondary_lines=infile.readlines() + infile.close() + primary_files=read_contents(primary) + + outfile=open(secondary,"w") + for line in secondary_lines: + mydat = line.split() + if mydat[1] not in primary_files: + # line already ends with \n + outfile.write(line) + outfile.close() + + if os.path.getsize(secondary)==0: + os.unlink(secondary); + + return 0 diff -Nur --exclude '.*' portage-2.0.51.20.orig/pym/portage.py portage-2.0.51.20.installwatch/pym/portage.py --- portage-2.0.51.20.orig/pym/portage.py 2005-04-23 00:39:43.000000000 -0700 +++ portage-2.0.51.20.installwatch/pym/portage.py 2005-04-28 17:02:27.000000000 -0700 @@ -1564,7 +1564,7 @@ # XXX This would be to replace getstatusoutput completely. # XXX Issue: cannot block execution. Deadlock condition. -def spawn(mystring,mysettings,debug=0,free=0,droppriv=0,fd_pipes=None,**keywords): +def spawn(mystring,mysettings,debug=0,free=0,droppriv=0,fd_pipes=None,iw_log=None,**keywords): """spawn a subprocess with optional sandbox protection, depending on whether sandbox is enabled. The "free" argument, when set to 1, will disable sandboxing. This allows us to @@ -1589,7 +1589,9 @@ if ("sandbox" in features) and (not free): keywords["opt_name"] += " sandbox" - if droppriv and portage_gid and portage_uid: + if iw_log: + env["INSTALLWATCHFILE"]=iw_log + elif droppriv and portage_gid and portage_uid: keywords.update({"uid":portage_uid,"gid":portage_gid,"groups":[portage_gid],"umask":002}) return portage_exec.spawn_sandbox(mystring,env=env,**keywords) else: @@ -2314,7 +2316,7 @@ droppriv=actionmap[mydo]["args"][1],logfile=logfile) return retval -def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,cleanup=0,dbkey=None,use_cache=1,fetchall=0,tree="porttree"): +def doebuild(myebuild,mydo,myroot,mysettings,debug=0,listonly=0,fetchonly=0,cleanup=0,dbkey=None,use_cache=1,fetchall=0,tree="porttree",iw_log=None): global db ebuild_path = os.path.abspath(myebuild) @@ -2595,7 +2597,10 @@ return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) elif mydo in ["prerm","postrm","preinst","postinst","config"]: mysettings.load_infodir(pkg_dir) - return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=1,logfile=logfile) + free=1 + if iw_log: + free=0 + return spawn(EBUILD_SH_BINARY+" "+mydo,mysettings,debug=debug,free=free,logfile=logfile,iw_log=iw_log) try: mysettings["SLOT"],mysettings["RESTRICT"] = db["/"]["porttree"].dbapi.aux_get(mycpv,["SLOT","RESTRICT"]) @@ -6428,12 +6433,19 @@ print ">>> Merging",self.mycpv,"to",destroot # run preinst script + iw_log=None + if "installwatch" in features: + iw_log=self.dbtmpdir+"/PREINST" if myebuild: # if we are merging a new ebuild, use *its* pre/postinst rather than using the one in /var/db/pkg # (if any). - a=doebuild(myebuild,"preinst",root,self.settings,cleanup=cleanup,use_cache=0) + a=doebuild(myebuild,"preinst",root,self.settings,cleanup=cleanup,use_cache=0,iw_log=iw_log) else: - a=doebuild(inforoot+"/"+self.pkg+".ebuild","preinst",root,self.settings,cleanup=cleanup,use_cache=0) + a=doebuild(inforoot+"/"+self.pkg+".ebuild","preinst",root,self.settings,cleanup=cleanup,use_cache=0,iw_log=iw_log) + + if iw_log: + import portage_installwatch + portage_installwatch.process_iw_log(iw_log, iw_log, root, " ") # XXX: Decide how to handle failures here. if a != 0: @@ -6514,13 +6526,6 @@ self.dbdir = self.dbtmpdir print ">>> original instance of package unmerged safely." - # We hold both directory locks. - self.dbdir = self.dbpkgdir - self.delete() - movefile(self.dbtmpdir, self.dbpkgdir, mysettings=self.settings) - - self.unlockdb() - #write out our collection of md5sums if cfgfiledict.has_key("IGNORE"): del cfgfiledict["IGNORE"] @@ -6545,12 +6550,28 @@ portage_locks.unlockfile(mylock) #do postinst script + iw_log=None + if "installwatch" in features: + iw_log=self.dbtmpdir+"/POSTINST" if myebuild: # if we are merging a new ebuild, use *its* pre/postinst rather than using the one in /var/db/pkg # (if any). - a=doebuild(myebuild,"postinst",root,self.settings,use_cache=0) + a=doebuild(myebuild,"postinst",root,self.settings,use_cache=0,iw_log=iw_log) else: - a=doebuild(inforoot+"/"+self.pkg+".ebuild","postinst",root,self.settings,use_cache=0) + a=doebuild(inforoot+"/"+self.pkg+".ebuild","postinst",root,self.settings,use_cache=0,iw_log=iw_log) + + if iw_log: + import portage_installwatch + portage_installwatch.process_iw_log(iw_log, iw_log, root, " ") + portage_installwatch.remove_duplicates(self.dbtmpdir+"/CONTENTS", self.dbtmpdir+"/POSTINST") + portage_installwatch.remove_duplicates(self.dbtmpdir+"/CONTENTS", self.dbtmpdir+"/PREINST") + + # We hold both directory locks. + self.dbdir = self.dbpkgdir + self.delete() + movefile(self.dbtmpdir, self.dbpkgdir, mysettings=self.settings) + + self.unlockdb() # XXX: Decide how to handle failures here. if a != 0: