Upstart script: Detect shift key down at boot

I want to create a boot up potential which allows a different upstart/runlevel configurations to load based upon specific key downs at boot (or combos). How do I detect a key down event with an upstart script?

I'm on 10.04 if that helps.

Alternative methods to achieve the same result are acceptable, i.e., How can I use grub to do this?

To help provide more clarity, I am creating a step by step instruction based upon both answers given by Tuminoid and Lumbric. (Please help me improve this, my interpretation of Tuminoid's answer does not work.)

Note: Everyone must have a reason for doing this. To give meaning to the following two Step-by-Step Solutions, I'll describe my purpose for wanting to have upstart scripts decided at boot.

I am creating a Live DVD for surfing the Internet called Surfer. For security, I want to disable the admin account and leave a non-privileged account for surfing the web. I also want to give myself and users the ability to remaster Surfer so they can add more online apps and customise their Surfer account, then burn their own live custom Surfer iso. So I will need two boot profiles: Surfer and Remaster. One for secure online surfing, the other for remastering and development.

Lumbric's Solution

Create a Custom grub File

1. To add custom entries to grub, open this file with gedit for editing:

gksudo gedit /etc/grub.d/40_custom

You will have the right file if you see this text in it:

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.

2. In another gedit tab, open:

/boot/grub/grub.cfg

3. In grub.cfg, look for sections that begin with:

menuentry

Choose the default entry you use for booting. If you do not know the default entry by looking at this file, then reboot and take a note of the one you pick at boot. Do not save any changes if you have to reboot to find your default entry. When you are positive of your default entry, keep note that this is the entry to use for your custom profiles.

Each menuentry section will have some corresponding set of brackets:

menuentry ...text left out... {
  ...text left out...

}

that encapsulate a few lines of text. Copy the your default entry section. Copy from the word menuentry to the closing bracket. I will need two entries, so I will paste my default entry twice.

It should look something like the following for two custom boot profile entries. (Keep in mind I took out text to make this easier to read):

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries.  Simply type the
# menu entries you want to add after this comment.  Be careful not to change
# the 'exec tail' line above.

menuentry ...text left out... {
    ...text left out...

}

menuentry ...text left out... {
    ...text left out...

}

4. Find your boot line by pressing ctrl + f. Type in the search field:

linux /boot

Each boot line will should be highlighted.

5. Cut and paste this text at the end of the highlighted boot line, but change the number for each entry.

boot-profile=1

After pasting, the menu entry should look similar to this:

menuentry ...text left out... {
    ...text left out...
    linux /boot/vmlinuz-2.6.35-22-generic root=UUID=66cdrfwec91-0070-32f-b666-c9f2232j23232j ro quiet splash boot-profile=1
}

menuentry ...text left out... {
    ...text left out...
    linux /boot/vmlinuz-2.6.35-22-generic root=UUID=66cdrfwec91-0070-32f-b666-c9f2232j23232j ro quiet splash boot-profile=2
}

6. Now, we will edit the line we see at boot. For the menu entry identified above, edit the text that is between the word menuentry and the first bracket. Replace with the text Surfer:

menuentry "Surfer" {
    ...text left out...
    linux /boot/vmlinuz-2.6.35-22-generic root=UUID=66cdrfwec91-0070-32f-b666-c9f2232j23232j ro quiet splash boot-profile=1
}

Do the same for Remaster entry:

menuentry "Remaster" {
    ...text left out...
    linux /boot/vmlinuz-2.6.35-22-generic root=UUID=66cdrfwec91-0070-32f-b666-c9f2232j23232j ro quiet splash boot-profile=2
}

7. Save the file 40_custom.

8. Now, update grub with this command:

sudo update-grub

Note: If you do any upgrading or installation of programs that modify your kernel, be aware of lumbric's point made in his link. How can we know if our kernel is going to be upgraded? Help me clarify how we can prevent upgrading our kernel without knowing it.

9. Reboot. When at the boot menu, look for your new entries. If you see them, things are going great so far. Choose one of your custom entries and hit enter.

10. When logged in, open up Terminal. Enter this command.

cat /proc/cmdline

Grub writes the boot line to this file. The boot line is where you added boot-profile=1 at the end. So, if you chose surfer, then it should read boot-profile=1 at the end or boot-profile=2 if you chose Remaster. If not, reread from step one and correct any logical errors.

11. Now, the script must be written that will handle the boot entry chosen from grub menu. I'm choosing to use an upstart script, because I know how to do that. The upstart will start as soon as possible, check the contents of /proc/cmdline and use that to determine our choice at the boot menu. Here is the upstart start script:

/etc/bootChoice.conf

12. Past the following text in:

task
start on startup or bootChoice

script

profile=$(cat /proc/cmdline |sed 's/.*boot-profile=\([0-9]\).*/\1/g')

case $profile in
   1)
      # Run script for Surfer Profile
      initctl emit surferboot
      ;;
   2)
      # Run script for Remaster Profile
      initctl emit remasterboot
      ;;

   *)
      echo Error
      echo "Error" > /error
      ;;
