User:Goverp/Genlist

This article contains Article description::a shell script to generate gen_init_cpio input from a configuration file, for use when generating initramfs files or configurations for the linux kernel.

The idea is to create the configuration file to specify the programs used in your /init and list the resources (files, devices etc) that it uses. The script locates the program files, and the libraries on which they depend, and generates the necessary gen_init_cpio instructions to build the appropriate directory tree and files and so forth.

The script works by generating a list of all resources and their dependencies (i.e. libraries, files, and the directories in their paths), and throws the list at "tsort" to generate a minimal ordered set from it, and then expands the results into the gen_init_cpio input.

Usage is to run: and then do whatever is appropriate to your kernel/bootloader configuration.

{{

{{FileBox|title=gen_init-cpio input configuration|filename=/root/genlist.conf|lang=bash|1=
 * 1) Configuration file for the genlist initramfs_list builder
 * 2) It will be sourced by the builder, so its contents must contain a valid shell script
 * 3) The variables assigned should only be "monitor, programs, paths, files and slinks"

programs="mdadm fsck.f2fs busybox"
 * 1) Executable programs to be copied (with their dependent libraries) into the initramfs

paths="/proc /sys /dev/console /dev/tty0 /dev/tty1 /dev/null /mnt/root"
 * 1) Directory paths and device nodes to be created in the initramfs.
 * 2) If a path exist in the invoking environment,
 * 3) attrs, Uid, Guid and type will be copied from it,
 * 4) otherwise defaulted to 755 0 0
 * 5) You probably need at least /proc /sys /dev/console /dev/tty0 /dev/tty1 /dev/null /mnt/root

files="/init:=/usr/src/initramfs/init /etc/mdadm.conf"
 * 1) Files to be copied into the initramfs, optionally moved if there's a new path/name preceding ":="
 * 2) Don't forget to include your init file moved to "/init"

slinks="/bin/sh:=busybox" }}
 * 1) Symbolic links to create in the initramfs, format "link->target"

