diff --git a/Files/Makefile b/Files/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..466d472e6738ada771ea8b6f372508b0dd37fbc2
--- /dev/null
+++ b/Files/Makefile
@@ -0,0 +1,6 @@
+default:
+	@true
+
+.PHONY: clean
+clean:
+	@true
diff --git a/Files/auto.master.erb b/Files/auto.master.erb
new file mode 100644
index 0000000000000000000000000000000000000000..703d5bc4d9013cd1103043956dd92eeea8cf1e72
--- /dev/null
+++ b/Files/auto.master.erb
@@ -0,0 +1,3 @@
+/home	yp:auto_home	nosuid,nodev
+/mp	yp:auto_lysator	nosuid,nodev
+/pkg	yp:auto_pkg	nosuid,nodev
diff --git a/Files/common-account b/Files/common-account
new file mode 100644
index 0000000000000000000000000000000000000000..cc9518ad5712d3099f73445309e16dff77e1241c
--- /dev/null
+++ b/Files/common-account
@@ -0,0 +1,10 @@
+#
+# /etc/pam.d/common-account - authorization settings common to all services
+#
+# This file is included from other service-specific PAM config files,
+# and should contain a list of the authorization modules that define
+# the central access policy for use on the system.  The default is to
+# only deny service to users whose accounts are expired in /etc/shadow.
+
+account	sufficient	pam_krb5.so minimum_uid=100
+account	sufficient	pam_unix.so
diff --git a/Files/common-auth b/Files/common-auth
new file mode 100644
index 0000000000000000000000000000000000000000..cc30d3fb63e99f791418c6155288560f38ded396
--- /dev/null
+++ b/Files/common-auth
@@ -0,0 +1,14 @@
+#
+# /etc/pam.d/common-auth - authentication settings common to all services
+#
+# This file is included from other service-specific PAM config files,
+# and should contain a list of the authentication modules that define
+# the central authentication scheme for use on the system
+# (e.g., /etc/shadow, LDAP, Kerberos, etc.).  The default is to use the
+# traditional Unix authentication mechanisms.
+
+auth	required	pam_env.so
+auth	required	pam_group.so
+auth	sufficient	pam_unix.so likeauth nullok
+auth	sufficient	pam_krb5.so use_first_pass minimum_uid=100
+auth	required	pam_deny.so
diff --git a/Files/common-password b/Files/common-password
new file mode 100644
index 0000000000000000000000000000000000000000..63ded773882df4a14259cd6cf98de5863f74b792
--- /dev/null
+++ b/Files/common-password
@@ -0,0 +1,10 @@
+#
+# /etc/pam.d/common-password - password-related modules common to all services
+#
+# This file is included from other service-specific PAM config files,
+# and should contain a list of modules that define the services to be
+# used to change user passwords.  The default is pam_unix.
+
+password sufficient	pam_krb5.so minimum_uid=100
+password sufficient	pam_unix.so nullok md5 shadow use_authtok
+password required	pam_deny.so
diff --git a/Files/common-session b/Files/common-session
new file mode 100644
index 0000000000000000000000000000000000000000..e4f1ee341d14a1192398f4f89c1ffee2ad5df859
--- /dev/null
+++ b/Files/common-session
@@ -0,0 +1,11 @@
+#
+# /etc/pam.d/common-session - session-related modules common to all services
+#
+# This file is included from other service-specific PAM config files,
+# and should contain a list of modules that define tasks to be performed
+# at the start and end of sessions of *any* kind (both interactive and
+# non-interactive).
+
+session	required	pam_limits.so
+session	required	pam_krb5.so minimum_uid=100
+session	sufficient	pam_unix.so
diff --git a/Files/cups-printers.conf-client b/Files/cups-printers.conf-client
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/Files/hosts.erb b/Files/hosts.erb
new file mode 100644
index 0000000000000000000000000000000000000000..027267af640f5a51a094096334c6610518b49dbd
--- /dev/null
+++ b/Files/hosts.erb
@@ -0,0 +1,17 @@
+#
+# /etc/hosts: static lookup table for host names
+#
+
+#<ip-address>	<hostname.domain.org>	<hostname>
+127.0.0.1		localhost.localdomain	localhost
+<%= ipaddress %>	<%= fqdn %>	<%= hostname %>	
+2001:6b0:17:f0a0::<%= ipaddress.split('.')[3].to_i().to_s(16) %>	 <%= fqdn %>     <%= hostname %>
+
+130.236.254.1	liunet-gw.lysator.liu.se	liunet-gw
+130.236.254.2	ns-master.lysator.liu.se	ns-master
+130.236.254.4	ns-slave.lysator.liu.se		ns-slave
+130.236.254.25	hoover.lysator.liu.se		hoover loghost
+130.236.254.125	as-master.lysator.liu.se	as-master
+130.236.254.126	as-slave.lysator.liu.se		as-slave
+
+# End of file
diff --git a/Files/interfaces-Debian.erb b/Files/interfaces-Debian.erb
new file mode 100644
index 0000000000000000000000000000000000000000..eb21f4965f874993984ae748479f12e8766e252f
--- /dev/null
+++ b/Files/interfaces-Debian.erb
@@ -0,0 +1,14 @@
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet static
+	address <%= ipaddress %>
+	netmask 255.255.255.0
+	network	130.236.254.0
+	broadcast 130.236.254.255
+	gateway	130.236.254.1
+
+iface eth0 inet6 static
+	address 2001:6b0:17:f0a0::<%= ipaddress.split('.')[3].to_i().to_s(16) %>
+	netmask 64
diff --git a/Files/krb5.conf b/Files/krb5.conf
new file mode 100644
index 0000000000000000000000000000000000000000..4acf5a9164d8341ac17de9bc75daeffa3593e24b
--- /dev/null
+++ b/Files/krb5.conf
@@ -0,0 +1,24 @@
+# $HeadURL: file:///lysator/root/cfengine2/trunk/files/linux/krb5.conf $
+# $Id: krb5.conf 47 2008-09-02 13:48:31Z creideiki $
+#
+# /etc/krb5.conf, configuration file for MIT Kerberos.
+#
+
+[libdefaults]
+        default_realm = LYSATOR.LIU.SE
+
+[realms]
+        LYSATOR.LIU.SE = {
+                kdc = as-master.lysator.liu.se
+                kdc = as-slave.lysator.liu.se
+                admin_server = as-master.lysator.liu.se
+        }
+
+[domain_realm]
+        .lysator.liu.se = LYSATOR.LIU.SE
+
+[appdefaults]
+        kinit = {
+                renewable = true
+                forwardable = true
+        }
diff --git a/Files/nrpe.cfg b/Files/nrpe.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..75e049a93a22d7f01fecc96a44b6d345b8d4add8
--- /dev/null
+++ b/Files/nrpe.cfg
@@ -0,0 +1,28 @@
+# This file is centrally managed.  Do not edit.  Source:
+# svn+ssh://lsvn.lysator.liu.se/svnroot/nagios-plugins/trunk/nagios-plugins
+
+pid_file=/var/run/nrpe.pid
+server_port=5666
+nrpe_user=nagios
+nrpe_group=nagios
+dont_blame_nrpe=0
+debug=0
+command_timeout=60
+connection_timeout=300
+
+# OS-specific commands added by the lysator-nagios-plugins framework.
+include=/opt/nrpe/etc/nrpe-os.cfg
+
+# OS-and-site-specific commands, also from the lysator-nagios-plugins
+# framework.
+include=/opt/nrpe/etc/nrpe-os-site.cfg
+
+# Commands derived automatically from the current host configuration
+# the the lysator-nagios-plugins framework.
+include=/opt/nrpe/etc/nrpe-host-auto.cfg
+
+# Extra commands added manually by the host administrator.  This file
+# will not be overwritten by the next install of
+# lysator-nagios-plugins, so this is the proper place to adde
+# host-specific check commands.
+include=/opt/nrpe/etc/nrpe-host-manual.cfg
diff --git a/Files/nsswitch.conf-limited b/Files/nsswitch.conf-limited
new file mode 100644
index 0000000000000000000000000000000000000000..d53fdfa4d099abe73f409867768496c4c1a9920b
--- /dev/null
+++ b/Files/nsswitch.conf-limited
@@ -0,0 +1,20 @@
+# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
+# `info libc "Name Service Switch"' for information about this file.
+
+passwd:         compat
+group:          compat
+shadow:         compat
+
+hosts:          files dns
+networks:       files
+
+protocols:      db files
+services:       db files
+ethers:         db files
+rpc:            db files
+
+automount:	files nis
+netgroup:       nis
diff --git a/Files/nsswitch.conf-user b/Files/nsswitch.conf-user
new file mode 100644
index 0000000000000000000000000000000000000000..9583c424b96521f8c95706a3037df1e89c11f14f
--- /dev/null
+++ b/Files/nsswitch.conf-user
@@ -0,0 +1,20 @@
+# /etc/nsswitch.conf
+#
+# Example configuration of GNU Name Service Switch functionality.
+# If you have the `glibc-doc-reference' and `info' packages installed, try:
+# `info libc "Name Service Switch"' for information about this file.
+
+passwd:         nis files
+group:          nis files
+shadow:         files
+
+hosts:          files dns
+networks:       files
+
+protocols:      db files
+services:       db files
+ethers:         db files
+rpc:            db files
+
+automount:	files nis
+netgroup:       nis
diff --git a/Files/ntp.conf-client b/Files/ntp.conf-client
new file mode 100644
index 0000000000000000000000000000000000000000..09a6ef85e5d7bf9272e65ec5323e0f1dbdfbabd7
--- /dev/null
+++ b/Files/ntp.conf-client
@@ -0,0 +1,18 @@
+# NOTES:
+#  - you should only have to update the server line below
+#  - if you start getting lines like 'restrict' and 'fudge'
+#    and you didnt add them, AND you run dhcpcd on your
+#    network interfaces, be sure to add '-Y -N' to the
+#    dhcpcd_ethX variables in /etc/conf.d/net
+
+# Name of the servers ntpd should sync with
+# Please respect the access policy as stated by the responsible person.
+#server		ntp.example.tld		iburst
+
+server ntp1.lysator.liu.se iburst
+server ntp2.lysator.liu.se iburst
+server ntp3.lysator.liu.se iburst
+
+# you should not need to modify the following paths
+logfile		/var/log/ntp.log
+driftfile	/var/lib/ntp/ntp.drift
diff --git a/Files/puppet-Makefile.erb b/Files/puppet-Makefile.erb
new file mode 100644
index 0000000000000000000000000000000000000000..d395bf408eb4009e7bdcc5b468f8054bf4735af1
--- /dev/null
+++ b/Files/puppet-Makefile.erb
@@ -0,0 +1,67 @@
+CVSUSER		= bellman
+CVSHOST		= kjell.nsc.liu.se
+CVSDIR		= /net/storage/home/bellman/CvsRoot
+CVSROOT		= :ext:${CVSUSER}@${CVSHOST}:${CVSDIR}
+export CVS_RSH	= ssh
+CVSMODULE	= playpuppet
+
+CONFIGHOME	= /home/bellman/puppet
+PKGTREE		= /sw/packages/extra
+DISTHOSTS	= calle.nsc.liu.se
+PDISTHOSTS	:=$(shell echo ${DISTHOSTS} | tr ' ' ,)
+SKARP		= SKARP
+
+RM_R	= ${RM} -r
+MV	= mv -vi
+COPY_R	= cp -a
+MAKEFLAGS = --no-print-directory
+
+
+default:
+	@echo ""
+	@echo "There is no default target!"  >&2
+	@echo "You might want to do \`\`${MAKE} skarp''"  >&2
+	@echo ""
+	@exit 64
+
+
+skarp: ${SKARP}/CVS
+	cd ${SKARP} && chmod -R +w .
+	[ "x${NOCLEAN}" = "xtrue" ] || { cd ${SKARP} && ${MAKE} clean; }
+	cd ${SKARP} && cvs -q update -C -d .
+	cd ${SKARP} && ${MAKE}
+	cd ${SKARP} && find . -name '.#*.*.*' -exec ${RM} '{}' \;
+	cd ${SKARP} && chmod -R a-w .
+
+xskarp:
+	${MAKE} skarp NOCLEAN=true
+
+%/CVS:
+	@${MAKE} checkout TARGET=$(@D)
+
+checkout:
+	@[ -n "${TARGET}" ] || (echo "Variable TARGET not set" >&2; exit 64)
+	(cvs -q -d"${CVSROOT}" checkout -d "${TARGET}" "${CVSMODULE}")
+
+pdist:
+	@echo
+	@echo -e '\tNOTE!  This does not remove old files from target hosts!'
+	@echo
+	pdsh -w root@${PDISTHOSTS} mkdir -pv "${CONFIGHOME}" #"${PKGTREE}"
+	pdcp -w root@${PDISTHOSTS} -rp "${CONFIGHOME}/" "${CONFIGHOME}"
+	@#pdcp -w root@${PDISTHOSTS} -rp "${PKGTREE}" "${PKGTREE}"
+
+rdist:
+	@for host in ${DISTHOSTS}; do \
+	    echo; echo "-----  $$host  -----"; echo; \
+	    ( \
+		set -x; \
+		rsync -essh -avR --delete --delete-excluded \
+				--exclude CVS/ --exclude src/ \
+			"${CONFIGHOME}"/[A-Z][A-Z0-9]* \
+			"${CFEBINTREE}" \
+			"${PKGTREE}" \
+			"root@$$host:/"; \
+	    ); \
+	    echo; \
+	done
diff --git a/Files/puppetrun-rcinit.erb b/Files/puppetrun-rcinit.erb
new file mode 100644
index 0000000000000000000000000000000000000000..210d9b4eac999a8664987d7a72cc708431f5584e
--- /dev/null
+++ b/Files/puppetrun-rcinit.erb
@@ -0,0 +1,39 @@
+#!/bin/bash
+# chkconfig: 345 65 0
+# description: Run Puppet at boot.
+
+# Check that we're a priviledged user
+[ `id -u` = 0 ] || exit 0
+
+PUPPETDIR='<%= puphome %>'
+
+. /etc/init.d/functions
+
+set -u -e
+
+
+function start()
+{
+    echo "Configuring machine using Puppet from $PUPPETDIR"
+    action "" make -C "$PUPPETDIR" -s install
+}
+
+
+# See how we were called.
+case "$1" in
+    start|restart|reload|force-reload)
+	start
+	;;
+    stop)
+	exit 0
+	;;
+    status)
+	exit 3
+	;;
+    try-restart|condrestart)
+	exit 3
+	;;
+    *)
+	echo $"Usage: $0 {start|stop}"
+	exit 2
+esac
diff --git a/Files/resolv.conf b/Files/resolv.conf
new file mode 100644
index 0000000000000000000000000000000000000000..2441531152a26b15359f2bbd1ae97e024df0f8d2
--- /dev/null
+++ b/Files/resolv.conf
@@ -0,0 +1,3 @@
+nameserver 130.236.254.2
+nameserver 130.236.254.4
+search lysator.liu.se
diff --git a/Files/root-.bash_logout b/Files/root-.bash_logout
new file mode 100644
index 0000000000000000000000000000000000000000..af7c6fd8fd1d77b1081711bd0e497161446cc7d0
--- /dev/null
+++ b/Files/root-.bash_logout
@@ -0,0 +1,2 @@
+# ~/.bash_logout
+
diff --git a/Files/root-.bash_profile b/Files/root-.bash_profile
new file mode 100644
index 0000000000000000000000000000000000000000..a99681bb652c9e00d55d5ad23891baa35a571309
--- /dev/null
+++ b/Files/root-.bash_profile
@@ -0,0 +1,28 @@
+# .bash_profile
+
+stty sane
+
+if [ -r ~/.bashrc ]; then
+    . ~/.bashrc
+fi
+
+# User specific environment and startup programs
+
+case $TERM in
+    linux)
+	case $(tty) in
+	    # RedHat 8 default uses a font without bold, so hold off on bold
+	    /dev/tty[1])	bg=black fg=green  bold=off	;;
+	    /dev/tty[2])	bg=black fg=red	   bold=off	;;
+	    /dev/tty[3])	bg=black fg=blue   bold=off	;;
+	    /dev/tty[4])	bg=black fg=yellow bold=off	;;
+	    /dev/tty[56])	bg=black fg=white  bold=off	;;
+	esac
+	if [ -n "${fg:-}" ]; then
+	    setterm -background $bg -foreground $fg -bold $bold -store
+	fi
+	;;
+esac
+
+export	LESS="-i -X -z-2"
+export	EDITOR="vi"
diff --git a/Files/root-.bashrc b/Files/root-.bashrc
new file mode 100644
index 0000000000000000000000000000000000000000..264cd738d5ec7488708fbc99fdf28e473d08d5f2
--- /dev/null
+++ b/Files/root-.bashrc
@@ -0,0 +1,48 @@
+# .bashrc
+
+# Ugly hack
+if [ "${LOGNAME:-}" != "root" -a -n "${DISPLAY:-}" -a -z "${XAUTHORITY}" ];
+then
+    export XAUTHORITY=`eval "echo ~$LOGNAME"`/.Xauthority
+fi
+
+alias ls='ls -F'
+alias ty=less
+alias psg='ps waux | egrep'
+alias j='jobs -l'
+alias hi=history
+
+alias rm='rm -i'
+alias cp='cp -i'
+alias mv='mv -i'
+
+set +o interactive-comments
+set -o notify
+auto_resume=
+no_exit_on_failed_exec=
+HISTCONTROL=ignoredups; history_control=$HISTCONTROL
+
+PS1='[\u@\h \W]\$ '
+if [ "$TERM" = "linux" ]; then
+    _k='\[\033[00;30m\]'		# blacK
+    _e='\[\033[01;30m\]'		# grEy
+    _r='\[\033[00;31m\]'		# Red
+    _r1='\[\033[01;31m\]'		# Light Red
+    _g='\[\033[00;32m\]'		# Green
+    _g1='\[\033[01;32m\]'		# Light Green
+    _o='\[\033[00;33m\]'		# Orange
+    _y='\[\033[01;33m\]'		# Yellow
+    _b='\[\033[00;34m\]'		# Blue
+    _b1='\[\033[01;34m\]'		# Light Blue
+    _m='\[\033[00;35m\]'		# Magenta
+    _m1='\[\033[01;35m\]'		# Light Magenta
+    _c='\[\033[00;36m\]'		# Cyan
+    _c1='\[\033[01;36m\]'		# Light Cyan
+    _w0='\[\033[00;37m\]'		# Off-white
+    _w='\[\033[01;37m\]'		# White
+    _n='\[\033[00m\]'			# Normal
+    _B='\[\033[01m\]'			# Bold
+
+#    PS1="$_r[$_y\\u$_r@$_y\\h$_r $_w\\W$_r]$_c\\\$$_n "
+    unset _k _e _r _r1 _g _g1 _o _y _b _b1 _m _m1 _c _c1 _w0 _w _n _B
+fi
diff --git a/Files/root-.cvsrc b/Files/root-.cvsrc
new file mode 100644
index 0000000000000000000000000000000000000000..72e713cbe0be4577465449bc4d4de4921df8bc19
--- /dev/null
+++ b/Files/root-.cvsrc
@@ -0,0 +1,4 @@
+cvs -q
+#diff -U5
+update -d -P
+checkout -P
diff --git a/Files/root-.emacs b/Files/root-.emacs
new file mode 100644
index 0000000000000000000000000000000000000000..c36b059fb685184f513ddf2278ebfa2839d70734
--- /dev/null
+++ b/Files/root-.emacs
@@ -0,0 +1,86 @@
+;;;  ==================================================================
+;;;  Mina alldeles egna inst�llningar f�r Emacs
+
+(require 'iso-transl)
+;;(require 'jka-compr)
+(require 'complete)
+(if (>= emacs-major-version 20) 
+    (partial-completion-mode +1)
+  (setq PC-meta-flag t))
+(define-key	minibuffer-local-completion-map
+		"\M-?" 'minibuffer-completion-help)
+(define-key	minibuffer-local-must-match-map
+		"\M-?" 'minibuffer-completion-help)
+(define-key	emacs-lisp-mode-map
+		"\M-\t" 'PC-lisp-complete-symbol)
+(define-key	lisp-interaction-mode-map
+		"\M-\t" 'PC-lisp-complete-symbol)
+
+(load "uniquify" 'noerr 'nomsg)
+(setq uniquify-buffer-name-style 'post-forward-angle-brackets)
+
+
+;;;  ==================================================================
+;;;  Lite diverse saker som jag vill ha inst�llt.
+
+(let ((mode (current-input-mode)))
+  (rplaca (cdr (cdr mode)) '8bit)
+  (apply 'set-input-mode mode))
+(setq next-line-add-newlines nil)
+(setq inhibit-startup-message t)	; Inget dumt startupmeddelande
+(put 'eval-expression 'disabled nil)	; Jo, jag *vill* k�ra elisp
+(put 'narrow-to-region 'disabled nil)	; Klart att jag vet vad jag g�r!
+(put 'narrow-to-page 'disabled nil)
+(setq enable-recursive-minibuffers t)	; Varf�r inte?
+(setq visible-bell nil)
+(setq window-min-height 1)		; Inga dumma begr�nsningar
+(setq backup-by-copying-when-linked t)
+(setq search-slow-speed 2400)
+(setq diff-switches "-u")
+
+(setq Man-notify-method 'friendly)
+
+(menu-bar-mode -1)			; Menyer h�r hemma i restauranger!
+(mapcar (function (lambda (mode)	; Bort med blinkande mark�r och
+		    (if (fboundp mode)  ; eventuella knappar.
+			(apply mode '(-1)))))
+	'(blink-cursor-mode tool-bar-mode))
+
+;;;  Dessutom vill jag se vilken maskin jag k�r p�.  En del moder f�rst�r
+;;;  tyv�rr den informationen.  Info och Dired �r ett par syndare...
+(setq-default mode-line-buffer-identification
+	      (list (concat (if (= (user-uid) 0) "Root (@) " "")
+			    (capitalize ((lambda (x)
+					   (substring x 0
+						      (string-match "\\." x)))
+					 (or (getenv "HOSTNAME")
+ 					     (system-name))))
+			    ": %17b")))
+
+(setq auto-save-list-file-prefix nil)	; Inga .saves-filer, tack.
+
+(setq Info-mode-hook
+       (lambda ()
+	 (define-key Info-mode-map " " 'scroll-up)
+	 (define-key Info-mode-map "\C-?" 'scroll-down)))
+
+
+
+;;;  ==================================================================
+;;;  Lite diverse tangentbindningar
+
+(global-set-key "\C-x\C-o"		; Jag skriver j�mt fel...
+		(lambda (arg) (interactive "p") (other-window (- arg))))
+(global-set-key	"\r"		'newline-and-indent) ; S� h�r ska RET och
+(global-set-key	"\n"		'newline)	     ; LFD sitta...
+(global-set-key "\C-cg"		'goto-line)
+(global-set-key "\C-xv"		'scroll-other-window-down)
+(global-set-key "\M-&"		'query-replace-regexp)
+(global-set-key "\M- "		'fixup-whitespace)
+(global-set-key "\C-x\C-q"	'toggle-read-only)
+(global-set-key "\C-xV"		vc-prefix-map)
+(global-set-key [home]		'beginning-of-line)
+(global-set-key [end]		'end-of-line)
+(global-set-key [insert]	'yank)
+(global-set-key [f18]		'yank)
+(setq mouse-yank-at-point t)
diff --git a/Files/ssmtp.conf.erb b/Files/ssmtp.conf.erb
new file mode 100644
index 0000000000000000000000000000000000000000..2527ac6fd79ee4ccf23d2c92880c9874b3b92704
--- /dev/null
+++ b/Files/ssmtp.conf.erb
@@ -0,0 +1,21 @@
+#
+# Config file for sSMTP sendmail
+#
+# The person who gets all mail for userids < 1000
+# Make this empty to disable rewriting.
+root=root+<%= hostname %>
+
+# The place where the mail goes. The actual machine name is required no 
+# MX records are consulted. Commonly mailhosts are named mail.domain.com
+mailhub=mail
+
+# Where will the mail seem to come from?
+rewriteDomain=lysator.liu.se
+
+# The full hostname
+hostname=<%= fqdn %>
+
+# Are users allowed to set their own From: address?
+# YES - Allow the user to specify their own From: address
+# NO - Use the system generated From: address
+FromLineOverride=YES
diff --git a/Files/syslogd.conf b/Files/syslogd.conf
new file mode 100644
index 0000000000000000000000000000000000000000..44d51ebceb9a229918cf1b3ac80a7f645bbc8e49
--- /dev/null
+++ b/Files/syslogd.conf
@@ -0,0 +1,72 @@
+#  /etc/syslog.conf	Configuration file for syslogd.
+#
+#			For more information see syslog.conf(5)
+#			manpage.
+
+#
+# First some standard logfiles.  Log by facility.
+#
+
+auth,authpriv.*			/var/log/auth.log
+*.*;auth,authpriv.none		-/var/log/syslog
+#cron.*				/var/log/cron.log
+daemon.*			-/var/log/daemon.log
+kern.*				-/var/log/kern.log
+lpr.*				-/var/log/lpr.log
+mail.*				-/var/log/mail.log
+user.*				-/var/log/user.log
+
+#
+# Logging for the mail system.  Split it up so that
+# it is easy to write scripts to parse these files.
+#
+mail.info			-/var/log/mail.info
+mail.warning			-/var/log/mail.warn
+mail.err			/var/log/mail.err
+
+# Logging for INN news system
+#
+news.crit			/var/log/news/news.crit
+news.err			/var/log/news/news.err
+news.notice			-/var/log/news/news.notice
+
+#
+# Some `catch-all' logfiles.
+#
+*.=debug;\
+	auth,authpriv.none;\
+	news.none;mail.none	-/var/log/debug
+*.=info;*.=notice;*.=warning;\
+	auth,authpriv.none;\
+	cron,daemon.none;\
+	mail,news.none		-/var/log/messages
+
+#
+# Emergencies are sent to everybody logged in.
+#
+*.emerg				*
+
+#
+# I like to have messages displayed on the console, but only on a virtual
+# console I usually leave idle.
+#
+#daemon,mail.*;\
+#	news.=crit;news.=err;news.=notice;\
+#	*.=debug;*.=info;\
+#	*.=notice;*.=warning	/dev/tty8
+
+# The named pipe /dev/xconsole is for the `xconsole' utility.  To use it,
+# you must invoke `xconsole' with the `-file' option:
+# 
+#    $ xconsole -file /dev/xconsole [...]
+#
+# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
+#      busy site..
+#
+daemon.*;mail.*;\
+	news.err;\
+	*.=debug;*.=info;\
+	*.=notice;*.=warning	|/dev/xconsole
+
+*.*	@loghost
+
diff --git a/Files/xorg.conf-furfur.erb b/Files/xorg.conf-furfur.erb
new file mode 120000
index 0000000000000000000000000000000000000000..3eb48301ebffb32fd0bf53e77049d543ec02e6e5
--- /dev/null
+++ b/Files/xorg.conf-furfur.erb
@@ -0,0 +1 @@
+xorg.conf-ubuntu.erb
\ No newline at end of file
diff --git a/Files/xorg.conf-ubuntu.erb b/Files/xorg.conf-ubuntu.erb
new file mode 100644
index 0000000000000000000000000000000000000000..07efa5a291ff5b62d6237d03b517b2c68fbbd825
--- /dev/null
+++ b/Files/xorg.conf-ubuntu.erb
@@ -0,0 +1,33 @@
+# xorg.conf (X.Org X Window System server configuration file)
+#
+# This file was generated by dexconf, the Debian X Configuration tool, using
+# values from the debconf database.
+#
+# Edit this file with caution, and see the xorg.conf manual page.
+# (Type "man xorg.conf" at the shell prompt.)
+#
+# This file is automatically updated on xserver-xorg package upgrades *only*
+# if it has not been modified since the last upgrade of the xserver-xorg
+# package.
+#
+# Note that some configuration settings that could be done previously
+# in this file, now are automatically configured by the server and settings
+# here are ignored.
+#
+# If you have edited this file but would like it to be automatically updated
+# again, run the following command:
+#   sudo dpkg-reconfigure -phigh xserver-xorg
+
+Section "Device"
+	Identifier	"Configured Video Device"
+EndSection
+
+Section "Monitor"
+	Identifier	"Configured Monitor"
+EndSection
+
+Section "Screen"
+	Identifier	"Default Screen"
+	Monitor		"Configured Monitor"
+	Device		"Configured Video Device"
+EndSection
diff --git a/Files/yp.conf b/Files/yp.conf
new file mode 100644
index 0000000000000000000000000000000000000000..ccaeb49af0e00157bed29fb737caebbdc401f4ab
--- /dev/null
+++ b/Files/yp.conf
@@ -0,0 +1,2 @@
+domain lysator server ns-master.lysator.liu.se
+domain lysator server ns-slave.lysator.liu.se
diff --git a/Lib/facter/environ.rb b/Lib/facter/environ.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bb188cb72a720befbbc2f130353213bf10f15b4e
--- /dev/null
+++ b/Lib/facter/environ.rb
@@ -0,0 +1,9 @@
+# Note!  The variable names will be turned into lowercase by Puppet...
+
+ENV.each_pair do |var,value|
+    Facter.add("env_"+var) do
+        setcode do
+            value
+        end
+    end
+end
diff --git a/Lib/puppet/parser/functions/regexp_quote.rb b/Lib/puppet/parser/functions/regexp_quote.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5614ef93fd7c65f71c10adbfcf0ba22fe8f05e76
--- /dev/null
+++ b/Lib/puppet/parser/functions/regexp_quote.rb
@@ -0,0 +1,9 @@
+module Puppet::Parser::Functions
+    newfunction(:regexp_quote, :type => :rvalue) do |args|
+	if args.length != 1
+	    self.fail("regexp_quote(): wrong number of arguments" +
+		      " (#{args.length} for 1)")
+	end
+	return Regexp.quote(args[0])
+    end
+end
diff --git a/Lib/puppet/parser/functions/sprintf.rb b/Lib/puppet/parser/functions/sprintf.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0a50761f55c1a47b9da8d073c6b050991c5ce4d5
--- /dev/null
+++ b/Lib/puppet/parser/functions/sprintf.rb
@@ -0,0 +1,9 @@
+module Puppet::Parser::Functions
+    newfunction(:sprintf, :type => :rvalue) do |args|
+	if args.length < 1
+	    self.fail('sprintf(): Too few arguments (missing format string)')
+	end
+	fmt = args.shift()
+	return sprintf(fmt, *args)
+    end
+end
diff --git a/Lib/puppet/parser/functions/strftime.rb b/Lib/puppet/parser/functions/strftime.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f576e5948c28d2c3e9c63b1bc85450455d815a69
--- /dev/null
+++ b/Lib/puppet/parser/functions/strftime.rb
@@ -0,0 +1,12 @@
+module Puppet::Parser::Functions
+    newfunction(:strftime, :type => :rvalue) do |args|
+	if args.length < 1
+	    self.fail("strftime(): Missing format string parameter")
+	end
+	fmt = args.shift()
+	if args.length == 0
+	    return Time.now().strftime(fmt)
+	end
+	self.fail("strftime(): Too many arguments")
+    end
+end
diff --git a/Lib/puppet/type/delete_lines.rb b/Lib/puppet/type/delete_lines.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3b3888c1321b08690a5d35904d20bfc281108bd3
--- /dev/null
+++ b/Lib/puppet/type/delete_lines.rb
@@ -0,0 +1,125 @@
+require 'nsc_utils'
+
+
+Puppet::Type.newtype(:delete_lines) do
+
+    @doc = "Delete lines matching regexp :pattern in the file :file.
+
+	    Parameters are the same as for the regexp_replace_lines type
+	    (except, of course, that there is no :replacement parameter).
+	   "
+
+    newparam(:name) do
+	desc "Name/title"
+    end
+
+    newparam(:file) do
+	desc "The file to edit"
+    end
+
+    newparam(:group_start) do
+	desc "Regexp specifying lines that start groups.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:group_end) do
+	desc "Regexp specifying lines that ends groups.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:start) do
+	desc "Only delete lines after ones matching this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:end) do
+	desc "Stop deleting when reaching a line matching this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:pattern) do
+	desc "Delete lines looking like this regexp (implicitly anchored at
+	      the beginning and end of the line).
+	     "
+
+	validate do |value|
+	    if not value  or  value == ""
+		self.fail "Parameter 'pattern' must not be the empty string"
+	    end
+	end
+    end
+
+    newparam(:skip) do
+	desc "Don't delete lines looking like this regexp (implicitly
+	      anchored at the beginning and end of the line).
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newproperty(:ensure) do
+	desc "
+	     "
+	newvalue(:deleted)
+	defaultto :deleted
+
+	def retrieve
+	    @to_kill = []
+	    @saves = {}
+	    NSC_Utils::on_section_lines(self.resource, @saves) do |l,i|
+		@to_kill.push(i)
+	    end
+
+	    if @to_kill.length > 0
+		patsource = @saves[:pattern].source
+		return self.value == patsource ? false : patsource
+	    else
+		@saves = nil
+		return self.value
+	    end
+	end
+
+	def sync
+	    lines = @saves[:lines]
+	    for i in @to_kill
+		##puts "--DEBUG: #{i+1}: <#{lines[i].chomp}>"
+		lines[i] = nil
+	    end
+	    lines.delete(nil)
+	    fp = File.new(self.resource[:file], 'w')
+	    fp.write(lines.join(""))
+	    fp.close()
+	    @saves = nil
+	    # FIXME: I have no idea what we *should* return here, but at
+	    # least this makes Puppet report that something has been done.
+	    # FIXME: It would be nice if we could convince Puppet to save
+	    # the old version of the file in the filebucket.
+	    return :true
+	end
+    end
+
+end
+
diff --git a/Lib/puppet/type/ensure_line.rb b/Lib/puppet/type/ensure_line.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8b9dcdc1a15b5ed0629f8c61e03f584be64ce64e
--- /dev/null
+++ b/Lib/puppet/type/ensure_line.rb
@@ -0,0 +1,233 @@
+require 'nsc_utils'
+
+
+Puppet::Type.newtype(:ensure_line) do
+
+    @doc = "Make sure the line :line exists in the file :file.
+
+            If it does not already exist, the first line matching the
+            regexp :pattern will be replace by :line.
+
+            If no match against :pattern can be found either, :line is
+            inserted after or before the first line matching :where,
+            depending on if :addhow is set to 'append' (default) or
+            'prepend'.
+
+            If no line matches :where either, or if :where is not set,
+            :line is placed first or last in the file, depending on the
+            value of :addhow.
+
+	    FIXME!
+	    There are more parameters, but I don't have the energy to
+	    document them right now.
+
+            All regexps (:pattern, :sufficient, :start, :end and :where)
+	    are implicitly anchored at the beginning of the line, i.e, as
+	    if they always began with ^.
+
+            NOTE: the parameter names are not very pretty, and may change
+            in the future when I can think of better names.
+	   "
+
+    newparam(:name) do
+	desc "Name/title"
+    end
+
+    newparam(:file) do
+	desc "The file to edit"
+    end
+
+    newparam(:start) do
+	desc "Ensure line exist whithin section(s) beginning with this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:end) do
+	desc "Ensure line exist whithin section(s) ended with this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newproperty(:line) do
+	desc "How the wanted line should look"
+
+	validate do |value|
+	    if value == ""
+		self.fail "Parameter 'line' must not be the empty string"
+	    end
+	end
+
+	def retrieve
+	    resources = self.resource
+	    section_start, include_section_start =
+		NSC_Utils::boundary(resources, :start)
+	    section_end, include_section_end =
+		NSC_Utils::boundary(resources, :end)
+
+	    wanted_line = self.value
+	    searchpattern = self.resource[:pattern]
+	    if searchpattern
+		searchpattern = Regexp.new('^' + self.resource[:pattern])
+	    end
+	    sufficient = self.resource[:sufficient]
+	    if sufficient
+		sufficient = Regexp.new('^' + sufficient)
+	    end
+	    wherepattern = self.resource[:where]
+	    if wherepattern
+		wherepattern = Regexp.new('^' + wherepattern)
+	    end
+	    addhow = self.resource[:addhow]
+
+	    @lines = File.readlines(self.resource[:file])
+	    sections = NSC_Utils::extract_sections(
+		    NSC_Utils::enumerate(@lines),
+		    section_start, true,
+		    section_end, true,
+		    true)
+
+
+	    case self.resource[:section]
+	    when :first
+		sections = sections[0,1]
+	    when :last
+		sections = sections[-1,1] or []
+	    when :all
+		sections = sections
+	    end
+
+	    if @lines.length == 0
+		sections = [["", 0]]
+		include_section_start = include_section_end = false
+	    end
+
+	    @to_replace = []
+	    @to_add = []
+	    for sect in sections
+		found = rpos = ipos = false
+		firstline = sect[0][1]
+		lastline = sect[-1][1]
+		if not include_section_start
+		    sect = sect[1..-1] or []
+		end
+		if not include_section_end
+		    sect = sect[0..-2]
+		end
+		for l,i in sect
+		    l = l.chomp
+		    if l == wanted_line
+			found = i
+		    elsif sufficient and sufficient.match(l)
+			found = i
+		    elsif searchpattern and searchpattern.match(l)
+			rpos = i
+		    elsif not ipos and wherepattern and wherepattern.match(l)
+			ipos = i
+		    end
+		end
+
+		if found
+		    0
+		elsif rpos
+		    @to_replace.push(rpos)
+		elsif ipos
+		    if addhow == :prepend
+			@to_add.push(ipos)
+		    else
+			@to_add.push(ipos + 1)
+		    end
+		elsif addhow == :prepend
+		    @to_add.push(firstline + (include_section_start ? 0 : 1))
+		elsif addhow == :append
+		    @to_add.push(lastline + (include_section_end ? 1 : 0))
+		else
+		    self.fail('Internal error')
+		end
+	    end
+
+	    ##puts "--DEBUG: to_add     = [#{@to_add.join(', ')}]"
+	    ##puts "--DEBUG: to_replace = [#{@to_replace.join(', ')}]"
+	    if @to_replace.length > 0
+		return @lines[@to_replace[0]].chomp
+	    elsif @to_add.length > 0
+		return wanted_line == "" ? false : ""
+	    else
+		@lines = nil
+		return wanted_line
+	    end
+	end
+
+	def sync
+	    line = self.value + "\n"
+	    for i in @to_replace
+		@lines[i] = line
+	    end
+	    for i in @to_add.reverse
+		@lines.insert(i, line)
+	    end
+	    fp = File.new(self.resource[:file], "w")
+	    fp.write(@lines.join(""))
+	    fp.close()
+	    @lines = nil
+
+	    # FIXME: I have no idea what we *should* return here, but at
+	    # least this makes Puppet report that something has been done.
+	    # FIXME: It would be nice if we could convince Puppet to save
+	    # the old version of the file in the filebucket.
+	    return :true
+	end
+    end
+
+    newparam(:pattern) do
+	desc "Replace lines looking like this regexp.
+	      If not set, an exact match against :line is done.
+	     "
+	defaultto false
+    end
+
+    newparam(:sufficient) do
+	desc "Lines matching this regexp are sufficiently close to the target,
+	      and thus the line will be deemed to exist.
+
+	      If not set, an exact match against :line is required.
+	     "
+	defaultto false
+    end
+
+    newparam(:section) do
+	desc "Which section(s) to ensure the line's presence in.
+	      Allowed values are 'first', 'last', and 'all'.  Other
+	      sections are not touched.
+
+	      This parameter is meaningless if :start or :end is not set,
+	      as there will then only be one section.
+	     "
+	newvalues(:first, :last, :all)
+	defaultto :first
+    end
+
+    newparam(:where) do
+	desc "If set, and no existing line matches :pattern, place the new
+	      line after or before (according to :addhow) the first line
+	      matching this regexp.  If not set, or if no line matches
+	      :where, :line is added at the end of the file if :addhow is
+	      append, or at the beginning of the file if :addhow is prepend.
+	     "
+	defaultto false
+    end
+
+    newparam(:addhow) do
+	desc "Whether to append or prepend a new line."
+	newvalues(:append, :prepend)
+	defaultto :append
+    end
+end
diff --git a/Lib/puppet/type/regexp_replace_lines.rb b/Lib/puppet/type/regexp_replace_lines.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4103ee46cf289964199c04372c1334a14b02c860
--- /dev/null
+++ b/Lib/puppet/type/regexp_replace_lines.rb
@@ -0,0 +1,157 @@
+require 'nsc_utils'
+
+
+Puppet::Type.newtype(:regexp_replace_lines) do
+
+    @doc = "Replace lines in the file :file matching regexp :pattern with
+	    :replacement, unless they also match the regexp :skip. The
+	    pattern is implicitly anchored at the start and end of the line.
+	    The replacement text can use the standard Ruby regexp replacement
+	    back-references.
+
+	    Only lines within sections delimited by the patterns :start
+	    and :end are eligible for replacement.  Sections must in turn
+	    lie within \"groups\" that are delimited by the patterns
+	    :group_start and :group_end.  These patterns are anchored at
+	    the start of the line.
+
+	    An open section will be canceled if the surrounding group ends,
+	    and similarly an open group will be canceled if end of file is
+	    reached.  However, if the end patterns are not set, the sections
+	    and groups extend to the end of the group and file, respectively.
+	    Similarly, if the start patterns are not set, sections and
+	    groups begin at the start of the group and file, respectively.
+
+	    By default, the lines that mark the start and end of sections
+	    are not part of the sections themselves, and the lines that
+	    mark the start and end of groups are not part of the groups
+	    themselves.	 However, if the delimiter patterns start with a
+	    plus sign (+), the corresponding lines will be counted as being
+	    part of the sections or groups.
+
+	    Note that the replacement must be idempotent.  It is an error
+	    if the result of the replacement still matches :pattern.
+	   "
+
+    newparam(:name) do
+	desc "Name/title of action."
+    end
+
+    newparam(:file) do
+	desc "The file to edit"
+    end
+
+    newparam(:group_start) do
+	desc "Regexp specifying lines that start groups.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:group_end) do
+	desc "Regexp specifying lines that ends groups.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:start) do
+	desc "Only do replacements on lines after ones matching this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:end) do
+	desc "Stop replacing after reaching a line matching this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:pattern) do
+	desc "Replace lines looking like this regexp (implicitly anchored at
+	      the beginning and end of the line).
+	     "
+
+	validate do |value|
+	    if value == ""
+		self.fail "Parameter 'pattern' must not be the empty string"
+	    end
+	end
+    end
+
+    newparam(:skip) do
+	desc "Don't replace lines looking like this regexp (implicitly
+	      anchored at the beginning and end of the line).
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newproperty(:replacement) do
+	desc "What to replace the lines with.
+	      Standard Ruby backreferences can be used.
+	     "
+
+	def retrieve
+	    @to_replace = []
+	    @saves = {}
+	    NSC_Utils::on_section_lines(self.resource, @saves) do |l,i|
+		@to_replace.push(i)
+	    end
+
+	    if @to_replace.length > 0
+		patsource = @saves[:pattern].source
+		return self.value == patsource ? false : patsource
+	    else
+		@saves = nil
+		return self.value
+	    end
+	end
+
+	def sync
+	    lines = @saves[:lines]
+	    pattern = @saves[:pattern]
+	    skip = @saves[:skip]
+	    replacement = self.value
+	    for i in @to_replace
+		l = lines[i]
+		l2 = l.chomp
+		r = l2.sub(pattern, replacement)
+		##puts "--DEBUG: #{i+1}: <#{l2}> => <#{r}>"
+		if pattern.match(r) and not (skip and skip.match(r))
+		    msg = "Result of replacement still matches on line #{i+1}"
+		    self.fail(msg)
+		end
+		if l[-1,1] == "\n"
+		    r += "\n"
+		end
+		lines[i] = r
+	    end
+	    fp = File.new(self.resource[:file], 'w')
+	    fp.write(lines.join(""))
+	    fp.close()
+	    # FIXME: I have no idea what we *should* return here, but at
+	    # least this makes Puppet report that something has been done.
+	    # FIXME: It would be nice if we could convince Puppet to save
+	    # the old version of the file in the filebucket.
+	    return :true
+	end
+    end
+end
diff --git a/Lib/puppet/type/replace_sections.rb b/Lib/puppet/type/replace_sections.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d94c2d83950474167f96af7274b57fec3570778b
--- /dev/null
+++ b/Lib/puppet/type/replace_sections.rb
@@ -0,0 +1,127 @@
+require 'nsc_utils'
+
+
+Puppet::Type.newtype(:replace_sections) do
+
+    @doc = "Replace sections of lines in the file :file with :replacement.
+	    All the lines of the sections are replaced by the lines in
+	    :replacement; the old and the new section contents does not
+	    have to be the same number of lines.
+
+	    Sections are delimited by lines matching the regexps :start
+	    and :end.  By default, the delimiting lines are not part of
+	    the sections, but if the regexps start with a plus sign (+),
+	    they will be.
+
+	    Sections must be contained within groups that are delimited
+	    by lines matching the regexps :group_start and :group_end.
+	    See the documentation for regexp_replace_lines for further
+	    details about sections and groups.
+
+	    It is up to the caller to ensure that the replacements are
+	    idempotent.
+	   "
+
+    newparam(:name) do
+	desc "Name/title of action."
+    end
+
+    newparam(:file) do
+	desc "The file to edit"
+    end
+
+    newparam(:group_start) do
+	desc "Regexp specifying lines that start groups.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:group_end) do
+	desc "Regexp specifying lines that ends groups.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:start) do
+	desc "Only do replacements on lines after ones matching this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newparam(:end) do
+	desc "Stop replacing after reaching a line matching this regexp.
+	     "
+	defaultto false
+
+	munge do |value|
+	    return (value == "") ? false : value
+	end
+    end
+
+    newproperty(:replacement) do
+	desc "What to replace the lines with.
+	     "
+
+	def retrieve
+	    resources = self.resource
+	    @replacement = self.value
+	    if @replacement[-1,1] != "\n"
+		@replacement = @replacement + "\n"
+	    end
+
+	    @saves = {}
+	    @to_replace = []
+	    NSC_Utils::on_sections(resource, @saves) do |first, sect, last|
+		current = (sect.map {|l,i| l}).join('')
+		if current[-1,1] != "\n"
+		    current += "\n"
+		end
+		if current != @replacement
+		    @to_replace.push([first, last, current])
+		end
+	    end
+
+	    if @to_replace.length > 0
+		return @to_replace[0][2].chomp
+	    else
+		@saves = @replacement = @to_replace = nil
+		return self.value
+	    end
+	end
+
+	def sync
+	    lines = @saves[:lines]
+	    for s,e,old in @to_replace.reverse
+		##puts "--DEBUG: About to replace #{s}..#{e}"
+		if s == 0  and  e == -1
+		    lines.insert(0, @replacement)
+		else
+		    lines[s..e] = @replacement
+		end
+	    end
+
+	    fp = File.new(self.resource[:file], 'w')
+	    fp.write(lines.join(""))
+	    fp.close()
+
+	    @saves = @replacement = @to_replace = nil
+	    # FIXME: I have no idea what we *should* return here, but at
+	    # least this makes Puppet report that something has been done.
+	    # FIXME: It would be nice if we could convince Puppet to save
+	    # the old version of the file in the filebucket.
+	    return :true
+	end
+    end
+end
diff --git a/Lib/ruby/nsc_utils.rb b/Lib/ruby/nsc_utils.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cf5b81b0d9d882cc572bde8287a154d3ef586bf5
--- /dev/null
+++ b/Lib/ruby/nsc_utils.rb
@@ -0,0 +1,185 @@
+module NSC_Utils
+
+    def boundary(resources, name)
+	include_boundary = 0
+	boundary = resources[name]
+	if boundary
+	    if boundary[0,1] == "+"
+		include_boundary = true
+		boundary = boundary[1..boundary.length]
+	    else
+		include_boundary = false
+	    end
+	    boundary = Regexp.new('^' + boundary)
+	end
+	return [boundary, include_boundary]
+    end
+    module_function :boundary
+
+
+    def enumerate(lines)
+	result = []
+	lines.each_with_index do |l,i|
+	    result.push([l,i])
+	end
+	return result
+    end
+    module_function :enumerate
+
+
+    def extract_sections(lines, startp, include_start, endp, include_end,
+			   cancel_unclosed)
+	include_start = (include_start ? 0 : 1)
+	extracted = [[]]
+	inside = startp ? false : -1
+	for l,i in lines
+	    cl = l.chomp
+	    if not inside  and  startp  and  startp.match(cl)
+		inside = i
+		extracted.push([])
+	    end
+	    if inside  and  i >= inside + include_start
+		extracted[-1].push([l,i])
+	    end
+	    if inside  and  endp  and  endp.match(cl)
+		if not include_end
+		    extracted[-1].pop()
+		end
+		inside = false
+	    end
+	end
+	if inside  and  endp  and  cancel_unclosed
+	    extracted.pop()
+	end
+	extracted.delete([])
+	return extracted
+    end
+    module_function :extract_sections
+
+
+    def on_sections(resources, saves)
+	group_start, include_group_start =
+	    NSC_Utils::boundary(resources, :group_start)
+	group_end, include_group_end =
+	    NSC_Utils::boundary(resources, :group_end)
+	section_start, include_section_start =
+	    NSC_Utils::boundary(resources, :start)
+	section_end, include_section_end =
+	    NSC_Utils::boundary(resources, :end)
+
+	saves[:lines] = File.readlines(resources[:file])
+	groups = NSC_Utils::extract_sections(
+		NSC_Utils::enumerate(saves[:lines]),
+		group_start, include_group_start,
+		group_end, include_group_end,
+		true)
+	for grp in groups
+	    sections = NSC_Utils::extract_sections(
+		    grp,
+		    section_start, true,
+		    section_end, true,
+		    true)
+	    for sect in sections
+		firstline = sect[0][1]
+		lastline = sect[-1][1]
+		if not include_section_start
+		    sect = sect[1..-1] or []
+		    firstline += 1
+		end
+		if not include_section_end
+		    sect = sect[0..-2]
+		    lastline -= 1
+		end
+		yield [firstline, sect, lastline]
+	    end
+	end
+    end
+    module_function :on_sections
+
+
+    # FIXME!
+    # This function is supposed to replace the on_section_lines() function
+    # below.  However, it is as of yet untested, so for the time being,
+    # the old implementation is the one we use.
+    def on_section_lines_2(resources, saves)
+	saves[:pattern] = Regexp.new('^' + resources[:pattern] + '$')
+	saves[:skip] = resources[:skip]
+	if saves[:skip]
+	    saves[:skip] = Regexp.new('^' + saves[:skip] + '$')
+	end
+	NSC_Utils::on_sections do |firstline, sect, lastline|
+	    for l,i in sect
+		cl = l.chomp
+		if saves[:pattern].match(cl)
+		    unless saves[:skip]  and  saves[:skip].match(cl)
+			yield [l,i]
+		    end
+		end
+	    end
+	end
+    end
+    module_function :on_section_lines_2
+
+
+    def on_section_lines(resources, saves)
+	group_start, include_group_start =
+	    NSC_Utils::boundary(resources, :group_start)
+	group_end, include_group_end =
+	    NSC_Utils::boundary(resources, :group_end)
+	section_start, include_section_start =
+	    NSC_Utils::boundary(resources, :start)
+	section_end, include_section_end =
+	    NSC_Utils::boundary(resources, :end)
+	saves[:pattern] = Regexp.new('^' + resources[:pattern] + '$')
+	saves[:skip] = resources[:skip]
+	if saves[:skip]
+	    saves[:skip] = Regexp.new('^' + saves[:skip] + '$')
+	end
+
+	saves[:lines] = File.readlines(resources[:file])
+	groups = NSC_Utils::extract_sections(
+		NSC_Utils::enumerate(saves[:lines]),
+		group_start, include_group_start,
+		group_end, include_group_end,
+		true)
+	for grp in groups
+	    sections = NSC_Utils::extract_sections(
+		    grp,
+		    section_start, include_section_start,
+		    section_end, include_section_end,
+		    true)
+	    for sect in sections
+		for l,i in sect
+		    cl = l.chomp
+		    if saves[:pattern].match(cl)
+			unless saves[:skip]  and  saves[:skip].match(cl)
+			    yield [l,i]
+			end
+		    end
+		end
+	    end
+	end
+    end
+    module_function :on_section_lines
+
+    if false
+    _l1 = [ 'foo', 'bar', 'gazonk', 'del',
+	   'apelsin', 'citron', 'fromage',
+	   'choklad', 'pudding',
+	  ]
+    _l2 = File.readlines('/tmp/trh/grub.conf')
+    _nl1 = enumerate(_l1)
+    _nl2 = enumerate(_l2)
+    _sp1 = /^bar|^citron/
+    _ep1 = /^del|^pudding/
+    _ep2 = /^del|^choklad/
+    _ep3 = /^del|^fromage/
+    _ep4 = /^del|^gurksallad/
+    _ep5 = /^gurksallad/
+    end
+
+    X = 17
+    Y = 23
+    Z = 69
+
+end
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..4c9d2a39105d67db1cf38c3aa186bc025846bdc6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,85 @@
+RM	= rm -vf
+
+export	PUPHOME	= $(shell pwd)
+export	RUBYLIB	= ${PUPHOME}/Lib/ruby
+# We can't rely on $PATH when Puppet is run from cron.  Specifically,
+# /sbin and /usr/sbin are missing.
+export	PATH	= /bin:/usr/bin:/sbin:/usr/sbin
+
+LOGDIR	  := /var/log/puppet
+export LOGDIR			# Needed by the log-puppetrun class
+NOW	  := $(shell /bin/date +'%Y %m %d--%H.%M.%S')
+MONTHDIR  := $(word 1,${NOW})-$(word 2,${NOW})
+TIMESTAMP := $(word 1,${NOW})-$(word 2,${NOW})-$(word 3,${NOW})
+SHORTHOST := $(shell /bin/uname -n | /bin/sed 's/[.].*//')
+LOGSUBDIR := ${LOGDIR}/${SHORTHOST}/${MONTHDIR}
+LOGFILE	  := ${LOGSUBDIR}/${TIMESTAMP}
+
+# Extra flags to give to Puppet
+PUPFLAGS=
+# Extra environment variables to give to Puppet
+PUPENV	=
+
+# Command to strip "uninteresting" messages from cron runs
+#STRIPMSG= egrep -v '^notice:|^info:|^debug:'
+STRIPMSG= cat
+PUPPET	= ${PUPENV} puppet --color=false ${PUPFLAGS} --libdir="${PUPHOME}/Lib" --factpath="${PUPHOME}/Lib/facter"
+
+
+default:
+	cd Files && ${MAKE}
+	@#cd sendmail && ${MAKE}
+
+# Note that this target does not install, or change, the cron job that
+# runs Puppet.
+run:
+	mkdir -p "${LOGSUBDIR}"
+	@echo "Source:  ${PUPHOME}"	>"${LOGFILE}"
+	@echo "Command: ${PUPPET}"	>>"${LOGFILE}"
+	@echo ""			>>"${LOGFILE}"
+	${PUPPET} --tags pkgrepo SITE.pp 2>&1 | tee -a "${LOGFILE}"
+	${PUPPET} SITE.pp 2>&1 | tee -a "${LOGFILE}"
+	@if [ `wc -l <"${LOGFILE}"` -le 3 ]; then \
+	    /bin/rm -f "${LOGFILE}" ; true ; \
+	else \
+	    fin=`date +'%Y-%m-%d  %H:%M:%S'`; \
+	    echo ""			>>"${LOGFILE}" ; \
+	    echo "Finished:  $$fin"	>>"${LOGFILE}" ; \
+	fi
+
+# This target, however, does install the cron job.
+install:
+	@${MAKE} run PUPENV="PUP_DOINSTALL=true ${PUPENV}"
+
+# Run from cron
+# This target also installs the cron job, but it only logs problems
+# to the "terminal".
+# Note: egrep will return 1 if nothing matches, which is perfectly normal
+# in this case, thus the "; true".  Unfortunately we lose the exit status
+# of the make command, but it shouldn't matter much here.
+cron:
+	@${MAKE} run PUPENV="PUP_DOINSTALL=true ${PUPENV}" | \
+	    (${STRIPMSG}; true)
+
+# The 'kickstart' target is used when we run Puppet from the %post kickstart
+# script, since we don't want to actually start the services then.
+kickstart:
+	@${MAKE} run PUPENV="PUP_DOINSTALL=true PUP_RUNDAEMONS=false ${PUPENV}"
+
+# Perform a "dryrun", i.e run without actually making any changes.
+dry:
+	@${MAKE} run PUPFLAGS="--noop ${PUPFLAGS}"
+
+test:
+	${PUPPET} test.pp
+
+
+clean:
+	${RM} .puppethome
+	cd Files && ${MAKE} clean
+	@#cd sendmail && ${MAKE} clean
+	${RM}  *..tmp *.BAK *~ \#.*
+
+
+
+.PHONY: default run install cron kickstart test clean
diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..f9ed36f9eea24de5feb31131dc9ed7724c2c2580
--- /dev/null
+++ b/README
@@ -0,0 +1,134 @@
+This README file documents how these Puppet manifests are used.
+
+
+
+Unlike the "traditional" way of deploying Puppet, this doesn't
+use a central puppetmasterd with clients connecting to it using
+puppetd.  Instead, the manifests are supposed to be directly
+accessible in the file system on the clients, and the stand-alone
+puppet program is used.
+
+
+The manifests are under CVS revision control, in a central CVS
+repository.  To apply them on a machine, you check them out
+using something like:
+
+    [~]$ cd /config
+    [/config]$ cvs -d :ext:user@cvshost:repopath co -d TEST puppet
+    [/config]$ cd TEST
+
+and then run them using:
+
+    [/config/TEST]$ su
+    [/config/TEST]# make install
+
+This will run Puppet (twice, actually; more on that later).  The
+manifests will also install a cron job that runs Puppet from this
+directory (/config/TEST in this example) regularly.
+
+Other interesting targets in the Makefile are:
+
+- run	'make run' runs Puppet without installing the cron job
+	that runs Puppet.
+
+- dry	Similar to 'make run', but it also passes the --noop flag
+	to Puppet, so you can see what Puppet wants to do without
+	actually doing anything.  Useful when you aren't 100% sure
+	that your changes are correct. :-)
+
+- cron	This is the target that the cron job uses.  The difference
+	from 'make install' is that it can be configured to filter
+	out informational messages, so only error messages are
+	mailed to root.
+
+- kickstart
+	Similar to 'make install', but it will make sure all services
+	are stopped instead of running.  This is supposed to be used
+	when Puppet is run from inside RedHat kickstart, Solaris
+	jumpstart, or other similar situations.  It is not used by these
+	manifests, but it is used on some NSC clusters.
+
+
+Usually, you have the manifests checked out at least twice on
+every client, in different directories.  One such directory is
+slightly special; it is named SKARP (Swedish for "live", as in a
+live grenade), and is usually located under /config.  This is
+from where clients run normally, and you are not supposed to edit
+files directly there; it is kept read-only.
+
+When you want to change the manifests, you check them out in
+another directory (e.g, /config/TEST), and edit and test them
+there.  When you are satisfied, you check in your changes to CVS,
+and check them out under SKARP, using:
+
+    [/config]# make skarp
+    [/config]# cd SKARP
+    [/config/SKARP]# make install
+
+The Makefile in /config is installed automatically by the Puppet
+manifests.  The "skarp" target will check out the manifests from
+CVS into the SKARP subdirectory; it knows that SKARP is read-only,
+so you don't need to change permissions manually before or after
+checking out to SKARP.
+
+Note that it is important to run 'make install' from the SKARP
+subdirectory after checking out updated manifests there.
+Otherwise, the client might have its cron job pointing to a
+directory with broken or unmaintained manifests.  By running
+'make install', the cron job will be updated to point to the
+/config/SKARP directory.
+
+
+
+LOGGING
+=======
+
+The Makefile and Puppet manifests maintain logs of the Puppet
+runs, by default under /var/log/puppet/$HOSTNAME.  There will be
+one file named "puppetrun", which contain one line for each time
+Puppet is run, specifying:
+
+  - timestamp (date and time of day)
+  - operating system and OS version
+  - Puppet version
+  - directory from where the manifests where taken
+
+By doing a 'tail -n1' on this file, you can quickly see if Puppet
+is being run properly, and is running from the correct directory
+(i.e, SKARP).
+
+Logs of what Puppet actually did, including any error messages,
+are written in a separate file for each Puppet run.  They are
+kept in a subdirectory per month, and named after the date and
+time when Puppet ran.  Log files when nothing happened (no
+messages at all from Puppet, neither informational nor error
+messages) are however immediately deleted, so you only have the
+interesting logs around.
+
+
+
+THE PKGREPO TAG
+===============
+
+As mentioned above, the make targets will actually run Puppet
+twice, once with --tags pkgrepo, and once without.  In the
+manifests, resources that set up package repositories, set
+package build flags and so on, are tagged with the tag
+'pkgrepo'.  This is done because package repositories must be
+configured before we try to install any packages, and it is
+currently (as of Puppet 0.24.6) not possible to say that some
+resources must be implemented before *all* package resources.
+
+
+
+PUPPET EXTENSIONS
+=================
+
+There are a few custom resource types for editing lines in text
+files included here.  You can find them in the Lib/puppet/type
+subdirectory.  There are documentation strings in the source code
+for these that hopefully make their purpose understandable.
+
+There are also a couple of custom functions (which you can find
+in Lib/puppet/parser/functions), and a number of definitions (in
+util.pp) which makes use of the custom types.
diff --git a/SITE.pp b/SITE.pp
new file mode 100644
index 0000000000000000000000000000000000000000..7d4c86b7ed11a780a835b34864e706c4fdcb9f59
--- /dev/null
+++ b/SITE.pp
@@ -0,0 +1,60 @@
+$puphome  = $env_puphome
+$pupfiles = "$puphome/Files"
+
+# The normal location where we check out Puppet, and especially where
+# SKARP is located:
+$puptree  = "/config"
+# Where we log Puppet runs:
+$pup_logdir = $env_logdir
+
+$osdist = "${operatingsystem}-${lsbdistrelease}"
+
+# We set $running to stopped when Puppet is run during kickstart, and
+# use $running in every service definition when we would normally have
+# specified 'ensure => running', so services aren't started while the
+# machine is installing.
+$running = $env_pup_rundaemons ? { "false" => stopped, default => running, }
+
+
+import "util.pp"
+import "rootuser.pp"
+import "osfixes.pp"
+import "autofs.pp"
+import "printing.pp"
+import "time.pp"
+import "puppet.pp"
+import "environment.pp"
+import "nis.pp"
+import "network.pp"
+import "kerberos.pp"
+import "syslog.pp"
+import "mail.pp"
+import "nrpe.pp"
+
+
+node "furfur"
+{
+   $puptree = "/root/puppet"
+
+   include user-environment
+   include development-environment
+   include sysadm-environment
+   include workstation
+   include network
+   include kerberos
+   include syslog
+   include mail
+   include nrpe
+
+   include root-home
+   include osfixes
+   include log-puppetrun
+   include puppetmaster
+ #  include puppetify
+   include nis-client-user
+   include autofs
+   include printclient
+   include timeclient-static
+   include multimedia-station
+   
+}
diff --git a/autofs.pp b/autofs.pp
new file mode 100644
index 0000000000000000000000000000000000000000..36ed5fa013237d7a6f95debbf4e6c2862a6812ac
--- /dev/null
+++ b/autofs.pp
@@ -0,0 +1,24 @@
+class autofs
+{
+    package {
+	"autofs":
+	    ensure => installed,
+	    name => $operatingsystem? {
+		"Debian" => "autofs",
+	    };
+    }
+
+    file {
+	"/etc/auto.master":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    require => Package["autofs"],
+	    content => template("$pupfiles/auto.master.erb");
+    }
+
+    service { 
+	"autofs": 
+	    enable => true, ensure => $running, 
+	    require => Package["autofs"], subscribe => File["/etc/auto.master"];
+    }
+}
diff --git a/environment.pp b/environment.pp
new file mode 100644
index 0000000000000000000000000000000000000000..220e7b9630646f337e4dab0f37bfe91e4e194d33
--- /dev/null
+++ b/environment.pp
@@ -0,0 +1,142 @@
+class user-environment::debian
+{
+    package {
+	[
+	  "emacs", "screen", "telnet"
+	]:
+	    ensure => installed;
+    }
+}
+
+class user-environment
+{
+    include "user-environment::$operatingsystem"
+}
+
+
+
+class development-environment::debian
+{
+    package {
+	[
+	  "build-essential", "subversion", "cvs", "git"
+	]:
+	    ensure => installed;
+    }
+}
+
+class development-environment
+{
+    include "development-environment::$operatingsystem"
+}
+
+
+
+class sysadm-environment::debian
+{
+    package {
+	[
+	  "pciutils", "usbutils",
+	  "sysstat", "strace", "lsof", "lshw",
+	  "traceroute"
+	]:
+	    ensure => installed;
+    }
+}
+
+class sysadm-environment
+{
+    include "sysadm-environment::$operatingsystem"
+}
+
+
+
+class x11-client::debian
+{
+    package {
+	[
+	  "xterm",
+	  "gv", "xpdf"
+	]:
+	    ensure => installed;
+    }
+}
+
+# X11 programs that are of "generic" use, and can be of use remotely
+# as well.  Very graphically intensive applications that will almost
+# certainly be used on machines with a local display do not belong
+# here.
+class x11-client
+{
+    include "x11-client::$operatingsystem"
+}
+
+
+
+class x11-server::debian
+{
+    package {
+	[
+	]:
+	    ensure => installed;
+    }
+}
+
+class x11-server
+{
+    include "x11-server::$operatingsystem"
+
+    file {
+	"/etc/X11/xorg.conf":
+	    content => template("$pupfiles/xorg.conf-${hostname}.erb"),
+	    owner => "root", group => "root", mode => 0444,
+	    require => Class["x11-server::$operatingsystem"];
+    }
+}
+
+
+
+class workstation::debian
+{
+    package {
+	[
+	]:
+	    ensure => installed;
+    }
+}
+
+class workstation
+{
+    include x11-server
+    include x11-client
+    include "workstation::$operatingsystem"
+}
+
+
+
+class audio-station::debian
+{
+#    package {
+#	[ 
+#	]:
+#	    ensure => installed;
+#    }
+}
+
+class audio-station
+{
+    include "audio-station::$operatingsystem"
+}
+
+
+
+class multimedia-station::debian
+{
+}
+
+class multimedia-station
+{
+    include workstation
+    include audio-station
+    include "multimedia-station::$operatingsystem"
+}
diff --git a/kerberos.pp b/kerberos.pp
new file mode 100644
index 0000000000000000000000000000000000000000..303e0df8364d9ffccf557a56f5a502c2bffa1fb4
--- /dev/null
+++ b/kerberos.pp
@@ -0,0 +1,55 @@
+class pam
+{
+    file {
+	"/etc/pam.d/common-auth":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    source => "$pupfiles/common-auth";
+	 "/etc/pam.d/common-account":
+            ensure => file,
+            owner => "root", group => "root", mode => 644,
+            source => "$pupfiles/common-account";
+	 "/etc/pam.d/common-session":
+            ensure => file,
+            owner => "root", group => "root", mode => 644,
+            source => "$pupfiles/common-session";
+	 "/etc/pam.d/common-password":
+            ensure => file,
+            owner => "root", group => "root", mode => 644,
+            source => "$pupfiles/common-password";
+
+    }
+}
+
+class kerberos
+{
+    $krb5 = $operatingsystem ? {
+	"Debian" => "krb5-user",
+    }
+
+    $pam_krb5 = $operatingsystem ? {
+	"Debian" => "libpam-krb5"
+    }
+
+   $krb5_conf = $operatingsystem ? {
+	"Debian" => "/etc/krb5.conf",
+	default => "/etc/krb5/krb5.conf"
+   }
+
+    package {
+	[ $krb5, $pam_krb5 ]:
+	    ensure => installed,
+    }
+
+    file {
+	"/etc/krb5.conf":
+	    name => $krb5_conf,
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    require => Package[$krb5],
+	    source => "$pupfiles/krb5.conf";
+    }
+
+    include pam
+}
+
diff --git a/mail.pp b/mail.pp
new file mode 100644
index 0000000000000000000000000000000000000000..0536470d75dc0ffd718be1d33b74ad378ef49847
--- /dev/null
+++ b/mail.pp
@@ -0,0 +1,15 @@
+class mail
+{
+    package {
+	"ssmtp":
+	    ensure => installed;
+    }
+
+    file {
+	"/etc/ssmtp/ssmtp.conf":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    require => Package["ssmtp"],
+	    content => template("$pupfiles/ssmtp.conf.erb");
+   }
+}
diff --git a/network.pp b/network.pp
new file mode 100644
index 0000000000000000000000000000000000000000..de78da6768bcdfa1c8ea7fb512e110943dc59556
--- /dev/null
+++ b/network.pp
@@ -0,0 +1,60 @@
+class dns
+{
+    file {
+	"/etc/resolv.conf":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    source => "$pupfiles/resolv.conf";
+    }
+}
+
+class hosts
+{
+    file {
+        "/etc/hosts":
+            ensure => file,
+            owner => "root", group => "root", mode => 644,
+            content => template("$pupfiles/hosts.erb");
+     }
+}
+
+class interfaces
+{
+    file {
+	"interfaces":
+	    name => $operatingsystem? {
+		"Debian" => "/etc/network/interfaces"
+	    },
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    content => template("$pupfiles/interfaces-${operatingsystem}.erb");
+    }
+}
+
+class ssh
+{
+    package {
+	"ssh":
+	    ensure => installed
+    }
+
+    service {
+	"ssh":
+	    enable => true, ensure => $running,
+	    require => Package["ssh"], subscribe => File["/etc/ssh/sshd_config"];
+    }
+
+    file {
+	"/etc/ssh/sshd_config":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 600;
+    }
+}
+
+class network
+{
+    include interfaces
+    include hosts
+    include dns
+    include ssh
+}
diff --git a/nis.pp b/nis.pp
new file mode 100644
index 0000000000000000000000000000000000000000..76348fd12daf20b8a52fcffe84f06809fa5b129e
--- /dev/null
+++ b/nis.pp
@@ -0,0 +1,73 @@
+class nis-generic
+{
+    package {
+	 "nis":
+	    ensure => installed,
+	    name => $operatingsystem? {
+		"Debian" => "nis",
+	    };
+    }
+
+    file {
+	"/etc/defaultdomain":
+	   name => $operatingsystem? {
+	       "Debian" => "/etc/defaultdomain",
+            },
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    require => Package["nis"],
+	    content => "lysator"
+    }
+
+    service { 
+	"nis": 
+	    enable => true, ensure => $running, 
+	    pattern => "ypbind",
+	    require => Package["nis"], subscribe => File["/etc/defaultdomain"], subscribe => File["/etc/yp.conf"];
+    }
+}
+
+class nis-client-generic inherits nis-generic
+{
+    file {
+	"/etc/yp.conf":
+	    name => $operatingsystem? {
+		"Debian" => "/etc/yp.conf",
+	    },
+	    ensure => file,
+            owner => "root", group => "root", mode => 644,
+	    require => Package["nis"],
+	    source => "$pupfiles/yp.conf";
+    }
+}
+
+class nis-client-limited inherits nis-client-generic
+{
+    file {
+	"/etc/nsswitch.conf":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    source => "$pupfiles/nsswitch.conf-limited",
+	    require => Package["nis"];
+    }
+
+    ensure_line {
+	passwd_root:
+	    file => "/etc/passwd",
+	    line => "+@root:x::::/roots:";
+	passwd_nouser:
+	    file => "/etc/passwd",
+	    line => "+:x:::::/bin/nologin";
+    }
+}
+
+class nis-client-user inherits nis-client-generic
+{
+    file {
+        "/etc/nsswitch.conf":
+            ensure => file,
+            owner => "root", group => "root", mode => 644,
+            source => "$pupfiles/nsswitch.conf-user",
+            require => Package["nis"];
+    }
+}
diff --git a/nrpe.pp b/nrpe.pp
new file mode 100644
index 0000000000000000000000000000000000000000..ceedc1f26f4010fbed6011417b3f74d5f4fab2a8
--- /dev/null
+++ b/nrpe.pp
@@ -0,0 +1,36 @@
+class nrpe
+{
+    package {
+	"nagios-nrpe-plugin":
+	    ensure => installed,
+	    name => $operatingsystem? {
+		"Debian" => "nagios-nrpe-plugin",
+	    };
+	"nagios-nrpe-server":
+	    ensure => installed,
+	    name => $operatingsystem? {
+		"Debian" => "nagios-nrpe-server",
+	    };
+    }
+
+    file {
+	"/etc/nagios/nrpe.cfg":
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    require => Package["nagios-nrpe-server"],
+	    source => "$pupfiles/nrp.cfg";
+	"/opt/nrpe":
+	    ensure => directory,
+	    owner => "root", group => "root", mode => 644,
+	    require => Package["subversion"];
+    }
+
+    service { 
+	"nrpe": 
+	    name => "nagios-nrpe-server",
+	    enable => true, ensure => $running, 
+	    pattern => "nrpe",
+	    require => Package["nagios-nrpe-plugin"],
+	    require => Package["nagios-nrpe-server"];
+    }
+}
diff --git a/osfixes-debian.pp b/osfixes-debian.pp
new file mode 100644
index 0000000000000000000000000000000000000000..c6d48ea82ea195eb74fe9540a7cdb4afcef4dec6
--- /dev/null
+++ b/osfixes-debian.pp
@@ -0,0 +1,18 @@
+class debian-keep-os-updated
+{
+}
+
+class debian-no-networkmanager
+{
+    package {
+	[
+	    "network-manager", "gnome-network-manager"
+	]:
+	    ensure => absent;
+    }
+}
+
+class debian-fixes
+{
+    include debian-no-networkmanager
+}
diff --git a/osfixes-fedora.pp b/osfixes-fedora.pp
new file mode 100644
index 0000000000000000000000000000000000000000..d1d97c93d53fc69cd6ac0805df9846583f348f23
--- /dev/null
+++ b/osfixes-fedora.pp
@@ -0,0 +1,182 @@
+class fedora-package-repositories
+{
+    yumrepo {
+	"nsc":
+	    descr => "NSC - Internal repository",
+	    baseurl => "http://spocto.nsc.liu.se/repo/el5/\$basearch/",
+	    enabled => 0;
+    }
+}
+
+class fedora-extra-packages
+{
+    package {
+	[ ]:
+	    ensure => installed;
+    }
+}
+
+class fedora-keep-os-updated
+{
+    service {
+	"yum-updatesd":
+	    enable => false, ensure => stopped;
+    }
+    if "" {
+	exec {
+	    "/usr/bin/yum -d 1 -e 0 -y update":
+		logoutput => true,
+		schedule => updates;
+	}
+    } else {
+	cron {
+	    update-os:
+		command => "sleep \$[\$RANDOM\\%1200]; yum -d 1 -e 0 -y update",
+		user    => "root",
+		month   => "1-12", monthday => "1-31", weekday => "0-7",
+		hour    => "3", minute => "33";
+	}
+    }
+}
+
+class fedora-no-wtmp-rotation
+{
+    comment_lines {
+	wtmp-logrotate:
+	    file => "/etc/logrotate.conf",
+	    start => "+/var/log/[wb]tmp", end => "+\\}",
+	    comment => "## ";
+    }
+}
+
+class fedora-verbose-text-boot
+{
+    regexp_replace_lines {
+	no_rhgb_boot:
+	    file => "/boot/grub/grub.conf",
+	    pattern => "([ \t]*kernel[ \t].*)[ \t]rhgb(|[ \t].*)",
+	    replacement => "\\1\\2";
+	no_quiet_boot:
+	    file => "/boot/grub/grub.conf",
+	    pattern => "([ \t]*kernel[ \t].*)[ \t]quiet(|[ \t].*)",
+	    replacement => "\\1\\2";
+	no_hidden_menu:
+	    file => "/boot/grub/grub.conf",
+	    pattern => "hiddenmenu",
+	    replacement => "#\\&";
+    }
+    ensure_line {
+	grub_timeout:
+	    file => "/boot/grub/grub.conf",
+	    line => "timeout=5",
+	    pattern => "timeout[ \t]*=",
+	    where => "title",
+	    addhow => prepend;
+    }
+}
+
+class fedora-thunderbird-junk
+{
+    tidy {
+	"/tmp/tmprules.dat":
+	    backup => false, age => "5m";
+	# Wildcards doesn't seem to be supported.
+	##--"/tmp/tmprules-*.dat":
+	##--    backup => false, age => "5m";
+    }
+    exec {
+	"/bin/rm -vf /tmp/tmprules-*.dat":
+	logoutput => false, onlyif => "/bin/ls /tmp/tmprules-*.dat";
+    }
+}
+
+class fedora-profile-aliases
+{
+    tidy {
+	"/etc/profile.d/colorls.sh":		age => "0";
+	"/etc/profile.d/colorls.csh":		age => "0";
+	"/etc/profile.d/vim.sh":		age => "0";
+	"/etc/profile.d/vim.csh":		age => "0";
+	"/etc/profile.d/which-2.sh":		age => "0";
+	# We can't kill lang.sh, since other parts of Fedora
+	# explicitly source it.  But the i18n-settings class
+	# at least makes it less harmful...
+	##--"/etc/profile.d/lang.sh":		age => "0";
+	##--"/etc/profile.d/lang.csh":		age => "0";
+	"/etc/profile.d/ccache.sh":		age => "0";
+	"/etc/profile.d/ccache.csh":		age => "0";
+	"/etc/profile.d/gnome-ssh-askpass.sh":	age => "0";
+	"/etc/profile.d/gnome-ssh-askpass.csh":	age => "0";
+    }
+}
+
+class fedora-i18n-settings
+{
+    comment_lines {
+	sysconfig_no_lang:
+	    file => "/etc/sysconfig/i18n",
+	    pattern => "LANG=.*";
+    }
+    ensure_line {
+	sysconfig_lc_ctype:
+	    file => "/etc/sysconfig/i18n",
+	    line => "LC_CTYPE=sv_SE.UTF-8",
+	    pattern => "LC_CTYPE=",
+	    where => ".*LANG=", addhow => append;
+    }
+}
+
+class fedora-selinux
+{
+    rh_sysconfig {
+	selinux-selinux:
+	    subsystem => "selinux",
+	    setting => "SELINUX", value => "disabled";
+	selinux-selinuxtype:
+	    subsystem => "selinux",
+	    setting => "SELINUXTYPE", value => "targeted";
+    }
+}
+
+class fedora-lvm
+{
+    ensure_line {
+	lvm_filter_devices:
+	    file => "/etc/lvm/lvm.conf",
+	    line => "    filter = [ \"r|/dev/cdrom|\", \"r|/dev/fd.*|\" ]",
+	    pattern => "[ \t]*filter[ \t]*=.*",
+	    where => "^devices";
+	lvm_filter_devices_comment:
+	    file => "/etc/lvm/lvm.conf",
+	    line => "    # Trying to do LVM on CDs or floppies is foolish.",
+	    where => "[ \t]*filter[ \t]*=.*",
+	    addhow => prepend,
+	    require => Ensure_line[lvm_filter_devices];
+    }
+}
+
+
+class fedora8-fixes
+{
+    include fedora-package-repositories
+    include fedora-extra-packages
+    include fedora-no-wtmp-rotation
+    include fedora-verbose-text-boot
+    include fedora-thunderbird-junk
+    include fedora-profile-aliases
+    include fedora-i18n-settings
+    include fedora-selinux
+    include fedora-lvm
+
+    file { "/dev/shm":
+	    ensure => "directory",
+	    owner => "root", group => "root", mode => 1777;
+    }
+    ensure_line {
+	boot_runlevel:
+	    file    => "/etc/inittab",
+	    line    => "id:3:initdefault:",
+	    pattern => "^id:.*",
+	    where   => "^$|^[^#].*$", addhow => prepend;
+    }
+}
diff --git a/osfixes.pp b/osfixes.pp
new file mode 100644
index 0000000000000000000000000000000000000000..21d2465b1a76bd251e8a0eb558a1a299f25595d4
--- /dev/null
+++ b/osfixes.pp
@@ -0,0 +1,27 @@
+import "osfixes-debian.pp"
+
+schedule {
+    updates:
+	range => "3:00-5:00", period => daily, repeat => 1;
+}
+
+
+class keep-os-updated
+{
+    case $operatingsystem {
+	"Debian": {
+		include debian-keep-os-updated
+	}
+    }
+}
+
+
+class osfixes
+{
+    case $operatingsystem {
+	"Debian": {
+		include debian-fixes
+	}
+    }
+    include keep-os-updated
+}
diff --git a/printing.pp b/printing.pp
new file mode 100644
index 0000000000000000000000000000000000000000..1d3c9ab554a40f414bec0ea22138e9b3dfbee831
--- /dev/null
+++ b/printing.pp
@@ -0,0 +1,46 @@
+class cups-printclient
+{
+    file {
+	"/etc/cups/printers.conf":
+		source => "$pupfiles/cups-printers.conf-client",
+		owner => root, group => lp,  mode => 0600;
+    }
+}
+
+class cups-printserver
+{
+    file {
+	"/etc/cups/printers.conf":
+		source => "$pupfiles/cups-printers.conf-server",
+		owner => root, group => lp,  mode => 0600;
+    }
+}
+
+class cups
+{
+    service {
+	cups:
+	    subscribe => File["/etc/cups/printers.conf"]
+    }
+}
+
+
+class printclient
+{
+    case $operatingsystem {
+	"Fedora": {
+		include cups
+		include cups-printclient
+	}
+    }
+}
+
+class printserver
+{
+    case $operatingsystem {
+	"Fedora": {
+		include cups
+		include cups-printserver
+	}
+    }
+}
diff --git a/puppet.pp b/puppet.pp
new file mode 100644
index 0000000000000000000000000000000000000000..856cd0159c613ae73d9bfaf116ae144d7747f75b
--- /dev/null
+++ b/puppet.pp
@@ -0,0 +1,78 @@
+class puppetify
+{
+    $puppetpkg = $operatingsystem ? {
+	    "Debian" => "puppet",
+	    "Fedora" => "puppet",
+	    "CentOS" => "puppet",
+	    "Gentoo" => "app-admin/puppet"
+	}
+    package {
+	$puppetpkg:
+	    ensure => present;
+    }
+
+    if $env_pup_doinstall {
+	cron {
+	    run-puppet:
+		command => "sleep \$[\$RANDOM\\%300]; cd '$puphome' && make -s --no-print-directory cron",
+		user => "root",
+		month => "1-12", monthday => "1-31", weekday => "0-7",
+		hour => "*/4", minute => "7",
+		ensure => present;
+	}
+	file {
+	    "/etc/init.d/puppetrun":
+		ensure => file,
+		content => template("$pupfiles/puppetrun-rcinit.erb"),
+		owner => "root", group => "root", mode => 0555;
+	}
+	service {
+	    "puppetrun":
+		# We don't run this at boot, since it takes too long when
+		# don't have network (not uncommon on a portable).
+		# FIXME: This should be configurable per node.
+		enable => false,
+		ensure => stopped, hasstatus => true,
+		require => [ File["/etc/init.d/puppetrun"],
+			     Package[$puppetpkg] ];
+	    "puppet":
+		enable => false, ensure => stopped, hasstatus => true,
+		require => Package[$puppetpkg];
+	}
+    }
+}
+
+
+class puppetmaster
+{
+    file {
+	"$puptree/Makefile":
+	    ensure => file,
+	    content => template("$pupfiles/puppet-Makefile.erb"),
+	    owner => "root", group => "root", mode => 0444;
+    }
+}
+
+
+class log-puppetrun
+{
+    $runlogdir  = "$pup_logdir/${hostname}"
+    $runlogfile = "$runlogdir/puppetrun"
+    $timestamp  = strftime("%Y-%m-%d  %H:%M:%S")
+
+    file {
+	$runlogdir:
+	    ensure => directory, owner => root, group => root, mode => 0755;
+	$runlogfile:
+	    ensure => file, owner => root, group => root, mode => 0644,
+	    require => File[$runlogdir];
+    }
+    ensure_line {
+	log_puppetrun:
+	    file => $runlogfile,
+	    line => sprintf("%s\t%s\t%s\t%s",
+			    $timestamp, $osdist, $puppetversion, $puphome),
+	    require => File[$runlogfile],
+	    loglevel => info;
+    }
+}
diff --git a/rootuser.pp b/rootuser.pp
new file mode 100644
index 0000000000000000000000000000000000000000..a52c66fa431b66706bf73c4a2d7be110d522d24d
--- /dev/null
+++ b/rootuser.pp
@@ -0,0 +1,25 @@
+$root_home = $operatingsystem ? {
+	solaris => "/",
+	default => "/root",
+}
+
+class root-home
+{
+    file {
+	"$root_home/.emacs":
+		source => "$pupfiles/root-.emacs",
+		owner => "root", group => "root", mode => 0644;
+	"$root_home/.bashrc":
+		source => "$pupfiles/root-.bashrc",
+		owner => "root", group => "root", mode => 0644;
+	"$root_home/.bash_profile":
+		source => "$pupfiles/root-.bash_profile",
+		owner => "root", group => "root", mode => 0644;
+	"$root_home/.bash_logout":
+		source => "$pupfiles/root-.bash_logout",
+		owner => "root", group => "root", mode => 0644;
+	"$root_home/.cvsrc":
+		source => "$pupfiles/root-.cvsrc",
+		owner => "root", group => "root", mode => 0644;
+    }
+}
diff --git a/syslog.pp b/syslog.pp
new file mode 100644
index 0000000000000000000000000000000000000000..05bd1fb084ab18a6e74b27c29db5c16616149fdd
--- /dev/null
+++ b/syslog.pp
@@ -0,0 +1,33 @@
+class syslog
+{
+    $syslog_conf = $operatingsystem ? {
+	default => "/etc/syslog.conf"
+    }
+    
+    $syslog = $operatingsystem ? {
+	"Debian" => "sysklogd",
+	default => "syslogd"
+    }
+
+    file {
+	"/etc/syslog.conf":
+	    name => $syslog_conf,
+	    ensure => file,
+	    owner => "root", group => "root", mode => 644,
+	    source => "$pupfiles/syslogd.conf";
+    }
+
+    service { 
+	"$syslog": 
+	    enable => true, ensure => $running, 
+	    pattern => "syslog",
+	    subscribe => File["/etc/syslog.conf"];
+    }
+
+    cron {
+	"syslog_mark":
+    	    command => "/usr/bin/logger mark",
+	    user => root,
+	    minute => 43
+    }
+}
diff --git a/time.pp b/time.pp
new file mode 100644
index 0000000000000000000000000000000000000000..dd75860d54f6ed98d869b52e16003374a1e95094
--- /dev/null
+++ b/time.pp
@@ -0,0 +1,58 @@
+class ntp-generic
+{
+    package {
+	"ntp":
+	    ensure => installed,
+	    name => $operatingsystem ? {
+		"Debian"	=> "ntp",
+	    };
+    }
+    file {
+	"/var/lib/ntp":
+	    # Ntpd needs to be able to store /var/lib/ntp/drift.
+	    ensure => directory,
+	    owner => "ntp", group => "ntp", mode => 0755,
+	    require => Package["ntp"];
+    }
+    case $operatingsystem {
+	"Fedora", "CentOS": {
+	    rh_sysconfig {
+		ntpd_options:
+		    subsystem => "ntpd", setting => "OPTIONS",
+		    value => '"-g -u ntp:ntp -p /var/run/ntpd.pid"',
+		    require => Package["ntp"],
+		    notify => Service["ntp"];
+	    }
+	}
+	#"Ubuntu": {
+	#}
+    }
+    file {
+	"/etc/ntp.conf":
+	    name => "/etc/ntp.conf",
+	    owner => "root", group => "root", mode => 0444,
+	    require => Package["ntp"];
+    }
+    service {
+	"ntp":
+	    enable => true, ensure => $running,
+	    require => Package["ntp"], subscribe => File["/etc/ntp.conf"];
+    }
+}
+
+
+class timeserver inherits ntp-generic
+{
+    File["/etc/ntp.conf"] {
+	source => "$pupfiles/ntp.conf-server"
+    }
+}
+
+
+class timeclient-static inherits ntp-generic
+{
+    File["/etc/ntp.conf"] {
+	source => "$pupfiles/ntp.conf-client"
+    }
+}
+
diff --git a/util.pp b/util.pp
new file mode 100644
index 0000000000000000000000000000000000000000..d5ef71ea4e502738e11a8cccf4dd6d07e65c84d1
--- /dev/null
+++ b/util.pp
@@ -0,0 +1,510 @@
+# Comment out lines in the file $file matching the regexp $pattern.
+# The regexp is implicitly anchored at both ends of the line.
+# This is only done on matching lines within sections within groups;
+# see the regexp_replace_lines type for how sections and groups work.
+#
+# Commenting out is done by adding $comment (default "#") to the start
+# of the lines, and $comment_end to the end of the lines.
+
+define comment_lines($file, $pattern=".*", $comment="#", $comment_end="",
+       		     $group_start="", $group_end="", $start="", $end="")
+{
+    $quoted_comment = regexp_quote($comment)
+    $quoted_comment_end = regexp_quote($comment_end)
+
+    regexp_replace_lines {
+	"comment--$title--$file--$start--$end":
+	    file => $file,
+	    group_start => $group_start, group_end => $group_end,
+	    start => $start, end => $end,
+	    pattern => "${pattern}",
+	    skip => "${quoted_comment}${pattern}(${quoted_comment_end})?",
+	    replacement => "${comment}\\&${comment_end}";
+    }
+}
+
+
+
+define config_option($file, $option, $value, $ensure="present")
+{
+    case $ensure {
+	"present": {
+	    ensure_line {
+		"config_option--$file--$option":
+		    file    => $file,
+		    line    => "${option}=${value}",
+		    pattern => "^(#)?${option}[ \t]*=.*$";
+	    }
+	}
+	"absent": {
+	    # FIXME: This will create an out-commented line if none exists.
+	    ensure_line {
+		"config_option--$file--$option":
+		    file    => $file,
+		    line    => "#${option}=${value}",
+		    pattern => "^(#)?${option}[ \t]*=.*$";
+	    }
+	}
+	"purged": {
+	    delete_lines {
+		"config_option--$file--$option":
+		    file    => $file,
+		    pattern => "^(#)?${option}[ \t]*=.*$";
+	    }
+	}
+	default: {
+	    fail("Bad config_option parameter ensure: $ensure")
+	}
+    }
+}
+
+
+# Ensure that the variable $setting is set to $value for subsystem $subsystem.
+# If $service is set, that service is notified when the file is changed.
+#
+# Note that if you need spaces or other shell metacharacters in $value,
+# you need to quote it yourself.
+#
+# This definition is RedHat-specific, as it does its duty by editing
+# the /etc/sysconfig/$subsystem file.
+
+define rh_sysconfig($subsystem, $setting, $value, $ensure="present",
+		    $service="")
+{
+    config_option {
+	"rh_sysconfig--${subsystem}--${setting}":
+	    file   => "/etc/sysconfig/${subsystem}",
+	    option => $setting,
+	    value  => $value,
+	    ensure => $ensure,
+	    notify => $service ? { "" => [], default => Service[$service] };
+    }
+}
+
+
+
+# Configure settings in /etc/make.conf in Gentoo/Portage.
+# Parameters:
+# - $name	The name of the setting, e.g. CFLAGS, FEATURES or VIDEO_CARDS
+# - $value	The value of the setting.  The value will be surrounded
+#		by double quotes, but no other quoting of the value will
+#		be done.  Be careful to not have newlines in the value,
+#		since portage_makeconf won't be able to handle what it
+#		will write in the file.
+# - $ensure	'present' (default), 'absent' or 'purged'
+
+define portage_makeconf($value="", $ensure="present")
+{
+    config_option {
+	"portage_makeconf--$name":
+	    file   => "/etc/make.conf",
+	    option => $name,
+	    value  => "\"${value}\"",
+	    ensure => $ensure,
+	    tag    => "pkgrepo";
+    }
+}
+
+
+
+# Internal helper class for Gentoo/Portage.
+class portage_files__
+{
+    file {
+	"/etc/portage":
+	    ensure => directory,
+	    owner => "root", group => "root", mode => "0755";
+	[ "/etc/portage/package.use",
+	  "/etc/portage/package.keywords",
+	  "/etc/portage/package.mask",
+	  "/etc/portage/package.unmask"
+	]:
+	    ensure => file,
+	    owner => "root", group => "root", mode => "0444";
+    }
+}
+
+# Declare Portage USE flags for packages.
+# Parameters:
+# - $name	The package for which USE flags are set.
+#		The special name "GLOBAL" means set global use flags,
+#		i.e. set the USE variable in /etc/make.conf.
+# - $use	The USE flags to set.  When set to the empty string,
+#		the entire line for the package will be removed from
+#		/etc/portage/package.use.
+
+# Note: It might be tempting to add an automatic before => Package[$name],
+# but that would be a mistake.  Sometimes you want to set use flags for
+# packages that are only installed as dependencies for other packages,
+# not because you want that package for itself.  Having to add that
+# package to the Puppet manifests would be a nuisance, and wrong (it
+# would then appear in the Portage world file).
+#    Instead we tag the changes with 'pkgrepo', so it will be done in
+# the first run of Puppet from Make.
+
+define portage_useflags($use)
+{
+    case $name {
+	"GLOBAL": {
+	    portage_makeconf {
+		"USE": value => $use;
+	    }
+	}
+	default: {
+	    include portage_files__
+	    $quoted_package = regexp_quote($name)
+	    $quoted_use = regexp_quote($use)
+	    if $use {
+		ensure_line {
+		    "portage-useflags--$name":
+			file    => "/etc/portage/package.use",
+			line    => sprintf("%-24s\t%s", $name, $use),
+			pattern => "$quoted_package[ \t]+.*",
+			sufficient => "$quoted_package[ \t]+$quoted_use",
+			require => File["/etc/portage/package.use"],
+			tag	=> 'pkgrepo';
+		}
+	    } else {
+		delete_lines {
+		    "portage-useflags--$name":
+			file    => "/etc/portage/package.use",
+			pattern => "$quoted_package[ \t]+.*",
+			require => File["/etc/portage/package.use"],
+			tag	=> 'pkgrepo';
+		}
+	    }
+	}
+    }
+}
+
+
+
+# Declare Portage keyword for packages.
+# Parameters:
+# - $name	The package for which keyword is set.
+#		The special name "GLOBAL" means set global keyword,
+#		i.e. set the ACCEPT_KEYWORDS variable in /etc/make.conf.
+# - $keyword	The keyword to set.  When set to the empty string,
+#		the entire line for the package will be removed from
+#		/etc/portage/package.keywords.
+
+# Note: Don't add a before => Package[$name]; see the note for
+# portage_useflags for further explanation.
+
+define portage_keyword($keyword)
+{
+    case $name {
+	"GLOBAL": {
+	    portage_makeconf {
+		"ACCEPT_KEYWORDS": value => $keyword;
+	    }
+	}
+	default: {
+	    include portage_files__
+	    $quoted_package = regexp_quote($name)
+	    $quoted_keyword = regexp_quote($keyword)
+	    if $keyword {
+		ensure_line {
+		    "portage-keyword--$name":
+			file    => "/etc/portage/package.keywords",
+			line    => sprintf("%-24s\t%s", $name, $keyword),
+			pattern => "$quoted_package[ \t]+.*",
+			sufficient => "$quoted_package[ \t]+$quoted_keyword",
+			require => File["/etc/portage/package.keywords"],
+			tag	=> 'pkgrepo';
+		}
+	    } else {
+		delete_lines {
+		    "portage-keyword--$name":
+			file    => "/etc/portage/package.keywords",
+			pattern => "$quoted_package[ \t]+.*",
+			require => File["/etc/portage/package.keywords"],
+			tag	=> 'pkgrepo';
+		}
+	    }
+	}
+    }
+}
+
+
+
+# Set Gentoo config options, in /etc/conf.d.
+# Parameters:
+# - $subsystem	The subsystem for which an option is defined.  The option
+#		will be written to /etc/conf.d/$subsystem.
+# - $option	The name of the option to set.
+# - $value	The value of the option.  The value will be surrounded
+#		by double quotes, but no other quoting will be performed.
+# - $ensure	'present' (default), 'absent' or 'purged'.
+
+define gentoo_conf($subsystem, $option, $value, $ensure="present")
+{
+    config_option {
+	"gentoo--${subsystem}--${option}":
+	    file   => "/etc/conf.d/${subsystem}",
+	    option => $option,
+	    value  => "\"${value}\"",
+	    ensure => $ensure;
+    }
+}
+
+
+
+# Set options for a Xinetd service.
+# Assumes that the service definition is in a file in /etc/xinetd.d
+# with the same name as the service.
+
+define xinetd_srv_option($service, $option, $value)
+{
+    $qopt = regexp_quote($option)
+    $qval = regexp_quote($value)
+
+    ensure_line {
+	"xinetd--$service--$option":
+	    file	=> "/etc/xinetd.d/$service",
+	    line	=> sprintf("\t%-23s = %s", $option, $value),
+	    pattern	=> sprintf("[ \t]*%s[ \t]*=", $qopt),
+	    sufficient	=> sprintf("[ \t]*%s[ \t]*=[ \t]*%s[ \t]*$",
+				   $qopt, $qval),
+	    start	=> ".*\\{",
+	    end		=> "[ \t]*\\}",
+    }
+}
+
+
+
+# Create (or remove) a system accont, i.e one with a "low" uid.
+# The normal user type can't be instructed to create a system
+# account without hardcoding a uid, which we don't want to do.
+#
+# A group with the same name as the user will be created at the
+# same time.
+#
+# This implementation is RedHat specific.
+
+define rh_sysuser($comment="", $home="/", $shell="/sbin/nologin",
+		  $ensure="present")
+{
+    rh_sysgroup {
+	$name:
+	    ensure => $ensure;
+    }
+    case $ensure {
+	"present": {
+	    exec {
+		"sysuser--$name":
+		    command => "useradd -r -c '$comment' -M -d '$home' -s '$shell' -g '$name'  '$name'",
+		    unless  => "getent passwd '$name'",
+		    path    => "/sbin:/usr/sbin:/bin:/usr/bin",
+		    require => Rh_sysgroup[$name];
+	    }
+	}
+	"absent": {
+	    exec {
+		"sysuser--$name":
+		    command => "userdel -f '$name'",
+		    onlyif  => "getent passwd '$name'",
+		    path    => "/sbin:/usr/sbin:/bin:/usr/bin",
+		    before  => Rh_sysgroup[$name];
+	    }
+	}
+	default: {
+	    fail("Bad rh_sysuser parameter ensure: $ensure")
+	}
+    }
+    # These are here so we get auto-require from things that want
+    # user and/or group names, like the file type.
+    user {
+	"$name":
+	    ensure => $ensure,
+	    gid => $name, comment => $comment, home => $home, shell => $shell,
+	    require => Exec["sysuser--$name"]
+    }
+}
+
+
+define rh_sysgroup($ensure="present")
+{
+    case $ensure {
+	"present": {
+	    exec {
+		"sysgroup--$name":
+		    command => "groupadd -r '$name'",
+		    unless  => "getent group '$name'",
+		    path    => "/sbin:/usr/sbin:/bin:/usr/bin";
+	    }
+	}
+	"absent": {
+	    exec {
+		"sysgroup--$name":
+		    command => "groupdel '$name'",
+		    onlyif  => "getent group '$name'",
+		    path    => "/sbin:/usr/sbin:/bin:/usr/bin";
+	    }
+	}
+	default: {
+	    fail("Bad rh_sysgroup parameter ensure: $ensure")
+	}
+    }
+    # This is here so we get auto-require from things that want
+    # group names, like the file type.
+    group {
+	"$name":
+	    ensure => $ensure, require => Exec["sysgroup--$name"]
+    }
+}
+
+
+
+# Mirror a directory tree using rsync.
+#
+# The key in the file $sshkey must not be encrypted.
+#
+# For security, the remote host should have something like this as flags
+# in its .ssh/authorized_keys file for the key:
+#
+# command="/usr/bin/rsync --server -vlogDtprR --delete --delete-excluded . $dir"
+# no-pty
+# no-port-forwarding
+# no-agent-forwarding
+# no-X11-forwarding
+
+define rsync_mirror($source, $target,
+		    $sshkey="",
+		    $unless="", $onlyif="", $creates="",
+		    $month="1-12", $monthday="1-31", $weekday="0-7",
+		    $hour="", $minute="",
+		    $ensure="present"
+		   )
+{
+    if $sshkey {
+	$sshcmd = "ssh -i$sshkey"
+    } else {
+	$sshcmd = "ssh"
+    }
+    $rsynccmd = "rsync -aRqv --no-implied-dirs --delete --delete-excluded"
+    $command = "RSYNC_RSH=\"$sshcmd\"  $rsynccmd  '$source'  '$target'"
+
+    case $ensure {
+	"present": {
+	    exec {
+		"$rsynccmd '$source' '$target'":
+		    path => "/bin:/usr/bin",
+		    unless => $unless ? { "" => undef, default => $unless },
+		    onlyif => $onlyif ? { "" => undef, default => $onlyif },
+		    creates => $creates ? { "" => undef, default => $creates };
+	    }
+
+	    if $hour {
+		cron {
+		    "mirror--$title":
+			command => "PATH=/bin:/usr/bin;  $command",
+			user => "root",
+			month => $month, monthday => $monthday,
+			weekday => $weekday,
+			hour => $hour, minute => $minute;
+		}
+	    }
+	}
+	"absent": {
+	    tidy {
+		$target:
+		    recurse => true, rmdirs => true,
+		    size => 0,
+		    backup => false
+		    ;
+	    }
+	    cron {
+		"mirror--$title":
+		    command => "PATH=/bin:/usr/bin;  $command",
+		    ensure => absent;
+	    }
+	}
+	default: {
+	    fail("Bad rsync_mirror parameter ensure: $ensure")
+	}
+    }
+}
+
+
+
+# The interface type in Puppet 0.24.x is broken.  We thus define
+# our own version, although with a somewhat different API.
+
+define rh_interface($bootproto="static",
+		    $ipaddress="",
+		    $netmask="",
+		    $gateway="",
+		    $onboot="yes",
+		    $ensure="up",
+		    $persistent_dhcp="yes")
+{
+    file {
+	"/etc/sysconfig/network-scripts/ifcfg-$name":
+	    content => template("$pupfiles/rh-ifcfg.erb"),
+	    owner => "root", group => "root", mode => 0444,
+	    notify => Exec["rh_interface--ifconfig--$name/$ensure"];
+    }
+    case $ensure {
+	"up": {
+	    exec {
+		"rh_interface--ifconfig--$name/up":
+		    command => "/sbin/ifdown '$name' && /sbin/ifup '$name'",
+		    refreshonly => true,
+		    path => "/bin:/usr/bin:/sbin:/usr/sbin";
+	    }
+	}
+	"down": {
+	    exec {
+		"rh_interface--ifconfig--$name/down":
+		    command => "/sbin/ifdown '$name'",
+		    refreshonly => true,
+		    path => "/bin:/usr/bin:/sbin:/usr/sbin";
+	    }
+	}
+	default: {
+	    fail("Bad ensure parameter to rh_interface $title: $ensure")
+	}
+    }
+}
+
+
+
+# Define a sysctl setting.
+#
+# This sets the kernel's current value, as well as adding it to
+# /etc/sysctl.conf so it gets loaded immediately when booting.
+#
+# Setting $value to the empty string ("") will remove the setting
+# from /etc/sysctl.conf.  However, since there is no way to make
+# the running kernel revert to its default value, it will not take
+# effect until next boot.
+#
+# Note also that some sysctl parameters may require a restart of
+# the kernel subsystem to take effect (e.g, the NFS lock daemon).
+
+define sysctl($value)
+{
+    $qname = regexp_quote($name)
+
+    if $value {
+	ensure_line {
+	    "sysctl--$name":
+		file => "/etc/sysctl.conf",
+		line => "$name = $value",
+		pattern => "${qname}[ \t]*=.*";
+	}
+	exec {
+	    "sysctl-w-$name":
+		command => "sysctl -w $name=$value",
+		unless  => "[ \"`sysctl -n $name`\" = '$value' ]",
+		path => "/sbin:/bin:/usr/bin";
+	}
+    } else {
+	delete_lines {
+	    "sysctl--$name":
+		file => "/etc/sysctl.conf",
+		pattern => "${qname}[ \t]*=.*";
+	}
+    }
+}