Create a grub script that auto detect EFI systems, and boot first one - grub

I am working on an opensource project, bluebanquise, that aims to deploy baremetal infrastructures, and now that we support RHEL/CentOS 8 distributions, I wish to add other RHEL derivative distributions (Rocky Linux, Oracle Linux, Cloud Linux, etc etc), but also in the future other non RHEL distributions (Ubuntu, OpenSuse, etc).
By default, on our infrastructure, all servers boot over PXE by default. DHCP provides an iPXE rom that chains to other ipxe files, etc. At some point in this chain, if server has to boot on disk (so not deploying or booting in diskless), and if server is in EFI, iPXE chain to an grub2 image with an embed script that attempt to detect already installed OS and boot the first one found.
Note: Maybe this is not the best solution to boot from iPXE to disk in EFI. But I found nothing else.
Script is the following: grub2-efi-autofind.cfg
Basically, it searches for grub.cfg file for RedHat or CentOS Linux, and if found uses them to boot.
But now, I would like to make this script more "generic", so it can bypass the need of the distribution name, and only look for any /efi/*/grub.cfg file.
However, I fail to create a script able to do that :-(
I found an interesting script on this page, but I fail to adapt the for loop to my need. I tried to use it alone:
for efi in (*,gpt*)/efi/*/grub.cfg ; do
regexp --set=1:efi_device '^\((.*)\)/' "${efi}"
echo found efi
echo "${efi}"
echo "${efi_device}"
echo "${2}"
echo "${1}"
sleep --interruptible --verbose 10
done
But this does not output anything except "(,gpt)/efi/*/grub.cfg"
I tried to use the plain code of this page, and it indeed detect the file, but after few iterations in the automatic menu (I need to press twice on Detect EFI bootloaders to get the correct path). And this is not automatic.
The GRUB2 documentation does not have a lot of examples related to this, and I do not find a lot of exchanges on this matter over the web.
If one of you know a better way to boot an EFI system on disk from an iPXE rom, or if you know how to replace a search.file with a static path by a way to generically detect any grub.cfg in efi partitions, I would be very glad to read about it ! :-)
Thanks a lot for reading this, even if you do not have an answer.
With my best regards
Ox

Here is a simple GRUB script that searches for the first "disk" containing a file at /EFI/BOOT/BOOTX64.EFI and then conditionally display a menu entry to boot from it:
search --no-floppy --file --set=bootable_efi /EFI/BOOT/BOOTX64.EFI
if [ -n "${bootable_efi}" ]; then
menuentry "EFI Bootable USB" --class unknown {
set root="${bootable_efi}"
chainloader /EFI/BOOT/BOOTX64.EFI
boot
}
fi

