Thursday, February 22, 2024

switch_root PID 1

I am developing an initramfs with a custom init. For dev/test purposes I am spawning a bash shell at the end of the init startup sequence (but before the switch_root would normally happen) so I can work on complex parts of the boot process without doing a lot of build iteration.

I thought I had everything working, and did a test run - "exec switch_root /sysroot /sbin/init". That got me a nice kernel panic, the kind you get when init exits. Which is precisely what happens when you fork /bin/bash at the end of your init and exec switch_root from the forked shell.

The init process is PID 1 and the forked bash shell is... not PID 1. Attempting a switch_root from any process other than one running as PID 1 will not work.

The solution was to exec /bin/bash at the end of init so that the new shell becomes PID 1. Once I am happy with the way things work, I can get rid of the shell call and replace it with exec switch_root /sysroot /sbin/init.

Tuesday, January 30, 2024

Failed to connect to bus: No medium found

If you are trying to run a systemctl --user command as a non root user and getting "Failed to connect to bus: No medium found" this is probably because a few things are out of place.

First make sure you have linger turned on "sudo loginctl enable-linger $USER". This ensures that your services will hang around when you are not logged in.

Next, use the env command to make sure you have XDG_SESSION_ID and XDG_RUNTIME_DIR defined in your environment. If you do not see them, then you probably did not do a proper login. My guess is that you logged in as a different user and use the su command to change to your current user.

In that case, you pretty much have two choices.

  1. You can define XDG_RUNTIME_DIR manually with the following command: export XDG_RUNTIME_DIR=/run/user/$(id -u $USER)
  2. Log out and ssh back in directly as the desired user. This should trigger the PAM module that sets up the correct session variables.

Saturday, January 27, 2024

Fair and Honest Discourse

What follows are my personal rules and general philosophy for engaging in discourse. All of this information is tentative and will change in response to better information.

  • The purpose of discourse is to discard incorrect ideas.
  • I am a better person when I hold fewer incorrect ideas.
  • Discourse on a particular idea shall stop when:
    • requested.
    • it becomes unproductive.
    • everyone involved finds that they are in agreement.
  • Discourse that stops without general agreement is evidence of incomplete understanding.
  • General agreement is not evidence of the correctness of an idea.
  • Without the consent of those it affects, sensitive information shall be excluded from discourse.
  • A single piece of evidence is sufficient to disprove an idea.
  • An idea that has not been disproved must account for all relevant existing evidence.
  • An idea can never be proven to zero uncertainty.
  • Honest participation requires fair and full treatment of all proffered evidence.
  • One who discards an idea should graciously acknowledge the act.
  • One who discards an idea should credit the person who provided the evidence.
  • Goal posts can be moved as long as one clearly acknowledges their prior insufficiency.
  • The validity of a claim is not affected by its source.
  • The burden of proof is on the person making the claim.
  • Extraordinary claims require extraordinary evidence.
  • That which can be claimed without evidence can be discarded without evidence.

Monday, January 15, 2024

Furnace Maintenance

Disclaimer: Do your own research!  This information worked for me, but it may not be suitable for you. I am not responsible for anything that goes wrong when using this information.

This information applies to the following residential furnaces:

Carrier 58MVB, 58MVC, or 58MVP
Bryant: 355AAV, 355BAV, 355CAV, 355MAV
Payne: PG9UAA

All part numbers in the description below are current as of the date of this post. Manufacturers have a tendency to update part numbers, so do your research. The Carrier Enterprise site seems to be a good source of information, but unless you are an approved vendor you only have access to a minimal amount of information.

If your furnace is making a lot of grinding noises, your draft inducer assembly is probably going bad and needs to be replaced. This is an expensive part, but it is a small fraction of the cost of a new furnace and the replacement is something a reasonably handy homeowner can do on their own. The current draft inducer part number is 340793-762.

You will also need to ensure you have the correct pressure switch. If you have an old draft inducer assembly, your pressure switch will not fit on the new assembly and you will need to get a new one. If you have a 40,000 BTU/Hr furnace, you are going to need part number HK06NB025. For all other furnaces, you will need part number HK06NB023.

These pressure switches can be mounted to the bracket that comes with the 340793-762 draft inducer assembly, but you will need to find a few screws to make it happen. There are plenty of mount points on the pressure switch bodies, just be sure your screws are not so long that they pierce the plastic body of the pressure switch.

While undertaking this repair, it is important to check that the condensate trap and the hoses leading in to it are draining reliably. If any of these are plugged, condensation will back up and damage internal components of your furnace.

You might also find that some of your hoses are in bad shape. Part number 319873-715 is the hose that connects to the pressure switch (it has a pink label), and part number 319873-714 (green label) is the hose that connects to the condensate trap.

You should be able to find maintenance, troubleshooting, and installation manuals online. They will help a great deal when undertaking a repair like this.

Saturday, May 20, 2023

LVM - Restoring Files and Directories

If you run an AWS EC2 instance you can set up a policy that takes regular snapshots. For disaster recovery, it is pretty simple to convert a snapshot to a volume and attach it to your EC2 instance in place of the previously failed volume. It gets a little more complicated if you are trying to restore individual files or directories that are contained within logical volumes.

The first step is to convert your most recent snapshot into a volume and attach it to your EC2 instance. In general you should do this while your EC2 instance is running in order to avoid any confusion at startup. You should see a reference to the new volume in your kernel ring buffer shortly after it is attached. 

Because the volume group on your new volume has the same name and UUID as the volume group that is currently in use, you will not be able to restore files just yet. You will need to use the vgimportclone command to update the volume group name and UUID.

# vgimportclone <devicename>

Use the vgdisplay command to list your volume groups. You should now see a new volume group in the output which will need to be activated with the vgchange command. This should cause device nodes to show up in the /dev/mapper directory.

