# Authors: grml-team (grml.org), (c) Klaus Knopper <knopper@knopper.net>, (c) Michael Prokop <mika@grml.org>
# Bug-Reports: see http://grml.org/bugs/
# License: This file is licensed under the GPL v2.
-# Latest change: Mon Apr 16 19:32:25 CEST 2007 [mika]
+# Latest change: Don Sep 06 23:57:02 CEST 2007 [mika]
################################################################################
# {{{ path, variables, signals, umask, zsh
# {{{ Check if we are running from the grml-CD or HD
INSTALLED=""
-[ -e /GRML/etc/grml_cd ] || INSTALLED="yes"
+[ -e /etc/grml_cd ] || INSTALLED="yes"
# testcd
TESTCD=""
}
# }}}
-# {{{ Check if we are in interactive startup mode
-INTERACTIVE=""
-stringinstring "BOOT_IMAGE=expert " "$CMDLINE" && INTERACTIVE="yes"
-# }}}
-
# {{{ set firmware timeout via bootparam
config_fwtimeout(){
if checkbootparam fwtimeout ; then
# write keyboard related variables to file for later use
[ -d /etc/sysconfig ] || mkdir /etc/sysconfig
- echo "KEYTABLE=\"$KEYTABLE\"" > /etc/sysconfig/keyboard
- echo "XKEYBOARD=\"$XKEYBOARD\"" >> /etc/sysconfig/keyboard
- echo "KDEKEYBOARD=\"$KDEKEYBOARD\"" >> /etc/sysconfig/keyboard
- echo "KDEKEYBOARDS=\"$KDEKEYBOARDS\"" >> /etc/sysconfig/keyboard
+ if ! [ -e /etc/sysconfig/keyboard ] ; then
+ echo "KEYTABLE=\"$KEYTABLE\"" > /etc/sysconfig/keyboard
+ echo "XKEYBOARD=\"$XKEYBOARD\"" >> /etc/sysconfig/keyboard
+ echo "KDEKEYBOARD=\"$KDEKEYBOARD\"" >> /etc/sysconfig/keyboard
+ echo "KDEKEYBOARDS=\"$KDEKEYBOARDS\"" >> /etc/sysconfig/keyboard
+ fi
fi
[ -r /etc/sysconfig/keyboard ] && . /etc/sysconfig/keyboard
# Set default keyboard before interactive setup
if [ -n "$KEYTABLE" ] ; then
- einfo "Running loadkeys for ${WHITE}${KEYTABLE}${NORMAL} in background"
- loadkeys -q $KEYTABLE &
- eend $?
+ einfo "Running loadkeys for ${WHITE}${KEYTABLE}${NORMAL} in background"
+ loadkeys -q $KEYTABLE &
+ eend $?
fi
# we have to set up all consoles, therefore loop it over all ttys:
config_timezone(){
# don't touch the files if running from harddisk:
if [ -z "$INSTALLED" ]; then
- einfo "Setting timezone."
- KTZ="$(getbootparam tz 2>>$DEBUG)"
- [ -f "/usr/share/zoneinfo/$KTZ" ] && TZ="$KTZ"
- rm -f /etc/localtime
- cp "/usr/share/zoneinfo/$TZ" /etc/localtime && eend 0
+ KTZ="$(getbootparam tz 2>>$DEBUG)"
+ if [ -n "$KTZ" ] ; then
+ einfo "Setting timezone."
+ [ -f "/usr/share/zoneinfo/$KTZ" ] && TZ="$KTZ"
+ rm -f /etc/localtime
+ cp "/usr/share/zoneinfo/$TZ" /etc/localtime ; eend $?
+ fi
fi
}
# }}}
# {{{ CD Checker
config_testcd(){
if [ -n "$TESTCD" ]; then
- einfo "Checking CD data integrity as requested by '${WHITE}testcd${NORMAL}' boot option."
- einfo "Reading files and checking against GRML/md5sums, this may take a while..."
- echo -n "${RED}"
- ( cd /cdrom/GRML/ ; rm -f /tmp/md5sum.log ; md5sum -c md5sums 2>&1 | tee /tmp/md5sum.log )
- if [ "$?" = "0" ]; then
- echo " ${GREEN}Everything looks OK${NORMAL}"
- else
- echo "${RED} *** CHECKSUM FAILED FOR THESE FILES: ***"
- egrep -v '(^md5sum:|OK$)' /tmp/md5sum.log
- echo "${RED} *** DATA ON YOUR CD MEDIUM IS POSSIBLY INCOMPLETE OR DAMAGED, ***${NORMAL}"
- echo "${RED} *** OR YOUR COMPUTER HAS BAD RAM. ***${NORMAL}"
- echo -n "${CYAN}Hit return to contine, or press the reset button to quit.${NORMAL}\a\a\a "
- read a
- fi
- eend 0
+ einfo "Checking CD data integrity as requested by '${WHITE}testcd${NORMAL}' boot option."
+ einfo "Reading files and checking against GRML/md5sums, this may take a while..."
+ echo -n "${RED}"
+
+ # /linuxrc grml version:
+ [ -d /cdrom/GRML ] && TESTCD_PATH=/cdrom/GRML
+ # live-initramfs version:
+ [ -d /live/image/GRML ] && TESTCD_PATH=/live/image/GRML
+
+ if [ -n "$TESTCD_PATH" ] ; then
+ ( cd "$TESTCD_PATH" ; rm -f /tmp/md5sum.log ; md5sum -c md5sums 2>&1 | tee /tmp/md5sum.log )
+ else
+ echo "${RED} *** Error: Could not find md5sum file. ***"
+ fi
+
+ if [ "$?" = "0" ]; then
+ echo " ${GREEN}Everything looks OK${NORMAL}"
+ else
+ echo "${RED} *** CHECKSUM FAILED FOR THESE FILES: ***"
+ egrep -v '(^md5sum:|OK$)' /tmp/md5sum.log
+ echo "${RED} *** DATA ON YOUR CD MEDIUM IS POSSIBLY INCOMPLETE OR DAMAGED, ***${NORMAL}"
+ echo "${RED} *** OR YOUR COMPUTER HAS BAD RAM. ***${NORMAL}"
+ echo -n "${CYAN}Hit return to contine, or press the reset button to quit.${NORMAL}"
+ read a
+ fi
+
+ eend 0
fi
}
# }}}
echo "alias $BLACK off" >> /etc/modprobe.d/grml
echo "# end entry generated by config_blacklist of grml-autoconfig" >> /etc/modprobe.d/grml ; eend $?
else
- eerror "No given module for blacklist found. Blacklisting will not work therefor."
+ eerror "No given module for blacklist found. Blacklisting will not work therefore."
fi
else
ewarn "Backlisting via bootoption does not work on harddisk installations." ; eend 1
config_acpi_apm(){
if [ -d /proc/acpi ]; then
if checkbootparam "noacpi"; then
- ewarn "Skipping ACPI Bios detection as requested on boot commandline." ; eend 0
+ ewarn "Skipping ACPI Bios detection as requested via noacpi on boot commandline." ; eend 0
+ elif checkbootparam "nogrmlacpi" ; then
+ ewarn "Skipping ACPI Bios detection as requested via nogrmlacpi on boot commandline." ; eend 0
else
- einfo "ACPI Bios found, activating modules: "
+ einfo "ACPI Bios found, activating modules (disable via bootoption noacpi / nogrmlacpi): "
eindent
found=""
for a in /lib/modules/$KERNEL/kernel/drivers/acpi/*; do
# {{{ Interactive configuration
config_interactive(){
-if [ -n "$INTERACTIVE" ] ; then
-einfo "Entering interactive configuration second stage."
-
- echo " ${GREEN}Your console keyboard defaults to: ${MAGENTA}${KEYTABLE}"
- echo -n "${CYAN}Do you want to (re)configure your console keyboard?${NORMAL} [Y/n] "
- read a
- [ "$a" != "n" ] && /usr/sbin/dpkg-reconfigure console-data ; eend $?
-
- echo -n "${CYAN}Do you want to (re)configure your soundcard?${NORMAL} [Y/n] "
- read a
- [ "$a" != "n" ] && alsaconf && ( exec aumix -m 0 >>$DEBUG 2>&1 & ) ; eend $?
-
- echo -n "${CYAN}Do you want to (re)configure your graphics (X11) subsystem?${NORMAL} [Y/n] "
- read a
- [ "$a" != "n" ] && xorgcfg -textmode ; eend $?
- echo " ${GREEN}Interactive configuration finished. Everything else should be fine for now.${NORMAL}"
-fi
-eend 0
+ ewarn "config_interactive is deprecated nowadays."
+ ewarn "Please set CONFIG_INTERACTIVE='no' in /etc/grml/autoconfig" ; eend 0
}
# }}}
config_fstab(){
NOSWAP="yes" # we do not use swap by default!
-if checkbootparam "swap" -o checkbootparam "anyswap" ; then
+if checkbootparam "swap" || checkbootparam "anyswap" ; then
NOSWAP=''
checkbootparam "anyswap" && export ANYSWAP='yes' || export ANYSWAP=""
fi
-if checkbootparam "nofstab" -o checkbootparam "forensic" ; then
+if checkbootparam "nofstab" || checkbootparam "forensic" ; then
ewarn "Skipping /etc/fstab creation as requested on boot commandline." ; eend 0
else
einfo "Scanning for harddisk partitions and creating /etc/fstab. (Disable this via boot option: nofstab)"
if grep -q $p /proc/swaps ; then
ewarn "Not using swap partition ${WHITE}${p}${NORMAL} as it is already in use." ; eend 0
else
+ if [ -b "$p" ] ; then
einfo "Using swap partition ${WHITE}${p}${NORMAL}."
swapon $p 2>>$DEBUG ; eend $?
+ else
+ ewarn "$p is not a valid block device - not using it therefore." ; eend 0
+ fi
fi
;;
esac # dd-check
# use a swapfile
if [ -z "$NOSWAP" ] ; then
- mount -o "$MOUNTOPTS" -t $f $p $m 2>>$DEBUG || continue
+ mount -o "$MOUNTOPTS" -t $f $p $m 2>>$DEBUG && MOUNTED=1 || continue
# Activate swapfile, if exists
SWAPFILE="$(/bin/ls -1d $m/[Gg][Rr][Mm][Ll].[Ss][Ww][Pp] 2>/dev/null)"
fi
if [ -z "$NOSWAP" -a -n "$SWAPFILE" -a -f "$SWAPFILE" ]; then
mount -o remount,rw $m && MOUNTED=1
if swapon "$SWAPFILE" 2>>$DEBUG ; then
- einfo "Using GRML swapfile ${SWAPFILE}."
+ eindent
+ einfo "Using GRML swapfile ${WHITE}${SWAPFILE}${NORMAL}."
+ eoutdent
fnew="$SWAPFILE swap swap defaults 0 0"
stringinfile "$fnew" "/etc/fstab" || echo "$fnew" >> /etc/fstab
GRML_SWP="$GRML_SWP $SWAPFILE"
sleep 1
eend 0
done
+ if [ -n "$INSTALLED" ] ; then
+ ewarn 'If you want to disable automatic DHCP requests set CONFIG_DHCP=no in /etc/grml/autoconfig.'
+ eend 0
+ fi
fi
}
# }}}
einfo "Detecting CPU: `awk -F: '/^processor/{printf " Processor"$2" is"};/^model name/{printf $2};/^vendor_id/{printf vendor};/^cpu MHz/{printf " %dMHz",int($2)};/^cache size/{printf ","$2" Cache"};/^$/{print ""}' /proc/cpuinfo 2>>$DEBUG` " ; eend 0
fi
+ # Disclaimer: sorry for the tons of if/then/else... but this makes sure we use:
+ # * it only if we have the according kernel modules available
+ # * cpufreq only on laptops (check via /usr/sbin/laptop-detect) and not inside Virtual Box
+ # * current version of /etc/init.d/loadcpufreq from Debian (to stay in sync)
+ # -> parse output of the initscript and output it according to our look'n'feel
+ # * our own cpufreq-detect.sh if /etc/init.d/loadcpufreq isn't present
if ! cpufreq_check ; then
ewarn "Skipping cpufreq setup as module dependencies are not fulfilled." ; eend 1
else
- if /usr/sbin/laptop-detect ; then
+ if /usr/sbin/laptop-detect 1>/dev/null 2>&1 ; then
+ # Virtual Box supports ACPI and laptop-detect returns with '0', so check for it:
+ if [ -r /proc/acpi/battery/BAT0/info ] ; then
+ if grep -q 'OEM info: innotek' /proc/acpi/battery/BAT0/info ; then
+ einfo 'Virtual Box detected, skipping cpufreq setup.' ; eend 0
+ return 0
+ fi
+ fi
einfo "Detected Laptop - trying to use cpu frequency scaling:"
- if [ -r /usr/bin/cpufreq-detect.sh ] ; then
- eindent
+ eindent
+ if [ -x /etc/init.d/loadcpufreq ] ; then
+ SKIP_CPU_GOVERNOR=''
+ LOADCPUFREQ=$(mktemp)
+ /etc/init.d/loadcpufreq start >"$LOADCPUFREQ" 2>&1 ; RC=$?
+ if grep -q FATAL "$LOADCPUFREQ" ; then
+ eindent
+ SKIP_CPU_GOVERNOR=1
+ oldIFS="$IFS"
+ IFS="
+"
+ for line in $(grep FATAL "$LOADCPUFREQ" | sed 's/.*FATAL: //; s/ (.*)//') ; do
+ eerror "$line" ; eend $RC
+ done
+ IFS="$oldIFS"
+ eoutdent
+ elif grep -q done "$LOADCPUFREQ" ; then
+ MODULE=$(grep done "$LOADCPUFREQ" | sed 's/.*done (\(.*\))./\1/')
+ if [ -n "$MODULE" -a "$MODULE" != none ]; then
+ einfo "Loading cpufreq kernel module $MODULE" ; eend 0
+ else
+ ewarn "Could not find an appropriate kernel module for cpu frequency scaling." ; eend 1
+ fi
+ fi
+ rm -f $LOADCPUFREQ
+ elif [ -r /usr/bin/cpufreq-detect.sh ] ; then
. /usr/bin/cpufreq-detect.sh
if [ -n "$MODULE" -a "$MODULE" != none ]; then
- einfo "Loading modules ${MODULE} and cpufreq_ondemand, setting ondemand governor"
+ einfo "Loading modules ${MODULE}"
modprobe "$MODULE" 1>>$DEBUG || modprobe "$MODULE_FALLBACK" 1>>$DEBUG
- if modprobe cpufreq_ondemand && RC=0 || RC=1 ; then
- for file in $(find /sys/devices/system/cpu/ -name scaling_governor 2>/dev/null) ; do
- echo ondemand > $file
- done
+ RC=$?
+ if [[ $RC == 0 ]]; then
+ eend 0
+ else
+ SKIP_CPU_GOVERNOR=1
+ eend $1
fi
- eend $RC
else
ewarn "Could not detect an appropriate CPU for use with cpu frequency scaling - skipping." && eend 1
+ fi # $MODULE
+ fi # loadcpufreq
+
+ if [ -z "$SKIP_CPU_GOVERNOR" ] ; then
+ einfo "Loading cpufreq_ondemand, setting ondemand governor"
+ if modprobe cpufreq_ondemand ; RC=$? ; then
+ for file in $(find /sys/devices/system/cpu/ -name scaling_governor 2>/dev/null) ; do
+ echo ondemand > $file
+ done
fi
- eoutdent
- fi # cpufreq-detect
+ eend $RC
+ fi # cpu-governor
+
+ eoutdent
+
fi # laptop-detect
fi # cpufreq_check
fi # checkbootparam nocpu
eerror "aumix binary not available. Can not set sound volumes therefore." ; eend 1
else
+ if ! [ -r /proc/asound/cards ] ; then
+ ewarn "No soundcard present, skipping mixer settings therefore." ; eend 0
+ return
+ fi
+
if checkbootparam vol ; then
VOL="$(getbootparam 'vol' 2>>$DEBUG)"
if [ -z "$VOL" ] ; then
ERROR=$(aumix -w $VOL -v $VOL -p $VOL -m $VOL 2>&1) ; RC=$?
if [ -n "$ERROR" ] ; then
eindent
- eerror "Problem setting mixer volumes: $ERROR"
+ eerror "Problem setting mixer volumes: $ERROR (no soundcard?)"
eoutdent
fi
eend $RC
ewarn "Skipping check for AC97 modem controller as requested on boot commandline." ; eend 0
else
if [ -x /etc/init.d/sl-modem-daemon ] ; then
- if lspci | grep Intel | grep -q "AC'97 Modem Controller" ; then
- einfo "AC97 modem controller detected. Starting sl-modem-daemon in background."
- /etc/init.d/sl-modem-daemon start >>$DEBUG &
- eend 0
- fi
+ if lspci | grep Intel | grep -q "AC'97 Modem Controller" ; then
+ einfo "AC97 modem controller detected. Start it running 'Start sl-modem-daemon'."
+ eend 0
+ fi
fi
fi
}
}
config_automount(){
-if checkbootparam noautoconfig -o checkbootparam forensic ; then
+if checkbootparam noautoconfig || checkbootparam forensic ; then
ewarn "Skipping running automount of device(s) labeled GRMLCFG as requested." ; eend 0
else
if [ -z "$INSTALLED" ] ; then
config_swraid(){
if [ -z "$INSTALLED" ] ; then
# notice: checkbootparam "forensic" is just for users who don't know how to really use the bootoption
- if checkbootparam 'noraid' -o checkbootparam 'noswraid' -o \
- checkbootparam 'forensic' -o checkbootparam 'raid=noautodetect' ; then
+ if checkbootparam 'noraid' || checkbootparam 'noswraid' -o \
+ checkbootparam 'forensic' || checkbootparam 'raid=noautodetect' ; then
ewarn "Skipping SW-RAID code as requested on boot commandline." ; eend 0
else
if ! [ -x /sbin/mdadm ] ; then
}
# }}}
+# {{{ LVM (Logical Volumes)
+config_lvm(){
+ if [ -z "$INSTALLED" ] ; then
+ # notice: checkbootparam "forensic" is just for users who don't know how to really use the bootoption
+ if checkbootparam 'nolvm' ; then
+ ewarn "Skipping LVM code as requested on boot commandline." ; eend 0
+ else
+ # Debian etch provides /etc/init.d/lvm only, newer suites provide /etc/init.d/lvm2
+ if ! [ -x /sbin/lvm -a -x /sbin/lvdisplay ] || ! [ -x /etc/init.d/lvm2 -o -x /etc/init.d/lvm ] ; then
+ eerror "LVM not available, can not execute it." ; eend 1
+ else
+ if lvdisplay 2>&1 | grep -v 'No volume groups found' 1>/dev/null 2>&1 ; then
+ einfo "You seem to have logical volumes (LVM) on your system."
+ eindent
+ einfo "Just run 'Start lvm2' to activate them or boot using 'lvm' as bootoption for autostart."
+ eend 0
+ if checkbootparam 'lvm' ; then
+ einfo "Bootoption LVM found. Searching for logical volumes:"
+ /etc/init.d/lvm2 start ; eend $?
+ fi
+ eoutdent
+ fi
+ fi # check for lvm binary
+ fi # check for bootoption nolvm
+ fi # run only in live-cd mode
+}
+# }}}
+
# {{{ debnet: setup network based on an existing one found on a partition
config_debnet(){
if checkbootparam "debnet" ; then
}
# }}}
+# {{{ check for broken ipw3945 driver which causes problems (especially on hd install)
+config_ipw3945() {
+ if grep -q ipw3945 /proc/modules ; then
+ if ! iwconfig 2>/dev/null| grep -qe 'IEEE 802' -qe 'unassociated' ; then
+ ewarn "Broken ipw3945 network interface found, reloading module."
+ rmmod ipw3945
+ modprobe ipw3945
+ eend $?
+ fi
+ fi
+}
+# }}}
+
# {{{ disable console blanking
config_blanking(){
if checkbootparam "noblank" ; then
if stringinstring "BOOT_IMAGE=debian2hd " "$CMDLINE" ; then
+einfo "Bootoption debian2hd found. Setting up environment for automatic installation via grml-debootstrap." ; eend 0
+
if ! [ -x /usr/sbin/grml-debootstrap ] ; then
+ eindent
eerror "Bootoption debian2hd found, but grml-debootstrap is not available." ; eend 1
- eerror "Dropping you to a shell, just exit it to continue booting." ; eend 1
- /bin/zsh
+ eoutdent
+ exit 1
fi
-if checkbootparam "partition" ; then
- PARTITION=''
- PARTITION="$(getbootparam 'partition' 2>>$DEBUG)"
+if checkbootparam "target" ; then
+ TARGET=''
+ TARGET="$(getbootparam 'target' 2>>$DEBUG)"
# notice: the following checks whether the given partition is available, if not the skip
# execution of grml-debootstrap as it might result in data loss...
- if ! [ -r $PARTITION ] ; then
- ewarn "Partition $PARTITION does not exist. Skipping execution of grml-debootstrap therefore." ; eend 1
+ if ! [ -r "$TARGET" ] ; then
+ eerror "Target $TARGET does not exist. Skipping execution of grml-debootstrap therefore." ; eend 1
fi
+else
+ eindent
+ eerror "No bootoption named target found, can not continue execution of grml-debootstrap." ; eend 1
+ eoutdent
+ exit 1
fi
if checkbootparam "grub" ; then
MIRROR="$(getbootparam 'mirror' 2>>$DEBUG)"
fi
+if checkbootparam "boot_append" ; then
+ BOOT_APPEND=''
+ BOOT_APPEND="$(getbootparam 'boot_append' 2>>$DEBUG)"
+fi
+
if checkbootparam "password" ; then
PASSWORD=''
PASSWORD="$(getbootparam 'password' 2>>$DEBUG)"
fi
# now check which options are available
-if [ -n "PARTITION" ] ; then
- PARTITIONCMD="--target $PARTITION"
+if [ -n "TARGET" ] ; then
+ TARGETCMD="--target $TARGET"
else
- PARTITIONCMD=''
- ewarn "Partion not set via bootoption. Skipping execution of grml-debootstrap therefore."; eend 1
+ TARGETCMD=''
+ eindent
+ eerror "Target not set via bootoption. Skipping execution of grml-debootstrap therefore."; eend 1
+ eoutdent
+ exit 1
fi
[ -n "$GRUB" ] && GRUBCMD="--grub $GRUB" || GRUBCMD=''
[ -n "$GROOT" ] && GROOTCMD="--groot $GROOT" || GROOTCMD=''
[ -n "$RELEASE" ] && RELEASECMD="--release $RELEASE" || RELEASECMD=''
[ -n "$MIRROR" ] && MIRRORCMD="--mirror $MIRROR" || MIRRORCMD=''
-[ -n "$PASSWORD" ] && PASSWORDCMD="--password $PASSWORD" ] || PASSWORDCMD=''
+[ -n "$PASSWORD" ] && PASSWORDCMD="--password $PASSWORD" || PASSWORDCMD=''
+[ -n "$BOOT_APPEND" ] && BOOT_APPEND="--boot_append $BOOT_APPEND" || BOOT_APPEND=''
# and finally write script and execute it
cat>|/usr/bin/grml-debootstrap_noninteractive<<EOF
#!/bin/sh
-AUTOINSTALL='yes' grml-debootstrap $PARTITIONCMD $GRUBCMD $GROOTCMD $RELEASECMD $MIRRORCMD $PASSWORDCMD
+AUTOINSTALL='yes' grml-debootstrap $TARGETCMD $GRUBCMD $GROOTCMD $RELEASECMD $MIRRORCMD $PASSWORDCMD $BOOT_APPEND
EOF
chmod 750 /usr/bin/grml-debootstrap_noninteractive
-einfo "Bootoption debian2hd found. Running automatic installation via grml-debootstrap." && eend 0
-screen /usr/bin/grml-debootstrap_noninteractive ; einfo "Invoking a shell, just exit to continue booting..." ; /bin/zsh
+screen /usr/bin/grml-debootstrap_noninteractive
+einfo "Invoking a shell, just exit to continue booting..."
+/bin/zsh
fi # stringinstring "BOOT_IMAGE=debian2hd
}
DISTRI="$(getbootparam 'distri' 2>>$DEBUG)"
if [ -r /cdrom/desktop/"$DISTRI".jpg ] ; then
[ -n "$BOOTDEBUG" ] && einfo "Debug: bootoption distri found and file /cdrom/desktop/$DISTRI present" && eend 0
+ # make sure the desktop.jpg file is not a symlink, so copying does not file then
+ [ -L /usr/share/grml/desktop.jpg ] && rm /usr/share/grml/desktop.jpg
cp /cdrom/desktop/"$DISTRI".jpg /usr/share/grml/desktop.jpg
fi
fi
# }}}
## END OF FILE #################################################################
-# vim:foldmethod=marker
+# vim:foldmethod=marker expandtab ai ft=zsh shiftwidth=3