summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--BUGS21
-rw-r--r--COPYING19
-rw-r--r--Changelog204
-rw-r--r--FAQ53
-rw-r--r--INTERNALS194
-rw-r--r--README63
-rw-r--r--etc/pm-config126
-rwxr-xr-xinstall4
-rw-r--r--stuff120
-rw-r--r--todo2
-rwxr-xr-xusr/sbin/pm-autocast440
-rwxr-xr-xusr/sbin/pm-build-md5image27
-rwxr-xr-xusr/sbin/pm-exclude31
-rwxr-xr-xusr/sbin/pm-grab42
-rwxr-xr-xusr/sbin/pm-image-diff52
-rwxr-xr-xusr/sbin/pm-post-cast270
-rwxr-xr-xusr/sbin/pm-pre-cast54
-rwxr-xr-xusr/sbin/pm-report-all104
-rwxr-xr-xusr/sbin/pm-restore169
-rwxr-xr-xusr/sbin/pm-seer667
-rwxr-xr-xusr/sbin/pm-setup74
-rwxr-xr-xusr/sbin/pm-test227
-rwxr-xr-xusr/sbin/pm-test-cast94
-rwxr-xr-xusr/sbin/pm-update97
-rw-r--r--usr/share/fprotect.lst1
25 files changed, 3155 insertions, 0 deletions
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..24d3561
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,21 @@
+just so we are clear here is a list of bugs i already know about:
+
+prometheus can not handle sorcery bugs well, this means that if sorcery
+hangs for whatever reason, which it does, if you run it for days on end
+like have been, prometheus is helpless to stop it.
+
+If bugzilla goes down addbug breaks miserably and gives up, someday I
+will make prometheus handle this more gracefully
+
+pm-update doesnt do cvs, but seeing as how we still mostly use p4...
+i think thats okay for now.
+
+if you ctrl-c out of prometheus somewhere the lock file sticks around
+and prometheus sees it later when you reload it. For some reason i
+have been unsuccessful at making it check for the existance of another
+pm-test process.
+
+Some would say this md5sum image stuff is a bug because it takes so
+long. However in other files I have clearly stated my opinion on this.
+Besides it works really well.
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..cb50cd1
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,19 @@
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..c6dce4a
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,204 @@
+2006-04-19 Seth Alan Woolley
+ * pm-build-md5image: Really fix PRUNES to be properly regex.
+ * pm-config: Correct the regular expression to protect pm-config.
+
+2006-04-17 Seth Alan Woolley
+ * pm-autocast: Fix expect strings to be properly unanchored.
+ * pm-config: Fix PRUNEs to be properly regular expressions.
+
+2005-05-30 Paul Mahon
+ * pm-autocast: Updated some of the strings for the new sorcery
+
+0.3.3
+
+updated/rewrote pm-autocast to be more robust
+added sectioning support, now one can say
+pm-test ham Ham
+(ham being a section, and Ham being the bugzilla component)
+
+lists are now more configurable and are lists that may someday be sharable are
+moved to a shared directory under $PMDIR/lists
+
+various bits and pieces of code cleanup/documentation have appeared
+
+0.3.1-pre4-6
+
+fixed annoying bugs that cropped up in quick untested changes
+
+0.3.1-pre2-3
+
+forgot some stuff, minor changes getting it otd
+
+0.3.1-pre1
+
+added support for requires and optional_requires. Prometheus will look
+at the list of spells sorcery gave it, then after removing spells that
+it thinks there are problems with, picks one at random. If none of the
+spells please prometheus, it will go with the default. Note in the case
+of optional_requires [none] will be choosen if no other spells work
+because there is no [none] spell to upset prometheus.
+
+There is also support for the md5sum menu's. Prometheus notices and
+records what the problem is (missing, ignored or different). Then it does
+whatever the default action is, which is settable through sorcery. It
+notices if the default choice was fatal and records that as well. The
+reports will then also show any md5 problems in a fairly simplistic way.
+
+finally, in response to a bug, the PRUNE variables in the md5sum image
+building code are now open for adjustment through the PRUNE_LOCAL
+variable.
+
+0.3.0
+
+hamish added some quick/dirty code make prometheus work with requires
+
+0.2.7
+typo in pm-restore...grrr
+
+0.2.6
+
+added some more question responses to pm-autocast, and made the timeout
+feature actually work, 30 minutes of zero input will case pm-deadlock
+to be run in an attempt to break the ice, this is close to what a real
+person would do.
+
+triggers are now essentially denied in all cases, im tired of dealing
+with them atm. If it turns out to be essential I'll spend some time
+trying to make them work.
+
+swapped the ordering of stuff in pm-seer, maybe things will be more readable
+
+cast.log is now bzip2'd in the snapshots as they were getting excessively
+large at 9mb for most xfree86 casts...
+
+the now compressed cast.log is being attached to bugs using my snazzy
+bugattach script now found in the bugcli spell.
+
+pm-restore now filters out new/different/deleted files from
+/var/lib/sorcery/{protected,excluded} as well as from yet another
+list known as fprotect.lst. fprotect.lst currently will be a manually
+maintained list of 'notable exceptions' such as /etc/X11/xdm/Xsession. The
+idea is to reduce the number of bugs about random files being left
+behind. Someday in the future I may add code to add and remove things
+from the list after a grace-period if its needed.
+
+The code added to pm-restore was mostly bored from sorcery's filter function
+but modified to work for our purposes.
+
+Note: the filtering occurs _after_ pm-restore restores things, but
+before the reporting is done, so that the system still stays the same,
+but things we dont care about arent reported as bugs.
+
+0.2.5
+changed the way the problems.lst checks for spell updates
+the old method kept the version number and updated field of the broken
+spell, then looked for changes, once a change occured the spell was
+taken off the list.
+
+It occured to me that the UPDATED field is designed for specifying
+when an update _must_ occur, regardless of a version change, well at least
+that is what used to be the case, and rather than redefine it according to my
+goals, and force guru's to change the field whenever they update the spell,
+I've decided to use md5sums instead.
+
+prometheus now executes
+md5=$(tar c $SPELL_DIRECTORY|md5sum|cut -f1 -d' ')
+
+as a new way of detecting spell updates
+
+this is more robust in that even if a developer forgets to change the updated
+field, any changes in the spell will be detected (and assumed a fix for whatever
+our problem is)
+
+the only drawback is that fixes not related to a bug we found in the
+spell will cause the spell to be removed from the problems list, and be
+availible for casting again, leading to duplicate bugs. However given
+that spells are rarely updated except for version bumps and bug-fixes,
+and that prometheus bugs seem to be pretty high priority, this shouldn't
+be a problem.
+
+0.2.4
+pm-setup now adds sorcery's sustained list to the ignore lists
+
+fixed a bug in pm-autocast where cast seems to send an eof after the
+Do you want to cast these spells? [y]
+query, even though the user answers 'y'
+I had to instruct expect to wait for an eof...hopefully things wont
+break because of this
+
+pm-restore now uses the --notriggers option when calling dispel, this
+is to avoid the annoying problems shown in bug 4111
+
+pm-restore also now does snapshot restoration in a safer way
+instead of deleting new files first, it deletes them last, leaving room
+for restoring deleted files and changed files. This caused an obscure bug
+whereby cut (and other things) had been moved from /usr/bin to /bin and
+prometheus was deleting the /bin version before it restored the /usr/bin/
+version, essentially causing it to stop functioning. (sorry demon_lord)
+
+fixed a bug in pm-seer whereby spells with identical versions were
+masking each other off the summary.
+
+0.2.3
+apparently perl doesnt like this following:
+$foo="stuff++";
+m/^$foo/;
+it complains because it sees the ++ as a nested operator, and cant handle it.
+I found out that m/^\Q$foo/; solves our problem, so this does it.
+See bug 4057 for reference.
+
+fixed some other annoyances with pm-seer
+
+
+0.2.2
+
+fixed a bug in pm-update that made it impossible for prometheus to remove
+spells from the updated list.
+
+fixed some stuff in pm-seer, but nothing major
+
+
+0.2.1
+various bug fixes:
+made pm-report-all ask for a report of smaller size which was provoking
+bugs in pm-seer's trimming code
+
+tried to fix pm-seer's trimming code
+
+changed pm-post-cast to grep for 'failure' and 'failed instead of just
+'failure', sometime spells dont cast due to missing source and cast
+reports 'failed' which obviously doesnt match 'failure'
+
+made pm-seer's high level summary try to make a better guess at the
+number of things its talking about on each line.
+"At least 'n' cast(s) failed"
+
+0.2
+excluded.lst IS NO MORE you need to re-run pm-setup if you were running 0.1
+sorry
+
+there is a more complex exclusion system that now handles most implicit
+casts/dispels well.
+
+pm-autocast uses expect, which answers questions from cast in a sensable
+manner it can also choose defaults or answer some questions randomly.
+
+pm-deadlock was added as a helper to pm-autocast
+
+problems.lst was added, it stored a record of all borked spells and
+during pm-uopdates things are taken off if they are new.
+
+pm-update will sync off of p4
+
+pm-seer was revamped and says more stuff.
+
+pm-config was prettied to do better with defaults being overridden
+
+pm-report-all now has options to email you successes as well as failures
+
+files that end up in /usr/local after cast are noticed and dealt with
+appropriatly
+
+'various bug fixes'
+
+I think thats it...
diff --git a/FAQ b/FAQ
new file mode 100644
index 0000000..80c131b
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,53 @@
+What is it?
+prometheus is an automated testing suite for sourcemage
+
+Why is it?
+prometheus was written because we lack the man-power to test our
+grimoires, but we still have plenty of spare compute cycles.
+
+How does it work?
+you should read the INTERNALS file, but the quick overview is that it
+casts things, then dispels them and reports any problems it found.
+
+How do i run it:
+well you should look at the README file for that. But basically
+get into the environment you want to run it in (UML, chroot, etc.), then
+cast prometheus, then type 'pm-setup' then 'pm-test once' to get going.
+
+Its frozen at the end/begining of a cast!
+yes...this is a probem with sorcery and should be fixed soon by our
+wonderful hardworking sorcery team. In the meantime just do a 'pkill
+casting'. pm-test-cast will finish up and things will keep going.
+Eventually I will write an expect script to take care of this (and other
+problems/features). If you know expect I want to talk to you!
+
+Whats the deal with this reporting stuff?
+well its pointless to cast and dispel stuff if we dont get the information
+about it right? Well at the moment prometheus is getting support to
+email reports and/or submit them to bugzilla (cool huh?).
+
+I updated my system and prometheus got angry and reverted it.
+Yea...prometheus is _very_ good at this, you need to re-run pm-setup
+after you update stuff. Although thankfully there still should be cached
+copies of everything floating around, so you can hopefully resurrect things.
+
+The power went out on my prometheus box
+okay well you dont want to run pm-test right away because it things might
+have been left in a funny state. Look at the $PMDIR/state file to try and
+figure out where things were left off. Run the remaining commands. The
+entire list of things is
+pm-update
+pm-pre-cast
+pm-test-cast
+pm-post-cast
+pm-restore
+
+Most of them leave a record in the state file and get annoyed with you
+if its something they didnt expect. If it says for example pm-test-cast:start
+just run pm-test-cast and two subsequent scripts. After that you are
+free to keep runing pm-test. Eventually support for this will be added
+to pm-test, and you wont even have to think about it.
+
+My question isnt in here!
+email me with it! I will promptly put it in here (and answer it even,
+what a deal!).
diff --git a/INTERNALS b/INTERNALS
new file mode 100644
index 0000000..10b261e
--- /dev/null
+++ b/INTERNALS
@@ -0,0 +1,194 @@
+If you are reading this you probably want to know how prometheus works.
+
+Well you are in the right place, lets get going.
+
+Overview:
+Its best to think of prometheus in a layered sense.
+
+There are three 'layers', there are two top level scripts, pm-test and
+pm-report-all. These are the primary interface you (the user) will
+use. Next there is a middle level, these consist of scripts that do the
+actual work, and are run by the top level scripts. They are: pm-update,
+pm-pre-cast, pm-test-cast, pm-post-cast, pm-restore, pm-autocast and
+pm-seer. At the bottom level are pm-image-diff, pm-grab, pm-build-md5image
+and pm-exclude. Most of these bottom level scripts are written in perl
+and do list crunching that I wouldnt want to even try in bash.
+
+The languages of choice are bash, perl, and expect. The bottom level
+scripts are written in perl and everything above it is written in bash. If
+you feel any script is better suited to be written in another language,
+go for it, that is partly why i designed prometheus to be modular.
+
+There are also two other pieces to the puzzle: pm-setup, which essentially
+sets up prometheus (so pm-test can run), and pm-config which specifies
+all the configurable bits of prometheus.
+
+Top level:
+
+Prometheus is built around what i call the 'cast-dispel cycle'.
+In a nutshell:
+0. update (optional)
+1. choose what to cast
+2. cast
+3. dispel
+4. repeat
+
+This is the core design, and is the most reasonable and flexable way I
+could come up with. In between 2 and 3 prometheus notices what succeeded
+and what failed, and records it in what I call a snapshot (more on this
+later). After step 3 prometheus restores the system to the state it was
+in before step 1, with a few slight caveats. Then prometheus is ready
+to begin anew at step 0 or 1.
+
+pm-test implements this cast dispel cycle using the various tools from
+the middle level. It records a time that a cycle began at and the
+subsequent scripts use that as a reference point, and the snapshot is
+named after that time, which is kept in $PMDIR/currtime
+
+pm-report-all can be run by pm-test, or not, it depends on values from
+the config file. When it does it makes a 'report' from each of the
+'snapshots' then possibly sends off the report as an email or as a bug
+to bugzilla.
+
+Middle level:
+This is where the magic happens, hopefully you understand the cast-dispel
+cycle, here I will flesh out the details, by listing each of the scripts
+in the order it is run by pm-test.
+
+pm-update:
+this script does a sorcery update and a scribe update. It then notices if
+there are any new spells and adds them to the queue of uncast spells. The
+DO_UPDATE variable in pm-config specifies whether or not it runs, its
+value should be either 'yes' or 'no'.
+
+pm-pre-cast:
+this script clears out any old activity log, and broken source trees
+in /usr/src. The scripts that figure out what installed and did not
+install rely on the activity logs. To simplify this code the activity
+log is truncated before each run. If there is an old one, it gets saved
+into activity.old, however in a running system the activity log should
+actually not exist at this point; except if you happened to have done
+some work in between a cast-dispel cycle *WHICH IS NOT SUPPORTED BTW*
+(unless you run pm-setup first).
+
+pm-test-cast:
+this is the crux of the whole operation. This script chooses what to cast,
+and guess what? casts it. Thats it.
+
+pm-post-cast:
+this is one of the most difficult and complicated scripts, I dont like
+it. What it is reponsible for doing is parsing the activity log and
+figuring out what cast and what failed. It also uses
+/var/state/sorcery/packages for things that are installed. Then for every
+successful cast it copies over the compile log, list of installed files,
+and cached installed files. For failed casts it saves the broken source
+tree into the snapshot along with the compile log and config.log for
+easy access.
+
+pm-restore:
+this is the second most complicated script IMO, next to pm-post-cast. This
+script dispels everything in the success list left by pm-post-cast. Then
+it notices all the things that have changed on the system. Anything that
+was deleted or changed it restores, and anything that is new it
+deletes. All three of these categories are included in the snapshot,
+every related file is saved.
+
+pm-make-report:
+this script is eventually going to be re-written by Duane, for now it
+just makes a report listing the things attempted to be cast, the things
+that worked, the things that didnt and the first thing that broke in
+the activity log.
+
+Bottom level:
+
+pm-exclude:
+this simple perl script does what no unix utility I could find does. It computes the set difference between two files where each line is a member. Basically if you have the lists:
+a
+b
+c
+d
+
+and:
+
+a
+c
+
+Then running pm-exclude between the two will output b d, or the members
+in the first list not in the second list. I wanted to use comm for this,
+but it didnt seem to do it the way i wanted, i wanted to use diff, but
+would take longer to parse its output than to run a script that does
+it itself. If you know of a way to do this that is just as fast by all means
+GO FOR IT!
+
+pm-grab:
+this simple script takes an input list and gives you n random things
+from it where n is a command line option. Its sole user is pm-test-cast.
+
+pm-build-md5image:
+this script does a find across (almost) the entire file tree and computes
+the md5sum of every file it finds, and dumps it to stdout. It is usually
+redirected to a file. It is basically a ripoff from updatedb from the
+slocate package. It ignores files from prometheus, and sorcery and other
+things as best it can, you can look at the source if you really want to
+see what it ignores.
+
+pm-image-diff:
+This takes two images, from pm-build-md5image and dumps into the files
+new.lst different.lst and deleted.lst what files are new, different,
+or deleted. Between the first (old) and second (new) images. The md5sums
+are used to compare files (clever i know).
+
+*NOTE*
+it has been suggested to use the undocumented feature of installwatch,
+which backs up all files that are changed to another directory instead
+of doing thigns this way. I have considered this and while I realize
+that it is faster, I do not feel that it fulfills the needs and goals
+of prometheus for the following reasons:
+
+* prometheus is supposed to be rock solid. Using an undocumented feature
+ is hardly the model of stability.
+* sorcery already uses installwatch and may take advantage of this new
+ undocumented feature in the future, and therefore I would not think that
+ using it myself in anyway would be improve the stability of either,
+ rather it could lead to unusual and confusing problems that im not
+ willing to deal with.
+* the existing system works. Not only does it work, it works really well,
+ I find myself changing minor things on the system and then noticing that
+ prometheus found it and reverted it, everytime. That means that whatever
+ the original image is, prometheus will keep the system looking that way,
+ and this I regard as a good thing. (think reproducable)
+
+Snapshots:
+
+So what are these mythical snapshot things? Well they live in
+$PMDIR/snapshot/<time>
+all the sorcery /etc/sorcery/local settings are saved there
+the packages and depends file from /var/state/sorcery is saved there
+the current prometheus queues and lists are saved there.
+the activity log is there
+the success and failure subdirectory hold all the data pm-post-cast can
+dig up on each spell.
+the directories new, diff.new, diff.old, and deleted hold files that
+pm-image-diff notices, the lists pm-image-diff makes are also kept in
+the snapshot.
+
+Anything else i missed?
+
+pm-setup:
+this makes the directory $PMDIR and some of the necessary things under it,
+and also makes the initial md5sum_image file. It also takes that list
+and cpio's all the files over to $PMDIR/image. This is the 'image'
+to which prometheus will revert to after each cast-dispel cycle.
+
+lists:
+there are a number of lists that prometheus uses. the most
+important one for you is excluded.lst, it is by default everything
+in /var/state/sorcery/packages, but may include other stuff for some
+reason, if you remove things prometheus will think that got installed
+and dispel it, you have been warned. currcast.lst is everything
+that prometheus is currently casting already cast on the last
+cycle. success.lst and failure.lst are those things that succeeded or
+failed since the last run of pm-post-cast. known.lst is every spell that
+prometheus (pm-update) could find in the grimoire EXCEPT for those in
+the excluded.lst. uncast.lst is the current queue of things to cast. It
+gets slowly trickled down to nothing then refilled again.
diff --git a/README b/README
new file mode 100644
index 0000000..38277b6
--- /dev/null
+++ b/README
@@ -0,0 +1,63 @@
+Thank you for reading the prometheus README file.
+
+You probably want to know how to set yourself up.
+So I'll cut to the chase:
+vi /etc/prometheus/pm-config ; pm-setup && pm-test forever
+
+You may run prometheus whereever you feel like it, a chroot, in UML, or on
+a native system. The scripts assume the look and feel of a real system. So
+before you run them, chroot or do whatever you need to in order to get
+into the environment you need to be in. In other words, my scripts have
+no idea what a chroot is, nor do they care. If you want to use a chrooted
+environment chroot there first and then run the scripts (no kidding).
+
+There are two main scripts at the moment that are important:
+pm-setup
+and
+pm-test
+
+Get into your environment, whatever that means, and run pm-setup. This
+will image the system and put things in place. Try to make sure the system
+is in the state you want it to be in before you run pm-setup, repeatedly
+running it shouldnt hurt anything, i recommend clearing out the
+PMDIR/lists directory before you try this, or at least running pm-update
+again if the DO_UPDATES variable is set to 'no'. That way you wont end
+up with excluded spells on the casting list...
+
+
+After that finishes you should edit /etc/prometheus/pm-config
+this is where you get to override anything in /etc/pm-config
+IF YOU WANT TO SEND REPORTS TO BUGZILLA YOU MUST PUT YOUR LOGIN AND
+PASSWORD HERE. There is no default prometheus account, you have to have
+your own, this is so we can track bugs down to a user if we need to.
+
+
+Once the configuration is set how you want it you are ready to go.
+
+pm-test harnesses all the other scripts and makes this whole things
+work. You can give it the argument 'once', 'forever', or 'until'. These
+are all pretty self explainitory. Once will run through the cycle
+once. forever will run forever, and until takes a time after it, which
+must be parsable by the date program. prometheus will not start another
+cycle after that time. You can also stop the forever or until modes by
+touching the file $PMDIR/stop, that will stop prometheus after the
+current cycle finishes. Ctrl-C'ing out of prometheus is normally not a
+good idea and can cause the next bug report to have extra information
+in it from the previous pm-test run.
+
+Now you are probably thinking that you want to use the tinderbox
+overnight, well the easy solution is to run pm-test forever as a cron job,
+then at some later time touch the stop file. Or
+
+ pm-test until 6:00 am tomorrow
+
+Reporting can be run at runtime after each cycle by turning on the
+RUNTIME_REPORT variable (set it to 'yes'). Or you can run pm-report-all
+at some other time.
+
+Right now pm-seer (written by Duane Malcom) is being developed. There
+is also the old crappy pm-make-report script that I wrote in 30 seconds
+as a proof of concept.
+
+I think thats just about it.
+email me with questions or read the FAQ
diff --git a/etc/pm-config b/etc/pm-config
new file mode 100644
index 0000000..d5d3356
--- /dev/null
+++ b/etc/pm-config
@@ -0,0 +1,126 @@
+#for the sake of simplicity
+#please override these defaults in /etc/prometheus/pm-config
+#thank you
+
+if [ -e /etc/prometheus/pm-config ];then
+ source /etc/prometheus/pm-config
+fi
+ PMDIR=${PMDIR:=/prometheus}
+
+ SNAP_DIR=${SNAP_DIR:=${PMDIR}/snapshot}
+
+ #the location of all local lists
+ LIST_DIR=${LIST_DIR:=${PMDIR}/lists}
+ #the location of all shared lists (ie over nfs)
+ #problems, known and ignore may live safely here
+ #when locking is in place, this is a pre-emptive re-arrange
+ SHARE_DIR=${SHARE_DIR:=${LIST_DIR}/share}
+
+ #all lists of uncasts things (grimoires, sections, etc)
+ #live here, this directory by default is under the shared directory
+ #so that it too may be shared
+ UNCAST_DIR=${UNCAST_DIR:=${SHARE_DIR}/uncast/}
+
+ #if one really wants this can be shared...
+ FPROTECT=${FPROTECT:=${LIST_DIR}/fprotect.lst}
+
+ #this also can be shared if you really want (ie in a ubiquitous cluster)
+ WINNER=${WINNER:=${LIST_DIR}/winner.lst}
+ LOSER=${LOSER:=${LIST_DIR}/loser.lst}
+
+ #change these if you want to not share individual ones, etc.
+
+ #default uncast list
+ UNCAST=${UNCAST:=${UNCAST_DIR}/grimoire}
+
+ #other random stuff that is sharing safe (someday)
+ KNOWN=${KNOWN:=${SHARE_DIR}/known.lst}
+ PROBLEMS=${PROBLEMS:=${SHARE_DIR}/problems.lst}
+ IGNORE=${IGNORE:=${SHARE_DIR}/ignore.lst}
+
+
+ GRIMOIRE=${GRIMOIRE:=test}
+ GRIMOIRE_PATH=/var/lib/sorcery/codex/${GRIMOIRE}
+ CODEX_CACHE=${CODEX_CACHE:=codex.index}
+
+#how long to keep stale snapshots for
+ KEEP_SNAP=${KEEP_SNAP:='2 weeks'}
+
+#how many to cast each cycle, normally 1
+ NUM_CAST=${NUM_CAST:=1}
+
+ DO_UPDATES=${DO_UPDATES:=no}
+
+#force an update no matter what just happened (cast exited early because
+#pm-autocast decided it didnt like the depends list for whatever was chosen
+#normally its good to say no here because otherwise you'll spend all day
+#updating otherwise. But if you really really want to, its here.
+#this has no meaning if DO_UPDATES is set to no
+ FORCE_UPDATES=${FORCE_UPDATES:=no}
+
+#clean out everything in /var/spool/sorcery
+#after a cast/dispel cycle. Useful for machines with low
+#hard drive space
+ CLEAR_SPOOL=${CLEAR_SPOOL:=no}
+
+#please make sure whatever variables you need to set for p4 are set in
+#/etc/prometheus/pm-config
+ P4_SYNC=${P4_SYNC:=no}
+
+#turn this to no if you want prometheus to answer questions about optional
+#depends and configuration questions in a random manner instead of doing
+#the default
+ DO_DEFAULTS=${DO_DEFAULTS:=yes}
+
+#If this is yes pm-test will run pm-report-snap after each run
+#if not you must run it by other means
+#in either case it will do all unreported snapshots
+RUNTIME_REPORT=${RUNTIME_REPORT:=yes}
+
+
+#places prometheus wont touch
+PRUNE_PATHS=".*sorcery.* /boot/.* /dev/.* /devices/.* /root/.* /home/.* /proc/.* /sys/.* /tmp/.* /mnt/.* $PMDIR/.* /etc/prometheus/.* /usr/src/.* /etc/pm-config /var/log/.* /etc/ld.so.cache /var/run/.* /var/spool/mail/.* /var/cache/compiler/.* /usr/man/.* /usr/share/prometheus/.* /lib/modules/.* /usr/sbin/pm-.*"
+
+#sorcery files in /usr/sbin/...
+PRUNE_SORCERY="/usr/sbin/cast /usr/sbin/gaze /usr/sbin/dispel /usr/sbin/scribe /usr/sbin/summon /usr/sbin/scribbler"
+
+#adjust this (in the local config file) for any local issues you may have
+#with protecting files
+PRUNE_LOCAL=${PRUNE_LOCAL:=""}
+
+#send a report to bugzilla
+
+REPORT_BUGZILLA=${REPORT_BUGZILLA:=no}
+
+#email report somewhere
+
+REPORT_MAIL=${REPORT_MAIL:=yes}
+REPORT_ADDRESS=${REPORT_ADDRESS:="root@localhost"}
+
+#email success reports too (lets you know its working)
+#this will not send things to bugzilla
+#no success report will (should) ever be sent to bugzilla,
+#since its not a bug!
+REPORT_SUCCESS=${REPORT_SUCCESS:=yes}
+
+
+
+#BUGCLI STUFF:
+#you need a bugzilla account
+#PLEASE OVVERIDE THESE
+ BUGZILLA_USER=${BUGZILLA_USER:="j@random.net"}
+ export BUGCLI_PASSWORD=${BUGCLI_PASSWORD:="wrong_passwd"}
+
+#tailor the following as needed
+ export BUGCLI_PRODUCT=${BUGCLI_PRODUCT:="Prometheus"}
+#in pm-test section this can be changed to the section name
+export BUGCLI_COMPONENT=${BUGCLI_COMPONENT:="Unknown"}
+#override this
+ export BUGCLI_VERSION=${BUGCLI_VERSION:="test grimoire"}
+ export BUGCLI_PRIORITY=${BUGCLI_PRIORITY:="P2"}
+ export BUGCLI_PLATFORM=${BUGCLI_PLATFORM:="x86"}
+ export BUGCLI_OS=${BUGCLI_OS:="Linux"}
+ export BUGCLI_SEVERITY=${BUGCLI_SEVERITY:="normal"}
+ export BUGCLI_SERVER=${BUGCLI_SERVER:="bugs.sourcemage.org"}
+export BUGCLI_DIRECTORY=${BUGCLI_DIRECTORY:="/"}
+
diff --git a/install b/install
new file mode 100755
index 0000000..f1c6d55
--- /dev/null
+++ b/install
@@ -0,0 +1,4 @@
+cp -v etc/pm-config $1/etc
+cp -v usr/sbin/* $1/usr/sbin/
+mkdir -p $1/usr/share/prometheus 2>/dev/null
+cp usr/share/fprotect.lst $1/usr/share/prometheus
diff --git a/stuff b/stuff
new file mode 100644
index 0000000..f922238
--- /dev/null
+++ b/stuff
@@ -0,0 +1,120 @@
+lists:
+
+ignore.lst
+ do not cast or dispel these for any reason whatsoever
+ do not modify, this is for the user to handle
+ note: deal with triggers
+
+problems.lst
+ update in pm-post-cast
+ casts that failed, remove when there is a new version pm-update
+
+installed.lst
+ list of thigns installed during pm-pre-cast
+ update uncast.lst with it
+ use for finding out what installed during pm-test-cast in pm-post-cast
+ do not dispel anything on this list
+
+excluded.lst
+ the cat of the above three lists
+
+uncast.lst
+ things on the queue
+known.lst
+ everything that could be on the queue
+
+currcast.lst
+success.lst
+failure.lst
+
+missing.lst ???
+
+deleted.lst
+new.lst
+different.lst
+usrlocal.lst
+
+fprotect.lst files to not report a problem about even if they are
+ new/different/deleted
+
+files:
+md5sum_image
+ md5sum of every file in the 'image'
+currtime
+ magic time constant that this test will use for archival purposes
+state
+ what phase we were last in: (name of the script)
+unreported
+ snapshots that havent been reported on yet
+reported
+ log of things being reported
+
+dir-tree:
+/etc/pm-config
+/usr/doc/tinderbox/...
+/tinderbox/
+/tinderbox/state
+/tinderbox/currtime
+/tinderbox/md5sum_image
+/tinderbox/image/...
+/tinderbox/lists/...
+/tinderbox/snapshot/<time>/...
+/tinderbox/save
+/tinderbox/unreported
+
+scripts:
+
+pm-config (sh)
+ sourced by things
+
+high level:
+pm-setup
+ build the initial environment
+
+pm-test [forever|once|grimoire|until] (sh)
+ this is a high level entry point, used by users, runs eternally
+ or once (nightly), or over an entire grimoire once
+
+ ***the entire grimoire once feature has not been written yet
+
+low level:
+pm-build_md5image (sh,done)
+ make an md5sum of the system as it stands
+
+pm-update (sh,done)
+ scribe/sorcery update, rebuilds uncast.lst
+ update problems.lst
+
+pm-pre-cast (sh,done)
+ clean activity log
+ clean out /usr/src
+ make installed.lst
+
+pm-test-cast (perl,done)
+ splice off part of uncast.lst into currcast.lst
+ cast it
+
+pm-post-cast (sh,almost done)
+ partition off into success and fail lists
+ archive stuff somewhere
+ update problems.lst
+ check for /usr/local
+
+pm-image-diff (perl, done)
+ outputs missing new and different files
+
+pm-restore (sh,done)
+ dispel success.lst
+ run pm-build-md5sum on the system
+ report differences with the old image using pm-image-diff
+
+ restore files from image/remove aliens
+
+pm-report-all
+ report on everything in unreported
+
+pm-seer
+ awesome reporting
+
+pm-exclude (perl,done)
+ take two lists and report what is on the first list but not the second
diff --git a/todo b/todo
new file mode 100644
index 0000000..5ffdeec
--- /dev/null
+++ b/todo
@@ -0,0 +1,2 @@
+locking
+cast sections
diff --git a/usr/sbin/pm-autocast b/usr/sbin/pm-autocast
new file mode 100755
index 0000000..b1c04a8
--- /dev/null
+++ b/usr/sbin/pm-autocast
@@ -0,0 +1,440 @@
+#!/usr/bin/expect
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+#options
+#1 ignore list
+#2 log file
+#3 defaults?
+#... options to cast -c -r
+
+set goahead y
+set defaults yes
+
+#######SUBROUTINES###############
+#log stuff with pm-autocast: prepended
+proc log { str } {
+ send_log "pm-autocast:$str\n"
+}
+
+#send a y or n if defaults is set to 'y' and send a \r otherwise
+proc shrug { } {
+ global defaults
+ if { [string match n* $defaults] } {
+ if { [expr rand()] < 0.5 } {
+ return y
+ } else {
+ return n
+ }
+ } else {
+ return \r
+ }
+}
+
+
+#given a spell name and an ignore file, return 1 if castable
+#0 otherwise
+proc castable {ignorelist spell} {
+ if {[lsearch $ignorelist $spell] == -1} {
+ return 1
+ } else {
+ return 0
+ }
+}
+
+#this looks at the input for the next word, which in the callers
+#context should be a spellname
+proc get_spell {sid} {
+ expect {
+ -i $sid
+ -re "(\[^\n? ]*)" {
+ set spell [string trim $expect_out(1,string) "\r\n\t "]
+ log "found: $spell\n"
+ return $spell
+ }
+ }
+}
+
+#grab a word from the input (presumably a spell)
+#if that spell is uncastable exit
+proc cancast {sid ignorelist spell} {
+ if { [castable $ignorelist $spell] == 0 } {
+ log "killing cast because the spell is uncastable"
+ kill_cast $sid
+ }
+}
+
+proc kill_cast {sid} {
+ log "kill_cast"
+ #send a ctrl-c
+ send -i $sid "\x03"
+ #let everyone know we died prematurely
+ catch { exec touch /tmp/pm-autocast-quit }
+ #and exit
+ exit
+ # nlm
+}
+
+proc fix_deadlock { } {
+ catch {exec ps -A | grep casting | awk {{print $1}} | tail -1 | xargs kill}
+}
+
+#######################EXPECT ROUTINES##################
+#answers all questions for requires
+#FIXME: should notice if we've answered a requires question already
+#and pick the same one again
+proc handle_requires { sid ignorelist requires} {
+ log "handle_requires"
+ if {![string length $requires]} {
+ error "No provider!"
+ }
+ global defaults
+ global requires_lookup
+ set length 0
+ set lst { }
+ log "running handle_requires!!\n"
+ while 1 {
+ expect {
+ -i $sid
+ -re {Continue to use [^yn]*\[[yn]]} {
+ log "requirement already fulfilled"
+ send -i $sid y
+ return
+ }
+ -re {(.*?)Which one do you want.\s+\[.]} {
+ set buffer $expect_out(1,string)
+ break
+ }
+ timeout kill_cast $sid
+ eof kill_cast $sid
+ }
+ }
+
+ array set char_to_spell ""
+ array set spell_to_char ""
+ set optional 0
+ set optional_idx ""
+ foreach line [split $buffer \n] {
+ if {[catch {llength $line}]} {
+ # oops this line isn't parsable
+ continue
+ }
+ set num [string index [lindex $line 0] 1]
+ set spell [lindex $line 1]
+ if {![string length $num] || ![string length $spell]} {
+ log "empty line on $line"
+ continue
+ }
+ if {[string equal {[none]} $spell]} {
+ set optional 1
+ set optional_idx $num
+ } elseif {[castable $ignorelist $spell]} {
+ set char_to_spell($num) $spell
+ set spell_to_char($spell) $num
+ }
+ }
+
+ if {[llength [array names char_to_spell]] == 0} {
+ if {$optional} {
+ send -i $sid $optional_idx
+ } else {
+ log "ack, none of these are castable!"
+ kill_cast $sid
+ }
+ } else {
+ if {[string match y* $defaults]} {
+ send -i $sid \r
+ } else {
+ # if an answer was already choosen, try that
+ if {[info exists requires_lookup($requires)]} {
+ set prefered_answer $requires_lookup($requires)
+ if {[info exists spell_to_char($prefered_answer]} {
+ set answer $spell_to_char($prefered_answer)
+ }
+ }
+ if {![info exists answer]} {
+ set idx [expr {int(rand()*[llength [array names char_to_spell]])}]
+ set answer [lindex [array names char_to_spell] $idx]
+
+ set requires_lookup($requires) $char_to_spell($answer)
+ }
+ send -i $sid $answer
+ }
+ }
+ expect -i $sid \n { }
+}
+
+
+#this jots down md5 issues and records their bug status (input variable)
+#and using expect, determines if they are fatal or not
+proc md5abort { sid bug spell tarball } {
+ set out [open /tmp/pm-automd5.lst a]
+ puts -nonewline $out "$spell:[string trim $tarball ". \n\r"]:$bug:"
+ expect {
+ -i $sid
+ -gl {Abort? \[y]} {
+ puts $out "fatal"
+ close $out
+ send -i $sid \r
+ kill_cast $sid
+ }
+ -gl {Abort? \[n]} {
+ puts $out "ignored"
+ close $out
+ send -i $sid \r
+ }
+ }
+ close $out
+ error "How did i get here"
+}
+
+#this is supposed to answer all questions from the initial q/a session
+proc do_initial { sid ignorelist } {
+ log "Made it into pm-autocast.do_initial"
+ while { 1 } { expect {
+ -i $sid
+ "is not a spell" {
+ log "oops, oh well"
+ }
+ -re {\n([^\n]*?) preparing environment} {
+ set spell $expect_out(1,string)
+ cancast $sid $ignorelist $spell
+ }
+ -re {\n([^\n]*?) checking dependencies for} {
+ set spell $expect_out(1,string)
+ cancast $sid $ignorelist $spell
+ }
+ -re "Install\[^\n]*script\[^\n]*\\\[?]" {
+ send -i $sid n
+ }
+ -re "Enable \[^\n]*script\[^\n]*\\\[?]" {
+ send -i $sid n
+ }
+ -re {Would you like to [^\n]*? the init and/or xinetd script} {
+ expect {
+ -i $sid
+ -gl {Which one do you want} {
+ # 0 is the "neither"
+ send -i $sid 0
+ }
+ }
+ }
+ -re "\[^\n]*to be the default provider of\[^\n]*\\\[?]" {
+ send -i $sid n
+ }
+ -re "Get \[^\n]* grimoire\?" {
+ send -i $sid n
+ expect -i $sid "\n" { }
+ }
+ -re {requires some (\S+)[^\n]*?\n} {
+ handle_requires $sid $ignorelist $expect_out(1,string)
+ }
+ "Do you want to use " {
+ send -i $sid [shrug]
+ expect -i $sid "\n" { }
+ }
+ -re "(Do you want to cast )|(Cast )" {
+ if { [castable $ignorelist [get_spell $sid]] } {
+ send -i $sid [shrug]
+ } else {
+ send -i $sid n
+ }
+ expect -i $sid "\n" { }
+ }
+ {Configure host.def} {
+ send -i $sid n
+ }
+ -re {[^\n]*\[.*]} { ;#any random question we dont know
+ set question $expect_out(0,string)
+ expect {
+ -i $sid
+ -timeout 2 "\n" {
+ log "$question is not a question"
+ }
+ timeout {
+ send -i $sid [shrug]
+ }
+ }
+ }
+ -gl "cannot initialize curses" {
+ log "oops you gave me a menu"
+ }
+ "CONTINUE casting" { send -i $sid "y" }
+ "you want to dispel" { send -i $sid "n" }
+ -re "Dispel\[^\n]*\?" { send -i $sid "n" }
+ -re "Spells are to be cast" {
+ return confirm
+ }
+ eof {
+ log "premature eof"
+ exit
+ }
+ timeout {
+ send_user "timeout!!! waited too long for something to happen"
+ #lets shoot something hope it fixes everything
+ }
+ }}
+}
+#this is supposed to watch all the output after casting has started and deal with it correctly.
+proc watch_cast {sid} {
+ #if there is no output for a half hour
+ #signal a timeout, ie shoot something
+ #if 60 minutes isnt enough i suppose i can make it configurable
+ set timeout 3600
+ while { 1 } { expect {
+ -i $sid
+ timeout
+ { #something is wrong...
+ send_user "timeout, oh well..."
+ exp_continue
+ #lets shoot something hope it fixes everything
+ #fix_deadlock
+ }
+ -re "Do you wish to add -- options to ./configure\[^\n]*" {
+ send -i $sid "n"
+ }
+ "CONTINUE casting" { send -i $sid "y" }
+ "you want to dispel" { send -i $sid "n" }
+ -re "Dispel.*\?" { send -i $sid "n" }
+ -re {([^:]*): doesn't have an MD5 sum for the uncompressed ([^\n]*)\n} {
+ md5abort $sid "missing" $expect_out(1,string) $expect_out(2,string)
+ }
+ -re {([^:]*): MD5 sum is different for uncompressed ([^\n]*)\n} {
+ log "different md5sum\n"
+ md5abort $sid "different" $expect_out(1,string) $expect_out(2,string)
+ }
+ -re {([^:]*): MD5 sum was purposefully left out for the uncompressed ([^\n]*)\n[^\n]*\n} {
+ md5abort $sid "ignored" $expect_out(1,string) $expect_out(2,string)
+ }
+
+ -re "Shall the .* init script be enabled on install" {send -i $sid "n"}
+ "Attempt to fix spells that may have become broken" {send -i $sid "n"}
+ -re {has triggered a .*? on spell} {
+ expect {
+ -i $sid
+ -re {Proceed.\s+\[.]} { send -i $sid "n" }
+ }
+ }
+ -re "Do you want to cast these\[^\n]*\\\[\[yn]]" {
+ #you already asked me this...must be one of those damn triggers
+ send -i $sid "n"
+ }
+ -re ".* to be the default provider of \[^\n]*\\\[\[yn]]" { send -i $sid "n" }
+
+ #all real patterns should go before this
+ -re "\[^\n]*\\\[\[yn]]" {
+ log "read $expect_out(0,string)doing the default\n"
+ send -i $sid "\r"
+ }
+ -re {([^\n]*(\[.*])|(\(.*\))|.*:)} {
+ #this might be a question or it might not be
+ #so what we do is wait for 60 seconds for something to go
+ #with the hope that _something_ on the next line
+ #will come by, in which case it was a false alarm
+ set question $expect_out(0,string)
+ expect {
+ -i $sid
+ -timeout 20 "\n" {
+ #log "$question is not a question"
+ }
+ timeout {
+ #poke
+ send -i $sid "\n"
+ }
+ }
+ }
+ -gl {Finished processing install requests} {
+ expect {
+ -i $sid
+ eof { return end }
+ }
+ }
+ -gl {Spells that encountered problems} {
+ expect {
+ -i $sid
+ eof { return end }
+ }
+ }
+ -re "\[^\n]*\n" { } ;#matches anything else keeps timeout from triggering
+ eof {
+ log "Premature eof"
+ return end
+ #FIXME uh...
+ }
+ }}
+}
+
+######REAL CODE###########
+
+proc main {argv} {
+ global defaults
+ set argc [ llength $argv ]
+ if { $argc < 3 } {
+ puts "args are not >=3"
+ exit
+ }
+
+ set env(TERM) builtin-dump
+
+ set ignore_file [lindex $argv 0]
+ set fd [open $ignore_file r]
+ set ignorelist [read $fd]
+ close $fd
+ log_file -noappend [lindex $argv 1]
+ set defaults [ lindex $argv 2 ]
+
+ set spells [lrange $argv 3 end]
+
+ puts "going to cast -r -c $spells"
+ set cast_pid [spawn cast -r -c $spells]
+ set sid $spawn_id
+
+ set timeout 180
+ set state initial
+ while 1 {
+ switch -- $state {
+ initial {
+ set state [do_initial $sid $ignorelist ]
+ continue
+ }
+ confirm {
+ expect {
+ -i $sid
+ -re "Do you want to cast these\[^\n]*\\\[\[yn]]" {
+ send -i $sid y
+ set state in_cast
+ continue
+ }
+ }
+ }
+ in_cast {
+ set state [watch_cast $sid]
+ continue
+ }
+ end {
+ exit 0
+ }
+ }
+ }
+}
+
+# exp_internal 1
+puts "starting"
+main $argv
+puys "ending"
diff --git a/usr/sbin/pm-build-md5image b/usr/sbin/pm-build-md5image
new file mode 100755
index 0000000..2e09813
--- /dev/null
+++ b/usr/sbin/pm-build-md5image
@@ -0,0 +1,27 @@
+#!/bin/bash
+###################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+###################################################################
+
+source /etc/pm-config
+#proper credit should go to the slocate guys for this...
+
+
+PRUNE_REGEX=$(echo "$PRUNE_PATHS $PRUNE_SORCERY $PRUNE_LOCAL" |sed -e 's,^,\\\(^,' -e 's, ,$\\\)\\\|\\\(^,g' -e 's,$,$\\\),')
+find $1 \( ! \( -type d -o -type b -o -type c -o -type p -o -type s -o -regex $PRUNE_REGEX \) \) -exec md5sum '2>/dev/null' '{}' ';' 2>/dev/null
diff --git a/usr/sbin/pm-exclude b/usr/sbin/pm-exclude
new file mode 100755
index 0000000..0eccd80
--- /dev/null
+++ b/usr/sbin/pm-exclude
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+########################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+############################################################
+
+#this prints everything in ARGV[0] that isnt in ARGV[1]
+#it is a convenience script for excluding certain lines from files
+#please show me a unix utility for this!
+open FD1,$ARGV[0] or die;
+open FD2,$ARGV[1] or die;
+#put everything in FD2 into a hash
+$arr2{$_}=1 for(<FD2>);
+#read everything from FD1 and if we didnt see it in FD2 print it
+map {print if $arr2{$_}!=1 } <FD1>
diff --git a/usr/sbin/pm-grab b/usr/sbin/pm-grab
new file mode 100755
index 0000000..0d5dcaa
--- /dev/null
+++ b/usr/sbin/pm-grab
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+
+#0=file list
+#1=number to grab
+#srand;
+
+#read in a list and randomize it in memory
+open FD,$ARGV[0] or die "missing $ARGV[0]";
+srand;
+while(<FD>){
+ my $r = rand @new+1;
+ push(@new,$new[$r]);
+ $new[$r] = $_;
+}
+close FD;
+#figure out how many to take
+if(exists $ARGV[1]){
+ $num=$ARGV[1]-1;
+}else{
+ $num=0;
+}
+#print out some number of random things from the list we read in
+print @new[0..$num];
diff --git a/usr/sbin/pm-image-diff b/usr/sbin/pm-image-diff
new file mode 100755
index 0000000..4bd5b48
--- /dev/null
+++ b/usr/sbin/pm-image-diff
@@ -0,0 +1,52 @@
+#!/usr/bin/perl
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+$\="\n";
+sub loadimage{
+ my ($input)=@_;
+ my %hash;
+ open FD,$input or die;
+ while(<FD>){
+ ($md5,@filename)=split;
+ $hash{"@filename"}=$md5;
+ }
+ close FD;
+ return %hash;
+}
+
+%old=loadimage($ARGV[0]);
+%new=loadimage($ARGV[1]);
+
+#for all the new files, if the file didnt exist before, remember it
+#(because its a new file and should be beaten with reeds)
+open FD,">new.lst";
+map{print FD if ! defined $old{$_};} keys %new;
+close FD;
+#compare md5sums of old files with those of new files
+#for all old files, if it is still around and the md5 is different...
+#(because the file changed needs fixing (and to be beaten with reeds))
+open FD,">different.lst";
+map {print FD if defined $new{$_} and defined $old{$_} and $new{$_} ne $old{$_}} keys %old;
+close FD;
+#for all old files that aren't in the list of new files...
+#(because we have to restore it...then beat someone with reeds)
+open FD,">deleted.lst";
+map{print FD if ! defined $new{$_};} keys %old;
+close FD;
diff --git a/usr/sbin/pm-post-cast b/usr/sbin/pm-post-cast
new file mode 100755
index 0000000..6e32c39
--- /dev/null
+++ b/usr/sbin/pm-post-cast
@@ -0,0 +1,270 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+
+source /etc/pm-config
+source /etc/sorcery/config
+
+##################################subroutines#####################
+
+#1 spell name
+source_spell(){
+ unset SOURCE_DIRECTORY
+ unset SPELL
+ unset VERSION
+ unset UPDATED
+ codex_set_current_spell_by_name $1
+}
+
+spell_to_section(){
+ section=$(egrep "^$1 " $GRIMOIRE_PATH/$CODEX_CACHE 2>/dev/null|
+ cut -f2 -d' ' 2>/dev/null|
+ xargs basename 2>/dev/null)
+ [ -z $section ] && section=Unknown
+ echo $section
+}
+
+
+#this function creates the success and failure lists
+#
+#assume that the activity log was previously truncated before
+#pm-test-cast was run, future implementations may compare with
+#a time stamp
+#
+#success.lst is defined as anything in the current activity log
+#that had success in it at the end, and anything new in
+#/var/state/sorcery/packages
+#
+#failure.lst is defined as anything from the activity log with
+#failure or failed in it
+split_activity(){
+ echo "Splitting casting.lst into success and failure lists."
+ TMPFILE=/tmp/pm.tmp.$$
+
+ rm -rf $TMPFILE
+
+ egrep "^[^a-zA-Z]*[ \t]*cast" /var/log/sorcery/activity > $TMPFILE
+
+ #failures from the activity log
+ egrep "(failure[^\w]*$)|(failed)" $TMPFILE|awk '{print $3}'|
+ pm-exclude - $IGNORE > /tmp/tfailure.lst
+
+ #successes from the activity log
+ egrep "success[^\w]*$" $TMPFILE|awk '{print $3;}'|sort|uniq > /tmp/asuccess.lst
+
+ #successes from the packages file
+ #diff between what sorcery says is installed and the previous packages list
+ cut -f1 -d: /var/state/sorcery/packages |
+ pm-exclude - $LIST_DIR/installed.lst > /tmp/psuccess.lst
+
+ #combine the lists, although they should be the same
+ sort /tmp/psuccess.lst /tmp/asuccess.lst |uniq|
+ pm-exclude - $IGNORE > $LIST_DIR/success.lst
+
+ # anything we asked to get cast that didnt get installed
+ # is a failure
+ for each in `cat $LIST_DIR/currcast.lst`; do
+ grep -q $each $LIST_DIR/success.lst || echo $each >> /tmp/tfailure.lst
+ done
+ sort /tmp/tfailure.lst|uniq > $LIST_DIR/failure.lst
+
+}
+
+#1 magic time value
+#returns snapshot directory
+snap_generic(){
+
+ local SNAPSHOT=$1
+
+ #sanity
+ if [ ! -d $SNAP_DIR ]; then
+ echo "Uhm...you'r missing a snapshot directory, oh well"
+ echo "I'll make one anyway"
+ rm -rf $SNAP_DIR
+ mkdir -p $SNAP_DIR 2>/dev/null
+ fi
+
+ (
+ mkdir -p $SNAPSHOT
+ mkdir -p $SNAPSHOT/attachment
+ mv /tmp/pm-automd5.lst $SNAPSHOT
+ cp -Rp /etc/sorcery/local/ $SNAPSHOT
+ cp /var/state/sorcery/packages $SNAPSHOT
+ cp /var/state/sorcery/depends $SNAPSHOT
+ bzip2 -c /tmp/cast.log > $SNAPSHOT/attachment/cast.log.bz2
+ mv /tmp/cast.err $SNAPSHHOT
+ ) 2>/dev/null
+}
+
+#both snap_sucess and snap_failure need to have some of the sorcery variables
+#defined, so be sure to source /etc/sorcery/config first
+#please note: they dont use subroutines of sorcery's, just variable in that
+#particular config file
+
+#1 list path
+#2 snapshot path
+snap_success(){
+ local SNAPSHOT=$1
+ local LOG_DIR=/var/log/sorcery
+
+ for each in $(cat $LIST_DIR/success.lst); do
+ echo "Snapshotting successful cast of $each"
+
+ local SDIR=$SNAPSHOT/success/$each
+
+ mkdir -p $SDIR 2>/dev/null
+
+ mkdir $SDIR/compile 2>/dev/null
+ mkdir $SDIR/install 2>/dev/null
+ mkdir $SDIR/md5sum 2>/dev/null
+ mkdir $SDIR/cache 2>/dev/null
+
+ #this needs to be a function somehow
+ source_spell "$each"
+ if [ $SPELL != $each ]; then
+ echo "$SPELL:$each" >> $SNAPSHOT/conflicted
+ fi
+ cp $LOG_DIR/compile/$each-${VERSION}* $SDIR/compile 2>/dev/null
+ cp $LOG_DIR/install/$each-* $SDIR/install 2>/dev/null
+ cp $LOG_DIR/md5sum/$each-* $SDIR/md5sum 2>/dev/null
+ cp $LOG_DIR/cache/$each-* $SDIR/cache 2>/dev/null
+ done
+}
+
+#1 list path
+#2 snapshot dir
+snap_failure(){
+ local SNAPSHOT=$1
+ for each in $(cat $LIST_DIR/failure.lst);do
+ echo "Snapshotting failed cast of $each"
+
+ local SDIR=$SNAPSHOT/failure/$each
+
+ mkdir -p $SDIR 2>/dev/null
+ mkdir -p $SDIR/compile 2>/dev/null
+
+ source_spell "$each"
+
+ #sanity...
+ if [ $SPELL != $each ]; then
+ echo "$SPELL:$each" >> $SNAPSHOT/conflicted
+ fi
+
+ md5=$(tar c $SCRIPT_DIRECTORY 2>/dev/null | md5sum|cut -f1 -d' ')
+ #LOCK
+ echo $SPELL:$md5 >> $PROBLEMS
+ #UNLOCK
+
+ if [ ! -e $SOURCE_DIRECTORY ];then
+ echo "no build directory for $each"
+ else
+ tar -czf $SDIR/$each.tar.gz $SOURCE_DIRECTORY
+ if [ -e $SOURCE_DIRECTORY/config.log ];then
+ bzip2 -c $SOURCE_DIRECTORY/config.log > $SDIR/attachment/$each-config.log.bz2
+ fi
+
+ fi
+ cp /var/log/sorcery/compile/$each-${VERSION}* $SDIR/compile 2>/dev/null
+ done
+}
+
+check_usrlocal(){
+ echo "checking for /usr/local files"
+ #files that were there before
+ grep "/usr/local" $PMDIR/md5sum_image |awk '{print $2}' > /tmp/usrlocal.ok
+ #files that are there now
+ find /usr/local \( -type p -o -type s \) -prune -o -type d -o -print >/tmp/usrlocal.found
+ #if theres nothing in /usr/local at all, return
+ if [[ ! -s /tmp/usrlocal.found ]]; then return ; fi
+
+ #otherwise clear off whats already there from our list
+ pm-exclude /tmp/usrlocal.found /tmp/usrlocal.ok > $SNAPSHOT/usrlocal.lst
+ #if theres still something there save it all for later consumption
+ if [[ -s $SNAPSHOT/usrlocal.lst ]]; then
+ mkdir -p $SNAPSHOT/usrlocal 2>/dev/null
+ for each in $(cat $SNAPSHOT/usrlocal.lst );do
+ echo "$each should not be in /usr/local"
+ mkdir -p usrlocal/$(dirname $each) 2>/dev/null
+ cp $each $SNAPSHOT/usrlocal/
+ done
+ fi
+ rm /tmp/usrlocal.*
+}
+###########################end-subroutines#####################
+###########################begin real code#####################
+
+#override from defaults
+#sanity checks
+#FIXME make a library for this
+if ! grep -q "pm-test-cast:stop" $PMDIR/state ; then
+ echo "Uhm...are you sure you just ran pm-test-cast?"
+ if [ ! -e $LIST_DIR/currcast.lst ];then
+ echo "Geez you dont even have a currcast.lst!"
+ echo "What do you take me for?"
+ exit 1
+ fi
+ yn='n'
+ read -p "do you want to continue? y/n" -t 60 yn
+ if [[ ! $yn=='y' ]]; then
+ exit 1
+ fi
+fi
+echo "pm-post-cast:start" > $PMDIR/state
+
+
+
+if [ ! -e $PMDIR/currtime ];then
+ echo "Uhm...there isnt a currtime file, oh well"
+ echo "I'll make one anyway"
+ date +%Y%m%d%H%M > $PMDIR/currtime
+fi
+currtime=$(cat $PMDIR/currtime)
+
+#real stuff
+split_activity
+
+#new list "WINNER" list any and all spells that successfully cast at least once
+#someday a script will synthesize winner lists into a pretty listing with
+#frequencies
+touch $WINNER
+cat $LIST_DIR/success.lst >> $WINNER
+cat $LIST_DIR/failure.lst >> $LOSER
+
+
+echo "Make snapshot of cast"
+SNAPSHOT="$SNAP_DIR/$currtime"
+snap_generic "$SNAPSHOT"
+
+#determine the component
+if [ -e $LIST_DIR/failure.lst ] ; then
+ spell_to_section $(head -1 $LIST_DIR/failure.lst)> $SNAPSHOT/component
+else
+ spell_to_section $(head -1 $LIST_DIR/currcast.lst)> $SNAPSHOT/component
+fi
+
+source /etc/sorcery/config #oh boy...but dont worry, I only want the variables
+snap_success "$SNAPSHOT"
+snap_failure "$SNAPSHOT"
+
+check_usrlocal $SNAPSHOT
+
+echo "pm-post-cast:stop" > $PMDIR/state
+exit 0
+###########################end real code########################
diff --git a/usr/sbin/pm-pre-cast b/usr/sbin/pm-pre-cast
new file mode 100755
index 0000000..142d0cd
--- /dev/null
+++ b/usr/sbin/pm-pre-cast
@@ -0,0 +1,54 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+source /etc/pm-config
+
+#this script cleans up the activity log
+#cleans up /usr/src
+#and saves the /var/state/sorcery/packages file
+
+
+if [ ! -e $PMDIR/currtime ]; then
+ echo "Uhm...there isnt a currtime file, oh well"
+ echo "I'll make one anyway"
+ date +%Y%m%d%H%M > $PMDIR/currtime
+fi
+TIME=$(cat $PMDIR/currtime)
+echo "pm-pre-cast:start" > $PMDIR/state
+if [ -e /var/log/sorcery/activity ]; then
+ echo "theres still an activity log"
+ cat /var/log/sorcery/activity >> /var/log/sorcery/activity.old
+fi
+echo "Making a new activity log"
+cat /dev/null > /var/log/sorcery/activity
+
+
+cd /usr/src
+echo "Clearing out broken source trees"
+ls|egrep -v "^linux"|xargs rm -rf
+echo "Clearing /tmp"
+rm -rf /tmp/*
+
+echo "saving packages list"
+cp /var/state/sorcery/packages $PMDIR
+cut -f1 -d: /var/state/sorcery/packages > $LIST_DIR/installed.lst
+
+echo "pm-pre-cast:stop" > $PMDIR/state
+exit 0
diff --git a/usr/sbin/pm-report-all b/usr/sbin/pm-report-all
new file mode 100755
index 0000000..4b1b808
--- /dev/null
+++ b/usr/sbin/pm-report-all
@@ -0,0 +1,104 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+source /etc/pm-config || exit 1
+
+clean_old(){
+ ls $SNAP_DIR > /tmp/snaps.1
+ touch $PMDIR/unreported $PMDIR/save
+ cat $PMDIR/unreported $PMDIR/save > /tmp/snaps.save 2>/dev/null
+
+ pm-exclude /tmp/snaps.1 /tmp/snaps.save > /tmp/snaps.2
+
+ if [[ $KEEP_SNAP == "" ]]; then
+ KEEP_SNAP="1 week"
+ fi
+ let deltime=$(date -d "$KEEP_SNAP ago" +%Y%m%d%H%M)
+
+ for each in $(cat /tmp/snaps.2);do
+ let snaptime=$each
+ if (( $snaptime < $deltime ));then
+ echo "deleting snapshot: $snaptime < $deltime"
+ rm -rf $SNAP_DIR/$each
+ fi
+ done
+}
+
+#we move first to slice off any unreported snapshots
+#since this can run asynchronously with pm-test
+
+mv $PMDIR/unreported /tmp/unreported.tmp
+sort /tmp/unreported.tmp|uniq > /tmp/unreported
+touch $PMDIR/unreported
+
+
+#this is bad...FIXME!
+for each in $(cat /tmp/unreported); do
+ SNAPSHOT=$SNAP_DIR/$each
+ rm -rf $SNAPSHOT/report.*
+ echo reporting on $each in $SNAPSHOT
+ pm-seer --snapshot $each --summary --compile 200
+ if [ -s $SNAPSHOT/report.failure ]; then
+ echo "failure"
+ [[ -s $SNAPSHOT/title ]] && summary=$(cat $SNAPSHOT/title) || summary="$HOSTNAME:$each"
+ if [[ $REPORT_BUGZILLA == 'yes' && $(which addbug 2>/dev/null) ]]; then
+ echo sending $each to bugzilla
+ (
+ echo -n "bugzilla:$each:"
+ [[ -s $SNAPSHOT/component ]] && BUGCLI_COMPONENT=$(cat $SNAPSHOT/component)
+ foo=$(addbug --summary "$summary" \
+ --description "`cat $SNAPSHOT/report.failure`"\
+ --login "$BUGZILLA_USER"|
+ grep -e '\(Title: Bug .* Submitted\)\|\(bugid: \)\|\(error: \)')
+ bugid=$(echo $foo|perl -ne "print /([0-9]+)/")
+ [[ $bugid == "" ]] && echo $each >> /prometheus/report_mia
+ echo $bugid
+
+ [[ $(which bugattach 2>/dev/null) ]] &&
+ for file in $SNAPSHOT/attachment/* ; do
+ bar=$(bugattach --login "$BUGZILLA_USER" \
+ --description "cast.log.bz2 for $each" \
+ --bugid $bugid \
+ --filename $file)
+ echo attachment:$file:$bar
+ done
+ )>> $PMDIR/reported
+ fi
+#i hate this feature and want it to go away
+ if [[ $REPORT_MAIL == 'yes' &&
+ -x /usr/bin/mutt && REPORT_ADDRESS != "" ]] ; then
+ echo "sending mail"
+ cat $SNAPSHOT/report.failure |
+ mutt $REPORT_ADDRESS -s "$summary"
+ echo "email:$each:$REPORT_ADDRESS" >> $PMDIR/reported
+ fi
+ elif [[ -s $SNAPSHOT/report.success &&
+ $REPORT_SUCCESS == 'yes' &&
+ $REPORT_MAIL == 'yes' &&
+ -x /usr/bin/mutt && REPORT_ADDRESS != "" ]] ;then
+ echo "success"
+ echo "sending mail"
+ cat $SNAPSHOT/report.success |
+ mutt $REPORT_ADDRESS -s "$each prometheus success"
+ echo "email:$each:$REPORT_ADDRESS" >> $PMDIR/reported
+ fi
+done
+rm /tmp/unreported
+clean_old
diff --git a/usr/sbin/pm-restore b/usr/sbin/pm-restore
new file mode 100755
index 0000000..ee8bd95
--- /dev/null
+++ b/usr/sbin/pm-restore
@@ -0,0 +1,169 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+
+
+#########################borrowed/modified from sorcery ###################
+filter() {
+
+ if [ -f $1 ]; then
+ RID_LIST=`for each in $(cat $1); do echo -n "^$each\|"; done
+ echo -n "/dev/null"`
+ grep -v "$RID_LIST"
+ else
+ cat
+ fi
+}
+
+fix_list(){
+ rm -rf /tmp/$1
+ filter $FPROTECT < $1 |
+ filter $SHARE_DIR/badfiles.lst |
+ filter /var/lib/sorcery/protected |
+ filter /var/lib/sorcery/excluded > /tmp/$1
+ mv /tmp/$1 .
+}
+
+##########################################################
+source /etc/pm-config || exit 10
+if ! grep -q "pm-post-cast:stop" $PMDIR/state ; then
+ echo "Uhm...are you sure you just ran pm-post-cast?"
+ if [ ! -e $LIST_DIR/success.lst ] ||
+ [ ! -e $LIST_DIR/failure.lst ];then
+ echo "Geez you dont even have a currcast.lst!"
+ echo "What do you take me for?"
+ exit 1
+ fi
+ yn='n'
+ read -p "do you want to continue? y/n" -t 60 yn
+ if [[ ! $yn=='y' ]]; then
+ exit 1
+ fi
+fi
+echo "pm-restore-cast:start" > $PMDIR/state
+
+if [ ! -e $PMDIR/currtime ];then
+ echo "Uhm...there isnt a currtime file, oh well"
+ echo "I'll make one anyway"
+ date +%Y%m%d%H%M > $PMDIR/currtime
+fi
+currtime=$(cat $PMDIR/currtime)
+
+SNAPSHOT="$SNAP_DIR/$currtime"
+
+if [ ! -d $SNAPSHOT ]; then
+ echo "Uhm...theres no snapshot directory: $SNAPSHOT"
+ echo "Oh well..."
+ rm -rf $SNAPSHOT
+ mkdir $SNAPSHOT
+fi
+
+if [ -s $LIST_DIR/success.lst ]; then
+ echo "dispelling:"
+ cat $LIST_DIR/success.lst
+ cat $LIST_DIR/success.lst|xargs dispel --notriggers |tee /tmp/dispellog.$$
+ mv /tmp/dispellog.$$ $SNAPSHOT/dispel.log
+ ldconfig
+else
+ echo "nothing to dispel!"
+fi
+
+echo "Making an md5sum image (this takes a while)"
+
+
+pm-build-md5image / > $SNAPSHOT/md5sum_image
+
+echo "Finding differences between images"
+
+cd $SNAPSHOT
+
+pm-image-diff $PMDIR/md5sum_image $SNAPSHOT/md5sum_image
+mkdir deleted diff.old diff.new new 2>/dev/null
+rm -f /tmp/badfiles.lst
+touch /tmp/badfiles.lst
+touch $SHARE_DIR/badfiles.lst
+
+old_ifs=$IFS
+IFS="
+"
+
+echo "Fixing changed files"
+if [ -s different.lst ]; then
+ rm -f diff.log
+ for each in $(cat different.lst );do
+ echo "$each was changed, restoring old and saving new"
+ mkdir -p diff.new/$(dirname $each) 2>/dev/null
+ cp $each diff.new/$each
+ mkdir -p diff.old/$(dirname $each) 2>/dev/null
+ cp $PMDIR/image/$each diff.old/$each
+ mkdir -p $(dirname $each) 2>/dev/null
+ cp $PMDIR/image/$each $each
+ ( echo --------$each--------
+ diff diff.old/$each diff.new/$each
+ ) >> diff.log
+ done
+ fix_list different.lst
+ cat different.lst >> /tmp/badfiles.lst
+fi
+
+echo "Restoring deleted files"
+if [ -s deleted.lst ]; then
+ for each in $(cat deleted.lst );do
+ echo "$each is deleted, restoring"
+ mkdir -p deleted/$(dirname $each) 2>/dev/null
+ cp $PMDIR/image/${each} deleted/$each
+ mkdir -p $(dirname $each) 2>/dev/null
+ cp $PMDIR/image/$each $each
+ done
+ fix_list deleted.lst
+ cat deleted.lst >> /tmp/badfiles.lst
+fi
+
+#this needs to happen last
+#or else bad things happen
+echo "Removing new files"
+if [ -s new.lst ]; then
+ for each in $(cat new.lst ); do
+ echo "$each is a new file, fixing..."
+ mkdir -p $(dirname $each) 2>/dev/null
+ mv $each new
+ rdir=$(dirname $each)
+ while (rmdir $rdir 2> /dev/null); do
+ rmdir=$(dirname $rdir)
+ done
+ done
+ fix_list new.lst
+ cat new.lst >> /tmp/badfiles.lst
+fi
+IFS=$old_ifs
+
+cat $SHARE_DIR/badfiles.lst >> /tmp/badfiles.lst
+sort /tmp/badfiles.lst|uniq > $SHARE_DIR/badfiles.lst
+rm /tmp/badfiles.lst
+
+ldconfig
+
+echo "Copying lists to snapshot"
+cp -Rp $PMDIR/lists $SNAPSHOT
+echo "Saving activity log"
+mv /var/log/sorcery/activity $SNAPSHOT
+echo "adding snapshot to list of unreported snapshots"
+echo $currtime >> $PMDIR/unreported
+echo "pm-restore-cast:stop" > $PMDIR/state
diff --git a/usr/sbin/pm-seer b/usr/sbin/pm-seer
new file mode 100755
index 0000000..5c87327
--- /dev/null
+++ b/usr/sbin/pm-seer
@@ -0,0 +1,667 @@
+#!/usr/bin/perl
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Duane Malcolm d.malcolm@auckland.ac.nz
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+
+#todo:
+# remove useless options
+# make summon fail list
+# title
+# clean up config/option crap...
+# output trimming
+#
+
+# Seer is a reporting tool for prometheus.
+
+sub seer_usage {
+ print STDOUT << "END_OF_HELP";
+Seer is a reporting tool prometheus output.
+
+Function:
+ --summary : implies all other reporting options in the order they appear
+ --snapshot <snapshot> : snapshot name
+ --output <output dir> : full path to output dir (default is snapshot dir)
+ --bzip2 : bzip2 the final report
+
+reporting options:
+ --coord : the where and then when
+ --hls : make a high level summary
+
+ --analysis : try to say something intelligent
+ --lists : reports current, failure and success lists
+ --md5 : lists the md5sum problems encountered if any
+
+ --activity : reports activity
+ --depends : lists dependency settings
+ --grimoires : reports grimoires installed and their ordering
+ --system : reports system and prometheus settings
+ --installed : lists things installed (a bit redundant)
+
+
+ --compile <#lines|full> : reports #line or all of compile log
+
+ --badfiles : report files that were deleted, left, or changed
+ --usrlocal : files that somehow ended up in /usr/local
+
+END_OF_HELP
+}
+
+
+# Perl tips:
+# - map { print $_; } @list; to print a list
+# - for @list { print $_;}
+# - read a file into a list like this: @list=<FILE>;
+# - print "looping" while(1);
+# - set the field seperator: $"
+# - end of a list seperator: $\
+# - $CONFIG{"current_list"}=$CONFIG{lists_dir}/currcast.lst");
+
+
+#DEFAULTS
+%CONFIG=('compile_lines',40, 'config_lines','full', 'max_size',65536, 'help',0);
+seer_initialize();
+seer_parse();
+if($CONFIG{"help"}){
+ seer_usage();
+ exit 1;
+} elsif(!defined $CONFIG{snapshot_name}){
+ print "You need to give me a snapshot";
+ exit 1;
+}
+seer_build_config();
+main();
+
+###################
+### SUBROUTINES ###
+###################
+sub seer_build_config(){
+ #this is where the snapshot is located
+ $CONFIG{"snapshot_dir"} ="$PMCONFIG{SNAP_DIR}/$CONFIG{snapshot_name}";
+ $CONFIG{"output_dir"} ="$CONFIG{snapshot_dir}"
+ if (! defined $CONFIG{output_dir});
+
+ #files and stuff
+ $CONFIG{"current_list"} ="$CONFIG{snapshot_dir}/lists/currcast.lst";
+ $CONFIG{"success_list"} ="$CONFIG{snapshot_dir}/lists/success.lst";
+ $CONFIG{"failure_list"} ="$CONFIG{snapshot_dir}/lists/failure.lst";
+ $CONFIG{"config_file"} ="$CONFIG{snapshot_dir}/local/config";
+ $CONFIG{"grimoire_file"} ="$CONFIG{snapshot_dir}/local/grimoire";
+ $CONFIG{"activity_file"} ="$CONFIG{snapshot_dir}/activity";
+ $CONFIG{"compile_logs"} ="$CONFIG{snapshot_dir}/failure";
+ $CONFIG{"config_logs"} ="$CONFIG{snapshot_dir}/failure";
+ $CONFIG{"installed_file"} ="$CONFIG{snapshot_dir}/packages";
+ $CONFIG{"depends_file"} ="$CONFIG{snapshot_dir}/depends";
+
+ #stuff for bad files
+ $CONFIG{"old_list"} ="$CONFIG{snapshot_dir}/old.lst";
+ $CONFIG{"new_list"} ="$CONFIG{snapshot_dir}/new.lst";
+ $CONFIG{"diff_list"} ="$CONFIG{snapshot_dir}/different.lst";
+ $CONFIG{"diff_log"} ="$CONFIG{snapshot_dir}/diff.log";
+ $CONFIG{"usrlocal_list"} ="$CONFIG{snapshot_dir}/usrlocal.lst";
+ $CONFIG{"pm_automd5"} ="$CONFIG{snapshot_dir}/pm-automd5.lst";
+}
+
+sub main() {
+ seer_read();
+
+ if(defined %ERROR){
+ $CONFIG{output_file}="$CONFIG{output_dir}/report.failure";
+ open FILE, ">$CONFIG{output_dir}/title" or
+ die "Error: Can't open $CONFIG{output_dir}/title\n";
+ seer_write_title() if($CONFIG{title});
+ close FILE;
+ }else{
+ $CONFIG{output_file}="$CONFIG{output_dir}/report.success";
+ }
+
+ $not_done = 1;
+ while($not_done) {
+print "in loop";
+ seer_write();
+print "did write";
+ $total = 0;
+ map { $total += $LINES{$_}} keys %LINES;
+print "$total";
+ if ( $total > $CONFIG{max_size} ) {
+print "doing the trim";
+ seer_trim();
+ } else {
+ $not_done = 0;
+ }
+ }
+
+ if($CONFIG{bzip2}){
+ `bzip2 -f $CONFIG{output_file}`;
+ }
+}
+
+sub seer_initialize{
+ #source the config file and then output all the variables
+ #store the output in $foo
+ my $foo=`bash -norc -noprofile -c "source /etc/pm-config 2>/dev/null && set"`;
+ if ($?!=0){
+ seer_usage();
+ exit 1;
+ }
+ map{
+ ($key,$value)=split "=",$_;
+ $PMCONFIG{$key}=$value;
+ }split "\n",$foo;
+}
+
+
+sub seer_parse{
+ for($i=0;$i<=@ARGV;$i++){
+ if($ARGV[$i]=~/^--summary/){
+ %CONFIG=(%CONFIG, coord,1, hls,1, analysis,1, activity,1, lists,1,
+ installed,1, depends,1, "system",1, "local",1,
+ grimoires,1, compile,1, badfiles,1,
+ usrlocal,1,md5,1,title,1);
+ }elsif($ARGV[$i]=~/^--snapshot/){
+ if($ARGV[$i+1] ne /--|-/){$CONFIG{snapshot_name}=$ARGV[++$i];}}
+ elsif($ARGV[$i]=~/^--output/){
+ if($ARGV[$i+1] ne /--|-/){%CONFIG=(%CONFIG,"output_dir",$ARGV[++$i]);}}
+ elsif($ARGV[$i]=~/^--system/){%CONFIG=(%CONFIG,"system",1);}
+ elsif($ARGV[$i]=~/^--grimoires/){%CONFIG=(%CONFIG,"grimoires",1);}
+ elsif($ARGV[$i]=~/^--hls/){%CONFIG=(%CONFIG,"hls",1);}
+ elsif($ARGV[$i]=~/^--coord/){%CONFIG=(%CONFIG,"coord",1);}
+ elsif($ARGV[$i]=~/^--analysis/){ %CONFIG=(%CONFIG,"analysis",1); }
+ elsif($ARGV[$i]=~/^--activity/){%CONFIG=(%CONFIG,"activity",1);}
+ elsif($ARGV[$i]=~/^--lists/){%CONFIG=(%CONFIG,"lists",1);}
+ elsif($ARGV[$i]=~/^--compile/){
+ %CONFIG=(%CONFIG,"compile",1);
+ if($ARGV[$i+1] ne /--|-/){%CONFIG=(%CONFIG,"compile_lines",$ARGV[$i+1]);++$i;}}
+ elsif($ARGV[$i]=~/^--installed/){%CONFIG=(%CONFIG,"installed",1);}
+ elsif($ARGV[$i]=~/^--badfiles/){%CONFIG=(%CONFIG,"badfiles",1);}
+ elsif($ARGV[$i]=~/^--md5/){%CONFIG=(%CONFIG,"md5",1);}
+ elsif($ARGV[$i]=~/^--usrlocal/){%CONFIG=(%CONFIG,"usrlocal",1);}
+ elsif($ARGV[$i]=~/^--depends/){%CONFIG=(%CONFIG,"depends",1);}
+ elsif($ARGV[$i]=~/^--bzip2/){%CONFIG=(%CONFIG,"bzip2",1);}
+ elsif($ARGV[$i]=~/^--help/){%CONFIG=(%CONFIG,"help",1);}
+ }
+
+ return %CONFIG;
+}
+
+
+# this code was a crutch and it sucks if i dont need it
+# in the next few weeks (today is 4/1), then its going away
+sub seer_trim_options{
+ #trim config to a number if its 'full'
+ if($CONFIG{config} and ($OPTIONS{config_lines} eq "full")){
+ if($CONFIG{compile} and $CONFIG{compile_lines} ne "full"){
+ $CONFIG{config_lines}=$CONFIG{compile_lines};
+ }else{
+ $CONFIG{config_lines}=160;
+ }
+ #trim compile if its 'full'
+ }elsif($CONFIG{compile} and $CONFIG{compile_lines} eq "full"){
+ $CONFIG{compile_lines}=160;
+ #trim config to less than compile if its bigger, or trim compile
+ }elsif($CONFIG{compile} and $CONFIG{compile_lines}>0){
+ if($CONFIG{config} and $CONFIG{compile_lines}<$CONFIG{config_lines}){
+ if($CONFIG{config_lines}>40){
+ $CONFIG{config_lines}/=2;
+ }else{
+ $CONFIG{config}=0
+ }
+ }else{
+ if($CONFIG{compile_lines}>40){
+ $CONFIG{compile_lines}/=2;
+ }else{
+ $CONFIG{compile}=0;
+ }
+ }
+ }elsif($CONFIG{config} and $CONFIG{config_lines}>0){
+ if($CONFIG{config_lines}>40){
+ $CONFIG{config_lines}/=2;
+ }else{
+ $CONFIG{config}=0
+ }
+ }elsif($CONFIG{config}){ $CONFIG{config}=0;
+ }elsif($CONFIG{compile}){ $CONFIG{compile}=0;
+ #if we get this far we are in serious trouble
+ #basically the number of compile/config logs is so huge
+ #that we cant make a report, so basically we need to just turn things off
+ }elsif($CONFIG{depends}){ $CONFIG{depends}=0;
+ }elsif($CONFIG{grimoires}){ $CONFIG{grimoires}=0;
+ }elsif($CONFIG{"system"}){ $CONFIG{"system"}=0;
+ }elsif($CONFIG{md5}){ $CONFIG{md5}=0;
+ }elsif($CONFIG{badfiles}){ $CONFIG{badfiles}=0;
+ }elsif($CONFIG{usrlocal}){ $CONFIG{usrlocal}=0;
+ }elsif($CONFIG{installed}){ $CONFIG{installed}=0;
+ }elsif($CONFIG{coord}){ $CONFIG{coord}=0;
+ }elsif($CONFIG{lists}){ $CONFIG{lists}=0;
+ }elsif($CONFIG{analysis}){ $CONFIG{analysis}=0;
+ }else{ $CONFIG{max_size}="ignore"; }
+ #all that remains is the hls, which is a maximum of 4 lines...
+ #that _cant_ be too big...
+}
+
+#all code related to trimmming options goes here
+#this function is also meant to be recalled repeatedly if necessary
+#so it has to work in all cases
+sub seer_trim{
+print "TRIMING\n";
+
+ my $max_val = -1;
+ my $max_name = "compile";
+ map {
+ print "$_ $LINES{$_}\n";
+ if ($LINES{$_} > $max_val) {
+ $max_val=$LINES{$_};
+ $max_name=$_;
+ }
+ $LINES{$key}=0;
+ } keys %LINES;
+ print "The biggest item was $max_name at $max_val\n";
+
+ if($max_name eq compile) {
+ if ( $CONFIG{compile_lines} eq full) {
+ $CONFIG{compile_lines} = 160;
+ } elsif ( $CONFIG{compile_lines} > 30 ) {
+ $CONFIG{compile_lines} = int ($CONFIG{compile_lines} * 0.9);
+ } else {
+ $CONFIG{compile} = 0;
+ }
+ print "Trimming compile size to $CONFIG{compile_lines}\n";
+ } elsif ( $max_name eq hdr) {
+ die "the report is somehow too big when nothing is left but a header";
+ } else {
+ print "Turning off $max_name";
+ $CONFIG{$max_name} = 0;
+ }
+}
+
+##############################################
+sub seer_read{
+ if($CONFIG{lists}){ seer_read_list(); }
+ if($CONFIG{activity}){ seer_read_activity(); }
+ if($CONFIG{depends} or $CONFIG{analysis}){seer_read_depends();}
+ if($CONFIG{installed} or $CONFIG{analysis}){seer_read_installed();}
+ if($CONFIG{badfiles}){ seer_read_badfiles(); }
+ if($CONFIG{md5}){ seer_read_md5(); }
+ if($CONFIG{usrlocal}){ seer_read_usrlocal(); }
+ return;
+}
+
+##############################################
+sub seer_write{
+ open FILE, ">$CONFIG{output_file}" or die "Error: Can't open $CONFIG{output_file}\n";
+ $count = 0;
+ cprint (FILE,"PROMETHEUS REPORT\n\n");
+ $LINES{hdr} = $count; $count = 0;
+ seer_write_hls() if($CONFIG{hls});
+ $LINES{hls} = $count; $count = 0;
+ seer_write_coord() if($CONFIG{coord});
+ $LINES{coord} = $count; $count = 0;
+ seer_write_lists() if($CONFIG{lists});
+ $LINES{lists} = $count; $count = 0;
+ seer_write_analysis() if($CONFIG{analysis});
+ $LINES{analysis} = $count; $count = 0;
+ seer_write_compile() if($CONFIG{compile});
+print "COMPILE COUNT IS $count\n";
+ $LINES{compile} = $count; $count = 0;
+ seer_write_badfiles() if($CONFIG{badfiles});
+ $LINES{badfiles} = $count; $count = 0;
+ seer_write_md5() if($CONFIG{md5});
+ $LINES{md5} = $count; $count = 0;
+ seer_write_usrlocal() if($CONFIG{usrlocal});
+ $LINES{usrlocal} = $count; $count = 0;
+ seer_write_system() if($CONFIG{system});
+ $LINES{system} = $count; $count = 0;
+ seer_write_activity() if($CONFIG{activity});
+ $LINES{activity} = $count; $count = 0;
+ seer_write_grimoire() if($CONFIG{grimoire});
+ $LINES{grimoire} = $count; $count = 0;
+ seer_write_depends() if($CONFIG{depends});
+ $LINES{depends} = $count; $count = 0;
+ seer_write_installed() if($CONFIG{installed});
+ $LINES{installed} = $count; $count = 0;
+ close FILE;
+}
+
+########################################
+#################
+#seer_read stuff#
+#################
+
+sub seer_read_list{
+ open FILE, "<$CONFIG{current_list}";
+ @CURRENT_LIST=<FILE>;
+ close FILE;
+ open FILE, "<$CONFIG{success_list}";
+ @SUCCESS_LIST=<FILE>;
+ close FILE;
+ open FILE, "<$CONFIG{failure_list}";
+ @FAILURE_LIST=<FILE>;
+ close FILE;
+ $ERROR{cast}=@FAILURE_LIST if (@FAILURE_LIST);
+}
+
+
+sub seer_read_activity{
+ open FILE, "<$CONFIG{activity_file}";
+ while (<FILE>){
+ ($date,$task,$spell,$version,$result,$info)=(/\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.*)/);
+ my @foo=($result,$task,$spell,$version,$info);
+ push @ACTIVITY,\@foo;
+ }
+ close FILE;
+}
+
+sub seer_read_depends{
+ open FILE, "<$CONFIG{depends_file}";
+ @depends=<FILE>;
+ close FILE;
+}
+sub seer_read_installed{
+ open FILE, "<$CONFIG{installed_file}";
+ @installed=<FILE>;
+ close FILE;
+}
+
+
+#since its of no use atm to waste memory sucking this in, im just going
+#to set some flags and refer to them later
+#XXXX
+sub seer_read_badfiles{
+ if( -s $CONFIG{new_list}){
+ open FILE, "<$CONFIG{new_list}";
+ @new_files=sort <FILE>;
+ $ERROR{new_files}=@new_files;
+ close FILE
+ }
+ if( -s $CONFIG{old_list}){
+ open FILE, "$CONFIG{old_list}";
+ @old_files=sort <FILE>;
+ $ERROR{old_files}=@old_files;
+ close FILE
+ }
+ if( -s $CONFIG{diff_list}){
+ open FILE, "$CONFIG{diff_list}";
+ @diff_files=sort <FILE>;
+ $ERROR{diff_files}=@diff_files;
+ close FILE
+ }
+}
+
+sub seer_read_usrlocal{
+ if( -s $CONFIG{usrlocal_list}){
+ open FILE, "$CONFIG{usrlocal_list}";
+ @usrlocal_files=<FILE>;
+ $ERROR{usrlocal}=@usrlocal_files;
+ close FILE
+ }
+}
+
+
+sub seer_read_md5{
+ open FILE, "<$CONFIG{pm_automd5}";
+ @md5probs=<FILE>;
+ foreach $bar (@md5probs){
+ if($bar=~/.*:.*:(missing)|(different):*/){
+ $ERROR{md5}++;
+ }
+ }
+ close FILE;
+}
+
+##################
+#seer_write stuff#
+##################
+sub seer_write_title{
+ my $hostname=`hostname`;
+ chomp $hostname;
+
+ #Theres more than one way to do it.
+ #and I'll be damned if Im going to set and then check a variable
+ #when a goto will suffice, besides I like control flow to hold conditions
+ for($i=0;$i<@ACTIVITY;$i++){
+ ($result,$task,$spell,$version,$info)=@{$ACTIVITY[$i]};
+ if ($task =~ /summon/ and $result =~ /fail/) {
+ print FILE "summon failure $spell";
+ goto end;
+ }
+ elsif ($task =~ /cast/ and $result =~ /fail/) {
+ print FILE "cast failure $spell";
+ goto end;
+ }
+ }
+ $spell=$CURRENT_LIST[0];
+ chomp $spell;
+ foreach $key (sort keys %ERROR) {
+ if($key eq "md5") {
+ print FILE "$ERROR{$key} md5sum problems"; goto end;
+ } elsif($key eq "new_files"){
+ print FILE "$ERROR{$key} file(s) left behind from $spell"; goto end;
+ } elsif($key eq "old_files"){
+ print FILE "$ERROR{$key} file(s) removed from $spell"; goto end;
+ } elsif($key eq "diff_files"){
+ print FILE "$ERROR{$key} file(s) changed from $spell"; goto end;
+ } elsif($key eq "usrlocal"){
+ print FILE "$ERROR{$_} file(s) in /usr/local from $spell"; goto end;
+ }
+ }
+ end:
+ print FILE ":$hostname:$CONFIG{snapshot_name}\n";
+}
+
+sub seer_write_hls{
+ cprint (FILE, "HIGH LEVEL SUMMARY:\n");
+ if( defined %ERROR ){
+ map{
+ if($_ eq "cast"){
+ $foo=@FAILURE_LIST;
+ cprint (FILE, " At least $foo cast(s) failed.\n");
+ }
+ cprint (FILE, " At least $ERROR{$_} md5sum problems occured.\n") if($_ eq "md5");
+ cprint (FILE, " At least $ERROR{$_} file(s) left behind after the dispel.\n") if($_ eq "new_files");
+ cprint (FILE, " At least $ERROR{$_} file(s) removed after a dispel when it shouldnt have.\n") if($_ eq "old_files");
+ cprint (FILE, " At least $ERROR{$_} file(s) left changed after the dispel.\n") if($_ eq "diff_files");
+ cprint (FILE, " At least $ERROR{$_} file(s) placed in /usr/local after a cast.\n") if($_ eq "usrlocal");
+ } sort keys %ERROR
+ }else{
+ cprint (FILE, "I found no problems.\n");
+ }
+ cprint (FILE, "\n\n");
+
+}
+
+sub seer_write_coord{
+ cprint (FILE,"PROMETHEUS COORDINATES:\n");
+ cprint (FILE,`hostname`);
+ cprint (FILE,"$CONFIG{snapshot_name}\n\n");
+}
+
+sub seer_write_analysis{
+ if($CONFIG{analysis}){
+ %DONE=();
+ cprint (FILE,"SUMMARY:\n");
+ for($i=0;$i<@ACTIVITY;$i++){
+ ($result,$task,$spell,$version,$info)=@{$ACTIVITY[$i]};
+ if (($task =~ /cast/) and ($DONE{"$spell $version"} ne 1)){
+ %DONE=(%DONE,"$spell $version",1);
+ cprint (FILE,"$result $spell-$version $info\n");
+ for($j=0;$j<@depends;$j++){
+ if($depends[$j]=~/^\Q$spell:/){
+ ($depends_name,$state,$depend_type)=($depends[$j]=~/\s*\w+:(\S+):(\S+):(\w+).*/);
+ $installed_spell="not";
+ for($k=0;$k<@installed;$k++){
+ ($name)=($installed[$k]=~/(\S+):\d+:\w+/);
+ if($depends_name eq $name){
+ ($installed_spell,$date,$installed_state,$installed_version)=($installed[$k]=~/\s*(\S+):(\d+):(\w+):(\S+).*/);
+ }
+ }
+ if($installed_spell eq "not"){
+ cprint (FILE," $depend_type($state): $depends_name [not installed]\n");
+ }else{
+ cprint (FILE," $depend_type($state): $depends_name [$installed_spell-$installed_version $installed_state($date)]\n");
+ }
+ }
+ }
+ }
+ }
+ cprint (FILE,"\n\n");
+ }
+}
+
+sub seer_write_system{
+ if($CONFIG{system}){
+ cprint (FILE,"SYSTEM SETTINGS:\n");
+ map {
+ if(/SORCERY_BRANCH|ARCHITECT|OPTIM/){
+ cprint (FILE," $_=$SYSTEM{$_}\n");}
+ } keys %SYSTEM;
+ cprint (FILE,"\n");
+ cprint (FILE,"LOCAL SETTINGS:\n");
+ map{
+ if((/KEEP_SNAP|GRIMOIRE|NUM_CAST|MACHTYPE|DO_DEFAULTS/)){
+ cprint (FILE," $_ = $PMCONFIG{$_}\n");
+ }
+ }keys %PMCONFIG;
+ cprint (FILE,"\n\n");
+ }
+}
+
+sub seer_write_grimoire{
+ if($CONFIG{grimoire}){
+ cprint (FILE,"GRIMOIRES:\n");
+ map{ cprint (FILE, " $_\n"); } @grimoires;
+ cprint (FILE, "\n\n");
+ }
+}
+
+sub seer_write_activity{
+ if($CONFIG{activity} or $CONFIG{summary}){
+ cprint (FILE,"ACTIVITY LOG:\n");
+ map{ cprint (FILE," @$_\n"); }@ACTIVITY;
+ cprint (FILE, "\n\n");
+ }
+}
+
+sub seer_write_lists{
+ if($CONFIG{lists} or $CONFIG{summary}){
+ cprint (FILE,"CURRENT LIST:\n");
+ map { cprint (FILE," $_"); } @CURRENT_LIST;
+ cprint (FILE,"\n\n");
+ cprint (FILE,"FAILURE LIST:\n");
+ map { cprint (FILE," $_"); } @FAILURE_LIST;
+ cprint (FILE,"\n\n");
+ cprint (FILE,"SUCCESS LIST:\n");
+ map { cprint (FILE," $_"); } @SUCCESS_LIST;
+ cprint (FILE,"\n\n");
+ }
+}
+
+sub seer_write_compile{
+ if($CONFIG{compile}){
+ cprint (FILE,"COMPILE LOGS: \n");
+ @files=glob ("$CONFIG{compile_logs}/*/compile/*bz2");
+ foreach $file (@files){
+ @compile_log = `bzcat $file`;
+ ($file)=($file=~/\S*\/(\S+)$/); #wtf
+ $file_length=@compile_log;
+ if($CONFIG{compile_lines}=~/full/){
+ $start=0;
+ } else{
+ $start=$file_length-$CONFIG{compile_lines};
+ if ($start<0){$start=0;};
+ }
+ cprint (FILE,"\nCOMPILE LOG: $file [Lines: $start-$file_length]\n\n");
+
+ for($i=$start;$i<=$file_length;$i++){
+ cprint (FILE,"$compile_log[$i]");
+ }
+ cprint (FILE,"\n");
+ }
+ cprint (FILE,"\n");
+ }
+}
+
+
+sub seer_write_installed{
+ if($CONFIG{installed}){
+ cprint (FILE,"INSTALLED SPELLS:\n");
+ @installed=sort(@installed);
+ map { cprint (FILE," $_"); } @installed;
+ cprint (FILE,"\n\n");
+ }
+}
+
+sub seer_write_depends{
+ if($CONFIG{depends}){
+ my %SPELLHASH;
+ map{
+ chomp;
+ $SPELLHASH{$_}=1;
+ } (@SUCCESS_LIST,@FAILURE_LIST);
+ cprint (FILE,"DEPENDENCIES:\n");
+ map{
+ ($spell,$depend,$value,$type,$option_on,$option_off)=split ':';
+ if($SPELLHASH{$spell}==1 or $SPELLHASH{$depend}==1){
+ cprint (FILE,$_);
+ }
+ }sort @depends;
+ cprint (FILE,"\n\n");
+ }
+}
+
+sub seer_write_badfiles{
+ if($ERROR{new_files}){
+ cprint (FILE,"FILES OF UNKNOWN ORIGIN:\n");
+ cprint (FILE,"@new_files");
+ cprint (FILE,"\n");
+ }
+ if($ERROR{old_files}){
+ cprint (FILE,"\nMISSING FILES:\n");
+ cprint (FILE,"@old_files");
+ cprint ("\n");
+ }
+ if($ERROR{diff_files}){
+ cprint (FILE,"\nCHANGED FILES:\n");
+ cprint (FILE,"@diff_files");
+ cprint (FILE,"\n");
+ }
+}
+
+sub seer_write_md5{
+ if(defined @md5probs){
+ cprint (FILE,"PROBLEMS WITH MD5SUMS\n");
+ cprint (FILE,"@md5probs");
+ cprint (FILE,"\n\n");
+ }
+}
+
+sub seer_write_usrlocal{
+ if($CONFIG{usrlocal} and $ERROR{usrlocal}){
+ cprint (FILE,"/USR/LOCAL FILES:\n");
+ cprint (FILE ,"@usrlocal_files");
+ cprint (FILE,"\n\n");
+ }
+}
+
+
+sub cprint{
+ ($FH,$out)=@_;
+ $retval=print $FH $out;
+ $count+=length $out;
+ return $retval;
+}
diff --git a/usr/sbin/pm-setup b/usr/sbin/pm-setup
new file mode 100755
index 0000000..f775f3b
--- /dev/null
+++ b/usr/sbin/pm-setup
@@ -0,0 +1,74 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+
+if [ -e /etc/pm-config ];then
+ source /etc/pm-config
+else
+ echo "no config file! incomplete install? FIXME"
+ exit 1
+fi
+echo ""
+echo "I am going to take an image of your system,"
+echo "please make sure you're system is setup how you want it"
+echo "because after this I will use it as a reference point and revert"
+echo "back to it as best as I can (and im very good at it)."
+echo ""
+echo "You can re-run this process later if you really need to change something"
+echo ""
+yn='n'
+read -p "Are you sure everything is the way you want it? y/n" yn
+if [[ $yn == 'y' ]]; then
+ echo Making the directories
+ mkdir -p $PMDIR $SNAP_DIR $PMDIR/image $LIST_DIR 2>/dev/null
+ mkdir -p $SHARE_DIR
+ mkdir -p $UNCAST_DIR
+ touch $UNCAST
+ touch $WINNER
+ touch $KNOWN
+ touch $PROBLEMS
+ touch $IGNORE
+ touch $PMDIR/state
+
+ echo Building an md5sum image of all the important files on your system
+ pm-build-md5image / | tee $PMDIR/md5sum_image
+ echo Backing up all those important files
+ echo I will do my best to keep the system looking like this, make sure
+ echo all your config files are the way you want them!
+ cut -f3 -d' ' $PMDIR/md5sum_image|sed 's/ /\\ /'|cpio -p -dmVau $PMDIR/image
+#LOCK
+ echo Making a list of spells to ignore, feel free to edit
+ echo "$IGNORE"
+ echo Under no circumstances will anything in this list be cast
+ echo or dispelled either excplicitly or implicitly
+ cp $IGNORE /tmp/ignore.lst
+ cat /var/lib/sorcery/sustained >> /tmp/ignore.lst
+ cut -f1 -d: /var/state/sorcery/packages >> /tmp/ignore.lst
+ grep kernel $GRIMOIRE_PATH/$CODEX_CACHE|cut -f1 -d' ' >> /tmp/ignore.lst
+ sort /tmp/ignore.lst|uniq > $IGNORE
+ touch $FPROTECT
+ sort /usr/share/prometheus/fprotect.lst $FPROTECT|uniq > /tmp/fp.lst
+ mv /tmp/fp.lst $FPROTECT
+#UNLOCK
+
+else
+ echo Please run pm-setup when you are actually ready
+fi
+
diff --git a/usr/sbin/pm-test b/usr/sbin/pm-test
new file mode 100755
index 0000000..ce571e1
--- /dev/null
+++ b/usr/sbin/pm-test
@@ -0,0 +1,227 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+source /etc/pm-config
+
+#initially do an update (as no recent cast failed)
+#FIXME (optionalize this)
+SKIP_UPDATE=0
+
+usage(){
+cat << EOF
+This is the front end to prometheus
+it will in one of several modes:
+
+once run through a cast/dispel cycle once, then exit
+
+once spell cast spell, useful for debugging, or testing just
+ one particular spell
+
+forever run through cast/dispel cycles indefinitly or
+ until the stop file appears in the $PMDIR/stop
+
+grimoire this is exactly like forever except it stops
+ once it has picked everything in the grimoire
+
+until time this will continue running until <time> in the future
+ time must be something parsable by the date program
+
+section name this will cast everything in a section, or at least
+ attempt to. (if a dependancy breaks and multiple spells
+ depend on it, none of them will get cast).
+EOF
+}
+
+check_val() {
+ if [[ $1 != 0 ]] ; then
+ echo "we have a problem,"
+ echo "please remember what messages you have recieved"
+ echo "and email astitt@sourcemage.org a bug report"
+ exit 1
+ fi
+}
+
+#here we expect to be in whatever environment is necessary for this to work
+#be it chroot, native or uml, assume all the normal stuff should work
+#we also expect that the list that our first argument is a non-empty list
+#of spells to cast
+#that list will be adjusted by the end of this function
+cast_dispel_cycle(){
+ #re-source to update dynamically
+ local UNCAST=$1
+ shift
+ source /etc/pm-config
+ if [[ $CLEAR_SPOOL == "yes" ]]; then
+ echo "Cleaning /var/spool/sorcery/* for you"
+ rm -f /var/spool/sorcery/* 2>/dev/null
+ fi
+ if [ ! -e $PMDIR/md5sum_image ]; then
+ echo "Uhm...where is your md5sum_image?"
+ echo "oh well I'll make one for you anyways, hang on."
+ pm-build-md5image / > $PMDIR/md5sum_image
+ check_val $?
+ echo "got that taken care of..."
+ fi
+
+ currtime=$(date +%Y%m%d%H%M)
+ echo $currtime > $PMDIR/currtime
+
+ #truncate activity log
+ if [ -e /var/log/sorcery/activity ]; then
+ cat /var/log/sorcery/activity >> /var/log/sorcery/activity.old
+ rm /var/log/sorcery/activity
+ touch /var/log/sorcery/activity
+ fi
+ if [[ $DO_UPDATES == "yes" && $SKIP_UPDATE != 1 ]]; then
+ echo "running pm-update"
+ echo "this will download new stuff"
+ #it also ensures
+ pm-update
+ check_val $?
+ else
+ echo "skipping update"
+ fi
+ echo "running pm-pre-cast"
+ pm-pre-cast
+ check_val $?
+ rm -rf /tmp/pm-autocast-quit
+ echo "running pm-post-cast"
+ echo pm-test-cast -s $UNCAST $*
+ pm-test-cast -s $UNCAST $*
+ check_val $?
+ if [[ -e /tmp/pm-autocast-quit && ! -s /tmp/pm-automd5.lst ]];then
+ echo "pm-autocast quit early"
+ echo "I think I'll stop now"
+ SKIP_UPDATE=1
+ return
+ fi
+ echo "okay got that out of the way"
+ echo "now to make the first half of the snapshot"
+ pm-post-cast
+ check_val $?
+ echo "all right, lets restore the system back to the initial state"
+ echo "and make the second half of the snapshot"
+ echo "running pm-restore, hold on!"
+ pm-restore
+ check_val $?
+ SKIP_UPDATE=0
+}
+
+#this actually has a way out
+#just make the $PMDIR/stop file exist
+cycle_forever(){
+ while [ ! -e $PMDIR/stop ]; do
+ if [[ ! -s $UNCAST ]]; then
+ #reload
+ cat $KNOWN > $UNCAST
+ fi
+ #FIXME (find section?)
+ cast_dispel_cycle $UNCAST
+ [[ $RUNTIME_REPORT == 'yes' ]] && pm-report-all
+ done
+ rm -f $PMDIR/stop
+}
+
+cycle_until(){
+ stop_time=$(date -d "$*" +%Y%m%d%H%M) || exit 1
+ until [[ $stop_time < $(date +%Y%m%d%H%M) || -e $PMDIR/stop ]]; do
+ if [[ ! -s $UNCAST ]]; then
+ #reload
+ cat $KNOWN > $UNCAST
+ fi
+ cast_dispel_cycle $UNCAST
+ [[ $RUNTIME_REPORT == 'yes' ]] && pm-report-all
+ done
+ rm -f $PMDIR/stop
+}
+
+#this runs through whatever UNCAST is set to until its empty
+#the list must also be non-empty (or else it will just exit
+do_list(){
+ while [ ! -e $PMDIR/stop ]; do
+ if [[ ! -s $UNCAST ]]; then
+ echo "$UNCAST is empty, thats all Im going to do for you"
+ break
+ fi
+ cast_dispel_cycle $UNCAST
+ [[ $RUNTIME_REPORT == 'yes' ]] && pm-report-all
+ done
+ rm -f $PMDIR/stop
+}
+
+#check for a section list, if there is one, make it
+#set the bugzilla component
+do_section(){
+ local section=$1
+ local component=$2
+ #adjust some globals
+ UNCAST=$UNCAST_DIR/$section
+
+ if [[ ! -s $UNCAST ]] ; then
+ echo "the $section uncast list appears to be empty, rebuilding..."
+ grep $GRIMOIRE_PATH/$section $GRIMOIRE_PATH/$CODEX_CACHE | cut -f1 -d' ' > $UNCAST
+ fi
+ echo "There are $(wc -l < $UNCAST) items to be cast"
+ do_list
+}
+
+check_lock(){
+ if [[ -e /var/run/pm.lock ]]; then
+ echo "it looks like im already running"
+ yn='n'
+ read -p "Do you really want to keep going?" yn
+ if [ $yn != 'y' ];then
+ exit 1
+ fi
+ else
+ touch /var/run/pm.lock
+ fi
+}
+
+#how are we running?
+case $1 in
+ once) check_lock
+ shift
+ cast_dispel_cycle $UNCAST $*
+ ;;
+#these next two will automagically update the UNCAST list
+#which at this point cannot be
+ forever) check_lock
+ cycle_forever
+ ;;
+ until) check_lock
+ shift
+ cycle_until $*
+ ;;
+ grimoire) check_lock
+ do_list
+ ;;
+ section) check_lock
+ do_section $2
+ ;;
+ list) check_lock
+ UNCAST=$2
+ do_list
+ ;;
+ *) usage
+ exit 1
+ ;;
+esac
+rm /var/run/pm.lock 2>/dev/null
diff --git a/usr/sbin/pm-test-cast b/usr/sbin/pm-test-cast
new file mode 100755
index 0000000..b26f624
--- /dev/null
+++ b/usr/sbin/pm-test-cast
@@ -0,0 +1,94 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+source /etc/pm-config
+
+echo "pm-test-cast:start" > $PMDIR/state
+
+CAST_LIST=""
+
+#heavily borrowed code, thanks sorcery team ;)
+while [ -n "$1" ]; do
+ if echo $1 | grep -q "^-"; then
+ case $1 in
+ -s) export UNCAST=$2 ; shift 2 ;;
+ *) shift 1 ;;
+ esac
+ else
+ CAST_LIST="$CAST_LIST $1"
+ shift
+ fi
+done
+
+#LOCK
+
+#re-generate the excluded list
+#which is anything that is in problems, ignore, or installed
+#winners are okay (otherwise nothing would get done)
+(
+ cut -f1 -d: $PROBLEMS
+ cat $IGNORE $LIST_DIR/installed.lst
+) |sort|uniq > /tmp/excluded.lst
+
+#remove anything excluded from the uncast list, this is mostly an optimization
+#pm-update will put things back on the uncast lists as they come off of
+#the problems list
+#FIXME...
+pm-exclude $UNCAST /tmp/excluded.lst |
+pm-exclude - $WINNER > /tmp/uncast
+mv /tmp/uncast $UNCAST
+
+#this sets currcast.lst to whatever it is we want to cast
+#someday this may be more complex, but not today
+if [[ -z $CAST_LIST ]]; then
+ #empty, take something from the uncast list, whatever that may be
+ pm-grab $UNCAST $NUM_TEST > /tmp/currcast.lst
+else
+ rm -rf /tmp/currcast.lst 2>/dev/null
+ for each in $CAST_LIST; do echo $each >> /tmp/currcast.lst ; done
+fi
+#remove what we are going to cast from the uncast list
+pm-exclude $UNCAST /tmp/currcast.lst > /tmp/uncast
+
+#order is important here, we want the currcast.lst copied before we copy in
+#the truncated $UNCAST, if we do it the other way, we could lose spells
+#this way we could only recast some spells, which is 'okay'
+
+mv /tmp/currcast.lst $LIST_DIR
+mv /tmp/uncast $UNCAST
+
+#UNLOCK
+
+#sanity...
+echo "making sure color is off in sorcery"
+echo "if its on things will break horribly"
+cat /etc/sorcery/local/config|sed 's/color *"on"/color "off"/' > /tmp/config
+mv /tmp/config /etc/sorcery/local/config
+
+#cast the stupid thing
+cat $PMDIR/lists/currcast.lst|
+xargs pm-autocast /tmp/excluded.lst /tmp/cast.log $DO_DEFAULTS
+#xargs pm-autocast /tmp/excluded.lst /tmp/cast.log $DO_DEFAULTS
+if [[ -e /tmp/pm-autocast-quit ]] ; then
+ cat $PMDIR/lists/currcast.lst >> $LIST_DIR/skipped.lst
+fi
+
+rm /tmp/excluded.lst
+echo "pm-test-cast:stop" > $PMDIR/state
diff --git a/usr/sbin/pm-update b/usr/sbin/pm-update
new file mode 100755
index 0000000..4717f14
--- /dev/null
+++ b/usr/sbin/pm-update
@@ -0,0 +1,97 @@
+#!/bin/bash
+##################################################################
+#Copyright (C) 2003 Andrew Stitt astitt@sourcemage.org
+#Copyright (C) 2003 Source Mage GNU/Linux (www.sourcemage.org)
+#
+#This program is free software; you can redistribute it and/or
+#modify it under the terms of the GNU General Public License
+#as published by the Free Software Foundation; either
+#version 2 of the License, or (at your option) any later
+#version.
+#
+#This program is distributed in the hope that it will be useful,
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#GNU General Public License for more details.
+#
+#You should have received a copy of the GNU General Public License
+#along with this program; if not, write to the Free Software
+#Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+##################################################################
+
+
+#todo: make it update all the possible uncast lists
+#from UNCAST_DIR
+
+source /etc/pm-config
+if [ ! -d $PMDIR ]; then
+ rm -rf $PMDIR
+ mkdir -p $PMDIR 2>/dev/null
+fi
+if [ ! -d $LIST_DIR ]; then
+ rm -rf $LIST_DIR
+ mkdir -p $LIST_DIR 2>/dev/null
+fi
+
+#LOCK
+touch $UNCAST
+touch $KNOWN
+
+echo "pm-update:start" > $PMDIR/state
+
+#sorcery update #this may cause problems
+scribe update
+
+#i really dont like the idea of supporting this
+if [[ P4_SYNC == 'yes' && -x $(which p4) ]]; then
+ p4 sync
+fi
+
+if [ ! -e $GRIMOIRE_PATH/$CODEX_CACHE ];then
+ echo "Uhm...where is your codex.index file?"
+ echo "are you testing the wrong grimoire?"
+ exit 1
+fi
+
+
+#LOCK
+echo finding new spells
+cut -f1 -d' ' $GRIMOIRE_PATH/$CODEX_CACHE|sort|uniq |
+pm-exclude - $IGNORE |
+pm-exclude - $KNOWN >/tmp/newspells
+
+echo "append to uncast and known lists"
+sort $KNOWN /tmp/newspells |uniq >/tmp/known.lst
+mv /tmp/known.lst $KNOWN
+
+sort $UNCAST /tmp/newspells |uniq >/tmp/uncast.lst
+mv /tmp/uncast.lst $UNCAST
+
+cp $PROBLEMS $PROBLEMS.backup
+. /etc/sorcery/config
+rm -rf /tmp/problems.lst
+touch /tmp/problems.lst
+for each in $(cat $PROBLEMS); do
+ spell=$(echo $each|cut -f1 -d:)
+ md5=$(echo $each|cut -f2 -d:)
+ echo "checking on $spell"
+ tmp=$(grep "^$spell " -m 1 $GRIMOIRE_PATH/$CODEX_CACHE|cut -f2 -d' ')
+ if [[ $tmp == "" ]];then
+ echo "Spell $spell not found"
+ continue
+ fi
+ SCRIPT_DIRECTORY=$tmp/$spell
+ newmd5=$(tar c $SCRIPT_DIRECTORY 2>/dev/null |md5sum|cut -f1 -d' ')
+ if [[ $md5 == $newmd5 ]]; then
+ echo "$spell hasnt changed yet"
+ echo $spell:$newmd5 >> /tmp/problems.lst
+ else
+ echo "$spell has changed removing from list"
+ echo $spell >> $LIST_DIR/removed.lst
+ fi
+done
+mv /tmp/problems.lst $PROBLEMS
+#UNLOCK
+
+echo "pm-update:stop" > $PMDIR/state
+
diff --git a/usr/share/fprotect.lst b/usr/share/fprotect.lst
new file mode 100644
index 0000000..cfdee14
--- /dev/null
+++ b/usr/share/fprotect.lst
@@ -0,0 +1 @@
+/etc/X11/xdm/Xsession