# embeded linux post install [linux mass config](/wCXyj5uWTWmBode5vdr8Gw) ## 目的 post install on linux 一般是在 system core, user own app 外,需要額外的 middleware, set up 工具 去幫助 user app 達成更好的效果,甚至是必要的環境設定。 1. system core: e.g. glibc 2. middleware 1. mosquitto, x windows, WM, x11vnc, avahi(mDNS)... 2. 一般不會是 libs for user app。 3. user app 1. linux flutter apps 理論上只要 libgtk3,但 沒有 windows manager 可能會有各種奇怪反應。 post install 集中在 layer 2 middleware,也是接下來的討論方向。 ## design aspect of buildroot buildroot deliberately usually ship without pkg manager(PM). 這牽涉到 buildroot 的設計理念。 * Image-based design: Buildroot cross-compiles everything and emits a complete, immutable rootfs image. No in-field dependency resolution. * Reproducibility: Exact versions, configs, and toolchain are fixed at build time. A runtime PM would break determinism. * ABI/toolchain coupling: Third‑party binary feeds (Debian/Alpine/OpenWrt) are built with different toolchains/ABIs and often break on Buildroot systems. * Footprint and robustness: Embedded systems often use read-only rootfs, limited storage/flash write cycles, and need a small attack surface. * 根目錄 用 read only e.g. `/bin`, `/usr/bin` --> 無法更新。 * [`squashfs`](https://www.toradex.com/blog/shi-yongsquashfs-heoverlayfs-ti-gao-qian-ru-shilinux-wen-jian-xi-tong-ke-kao-xing)+`overlayfs` * Maintenance model: Updates are done by rebuilding a new image, not by partial upgrades. 所以對於 buildroot 產生的系統來說,任何些微的安裝,實際上都是 重新 copy 整個系統。即使 image 可以用 OTA update。 > Buildroot 属于“通用”工具,它可以跨系统广泛使用。这种理念的另一个有趣点就是:Buildroot 会生成根文件系统镜像,而非完整的发行版。也就是说,每当需要更新时,您都需要重新生成整个镜像。嵌入式系统默认不同于桌面或服务器发行版。正是在这样的理念下,项目团队专门设计成如此机制。故障或部分更新都有可能造成灾难性的影响,而从头开始重建镜像,就可以完全避免这种问题。 > [ref](https://zhuanlan.zhihu.com/p/671536247) > buildroot 可增量編譯 (by make 天性),但不能分享 builded package 相比之下,yocto 把選擇權給 user,他是 general purpose distribution build tool。 ## incompatible to apt dpkg/apt, rpm/dnf 的想法不同。 `dpkg -i` 不會自動 install deps,`apt install` 才會。 pkg 間的依賴關係就是一張網,一切的 root 就是 glibc (libc6.so)。系統出廠時就要帶有 PM,且 mark 好所有 default installed pkgs。 而且 PM 的期待是盡量大的 tree graph,如此,更新 一個靠近 root 的節點,大量 leaf 節點就已更新。 當 這種 PM 放在沒有 PM 的 embeded OS 時,它自然就會想重裝 glibc,好讓所有 pkgs 都在他的控制下。 而重裝 glibc 就會導致 glibc API version break,導致系統預裝的軟體全部失效 --> system break。 唯一可能事後重用 PM 的方法,就是將目前系統裝的 libs,以 PM 的 pkgs 表達 (read only),使 PM 了解系統所處的狀態。 或是兩個 glibc(獨立系統)-->虛擬機,"graph in scope"。 ### characteristic To mark a package as installed with dpkg, you can use the apt-mark command, which is a front-end for dpkg selections. ``` sudo apt-mark manual <package-name> ``` dpkg 好像沒有 直接的 command,會變成需要手動寫file。 --- I just manual install a lib through `make install`. How can I use dpkg to mark this libs is installed? Short answer: you can’t. dpkg only tracks .deb packages. A plain make install is invisible to it. Practical options: * Reinstall via a .deb (recommended, tracked and removable) * Use checkinstall to wrap make install and produce a .deb: * ``` # build make # build and install a .deb tracked by dpkg sudo checkinstall --pkgname=your-lib-name --pkgversion=1.0 --pkgrelease=1 --install=yes ``` * Satisfy dependencies with a dummy package (no files, just marks “installed”) * Use `equivs` to create a fake package that Provides the library: * ``` sudo apt-get update sudo apt-get install -y equivs mkdir -p ~/pkg && cd ~/pkg equivs-control your-lib # Edit the generated 'your-lib' control file: # Package: libfoo1 # Version: 1.0 # Architecture: amd64 # Provides: libfoo1 (= 1.0) # Conflicts: libfoo1 # Replaces: libfoo1 # Description: Dummy package to satisfy libfoo1 (installed from source) equivs-build your-lib sudo dpkg -i ./libfoo1_1.0_amd64.deb ``` * <-- only option for my use case * 沒看過有人實際使用。 還要先想辦法弄出 equivs。 好處是**維持在單一的 PM系統**。 ### force install 如果放棄完整的deps 解析,是否也還能用? 如果 glibc 週圍一圈 pkgs force install ,後續 pkgs 是否還會提醒 deps break。 ```bash alex@alex-Lenovo-Y520-15IKBN:~$ apt-rdepends zstd Reading package lists... Done Building dependency tree... Done Reading state information... Done zstd Depends: libc6 (>= 2.34) Depends: libgcc-s1 (>= 3.3.1) Depends: liblz4-1 (>= 0.0~r127) Depends: liblzma5 (>= 5.1.1alpha+20120614) Depends: libstdc++6 (>= 12) Depends: zlib1g (>= 1:1.1.4) libc6 Depends: libcrypt1 (>= 1:4.4.10-10ubuntu4) Depends: libgcc-s1 libcrypt1 Depends: libc6 (>= 2.25) libgcc-s1 Depends: gcc-13-base (= 13.1.0-8ubuntu1~22.04) Depends: libc6 (>= 2.35) gcc-13-base liblz4-1 Depends: libc6 (>= 2.14) liblzma5 Depends: libc6 (>= 2.34) libstdc++6 Depends: gcc-13-base (= 13.1.0-8ubuntu1~22.04) Depends: libc6 (>= 2.34) Depends: libgcc-s1 (>= 4.3) zlib1g Depends: libc6 (>= 2.14) debtree zstd | dot -Tpng -o dependencies.png #prefer ``` ```bash mkdir -p /var/lib/dpkg/info/ touch /var/lib/dpkg/status ``` verify ```bash dpkg --force-depends -i lz4_1.9.2-2ubuntu0.20.04.1_armhf.deb liblz4-tool_1.9.2-2ubuntu0.20.04.1_all.deb #only left libc6.so lz4 - root@buildroot-tx7:~/armv7_out# dpkg -i zstd_1.4.4+dfsg-3ubuntu0.1_armhf.deb dpkg: package zstd depends on libc6, which is not installed or flagged to be installed dpkg --force-depends -i zstd_1.4.4+dfsg-3ubuntu0.1_armhf.deb liblzma5_5.2.4-1ubuntu1.1_armhf.deb liblz4-1_1.9.2-2ubuntu0.20.04 .1_armhf.deb cd / && find / -name liblzma* ln -s /lib/arm-linux-gnueabihf/* /lib/ cd / && find -name liblz4 ln -s /usr/lib/arm-linux-gnueabihf/* /usr/lib/ # cp -r -u /media/sda1/armv7_out/ /root/ ``` 所以 dpkg 一樣 會去找 deps,後續的install 也會跳出 liblzma5 depends on libc6,很難說是正常用。 小 program e.g. lz4, zstd, nano 還算能正常安裝。 試大一點的 e.g. mosquitto。 首先是 deps 很多,很難區分 mosquitto 的deps。直接force install 之後 ``` root@buildroot-tx7:~/armv7_out# which mosquitto /usr/sbin/mosquitto root@buildroot-tx7:~/armv7_out# ldd /usr/sbin/mosquitto libwrap.so.0 => not found libsystemd.so.0 => not found libdlt.so.2 => not found libpthread.so.0 => /lib/libpthread.so.0 (0xa6ecd000) libssl.so.1.1 => /usr/lib/libssl.so.1.1 (0xa6e63000) libcrypto.so.1.1 => /usr/lib/libcrypto.so.1.1 (0xa6c70000) libanl.so.1 => /lib/libanl.so.1 (0xa6c6d000) libdl.so.2 => /lib/libdl.so.2 (0xa6c6a000) libm.so.6 => /lib/libm.so.6 (0xa6c24000) libwebsockets.so.15 => not found libc.so.6 => /lib/libc.so.6 (0xa6aa2000) /lib/ld-linux-armhf.so.3 (0xa6f0d000) libatomic.so.1 => /lib/libatomic.so.1 (0xa6a96000) ``` 裝 libwrap 有遇到 post install error,看起來是有copy \*.so,但沒有成功(mark in dpkg)。 重覆上面的過程,把所有 `.so` 安裝,最後是能夠啟動 mosquitto(沒有測全部功能)。 理論上 apt 會下載所有deps,但實際上,大部份都要手動指定pkg name並下載,只能當作 default ubuntu OS 真的有安裝這麼多東西。 一個方式是先 generate pre-installed pkgs list,重下載一次。 其它用到 gpu 的,e.g. `glxinfo` need `libGL.so.1` 還沒有試,如果 pkgs 碰到驅動可能會有問題。 有 preinstall script 的也很容易fail(裝不了)。 ```bash root@buildroot-tx7:~/armv7_out# dpkg -i --force-depends firejail_0.9.62-3ubuntu0.1_armhf.deb Unpacking firejail (from firejail_0.9.62-3ubuntu0.1_armhf.deb)... /var/lib/dpkg/info/firejail.preinst: line 4: dpkg-maintscript-helper: not found dpkg: preinst failed, exit code 32512 ``` 我們目前用的 dpkg 並非完整的版本。 總之,勉強算是能 work,但真的很難算可靠。 ### opkg opkg 是 buildroot 唯一有整合的 package manager。 就算想啟用,第一版的出廠系統就要啟動 opkg。從這個角度看,其實和需要預裝的 apt, dnf 在應用上也沒有大差別。 1. In Buildroot menuconfig: * System configuration → Package manager → select opkg * Enable “install package manager in target” and “generate packages” * Use a writable rootfs (e.g., ext4) or an overlay for /usr and /var 2. Build your image. Buildroot will produce a feed directory containing .ipk and Packages.gz. 3. Host the feed (on your dev PC): * ```bash # from the feed directory Buildroot generated (e.g., output/feeds/<arch>/) python3 -m http.server 8000 ``` 4. Point the target’s opkg to your feed: * ```bash src/gz br-feed http://<your-host-ip>:8000 dest root / lists_dir /var/opkg-lists option overlay_root / # if using overlayfs ``` 5. Use it on target: * ```bash opkg update opkg install <your-package-name> ``` Notes * > Only install packages built by the same Buildroot config/toolchain. Do not mix with OpenWrt, Debian, Alpine, etc., repos. * 實際上,opkg + buildroot **還是以 單一的 build 為核心idea**,而非嘗試將不同 build env 的 binary 共用。 * > For robust updates, consider image/OTA solutions (RAUC, SWUpdate, Mender, OSTree) instead of a package manager. ## alpine apk 如果和 預裝的 libs系統 完全分隔且平行,可能就能使用預裝系統。 alpine apk 基本是 static link 所以和一般 glibc 完全分開。 先 cross compile apk,理論上就能裝了。 alpine apk 還是會裝到 lib (libxxx.deb),有部份仍舊是 dynamic link。 像 flutter 這樣,直接在release page 要求裝[特定 lib](https://docs.flutter.dev/platform-integration/linux/building#prepare-linux-apps-for-distribution) 的也有,有安裝lib 的能力可能是可以run flutter 的。 要注意的是 用 apk 的原因始終是 standalone executable,libs 問題始終會是不得已才需要解決的部份。 [在 Linux 作業系統上使用 musl libc 來編譯出靜態可攜的程式,擺脫對於 glibc 的依賴](https://magiclen.org/musl-libc/) ### installed musl lib linked by glibc app? 應該是不會 有 link 錯誤 libs 的問題,因為 glibc 和 musl 的 linker 完全不同,`ld-linux-.so.`, `ld-musl-<arch>.so`。 但安裝時,看起來是可能會把 glibc ver libs 覆蓋掉。 如果在 glic OS 裡用 apk,一般會建議完全和 glibc 分離。 ``` sudo mkdir -p /opt/alpine sudo apk --root /opt/alpine --initdb add alpine-base # run an Alpine binary with the musl loader (examples) # x86_64 /opt/alpine/lib/ld-musl-x86_64.so.1 --library-path /opt/alpine/lib:/opt/alpine/usr/lib /opt/alpine/bin/busybox # aarch64 /opt/alpine/lib/ld-musl-aarch64.so.1 --library-path /opt/alpine/lib:/opt/alpine/usr/lib /opt/alpine/bin/busybox ``` 可是 linker 和 path 可能會找不到路徑 (可以用一堆 `ln -s` 解決),還是先試試混在一起。 ## standalone (self) build 對於自己 build by conan 的 app。 或是 dart, go 這類大部份只有 link 核心 lib,e.g. libc, libdl, libpthread ... 的語言。 這類 executable app,基本上是 deps graph 的 leaf ,app 可以完全獨立控管自己 deps。受限 lgpl 所以 沒有 static link 的 dynamic libs 也可以copy 一份。 如果不好 cross compile,docker emulated compile 一般都能 work。 我們要的是 middleware executable。從這個角度來說,如果所有 app 的編譯都是 standalone focus,則根本不需要考慮 package manager 相關的問題。 ## parallel image (impractical) 直接建立一個非常類似的 image ,每次更新就 copy all folder `/usr/*`, `/bin`, `/lib`, `/opt`, `/etc`, `/var`...。 `sys`? 一般情況是既然建立了 image,直接走正常的燒錄流程就好,change whole iamge 也是最正規的。 我們的問題是沒有燒錄的方法,或是關鍵的 firmware,所以才用這種奇怪的方案。 有燒錄了話,level 1, level 2(middleware) 都直接 壓進 image 裡。只留 user app 事後加入。 但是 只要差一點點,copy 進去就可能整個crash OS。 ## 反推最佳 OS creation 其實,就算不想要 end user 動態更新,也應該裝套件管理器。 因為完全可以用套件管理器裝完後,disable PM,再重新基於裝完的結果產生 image ,就像 docker。也沒有人說 docker 不穩。 重點是**有套件管理器才能快速做客制化的動作**。 PM 只是一個工具,不會限制最終目標。相反,只有提供的功能太少才會導致限制。buildroot 強制全新的image,就導致只有唯一的方法能解決問題。如果唯一的方法門檻又很高,本質上就是不好的設計。 另外理論上,linux 是 GPL,所以拿到開發板就能要求拿source code。但實際上,有各種推託方式。而且,採取法律行動,也需要相當的資源基礎。 與其跟廠商扯皮,不如廠商搭好一個台階。 ## solution selection 基本上可以分三種 solution 1. 要求 system 提供者加入PM e.g. apt or opkg 2. 用上述的奇怪方式,在 target system 重用 PM 下載的 pkg 3. 要求 整個 build system code e.g. buildroot 以 scope 來說 3>1>2,所以有 3 就等於可以採取任何方案。但 如果 3 是可能的選項,一般在最開始就會提供。所以更多的是有不想擺在檯面上的理由。 對雙方(提供方和用戶)最好的選擇是 1,提供方免於提供support,並維持控制。user 則可以達成可靠的部份定制。 只對提供方有利的選擇是2,因為不用出力。 ## other * [全志T507如何在Ubuntu定制文件系统](https://www.forlinx.com/article-new-c22/1044.html) ### buildroot * [ 如何定制Buildroot文件系统-基于TQT507开发板](https://www.bilibili.com/opus/798380596052099138) * 如果需要的 pkgs 有寫好的腳本 e.g. zstd,只要選選項就好 (Target packages -> Compression and decompression -> (select) zstd) * 如果沒有 e.g. FRP(Fast Reverse Proxy),就要寫一堆腳本 (custom package)。 * 下載 prebuild binary * or 編譯命令 e.g. `(cd $(@D) && $(GO_ENV) $(GOLANG) build -o $(@D)/bin/frpc ./cmd/frpc)` * 缺乏標準的 build packages 方法,很難應對很多deps 的 3rd party app。 ### yocto [Set Up Yocto for Raspberry Pi](https://medium.com/@boussettaachraf26/set-up-yocto-for-raspberry-pi-31b4a1ec4b10)