How to use chroot in a Makefile? - linux

I am building my own Debian-based Linux with own kernel and software. One of the last steps of the make-process has to be done in a chrooted environment:
Install the custom kernel using dpkg
Create symbolic links to the kernel and initrd.img
Execute ldconfig
Set my custom theme for the splash screen using plymouth
Update the initrd.img
While the installation of the kernel succeeds and the symbolic links are acutally created, all other commands do not seem to work. If I boot into the system the splash screen is set to the default and the initrd.img cannot find the HDD nor the kernel. So the updating of the initrd.img inside the dpkg-installation process seems to fail somehow. The plymouth script to set the theme does not work either.
To fix this, I manually chroot into the system and do the following:
Set my custom theme for the splash screen using plymouth
Execute ldconfig
Update the initrd.img
This works perfectly fine. Next time I boot the system, my splash screen is shown and everything starts properly.
Here is my approach to get this done in my Makefile:
cp $(INTEGRATION_KERNEL_IMAGE) $(ROOTFS)/tmp/kernel.deb
cd $(ROOTFS); /usr/bin/sudo /bin/mount -t proc proc proc/; /usr/bin/sudo /bin/mount -t sysfs sys sys/; /usr/bin/sudo /bin/mount -o bind /dev dev/
/usr/sbin/chroot --userspec=0:0 $(ROOTFS) /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin HOME=/root bash -c "/usr/bin/dpkg --force-not-root -i /tmp/kernel.deb"
/usr/sbin/chroot --userspec=0:0 $(ROOTFS) /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin HOME=/root bash -c "/bin/ln -nsf vmlinuz-3.2.54-rt75custom /boot/vmlinuz"
/usr/sbin/chroot --userspec=0:0 $(ROOTFS) /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin HOME=/root bash -c "/bin/ln -nsf initrd.img-3.2.54-rt75custom /boot/initrd.img"
/usr/sbin/chroot --userspec=0:0 $(ROOTFS) /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin HOME=/root bash -c "/sbin/ldconfig"
/usr/sbin/chroot --userspec=0:0 $(ROOTFS) /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin HOME=/root bash -c "/bin/bash /usr/sbin/plymouth-set-default-theme my_theme"
/usr/sbin/chroot --userspec=0:0 $(ROOTFS) /usr/bin/env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin HOME=/root bash -c "/bin/bash /usr/sbin/update-initramfs -u"
/usr/bin/sudo /bin/umount $(ROOTFS)/proc; /usr/bin/sudo /bin/umount $(ROOTFS)/sys; /usr/bin/sudo /bin/umount $(ROOTFS)/dev
The output of make does not provide any errors on this topic. Well, it possibly cannot do this because make does not know what is going on inside the chrooted environment. But how can I find out what is going wrong?
A possible workaround would be to put everything I mentioned above in a shell script and execute this in the chrooted environment. But I would prefer to do everything in the Makefile and I do not know if the workaround really works. I have not verified this yet.

Have you tried saving command output in the chroot environment and extracting it later? For example:
/usr/sbin/chroot [...] bash -c "/usr/bin/dpkg [...] >> /root/chroot.log"
or
/usr/sbin/chroot [...] bash -c "/usr/bin/dpkg [...] | tee -a /root/chroot.log"
followed by
cp $(ROOTFS)/root/chroot.log .
In the long run I would suggest to avoid code duplication and Makefile clutter, either by passing everything in a single chroot command or by copying over a script.
You should be able to get rid of most or all of the bash -c and /bin/bash invocations. That should simplify things even more.

Related

Why commands like "su", "passwd" in busybox cannot work properly?

