Announcement

Collapse
No announcement yet.

Adding an ISO to grub

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Adding an ISO to grub

    [EDIT] The addiso.sh code is now the "final" version. It's still in "testing mode, easy to make "live".
    The latest version will always be at https://sourceforge.net/projects/grub-iso-adder/ not much use updating it everywhere.

    Being slightly bored with USB sticks, SD cards and the like to install distros,
    and to celebrate the release of Kubuntu 20.04,
    I'm trying to make it easy to add a downloaded ISO to the grub menu so it can be booted - and installed - without any external medium.
    So I made a little "shortcut" to do that.

    It has two parts: A Dolphin Service Menu and a shell script.
    The Service Menu calls the script and passes the name of the ISO to it.
    So, first you take this code:
    Code:
    [Desktop Entry]
    Type=Service
    Icon=application-x-cd-image
    X-KDE-ServiceTypes=KonqPopupMenu/Plugin
    MimeType=application/x-cd-image;model/x.stl-binary;
    Actions=addiso;
    X-KDE-Priority=TopLevel
    Encoding=UTF-8
    
    [Desktop Action addiso]
    Name=Add ISO to grub
    Icon=application-x-cd-image
    Exec=konsole --hold -e ~/.local/share/kservices5/ServiceMenus/addiso.sh "%f"
    And save it as addiso.desktop in ~/.local/share/kservices5/ServiceMenus (or wherever you keep your service menus, but on later versions of Kubuntu/neon it's probably there).

    Then you take this one:
    Code:
    #!/bin/bash
    
    ########### This script will add an entry to your grub boot menu for the ISO selected by the Dolphin Service Menu ##########
    ########### It will not work if not called from it ##########
    ###########
    ########### This is the experimental version ###########
    ########### To actually make it work, comment out the "echo "$grubentry" >>customentry.txt" line ##########
    ########### and uncomment the following one  ###########
    ########### Do the same for the"ls -la" one  ###########
    isoentry="$1";isoshort="${isoentry##*/}"
    echo A couple of checks:
    
    FILE=$(which isoinfo)
    if [ -f "$FILE" ]; then
    echo "$FILE exist." $'\e[0;36m' "OK."$'\e[0;37m'
    else 
    echo "isoinfo is" $'\e[0;31m' "not installed." $'\e[0;37m' "Please [sudo apt] install genisoimage and run this again.";exit
    fi
    echo
    part=$(df -P . | sed -n '$s/[[:blank:]].*//p');p1=${part:7:1};p2=${part:8:1};p3=$(tr abcdefghij 0123456789 <<< "$p1");p4="hd"$p3,$p2
    ptype=${part:5:1}
    case $ptype in 
    n) p1=${part:9:1};p2=${part:13:1};p4="hd"$p1,$p2                   ;;
    s) p1=${part:7:1};p2=${part:8:1};p3=$(tr abcdefghij 0123456789 <<< "$p1");p4="hd"$p3,$p2                         ;;
    esac
    echo The partition your ISO is on is $'\e[0;32m' $part $'\e[0;37m'
    echo "grub notation": $p4
    echo
    echo
    grbv=$(grub-install --version  | awk '/(GRUB)/ {print substr($3,4,1)}')
    case $grbv in 
    2) echo Your grub version is $'\e[0;32m' "2.0"$grbv". OK." $'\e[0;37m'
    lz=$(isoinfo -l -i $isoentry |grep -i initrd | awk '{ print substr($NF, 1, length($NF)-2)}' | tr '[A-Z]' '[a-z]')
    grubentry="$(cat <<-EOF
    #
    #
    #!/bin/sh
    exec tail -n +3 \$0
    menuentry "$isoshort" {
        set isofile="$isoentry"
        loopback loop ($p4)\$isofile
        linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=\$isofile noprompt noeject toram
        initrd (loop)/casper/$lz
    }
    EOF
    )"              ;;
    4) echo Your grub version is $'\e[0;33m' "2.0"$grbv". Grub 2.04 is known to have problems with loopack devices." $'\e[0;37m'
    isoentry="$1"
    lz=$(isoinfo -l -i $isoentry |grep -i initrd | awk '{ print substr($NF, 1, length($NF)-2)}' | tr '[A-Z]' '[a-z]')
    grubentry="$(cat <<-EOF
    #
    #
    #!/bin/sh
    exec tail -n +3 \$0
    menuentry "$isoshort" {
        set isofile="$isoentry"
        rmmod tpm
        loopback loop ($p4)\$isofile
        linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=\$isofile noprompt noeject toram
        initrd (loop)/casper/$lz
    }
    EOF
    )"             ;;
    ###read -rsn1 -p"Press any key to continue";echo ;;
    esac
    
    echo $'\e[0;36m'Adding entry to /etc/grub.d/40_custom...
    #echo "$grubentry" | sudo tee -a /etc/grub.d/40_custom ## This is the actual line
    echo "$grubentry" >>~/.local/share/kservices5/ServiceMenus/customentry.txt ## This is the line for testing purposes :·)
    echo $'\e[0;36m'Done
    echo Entry added to /etc/grub.d/40_custom
    echo
    #echo The added entry was:
    #echo $'\e[0;37m'
    #tail -10 ~/.local/share/kservices5/ServiceMenus/customentry.txt
    #echo $'\e[0;36m'
    echo
    while true; do
    read -p "Does that look correct? y/n: " yn
    case $yn in
        [Nn]* ) echo Close the window to exit.;exit;;
        [Yy]* ) echo "OK. Let's update grub then."; break;;
    esac
    done
    echo
    echo
    while true; do
    read -p "Would you like to update grub? y/n: " yn
    case $yn in
        [Nn]* ) echo Close the window to exit.;exit;;
        [Yy]* ) echo OK.; break ;;
    esac
    done
    echo
    echo "Updating grub..."
    echo $'\e[0;37m'
    #pkexec update-grub ## This is the actual line
    ls -la ## This is the line for testing purposes :·)
    echo $'\e[0;36m'
    echo "Let's check. "
    echo "These are the last 24 lines of /boot/grub/grub.cfg:"
    echo $'\e[0;37m'
    tail -24 /boot/grub/grub.cfg
    echo $'\e[0;36m'
    echo "You should find an entry for 'New bootable ISO' in your grub menu that should boot the OS"
    echo
    echo Close the window to exit.
    and save it in the same directory as addiso.sh. (or save them with other names and adjust things accordingly).
    Make both executable.
    Close Dolphin and re-open it.

    Now, if you right-click on a .iso, you should have an entry that says "Add ISO to grub".
    (If you don't have a handy .iso to test it on, you can make one of a small directory :·).
    Clicking the entry will run the script.

    As it is, it's in "testing" mode.
    Instead of adding the entry to /etc/grub.d/40_custom, it adds it to a local text file.
    Instead of actually running update-grub, it lists the current directory.
    Instructions on how to make it "live" are at the top of the script.

    Now,
    It's pretty bad code.
    Especially the "$'\044\151\163\157\146\151\154\145'" stunt to pass a $0 variable as text, is silly. I haven't found another solution, I just made that up, it does work, but there's probably a better way.
    And I'm sure it can be "optimised" in many ways.

    Still, it works.
    At least on my system, it does add an entry to /etc/grub.d/40_custom for the ISO that was chosen in Dolphin.

    But I'd certainly appreciate it if someone would try it too and let me know.
    Last edited by Don B. Cilly; May 13, 2020, 04:11 AM. Reason: Updating with jlittle's suggestions and other fixes

    #2
    Especially the "$'\044\151\163\157\146\151\154\145'" stunt to pass a $0 variable as text, is silly.
    Code:
    pippo='$0'
    isopippo='$isofile'
    achieves the same result; within single quotes, bash treats nothing as special, except a single quote.
    However, to get a literal $0 and $isofile in the grub entry, just put a \ to escape the $, f.ex.
    Code:
    tail -n +3 \$0
    I suggest also using colour names:
    Code:
    white=$'\e[0;37m'
    blue=$'\e[0;36m'
    # then, f.ex.
    echo $blue"Let's check..."
    Regards, John Little

    Comment


      #3
      What I do is have a filesystem labelled "main" that has a directory named "iso" at the filesystem root. Then this grub entry:
      Code:
      submenu "isos in main" {
      # this builds a submenu of the isos found in my SSD iso directory
      # the entries work with ubuntu isos
      search --no-floppy --set=root --label "main"
      set isoloc="($root)/iso"
      for ffile in $isoloc/*.iso
      do
          if regexp --set=1:idev --set=2:ifile --set=3:ibase --set=4:iname '^(.*\))((.*/([^/]+))\.iso)$' $ffile
          then
              menuentry $iname $idev $ifile $ibase {
                  set dev=$2
                  set base=$2$4
                  set isofile=$3
                  loopback loop ($root)$isofile
                  linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject toram
                  initrd (loop)/casper/initrd
              }
          fi
      done
      }
      generates a menu of the isos it finds there. I just have to download or copy an iso into that directory, no editing or update-grub required. Download complete, reboot.

      Note that those particular linux and initrd commands work with *buntu isos; something more involved is needed if other distros or tools are to boot. I use the mechanisms from the aguslr multibootusb project on github; there's many types of iso supported, and if something breaks, the project's users usually have a fix in a short time. Presently I have *buntus, gentoo, systemrescuecd, clonezilla, and supergrub2 (though that last complains about iso booting being unsupported).
      Regards, John Little

      Comment


        #4
        The "\ to escape the $" works perfectly. Thanks :·)
        I don't know how I couldn't find it by groogling around. I actually spent hours trying to solve that problem (and others, which I did).
        I was obviously looking for the wrong questions... but you know how it is with groogling sometimes, and the fact is I have hardly been sleeping for a few days.

        Anyway, I've edited the script.
        Haven't bothered with the colours yet. It works as it is. I will eventually, but it's irrelevant.

        Originally posted by jlittle View Post
        What I do is have a filesystem labelled "main" that has a directory named "iso" at the filesystem root. Then this grub entry: ...
        So... how does one make that filesystem? It looks like a better solution than mine...

        Comment


          #5
          Originally posted by Don B. Cilly View Post
          So... how does one make that filesystem? It looks like a better solution than mine...
          You don't need to make a separate filesystem, any existing one will do. Just apply a label (using the KDE partition manager or GParted, say). "main" is the only permanentish partition (other than the EFI one and a swap) my Kubuntu system has (I have multiple installs in the same btrfs, as discussed often on KFN), other than backup ones on discs that come and go.

          Using the label simplifies the grub entry; if you prefer, you could use the UUID, and change --label to --fs-uuid. (I dislike UUIDs, they're ugly, error prone, and unmemorable.)
          Regards, John Little

          Comment


            #6
            Yes, all partitions here (except the swap one) are labelled.
            So I can make a grub entry just like that (in /boot/grub/grub.cfg, right?), except with
            search --no-floppy --set=root --label "SSD2" (the label for this system's /root) and grub will show entries for all .isos it finds in /iso/?

            That's wonderful.
            Why didn't you tell us before? Click image for larger version

Name:	icon_smile_crazy.gif
Views:	7
Size:	725 Bytes
ID:	644658
            I will try (later in the day, so later in the night there ;·) and will let you know.

            Comment


              #7
              Tch
              I made a submenu in grub.cfg under the boot entry of my neon:
              Code:
              menuentry 'neon GNU/Linux' --class neon --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-5c3af4ec-b016-4eea-accd-23d517b87248' {
              	recordfail
              	load_video
              	gfxmode $linux_gfx_mode
              	insmod gzio
              	if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi
              	insmod part_gpt
              	insmod ext2
              	set root='hd2,gpt2'
              	if [ x$feature_platform_search_hint = xy ]; then
              	  search --no-floppy --fs-uuid --set=root --hint-bios=hd2,gpt2 --hint-efi=hd2,gpt2 --hint-baremetal=ahci2,gpt2  5c3af4ec-b016-4eea-accd-23d517b87248
              	else
              	  search --no-floppy --fs-uuid --set=root 5c3af4ec-b016-4eea-accd-23d517b87248
              	fi
                   linux	/boot/vmlinuz-5.3.0-46-generic root=UUID=5c3af4ec-b016-4eea-accd-23d517b87248 ro  quiet splash $vt_handoff
              	initrd	/boot/initrd.img-5.3.0-46-generic
              }
              submenu "isos in main" {
              # this builds a submenu of the isos found in my SSD iso directory
              # the entries work with ubuntu isos
              search --no-floppy --set=root --label "main"
              set isoloc="($root)/iso"
              for ffile in $isoloc/*.iso
              do
               if regexp --set=1:idev --set=2:ifile --set=3:ibase --set=4:iname '^(.*\))((.*/([^/]+))\.iso)$' $ffile
               then
                   menuentry $iname $idev $ifile $ibase {
                       set dev=$2
                       set base=$2$4
                       set isofile=$3
                       loopback loop ($root)$isofile
                       linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject toram
                       initrd (loop)/casper/initrd
                   }
               fi
              done
              }
              submenu 'Advanced options for neon GNU/Linux' $menuentry_id_option 'gnulinux-advanced-5c3af4ec-b016-4eea-accd-23d517b87248' { ... etc...
              But under advanced options in grub at boot for neon I can find no entry for anything except the old kernels.
              REFInd doesn't find ;·) it either :·/
              The ISO is in /iso:
              Code:
              not@all:/iso$ ll
              total 1556492
              drwxr-xr-x  2 root root       4096 Apr 25 10:47 ./
              drwxr-xr-x 24 root root       4096 Apr 25 10:46 ../
              -rw-rw-r--  1 not  not  1593835520 Apr 25 10:32 neon-user-20200416-1115.iso
              What am I doing wrong?

              Comment


                #8
                Originally posted by Don B. Cilly View Post
                So I can make a grub entry just like that (in /boot/grub/grub.cfg, right?), except with
                search --no-floppy --set=root --label "SSD2" (the label for this system's /root) and grub will show entries for all .isos it finds in /iso/?
                Yes. Now, if you let grub generate your grub.cfg, you'd best put it in /etc/grub.d/40_custom, or in custom.cfg alongside grub.cfg (41_custom generates script that sources custom.cfg if it exists.)
                Regards, John Little

                Comment


                  #9
                  Our replies crossed.
                  Originally posted by Don B. Cilly View Post
                  What am I doing wrong?
                  Did you change "main" to "SDD2"?

                  It won't be in the advanced options submenu, unless you nest the iso submenu in the advance options one.

                  Maybe a grub submenu doesn't display if it has no entries. You could add a placeholder in the submenu, say
                  Code:
                  menuentry "hello" {
                     echo hello
                     sleep 10
                  }
                  That'll tell you if it is recognizing the submenu. grub script is painful to debug, that regex was tedious to get right.
                  Regards, John Little

                  Comment


                    #10
                    Just me, but it seems complicated. I have this in my 40_custom

                    Code:
                    menuentry 'Latest ISO' {
                        set isofile="/@grub/latest.iso"
                        loopback loop (hd0,3)$isofile
                        linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject
                        initrd (loop)/casper/initrd.lz
                    }
                    I just rename the downloaded ISO, move it to the grub subvolume (or whatever folder you want, as long as grub can access it), and boot it.

                    No fancy scripts, edits, or running update-grub required.

                    Please Read Me

                    Comment


                      #11
                      Originally posted by oshunluvr View Post
                      Just me, but it seems complicated.
                      Definitely. Last year I was testing daily isos for several flavours for eoan, and it was very convenient, so I think I came out ahead.
                      Code:
                      set isofile="/@grub/latest.iso"
                      Note that grub supports symlinks, at least within the same directory hierarchy. So with your approach your latest.iso could point to the original iso name, which might avoid confusion. (More generally, grub boots *buntus fine with symlinks to subvolumes, and they work in /etc/fstab too.)
                      Code:
                      loopback loop (hd0,3)$isofile
                      (Thinking of the casual reader of this thread) the usual warning about grub device names changing applies. It bit me once. It's only an extra line to do search --no-floppy --set=root --label label and use $root.
                      Regards, John Little

                      Comment


                        #12
                        Oh boy. It's not just that I hate grub, it's also that it definitely hates me.
                        Yesterday I didn't have much time or much inclination... so this morning I gave it another go.
                        Couldn't do it.
                        Then I remembered that my neon grub was so messed up I simply had to use rEFInd.
                        So (aha!) I booted K20.04, went the 40_custom route, and sure enough the "ubuntu" grub showed the entry.
                        Except, when I tried to boot it...
                        Out of memory
                        No server found
                        You have to load the kernel first


                        which, from what I can find, seems to be this bug (November '19, Confirmed, Undecided, Unassigned).
                        which seems to apply to grub 2.04 only.

                        Tch. I wish one could do it with rEFInd. But Rod Smith says he's "working on it", but hasn't done it yet.
                        Maybe if I delete my grub.cfg on neon... re-install grub2... maybe later I'll try it

                        [EDIT] I checked. On neon I have grub 2.02-2, on K20 2.04-1.

                        [EDIT2] Some progress. I deleted(moved) my neon grub.cfg. It worked, the ISO shows at boot. And it boots (with grub2.02)... and promptly kernel-panicks. But it boots :·)
                        I'll investigate from here.
                        Last edited by Don B. Cilly; Apr 26, 2020, 01:52 AM.

                        Comment


                          #13
                          Well, isn't grub just lovely :·(
                          As suspected it was initrd which is back (since the last time I tried) to being called initrd.lz
                          So I edited the entry. After which, my grub.cfg showed:
                          Code:
                          ### BEGIN /etc/grub.d/40_custom ###
                          menuentry 'Latest ISO' {
                          set isofile="/iso/neon-user-20200416-1115.iso"
                          loopback loop (hd2,2)$isofile
                          linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noprompt noeject toram
                          initrd (loop)/casper/initrd.[B]lz[/B]
                          }
                          Retried. Error: file casper/initrd not found

                          Double-checked. It's still inird.lz. But grub complains it can't find initrd.
                          Oh well :·/

                          Click image for larger version

Name:	initrd.png
Views:	1
Size:	59.1 KB
ID:	644660

                          [EDIT] So, I checked the actual grub entry with "e". It's initrd (no lz). But grub.cfg says initrd.lz. I'll re-update-grub and see...

                          [EDIT2] Nothing. I checked 40_custom, updated grub, re-checked grub.cgf, it said .lz, rebooted, casper/initrd not found. Editing the grub entry manually boots the ISO just fine.
                          Oh well :·/
                          Last edited by Don B. Cilly; Apr 26, 2020, 03:53 AM.

                          Comment


                            #14
                            Originally posted by jlittle View Post
                            Definitely. Last year I was testing daily isos for several flavours for eoan, and it was very convenient, so I think I came out ahead.
                            symlink is a good idea. I can see in your use-case that keeping the ISO names would be paramount. I rarely do a bare metal test or install these days, in fact it's been since 2018. Now that 20.04 is here, I will be installing Kubuntu and possibly a new KDEneon if they make one, but then not again until I retire which will be 2022ish unless something goes horribly wrong.

                            Please Read Me

                            Comment


                              #15
                              Well, assuming one's grub works as expected, the little service-menu/script thingy above could be useful to some if they don't want to mess with grub configuration directly, rename/move ISOs... or whatever :·)

                              One thing I'm curious about: Will df -P always return partition names in the form /dev/sdxy or can it return things like /dev/nvme0n1, /dev/mmcblk0, and the like?
                              Because in that case the little conversion line from df to grub notation ...

                              And another thing: The pesky initrd(.lz). Assuming it's now called that, I put it as such in the script.
                              I hope it's going to be .lz "from now on"... does anyone know?

                              Comment

                              Working...
                              X