#! /bin/sh ##### DO NOT ADD LINE BEFORE THIS ONE: --install depends on it VERSION=0.7 if ( echo $@ | grep -e "--version" ) 1> /dev/null; then echo "Installation script: $0" echo "suspend script (Version: $VERSION)" exit 0 fi # suspend - invokes software suspend # ChangeLog: # 0.7: - Fix auto bug in start services with 0.6-go1 version by Gunter Ohrner # : # - splitted SWSUSP_STOP_SERVICES_BEFORE_SUSPEND/auto into # SWSUSP_RESTART_SERVICES, SWSUSP_STOP_SERVICES_BEFORE_SUSPEND # and SWSUSP_START_SERVICES_AFTER_RESUME so you can "start but # not stop" or "stop but not start" scripts without loosing the # "smart" service restart facility (suspend.sh's "auto" # behaviour) # - Tweaked the init-script-call-order a bit such that # incorrectly switched script calls are avoided (I hope...) # - A bug in STOP_SERVICES-auto-mode introduced in 0.6 and caused # by merging my 0.5-go1 changes into the official script was # fixed/removed by these changes. # - cleaned up and sorted Debian GNU/Linux's RESTART_SERVICES # default list # 0.6: - Change pcmcia order for redhat (default stuff) # - Merge with 0.5-go1 version by Gunter Ohrner : # - Fix a bug introduced in RestartServices: one want to restart services in # reverse order in auto mode. # 0.5-go1: # - Check for all init scripts mentioned in the config file whether # they need to be called and call them in the order specified in the # runlevel configuration. # - Add support for Debian GNU/Linux # 0.5: - Fix for multiple ttys by server X (deleted) # - Fix uncorrect trick with nvidia option, deal with xauth, # and add more user friendly pop-up message # - Add an hdparm command to disable cache writing before suspend # 0.4: - Add a --version option # - Add the xinit /bin/false trick for NVidia cards # - Suppress vmware in defaults stopped services # - Add a sync call # - Reverse the default SWSUSP_SAVE_CLOCK_ON_SUSPEND to "no" because # if a problem occur and we don't restore clock on resume, then # we'll corrupt the CMOS clock. # - Use /bin/echo instead of builtin for suspending. It seems that # sometimes echo 4 > /proc/acpi/sleep never returns, so we execute # it in background. # - Test executables /etc/init.d scripts instead of readable. # - Modify Mandrake detection since drakconf place has changed. # - Add postfix and nfs to default stop services # - /dev/tty may not be available: adding a test and /dev/console output # 0.3: - Add version, fix typos (french message :-) and add changelog. # - Modify hw clock use. Always read on resume, but write before # suspend is configurable (to fix drift amplification problem) # - Add a diff in install phase when configuration file already exists # - Change gpm place in default start/stop services. # - Add a test for root id # - Add some udelays for proper unloading of modules # - Add a lock file to prevent two scripts from running together # - Add a configuration option for using acpi interface or # enable reboot instead of halt # - Change alsa place in default start/stop services, since usb # and pcmcia can use snd module to notify events. # - Add a check on running of some incompatible programs. # - Restore console font # - Add a --nosuspend option # - Modify --install procedure to suit different distros # 0.2: - Modification by Florent (fchabaud@free.fr): first version # published at sourceforge.net # - Add a --install option that installs the script and its # configuration file /etc/suspend.conf # - Add state control to exactly recover session when suspending # is aborted. # - Completely reorganize script (sorry Doug :-) # - suppress the smbfs and nfs unmounts (rationale: this # should be left to configuration - the user can set its samba # mount points if he/she wants them to be unmounted) # - suppress the grub/lilo alteration (rationale: I don't like # the idea that swsusp can modify the boot configuration of a machine ;-) # - modify the modules loading/unloading. The script now try to # unload all unused modules. On resume the specified modules # are loaded, whether they were unloaded or not (rationale: # this should be more robust). # 0.1: First script posted by Doug (doug@tunl.duke.edu) # PATH=/bin:/sbin:/usr/bin:/usr/bin/X11 TMP=/dev/shm/suspend.$$ if ! (touch $TMP) ;then TMP=/tmp/suspend.$$ if ! (touch $TMP) ;then echo "Can't open a temporary file in /dev/shm nor /tmp" echo "Aborting !" exit 1 fi fi ID=`id -u` if [ "$ID" != "0" ]; then echo "You must be root to use this script." echo "Use sudo to enable it for users." exit 1 fi if ( echo $@ | grep -e "--silent" ) 1> /dev/null; then VERBOSE='/dev/null' HWCLKDEBUG="" else if [ -w /dev/tty ] ;then VERBOSE='/dev/tty' else VERBOSE='/dev/console' fi HWCLKDEBUG="--debug" fi # This overides failsafe cancellation and forces suspend or installation if ( echo $@ | grep -e "--force" ) 1> /dev/null; then FORCE='yes' else FORCE='no' fi ######################################### INSTALL ##################################################### if ( echo $@ | grep -e "--install" ) 1> /dev/null; then DISTRIB=Unknown if [ -d /etc/rc.config.d ] ; then DISTRIB=SuSE elif [ -x /usr/X11R6/bin/drakconf -o -x /usr/sbin/drakconf ] ; then DISTRIB=Mandrake elif [ -f /etc/debian_version ] ; then DISTRIB=Debian fi BINNAME=suspend case "$DISTRIB" in SuSE) CONFIGFILE=/etc/rc.config.d/suspend.rc.config RLVLDIR=/etc/init.d INSTALLDIR=/usr/sbin IFDOWN=/etc/init.d/network OPTDOWN=stop IFUP=/etc/init.d/network OPTUP=start SHELL=/bin/bash LOCKFILE=/var/lock/subsys/swsusp SETSYSFONT=/etc/init.d/kbd OPTSYSFONT=start DEFAULTSTARTSERVICES= DEFAULTRESTARTSERVICES="postfix alsasound irda gpm xntpd inetd nfs dhclient network pcmcia usbmgr" DEFAULTSTOPSERVICES= SWSUSP_UMOUNTS="/floppy /cdrom /var/adm/mount" ;; Mandrake) CONFIGFILE=/etc/suspend.conf RLVLDIR=/etc/init.d INSTALLDIR=/usr/local/sbin IFDOWN=/etc/sysconfig/network-scripts/ifdown OPTDOWN= IFUP=/etc/sysconfig/network-scripts/ifup OPTUP= SHELL=/bin/bash LOCKFILE=/var/lock/subsys/swsusp SETSYSFONT=/sbin/setsysfont OPTSYSFONT= DEFAULTSTARTSERVICES= DEFAULTRESTARTSERVICES="postfix xntpd xinetd netfs nfs gpm pcmcia irda sound alsa usb" DEFAULTSTOPSERVICES= SWSUSP_UMOUNTS="/mnt/floppy /mnt/cdrom" ;; Debian) CONFIGFILE=/etc/suspend.conf RLVLDIR=/etc INSTALLDIR=/usr/local/sbin IFDOWN=/bin/true OPTDOWN= IFUP=/bin/true OPTUP= SHELL=/bin/sh LOCKFILE=/var/lock/subsys/swsusp SETSYSFONT=/etc/init.d/console-screen.sh OPTSYSFONT= DEFAULTSTARTSERVICES="mountnfs.sh modutils" DEFAULTRESTARTSERVICES="postfix exim ntpdate xntpd inetd xinetd noflushd sleepd anacron gpm pcmcia irda alsa setserial etc-setserial nfs-common networking ifupdown netenv laptop-net ipsec" DEFAULTSTOPSERVICES="umountnfs.sh" SWSUSP_UMOUNTS="/mnt /floppy /cdrom" ;; *) CONFIGFILE=/etc/suspend.conf RLVLDIR=/etc/init.d INSTALLDIR=/usr/local/sbin IFDOWN=/etc/sysconfig/network-scripts/ifdown OPTDOWN= IFUP=/etc/sysconfig/network-scripts/ifup OPTUP= SHELL=/bin/sh LOCKFILE=/var/lock/swsusp SETSYSFONT=/sbin/setsysfont OPTSYSFONT= DEFAULTSTARTSERVICES= DEFAULTRESTARTSERVICES="postfix xntpd inetd xinetd nfs pcmcia network gpm irda sound alsa alsasound kbd usb usbmgr" DEFAULTSTOPSERVICES= SWSUSP_UMOUNTS="/misc/floppy /misc/cd /mnt/floppy /mnt/cdrom /floppy /cdrom" ;; esac DATE=`date` cat > $TMP < /dev/null; then echo "Script: $0" echo "Installation: $DATE" echo "Distribution: $DISTRIB" echo "Version: $VERSION" exit 0 fi CONFIGFILE=$CONFIGFILE RLVLDIR=$RLVLDIR IFDOWN=$IFDOWN OPTDOWN=$OPTDOWN IFUP=$IFUP OPTUP=$OPTUP LOCKFILE=$LOCKFILE SETSYSFONT=$SETSYSFONT OPTSYSFONT=$OPTSYSFONT # DO NOT EDIT THIS FILE (change the above script and reinstall) EOF nb=`wc -l < $0` nb=$[ $nb - 2 ] tail -$nb $0 >> $TMP if [ ! -d $INSTALLDIR ] ;then echo "Directory $INSTALLDIR doesn't exist: installation aborted" rm $TMP exit 2 fi echo "Creating $INSTALLDIR/$BINNAME" > $VERBOSE if ! (mv -f $TMP $INSTALLDIR/$BINNAME) ;then echo "Cannot install $INSTALLDIR/$BINNAME" rm $TMP exit 1 fi chmod 700 $INSTALLDIR/$BINNAME # todo: Maybe create a small function to prevent this code duplication? SWSUSP_STOP_SERVICES_BEFORE_SUSPEND= for service in $DEFAULTSTOPSERVICES ;do if [ -x /etc/init.d/$service ] ; then SWSUSP_STOP_SERVICES_BEFORE_SUSPEND="$SWSUSP_STOP_SERVICES_BEFORE_SUSPEND $service" fi done SWSUSP_START_SERVICES_AFTER_RESUME= for service in $DEFAULTSTARTSERVICES ;do if [ -x /etc/init.d/$service ] ; then SWSUSP_START_SERVICES_AFTER_RESUME="$SWSUSP_START_SERVICES_AFTER_RESUME $service" fi done SWSUSP_RESTART_SERVICES= for service in $DEFAULTRESTARTSERVICES ;do if [ -x /etc/init.d/$service ] ; then SWSUSP_RESTART_SERVICES="$SWSUSP_RESTART_SERVICES $service" fi done cat > $TMP < # before suspension. # Default: none SWSUSP_DISABLE_HW_DISK_CACHE="" EOF if [ ! -f $CONFIGFILE -o "$FORCE" = "yes" ] ;then echo "Creating file $CONFIGFILE" > $VERBOSE mv $TMP $CONFIGFILE else echo "Configuration file $CONFIGFILE exists: not overwritten" > $VERBOSE diff -u $CONFIGFILE $TMP > $VERBOSE rm $TMP fi exit 0 fi ############################### END OF INSTALL ################################################### EXE=`basename $0` cat > $TMP < /dev/null; then cat $TMP rm $TMP exit 0 fi rm $TMP #Defining a few more command-line options... if ( echo $@ | grep -e "--kill" ) 1> /dev/null; then KILL_PROCS='yes' else KILL_PROCS='no' fi # This does everything except suspension if ( echo $@ | grep -e "--nosuspend" ) 1> /dev/null; then NOSUSPEND='yes' else NOSUSPEND='no' fi ############################################################# # Function definitions ############################################################# SERVICES= T_IFS= if ! which usleep > /dev/null ; then # fallback function if usleep-utility isn't available usleep() { # calculate sleep time in seconds sleeptime=$((($1)/100000)) [ $((($1) % 100000)) -ne 0 ] && $sleeptime=$(($sleeptime+1)) sleep $sleeptime } fi CheckProgs() { ret=0; if [ $KILL_PROCS = "yes" ]; then for sig in 15 9 ;do for prog in $SWSUSP_NON_COMPATIBLE_PROGS; do ps ax > $TMP pids=`awk '($5 == "'$prog'"){print $1}' $TMP` for pid in $pids; do echo "Killing($sig) $prog($pid)..." > $VERBOSE kill -$sig $pid 1> $VERBOSE 2> /dev/null usleep 500000 done done done fi for prog in $SWSUSP_NON_COMPATIBLE_PROGS; do ps ax > $TMP pids=`awk '($5 == "'$prog'"){print $1}' $TMP` if [ "$pids" != "" ];then echo "Exception program $prog: $pids" > $VERBOSE ret=1 fi done rm $TMP return $ret } SwitchFromX() { if [ "$SWSUSP_LEAVE_X_BEFORE_SUSPEND" != "yes" ]; then return 0; fi echo "Switching to console..." > $VERBOSE chvt 1 usleep 500000 # This to ensure usb mouse is released by X return $? } SwitchToX() { if [ "$SWSUSP_LEAVE_X_BEFORE_SUSPEND" = "nvidia" ]; then export HOME=/dev/shm xauth -v < $VERBOSE # This idea is extracted from the SuSE script /usr/bin/wttyhx # +k will make the list by ascending time usage ps axO+k | grep "X " > $TMP pids=`grep -v grep $TMP | awk '{print $1}'` rm $TMP tty=1 # if we don't find an X we remain on VT1 for pid in $pids ;do tty=`ls -l /proc/${pid}/fd | sed -ne '/^.*\/dev\/tty/s///p'` done # The last tty should be the X server with larger time usage chvt $tty return $? } ExecuteServices() { ret=0 # What action to execute on init scripts? startstop="$1" shift # What scripts to execute at all? services_to_execute=" $@ " runlevel=`runlevel | cut -d\ -f2` # only start/stop services really active in this runlevel and # do it in the correct order if [ "$startstop" = "start" ] ; then startscripts="$RLVLDIR/rcS.d/S* $RLVLDIR/rc$runlevel.d/S*" elif [ "$startstop" = "stop" ] ; then startscripts="$RLVLDIR/rc$runlevel.d/S*" # stop services - reverse the order in which the scripts are called for script in $startscripts ; do tmp_scriptlist="$script $tmp_scriptlist" done # (order of rc0.d/S* scripts *must not* be reversed!) startscripts="$tmp_scriptlist $RLVLDIR/rc0.d/S*" fi # execute all needed init scripts for startscript in $startscripts ; do service=${startscript##*/S??} # strip off directory and startup sequence number if [ -z "${services_to_execute##* $service *}" ] ; then # service is active, start/stop it test -x /etc/init.d/"$service" || continue /etc/init.d/"$service" "$startstop" || ret=$? # remember the last error code fi done return $ret # return the last error code } StopServices() { ExecuteServices "stop" "$SWSUSP_RESTART_SERVICES $SWSUSP_STOP_SERVICES_BEFORE_SUSPEND" return 0 # we don't want to abort if stopping one of the services failed } RestartServices() { ExecuteServices "start" "$SWSUSP_RESTART_SERVICES $SWSUSP_START_SERVICES_AFTER_RESUME" return $? } UmountDevices() { ret=0 if [ $KILL_PROCS = "yes" ]; then for MNT in $SWSUSP_UMOUNTS; do if [ `grep -c " $MNT " /proc/mounts` != 0 ]; then echo "Terminating users of $MNT..." > $VERBOSE fuser -s -k -15 $MNT 1> $VERBOSE 2> /dev/null fi done #sleep a few seconds before SIGKILL and umount sleep 3 fi #Now kill procs and do umount for MNT in $SWSUSP_UMOUNTS; do if [ `grep -c " $MNT " /proc/mounts` != 0 ]; then if [ $KILL_PROCS = "yes" ]; then echo "Sending SIGKILL to remaining users of $MNT" > $VERBOSE fuser -s -k -9 $MNT 1> $VERBOSE 2> /dev/null fi echo "Unmounting $MNT" > $VERBOSE # make sure everything really is being unmounted if ! ( umount $MNT ); then ret=1 fi fi done return $ret } RemountDevices() { ret=0 for MNT in $SWSUSP_REMOUNT ;do if [ `grep -c " $MNT " /proc/mounts` = 0 ]; then echo "Mounting $MNT..." > $VERBOSE /bin/mount $MNT fi done } StopInterfaces() { ret=0 for IF in $SWSUSP_DOWNIFS; do if [ `ifconfig | grep -c "^$IF "` != 0 ]; then if [ ! -x $IFDOWN ] ;then echo "No script $IFDOWN !!!" return 2 else echo "Bringing $IF down..." > $VERBOSE if ! ($IFDOWN $OPTDOWN $IF) ;then echo "Bringing $IF down failed !" ret=1 else T_IFS="$IF $T_IFS" fi fi fi done return $ret } RestartInterfaces() { ret=0 if [ "$SWSUSP_UPIFS" = "auto" ] ;then SWSUSP_UPIFS="$T_IFS" fi for IF in $SWSUSP_UPIFS ;do echo "Bringing up $IF..." > $VERBOSE if ! ($IFUP $OPTUP $IF) ;then ret=1 fi done return $ret } UnloadModules() { if [ "$SWSUSP_UNLOAD_MODULES_BEFORE_SUSPEND" != "yes" ] ;then return 0 fi cat /proc/modules > $VERBOSE modules=`awk '($3==0){print $1}' /proc/modules` for module in $modules ;do echo "Removing $module" > $VERBOSE modprobe -r $module usleep 100000 done modaft=`awk '($3==0){print $1}' /proc/modules` while [ "$modaft" != "$modules" ] ; do cat /proc/modules > $VERBOSE modules=$modaft usleep 500000 for module in $modules ;do echo "Removing $module" > $VERBOSE modprobe -r $module usleep 100000 done modaft=`awk '($3==0){print $1}' /proc/modules` done echo "Loaded modules:" > $VERBOSE cat /proc/modules > $VERBOSE } ReloadModules() { ret=0 for MOD in $SWSUSP_INSERTMODS ;do echo "Loading $MOD..." > $VERBOSE if ! (modprobe $MOD) ;then ret=1 fi done return $ret } Suspend() { ret=0 if [ "$SWSUSP_SAVE_CLOCK_ON_SUSPEND" = "yes" ] ;then echo "Saving clock state..." > $VERBOSE hwclock --systohc $HWCLKDEBUG sleep 1 #This is to be sure that the clock syncing is done fi if [ -x $SETSYSFONT ];then chvt 1 fi sync if [ "$NOSUSPEND" = "yes" ] ;then echo "Suspension system call not performed..." > $VERBOSE if [ "$SWSUSP_FORCE_SUSPEND_MODE" = "acpi" -o "$SWSUSP_FORCE_SUSPEND_MODE" = "ACPI" ];then echo "/bin/echo 4 > /proc/acpi/sleep" > $VERBOSE else echo "/bin/echo \"1 $SWSUSP_FORCE_SUSPEND_MODE\" > /proc/sys/kernel/swsusp" fi else if [ -w "$SWSUSP_DISABLE_HW_DISK_CACHE" ] ;then echo "Disabling hardware disk cache on $SWSUSP_DISABLE_HW_DISK_CACHE" > $VERBOSE hdparm -W 0 $SWSUSP_DISABLE_HW_DISK_CACHE fi echo "Suspension system call in progress..." > $VERBOSE if [ "$SWSUSP_FORCE_SUSPEND_MODE" = "acpi" -o "$SWSUSP_FORCE_SUSPEND_MODE" = "ACPI" ];then if ! (/bin/echo 4 > /proc/acpi/sleep &) ;then ret=1 fi else if ! (/bin/echo "1 $SWSUSP_FORCE_SUSPEND_MODE" > /proc/sys/kernel/swsusp) ;then ret=1 fi fi fi if [ $ret = 0 ] ;then sleep 5 ps ax > $TMP pids=`awk '($5 == "/bin/echo"){print $1}' $TMP` for pid in $pids; do echo "Killing($sig) $prog($pid)..." > $VERBOSE #kill -$sig $pid 1> $VERBOSE 2> /dev/null usleep 500000 done fi if [ -x $SETSYSFONT ];then chvt 1 $SETSYSFONT $OPTSYSFONT if [ "$SWSUSP_LEAVE_X_BEFORE_SUSPEND" != "yes" ];then SwitchToX force fi fi return $ret } RestoreClock() { ret=0 echo "Restoring clock..." > $VERBOSE hwclock --hctosys $HWCLKDEBUG ret=$? sleep 1 #This is to be sure that the clock syncing is done return $ret } RESUME() { state=$1 resumestate=6 if [ $state -ge $resumestate ];then echo "Resuming (suspend script vers. $VERSION)..." > $VERBOSE RestoreClock fi resumestate=$[ $resumestate - 1 ] if [ $state -ge $resumestate ];then #echo "Reload modules..." > $VERBOSE ReloadModules fi resumestate=$[ $resumestate - 1 ] if [ $state -ge $resumestate ];then #echo "Starting interfaces..." > $VERBOSE RestartInterfaces fi resumestate=$[ $resumestate - 1 ] if [ $state -ge $resumestate ];then #echo "Remounting devices..." > $VERBOSE RemountDevices fi resumestate=$[ $resumestate - 1 ] if [ $state -ge $resumestate ];then #echo "Restarting services..." > $VERBOSE RestartServices fi resumestate=$[ $resumestate - 1 ] if [ $state -ge $resumestate ];then SwitchToX fi resumestate=$[ $resumestate - 1 ] # resumestate should be zero at this point return $resumestate } FAIL(){ if [ $FORCE = "yes" ]; then # Then continue anyway. echo echo "WARNING...FAILSAFES OVERIDDEN..." echo "Do not mount shared drives from other OS's while suspended" echo else echo echo " ********* SUSPEND CANCELLED *********" echo RESUME $1 rm $LOCKFILE exit 255 fi } SUSPEND() { state=0 echo "Preparing to suspend (suspend script vers. $VERSION)..." > $VERBOSE if ! CheckProgs ;then FAIL $state fi if ! SwitchFromX ;then FAIL $state fi state=$[$state + 1] #echo "Stopping services..." > $VERBOSE if ! StopServices ;then FAIL $state fi state=$[$state + 1] #echo "Unmounting devices..." > $VERBOSE if ! UmountDevices ;then FAIL $state fi state=$[$state + 1] #echo "Stopping interfaces..." > $VERBOSE if ! StopInterfaces ;then FAIL $state fi state=$[$state + 1] #echo "Unloading modules..." > $VERBOSE if ! UnloadModules ;then FAIL $state fi state=$[$state + 1] if ! Suspend ;then FAIL $state fi state=$[$state + 1] return $state } ############################################################# # Main ############################################################# if [ -f $LOCKFILE ] ;then pid=`cat $LOCKFILE` if (ps $pid > /dev/null 2> /dev/null) ;then echo "File $LOCKFILE is present. Suspension script `cat $LOCKFILE`" echo "should be in progress. If this is not the case, then erase" echo "this file" exit 1 fi fi echo $$ > $LOCKFILE SUSPEND state=$? RESUME $state ret=$? rm $LOCKFILE exit $ret