Posts in category “Linux”

PopOS System Recovery and Partition Migration Notes

Background

Yesterday, my PopOS system broke unexpectedly. The refresh install option failed, and attempting to reinstall while preserving the old /var and /home partitions was unsuccessful.

Solution: Fresh Custom Installation

Performed a clean custom installation with:

  • /boot/efi - formatted
  • / - formatted
  • /recovery - formatted

This approach worked successfully, then migrated data from old partitions.

Partition Migration Process

Tools Used

parted --list    # Get partition information
blkid           # Get partition UUIDs

Note: Couldn't stop GDM or enter single-user mode (system would hang). Performed migration on running system instead.

1. Home Partition Migration (/dev/nvme0n1p8)

sudo mv /home /home.bak          # Backup current home
sudo mkdir /home
sudo mount /dev/nvme0n1p8 /home
ls /home                         # Confirm correct partition
sudo blkid /dev/nvme0n1p8       # Get UUID
sudo vi /etc/fstab

Added to /etc/fstab:

UUID=32e4ed56-6ed9-4f14-9585-ffff54f997b2 /home ext4 defaults 0 2

Rebooted to verify system stability.

2. Backup Partition Setup (/dev/nvme0n1p10)

sudo parted --list
sudo blkid /dev/nvme0n1p10
sudo vi /etc/fstab

Added to /etc/fstab:

UUID=8aba0dd5-7058-4036-8a08-fcd1e0e002bc /backup ext4 defaults 0 2
sudo mount -a                   # Test mount configuration

3. Var Partition Migration (/dev/nvme0n1p7)

# Mount old var partition and backup data
sudo mkdir /mnt/var
sudo mount /dev/nvme0n1p7 /mnt/var
sudo tar -czpvf /backup/old-var-backup.tar.gz -C /mnt var

or backup docker/crontabs only

sudo tar -czpvf /backup/old-docker-backup.tar.gz -C /mnt var/lib/docker
sudo tar -czpvf /backup/old-cron-backup.tar.gz -C /mnt var/spool/cron

# Prepare new var partition
sudo umount /mnt/var
sudo mkfs.ext4 /dev/nvme0n1p7   # Format old var partition

