FreeBSDのバージョンを取得する

そんなものはfreebsd-versionコマンドで取れるのは分かってる。
でも、どうしてもコマンドを叩けない、叩きたくない、というときのためのメモ。
主にjailの管理用で、User Land側のバージョン取得が目的。

目的のUser Landバージョンはfreebsd-version -uで取れる。
なんと答えはこいつ自身が持っていた。シェルスクリプトにハードコード。

cat /bin/freebsd-version | grep USERLAND_VERSION=
USERLAND_VERSION="11.2-RELEASE-p2"

もちろんこれは実装依存なので、できるだけ依存すべきではない。が、こちらの要求もやや無茶なのでしょうがない。
あと、freebsd-versionは10.0で導入されたようなので、9.xにはないことにも注意。(とっくにEOLだけど)

HISTORY
     The freebsd-version command appeared in FreeBSD 10.0.

おまけ(Kernelバージョンの取得)

Kernelバージョンはきっとkernelが持っているだろうと思ってstrings /boot/kernel/kernelすれば、11.2-RELEASEとか出てくる。

以下、カーネルバージョンの取得スクリプト。無駄にがんばった。
64bit intの計算が曲者。結局10進数に変換してからbcを使った。

#!/bin/sh

data_address=0x$(objdump -h /boot/kernel/kernel | awk '{ if ($2 == ".data") { print $4 }}')
data_offset=0x$(objdump -h /boot/kernel/kernel | awk '{ if ($2 == ".data") { print $6 }}')

version_address=0x$(objdump -j .data -t /boot/kernel/kernel | awk '{ if ($6 == "version") { print $1 } }')
version_length=0x$(objdump -j .data -t /boot/kernel/kernel | awk '{ if ($6 == "version") { print $5 } }')

# convert to decimal
data_address=$(printf "%u" $data_address)
data_offset=$(printf "%u" $data_offset)
version_address=$(printf "%u" $version_address)
version_length=$(printf "%u" $version_length)

version_offset=$(bc -e "$data_offset + $version_address - $data_address" < /dev/null)
dd if=/boot/kernel/kernel iseek=$version_offset bs=1 count=$version_length 2>/dev/null | tr -d '\00'
$ sh get_kernel_version
FreeBSD 11.2-RELEASE-p2 #0: Tue Aug 14 21:45:40 UTC 2018
    root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC

おまけ2

awkで64-bit intを計算してみる。途中からおかしい。

% echo 0x81448760 0x8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'
0x13c660
% echo 0xf81448760 0xf8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                                 
0x13c660
% echo 0xff81448760 0xff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                               
0x13c660
% echo 0xfff81448760 0xfff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                             
0x13c660
% echo 0xffff81448760 0xffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                           
0x13c660
% echo 0xfffff81448760 0xfffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                         
0x13c660
% echo 0xffffff81448760 0xffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                       
0x13c660
% echo 0xfffffff81448760 0xfffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                     
0x13c680
% echo 0xffffffff81448760 0xffffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                   
0x13c800
% echo 0xfffffffff81448760 0xfffffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                 
0x138000
% echo 0xffffffffff81448760 0xffffffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                               
0x180000

floatっぽいなと思ってpythonで試す。予想通り。

Python 2.7.12 (default, Jul  7 2016, 02:34:32) 
[GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd9
Type "help", "copyright", "credits" or "license" for more information.
>> print("0x%x" % int(float(0xffffffffff81448760) - float(0xffffffffff8130c100)))                                                                                                                                                
0x180000

shは?

$ printf "0x%x\n" $(( 0xF000000000000000 - 0x1 ))
0x7ffffffffffffffe

POSIX.1な範囲で(unsigned) 64-bit intの計算ができる子はbc以外にいないものか……。