I finally found a way to do it, adding a lot of echo and sleep, I was able to find a pattern that works:
echo " Loading modules..."
insmod part_gpt
insmod fat
insmod chain
insmod part_msdos
insmod ext2
echo
echo "Scanning, first pass..."
for efi in (*,gpt*)/efi/*/grub.cfg (*,gpt*)/efi/*/*/grub.cfg (*,gpt*)/grub.cfg (*,gpt*)/*/grub.cfg ; do
regexp --set=1:efi_device '^\((.*)\)/' "${efi}"
done
echo "Scanning, second pass..."
for efi in (*,gpt*)/efi/*/grub.cfg (*,gpt*)/efi/*/*/grub.cfg (*,gpt*)/grub.cfg (*,gpt*)/*/grub.cfg ; do
regexp --set=1:efi_device '^\((.*)\)/' "${efi}"
if [ -e "${efi}" ]; then
efi_found=true
echo " >> Found operating system! <<"
echo " Path: ${efi}"
echo " Booting in 5s..."
sleep --interruptible --verbose 5
configfile "${efi}"
boot
fi
done
However, while it works, I do not understand why... Basically, the first time it does the for loop, grub finds nothing.
The second time it does the loop, files are found. So workaround here is to do a first time the loop, doing nothing, then redo it another time with conditional and boot.
If one day someone understand what is going on here, I am very interested :-)
For now, it works like a charm as is.

Related

How do I set an entry to PATH in .zshrc based on the user's CPU architecture?

My colleagues and I use the same .zshrc file. We would like it to automatically set an entry to PATH if the user has an M1 macbook which has a different way of installing a package (ansible in this case) compared to Intel based macs.
If user has an M1 macbook this should be added to PATH:
$HOME/Library/Python/3.8/bin
How do I go about doing that?
I was thinking about using uname -p to output processor architecture and then make a conditional within .zshrc but I'm not sure where to start. For instance I don't know if it's possible to add an entry to an already existing PATH.
if [[ $(arch) = arm64 ]]; then
PATH+=:$HOME/Library/Python/3.8/bin
fi

mkfs.vfat and mkfs.ext3 in CYGWIN

I'm running a bash script in the Embedded Command Line that comes with Quartus II. The bash script was sent to me from someone using linux and I was able to get most of the script to work(removing sudo, changing path, etc.). The only two commands that are still giving me errors are mkfs.ext3 and mkfs.vfat. Are these tucked away somewhere in cygwin that I need to add a path or is there another workaround? I've read some people use mkdofsf but cygwin doesn't appear to have that either.
UPDATE:
Tried using /sbin/mkfs - t vfat and /sbin/mkfs -t ext3 but this left me with a similiar error where mkfs.vfat and mkfs.ext3 were not found.
UPDATE 2: Installed e2fsprogs to solve the error with mkfs.ext3 but this doesn't solve the vfat formatting issue.
These are part of the package e2fsprogs which is available in Cygwin. I've never tried these commands, assuming that access to the raw devices would be blocked by Windows. But I see that cfdisk seems to work. It at least starts up, anyway. So, maybe it is doable.
If they don't work on raw devices, if your scripts are creating file systems on a file or can be modified to do that, that should probably work.
Anyway, e2fsprogs is what you need, but be careful here. Whenever you start making or editing file systems, you have the opportunity to really screw up your system. Just be attentive to what the script is doing, and don't blindly go forward.

What is the name of the command that shows disk access in real time?

I am running Mac OS X ( 10.9.4 ) using the built in Terminal, and bash version:
3.2.51(1)-release (x86_64-apple-darwin13)
At one point in the past, there was a CLI command I could issue that would show me what was going on with my drives, kind of like a tail -f system.log but deeper into the system where it would look at the actual drives and what was going on. A fully idle machine, say one that was sleeping and I was ssh'd into, would show a page that was doing little or nothing. Saving a file, or doing anything disk related, and tons of data would scroll past, which you could pipe to a file and grep through.
I have goggled and can't find this command.
Not sure about Mac OS X, but on Linux, there are several:
iostat
iotop
sar
dstat
vmstat
iozone
Most of them should be available on Mac OS X. I know that iostat is.
Or you can always look in
/sys/block/[device]/stat
or
/proc/diskstats
This was in fact a duplicate of question noted by anishane.
The command I was looking for was fs_usage but lsof, while not exactly what I wanted, is a close second in being helpful to track down the problems I'm running into.
I think you are looking for iosnoop. Seems like it is a wrapper on dstat only, as when I ran it without sudo it gave me the following error.
% iosnoop
dtrace: system integrity protection is on, some features will not be available
dtrace: failed to initialize dtrace: DTrace requires additional privileges

mount a drive on Mac OS X with bash script (and NOT use expect)

I'd like to mount a Samba drive in OS X using bash. This line does the trick:
mount -t smbfs //$SAMBAUSER#$ADRESS/$NAMEOFSHARE $MACOSXPATH
only one problem. I want it done without user input - which means no password can be manually put in. And I'm not going to ask my users to download fink just so they can install expect (as seen here).
I tried applying the accepted solution to a similar StackOverflow problem shown here by doing this:
echo "mypassword" | mount -t smbfs //$SAMBAUSER#$ADRESS/$NAMEOFSHARE $MACOSXPATH --stdin
but no luck - that doesn't work and Mac OS X tells me I used mount command improperly:
usage: mount [-dfruvw] [-o options] [-t ufs | external_type] special node
mount [-adfruvw] [-t ufs | external_type]
mount [-dfruvw] special | node
Any suggestions? This would be easy with an expect script - but that would ruin the user experience to have that prerequisite in my mind.
If mount(8) can't just call the mount syscall on the filesystem, it looks for a program to help it. On FreeBSD and Mac OS X, those helper programs follow the naming convention mount_XXX, where XXX is the -t argument's value.
That means you want to check the mount_smbfs(8) man page, which tells us about -N:
-N Do not ask for a password. At run time, mount_smbfs reads the ~/Library/Preferences/nsmb.conf
file for additional configuration parameters and a password. If no password is found,
mount_smbfs prompts for it.
Unfortunately the man page trail ends with one for nsmb.conf that doesn't mention anything about storing passwords. On FreeBSD 8.0, at least, the solution is to put a password key with a plain text(!) password value under a [SERVER:USER] heading. That would be type C according to the linked nsmb.conf man page.
So it seems that you'll want to dump a pre-configured nsmb.conf into your user's ~/Library/Preferences/ directory and then call your mount command with -N. As far as I know you can't provide a hashed value, which is not especially awesome. I'll try to get access to a MacBook in a few hours to test this.
NB: This is not how to do it with the GNU toolchain. If you're on Linux, you're probably going to be using something like mount.cifs(8). The correct solution in that case is the credentials=filename option (used after -o, of course), where filename is a file of credentials in key=value form, separated by newlines. See http://linux.die.net/man/8/mount.cifs
The answer in this apple support discussion thread worked for me:
osascript -e 'mount volume "smb://user:password#server/share"'
I'll give you 2 options. First, include the password on the command line:
mount -t smbfs //$SAMBAUSER:$PASSWORD#$ADRESS/$NAMEOFSHARE $MACOSXPATH
This is not a great option because the command line (including password) is visible to anyone who happens to be logged in at the moment. Not great, but it is a possibility.
Second, use expect. The Mac OS X Hints article you linked dates from 2002, when OS X v10.2 would've been current. While 10.2 apparently didn't include expect as a standard component, 10.6 does, and I'm pretty sure it's been included for several versions now.
#!/usr/bin/expect -f
spawn mount -t smbfs //fred#12.34.56.78/myfiles /tmp/mountpoint
expect "Password:"
send "wibble\r"
wait
You not only can use a hashed value, you're expected to, at least in older releases. The crypt option for smbutil is not DOD-level security but as with most security, you're trying to keep the honest people honest: the bent ones will find a way. It seems to be broken in Mountain Lion but the nsmb.conf file in ~/Library/Preferences should be secure at the OS level. The /etc/nsmb.conf would override it if it exists. I'm sure there's a reason why plain text files are obsolete but didn't we go this with NetInfo? How long did it take for Apple to allow the old standbys (/etc/hosts, /etc/passwd) to be used?

Keeping shell configurations in sync across multiple machines

I'm a pretty active command line user and I have shell accounts all over the place. MacBooks, Linux desktop machines, Linux servers, Cygwin on XP, you name it.
How can I keep my shell configuration (.bashrc, .vimrc etc.) in sync across all these machines using the limited tools available across all platforms?
I have been using rsync in a pretty limited manner that involves manually copying some files over when I need them, but I want a standard way for setting up the same basic shell environment across all my machines. Tell me your shell account management strategy.
I have folder on Dropbox with global, per OS, and per machine shell configs:
$ ls ~/Dropbox/shell/bash
bashbootstrap bashrc
bashrc-Darwin bashrc-Darwin-laptopname bashrc-Darwin-mininame
bashrc-Linux bashrc-Linux-machineone bashrc-Linux-machinetwo
bashrc is loaded on every machine, bashrc-Linux, bashrc-Darwin are loaded on their respective OSes, and several configs are specific to individual machines. (By the way, Darwin is the name of OS X's BSD-like kernel.)
What ties it all together is the bashbootstrap file. It loads each applicable config file in order of increasing specificity, this allows per OS and per machine overrides to have higher precedence. Additionally, we silently skip missing config files; you need not create empty config files for each of your machines to keep the script happy.
On a new machine, after installing Dropbox on ~/Dropbox, I move away the default .bashrc and just symlink the bootstrap file in its place instead:
$ mv ~/.bashrc ~/.bashrc.bak
$ ln -s ~/Dropbox/shell/bash/bashbootstrap ~/.bashrc
Oh, and here are the contents of the bashbootstrap file:
if [ -z "$PS1" ]; then
return
fi
dropboxshelldir=~/Dropbox/shell
dropboxdir=$dropboxshelldir/bash
masterbashrc=$dropboxdir/bashrc
osbashrc=$masterbashrc-`uname`
localbashrc=$osbashrc-`hostname | cut -d. -f1`
echo -n "Applicable shell configs: "
for bashfile in "$masterbashrc" "$osbashrc" "$localbashrc"; do
if [ -r $bashfile ]; then
. $bashfile
echo -n "`basename $bashfile` "
fi
done
echo
# Set convenience aliases
myed=${VISUAL:-${EDITOR:-vim}}
alias editbashrc="$myed $masterbashrc"
alias editosbashrc="$myed $osbashrc"
alias editlocalbashrc="$myed $localbashrc"
One final note, this script also provides three convenience aliases for editing your Bash config files without having to remember where they are stored.
editbashrc: Edit the global config file.
editosbashrc: Edit the OS-specific config file.
editlocalbashrc: Edit the machine-specific config file.
I only tested this on Bash, but it could work on other Bash like shells. But, as they say, your mileage may vary.
I made a blog post about this here.
I've used version control for this in the past (svn, mercurial, etc...). You can set up your own server, or use a hosted one. Dropbox also works.
If you have access to source control, I'd just check them in. This way you get to sync them across multiple machines and you can compare/roll-back if need be. If you don't have CVS/SVN at work there are free options available.
I prefer slightly different configuration files for Vim, Fish, Sway, etc. on different machines, so I made a program (filetailor) to handle this and then sync the result with Git.
filetailor is an open-source Python program that can make small changes to files using device-specific variables or using device-specific comments in the files.
For example, the following line would be commented out on every device except the one with hostname device1.
alias MYHOME='/home/dev1home/' #{filetailor device1}

Resources