Laptop Maintenance Mode for Gentoo Linux

I run Gentoo Linux on a variety of laptops, and while I'm generally happy with it, there are some places where the default system just doesn't fit my needs. Sometimes you can just feel how parts of the software were designed for 24/7 servers and not for mobile devices. For example, there are some processes that need to be run frequently, but that you really don't want to kick in when you're out in the field and running on battery power - updatedb, makewhatis or Leafnode's texpire for example. And there are some other maintenance actions that should be performed on a regular basis, too - emerge --sync and backup for example. And most of the time, I just don't feel inclined to manually perform all these tasks when retuning home after a working day. This is why I added a "maintenance mode" to my laptop installations. Usage of this maintenance mode is very straight forward - plug the laptop into the local network, power it on, select "maintenance mode" in the boot loader menu and go away. The system will automatically perform all actions and then power down.

Building this maintenance mode is rather easy, as you'll shortly see. I'll assume you're working with Gentoo - this will most probably work for other distros, but you'll have to adapt paths and other details.

First, we need another runlevel - let's call it "maintenance" for obvious reasons. If you've only got the default runlevel, you can copy and adapt it:

# cp -r /etc/runlevels/default /etc/runlevels/maintenance

I tend to have several different runlevels on my laptops - one for the "home" configuration, another one for the "roadwarrior" setup. Doesn't really matter, you can use any existing runlevel as a template or create a new one from scratch with

# mkdir /etc/runlevels/maintenance

Now we need to select the services we want to run in maintenance mode. As a rule of thumb, try to start as few services as possible. Do not start any *cron* services (anacron, vixie-cron, ...). This is how one of my laptops is configured:

# update-rc show
           alsasound |      boot                         
             anacron | away      home                    
            bbackupd |                maintenance        
            bootmisc |      boot                         
             checkfs |      boot                         
           checkroot |      boot                         
               clock |      boot                         
         consolefont |      boot                         
        cpufrequtils | away      home maintenance        
               cyrus | away      home                    
                dbus |      boot                         
                exim | away      home                    
                hald | away      home maintenance        
            hostname |      boot                         
             keymaps |      boot                         
               local | away      home maintenance        
          localmount |      boot                         
             modules |      boot                         
            net.eth0 |           home                    
            net.eth1 |           home                    
              net.lo |      boot                         
            netmount | away      home                    
                nscd |      boot                         
          ntp-client |                maintenance        
           rmnologin |      boot                         
           saslauthd | away      home maintenance        
           syslog-ng | away      home maintenance        
             urandom |      boot                         
          vixie-cron | away      home                    
                 xdm | away      home                    
              xinetd | away      home                    

Next, we need to adapt the configuration of the boot loader - in my case, /boot/grub/menu.lst - and add a new menu entry with a softlevel option. The result might look like this:

default 0
timeout 3
splashimage=(hd0,0)/boot/grub/splash.xpm.gz

title home
root (hd0,0)
kernel /boot/kernel root=/dev/ram0 init=/linuxrc ramdisk=8192 real_root=/dev/sda2 softlevel=home
initrd /boot/initramfs

title on the road
root (hd0,0)
kernel /boot/kernel root=/dev/ram0 init=/linuxrc ramdisk=8192 real_root=/dev/sda2 softlevel=away
initrd /boot/initramfs

title maintenance
root (hd0,0)
kernel /boot/kernel root=/dev/ram0 init=/linuxrc ramdisk=8192 real_root=/dev/sda2 softlevel=maintenance
initrd /boot/initramfs

Now we add a new init script to the maintenance runlevel. A nice goodie: vi /etc/init.d/sysmaint already provides you with a nice template - just add some code. This is what my script looks like:

depend() {
        after *
}

start() {
        for script in `ls /etc/sysmaint/scripts/* | sort`
        do
                source ${script}
        done
}

As you can see, it simply executes all scripts placed in /etc/sysmaint/scripts in order. Caution: The script file names may not containe spaces because this breaks the iteration. The after * is perhaps not the cleanest way to ensure that this is the last init script run, but it works for me.

Now we need to create and populate the directory /etc/sysmaint/scripts. Here are some of my scripts.

# file 01_logrotate
ebegin "Rotating log files"
/usr/sbin/logrotate /etc/logrotate.conf
eend $?

Pretty straightforward. Don't forget to comment out the corresponding line in /etc/cron.daily/logrotate.cron.

# file 03_news_daily
ebegin "Expiring news articles"
/usr/sbin/texpire
eend $?

Obviously only of interest if you're using Leafnode. Again, don't forget to disable the corresponding cron job if you've enabled it (it's disabled by default).

# file 12_backup_cyrus_mailboxlist
#
# dumps the Cyrus IMAPD mailbox configuration to /var/backup/cyrus
#

# remove old backup files
ebegin "Cleaning up old Cyrus backup files"
mkdir -p /var/backup/cyrus
find /var/backup/cyrus -type f -ctime 14 -exec rm \{\} \;
eend $?

# create new backup
ebegin "Backing up Cyrus mailbox list"
su - cyrus -c "/usr/lib/cyrus/ctl_mboxlist -d" | bzip2 > /var/backup/cyrus/mboxlist-`date +%Y-%m-%d-%H-%M`
eend $?

For Cyrus users only - you get the idea.

# file 20_backup
ebegin "Backing up files"
/usr/sbin/bbackupctl -q sync
# wait until the backup is finished
/usr/sbin/bbackupctl -q wait-for-sync 2>&1 | grep -v "not in automatic mode"
eend 0

I use BoxBackup in snapshot mode. That's admittedly a crazy way to wait for a backup to finish, but it works quite well.

# file 30_portage_rsync
ebegin "Updating the Gentoo Portage repository"
/usr/bin/emerge --sync --quiet
eend $?

Note that this is only advisable if you've got a local rsync mirror.

# file 40_slocate_updatedb
ebegin "Updating the slocate file database"
/usr/bin/updatedb
eend $?

Again, don't forget to disable the cron job /etc/cron.daily/slocate.

# file 50_makewhatis
ebegin "Updating the 'whatis' index"
/usr/sbin/makewhatis
eend $?

And once again, disable /etc/cron.daily/makewhatis.

And finally...

# file 99_halt
/sbin/shutdown -h now

Now there's only one thing left to do - hook up the new init script in the new runlevel:

# chmod 0755 /etc/init.d/sysmaint
# update-rc add sysmaint maintenance

As you can see, there is very little magic to it. Adapt it to your needs and have fun.

Theme by Danetsoft and Danang Probo Sayekti inspired by Maksimer