Table of Content
Experimental Environment
We use a Debian Linux system on an Oracle Virtual Machine as our experimental environment.
The init Program
When Linux boots, the bootstrap program loads and transfers the control of the
CPU to the Linux kernel. The Linux kernel in terms loads a program, by default
the init
program. For instance, on a Debian 10 Linux system, we can list
the program,
$ ls -l /sbin/init
We can run our own init
program and you can place the program at any
directory. We inform the Linux kernel the path of the program by a
kernel parameter. On the Debian 10 Linux system with the Grub bootloader,
the general steps are,
- Edit
/etc/default/grub
- Use
init=
kernel parameter. See Linux kernel manual. - Run
update-grub -o /boot/grub/grub.cfg
- Reboot
Running a Custom init Program
Let’s say that we want the Linux system runs a simple game, called “Guess the number”, but nothing else when it boots. Follow the steps below.
Creating a Linux VM Clone
If the disk space is a concern, you can create a linked clone of the Linux VM. A linked clone essentially stores the “delta” or the difference from (a snapshot of) the VM it cloned from. The rest of the steps are on the clone. The advantage of using a clone is that we can easily remove and recreate one if we have done irreparable damage to the clone.
Creating the Game Program
On the clone, create, compile, and test-run the guessthenumber.c
whose
content is as follows,
/*
* guessmynumber.c
*
* To compile:
* gcc -Wall guessmynumber.c -o guessmynumber
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void guessmynumber();
int main() {
int loop = 1;
size_t size = 0;
char *line = NULL;
srandom(time(NULL));
while (loop) {
printf(
"I have a number in my mind. \n"
"The number is between 0 and 1024.\n"
"Guess what it is. \n"
"I'll tell you your guess is bigger or smaller than the number.\n"
"You are the winner if you guess it as few as possible!\n");
guessmynumber();
while (1) {
printf("Do you want to play again? (Y/N)\n");
getline(&line, &size, stdin);
if (strncasecmp(line, "Y", 1) == 0) {
break;
}
else if (strncasecmp(line, "N", 1) == 0) {
loop = 0;
break;
}
}
}
free(line);
/* exit to shell */
system("/bin/bash");
}
void guessmynumber() {
int leastguesses = ceil(log(1024)/log(2));
int number;
int guess;
int nguesses;
size_t size;
int loop;
char *line;
nguesses = 0;
loop = 1;
number = (double)random()/(double)(RAND_MAX) * 1024;
printf("number = %d\n", number);
while(loop) {
printf("What is your guess:\n");
line = NULL;
size = 0;
getline(&line, &size, stdin);
if (sscanf(line, "%d", &guess) > 0) {
nguesses ++;
printf("Your guess is %d\n", guess);
if (guess > number) {
printf(
"Your guess is too big\n"
"Remember my number is between 0 and 1024. Try again\n");
} else if (guess < number) {
printf(
"Your guess is too small\n"
"Remember my number is between 0 and 1024. Try again\n");
} else {
printf(
"Bingo! You did it in %d guesses.\n"
"My number is indeed %d\n", nguesses, number);
loop = 0;
}
if (nguesses > leastguesses) {
printf(
"Sorry. You failed. "
"Other people can guess it no more than %d gueeses\n",
leastguesses);
loop = 0;
}
}
free(line);
}
}
Configuring Grub to Run the Game at Boot
Liked discussed, do these,
- Figure out the absolute path name to
guessmynumber
program. In this example, assume it is/home/brooklyn/guessmynumber
. To verify it, you can run the program using the path, e.g.,$ /home/brooklyn/guessmynumber I have a number in my mind. The number is between 0 and 1024. Guess what it is. I'll tell you your guess is bigger or smaller than the number. You are the winner if you guess it as few as possible! number = 486 What is your guess: ^C $
If you observe something similar to the following, your path name is wrong,
-bash: /home/brooklyn/guessmynumber: No such file or directory
- Edit
/etc/default/grub
. Replace the lineGRUB_CMDLINE_LINUX_DEFAULT="quiet"
by
GRUB_CMDLINE_LINUX_DEFAULT="init=/home/brooklyn/guessmynumber"
- Make sure
/usr/sbin
is inPATH
variable, for which, do this,export PATH=/usr/sbin:$PATH
- Run
update-grub -o /boot/grub/grub.cfg
. If you save a backup of the original/boot/grub/grub.cfg
, e.g., as/boot/grub/grub.cfg.bu
, we can figure out the effect the change. For instance,$ diff /boot/grub/grub.cfg /boot/grub/grub.cfg.bu 119c119 < linux /boot/vmlinuz-4.19.0-14-686 root=UUID=9031b7f8-4042-4555-be25-874dcbad8aa6 ro init=/home/brooklyn/guessmynumber --- > linux /boot/vmlinuz-4.19.0-14-686 root=UUID=9031b7f8-4042-4555-be25-874dcbad8aa6 ro quiet 137c137 < linux /boot/vmlinuz-4.19.0-14-686 root=UUID=9031b7f8-4042-4555-be25-874dcbad8aa6 ro init=/home/brooklyn/guessmynumber --- > linux /boot/vmlinuz-4.19.0-14-686 root=UUID=9031b7f8-4042-4555-be25-874dcbad8aa6 ro quiet 171c171 < linux /boot/vmlinuz-4.19.0-13-686 root=UUID=9031b7f8-4042-4555-be25-874dcbad8aa6 ro init=/home/brooklyn/guessmynumber --- > linux /boot/vmlinuz-4.19.0-13-686 root=UUID=9031b7f8-4042-4555-be25-874dcbad8aa6 ro quiet $
- Reboot the Linux system, and observe what happens.