{{FileBox|title=gen_init-cpio input generator|filename=/root/genlist|lang=bash|1=
 * 1) ! /bin/sh

doLicence { cat <<- 'endLicence' genlist.sh - Version 0.9 - Generate gen_init_cpio input from genlist.conf

Copyright (C) 2020 Paul Gover

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by   the Free Software Foundation, either version 3 of the License, or    (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see . endLicence }

doHelp { cat << 'endHelp' Usage: genlist.sh [OPTION]...

Where the output is determined by the genlist.conf file.

Options -h	print this help text -l	print the licence information -d	print the list of Gentoo packages containing all the programs listed in genlist.sh	(Monitor updates to these packages to trigger rerunning genlist.sh)

Files genlist.conf in the same directory as genlist.sh, is sourced as a shell script. It should define three variables: programs, a list of the programs used by the initramfs's init script or program typically these will include either busybox or a shell such as dash and at least switch_root paths, a list of directories and nodes needed by the initramfs; typically these will include /proc, /sys, /dev and /dev/console files, a list of "name" or "name:=location" of files to be copied into the initramfs typically including at least "/init:= " slinks, a list of symbolic links to be created, typically including "/bin/sh:=busybox" or "/bin/sh:=" endHelp }

error { printf "%s\n" "$*" >&2 exit 1 }
 * 1) Issue an error message and quit

DEFAULT_PROPERTIES="755 0 0 0 0 directory"

listProgramDependencies { local program path library file for program in "$@" do path=$(which "$program") listFileDependencies "$path"
 * 1) For each program in the paramter list,
 * 2) list the program file, and its dynamically-linked libraries

ldd "$path" 2>/dev/null {{!}} while read library do case "$library" in			*{{=}}\>*)				file="${library#*=>}"				file="${file%(*}" listFileDependencies "$file" ;;			*\/*)				file="${library%(*}" listFileDependencies "$file" ;;			linux-*)				# Kernel-provided				;;			*) error "Unexpected ldd $path output $library" ;;			esac done done }

listPathDependencies { local path parent for path in "$@" do [ -n "${path##/*}" ] && continue		# Ignore relative paths
 * 1) For each file in the parameters,
 * 2) return lines specifying the dependency chain of directories in its path, plus a dependency on itself
 * 3) (thus handling root directories such as /proc).
 * 4) "/usr/src/linux" would generate "/usr/src/linux /usr/src" and "/usr/src /usr"
 * 5) as well as "/usr/src/linux /usr/src/linux"

parent="$path" while [ "$parent" ] do printf "%s %s\n" "$path" "$parent" path="$parent" parent="${path%/*}" done done }

listFileDependencies { local file path parent for file in "$@" do       path="${file%:=*}" parent="${path%/*}" printf "%s %s\n" "$file" "$path" listPathDependencies "$path" done }
 * 1) Like listPathDependencies, but ignoring any location in "name:=location"

listLinkDependencies { local link name for link in "$@" do name="${link%:=*}" listPathDependencies "${name%/*}" done }
 * 1) Like listPathDependencies, but ignoring locations and the link files themselves.
 * 2) The gen_init_cpio lines are generated separately.

doStat { local st	if st=$(stat --dereference --format "%n %a %u %g %t %T %F" "$1" 2>/dev/null) then	parseStat $st else	parseStat $1 $DEFAULT_PROPERTIES fi }
 * 1) Stat the named file, and set nonlocal variables accordingly.
 * 2) Use DEFAULT_PROPERTIES if the file does not exist on the executing system

parseStat { name=$1 attr=$2 uid=$3 gid=$4 maj=$5 min=$6 type=$7 }
 * 1) Note, parseStat sets nonlocal variables

expand { local line while read line do expandFile "$line" done }
 * 1) A pipe filter to convert the file name or "name:=location" pairs in the input
 * 2) into gen_init_cpio input lines

expandFile { local target source local name attr uid gid maj min type	# Fields from parsed stat call target="${1%:=*}"
 * 1) Print a gen_init_cpio input line to create a node/directory/file/pipe or socket
 * 2) Input is the name or "name:=location" pair from which to generate the line
 * 3) The output will have attr, uid, guid etc. copied from the executiing system's version of the named file,
 * 4) assuming it has one, otherwise the default.
 * 5) Symbolic links get treated as files, the line getting the link's name but the stat for its target

if [ "$target" = "$1" ] then	source="$target" else	source="${1#*:=}" fi

doStat "$source" case "$type" in	character)     printf "nod   %-61s %4s %4s %4s c %s %s\n" "$target" "$attr" "$uid" "$gid" "$maj" "$min";;	block)          printf "nod   %-61s %4s %4s %4s b %s %s\n" "$target" "$attr" "$uid" "$gid" "$maj" "$min";; directory)     printf "dir   %-61s %4s %4s %4s\n" "$target" "$attr" "$uid" "$gid";;	symbolic)	printf "file  %-30s %-30s %4s %4s %4s\n" "$target" "$source" "$attr" "$uid" "$gid";; regular)	printf "file %-30s %-30s %4s %4s %4s\n" "$target" "$source" "$attr" "$uid" "$gid";;	# Not sure pipes and socks are relevant, but gen_init_cpio supports them	pipe)		printf "pipe  %-61s %4s %4s %4s\n" "$target" "$attr" "$uid" "$gid";; sock)		printf "sock %-61s %4s %4s %4s\n" "$target" "$attr" "$uid" "$gid";;	*) 		error "Unexpected type $type from stat $source";; esac }


 * 1) Mainline code
 * 1) Mainline code

mypath="${0%/*}" if [ "$mypath" = "$0" ] then mynameext="$mypath" mypath="." else mynameext="${0##*/}" mypath="$mypath" fi
 * 1) Preamble: find the config file, and handle any parameters

myname="${mynameext%.*}" myconf="$mypath/$myname.conf" if [ -r "$myconf" ] then 	. "$myconf" else	error "No configuration $myconf" fi

[ "0" != "$(id -u)" ] && printf "\n%s\n\n" "Warning - $myname may give incomplete output if not run as root" >&2

while getopts hld f do	case $f in	h)	doHelp ; exit ;;	l)	doLicence ; exit ;; d)	qfile -q $(which $programs) {{!}} sort -u ; exit ;;	*)	doHelp ; exit ;; esac done shift $(( OPTIND - 1 ))
 * 1) Handle options


 * 1) Preamble over.

{	listPathDependencies $paths; listProgramDependencies $programs; listFileDependencies $files; listLinkDependencies $slinks;	# link dependencies are just the parent directories } {{!}} tsort {{!}} tac {{!}} expand
 * 1) Generate a list of all files, nodes and directories, with their dependencies.
 * 2) Chuck the lot at tsort to build a minimal list in the right order.
 * 3) (actually the reverse order, so in turn chuck that at tac.
 * 4) Finally, expand the results to make gen_init_cpio input.

for link in $slinks do name="${link%:=*}" target="${link#*:=}" [ -z "$target" ] && error "Invalid slink definition $link"
 * 1) And handle slinks, which may not be files, and therefore need different handling

printf "slink %-30s %-30s %4s %4s %4s\n" "$name" "$target" 777 0 0 done

}}