esac

end script

13. Now, a signal will be emitted based on our choice. In order to handle each signal, two files must be created. One called: surfer.conf and another called remaster.conf. Place them in /etc/init/. Their contents will be similar to this:

# surfer user boot

task
start on surferboot

script
    echo "surfer test-data" > /home/surfer/surfer
end script

We can see that when the surferboot signal is emitted, that the script above is started determined by this line: start on surferboot Place all custom boot script code between script and end script. Same thing will be done for Remaster.

I tried this, it works. Thanks Lumbric.

Tuminoid's Solution

First, we must let grub know we want to call a script. We will place the path of the script inside /etc/default/grub.

1. Open the file: /etc/default/grub.

gksudo gedit /etc/default/grub

2. Add this line at the end of the file:

init=/sbin/preinit

3. Save and exit. Then, run this command:

update-grub

Now we will create the file that we made grub aware of.

4. Create the file with gedit:

gksudo gedit /sbin/preinit

5. Paste the following text inside:

#!/bin/sh 
EVENT="startup" 
while [ /bin/true ]; do   
  echo "Select boot:"   
  echo " 1) Surfer"   
  echo " 2) Remaster"   
  echo ""   
  read selection   
  case "$selection" in 
1)   
  EVENT="surfer"   
  ;; 
2)   
  EVENT="remaster"   
  ;;
*)   
  ;;   
  esac 
done

exec /sbin/init --startup-event=$EVENT

6. Make the script executable:

chmod +x /sbin/preinit

WIP NOTE: I've tried this up to this point and it does not work. Please help improve this interpretation of the answer by pointing out where I am going wrong.

Last Section: This section unfinished. Waiting for clarity on first steps to finish.

The events above are upstart signals. If you haven't used upstart signals, they will call a script in the directory: /etc/init. So, I will make two files in /etc/init called:

/etc/init/surfer.conf

/etc/init/remaster.conf

In these files I will use bash scripts to configure each boot.

I will create at upstart /etc/gdm/custom.conf to force an auto-login if Surfer is chosen. I will also open /etc/shadow and disable remasters password.

Alternatively, I will create /etc/gdm/custom.conf for a forced auto-login to the user remaster for remastering and development.


Solution 1:

To catch a Shift key down event during boot seems to be an interesting task, but I have to no idea how to achieve this. But I described in a recent Q&A how to use the GRUB menu in order to run different start up scripts after login.

The script from Step 3 is meant to run after login, but it can be placed also in /etc/rcXY.

Solution 2:

You cannot do that for an Upstart script. They are non-interactive by nature.

What you could do is make a Grub menu entry that points to a a shell script as an init, prompt you for a type of boot you want (or interact with you in anyway you like) and then at the end do exec /sbin/init --startup-event=<type of boot>. This causes Upstart's startup event be something else than startup and you can build your boot-up to whatever you like.

For example, put something like this into /sbin/preinit:

#!/bin/sh 
EVENT="startup" 
while [ /bin/true ]; do   
  echo "Select boot:"   
  echo " 1) Normal"   
  echo " 2) Minimal"  
  echo " 3) Foobar"  
  echo ""   
  read selection   
  case "$selection" in 
2)   
  EVENT="minimal-startup"   
  ;; 
3)   
  EVENT="foobar-startup"   
  ;;
*)   
  ;;   
  esac 
done

exec /sbin/init --startup-event=$EVENT

Using this solution, you don't need to copy /etc/init around, but can have nice static set of jobs, that just execute based on different start signal.