If you have used the xfs filesystem format on your logical volumes, your filesystems will have identical UUIDs to the ones currently mounted. There are ways of updating the xfs UUID, or you can pass the -o nouuid argument to the mount command.

Once you are done restoring files, unmount the volumes, deactivate the volume group with the vgchange command, detach the volume from your EC2 instance, and delete it.


Wednesday, January 19, 2022

Quick and Dirty Function to get Rounded Up Log2

 Here is a quick and dirty C/C++ function to return the rounded up log base 2 of any integer.

int ceil_log_base_2(int value) {
    int ones = 0;
    int shifts = 0;
    while (value > 0) {
        shifts++;
        if (1 & value)
            ones++;
        value = value >> 1;
    }
    return --shifts + (ones > 1 ? 1 : 0);
}

My buddy Rob jumped in and spun this one x86 assembly style:

int __builtin_popcount (unsigned int x);
int __builtin_clz (unsigned int x);

int rob_ceil_log_base_2 (int value) {
    return ((sizeof(int) * 8) - __builtin_clz (value)) +
        (__builtin_popcount(value) > 1 ? 1 : 0) - 1;
}

And if you prefer inline, Rob has you covered there too:

int __builtin_popcount (unsigned int x);
int __builtin_clz (unsigned int x);
#define rob_ceil_log_base_2(value) (int) (sizeof(value) * 8) - \     __builtin_clz (value) + (__builtin_popcount(value) > 1 ? 0 : -1)

Sunday, February 21, 2021

Bootable Linux Sparse Virtual Disk Images

The following recipe will get you a bootable sparse disk image that is 20GB in size, but only takes up a minimal amount of disk space (about 4.5MB to start). This process is suitable for creating disk images for Linux virtual machines.

First step is to create your sparse file:

truncate example.img --size 20G

This sets the size to about 20 gigabytes, but in reality it is not taking up any space:

# ls --size --block-size=1 example.img

0 example.img

# stat --format='%s' example.img

21474836480

Since you probably want to install a bootloader in order to make this a bootable image, we are going to need a partition table. I prefer the parted command for this over our old friend fdisk, since parted is a bit easier to script.

# parted example.img mklabel gpt

This creates a GPT partition table in the first 2048 sectors of the image file. The default partition type is MBR, which is fine if you plan on staying under 2TB, and do not mind dealing with extended and logical partitions. I see little to be lost by using GPT, since it part of UEFI, and is backward compatible with legacy BIOS type systems.

# parted example.img print

Model:  (file)
Disk /tmp/example.img: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start  End  Size  File system  Name  Flags

Checking on the size, we see that our image file takes up about 40 kbytes, despite still appearing to be 20 gigabytes in size.

# ls --size --block-size=1 example.img

40960 example.img

# stat --format='%s' example.img

21474836480
 

Now we can add a partition. In this case I am only going to create one partition that uses all of the available space and I am going to give it the name "vm-root" to avoid confusion later.

# parted example.img mkpart primary 0% 100%

# parted example.img name 1 vm-root

# parted example.img print

Model:  (file)
Disk /tmp/example.img: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Disk Flags:

Number  Start   End     Size    File system  Name     Flags
 1      1049kB  21.5GB  21.5GB               vm-root

Now we format the partition; I generally use ext4 these days. There is a fairly significant limitation to the ext4 mkfs tooling that forces us to use loopback devices at this point.

It was my hope that mkfs would figure out the partition size on its own, or perhaps let me specify it as an argument. But all attempts to do that caused mkfs to overrun the partition boundaries and break the backup GPT partition table.

When you specify fs-size to the mkfs.ext4 command, what you are specifying is the usable space you want, not the actual size of the available volume. The mkfs.ext4 command gets the volume size from the kernel's block layer, and then juggles a lot of complex logic to figure out how much space needs to be burned for meta information like superblocks and inode tables.

I probably could have figured out the math for the fs-size argument and formatted the image file partition directly, but there are too many variables to make me feel like that is a good use of my time. That being said, it would be of nice if the mkfs tooling got a virtual image mode that either allowed you to specify the device size, or detected the partition size from the partition table.

We need to bind our image file and partition to a loopback device:

DEV=$(losetup --show --find --partscan example.img)

 And now we can format our new partition:

# mkfs.ext4 -F ${DEV}p1

mke2fs 1.45.6 (20-Mar-2020)
/dev/loop0p1 contains a ext4 file system
        created on Sun Feb 21 19:32:16 2021
Discarding device blocks: done                            
Creating filesystem with 5242368 4k blocks and 1310720 inodes
Filesystem UUID: ede5cf4d-f7f6-4747-8c7a-28d794f92b92
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

 Another size check shows that we are now up to about 4.5 megabytes.

# ls --size --block-size=1 example.img

4505600 example.img

# stat --format='%s' example.img

21474836480

Pretty good so far. Now we can mount our new volume and take a look around.

# mkdir img-mp

# mount ${DEV}p1 img-mp

# df img-mp

Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/loop0p1    20509264 45080  19399328   1% /tmp/img/img-mp

And now for the cleanup:

# umount ${DEV}p1

# losetup -d ${DEV}

To make this a bootable image, mount your volume again and copy the Linux OS file tree of your choice into the mounted volume. Then use the grub-install command to install the boot loader.

# grub-install --modules=part_gpt --root-directory /tmp/img/img-mp ${DEV}

Since this is a sparse image, it will grow larger as more data is written to it until you hit the size limit, but it will not get smaller when data is deleted.

The simplest way to compact this image is to use the zerofree command to zero out the empty space, and then use your VM hypervisor's tools to do the rest, such as virt-sparsify or VirtualBox modifymedium ${FILENAME} --compact.