Background:
Debugging linux kernel 6.0 with qemu-system-x86_64. The start commandline is as follows:
qemu-system-x86_64 -kernel ./bzImage -initrd ./rootfs.img -serial stdio -append " console=ttyS0 nokaslr"
The initrd rootfs.img is made by busybox-1.35.0 using the following commands:
$ make menuconfig #choose [*] Build static binary (no shared libs)
$ make && make install
$ cd _install
$ ls
bin linuxrc sbin usr
$ mkdir -p dev proc etc sys\kernel\debug sys\dev
$ vim init
The init file is filled with:
#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
mdev -s
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
# normal user
setsid /bin/cttyhack setuidgid 1000 /bin/sh
$ find . | cpio -o --format=newc > ./rootfs.img
================================================================
The problem:
When I runqemu-system-x86_64 -kernel ./bzImage -initrd ./rootfs.img -serial stdio -append " console=ttyS0 nokaslr" to start qemu. And enter the kernel successfully. But when I run "su" the problem occurs:
{==DBG==} INIT SCRIPT
{==DBG==} Boot took 2.63 seconds
/ $ su
su: must be suid to work properly
/ $
================================================================
What I tried:
I tried to google the problem. But only find to escalate the privilege.
Then I tried:
/ $ cd bin
/bin $ chmod u+s busybox
/bin $ ls -l busybox
-rwsr-xr-x 1 1000 1000 2408664 Oct 11 12:57 busybox
/bin $ su
su: must be suid to work properly
/bin $
Obviously the 'solution' failed.
================================================================
So what can I do to solve this problem? Or what causes this problem? Any help would be appreciated! Thanks in advance!
The suid bit that you added with chmod u+s busybox changes the current user to the owner of /bin/busybox, which as you can see is 1000.
So you want to change /bin/busybox to be owned by root:
$ chown root:root /bin/busybox
But you won't be able to do that from within your non-root shell; you must make this change in the root image rootfs.img.
It probably makes sense to have all files in the image owned by root. You don't need to change the ownership in the host file system, because you can do it while building the image:
$ find . | cpio -o --format=newc --owner=+0:+0 > ./rootfs.img
^^^^^^^^^^^^^

Shell Script won't run from NodeJS because it has "set -o pipefail" [duplicate]

The below mentioned line of code used to work for me all the time on a Ubuntu 16.04 distribution, but suddenly option-name pipefail is an illegal option:
set -eu -o pipefail
returns:
set: Illegal option -o pipefail
Why does this happen? I run the command on a completely new installed system and as part of a shell script. The code is placed right at the beginning:
myscript.sh:
1 #!/bin/bash
2 set -eu -o pipefail
3 ...
The script is run as sudo:
sudo sh ./myscript.sh
You are running bin/sh, on Ubuntu it is a symbolic link pointing to /bin/dash, but pipefail is a bashism.
Make the script executable:
chmod +x myscript.sh
and then run the script as follows:
sudo ./myscript.sh
I had the same error when running script from zsh and the script began with incorrect shebang.
WRONG, missing ! after #:
#/bin/bash
rest-of-the-script
Correct:
#!/bin/bash
rest-of-the-script

busybox init script. Mount not found

