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,

  1. Edit /etc/default/grub
  2. Use init= kernel parameter. See Linux kernel manual.
  3. Run update-grub -o /boot/grub/grub.cfg
  4. 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,

  1. 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 the observe something similar to the following, your path name is wrong,

    -bash: /home/brooklyn/guessmynumber: No such file or directory
    
  2. Edit /etc/default/grub. Replace the line
    GRUB_CMDLINE_LINUX_DEFAULT="quiet"
    

    by

    GRUB_CMDLINE_LINUX_DEFAULT="init=/home/brooklyn/guessmynumber"
    
  3. Make sure /usr/sbin is in PATH variable, for which, do this,
    export PATH=/usr/sbin:$PATH	
    
  4. 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
    $
    
  5. Reboot the Linux system, and observe what happens.