# Copy current var data to new partition
sudo mkdir /mnt/new-var
sudo mount /dev/nvme0n1p7 /mnt/new-var
sudo cp -av /var/* /mnt/new-var/

# Update fstab and switch to new partition
sudo blkid /dev/nvme0n1p7
sudo vi /etc/fstab              # Add var partition entry
sudo mv /var /var.old && sudo mkdir /var && sudo mount -a

Restore old var data

# update system
apt update && apt upgrade

sudo apt purge nano # enable vi EDITOR in visudo
sudo apt install curl

# re-enable davidwei ALL=(ALL:ALL) NOPASSWD:ALL

# install docker
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor --yes -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # add docker apt source
sudo apt update && sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# add current user to docker group
sudo usermod -aG docker davidwei

# restore old docker/cron data
# 1. Stop Docker services properly
sudo systemctl stop docker.socket
sudo systemctl stop docker.service

# 2. Extract docker data in one command
sudo tar -xzpf /backup/old-var-backup.tar.gz -C / var/lib/docker
sudo tar -xzpf /backup/old-var-backup.tar.gz -C / var/spoon/cron

or if there are spearate backups:

sudo tar -xzpf /backup/old-var-backup.tar.gz -C / var/lib/docker
sudo tar -xzpf /backup/old-var-backup.tar.gz -C / var/spool/cron/crontabs

sudo chmod og+rx /var/spool/cron/crontabs/
sudo chown davidwei:davidwei /var/spool/cron/crontabs/davidwei
sudo systemctl restart cron

# 3. Start Docker services
sudo systemctl start docker.service
sudo systemctl start docker.socket

# 4. Verify
docker volume ls
docker images
docker ps -a

# re-install tailscale / set no-expiry / rename to xps / change cloudflare dns to new IP
curl -fsSL https://tailscale.com/install.sh | sh

# re-install google-chrome
sudo dpkg -i ~/Downloads/google-chrome-stable_current_amd64.deb

# reinstall other utils
sudo apt install tree ncdu vim gnome-sushi mc clang ninja-build mesa-utils libgtk-3-dev cmake mysql-client-core-8.0 libstdc++-12-dev build-essential pkg-config htop mc ssh meld

# reinstall fcitx
sudo apt install fcitx5-rime fcitx5-chinese-addons

Key Lessons Learned

  • Fresh installation with custom partitioning was more reliable than trying to preserve old partitions during install
  • System migration on a running system worked fine when rescue mode wasn't accessible
  • Always backup critical data before formatting partitions
  • Test each partition mount configuration before proceeding to the next step

Status: Migration completed successfully. System running normally with all data preserved.

Quick Fix: Claude Code Image Paste in Linux Terminal

Can't paste images to Claude Code in your Linux terminal? Here's a one-minute fix for Kitty users.

The Fix

1. Create the script (~/bin/clip2path):

#!/usr/bin/env bash
set -e

if [ -n "$WAYLAND_DISPLAY" ]; then
    types=$(wl-paste --list-types)
    if grep -q '^image/' <<<"$types"; then
        ext=$(grep -m1 '^image/' <<<"$types" | cut -d/ -f2 | cut -d';' -f1)
        file="/tmp/clip_$(date +%s).${ext}"
        wl-paste > "$file"
        printf '%q' "$file" | kitty @ send-text --stdin
    else
        wl-paste --no-newline | kitty @ send-text --stdin
    fi
elif [ -n "$DISPLAY" ]; then
    types=$(xclip -selection clipboard -t TARGETS -o)
    if grep -q '^image/' <<<"$types"; then
        ext=$(grep -m1 '^image/' <<<"$types" | cut -d/ -f2 | cut -d';' -f1)
        file="/tmp/clip_$(date +%s).${ext}"
        xclip -selection clipboard -t "image/${ext}" -o > "$file"
        printf '%q' "$file" | kitty @ send-text --stdin
    else
        xclip -selection clipboard -o | kitty @ send-text --stdin
    fi
fi

2. Make executable:

chmod +x ~/bin/clip2path

3. Add to ~/.config/kitty/kitty.conf:

allow_remote_control yes
listen_on unix:/tmp/kitty-socket
map ctrl+v launch --type=background --allow-remote-control --keep-focus ~/bin/clip2path

4. Install dependencies:

# X11 users only
sudo apt install xclip

5. Restart Kitty

6. Setup automatic cleanup (optional):

# Add to crontab to clean old screenshots daily
(crontab -l 2>/dev/null; echo "0 3 * * * find /tmp -name 'clip_*' -type f -mtime +1 -delete") | crontab -

Now Ctrl+V automatically saves clipboard images as temp files and pastes their paths. Works on both Wayland and X11.

Simplify MySQL Connections with a Smarter Bash Function

Working with multiple MySQL environments is a common scenario for developers. Whether you're connecting to development, staging, or production databases, you often need different connection parameters for each environment. While MySQL's configuration files help manage these connections, switching between them can be cumbersome. Let me introduce a simple but powerful bash function that will streamline this process.

The Problem

If you work with MySQL regularly, you likely have multiple connection profiles defined in your ~/.my.cnf file using the [group] syntax. To use a specific configuration group, you need to use the --defaults-group-suffix option:

mysql --defaults-group-suffix=dev
mysql --defaults-group-suffix=prod

This works, but it's verbose and typo-prone.

The Solution: A Smarter Bash Function

Here's a bash function that makes working with MySQL much more convenient:

m() {
    if [ $# -eq 0 ]; then
        # If no arguments provided, run mysql directly
        mysql
    elif [[ "$1" == -* ]]; then
        # If first argument starts with -, pass all arguments directly to mysql
        mysql "$@"
    else
        # Otherwise, treat first argument as suffix
        local suffix=$1
        shift
        mysql --defaults-group-suffix=$suffix "$@"
    fi
}

How It Works

This function provides three different behaviors depending on how you call it:

  1. No arguments: Simply runs the MySQL client with default settings.

    m
    
  2. First argument starts with a dash: Passes all arguments directly to MySQL, just like you're using the mysql command.

    m -e "SHOW DATABASES"
    
  3. First argument doesn't start with a dash: Uses the first argument as the defaults-group-suffix and passes the remaining arguments to MySQL.

    m dev -e "SELECT * FROM users"
    

Practical Examples

Example 1: Connect to Different Environments

m dev    # Connect to development database
m prod   # Connect to production database
m stage  # Connect to staging database

Example 2: Run Commands Directly

m dev -e "SELECT COUNT(*) FROM orders"
m prod -N -e "SELECT id FROM customers LIMIT 5"  # -N removes column names

Example 3: Use MySQL Options Directly

m -N -e "SELECT VERSION()"  # Quick query without environment prefix
m -t -e "SHOW TABLES"  # Table format output

Setup Instructions

  1. Add the function to your .bashrc or .zshrc file.
  2. Set up your MySQL configuration groups in ~/.my.cnf:
[client]
# Default settings

[clientdev]
user=devuser
password=devpass
host=dev-db.example.com

[clientprod]
user=produser
password=prodpass
host=prod-db.example.com

[mysql]
database=YourDataBase
  1. Source your shell configuration file or restart your terminal.

Benefits

  • Brevity: No need to type connection parameters (-h, -u, -p) repeatedly, as they're stored in your config file.
  • Flexibility: Pass any MySQL options like -N (skip column names) or -e (execute) alongside environment selection.
  • Consistency: Maintain the standard MySQL command syntax for direct option usage.

This simple function has saved me countless keystrokes and made database work significantly smoother. It's especially handy for running quick queries with options like -e (execute) and -N (no headers) without having to type connection details every time. Give it a try in your workflow—small improvements like this add up to a much more efficient development experience!

Do you have any favorite shell functions that make your development life easier? Share them in the comments!

Quick Guide to Installing postmarketOS on Asus C100P

This guide helps you install PostmarketOS on your C100P's internal storage (emmc hard disk).

Step 1: Install pmbootstrap

First, install the latest version of pmbootstrap on Debian 12 via git:

git clone https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git
cd pmbootstrap
mkdir -p ~/.local/bin
ln -s "$PWD/pmbootstrap.py" ~/.local/bin/pmbootstrap
pmbootstrap --version

Make sure to add ~/.local/bin to your PATH environment variable.

Step 2: Initialize pmbootstrap

Run pmbootstrap --init, and if any dependencies are missing, install them using sudo apt install <package> as instructed. For the vendor, select google instead of asus, and for the codename, choose veyron.

Step 3: Prepare a USB Drive

Prepare a USB drive and plug it into your computer. Use sudo fdisk -l to check the device name of the USB drive (usually /dev/sdb or something else). Be very careful, as choosing the wrong device can lead to data loss. Then, use the following command to begin the installation:

pmbootstrap install --sdcard /dev/sdx

Remember to replace /dev/sdx with the correct device name of your USB drive.

Step 4: If You Encounter Mounting Errors

If you run into errors when trying to mount, try using a different USB drive. I had an issue with a 128GB Sandisk drive, but it worked fine with a 32GB SD card.

Step 5: Boot Into Asus C100P

Boot your Asus C100P using the prepared USB drive, and connect to a Wi-Fi network.

Step 6: Install pmbootstrap on C100P

On the C100P, install pmbootstrap using the same steps from Step 1, then run "sudo apk add openssl".

Step 7: Initialize pmbootstrap

Run pmbootstrap init on the C100P, following the same instructions from Step 2.

Step 8: Install to eMMC

Use the following command to install postmarketOS to the eMMC:

pmbootstrap install --sdcard /dev/mmcblk0 # on my c100p it is mmcblk0, please check yours before press enter!

Step 9: Done! Remove the USB Drive

Once the installation is complete, remove the USB drive. postmarketOS should now be installed on your Asus C100P!

继续折腾:在 Asus C100P 上安装 postmarketOS 简要记录

这篇blog教你如何将 postmarketOS 安装到 C100P 的内置emmc硬盘上。我装了最新的 edge 系统和 Mate桌面,还余 10G空间可用。

步骤 1: 安装 pmbootstrap

首先,在 Debian 12 上通过 git 安装最新版 pmbootstrap:

git clone https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git
cd pmbootstrap
mkdir -p ~/.local/bin
ln -s "$PWD/pmbootstrap.py" ~/.local/bin/pmbootstrap
pmbootstrap --version

确保把 ~/.local/bin 添加到你的 PATH 环境变量中。

步骤 2: 初始化 pmbootstrap

运行 pmbootstrap --init,如果有缺少的依赖包,按提示使用 sudo apt install <package> 安装。对于厂商,选择 google 而不是 asus,对于 codename,选择 veyron

步骤 3: 准备优盘

准备一个优盘并接入电脑,运行 sudo fdisk -l 来确认优盘的设备名(通常是 /dev/sdb 或其他)。一定要小心,不要选错设备名,否则可能会导致数据丢失。然后使用以下命令开始安装:

pmbootstrap install --sdcard /dev/sdx

注意,将 /dev/sdx 替换为你自己的优盘设备名。

步骤 4: 如果出现挂载错误

如果出现挂载错误,尝试换一块优盘。我遇到的问题是,128GB 的 Sandisk 优盘无法正常工作,但换成了一块 32GB 的 SD 卡后就顺利安装了。

步骤 5: 启动到 Asus C100P 上

用准备好的优盘启动 Asus C100P,连接 Wi-Fi 网络。

步骤 6: 安装 pmbootstrap 到 C100P

在 C100P 上安装 pmbootstrap,按照步骤 1 中的方式安装。

步骤 7: 初始化 pmbootstrap

在 C100P 上运行 pmbootstrap init,按照步骤 2 中的说明完成初始化。

步骤 8: 安装到 eMMC

运行以下命令将 postmarketOS 安装到 eMMC:

pmbootstrap install --sdcard /dev/mmcblk0

步骤 9: 完成并移除优盘

安装完成后,拔掉优盘,你的 Asus C100P 上就安装好了 postmarketOS!