I'm running in to what appears to be a bit of an odd one.
Base machine is Ubuntu 18.04. I'm experimenting with creating a custom initramfs + init script to use with custom compiled kernels that are being used with qemu instances.
From the directory I'm using as the base for the initramfs:
[~/initramfs] $ find .
.
./proc
./root
./dev
./dev/console
./dev/sda1
./dev/null
./dev/tty
./sbin
./init
./etc
./lib64
./mnt
./mnt/root
./lib
./bin
./bin/busybox
./sys
Just the basics needed for now. The busybox binary comes from the busybox-static package, and I've confirmed it's statically complied:
[~/initramfs]$ ldd bin/busybox
not a dynamic executable
In the init script, I have:
#!/bin/busybox sh
mount -t proc none /proc
mount -t sysfs none /sys
echo "Hi there"
umount /sys
umount /proc
poweroff
From there, create an initramfs.gz:
find . -print0 | cpio --null --create --verbose --format=newc | pigz --best > ~/initramfs.gz
When I set that as the target initrd for qemu, kernel starts up as expected, then:
[ 0.777443] Run /init as init process
/init: line 3: mount: not found
/init: line 4: mount: not found
Hi there
/init: line 8: umount: not found
/init: line 9: umount: not found
/init: line 11: poweroff: not found
mount is part of busybox. So that's strange.
If I modify the init script and put in /bin/busybox sh as the first command to be executed, that gets me to a busybox shell as you'd expect.
[ 0.789949] Run /init as init process
BusyBox v1.27.2 (Ubuntu 1:1.27.2-2ubuntu3.2) built-in shell (ash)
Enter 'help' for a list of built-in commands.
sh: can't access tty; job control turned off
/ # [ 1.364618] input: ImExPS/2 Generic Explorer Mouse as /devices/platform/i8042/serio1/input/input3
[ 1.386482] tsc: Refined TSC clocksource calibration: 3392.105 MHz
[ 1.388387] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x30e52cb7a6c, max_idle_ns: 440795310382 ns
[ 1.391965] clocksource: Switched to clocksource tsc
/ #
And then help shows:
/ # help
Built-in commands:
------------------
. : [ [[ alias bg break cd chdir command continue echo eval exec[ 71.772009] random: fast init done
exit export false fg getopts hash help history jobs kill let
local printf pwd read readonly return set shift source test times
trap true type ulimit umask unalias unset wait [ [[ acpid adjtimex
ar arp arping ash awk basename blkdiscard blockdev brctl bunzip2
bzcat bzip2 cal cat chgrp chmod chown chpasswd chroot chvt clear
cmp cp cpio crond crontab cttyhack cut date dc dd deallocvt depmod
devmem df diff dirname dmesg dnsdomainname dos2unix dpkg dpkg-deb
du dumpkmap dumpleases echo ed egrep env expand expr factor fallocate
false fatattr fdisk fgrep find fold free freeramdisk fsfreeze
fstrim ftpget ftpput getopt getty grep groups gunzip gzip halt
head hexdump hostid hostname httpd hwclock i2cdetect i2cdump
i2cget i2cset id ifconfig ifdown ifup init insmod ionice ip ipcalc
ipneigh kill killall klogd last less link linux32 linux64 linuxrc
ln loadfont loadkmap logger login logname logread losetup ls
lsmod lsscsi lzcat lzma lzop md5sum mdev microcom mkdir mkdosfs
mke2fs mkfifo mknod mkpasswd mkswap mktemp modinfo modprobe more
mount mt mv nameif nc netstat nl nproc nsenter nslookup od openvt
partprobe passwd paste patch pidof ping ping6 pivot_root poweroff
printf ps pwd rdate readlink realpath reboot renice reset rev
rm rmdir rmmod route rpm rpm2cpio run-parts sed seq setkeycodes
setpriv setsid sh sha1sum sha256sum sha512sum shred shuf sleep
sort ssl_client start-stop-daemon stat static-sh strings stty
su sulogin svc swapoff swapon switch_root sync sysctl syslogd
tac tail tar taskset tee telnet telnetd test tftp time timeout
top touch tr traceroute traceroute6 true truncate tty tunctl
ubirename udhcpc udhcpd uevent umount uname uncompress unexpand
uniq unix2dos unlink unlzma unshare unxz unzip uptime usleep
uudecode uuencode vconfig vi w watch watchdog wc wget which who
whoami xargs xxd xz xzcat yes zcat
So I go looking for mount, and discover which isn't found either. Oh but it works if I prepend it with /bin/busybox to call it direct...:
/ # type mount
mount is mount
/ # which mount
sh: which: not found
/ # /bin/busybox which mount
/ #
and I can execute the commands successfully if I add /bin/busybox to them:
/ # /bin/busybox mount -t proc none /proc
/ #
It seems really random what will and what won't work from busybox, what does and what doesn't get found, e.g. find is fine:
/ # find
.
./test
./sys
./bin
./bin/busybox
./lib
./mnt
./mnt/root
./lib64
./etc
./init
./sbin
./proc
./root
./dev
./dev/tty
./dev/null
./dev/sda1
./dev/console
I can work around this by prepending every command in the init file with /bin/busybox, but I'd really rather not if I don't have to!
You have to symlink all the applets you want, e.g. ln -s /bin/busybox /bin/mount. See USAGE in the busybox docs:
USAGE
BusyBox is a multi-call binary. A multi-call binary is an
executable program that performs the same job as more than one utility
program. That means there is just a single BusyBox binary, but that
single binary acts like a large number of utilities. This allows
BusyBox to be smaller since all the built-in utility programs (we call
them applets) can share code for many common operations.
You can also invoke BusyBox by issuing a command as an argument on the
command line. For example, entering
/bin/busybox ls
will also cause BusyBox to behave as 'ls'.
Of course, adding '/bin/busybox' into every command would be painful.
So most people will invoke BusyBox using links to the BusyBox binary.
For example, entering
ln -s /bin/busybox ls
./ls
will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled into BusyBox). Generally speaking, you should never
need to make all these links yourself, as the BusyBox build system
will do this for you when you run the 'make install' command.
If you invoke BusyBox with no arguments, it will provide you with a
list of the applets that have been compiled into your BusyBox binary.
The commands that happen to work without it are the ones implemented as fork-free and therefore can be invoked as builtins.

Execute a shell script in current shell with sudo permission

To execute a shell script in current shell, we need to use a period . or a source command. But why does it not work with a sudo permission?
I have a script with execute permission called setup.sh. When I use a period, I get this:
$ sudo . ./setup.sh
sudo: .: command not found
The source command also produces a similar error. Am I missing out something? What should I do to run the script with sudo permission in the same/current shell?
I'm not sure if this breaks any rules but
sudo bash script.sh
seems to work for me.
What you are trying to do is impossible; your current shell is running under your regular user ID (i.e. without root the access sudo would give you), and there is no way to grant it root access. What sudo does is create a new *sub*process that runs as root. The subprocess could be just a regular program (e.g. sudo cp ... runs the cp program in a root process) or it could be a root subshell, but it cannot be the current shell.
(It's actually even more impossible than that, because the sudo command itself is executed as a subprocess of the current shell -- meaning that in a sense it's already too late for it to do anything in the "current shell", because that's not where it executes.)
I think you are confused about the difference between sourcing and executing a script.
Executing a script means creating a new process, and running the program. The program can be a shell script, or any other type of program. As it is a sub process, any environmental variables changed in the program will not affect the shell.
Sourcing a script can only be used with a bash script (if you are running bash). It effectively types the commands in as if you did them. This is useful as it lets a script change environmental variables in the shell.
Running a script is simple, you just type in the path to the script. . is the current directory. So ./script.sh will execute the file script.sh in the current directory. If the command is a single file (eg script.sh), it will check all the folders in the PATH variable to find the script. Note that the current directory isn't in PATH, so you can't execute a file script.sh in the current directory by running script.sh, you need to run ./script.sh (unless the current directory is in the PATH, eg you can run ls while in the /bin dir).
Sourcing a script doesn't use the PATH, and just searches for the path. Note that source isn't a program - otherwise it wouldn't be able to change environmental variables in the current shell. It is actually a bash built in command. Search /bin and /usr/bin - you won't find a source program there. So to source a file script.sh in the current directory, you just use source script.sh.
How does sudo interact with this? Well sudo takes a program, and executes it as root. Eg sudo ./script.sh executes script.sh in a sub process but running as root.
What does sudo source ./script.sh do however? Remember source isn't a program (rather a shell builtin)? Sudo expects a program name though, so it searches for a program named source. It doesn't find one, and so fails. It isn't possible to source a file running as root, without creating a new subprocess, as you cannot change the runner of a program (in this case, bash) after it has started.
I'm not sure what you actually wanted, but hopefully this will clear it up for you.
Here is a concrete example. Make the file script.sh in your current directory with the contents:
#!/bin/bash
export NEW_VAR="hello"
whoami
echo "Some text"
Make it executable with chmod +x script.sh.
Now observe what happens with bash:
> ./script.sh
david
Some text
> echo $NEW_VAR
> sudo ./script.sh
root
Some text
> echo $NEW_VAR
> source script.sh
david
Some text
> echo $NEW_VAR
hello
> sudo source script.sh
sudo: source: command not found
Basically sudo expects, an executable (command) to follow & you are providing with a .
Hence the error.
Try this way $ sudo setup.sh
If you really want to "ExecuteCall a shell script in current shell with sudo permission" you can use exec to...
replace the shell with a given program (executing it, not as new process)
I insist on replacing "execute" with "call" because the former has a meaning that includes creating a new process and ID, where the latter is ambiguous and leaves room for creativity, of which I am full.
Consider this test case and look closely at pid 1337
# Don't worry, the content of this script is cat'ed below
$ ./test.sh -o foo -p bar
User ubuntu is running...
PID TT USER COMMAND
775 pts/1 ubuntu -bash
1408 pts/1 ubuntu \_ bash ./test.sh -o foo -p bar
1411 pts/1 ubuntu \_ ps -t /dev/pts/1 -fo pid,tty,user,args
User root is running...
PID TT USER COMMAND
775 pts/1 ubuntu -bash
1337 pts/1 root \_ sudo ./test.sh -o foo -p bar
1412 pts/1 root \_ bash ./test.sh -o foo -p bar
1415 pts/1 root \_ ps -t /dev/pts/1 -fo pid,tty,user,args
Take 'exec' out of the command and this script would get cat-ed twice. (Try it.)
#!/usr/bin/env bash
echo; echo "User $(whoami) is running..."
ps -t $(tty) -fo pid,tty,user,args
if [[ $EUID > 0 ]]; then
# exec replaces the current process effectively ending execution so no exit is needed.
exec sudo "$0" "$#"
fi
echo; echo "Take 'exec' out of the command and this script would get cat-ed twice. (Try it.)"; echo
cat $0
Here is another test using sudo -s
$ ps -fo pid,tty,user,args; ./test2.sh
PID TT USER COMMAND
10775 pts/1 ubuntu -bash
11496 pts/1 ubuntu \_ ps -fo pid,tty,user,args
User ubuntu is running...
PID TT USER COMMAND
10775 pts/1 ubuntu -bash
11497 pts/1 ubuntu \_ bash ./test2.sh
11500 pts/1 ubuntu \_ ps -fo pid,tty,user,args
User root is running...
PID TT USER COMMAND
11497 pts/1 root sudo -s
11501 pts/1 root \_ /bin/bash
11503 pts/1 root \_ ps -fo pid,tty,user,args
$ cat test2.src
echo; echo "User $(whoami) is running..."
ps -fo pid,tty,user,args
$ cat test2.sh
#!/usr/bin/env bash
source test2.src
exec sudo -s < test2.src
And a simpler test using sudo -s
$ ./exec.sh
bash's PID:25194 user ID:7809
systemd(1)───bash(23064)───bash(25194)───pstree(25196)
Finally...
bash's PID:25199 user ID:0
systemd(1)───bash(23064)───sudo(25194)───bash(25199)───pstree(25201)
$ cat exec.sh
#!/usr/bin/env bash
pid=$$
id=$(id -u)
echo "bash's PID:$pid user ID:$id"
pstree -ps $pid
# the quoted EOF is important to prevent shell expansion of the $...
exec sudo -s <<EOF
echo
echo "Finally..."
echo "bash's PID:\$\$ user ID:\$(id -u)"
pstree -ps $pid
EOF
It works without "sudo".
bash setup.sh
Even the first answer is absolutely brilliant, you probably want to only run script under sudo.
You have to specify the absolute path like:
sudo /home/user/example.sh
sudo ~/example.sh
(both are working)
THIS WONT WORK!
sudo /bin/sh example.sh
sudo example.sh
It will always return
sudo: bin/sh: command not found
sudo: example.sh: command not found
The answers here explain why it happens but I thought I'd add my simple way around the issue. First you can cat the file into a variable with sudo permissions. Then you can evaluate the variable to execute the code in the file in your current shell.
Here is an example of reading and executing an .env file (ex Docker)
sensitive_stuff=$(sudo cat ".env")
eval "${sensitive_stuff}"
echo $ADMIN_PASSWORD
Easiest method is to type:
sudo /bin/sh example.sh

Beaglebone inittab issue

I am developing an application in beaglebone.
I want to add start up scripts to my Beaglebone but I can not find /etc/inittab.
I am using the image : Angstrom-Cloud9-IDE-GNOME-eglibc-ipk-v2012.05-beaglebone-2012.06.18.img.xz
I think in the previous versions of image there is /etc/initab but for the new distributions I could not find the inittab :/
I want to apply this : Automatic login on Angstrom Linux
but I can not because there is no /etc/inittab.
Where is the inittab in new distributions.
When I write uname -r it gives:
3.2.23
Regards
inittab has been replaced by systemd
This is how I did it for the serial console. You can probably adapt it easily for tty1 by replacing "serial-getty#..." by "getty#...", but I haven't tested it.
cp /lib/systemd/system/serial-getty#.service /etc/systemd/system/autologin#.service
rm /etc/systemd/system/getty.target.wants/serial-getty#ttyO0.service
ln -s /etc/systemd/system/autologin#.service /etc/systemd/system/getty.target.wants/serial-getty#ttyO0.service
Create the following script file in any location (/home/root/autologin.sh in my case)
#!/bin/sh
exec /bin/login -f root
Make it executable
chmod a+x autologin.sh
Edit /etc/systemd/system/autologin#.service and update the ExecStart command by adding the -n (Do not prompt the user for a login name) and -l (Invoke the specified login_program instead of /bin/login) options.
ExecStart=-/sbin/agetty -n -l /home/root/autologin.sh -s %I 115200

Resources