diff --git a/.pipelines/templates/PackageBuild.yml b/.pipelines/templates/PackageBuild.yml index 72dca7ea91e..bf625f0c1fd 100644 --- a/.pipelines/templates/PackageBuild.yml +++ b/.pipelines/templates/PackageBuild.yml @@ -62,6 +62,10 @@ parameters: - "false" - "true" + - name: maxCascadingRebuilds + type: string + default: "" + - name: outputArtifactsFolder type: string default: "$(Build.ArtifactStagingDirectory)" @@ -146,6 +150,7 @@ steps: echo "ERROR: toolchain archive not found!" >&2 exit 1 fi + echo "##vso[task.setvariable variable=toolchainArchive]$toolchain_archive" sudo make -C "${{ parameters.buildRepoRoot }}/toolkit" toolchain TOOLCHAIN_ARCHIVE="$toolchain_archive" displayName: "Populate toolchain" @@ -192,18 +197,24 @@ steps: use_ccache_arg="USE_CCACHE=n" fi + if [[ -n "${{ parameters.customToolchainArtifactName }}" ]]; then + toolchain_archive_arg="TOOLCHAIN_ARCHIVE=$(toolchainArchive)" + fi + sudo make -C "${{ parameters.buildRepoRoot }}/toolkit" build-packages -j$(nproc) \ CONCURRENT_PACKAGE_BUILDS=${{ parameters.concurrentPackageBuilds }} \ CONFIG_FILE="" \ + MAX_CASCADING_REBUILDS="${{ parameters.maxCascadingRebuilds }}" \ MAX_CPU="${{ parameters.maxCPU }}" \ REBUILD_TOOLS=y \ REPO_LIST="${{ parameters.extraPackageRepos }}" \ SPECS_DIR="${{ parameters.buildRepoRoot }}/${{ parameters.specsFolderPath }}" \ SRPM_PACK_LIST="${{ parameters.srpmPackList }}" \ + TEST_RERUN_LIST="${{ parameters.testRerunList }}" \ $delta_fetch_arg \ $quick_rebuild_packages_arg \ $run_check_arg \ - TEST_RERUN_LIST="${{ parameters.testRerunList }}" \ + $toolchain_archive_arg \ $use_ccache_arg displayName: "Build packages" diff --git a/SPECS-EXTENDED/buildah/buildah.spec b/SPECS-EXTENDED/buildah/buildah.spec index eea3f58b6b5..8fc32238151 100644 --- a/SPECS-EXTENDED/buildah/buildah.spec +++ b/SPECS-EXTENDED/buildah/buildah.spec @@ -127,7 +127,7 @@ cp imgtype %{buildroot}/%{_bindir}/%{name}-imgtype - Bump release to rebuild against glibc 2.35-6 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.18.0-20 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.18.0-19 - Bump release to rebuild with updated version of Go. diff --git a/SPECS-EXTENDED/containernetworking-plugins/containernetworking-plugins.spec b/SPECS-EXTENDED/containernetworking-plugins/containernetworking-plugins.spec index 78ee197a95d..22b07318739 100644 --- a/SPECS-EXTENDED/containernetworking-plugins/containernetworking-plugins.spec +++ b/SPECS-EXTENDED/containernetworking-plugins/containernetworking-plugins.spec @@ -130,7 +130,7 @@ install -p plugins/ipam/dhcp/systemd/cni-dhcp.socket %{buildroot}%{_unitdir} %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.1.1-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.1.1-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS-EXTENDED/delve/delve.spec b/SPECS-EXTENDED/delve/delve.spec index 03679912611..b237eb36bcb 100644 --- a/SPECS-EXTENDED/delve/delve.spec +++ b/SPECS-EXTENDED/delve/delve.spec @@ -73,7 +73,7 @@ done %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.5.0-16 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.5.0-15 - Bump release to rebuild with updated version of Go. diff --git a/SPECS-EXTENDED/linuxptp/linuxptp.signatures.json b/SPECS-EXTENDED/linuxptp/linuxptp.signatures.json deleted file mode 100644 index 6896ea2f024..00000000000 --- a/SPECS-EXTENDED/linuxptp/linuxptp.signatures.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Signatures": { - "clknetsim-79ffe4.tar.gz": "2d60fb5d6a12dd12fafa07b86a0ed8eba2bf552987e960c6d468c4869199dd6a", - "linuxptp-e05809.tar.gz": "296bcd84942ad0f9d0dccd703b89ba19a70a2633990598d0460dc7d017d15360", - "linuxptp-testsuite-a7f6e1.tar.gz": "ac0fc97a5e4b3ae61665bb07c922fdcf1e0d62ccc097a3de4367d72c2b0ec70a", - "phc2sys.service": "4bab3fe8ba6b801d6092d820c4fa4514973f441af5967f4c597ecd60d863c752", - "ptp4l.service": "2d85b55077bb99091ddf66917b86044f75fe31584b647a7df06994eee5ecf6bd", - "timemaster.conf": "c8bbe715944126ac4063199981c4e157228ee52ed7caa71e2a9523d5d0c57943", - "timemaster.service": "01af35467d2400f12e7c95df94e069bba89ad06c048b6f346fc26676db2e6b42" - } -} \ No newline at end of file diff --git a/SPECS-EXTENDED/podman/podman.spec b/SPECS-EXTENDED/podman/podman.spec index 8d7f427b7f7..cd274d189a8 100644 --- a/SPECS-EXTENDED/podman/podman.spec +++ b/SPECS-EXTENDED/podman/podman.spec @@ -391,7 +391,7 @@ cp -pav test/system %{buildroot}/%{_datadir}/%{name}/test/ - Bump release to rebuild against glibc 2.35-6 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 4.1.1-17 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 4.1.1-16 - Bump release to rebuild with updated version of Go. diff --git a/SPECS-EXTENDED/umoci/umoci.spec b/SPECS-EXTENDED/umoci/umoci.spec index bc9bad6e2cc..d26a76e926a 100644 --- a/SPECS-EXTENDED/umoci/umoci.spec +++ b/SPECS-EXTENDED/umoci/umoci.spec @@ -40,7 +40,7 @@ go test -mod=vendor %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.4.7-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.4.7-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS-SIGNED/kernel-azure-signed/kernel-azure-signed.spec b/SPECS-SIGNED/kernel-azure-signed/kernel-azure-signed.spec index 4787b7b70da..928e21f1c9c 100644 --- a/SPECS-SIGNED/kernel-azure-signed/kernel-azure-signed.spec +++ b/SPECS-SIGNED/kernel-azure-signed/kernel-azure-signed.spec @@ -9,7 +9,7 @@ %define uname_r %{version}-%{release} Summary: Signed Linux Kernel for Azure Name: kernel-azure-signed-%{buildarch} -Version: 5.15.137.1 +Version: 5.15.138.1 Release: 1%{?dist} License: GPLv2 Vendor: Microsoft Corporation @@ -153,6 +153,12 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %exclude /module_info.ld %changelog +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Bump release to match kernel + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS-SIGNED/kernel-hci-signed/kernel-hci-signed.spec b/SPECS-SIGNED/kernel-hci-signed/kernel-hci-signed.spec index d49870a5133..1113a401f15 100644 --- a/SPECS-SIGNED/kernel-hci-signed/kernel-hci-signed.spec +++ b/SPECS-SIGNED/kernel-hci-signed/kernel-hci-signed.spec @@ -4,7 +4,7 @@ %define uname_r %{version}-%{release} Summary: Signed Linux Kernel for HCI Name: kernel-hci-signed-%{buildarch} -Version: 5.15.137.1 +Version: 5.15.138.1 Release: 1%{?dist} License: GPLv2 Vendor: Microsoft Corporation @@ -149,6 +149,12 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %exclude /module_info.ld %changelog +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Bump release to match kernel + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS-SIGNED/kernel-signed/kernel-signed.spec b/SPECS-SIGNED/kernel-signed/kernel-signed.spec index 6ac1dda85b5..d4551bddd70 100644 --- a/SPECS-SIGNED/kernel-signed/kernel-signed.spec +++ b/SPECS-SIGNED/kernel-signed/kernel-signed.spec @@ -9,8 +9,8 @@ %define uname_r %{version}-%{release} Summary: Signed Linux Kernel for %{buildarch} systems Name: kernel-signed-%{buildarch} -Version: 5.15.137.1 -Release: 1%{?dist} +Version: 5.15.138.1 +Release: 4%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -153,6 +153,21 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %exclude /module_info.ld %changelog +* Tue Nov 28 2023 Juan Camposeco - 5.15.138.1-4 +- Bump release to match kernel + +* Tue Nov 28 2023 Thien Trung Vuong - 5.15.138.1-3 +- Bump release to match kernel + +* Wed Nov 22 2023 David Daney - 5.15.138.1-2 +- Bump release to match kernel + +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Bump release to match kernel + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS/KeysInUse-OpenSSL/KeysInUse-OpenSSL.spec b/SPECS/KeysInUse-OpenSSL/KeysInUse-OpenSSL.spec index a0ec74a4271..a9412893207 100644 --- a/SPECS/KeysInUse-OpenSSL/KeysInUse-OpenSSL.spec +++ b/SPECS/KeysInUse-OpenSSL/KeysInUse-OpenSSL.spec @@ -75,7 +75,7 @@ fi %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.3.4-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.3.4-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md b/SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md index d86d4d82798..a2dcd4a1994 100644 --- a/SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md +++ b/SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md @@ -5,7 +5,7 @@ The CBL-Mariner SPEC files originated from a variety of sources with varying lic | CentOS | [MIT](https://www.centos.org/legal/#licensing-policy) | crash-ptdump-command
delve
fstrm
nodejs-nodemon
rhnlib
rt-setup
rt-tests
rtctl
tuned | | Ceph source | [LGPL2.1](https://github.com/ceph/ceph/blob/master/COPYING-LGPL2.1) | ceph | | Debian | [MIT](https://opensource.org/licenses/MIT) | prometheus-process-exporter | -| Fedora | [Fedora MIT License Declaration](https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#License_of_Fedora_SPEC_Files) | a52dec
abseil-cpp
accountsservice
acpica-tools
acpid
adcli
adobe-mappings-cmap
adobe-mappings-pdf
advancecomp
adwaita-icon-theme
afflib
aide
alsa-firmware
alsa-plugins
amtk
amtterm
annobin
ansible-freeipa
archivemount
argparse-manpage
arptables
arpwatch
asio
aspell
aspell-en
at
at-spi2-atk
at-spi2-core
atf
atk
atop
attr
audiofile
augeas
authbind
authd
authselect
autoconf213
avahi
babeltrace
babeltrace2
babl
baekmuk-ttf-fonts
bats
bcache-tools
biosdevname
blosc
bluez
bmake
bogofilter
bolt
boom-boot
booth
botan2
breezy
brotli
buildah
busybox
bwidget
byacc
ca-certificates
cachefilesd
cairomm
calamares
capstone
catatonit
catch
catch1
cdrdao
celt051
cereal
certmonger
cfitsio
cgdcbxd
chan
CharLS
checkpolicy
checksec
chrony
cim-schema
cjkuni-uming-fonts
cjose
cldr-emoji-annotation
clucene
clutter
clutter-gst3
clutter-gtk
cmocka
cogl
collectd
colm
color-filesystem
colord
colorize
compat-lua
compiler-rt
conda
conmon
conntrack-tools
console-setup
container-exception-logger
containernetworking-plugins
convmv
corosync
corosync-qdevice
cpp-hocon
cppcheck
cpprest
cpptest
cpuid
criu
crypto-policies
cryptsetup
cscope
ctags
CUnit
cups
custodia
Cython
dbus-c++
dbus-python
dbxtool
dconf
dcraw
debootstrap
deltarpm
desktop-file-utils
device-mapper-persistent-data
dietlibc
diffstat
ding-libs
discount
distribution-gpg-keys
dleyna-connector-dbus
dleyna-core
dmraid
dnf
dnf-plugins-core
docbook-dtds
docbook-simple
docbook-slides
docbook-style-dsssl
docbook-utils
docbook2X
docbook5-schemas
docbook5-style-xsl
dogtail
dos2unix
dotconf
dovecot
dpdk
dpkg
driverctl
dropwatch
drpm
dumpet
dvd+rw-tools
dwarves
dwz
dyninst
ebtables
edac-utils
edk2
efax
efi-rpm-macros
egl-wayland
eglexternalplatform
elinks
enca
enchant
enchant2
enscript
environment-modules
evemu
execstack
exempi
exiv2
extra-cmake-modules
fabtests
facter
fakechroot
fakeroot
fapolicyd
fdk-aac-free
fdupes
fence-virt
fetchmail
fftw
filebench
fio
fipscheck
firewalld
fish
flac
flatbuffers
flite
fltk
fmt
fontawesome-fonts
fontpackages
fonts-rpm-macros
foomatic-db
freeglut
freeipmi
freeradius
freetds
freexl
fribidi
fros
frr
fsverity-utils
fuse-overlayfs
fuse-sshfs
fuse-zip
fuse3
future
fxload
gavl
gconf-editor
GConf2
gcovr
gcr
gdal
gdisk
gdk-pixbuf2
generic-logos
genwqe-tools
geoclue2
GeoIP
GeoIP-GeoLite-data
geolite2
geos
gfs2-utils
ghc-srpm-macros
giflib
gl-manpages
glew
glm
glog
glusterfs
gnome-desktop-testing
gnome-doc-utils
gnome-icon-theme
gnome-keyring
gnu-efi
go-rpm-macros
gom
google-api-python-client
google-crosextra-caladea-fonts
google-crosextra-carlito-fonts
google-guice
google-noto-cjk-fonts
google-noto-emoji-fonts
google-roboto-slab-fonts
gphoto2
gpm
gpsbabel
graphene
graphite2
graphviz
grubby
gsettings-desktop-schemas
gsl
gsm
gspell
gssdp
gssntlmssp
gstreamer1
gstreamer1-plugins-base
gtk-vnc
gtk2
gtk3
gtkspell
gupnp
gupnp-av
gupnp-dlna
gupnp-igd
hardening-check
hdf
hdf5
heimdal
help2man
hexedit
hicolor-icon-theme
hiera
highlight
hivex
hostname
hping3
hsakmt
htop
hunspell
hunspell-af
hunspell-ar
hunspell-as
hunspell-ast
hunspell-az
hunspell-be
hunspell-bg
hunspell-bn
hunspell-br
hunspell-ca
hunspell-cop
hunspell-csb
hunspell-cv
hunspell-cy
hunspell-da
hunspell-de
hunspell-dsb
hunspell-el
hunspell-en
hunspell-eo
hunspell-es
hunspell-et
hunspell-eu
hunspell-fa
hunspell-fj
hunspell-fo
hunspell-fr
hunspell-fur
hunspell-fy
hunspell-ga
hunspell-gd
hunspell-gl
hunspell-grc
hunspell-gu
hunspell-gv
hunspell-haw
hunspell-hi
hunspell-hil
hunspell-hr
hunspell-hsb
hunspell-ht
hunspell-hu
hunspell-hy
hunspell-ia
hunspell-id
hunspell-is
hunspell-it
hunspell-kk
hunspell-km
hunspell-kn
hunspell-ko
hunspell-ku
hunspell-ky
hunspell-la
hunspell-lb
hunspell-ln
hunspell-mai
hunspell-mg
hunspell-mi
hunspell-mk
hunspell-ml
hunspell-mn
hunspell-mos
hunspell-mr
hunspell-ms
hunspell-mt
hunspell-nds
hunspell-ne
hunspell-nl
hunspell-no
hunspell-nr
hunspell-nso
hunspell-ny
hunspell-om
hunspell-or
hunspell-pa
hunspell-pl
hunspell-pt
hunspell-quh
hunspell-ro
hunspell-ru
hunspell-rw
hunspell-se
hunspell-shs
hunspell-si
hunspell-sk
hunspell-sl
hunspell-smj
hunspell-so
hunspell-sq
hunspell-sr
hunspell-sv
hunspell-sw
hunspell-ta
hunspell-te
hunspell-tet
hunspell-th
hunspell-tk
hunspell-tl
hunspell-tn
hunspell-tpi
hunspell-ts
hunspell-uk
hunspell-uz
hunspell-ve
hunspell-vi
hunspell-wa
hunspell-xh
hunspell-yi
hwdata
hwloc
hyperscan
hyperv-daemons
hyphen
hyphen-as
hyphen-bg
hyphen-bn
hyphen-ca
hyphen-da
hyphen-de
hyphen-el
hyphen-es
hyphen-fa
hyphen-fo
hyphen-fr
hyphen-ga
hyphen-gl
hyphen-grc
hyphen-gu
hyphen-hi
hyphen-hsb
hyphen-hu
hyphen-ia
hyphen-id
hyphen-is
hyphen-it
hyphen-kn
hyphen-ku
hyphen-lt
hyphen-mi
hyphen-ml
hyphen-mn
hyphen-mr
hyphen-nl
hyphen-or
hyphen-pa
hyphen-pl
hyphen-pt
hyphen-ro
hyphen-ru
hyphen-sa
hyphen-sk
hyphen-sl
hyphen-sv
hyphen-ta
hyphen-te
hyphen-tk
hyphen-uk
ibus
ibus-chewing
ibus-hangul
ibus-kkc
ibus-libzhuyin
ibus-m17n
ibus-rawcode
ibus-sayura
ibus-table
ibus-table-chinese
icc-profiles-openicc
icon-naming-utils
icoutils
iftop
iio-sensor-proxy
ilmbase
im-chooser
imaptest
imsettings
indent
infinipath-psm
inih
iniparser
intel-cmt-cat
intel-ipsec-mb
ioping
IP2Location
ipa-pgothic-fonts
ipcalc
ipmitool
iprutils
iptraf-ng
iptstate
irssi
iscsi-initiator-utils
isns-utils
iso-codes
isomd5sum
iw
iwd
jabberpy
jasper
javapackages-bootstrap
javapackages-tools
jbigkit
jdom2
jemalloc
jfsutils
jimtcl
jose
js-jquery
jsoncpp
Judy
kata-containers
kde-filesystem
kde-settings
kexec-tools
keybinder3
keycloak-httpd-client-install
kf5
kf5-kconfig
kf5-kcoreaddons
kf5-ki18n
kf5-kwidgetsaddons
kpmcore
kronosnet
ksh
kyotocabinet
kyua
ladspa
lame
langtable
lapack
lasso
latencytop
lato-fonts
lcms2
lcov
ldns
leatherman
ledmon
lensfun
leveldb
lftp
libabw
libaec
libao
libappstream-glib
libart_lgpl
libasyncns
libatasmart
libavc1394
libblockdev
libbpf
libbsd
libburn
libbytesize
libcacard
libcanberra
libcdio
libcdio-paranoia
libcdr
libcgroup
libchewing
libcli
libcmis
libcmpiutil
libcomps
libcroco
libdaemon
libdap
libdatrie
libdazzle
libdbi
libdbi-drivers
libdbusmenu
libdc1394
libdeflate
libdmx
libdnf
libdrm
libdvdnav
libdvdread
libdwarf
libeasyfc
libecap
libecb
libell
libEMF
libeot
libepoxy
libepubgen
libesmtp
libetonyek
libev
libevdev
libewf
libexif
libexttextcat
libfabric
libfontenc
libfreehand
libftdi
libgadu
libgdither
libgee
libgee06
libgeotiff
libgexiv2
libgit2
libgit2-glib
libglade2
libglvnd
libgovirt
libgphoto2
libgsf
libgta
libguestfs
libgusb
libgxim
libgxps
libhangul
libhugetlbfs
libibcommon
libical
libICE
libicns
libid3tag
libIDL
libidn2
libiec61883
libieee1284
libimobiledevice
libindicator
libinput
libiodbc
libipt
libiptcdata
libiscsi
libisoburn
libisofs
libjcat
libkcapi
libkeepalive
libkkc
libkkc-data
libkml
liblangtag
libldb
libldm
liblerc
liblockfile
liblognorm
liblouis
liblqr-1
liblzf
libmad
libmediaart
libmicrohttpd
libmikmod
libmodman
libmodplug
libmodulemd1
libmpcdec
libmspub
libmtp
libmusicbrainz5
libmwaw
libnbd
libnet
libnetfilter_log
libnfs
libnotify
libntlm
libnumbertext
liboauth
libodfgen
libofa
libogg
liboggz
liboil
libomxil-bellagio
libopenraw
liboping
libosinfo
libotf
libotr
libpagemaker
libpaper
libpciaccess
libpeas
libpfm
libpinyin
libplist
libpmemobj-cpp
libpng12
libpng15
libproxy
libpsm2
libpwquality
libqb
libqxp
libraqm
LibRaw
libraw1394
libreport
libreswan
librevenge
librsvg2
librx
libsamplerate
libsass
libsecret
libsemanage
libsigc++20
libsigsegv
libslirp
libSM
libsmbios
libsmi
libsndfile
libsodium
libspiro
libsrtp
libssh
libstaroffice
libstemmer
libstoragemgmt
libtdb
libteam
libtevent
libthai
libtnc
libtomcrypt
libtommath
libtraceevent
libtranslit
libucil
libunicap
libuninameslist
liburing
libusbmuxd
libuser
libutempter
libvarlink
libverto
libvirt-dbus
libvirt-glib
libvirt-java
libvirt-python
libvisio
libvisual
libvoikko
libvorbis
libvpx
libwacom
libwnck3
libwpd
libwpe
libwpg
libwps
libwvstreams
libX11
libXau
libXaw
libxcb
libXcomposite
libxcrypt
libXcursor
libXdamage
libXdmcp
libXext
libxfce4util
libXfixes
libXfont2
libXft
libXi
libXinerama
libxkbcommon
libxkbfile
libxklavier
libxmlb
libXmu
libXpm
libXrandr
libXrender
libXres
libXScrnSaver
libxshmfence
libXt
libXtst
libXv
libXxf86vm
libyami
libyang
libyubikey
libzip
libzmf
lilv
linuxconsoletools
linuxptp
lksctp-tools
lldpd
lockdev
logwatch
lpsolve
lrzsz
lua
lua-expat
lua-filesystem
lua-json
lua-lpeg
lua-lunit
lua-rpm-macros
lua-term
luajit
luksmeta
lutok
lv2
lzip
lzop
m17n-db
m17n-lib
mac-robber
mailcap
mailx
malaga
malaga-suomi-voikko
mallard-rng
man-pages-cs
man-pages-es
man-pages-it
man-pages-ja
man-pages-ko
man-pages-pl
man-pages-ru
man-pages-zh-CN
mariadb-connector-c
mariadb-connector-odbc
marisa
maven-compiler-plugin
maven-jar-plugin
maven-resolver
maven-resources-plugin
maven-surefire
maven-wagon
mcelog
mcpp
mcstrans
mdadm
mdds
meanwhile
mecab
mecab-ipadic
media-player-info
memcached
memkind
mesa
mesa-libGLU
metis
microcode_ctl
microdnf
minicom
minizip
mksh
mobile-broadband-provider-info
mock
mock-core-configs
mod_auth_gssapi
mod_auth_mellon
mod_auth_openidc
mod_authnz_pam
mod_fcgid
mod_http2
mod_intercept_form_submit
mod_lookup_identity
mod_md
mod_security
mod_security_crs
mod_wsgi
mokutil
mpage
mrtg
mstflint
mt-st
mtdev
mtools
mtr
mtx
multilib-rpm-config
munge
mutt
mythes
mythes-bg
mythes-ca
mythes-cs
mythes-da
mythes-de
mythes-el
mythes-en
mythes-eo
mythes-es
mythes-fr
mythes-ga
mythes-hu
mythes-mi
mythes-ne
mythes-nl
mythes-pl
mythes-pt
mythes-ro
mythes-ru
mythes-sk
mythes-sl
mythes-sv
mythes-uk
nbd
nbdkit
neon
netavark
netcdf
netcf
netlabel_tools
netpbm
netsniff-ng
nfs4-acl-tools
nftables
nilfs-utils
nkf
nload
nlopt
nodejs-packaging
nss-pam-ldapd
nss_nis
nss_wrapper
ntfs-3g
ntfs-3g-system-compression
numad
numatop
numpy
nvmetcli
nvml
oath-toolkit
ocaml
ocaml-alcotest
ocaml-astring
ocaml-base
ocaml-bigarray-compat
ocaml-bisect-ppx
ocaml-calendar
ocaml-camlp5
ocaml-camomile
ocaml-cinaps
ocaml-cmdliner
ocaml-compiler-libs-janestreet
ocaml-cppo
ocaml-csexp
ocaml-csv
ocaml-ctypes
ocaml-curses
ocaml-dune
ocaml-extlib
ocaml-fileutils
ocaml-findlib
ocaml-fmt
ocaml-fpath
ocaml-gettext
ocaml-integers
ocaml-libvirt
ocaml-luv
ocaml-lwt
ocaml-markup
ocaml-migrate-parsetree
ocaml-mmap
ocaml-num
ocaml-ocamlbuild
ocaml-ocplib-endian
ocaml-ounit
ocaml-parsexp
ocaml-ppx-derivers
ocaml-ppxlib
ocaml-re
ocaml-react
ocaml-result
ocaml-seq
ocaml-sexplib
ocaml-sexplib0
ocaml-stdio
ocaml-topkg
ocaml-tyxml
ocaml-uuidm
ocaml-uutf
ocaml-xml-light
ocaml-zarith
ocl-icd
oddjob
ogdi
omping
opa
opal
open-vm-tools
openblas
opencc
opencl-filesystem
opencl-headers
opencryptoki
opencsd
opendnssec
OpenEXR
openjade
openjpeg2
openmpi
openobex
openoffice-lv
openrdate
opensc
openslp
opensm
opensp
openssl
openssl-ibmpkcs11
openssl-pkcs11
openwsman
optipng
opus
opusfile
orangefs
ORBit2
orc
os-prober
osinfo-db
osinfo-db-tools
overpass-fonts
p11-kit
p7zip
pacemaker
pacrunner
pakchois
pam_krb5
pam_wrapper
papi
paps
parallel
patchelf
patchutils
pbzip2
pcp
pcsc-lite
pcsc-lite-ccid
PEGTL
perl
perl-Algorithm-C3
perl-Algorithm-Diff
perl-Alien-Build
perl-Alien-pkgconf
perl-AnyEvent
perl-AnyEvent-AIO
perl-AnyEvent-BDB
perl-App-cpanminus
perl-App-FatPacker
perl-AppConfig
perl-Archive-Extract
perl-Archive-Zip
perl-Authen-SASL
perl-B-Debug
perl-B-Hooks-EndOfScope
perl-B-Hooks-OP-Check
perl-B-Keywords
perl-B-Lint
perl-bareword-filehandles
perl-BDB
perl-Bit-Vector
perl-boolean
perl-Browser-Open
perl-BSD-Resource
perl-Business-ISBN
perl-Business-ISBN-Data
perl-Bytes-Random-Secure
perl-Capture-Tiny
perl-Carp-Clan
perl-CBOR-XS
perl-Class-Accessor
perl-Class-C3
perl-Class-C3-XS
perl-Class-Data-Inheritable
perl-Class-Factory-Util
perl-Class-Inspector
perl-Class-ISA
perl-Class-Load
perl-Class-Load-XS
perl-Class-Method-Modifiers
perl-Class-Singleton
perl-Class-Tiny
perl-Class-XSAccessor
perl-Clone
perl-Color-ANSI-Util
perl-Color-RGB-Util
perl-ColorThemeBase-Static
perl-ColorThemeRole-ANSI
perl-ColorThemes-Standard
perl-ColorThemeUtil-ANSI
perl-Compress-Bzip2
perl-Compress-LZF
perl-Compress-Raw-Lzma
perl-Config-AutoConf
perl-Config-INI
perl-Config-INI-Reader-Multiline
perl-Config-IniFiles
perl-Config-Simple
perl-Config-Tiny
perl-Const-Fast
perl-Convert-ASN1
perl-Convert-Bencode
perl-Coro
perl-Coro-Multicore
perl-CPAN-Changes
perl-CPAN-DistnameInfo
perl-CPAN-Meta-Check
perl-Cpanel-JSON-XS
perl-Crypt-CBC
perl-Crypt-DES
perl-Crypt-IDEA
perl-Crypt-OpenSSL-Bignum
perl-Crypt-OpenSSL-Guess
perl-Crypt-OpenSSL-Random
perl-Crypt-OpenSSL-RSA
perl-Crypt-PasswdMD5
perl-Crypt-Random-Seed
perl-CSS-Tiny
perl-Data-Dump
perl-Data-Munge
perl-Data-OptList
perl-Data-Peek
perl-Data-Section
perl-Data-UUID
perl-Date-Calc
perl-Date-ISO8601
perl-Date-Manip
perl-DateTime
perl-DateTime-Format-Builder
perl-DateTime-Format-DateParse
perl-DateTime-Format-HTTP
perl-DateTime-Format-IBeat
perl-DateTime-Format-ISO8601
perl-DateTime-Format-Mail
perl-DateTime-Format-Strptime
perl-DateTime-Locale
perl-DateTime-TimeZone
perl-DateTime-TimeZone-SystemV
perl-DateTime-TimeZone-Tzfile
perl-DBD-MySQL
perl-Devel-CallChecker
perl-Devel-Caller
perl-Devel-CheckBin
perl-Devel-CheckLib
perl-Devel-Cycle
perl-Devel-EnforceEncapsulation
perl-Devel-GlobalDestruction
perl-Devel-GlobalDestruction-XS
perl-Devel-Hide
perl-Devel-Leak
perl-Devel-LexAlias
perl-Devel-Size
perl-Devel-StackTrace
perl-Devel-Symdump
perl-Digest-BubbleBabble
perl-Digest-CRC
perl-Digest-HMAC
perl-Digest-SHA1
perl-Dist-CheckConflicts
perl-DynaLoader-Functions
perl-Email-Address
perl-Email-Date-Format
perl-Encode-Detect
perl-Encode-EUCJPASCII
perl-Encode-IMAPUTF7
perl-Encode-Locale
perl-Env-ShellWords
perl-Error
perl-EV
perl-Eval-Closure
perl-Event
perl-Exception-Class
perl-Expect
perl-ExtUtils-Config
perl-ExtUtils-Depends
perl-ExtUtils-Helpers
perl-ExtUtils-InstallPaths
perl-ExtUtils-PkgConfig
perl-FCGI
perl-Fedora-VSP
perl-FFI-CheckLib
perl-File-BaseDir
perl-File-BOM
perl-File-chdir
perl-File-CheckTree
perl-File-Copy-Recursive
perl-File-DesktopEntry
perl-File-Find-Object
perl-File-Find-Object-Rule
perl-File-Find-Rule
perl-File-Find-Rule-Perl
perl-File-Inplace
perl-File-Listing
perl-File-MimeInfo
perl-File-pushd
perl-File-ReadBackwards
perl-File-Remove
perl-File-ShareDir
perl-File-ShareDir-Install
perl-File-Slurp
perl-File-Slurp-Tiny
perl-File-Slurper
perl-File-Type
perl-Font-TTF
perl-FreezeThaw
perl-GD
perl-GD-Barcode
perl-generators
perl-Getopt-ArgvFile
perl-gettext
perl-Graphics-ColorNamesLite-WWW
perl-GSSAPI
perl-Guard
perl-Hook-LexWrap
perl-HTML-Parser
perl-HTML-Tagset
perl-HTML-Tree
perl-HTTP-Cookies
perl-HTTP-Daemon
perl-HTTP-Date
perl-HTTP-Message
perl-HTTP-Negotiate
perl-Image-Base
perl-Image-Info
perl-Image-Xbm
perl-Image-Xpm
perl-Import-Into
perl-Importer
perl-inc-latest
perl-indirect
perl-Inline-Files
perl-IO-AIO
perl-IO-All
perl-IO-CaptureOutput
perl-IO-Compress-Lzma
perl-IO-HTML
perl-IO-Multiplex
perl-IO-SessionData
perl-IO-Socket-INET6
perl-IO-String
perl-IO-stringy
perl-IO-Tty
perl-IPC-Run
perl-IPC-Run3
perl-IPC-System-Simple
perl-JSON
perl-JSON-Color
perl-JSON-MaybeXS
perl-LDAP
perl-libnet
perl-libwww-perl
perl-libxml-perl
perl-Lingua-EN-Inflect
perl-List-MoreUtils-XS
perl-local-lib
perl-Locale-Codes
perl-Locale-Maketext-Gettext
perl-Locale-Msgfmt
perl-Locale-PO
perl-Log-Message
perl-Log-Message-Simple
perl-LWP-MediaTypes
perl-LWP-Protocol-https
perl-Mail-AuthenticationResults
perl-Mail-DKIM
perl-Mail-IMAPTalk
perl-Mail-SPF
perl-MailTools
perl-Math-Int64
perl-Math-Random-ISAAC
perl-MIME-Charset
perl-MIME-Lite
perl-MIME-Types
perl-Mixin-Linewise
perl-MLDBM
perl-Mock-Config
perl-Module-Build-Tiny
perl-Module-CPANfile
perl-Module-Implementation
perl-Module-Install-AuthorRequires
perl-Module-Install-AuthorTests
perl-Module-Install-AutoLicense
perl-Module-Install-GithubMeta
perl-Module-Install-ManifestSkip
perl-Module-Install-ReadmeFromPod
perl-Module-Install-ReadmeMarkdownFromPod
perl-Module-Install-Repository
perl-Module-Install-TestBase
perl-Module-Load-Util
perl-Module-Manifest
perl-Module-Manifest-Skip
perl-Module-Package
perl-Module-Package-Au
perl-Module-Pluggable
perl-Module-Runtime
perl-Module-Signature
perl-Mojolicious
perl-Moo
perl-Mozilla-CA
perl-Mozilla-LDAP
perl-MRO-Compat
perl-multidimensional
perl-namespace-autoclean
perl-namespace-clean
perl-Net-CIDR-Lite
perl-Net-Daemon
perl-Net-DNS
perl-Net-DNS-Resolver-Mock
perl-Net-DNS-Resolver-Programmable
perl-Net-HTTP
perl-Net-IMAP-Simple
perl-Net-IMAP-Simple-SSL
perl-Net-IP
perl-Net-LibIDN2
perl-Net-Patricia
perl-Net-SMTP-SSL
perl-Net-SNMP
perl-Net-Telnet
perl-Newt
perl-NNTPClient
perl-NTLM
perl-Number-Compare
perl-Object-Deadly
perl-Object-HashBase
perl-Package-Anon
perl-Package-Constants
perl-Package-DeprecationManager
perl-Package-Generator
perl-Package-Stash
perl-Package-Stash-XS
perl-PadWalker
perl-Paper-Specs
perl-PAR-Dist
perl-Parallel-Iterator
perl-Params-Classify
perl-Params-Util
perl-Params-Validate
perl-Params-ValidationCompiler
perl-Parse-PMFile
perl-Parse-RecDescent
perl-Parse-Yapp
perl-Path-Tiny
perl-Perl-Critic
perl-Perl-Critic-More
perl-Perl-Destruct-Level
perl-Perl-MinimumVersion
perl-Perl4-CoreLibs
perl-PerlIO-gzip
perl-PerlIO-utf8_strict
perl-PkgConfig-LibPkgConf
perl-Pod-Coverage
perl-Pod-Coverage-TrustPod
perl-Pod-Escapes
perl-Pod-Eventual
perl-Pod-LaTeX
perl-Pod-Markdown
perl-Pod-Parser
perl-Pod-Plainer
perl-Pod-POM
perl-Pod-Spell
perl-PPI
perl-PPI-HTML
perl-PPIx-QuoteLike
perl-PPIx-Regexp
perl-PPIx-Utilities
perl-prefork
perl-Probe-Perl
perl-Razor-Agent
perl-Readonly
perl-Readonly-XS
perl-Ref-Util
perl-Ref-Util-XS
perl-Regexp-Pattern-Perl
perl-Return-MultiLevel
perl-Role-Tiny
perl-Scope-Guard
perl-Scope-Upper
perl-SGMLSpm
perl-SNMP_Session
perl-Socket6
perl-Software-License
perl-Sort-Versions
perl-Specio
perl-Spiffy
perl-strictures
perl-String-CRC32
perl-String-Format
perl-String-ShellQuote
perl-String-Similarity
perl-Sub-Exporter
perl-Sub-Exporter-Progressive
perl-Sub-Identify
perl-Sub-Info
perl-Sub-Install
perl-Sub-Name
perl-Sub-Quote
perl-Sub-Uplevel
perl-SUPER
perl-Switch
perl-Syntax-Highlight-Engine-Kate
perl-Sys-CPU
perl-Sys-MemInfo
perl-Sys-Virt
perl-Taint-Runtime
perl-Task-Weaken
perl-Term-Size-Any
perl-Term-Size-Perl
perl-Term-Table
perl-Term-UI
perl-TermReadKey
perl-Test-Base
perl-Test-ClassAPI
perl-Test-CPAN-Meta
perl-Test-CPAN-Meta-JSON
perl-Test-Deep
perl-Test-Differences
perl-Test-DistManifest
perl-Test-Distribution
perl-Test-EOL
perl-Test-Exception
perl-Test-Exit
perl-Test-FailWarnings
perl-Test-Fatal
perl-Test-File
perl-Test-File-ShareDir
perl-Test-Harness
perl-Test-HasVersion
perl-Test-InDistDir
perl-Test-Inter
perl-Test-LeakTrace
perl-Test-LongString
perl-Test-Manifest
perl-Test-Memory-Cycle
perl-Test-MinimumVersion
perl-Test-MockObject
perl-Test-MockRandom
perl-Test-Needs
perl-Test-NoTabs
perl-Test-NoWarnings
perl-Test-Object
perl-Test-Output
perl-Test-Pod
perl-Test-Pod-Coverage
perl-Test-Portability-Files
perl-Test-Requires
perl-Test-RequiresInternet
perl-Test-Script
perl-Test-Simple
perl-Test-SubCalls
perl-Test-Synopsis
perl-Test-Taint
perl-Test-TrailingSpace
perl-Test-utf8
perl-Test-Vars
perl-Test-Warn
perl-Test-Without-Module
perl-Test2-Plugin-NoWarnings
perl-Test2-Suite
perl-Test2-Tools-Explain
perl-Text-CharWidth
perl-Text-CSV_XS
perl-Text-Diff
perl-Text-Glob
perl-Text-Iconv
perl-Text-Soundex
perl-Text-Unidecode
perl-Text-WrapI18N
perl-Tie-IxHash
perl-TimeDate
perl-Tree-DAG_Node
perl-Unicode-EastAsianWidth
perl-Unicode-LineBreak
perl-Unicode-Map8
perl-Unicode-String
perl-Unicode-UTF8
perl-UNIVERSAL-can
perl-UNIVERSAL-isa
perl-Unix-Syslog
perl-URI
perl-Variable-Magic
perl-Version-Requirements
perl-WWW-RobotRules
perl-XML-Catalog
perl-XML-DOM
perl-XML-Dumper
perl-XML-Filter-BufferText
perl-XML-Generator
perl-XML-Grove
perl-XML-Handler-YAWriter
perl-XML-LibXML
perl-XML-LibXSLT
perl-XML-NamespaceSupport
perl-XML-Parser-Lite
perl-XML-RegExp
perl-XML-SAX
perl-XML-SAX-Base
perl-XML-SAX-Writer
perl-XML-Simple
perl-XML-TokeParser
perl-XML-TreeBuilder
perl-XML-Twig
perl-XML-Writer
perl-XML-XPath
perl-XML-XPathEngine
perl-XString
perl-YAML-LibYAML
perl-YAML-PP
perl-YAML-Syck
perltidy
pesign
phodav
php
php-pear
php-pecl-zip
physfs
picosat
pinfo
pipewire
pixman
pkcs11-helper
pkgconf
plexus-cipher
plexus-containers
plexus-sec-dispatcher
plotutils
pmdk-convert
pmix
pngcrush
pngnq
po4a
podman
poetry
policycoreutils
polkit-pkla-compat
portreserve
postfix
potrace
powertop
ppp
pps-tools
pptp
priv_wrapper
procmail
prometheus
prometheus-node-exporter
ps_mem
psacct
psutils
ptlib
publicsuffix-list
pugixml
pulseaudio
puppet
pwgen
pyatspi
pybind11
pycairo
pyelftools
pyflakes
pygobject3
PyGreSQL
pykickstart
pylint
pyparted
pyproject-rpm-macros
pyserial
python-absl-py
python-aiodns
python-aiohttp
python-alsa
python-argcomplete
python-astroid
python-astunparse
python-async-generator
python-augeas
python-azure-sdk
python-beautifulsoup4
python-betamax
python-blinker
python-blivet
python-cached_property
python-charset-normalizer
python-cheetah
python-click
python-cmd2
python-colorama
python-CommonMark
python-conda-package-handling
python-configshell
python-cpuinfo
python-cups
python-curio
python-cytoolz
python-d2to1
python-dbus-client-gen
python-dbus-python-client-gen
python-dbus-signature-pyparsing
python-dbusmock
python-ddt
python-debtcollector
python-decorator
python-distlib
python-dmidecode
python-dns
python-dtopt
python-dulwich
python-enchant
python-entrypoints
python-ethtool
python-evdev
python-extras
python-faker
python-fasteners
python-fields
python-filelock
python-fixtures
python-flake8
python-flask
python-flit
python-flit-core
python-fluidity-sm
python-frozendict
python-funcsigs
python-gast
python-genshi
python-google-auth
python-google-auth-oauthlib
python-greenlet
python-gssapi
python-h5py
python-hs-dbus-signature
python-html5lib
python-httplib2
python-humanize
python-hwdata
python-importlib-metadata
python-inotify
python-into-dbus-python
python-IPy
python-iso8601
python-isodate
python-isort
python-itsdangerous
python-justbases
python-justbytes
python-jwcrypto
python-jwt
python-kdcproxy
python-kerberos
python-kmod
python-kubernetes
python-lazy-object-proxy
python-ldap
python-linux-procfs
python-lit
python-markdown
python-mccabe
python-memcached
python-mimeparse
python-mock
python-monotonic
python-more-itertools
python-mpmath
python-msal
python-msrestazure
python-mutagen
python-networkx
python-nose2
python-ntlm-auth
python-oauth2client
python-openpyxl
python-openstackdocstheme
python-oslo-i18n
python-oslo-sphinx
python-paramiko
python-pefile
python-pexpect
python-pkgconfig
python-platformdirs
python-pluggy
python-podman-api
python-process-tests
python-productmd
python-ptyprocess
python-pycares
python-pycosat
python-pydbus
python-pymongo
python-PyMySQL
python-pyperclip
python-pyroute2
python-pyrsistent
python-pysocks
python-pytest-benchmark
python-pytest-cov
python-pytest-expect
python-pytest-flake8
python-pytest-forked
python-pytest-mock
python-pytest-relaxed
python-pytest-runner
python-pytest-subtests
python-pytest-timeout
python-pytest-xdist
python-pytoml
python-pyudev
python-pywbem
python-qrcode
python-rdflib
python-recommonmark
python-redis
python-requests-file
python-requests-ftp
python-requests-kerberos
python-requests-mock
python-requests-oauthlib
python-requests-toolbelt
python-requests_ntlm
python-responses
python-retrying
python-rfc3986
python-rpm-generators
python-rpmfluff
python-rtslib
python-ruamel-yaml
python-ruamel-yaml-clib
python-s3transfer
python-schedutils
python-semantic_version
python-should_dsl
python-simpleline
python-slip
python-sniffio
python-soupsieve
python-sphinx
python-sphinx-epytext
python-sphinx-theme-py3doc-enhanced
python-sphinx_rtd_theme
python-sphinxcontrib-apidoc
python-sphinxcontrib-applehelp
python-sphinxcontrib-devhelp
python-sphinxcontrib-htmlhelp
python-sphinxcontrib-httpdomain
python-sphinxcontrib-jsmath
python-sphinxcontrib-qthelp
python-sphinxcontrib-serializinghtml
python-sqlalchemy
python-suds
python-systemd
python-tempita
python-templated-dictionary
python-termcolor
python-testpath
python-testresources
python-testscenarios
python-testtools
python-tidy
python-toml
python-tomli
python-toolz
python-tornado
python-tox
python-tox-current-env
python-tqdm
python-trio
python-typing-extensions
python-uamqp
python-unittest2
python-uritemplate
python-urwid
python-varlink
python-virt-firmware
python-voluptuous
python-waitress
python-webencodings
python-webtest
python-wheel
python-whoosh
python-winrm
python-wrapt
python-xmltodict
python-yubico
python-zipp
python-zmq
python3-mallard-ducktype
python3-pytest-asyncio
python3-typed_ast
pyusb
pywbem
pyxattr
qemu
qhull
qpdf
qperf
qr-code-generator
qt5-qtbase
qt5-qtconnectivity
qt5-qtdeclarative
qt5-qtsensors
qt5-qtserialport
qt5-qtsvg
qt5-qttools
qt5-rpm-macros
quagga
quota
radvd
ragel
raptor2
rarian
rasdaemon
rasqal
rcs
rdist
rdma-core
re2
re2c
realmd
rear
recode
redland
resource-agents
rest
rhash
rlwrap
rp-pppoe
rpm-mpi-hooks
rpmdevtools
rpmlint
rtkit
rtl-sdr
ruby-augeas
rubygem-bson
rubygem-coderay
rubygem-diff-lcs
rubygem-flexmock
rubygem-hpricot
rubygem-introspection
rubygem-liquid
rubygem-maruku
rubygem-metaclass
rubygem-mongo
rubygem-mustache
rubygem-mysql2
rubygem-pkg-config
rubygem-rake
rubygem-rake-compiler
rubygem-ronn
rubygem-rouge
rubygem-rspec
rubygem-rspec-expectations
rubygem-rspec-mocks
rubygem-rspec-support
rubygem-thread_order
rusers
rust-cbindgen
samba
sanlock
sassist
satyr
sbc
sblim-cim-client2
sblim-cmpi-base
sblim-cmpi-devel
sblim-cmpi-fsvol
sblim-cmpi-network
sblim-cmpi-nfsv3
sblim-cmpi-nfsv4
sblim-cmpi-params
sblim-cmpi-sysfs
sblim-cmpi-syslog
sblim-indication_helper
sblim-sfcb
sblim-sfcc
sblim-sfcCommon
sblim-testsuite
sblim-wbemcli
scl-utils
scotch
screen
scrub
SDL
SDL2
SDL_sound
sdparm
seabios
secilc
selinux-policy
sendmail
serd
setools
setserial
setuptool
sgabios
sgml-common
sgpio
shared-mime-info
sharutils
sip
sisu
skkdic
sleuthkit
slirp4netns
smartmontools
smc-tools
socket_wrapper
softhsm
sombok
sord
sos
sound-theme-freedesktop
soundtouch
sox
soxr
sparsehash
spausedd
speex
speexdsp
spice-protocol
spice-vdagent
spirv-headers
spirv-tools
splix
squashfs-tools
squid
sratom
sscg
star
startup-notification
stunnel
subscription-manager
suitesparse
SuperLU
supermin
switcheroo-control
symlinks
sympy
sysfsutils
systemd-bootchart
t1lib
t1utils
taglib
tang
targetcli
tbb
tcl-pgtcl
tclx
teckit
telnet
tidy
time
tini
tinycdb
tix
tk
tlog
tmpwatch
tn5250
tofrodos
tokyocabinet
tpm-quote-tools
tpm-tools
tss2
ttembed
ttmkfdir
tuna
twolame
uchardet
uclibc-ng
ucpp
ucs-miscfixed-fonts
ucx
udftools
udica
udisks2
uglify-js
uid_wrapper
unicode-emoji
unicode-ucd
unique3
units
upower
uriparser
urlview
usb_modeswitch
usb_modeswitch-data
usbguard
usbip
usbmuxd
usbredir
usermode
ustr
uthash
uuid
uw-imap
v4l-utils
vhostmd
vino
virglrenderer
virt-p2v
virt-top
virt-what
virt-who
vitess
vmem
volume_key
vorbis-tools
vte291
vulkan-headers
vulkan-loader
watchdog
wavpack
wayland
wayland-protocols
web-assets
webrtc-audio-processing
websocketpp
whois
wireguard-tools
wireless-regdb
wireshark
woff2
wordnet
words
wpebackend-fdo
wsmancli
wvdial
x3270
xapian-core
Xaw3d
xcb-proto
xcb-util
xcb-util-image
xcb-util-keysyms
xcb-util-renderutil
xcb-util-wm
xdelta
xdg-dbus-proxy
xdg-utils
xerces-c
xfconf
xfsdump
xhtml1-dtds
xkeyboard-config
xmlstarlet
xmltoman
xmvn
xorg-x11-apps
xorg-x11-drv-libinput
xorg-x11-font-utils
xorg-x11-fonts
xorg-x11-proto-devel
xorg-x11-server
xorg-x11-server-utils
xorg-x11-util-macros
xorg-x11-utils
xorg-x11-xauth
xorg-x11-xbitmaps
xorg-x11-xinit
xorg-x11-xkb-utils
xorg-x11-xtrans-devel
xrestop
xterm
xxhash
yajl
yaml-cpp
yasm
yelp-tools
yelp-xsl
ykclient
yp-tools
ypbind
ypserv
z3
zenity
zerofree
zfs-fuse
zipper
zopfli
zziplib | +| Fedora | [Fedora MIT License Declaration](https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing#License_of_Fedora_SPEC_Files) | a52dec
abseil-cpp
accountsservice
acpica-tools
acpid
adcli
adobe-mappings-cmap
adobe-mappings-pdf
advancecomp
adwaita-icon-theme
afflib
aide
alsa-firmware
alsa-plugins
amtk
amtterm
annobin
ansible-freeipa
archivemount
argparse-manpage
arptables
arpwatch
asio
aspell
aspell-en
at
at-spi2-atk
at-spi2-core
atf
atk
atop
attr
audiofile
augeas
authbind
authd
authselect
autoconf213
avahi
babeltrace
babeltrace2
babl
baekmuk-ttf-fonts
bats
bcache-tools
biosdevname
blosc
bluez
bmake
bogofilter
bolt
boom-boot
booth
botan2
breezy
brotli
buildah
busybox
bwidget
byacc
ca-certificates
cachefilesd
cairomm
calamares
capstone
catatonit
catch
catch1
cdrdao
celt051
cereal
certmonger
cfitsio
cgdcbxd
chan
CharLS
checkpolicy
checksec
chrony
cim-schema
cjkuni-uming-fonts
cjose
cldr-emoji-annotation
clucene
clutter
clutter-gst3
clutter-gtk
cmocka
cogl
collectd
colm
color-filesystem
colord
colorize
compat-lua
compiler-rt
conda
conmon
conntrack-tools
console-setup
container-exception-logger
containernetworking-plugins
convmv
corosync
corosync-qdevice
cpp-hocon
cppcheck
cpprest
cpptest
cpuid
criu
crypto-policies
cryptsetup
cscope
ctags
CUnit
cups
custodia
Cython
dbus-c++
dbus-python
dbxtool
dconf
dcraw
debootstrap
deltarpm
desktop-file-utils
device-mapper-persistent-data
dietlibc
diffstat
ding-libs
discount
distribution-gpg-keys
dleyna-connector-dbus
dleyna-core
dmraid
dnf
dnf-plugins-core
docbook-dtds
docbook-simple
docbook-slides
docbook-style-dsssl
docbook-utils
docbook2X
docbook5-schemas
docbook5-style-xsl
dogtail
dos2unix
dotconf
double-conversion
dovecot
dpdk
dpkg
driverctl
dropwatch
drpm
dumpet
dvd+rw-tools
dwarves
dwz
dyninst
ebtables
edac-utils
edk2
efax
efi-rpm-macros
egl-wayland
eglexternalplatform
elinks
enca
enchant
enchant2
enscript
environment-modules
evemu
execstack
exempi
exiv2
extra-cmake-modules
fabtests
facter
fakechroot
fakeroot
fapolicyd
fdk-aac-free
fdupes
fence-virt
fetchmail
fftw
filebench
fio
fipscheck
firewalld
fish
flac
flatbuffers
flite
fltk
fmt
fontawesome-fonts
fontpackages
fonts-rpm-macros
foomatic-db
freeglut
freeipmi
freeradius
freetds
freexl
fribidi
fros
frr
fsverity-utils
fuse-overlayfs
fuse-sshfs
fuse-zip
fuse3
future
fxload
gavl
gconf-editor
GConf2
gcovr
gcr
gdal
gdisk
gdk-pixbuf2
generic-logos
genwqe-tools
geoclue2
GeoIP
GeoIP-GeoLite-data
geolite2
geos
gfs2-utils
ghc-srpm-macros
giflib
gl-manpages
glew
glm
glog
glusterfs
gnome-desktop-testing
gnome-doc-utils
gnome-icon-theme
gnome-keyring
gnu-efi
go-rpm-macros
gom
google-api-python-client
google-crosextra-caladea-fonts
google-crosextra-carlito-fonts
google-guice
google-noto-cjk-fonts
google-noto-emoji-fonts
google-roboto-slab-fonts
gphoto2
gpm
gpsbabel
graphene
graphite2
graphviz
grubby
gsettings-desktop-schemas
gsl
gsm
gspell
gssdp
gssntlmssp
gstreamer1
gstreamer1-plugins-base
gtk-vnc
gtk2
gtk3
gtkspell
gupnp
gupnp-av
gupnp-dlna
gupnp-igd
hardening-check
hdf
hdf5
heimdal
help2man
hexedit
hicolor-icon-theme
hiera
highlight
hivex
hostname
hping3
hsakmt
htop
hunspell
hunspell-af
hunspell-ar
hunspell-as
hunspell-ast
hunspell-az
hunspell-be
hunspell-bg
hunspell-bn
hunspell-br
hunspell-ca
hunspell-cop
hunspell-csb
hunspell-cv
hunspell-cy
hunspell-da
hunspell-de
hunspell-dsb
hunspell-el
hunspell-en
hunspell-eo
hunspell-es
hunspell-et
hunspell-eu
hunspell-fa
hunspell-fj
hunspell-fo
hunspell-fr
hunspell-fur
hunspell-fy
hunspell-ga
hunspell-gd
hunspell-gl
hunspell-grc
hunspell-gu
hunspell-gv
hunspell-haw
hunspell-hi
hunspell-hil
hunspell-hr
hunspell-hsb
hunspell-ht
hunspell-hu
hunspell-hy
hunspell-ia
hunspell-id
hunspell-is
hunspell-it
hunspell-kk
hunspell-km
hunspell-kn
hunspell-ko
hunspell-ku
hunspell-ky
hunspell-la
hunspell-lb
hunspell-ln
hunspell-mai
hunspell-mg
hunspell-mi
hunspell-mk
hunspell-ml
hunspell-mn
hunspell-mos
hunspell-mr
hunspell-ms
hunspell-mt
hunspell-nds
hunspell-ne
hunspell-nl
hunspell-no
hunspell-nr
hunspell-nso
hunspell-ny
hunspell-om
hunspell-or
hunspell-pa
hunspell-pl
hunspell-pt
hunspell-quh
hunspell-ro
hunspell-ru
hunspell-rw
hunspell-se
hunspell-shs
hunspell-si
hunspell-sk
hunspell-sl
hunspell-smj
hunspell-so
hunspell-sq
hunspell-sr
hunspell-sv
hunspell-sw
hunspell-ta
hunspell-te
hunspell-tet
hunspell-th
hunspell-tk
hunspell-tl
hunspell-tn
hunspell-tpi
hunspell-ts
hunspell-uk
hunspell-uz
hunspell-ve
hunspell-vi
hunspell-wa
hunspell-xh
hunspell-yi
hwdata
hwloc
hyperscan
hyperv-daemons
hyphen
hyphen-as
hyphen-bg
hyphen-bn
hyphen-ca
hyphen-da
hyphen-de
hyphen-el
hyphen-es
hyphen-fa
hyphen-fo
hyphen-fr
hyphen-ga
hyphen-gl
hyphen-grc
hyphen-gu
hyphen-hi
hyphen-hsb
hyphen-hu
hyphen-ia
hyphen-id
hyphen-is
hyphen-it
hyphen-kn
hyphen-ku
hyphen-lt
hyphen-mi
hyphen-ml
hyphen-mn
hyphen-mr
hyphen-nl
hyphen-or
hyphen-pa
hyphen-pl
hyphen-pt
hyphen-ro
hyphen-ru
hyphen-sa
hyphen-sk
hyphen-sl
hyphen-sv
hyphen-ta
hyphen-te
hyphen-tk
hyphen-uk
ibus
ibus-chewing
ibus-hangul
ibus-kkc
ibus-libzhuyin
ibus-m17n
ibus-rawcode
ibus-sayura
ibus-table
ibus-table-chinese
icc-profiles-openicc
icon-naming-utils
icoutils
iftop
iio-sensor-proxy
ilmbase
im-chooser
imaptest
imsettings
indent
infinipath-psm
inih
iniparser
intel-cmt-cat
intel-ipsec-mb
ioping
IP2Location
ipa-pgothic-fonts
ipcalc
ipmitool
iprutils
iptraf-ng
iptstate
irssi
iscsi-initiator-utils
isns-utils
iso-codes
isomd5sum
iw
iwd
jabberpy
jasper
javapackages-bootstrap
javapackages-tools
jbigkit
jdom2
jemalloc
jfsutils
jimtcl
jose
js-jquery
jsoncpp
Judy
kata-containers
kde-filesystem
kde-settings
kexec-tools
keybinder3
keycloak-httpd-client-install
kf5
kf5-kconfig
kf5-kcoreaddons
kf5-ki18n
kf5-kwidgetsaddons
kpmcore
kronosnet
ksh
kyotocabinet
kyua
ladspa
lame
langtable
lapack
lasso
latencytop
lato-fonts
lcms2
lcov
ldns
leatherman
ledmon
lensfun
leveldb
lftp
libabw
libaec
libao
libappstream-glib
libart_lgpl
libasyncns
libatasmart
libavc1394
libblockdev
libbpf
libbsd
libburn
libbytesize
libcacard
libcanberra
libcdio
libcdio-paranoia
libcdr
libcgroup
libchewing
libcli
libcmis
libcmpiutil
libcomps
libcroco
libdaemon
libdap
libdatrie
libdazzle
libdbi
libdbi-drivers
libdbusmenu
libdc1394
libdeflate
libdmx
libdnf
libdrm
libdvdnav
libdvdread
libdwarf
libeasyfc
libecap
libecb
libell
libEMF
libeot
libepoxy
libepubgen
libesmtp
libetonyek
libev
libevdev
libewf
libexif
libexttextcat
libfabric
libfontenc
libfreehand
libftdi
libgadu
libgdither
libgee
libgee06
libgeotiff
libgexiv2
libgit2
libgit2-glib
libglade2
libglvnd
libgovirt
libgphoto2
libgsf
libgta
libguestfs
libgusb
libgxim
libgxps
libhangul
libhugetlbfs
libibcommon
libical
libICE
libicns
libid3tag
libIDL
libidn2
libiec61883
libieee1284
libimobiledevice
libindicator
libinput
libiodbc
libipt
libiptcdata
libiscsi
libisoburn
libisofs
libjcat
libkcapi
libkeepalive
libkkc
libkkc-data
libkml
liblangtag
libldb
libldm
liblerc
liblockfile
liblognorm
liblouis
liblqr-1
liblzf
libmad
libmediaart
libmicrohttpd
libmikmod
libmodman
libmodplug
libmodulemd1
libmpcdec
libmspub
libmtp
libmusicbrainz5
libmwaw
libnbd
libnet
libnetfilter_log
libnfs
libnotify
libntlm
libnumbertext
liboauth
libodfgen
libofa
libogg
liboggz
liboil
libomxil-bellagio
libopenraw
liboping
libosinfo
libotf
libotr
libpagemaker
libpaper
libpciaccess
libpeas
libpfm
libpinyin
libplist
libpmemobj-cpp
libpng12
libpng15
libproxy
libpsm2
libpwquality
libqb
libqxp
libraqm
LibRaw
libraw1394
libreport
libreswan
librevenge
librsvg2
librx
libsamplerate
libsass
libsecret
libsemanage
libsigc++20
libsigsegv
libslirp
libSM
libsmbios
libsmi
libsndfile
libsodium
libspiro
libsrtp
libssh
libstaroffice
libstemmer
libstoragemgmt
libtdb
libteam
libtevent
libthai
libtnc
libtomcrypt
libtommath
libtraceevent
libtranslit
libucil
libunicap
libuninameslist
liburing
libusbmuxd
libuser
libutempter
libvarlink
libverto
libvirt-dbus
libvirt-glib
libvirt-java
libvirt-python
libvisio
libvisual
libvoikko
libvorbis
libvpx
libwacom
libwnck3
libwpd
libwpe
libwpg
libwps
libwvstreams
libX11
libXau
libXaw
libxcb
libXcomposite
libxcrypt
libXcursor
libXdamage
libXdmcp
libXext
libxfce4util
libXfixes
libXfont2
libXft
libXi
libXinerama
libxkbcommon
libxkbfile
libxklavier
libxmlb
libXmu
libXpm
libXrandr
libXrender
libXres
libXScrnSaver
libxshmfence
libXt
libXtst
libXv
libXxf86vm
libyami
libyang
libyubikey
libzip
libzmf
lilv
linuxconsoletools
linuxptp
lksctp-tools
lldpd
lockdev
logwatch
lpsolve
lrzsz
lua
lua-expat
lua-filesystem
lua-json
lua-lpeg
lua-lunit
lua-rpm-macros
lua-term
luajit
luksmeta
lutok
lv2
lzip
lzop
m17n-db
m17n-lib
mac-robber
mailcap
mailx
malaga
malaga-suomi-voikko
mallard-rng
man-pages-cs
man-pages-es
man-pages-it
man-pages-ja
man-pages-ko
man-pages-pl
man-pages-ru
man-pages-zh-CN
mariadb-connector-c
mariadb-connector-odbc
marisa
maven-compiler-plugin
maven-jar-plugin
maven-resolver
maven-resources-plugin
maven-surefire
maven-wagon
mcelog
mcpp
mcstrans
mdadm
mdds
meanwhile
mecab
mecab-ipadic
media-player-info
memcached
memkind
mesa
mesa-libGLU
metis
microcode_ctl
microdnf
minicom
minizip
mksh
mobile-broadband-provider-info
mock
mock-core-configs
mod_auth_gssapi
mod_auth_mellon
mod_auth_openidc
mod_authnz_pam
mod_fcgid
mod_http2
mod_intercept_form_submit
mod_lookup_identity
mod_md
mod_security
mod_security_crs
mod_wsgi
mokutil
mpage
mrtg
mstflint
mt-st
mtdev
mtools
mtr
mtx
multilib-rpm-config
munge
mutt
mythes
mythes-bg
mythes-ca
mythes-cs
mythes-da
mythes-de
mythes-el
mythes-en
mythes-eo
mythes-es
mythes-fr
mythes-ga
mythes-hu
mythes-mi
mythes-ne
mythes-nl
mythes-pl
mythes-pt
mythes-ro
mythes-ru
mythes-sk
mythes-sl
mythes-sv
mythes-uk
nbd
nbdkit
neon
netavark
netcdf
netcf
netlabel_tools
netpbm
netsniff-ng
nfs4-acl-tools
nftables
nilfs-utils
nkf
nload
nlopt
nodejs-packaging
nss-pam-ldapd
nss_nis
nss_wrapper
ntfs-3g
ntfs-3g-system-compression
numad
numatop
numpy
nvmetcli
nvml
oath-toolkit
ocaml
ocaml-alcotest
ocaml-astring
ocaml-base
ocaml-bigarray-compat
ocaml-bisect-ppx
ocaml-calendar
ocaml-camlp5
ocaml-camomile
ocaml-cinaps
ocaml-cmdliner
ocaml-compiler-libs-janestreet
ocaml-cppo
ocaml-csexp
ocaml-csv
ocaml-ctypes
ocaml-curses
ocaml-dune
ocaml-extlib
ocaml-fileutils
ocaml-findlib
ocaml-fmt
ocaml-fpath
ocaml-gettext
ocaml-integers
ocaml-libvirt
ocaml-luv
ocaml-lwt
ocaml-markup
ocaml-migrate-parsetree
ocaml-mmap
ocaml-num
ocaml-ocamlbuild
ocaml-ocplib-endian
ocaml-ounit
ocaml-parsexp
ocaml-ppx-derivers
ocaml-ppxlib
ocaml-re
ocaml-react
ocaml-result
ocaml-seq
ocaml-sexplib
ocaml-sexplib0
ocaml-stdio
ocaml-topkg
ocaml-tyxml
ocaml-uuidm
ocaml-uutf
ocaml-xml-light
ocaml-zarith
ocl-icd
oddjob
ogdi
omping
opa
opal
open-vm-tools
openblas
opencc
opencl-filesystem
opencl-headers
opencryptoki
opencsd
opendnssec
OpenEXR
openjade
openjpeg2
openmpi
openobex
openoffice-lv
openrdate
opensc
openslp
opensm
opensp
openssl
openssl-ibmpkcs11
openssl-pkcs11
openwsman
optipng
opus
opusfile
orangefs
ORBit2
orc
os-prober
osinfo-db
osinfo-db-tools
overpass-fonts
p11-kit
p7zip
pacemaker
pacrunner
pakchois
pam_krb5
pam_wrapper
papi
paps
parallel
patchelf
patchutils
pbzip2
pcp
pcsc-lite
pcsc-lite-ccid
PEGTL
perl
perl-Algorithm-C3
perl-Algorithm-Diff
perl-Alien-Build
perl-Alien-pkgconf
perl-AnyEvent
perl-AnyEvent-AIO
perl-AnyEvent-BDB
perl-App-cpanminus
perl-App-FatPacker
perl-AppConfig
perl-Archive-Extract
perl-Archive-Zip
perl-Authen-SASL
perl-B-Debug
perl-B-Hooks-EndOfScope
perl-B-Hooks-OP-Check
perl-B-Keywords
perl-B-Lint
perl-bareword-filehandles
perl-BDB
perl-Bit-Vector
perl-boolean
perl-Browser-Open
perl-BSD-Resource
perl-Business-ISBN
perl-Business-ISBN-Data
perl-Bytes-Random-Secure
perl-Capture-Tiny
perl-Carp-Clan
perl-CBOR-XS
perl-Class-Accessor
perl-Class-C3
perl-Class-C3-XS
perl-Class-Data-Inheritable
perl-Class-Factory-Util
perl-Class-Inspector
perl-Class-ISA
perl-Class-Load
perl-Class-Load-XS
perl-Class-Method-Modifiers
perl-Class-Singleton
perl-Class-Tiny
perl-Class-XSAccessor
perl-Clone
perl-Color-ANSI-Util
perl-Color-RGB-Util
perl-ColorThemeBase-Static
perl-ColorThemeRole-ANSI
perl-ColorThemes-Standard
perl-ColorThemeUtil-ANSI
perl-Compress-Bzip2
perl-Compress-LZF
perl-Compress-Raw-Lzma
perl-Config-AutoConf
perl-Config-INI
perl-Config-INI-Reader-Multiline
perl-Config-IniFiles
perl-Config-Simple
perl-Config-Tiny
perl-Const-Fast
perl-Convert-ASN1
perl-Convert-Bencode
perl-Coro
perl-Coro-Multicore
perl-CPAN-Changes
perl-CPAN-DistnameInfo
perl-CPAN-Meta-Check
perl-Cpanel-JSON-XS
perl-Crypt-CBC
perl-Crypt-DES
perl-Crypt-IDEA
perl-Crypt-OpenSSL-Bignum
perl-Crypt-OpenSSL-Guess
perl-Crypt-OpenSSL-Random
perl-Crypt-OpenSSL-RSA
perl-Crypt-PasswdMD5
perl-Crypt-Random-Seed
perl-CSS-Tiny
perl-Data-Dump
perl-Data-Munge
perl-Data-OptList
perl-Data-Peek
perl-Data-Section
perl-Data-UUID
perl-Date-Calc
perl-Date-ISO8601
perl-Date-Manip
perl-DateTime
perl-DateTime-Format-Builder
perl-DateTime-Format-DateParse
perl-DateTime-Format-HTTP
perl-DateTime-Format-IBeat
perl-DateTime-Format-ISO8601
perl-DateTime-Format-Mail
perl-DateTime-Format-Strptime
perl-DateTime-Locale
perl-DateTime-TimeZone
perl-DateTime-TimeZone-SystemV
perl-DateTime-TimeZone-Tzfile
perl-DBD-MySQL
perl-Devel-CallChecker
perl-Devel-Caller
perl-Devel-CheckBin
perl-Devel-CheckLib
perl-Devel-Cycle
perl-Devel-EnforceEncapsulation
perl-Devel-GlobalDestruction
perl-Devel-GlobalDestruction-XS
perl-Devel-Hide
perl-Devel-Leak
perl-Devel-LexAlias
perl-Devel-Size
perl-Devel-StackTrace
perl-Devel-Symdump
perl-Digest-BubbleBabble
perl-Digest-CRC
perl-Digest-HMAC
perl-Digest-SHA1
perl-Dist-CheckConflicts
perl-DynaLoader-Functions
perl-Email-Address
perl-Email-Date-Format
perl-Encode-Detect
perl-Encode-EUCJPASCII
perl-Encode-IMAPUTF7
perl-Encode-Locale
perl-Env-ShellWords
perl-Error
perl-EV
perl-Eval-Closure
perl-Event
perl-Exception-Class
perl-Expect
perl-ExtUtils-Config
perl-ExtUtils-Depends
perl-ExtUtils-Helpers
perl-ExtUtils-InstallPaths
perl-ExtUtils-PkgConfig
perl-FCGI
perl-Fedora-VSP
perl-FFI-CheckLib
perl-File-BaseDir
perl-File-BOM
perl-File-chdir
perl-File-CheckTree
perl-File-Copy-Recursive
perl-File-DesktopEntry
perl-File-Find-Object
perl-File-Find-Object-Rule
perl-File-Find-Rule
perl-File-Find-Rule-Perl
perl-File-Inplace
perl-File-Listing
perl-File-MimeInfo
perl-File-pushd
perl-File-ReadBackwards
perl-File-Remove
perl-File-ShareDir
perl-File-ShareDir-Install
perl-File-Slurp
perl-File-Slurp-Tiny
perl-File-Slurper
perl-File-Type
perl-Font-TTF
perl-FreezeThaw
perl-GD
perl-GD-Barcode
perl-generators
perl-Getopt-ArgvFile
perl-gettext
perl-Graphics-ColorNamesLite-WWW
perl-GSSAPI
perl-Guard
perl-Hook-LexWrap
perl-HTML-Parser
perl-HTML-Tagset
perl-HTML-Tree
perl-HTTP-Cookies
perl-HTTP-Daemon
perl-HTTP-Date
perl-HTTP-Message
perl-HTTP-Negotiate
perl-Image-Base
perl-Image-Info
perl-Image-Xbm
perl-Image-Xpm
perl-Import-Into
perl-Importer
perl-inc-latest
perl-indirect
perl-Inline-Files
perl-IO-AIO
perl-IO-All
perl-IO-CaptureOutput
perl-IO-Compress-Lzma
perl-IO-HTML
perl-IO-Multiplex
perl-IO-SessionData
perl-IO-Socket-INET6
perl-IO-String
perl-IO-stringy
perl-IO-Tty
perl-IPC-Run
perl-IPC-Run3
perl-IPC-System-Simple
perl-JSON
perl-JSON-Color
perl-JSON-MaybeXS
perl-LDAP
perl-libnet
perl-libwww-perl
perl-libxml-perl
perl-Lingua-EN-Inflect
perl-List-MoreUtils-XS
perl-local-lib
perl-Locale-Codes
perl-Locale-Maketext-Gettext
perl-Locale-Msgfmt
perl-Locale-PO
perl-Log-Message
perl-Log-Message-Simple
perl-LWP-MediaTypes
perl-LWP-Protocol-https
perl-Mail-AuthenticationResults
perl-Mail-DKIM
perl-Mail-IMAPTalk
perl-Mail-SPF
perl-MailTools
perl-Math-Int64
perl-Math-Random-ISAAC
perl-MIME-Charset
perl-MIME-Lite
perl-MIME-Types
perl-Mixin-Linewise
perl-MLDBM
perl-Mock-Config
perl-Module-Build-Tiny
perl-Module-CPANfile
perl-Module-Implementation
perl-Module-Install-AuthorRequires
perl-Module-Install-AuthorTests
perl-Module-Install-AutoLicense
perl-Module-Install-GithubMeta
perl-Module-Install-ManifestSkip
perl-Module-Install-ReadmeFromPod
perl-Module-Install-ReadmeMarkdownFromPod
perl-Module-Install-Repository
perl-Module-Install-TestBase
perl-Module-Load-Util
perl-Module-Manifest
perl-Module-Manifest-Skip
perl-Module-Package
perl-Module-Package-Au
perl-Module-Pluggable
perl-Module-Runtime
perl-Module-Signature
perl-Mojolicious
perl-Moo
perl-Mozilla-CA
perl-Mozilla-LDAP
perl-MRO-Compat
perl-multidimensional
perl-namespace-autoclean
perl-namespace-clean
perl-Net-CIDR-Lite
perl-Net-Daemon
perl-Net-DNS
perl-Net-DNS-Resolver-Mock
perl-Net-DNS-Resolver-Programmable
perl-Net-HTTP
perl-Net-IMAP-Simple
perl-Net-IMAP-Simple-SSL
perl-Net-IP
perl-Net-LibIDN2
perl-Net-Patricia
perl-Net-SMTP-SSL
perl-Net-SNMP
perl-Net-Telnet
perl-Newt
perl-NNTPClient
perl-NTLM
perl-Number-Compare
perl-Object-Deadly
perl-Object-HashBase
perl-Package-Anon
perl-Package-Constants
perl-Package-DeprecationManager
perl-Package-Generator
perl-Package-Stash
perl-Package-Stash-XS
perl-PadWalker
perl-Paper-Specs
perl-PAR-Dist
perl-Parallel-Iterator
perl-Params-Classify
perl-Params-Util
perl-Params-Validate
perl-Params-ValidationCompiler
perl-Parse-PMFile
perl-Parse-RecDescent
perl-Parse-Yapp
perl-Path-Tiny
perl-Perl-Critic
perl-Perl-Critic-More
perl-Perl-Destruct-Level
perl-Perl-MinimumVersion
perl-Perl4-CoreLibs
perl-PerlIO-gzip
perl-PerlIO-utf8_strict
perl-PkgConfig-LibPkgConf
perl-Pod-Coverage
perl-Pod-Coverage-TrustPod
perl-Pod-Escapes
perl-Pod-Eventual
perl-Pod-LaTeX
perl-Pod-Markdown
perl-Pod-Parser
perl-Pod-Plainer
perl-Pod-POM
perl-Pod-Spell
perl-PPI
perl-PPI-HTML
perl-PPIx-QuoteLike
perl-PPIx-Regexp
perl-PPIx-Utilities
perl-prefork
perl-Probe-Perl
perl-Razor-Agent
perl-Readonly
perl-Readonly-XS
perl-Ref-Util
perl-Ref-Util-XS
perl-Regexp-Pattern-Perl
perl-Return-MultiLevel
perl-Role-Tiny
perl-Scope-Guard
perl-Scope-Upper
perl-SGMLSpm
perl-SNMP_Session
perl-Socket6
perl-Software-License
perl-Sort-Versions
perl-Specio
perl-Spiffy
perl-strictures
perl-String-CRC32
perl-String-Format
perl-String-ShellQuote
perl-String-Similarity
perl-Sub-Exporter
perl-Sub-Exporter-Progressive
perl-Sub-Identify
perl-Sub-Info
perl-Sub-Install
perl-Sub-Name
perl-Sub-Quote
perl-Sub-Uplevel
perl-SUPER
perl-Switch
perl-Syntax-Highlight-Engine-Kate
perl-Sys-CPU
perl-Sys-MemInfo
perl-Sys-Virt
perl-Taint-Runtime
perl-Task-Weaken
perl-Term-Size-Any
perl-Term-Size-Perl
perl-Term-Table
perl-Term-UI
perl-TermReadKey
perl-Test-Base
perl-Test-ClassAPI
perl-Test-CPAN-Meta
perl-Test-CPAN-Meta-JSON
perl-Test-Deep
perl-Test-Differences
perl-Test-DistManifest
perl-Test-Distribution
perl-Test-EOL
perl-Test-Exception
perl-Test-Exit
perl-Test-FailWarnings
perl-Test-Fatal
perl-Test-File
perl-Test-File-ShareDir
perl-Test-Harness
perl-Test-HasVersion
perl-Test-InDistDir
perl-Test-Inter
perl-Test-LeakTrace
perl-Test-LongString
perl-Test-Manifest
perl-Test-Memory-Cycle
perl-Test-MinimumVersion
perl-Test-MockObject
perl-Test-MockRandom
perl-Test-Needs
perl-Test-NoTabs
perl-Test-NoWarnings
perl-Test-Object
perl-Test-Output
perl-Test-Pod
perl-Test-Pod-Coverage
perl-Test-Portability-Files
perl-Test-Requires
perl-Test-RequiresInternet
perl-Test-Script
perl-Test-Simple
perl-Test-SubCalls
perl-Test-Synopsis
perl-Test-Taint
perl-Test-TrailingSpace
perl-Test-utf8
perl-Test-Vars
perl-Test-Warn
perl-Test-Without-Module
perl-Test2-Plugin-NoWarnings
perl-Test2-Suite
perl-Test2-Tools-Explain
perl-Text-CharWidth
perl-Text-CSV_XS
perl-Text-Diff
perl-Text-Glob
perl-Text-Iconv
perl-Text-Soundex
perl-Text-Unidecode
perl-Text-WrapI18N
perl-Tie-IxHash
perl-TimeDate
perl-Tree-DAG_Node
perl-Unicode-EastAsianWidth
perl-Unicode-LineBreak
perl-Unicode-Map8
perl-Unicode-String
perl-Unicode-UTF8
perl-UNIVERSAL-can
perl-UNIVERSAL-isa
perl-Unix-Syslog
perl-URI
perl-Variable-Magic
perl-Version-Requirements
perl-WWW-RobotRules
perl-XML-Catalog
perl-XML-DOM
perl-XML-Dumper
perl-XML-Filter-BufferText
perl-XML-Generator
perl-XML-Grove
perl-XML-Handler-YAWriter
perl-XML-LibXML
perl-XML-LibXSLT
perl-XML-NamespaceSupport
perl-XML-Parser-Lite
perl-XML-RegExp
perl-XML-SAX
perl-XML-SAX-Base
perl-XML-SAX-Writer
perl-XML-Simple
perl-XML-TokeParser
perl-XML-TreeBuilder
perl-XML-Twig
perl-XML-Writer
perl-XML-XPath
perl-XML-XPathEngine
perl-XString
perl-YAML-LibYAML
perl-YAML-PP
perl-YAML-Syck
perltidy
pesign
phodav
php
php-pear
php-pecl-zip
physfs
picosat
pinfo
pipewire
pixman
pkcs11-helper
pkgconf
plexus-cipher
plexus-containers
plexus-sec-dispatcher
plotutils
pmdk-convert
pmix
pngcrush
pngnq
po4a
podman
poetry
policycoreutils
polkit-pkla-compat
portreserve
postfix
potrace
powertop
ppp
pps-tools
pptp
priv_wrapper
procmail
prometheus
prometheus-node-exporter
ps_mem
psacct
psutils
ptlib
publicsuffix-list
pugixml
pulseaudio
puppet
pwgen
pyatspi
pybind11
pycairo
pyelftools
pyflakes
pygobject3
PyGreSQL
pykickstart
pylint
pyparted
pyproject-rpm-macros
pyserial
python-absl-py
python-aiodns
python-aiohttp
python-alsa
python-argcomplete
python-astroid
python-astunparse
python-async-generator
python-augeas
python-azure-sdk
python-beautifulsoup4
python-betamax
python-blinker
python-blivet
python-cached_property
python-charset-normalizer
python-cheetah
python-click
python-cmd2
python-colorama
python-CommonMark
python-conda-package-handling
python-configshell
python-cpuinfo
python-cups
python-curio
python-cytoolz
python-d2to1
python-dbus-client-gen
python-dbus-python-client-gen
python-dbus-signature-pyparsing
python-dbusmock
python-ddt
python-debtcollector
python-decorator
python-distlib
python-dmidecode
python-dns
python-dtopt
python-dulwich
python-enchant
python-entrypoints
python-ethtool
python-evdev
python-extras
python-faker
python-fasteners
python-fields
python-filelock
python-fixtures
python-flake8
python-flask
python-flit
python-flit-core
python-fluidity-sm
python-frozendict
python-funcsigs
python-gast
python-genshi
python-google-auth
python-google-auth-oauthlib
python-greenlet
python-gssapi
python-h5py
python-hs-dbus-signature
python-html5lib
python-httplib2
python-humanize
python-hwdata
python-importlib-metadata
python-inotify
python-into-dbus-python
python-IPy
python-iso8601
python-isodate
python-isort
python-itsdangerous
python-junit-xml
python-justbases
python-justbytes
python-jwcrypto
python-jwt
python-kdcproxy
python-kerberos
python-kmod
python-kubernetes
python-lazy-object-proxy
python-ldap
python-linux-procfs
python-lit
python-markdown
python-mccabe
python-memcached
python-mimeparse
python-mock
python-monotonic
python-more-itertools
python-mpmath
python-msal
python-msrestazure
python-mutagen
python-networkx
python-nose2
python-ntlm-auth
python-oauth2client
python-openpyxl
python-openstackdocstheme
python-oslo-i18n
python-oslo-sphinx
python-paramiko
python-pefile
python-pexpect
python-pkgconfig
python-platformdirs
python-pluggy
python-podman-api
python-process-tests
python-productmd
python-ptyprocess
python-pycares
python-pycosat
python-pydbus
python-pymongo
python-PyMySQL
python-pyperclip
python-pyroute2
python-pyrsistent
python-pysocks
python-pytest-benchmark
python-pytest-cov
python-pytest-expect
python-pytest-flake8
python-pytest-forked
python-pytest-mock
python-pytest-relaxed
python-pytest-runner
python-pytest-subtests
python-pytest-timeout
python-pytest-xdist
python-pytoml
python-pyudev
python-pywbem
python-qrcode
python-rdflib
python-recommonmark
python-redis
python-requests-file
python-requests-ftp
python-requests-kerberos
python-requests-mock
python-requests-oauthlib
python-requests-toolbelt
python-requests_ntlm
python-responses
python-retrying
python-rfc3986
python-rpm-generators
python-rpmfluff
python-rtslib
python-ruamel-yaml
python-ruamel-yaml-clib
python-s3transfer
python-schedutils
python-semantic_version
python-should_dsl
python-simpleline
python-slip
python-sniffio
python-soupsieve
python-sphinx
python-sphinx-epytext
python-sphinx-theme-py3doc-enhanced
python-sphinx_rtd_theme
python-sphinxcontrib-apidoc
python-sphinxcontrib-applehelp
python-sphinxcontrib-devhelp
python-sphinxcontrib-htmlhelp
python-sphinxcontrib-httpdomain
python-sphinxcontrib-jsmath
python-sphinxcontrib-qthelp
python-sphinxcontrib-serializinghtml
python-sqlalchemy
python-suds
python-systemd
python-tempita
python-templated-dictionary
python-termcolor
python-testpath
python-testresources
python-testscenarios
python-testtools
python-tidy
python-toml
python-tomli
python-toolz
python-tornado
python-tox
python-tox-current-env
python-tqdm
python-trio
python-typing-extensions
python-uamqp
python-unittest2
python-uritemplate
python-urwid
python-varlink
python-virt-firmware
python-voluptuous
python-waitress
python-webencodings
python-webtest
python-wheel
python-whoosh
python-winrm
python-wrapt
python-xmltodict
python-yubico
python-zipp
python-zmq
python3-mallard-ducktype
python3-pytest-asyncio
python3-typed_ast
pyusb
pywbem
pyxattr
qemu
qhull
qpdf
qperf
qr-code-generator
qt5-qtbase
qt5-qtconnectivity
qt5-qtdeclarative
qt5-qtsensors
qt5-qtserialport
qt5-qtsvg
qt5-qttools
qt5-rpm-macros
quagga
quota
radvd
ragel
raptor2
rarian
rasdaemon
rasqal
rcs
rdist
rdma-core
re2
re2c
realmd
rear
recode
redland
resource-agents
rest
rhash
rlwrap
rp-pppoe
rpm-mpi-hooks
rpmdevtools
rpmlint
rtkit
rtl-sdr
ruby-augeas
rubygem-bson
rubygem-coderay
rubygem-diff-lcs
rubygem-flexmock
rubygem-hpricot
rubygem-introspection
rubygem-liquid
rubygem-maruku
rubygem-metaclass
rubygem-mongo
rubygem-mustache
rubygem-mysql2
rubygem-pkg-config
rubygem-rake
rubygem-rake-compiler
rubygem-ronn
rubygem-rouge
rubygem-rspec
rubygem-rspec-expectations
rubygem-rspec-mocks
rubygem-rspec-support
rubygem-thread_order
rusers
rust-cbindgen
samba
sanlock
sassist
satyr
sbc
sblim-cim-client2
sblim-cmpi-base
sblim-cmpi-devel
sblim-cmpi-fsvol
sblim-cmpi-network
sblim-cmpi-nfsv3
sblim-cmpi-nfsv4
sblim-cmpi-params
sblim-cmpi-sysfs
sblim-cmpi-syslog
sblim-indication_helper
sblim-sfcb
sblim-sfcc
sblim-sfcCommon
sblim-testsuite
sblim-wbemcli
scl-utils
scotch
screen
scrub
SDL
SDL2
SDL_sound
sdparm
seabios
secilc
selinux-policy
sendmail
serd
setools
setserial
setuptool
sgabios
sgml-common
sgpio
shared-mime-info
sharutils
sip
sisu
skkdic
sleuthkit
slirp4netns
smartmontools
smc-tools
socket_wrapper
softhsm
sombok
sord
sos
sound-theme-freedesktop
soundtouch
sox
soxr
sparsehash
spausedd
speex
speexdsp
spice-protocol
spice-vdagent
spirv-headers
spirv-tools
splix
squashfs-tools
squid
sratom
sscg
star
startup-notification
stunnel
subscription-manager
suitesparse
SuperLU
supermin
switcheroo-control
symlinks
sympy
sysfsutils
systemd-bootchart
t1lib
t1utils
taglib
tang
targetcli
tbb
tcl-pgtcl
tclx
teckit
telnet
tidy
time
tini
tinycdb
tix
tk
tlog
tmpwatch
tn5250
tofrodos
tokyocabinet
tpm-quote-tools
tpm-tools
tss2
ttembed
ttmkfdir
tuna
twolame
uchardet
uclibc-ng
ucpp
ucs-miscfixed-fonts
ucx
udftools
udica
udisks2
uglify-js
uid_wrapper
unicode-emoji
unicode-ucd
unique3
units
upower
uriparser
urlview
usb_modeswitch
usb_modeswitch-data
usbguard
usbip
usbmuxd
usbredir
usermode
ustr
uthash
uuid
uw-imap
v4l-utils
vhostmd
vino
virglrenderer
virt-p2v
virt-top
virt-what
virt-who
vitess
vmem
volume_key
vorbis-tools
vte291
vulkan-headers
vulkan-loader
watchdog
wavpack
wayland
wayland-protocols
web-assets
webrtc-audio-processing
websocketpp
whois
wireguard-tools
wireless-regdb
wireshark
woff2
wordnet
words
wpebackend-fdo
wsmancli
wvdial
x3270
xapian-core
Xaw3d
xcb-proto
xcb-util
xcb-util-image
xcb-util-keysyms
xcb-util-renderutil
xcb-util-wm
xdelta
xdg-dbus-proxy
xdg-utils
xerces-c
xfconf
xfsdump
xhtml1-dtds
xkeyboard-config
xmlstarlet
xmltoman
xmvn
xorg-x11-apps
xorg-x11-drv-libinput
xorg-x11-font-utils
xorg-x11-fonts
xorg-x11-proto-devel
xorg-x11-server
xorg-x11-server-utils
xorg-x11-util-macros
xorg-x11-utils
xorg-x11-xauth
xorg-x11-xbitmaps
xorg-x11-xinit
xorg-x11-xkb-utils
xorg-x11-xtrans-devel
xrestop
xterm
xxhash
yajl
yaml-cpp
yasm
yelp-tools
yelp-xsl
ykclient
yp-tools
ypbind
ypserv
z3
zenity
zerofree
zfs-fuse
zipper
zopfli
zziplib | | Fedora (Copyright Remi Collet) | [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/legalcode) | libmemcached-awesome
librabbitmq | | Fedora (ISC) | [ISC License](https://github.com/sarugaku/resolvelib/blob/main/LICENSE) | python-resolvelib | | Magnus Edenhill Open Source | [Magnus Edenhill Open Source BSD License](https://github.com/jemalloc/jemalloc/blob/dev/COPYING) | librdkafka | diff --git a/SPECS/LICENSES-AND-NOTICES/data/licenses.json b/SPECS/LICENSES-AND-NOTICES/data/licenses.json index 13253920531..f350b563704 100644 --- a/SPECS/LICENSES-AND-NOTICES/data/licenses.json +++ b/SPECS/LICENSES-AND-NOTICES/data/licenses.json @@ -184,6 +184,7 @@ "dogtail", "dos2unix", "dotconf", + "double-conversion", "dovecot", "dpdk", "dpkg", @@ -1679,6 +1680,7 @@ "python-isodate", "python-isort", "python-itsdangerous", + "python-junit-xml", "python-justbases", "python-justbytes", "python-jwcrypto", diff --git a/SPECS/application-gateway-kubernetes-ingress/application-gateway-kubernetes-ingress.spec b/SPECS/application-gateway-kubernetes-ingress/application-gateway-kubernetes-ingress.spec index 1b3832b0530..14bfbb8956a 100644 --- a/SPECS/application-gateway-kubernetes-ingress/application-gateway-kubernetes-ingress.spec +++ b/SPECS/application-gateway-kubernetes-ingress/application-gateway-kubernetes-ingress.spec @@ -55,7 +55,7 @@ cp appgw-ingress %{buildroot}%{_bindir}/ %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.4.0-16 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.4.0-15 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/azcopy/azcopy.spec b/SPECS/azcopy/azcopy.spec index f2ab205c6c2..0d40e9cf227 100644 --- a/SPECS/azcopy/azcopy.spec +++ b/SPECS/azcopy/azcopy.spec @@ -62,7 +62,7 @@ go test -mod=vendor %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 10.15.0-14 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 10.15.0-13 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/blobfuse/blobfuse.spec b/SPECS/blobfuse/blobfuse.spec index 6d6cb08b426..5315c277d99 100644 --- a/SPECS/blobfuse/blobfuse.spec +++ b/SPECS/blobfuse/blobfuse.spec @@ -47,7 +47,7 @@ install -p -m 755 build/blobfuse %{buildroot}%{_bindir}/ %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.4.5-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.4.5-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/blobfuse2/blobfuse2.signatures.json b/SPECS/blobfuse2/blobfuse2.signatures.json index ad7cdd0a363..4ea508fa30a 100644 --- a/SPECS/blobfuse2/blobfuse2.signatures.json +++ b/SPECS/blobfuse2/blobfuse2.signatures.json @@ -1,6 +1,6 @@ { "Signatures": { - "blobfuse2-2.1.1.tar.gz": "6bbed0d7db05ecfe7b7e12b5c4506dde1e2ef018ce1ac6fe6c8b7d697af24968", - "blobfuse2-2.1.1-vendor.tar.gz": "85cbf93aacaa63e583dd9a72f4823f9c993449d5f2ab2332d8b97b4bf91e7da0" + "blobfuse2-2.1.2.tar.gz": "4605015d99c7ffac37ae464aa1d23c11ecd6218122acb06f1c46ac7bdced908e", + "blobfuse2-2.1.2-vendor.tar.gz": "84229241b170316438aa408ae38216e01c54fffdbe50b59ae3b5ab1b4f7122c6" } } \ No newline at end of file diff --git a/SPECS/blobfuse2/blobfuse2.spec b/SPECS/blobfuse2/blobfuse2.spec index cd9bb51e4d3..557b7f029bc 100644 --- a/SPECS/blobfuse2/blobfuse2.spec +++ b/SPECS/blobfuse2/blobfuse2.spec @@ -1,7 +1,7 @@ %global debug_package %{nil} %define our_gopath %{_topdir}/.gopath -%define blobfuse2_version 2.1.1 +%define blobfuse2_version 2.1.2 %define blobfuse2_health_monitor bfusemon Summary: FUSE adapter - Azure Storage @@ -80,11 +80,14 @@ install -D -m 0644 ./setup/blobfuse2-logrotate %{buildroot}%{_sysconfdir}/logrot %{_sysconfdir}/logrotate.d/blobfuse2 %changelog +* Fri Nov 17 2023 Anubhuti Shruti - 2.1.2-1 +- Bump version to 2.1.2 + * Thu Nov 02 2023 Sourav Gupta - 2.1.1-1 - Bump version to 2.1.1 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.1.0-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.1.0-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/busybox/busybox-static.config b/SPECS/busybox/busybox-static.config index c66d90ebfaf..f591858c217 100644 --- a/SPECS/busybox/busybox-static.config +++ b/SPECS/busybox/busybox-static.config @@ -31,7 +31,7 @@ CONFIG_FEATURE_SUID_CONFIG=y CONFIG_FEATURE_SUID_CONFIG_QUIET=y # CONFIG_FEATURE_PREFER_APPLETS is not set CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe" -# CONFIG_SELINUX is not set +CONFIG_SELINUX=y # CONFIG_FEATURE_CLEAN_UP is not set CONFIG_PLATFORM_LINUX=y # @@ -176,7 +176,7 @@ CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y CONFIG_FEATURE_TAR_TO_COMMAND=y CONFIG_FEATURE_TAR_UNAME_GNAME=y CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y -# CONFIG_FEATURE_TAR_SELINUX is not set +CONFIG_FEATURE_TAR_SELINUX=y CONFIG_UNZIP=y CONFIG_FEATURE_UNZIP_CDF=y CONFIG_FEATURE_UNZIP_BZIP2=y diff --git a/SPECS/busybox/busybox.signatures.json b/SPECS/busybox/busybox.signatures.json index ce08593a4aa..77f30f15c10 100644 --- a/SPECS/busybox/busybox.signatures.json +++ b/SPECS/busybox/busybox.signatures.json @@ -2,6 +2,6 @@ "Signatures": { "busybox-1.35.0.tar.bz2": "faeeb244c35a348a334f4a59e44626ee870fb07b6884d68c10ae8bc19f83a694", "busybox-petitboot.config": "28a4006863e0125bb564159c120067cb83b52ee0a829579cd399274cc78a10be", - "busybox-static.config": "6f2f534548da57df8b1f5fd4dfe6ceece0f1b97bf7d0baa4c484ac9850cf8e37" + "busybox-static.config": "e97bc24c897e41e5a6fc6b54955b20e3c49ea5828f9ecba6ba520f8291470e58" } } \ No newline at end of file diff --git a/SPECS/busybox/busybox.spec b/SPECS/busybox/busybox.spec index 9fce04598a7..d8de5ea77c2 100644 --- a/SPECS/busybox/busybox.spec +++ b/SPECS/busybox/busybox.spec @@ -1,7 +1,7 @@ Summary: Statically linked binary providing simplified versions of system commands Name: busybox Version: 1.35.0 -Release: 8%{?dist} +Release: 9%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -15,6 +15,8 @@ Patch2: awk-input-numbers-are-never-octal-or-hex-only-progra.patch Patch3: CVE-2022-30065.patch Patch4: ash-fix-use-after-free-in-pattern-substituon-code.patch Patch5: ash-fix-use-after-free-in-bash-pattern-substitution.patch +Patch6: selinux-copy-file.patch +Patch7: selinux-cp-a.patch BuildRequires: gcc BuildRequires: glibc-static >= 2.35-6%{?dist} BuildRequires: libselinux-devel >= 1.27.7-2 @@ -94,6 +96,10 @@ install -m 644 docs/busybox.petitboot.1 %{buildroot}/%{_mandir}/man1/busybox.pet %{_mandir}/man1/busybox.petitboot.1.gz %changelog +* Thu Nov 16 2023 Chris PeBenito - 1.35.0-9 +- Enable SELinux features. +- Improve SELinux behavior for copy funtions. + * Wed Oct 04 2023 Minghe Ren - 1.35.0-8 - Bump release to rebuild against glibc 2.35-6 diff --git a/SPECS/busybox/selinux-copy-file.patch b/SPECS/busybox/selinux-copy-file.patch new file mode 100644 index 00000000000..7f724e26b67 --- /dev/null +++ b/SPECS/busybox/selinux-copy-file.patch @@ -0,0 +1,50 @@ +From 23b2d8b498939723413a60adc6b29e37ec46b91e Mon Sep 17 00:00:00 2001 +From: Chris PeBenito +Date: Wed, 25 Mar 2020 16:43:17 -0400 +Subject: copy_file(): Revise completion of SELinux security context + preserve/set. + +The existing setfscreatecon() at the beginning of copy_file() is the secure +method for setting the context of new files, but it doesn't apply to +existing files. Change the setfilecon() to only run on preexisting files. + +Signed-off-by: Chris PeBenito + +diff -ur busybox-1.35.0.orig/libbb/copy_file.c busybox-1.35.0/libbb/copy_file.c +--- busybox-1.35.0.orig/libbb/copy_file.c 2021-12-26 16:53:20.000000000 +0000 ++++ busybox-1.35.0/libbb/copy_file.c 2023-08-16 22:04:45.557799523 +0000 +@@ -327,19 +327,22 @@ + if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT)) + && is_selinux_enabled() > 0 + ) { +- security_context_t con; +- if (getfscreatecon(&con) == -1) { ++ /* Failure to preserve the security context isn't fatal here since ++ * the copy has been done at this point. */ ++ security_context_t con = NULL; ++ if (getfscreatecon(&con) < 0) + bb_simple_perror_msg("getfscreatecon"); +- return -1; +- } +- if (con) { +- if (setfilecon(dest, con) == -1) { +- bb_perror_msg("setfilecon:%s,%s", dest, con); +- freecon(con); +- return -1; +- } +- freecon(con); +- } ++ ++ if (setfscreatecon(NULL) < 0) ++ bb_perror_msg("can't reset fscreate"); ++ ++ /* setfscreatecon() only works when a file is created. If dest ++ * preexisted, use setfilecon instead */ ++ if (con && dest_exists) ++ if (fsetfilecon(dst_fd, con) < 0) ++ bb_perror_msg("fsetfilecon:%s,%s", dest, con); ++ ++ freecon(con); + } + #endif + #if ENABLE_FEATURE_CP_REFLINK diff --git a/SPECS/busybox/selinux-cp-a.patch b/SPECS/busybox/selinux-cp-a.patch new file mode 100644 index 00000000000..5a8bc42ff1e --- /dev/null +++ b/SPECS/busybox/selinux-cp-a.patch @@ -0,0 +1,48 @@ +From c2c58cb044b21630eb4aef08a92bb194ab27f20f Mon Sep 17 00:00:00 2001 +From: Chris PeBenito +Date: Fri, 26 Apr 2019 11:23:09 -0400 +Subject: cp: Have -a imply -c when SELinux is enabled. + +Have cp preserve SELinux context when using -a. Coreutils cp also does +this. + +Signed-off-by: Chris PeBenito + +diff -ur busybox-1.35.0.orig/coreutils/cp.c busybox-1.35.0/coreutils/cp.c +--- busybox-1.35.0.orig/coreutils/cp.c 2021-12-26 16:53:20.000000000 +0000 ++++ busybox-1.35.0/coreutils/cp.c 2023-08-16 20:43:47.187763692 +0000 +@@ -88,8 +88,7 @@ + //usage: "or: cp [-arPLHpfinlsu] SOURCE... { -t DIRECTORY | DIRECTORY }" + //usage:#define cp_full_usage "\n\n" + //usage: "Copy SOURCEs to DEST\n" +-//usage: "\n -a Same as -dpR" +-//usage: IF_SELINUX( ++//usage: "\n -a Same as -dpR" IF_SELINUX("c" + //usage: "\n -c Preserve security context" + //usage: ) + //usage: "\n -R,-r Recurse" +@@ -195,6 +194,12 @@ + flags |= FILEUTILS_DEREFERENCE; + + #if ENABLE_SELINUX ++ /* for -a, imply -c if SELinux is enabled. */ ++ if ((flags & FILEUTILS_ARCHIVE) && is_selinux_enabled() > 0) { ++ flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT; ++ } ++ ++ /* -c may be explicitly set */ + if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) { + selinux_or_die(); + } +diff -ur busybox-1.35.0.orig/include/libbb.h busybox-1.35.0/include/libbb.h +--- busybox-1.35.0.orig/include/libbb.h 2021-12-26 16:53:26.000000000 +0000 ++++ busybox-1.35.0/include/libbb.h 2023-08-16 20:44:39.681109910 +0000 +@@ -472,7 +472,7 @@ + FILEUTILS_MAKE_SOFTLINK = 1 << 7, /* -s */ + FILEUTILS_DEREF_SOFTLINK = 1 << 8, /* -L */ + FILEUTILS_DEREFERENCE_L0 = 1 << 9, /* -H */ +- /* -a = -pdR (mapped in cp.c) */ ++ FILEUTILS_ARCHIVE = 1 << 9, /* -a = -pdR, -pdRc if SELinux (mapped in cp.c) */ + /* -r = -dR (mapped in cp.c) */ + /* -P = -d (mapped in cp.c) */ + FILEUTILS_VERBOSE = (1 << 13) * ENABLE_FEATURE_VERBOSE, /* -v */ diff --git a/SPECS/cert-manager/cert-manager.spec b/SPECS/cert-manager/cert-manager.spec index 21a0a89ea5c..a953738b633 100644 --- a/SPECS/cert-manager/cert-manager.spec +++ b/SPECS/cert-manager/cert-manager.spec @@ -110,7 +110,7 @@ install -D -m0755 bin/webhook %{buildroot}%{_bindir}/ %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.11.2-6 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.11.2-5 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/cf-cli/cf-cli.spec b/SPECS/cf-cli/cf-cli.spec index ac0478fdb9c..1c30573bfa9 100644 --- a/SPECS/cf-cli/cf-cli.spec +++ b/SPECS/cf-cli/cf-cli.spec @@ -60,7 +60,7 @@ install -p -m 755 -t %{buildroot}%{_bindir} ./out/cf %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 8.4.0-14 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 8.4.0-13 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/cni-plugins/cni-plugins.spec b/SPECS/cni-plugins/cni-plugins.spec index 52f799172c6..257d2982eec 100644 --- a/SPECS/cni-plugins/cni-plugins.spec +++ b/SPECS/cni-plugins/cni-plugins.spec @@ -44,7 +44,7 @@ make -k check |& tee %{_specdir}/%{name}-check-log || %{nocheck} - Upgrade to version 1.3.0 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.9.1-16 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.9.1-15 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/cni/cni.spec b/SPECS/cni/cni.spec index 146010046bd..878f7520a79 100644 --- a/SPECS/cni/cni.spec +++ b/SPECS/cni/cni.spec @@ -114,7 +114,7 @@ install -m 755 -d "%{buildroot}%{cni_doc_dir}" %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.0.1-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.0.1-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/containerized-data-importer/containerized-data-importer.spec b/SPECS/containerized-data-importer/containerized-data-importer.spec index 17a8161426c..50daaca9830 100644 --- a/SPECS/containerized-data-importer/containerized-data-importer.spec +++ b/SPECS/containerized-data-importer/containerized-data-importer.spec @@ -199,7 +199,7 @@ install -m 0644 _out/manifests/release/cdi-cr.yaml %{buildroot}%{_datadir}/cdi/m %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.55.0-16 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.55.0-15 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/coredns/coredns.spec b/SPECS/coredns/coredns.spec index df99a7c537e..df018f3d6c0 100644 --- a/SPECS/coredns/coredns.spec +++ b/SPECS/coredns/coredns.spec @@ -62,7 +62,7 @@ install -p -m 755 -t %{buildroot}%{_bindir} %{name} - Upgrade to 1.11.1 to match version required by kubernetes * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.9.3-10 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.9.3-9 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/cri-o/cri-o.spec b/SPECS/cri-o/cri-o.spec index e648102a7f0..da4842f8a4c 100644 --- a/SPECS/cri-o/cri-o.spec +++ b/SPECS/cri-o/cri-o.spec @@ -204,7 +204,7 @@ mkdir -p /opt/cni/bin %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.21.2-18 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.21.2-17 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/cri-tools/cri-tools.spec b/SPECS/cri-tools/cri-tools.spec index cd2752b7736..544992fd313 100644 --- a/SPECS/cri-tools/cri-tools.spec +++ b/SPECS/cri-tools/cri-tools.spec @@ -45,7 +45,7 @@ install -p -m 755 -t %{buildroot}%{_bindir} "${BUILD_FOLDER}/critest" %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.28.0-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.28.0-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/csi-driver-lvm/csi-driver-lvm.spec b/SPECS/csi-driver-lvm/csi-driver-lvm.spec index 1c59d75c95a..b8553b7f306 100644 --- a/SPECS/csi-driver-lvm/csi-driver-lvm.spec +++ b/SPECS/csi-driver-lvm/csi-driver-lvm.spec @@ -64,7 +64,7 @@ install -D -m0755 bin/lvmplugin %{buildroot}%{_bindir}/ %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.4.1-14 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.4.1-13 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/dcos-cli/dcos-cli.spec b/SPECS/dcos-cli/dcos-cli.spec index 32355088a1d..d0e6feae547 100644 --- a/SPECS/dcos-cli/dcos-cli.spec +++ b/SPECS/dcos-cli/dcos-cli.spec @@ -46,7 +46,7 @@ go test -mod=vendor %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.2.0-14 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.2.0-13 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/double-conversion/double-conversion.signatures.json b/SPECS/double-conversion/double-conversion.signatures.json new file mode 100644 index 00000000000..1b85a4c917a --- /dev/null +++ b/SPECS/double-conversion/double-conversion.signatures.json @@ -0,0 +1,5 @@ +{ + "Signatures": { + "double-conversion-3.1.5.tar.gz": "a63ecb93182134ba4293fd5f22d6e08ca417caafa244afaa751cbfddf6415b13" + } +} \ No newline at end of file diff --git a/SPECS/double-conversion/double-conversion.spec b/SPECS/double-conversion/double-conversion.spec new file mode 100644 index 00000000000..6c3d1653ff5 --- /dev/null +++ b/SPECS/double-conversion/double-conversion.spec @@ -0,0 +1,179 @@ +%bcond_without static_libs # don't build static libraries +Summary: Library providing binary-decimal and decimal-binary routines for IEEE doubles +Name: double-conversion +Version: 3.1.5 +Release: 9%{?dist} +License: BSD +Vendor: Microsoft Corporation +Distribution: Mariner +URL: https://github.com/google/double-conversion +Source0: https://github.com/google/double-conversion/archive/v%{version}/%{name}-%{version}.tar.gz +BuildRequires: cmake +BuildRequires: gcc +BuildRequires: gcc-c++ +%undefine __cmake_in_source_build + +%description +Provides binary-decimal and decimal-binary routines for IEEE doubles. +The library consists of efficient conversion routines that have been +extracted from the V8 JavaScript engine. The code has been re-factored +and improved so that it can be used more easily in other projects. + +%package devel +Summary: Library providing binary-decimal and decimal-binary routines for IEEE doubles +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description devel +Contains header files for developing applications that use the %{name} +library. + +There is extensive documentation in src/double-conversion.h. Other +examples can be found in test/cctest/test-conversions.cc. + +%package static +Summary: Library providing binary-decimal and decimal-binary routines for IEEE doubles +Requires: %{name}-devel%{?_isa} = %{version}-%{release} + +%description static +Static %{name} library. + +%prep +%setup -q + +%build +%global _vpath_builddir build-shared +%cmake -DBUILD_TESTING=ON +%cmake_build + +%if %{with static_libs} +%global _vpath_builddir build-static +CXXFLAGS="%{optflags} -fPIC" %cmake -DBUILD_SHARED_LIBS=NO +%cmake_build +%endif + +%install +%if %{with static_libs} +%global _vpath_builddir build-static +%cmake_install +%endif + +%global _vpath_builddir build-shared +%cmake_install + +%check +%ctest + +%ldconfig_scriptlets + +%files +%license LICENSE +%doc README.md AUTHORS Changelog +%{_libdir}/libdouble-conversion.so.3* + +%files devel +%{_libdir}/libdouble-conversion.so +%{_libdir}/cmake/%{name} +%{_includedir}/%{name} + +%if %{with static_libs} +%files static +%{_libdir}/libdouble-conversion.a +%endif + +%changelog +* Wed Nov 22 2023 Sindhu Karri - 3.1.5-9 +- Initial CBL-Mariner import from Fedora 38 (license: MIT) +- Source license verified: BSD + +* Thu Jan 19 2023 Fedora Release Engineering - 3.1.5-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Thu Jul 21 2022 Fedora Release Engineering - 3.1.5-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Thu Jan 20 2022 Fedora Release Engineering - 3.1.5-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Wed Jul 21 2021 Fedora Release Engineering - 3.1.5-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Tue Jan 26 2021 Fedora Release Engineering - 3.1.5-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Mon Jul 27 2020 Fedora Release Engineering - 3.1.5-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue Jan 28 2020 Fedora Release Engineering - 3.1.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Sun Sep 22 2019 Orion Poplawski - 3.1.5-1 +- Update to 3.1.5 + +* Wed Jul 24 2019 Fedora Release Engineering - 3.0.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Thu Jan 31 2019 Fedora Release Engineering - 3.0.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Thu Jul 12 2018 Fedora Release Engineering - 3.0.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed Feb 07 2018 Fedora Release Engineering - 3.0.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Mon Sep 4 2017 Milan Bouchet-Valat - 3.0.0-1 +- New upstream release. + +* Sun Aug 06 2017 Björn Esser - 2.0.1-11 +- Rebuilt for AutoReq cmake-filesystem + +* Wed Aug 02 2017 Fedora Release Engineering - 2.0.1-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 2.0.1-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Feb 10 2017 Fedora Release Engineering - 2.0.1-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Wed Feb 03 2016 Fedora Release Engineering - 2.0.1-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Wed Jun 17 2015 Fedora Release Engineering - 2.0.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Fri Mar 13 2015 Orion Poplawski - 2.0.1-5 +- Use github source + +* Wed Mar 11 2015 Orion Poplawski - 2.0.1-4 +- Build with cmake + +* Sat Aug 16 2014 Fedora Release Engineering - 2.0.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 2.0.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Sat Feb 8 2014 Milan Bouchet-Valat - 2.0.1-1 +- New upstream version. +- Drop no longer needed custom SConstruct file and use new upstream SONAME. + +* Tue Dec 17 2013 Milan Bouchet-Valat - 2.0.0-4 +- Drop libstdc++-devel from BuildRequires. +- Move %%check after %%install. + +* Sat Dec 14 2013 Milan Bouchet-Valat - 2.0.0-3 +- Remove gcc-c++ from BuildRequires as it is an exception. +- Fix command in %%check and pass CXXFLAGS to scons. +- Use %%global instead of %%define. + +* Thu Dec 12 2013 Milan Bouchet-Valat - 2.0.0-2 +- Fix building when "--without static_libs" is passed. +- Remove %%ghost with libdouble-conversion.so.2. +- Drop BuildRoot. +- Use rm instead of %%{__rm} for consistency +- Use %%{?dist} in Release. + +* Wed Dec 11 2013 Milan Bouchet-Valat - 2.0.0-1 +- Initial Fedora package based on a PLD Linux RPM by Elan Ruusamäe : + http://git.pld-linux.org/gitweb.cgi?p=packages/double-conversion.git diff --git a/SPECS/etcd/etcd.spec b/SPECS/etcd/etcd.spec index c5fee5e574e..618131be03b 100644 --- a/SPECS/etcd/etcd.spec +++ b/SPECS/etcd/etcd.spec @@ -149,7 +149,7 @@ install -vdm755 %{buildroot}%{_sharedstatedir}/etcd - Upgrade to 3.5.9 to match version required by kubernetes * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.5.6-12 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.5.6-11 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/flannel/flannel.spec b/SPECS/flannel/flannel.spec index 2b45d3d6f7b..e2239921e12 100644 --- a/SPECS/flannel/flannel.spec +++ b/SPECS/flannel/flannel.spec @@ -52,7 +52,7 @@ install -p -m 755 -t %{buildroot}%{_bindir} ./dist/flanneld - Bump release to rebuild against glibc 2.35-6 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.14.0-19 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.14.0-18 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/frr/CVE-2023-47234.patch b/SPECS/frr/CVE-2023-47234.patch new file mode 100644 index 00000000000..a5da9becafd --- /dev/null +++ b/SPECS/frr/CVE-2023-47234.patch @@ -0,0 +1,89 @@ +From c37119df45bbf4ef713bc10475af2ee06e12f3bf Mon Sep 17 00:00:00 2001 +From: Donatas Abraitis +Date: Sun, 29 Oct 2023 22:44:45 +0200 +Subject: [PATCH] bgpd: Ignore handling NLRIs if we received MP_UNREACH_NLRI + +If we receive MP_UNREACH_NLRI, we should stop handling remaining NLRIs if +no mandatory path attributes received. + +In other words, if MP_UNREACH_NLRI received, the remaining NLRIs should be handled +as a new data, but without mandatory attributes, it's a malformed packet. + +In normal case, this MUST not happen at all, but to avoid crashing bgpd, we MUST +handle that. + +Reported-by: Iggy Frankovic +Signed-off-by: Donatas Abraitis +--- + bgpd/bgp_attr.c | 19 ++++++++++--------- + bgpd/bgp_attr.h | 1 + + bgpd/bgp_packet.c | 7 ++++++- + 3 files changed, 17 insertions(+), 10 deletions(-) + +diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c +index 1473dc772502..75aa2ac7cce6 100644 +--- a/bgpd/bgp_attr.c ++++ b/bgpd/bgp_attr.c +@@ -3414,15 +3414,6 @@ static int bgp_attr_check(struct peer *peer, struct attr *attr, + !length) + return BGP_ATTR_PARSE_WITHDRAW; + +- /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required +- to carry any other path attributes.", though if MP_REACH_NLRI or NLRI +- are present, it should. Check for any other attribute being present +- instead. +- */ +- if ((!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)) && +- CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI)))) +- return BGP_ATTR_PARSE_PROCEED; +- + if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGIN))) + type = BGP_ATTR_ORIGIN; + +@@ -3441,6 +3432,16 @@ static int bgp_attr_check(struct peer *peer, struct attr *attr, + && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) + type = BGP_ATTR_LOCAL_PREF; + ++ /* An UPDATE message that contains the MP_UNREACH_NLRI is not required ++ * to carry any other path attributes. Though if MP_REACH_NLRI or NLRI ++ * are present, it should. Check for any other attribute being present ++ * instead. ++ */ ++ if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)) && ++ CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI))) ++ return type ? BGP_ATTR_PARSE_MISSING_MANDATORY ++ : BGP_ATTR_PARSE_PROCEED; ++ + /* If any of the well-known mandatory attributes are not present + * in an UPDATE message, then "treat-as-withdraw" MUST be used. + */ +diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h +index fc347e7a1b4b..d30155e6dba0 100644 +--- a/bgpd/bgp_attr.h ++++ b/bgpd/bgp_attr.h +@@ -379,6 +379,7 @@ enum bgp_attr_parse_ret { + /* only used internally, send notify + convert to BGP_ATTR_PARSE_ERROR + */ + BGP_ATTR_PARSE_ERROR_NOTIFYPLS = -3, ++ BGP_ATTR_PARSE_MISSING_MANDATORY = -4, + }; + + struct bpacket_attr_vec_arr; +diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c +index a7514a26aa64..5dc35157ebf6 100644 +--- a/bgpd/bgp_packet.c ++++ b/bgpd/bgp_packet.c +@@ -1983,7 +1983,12 @@ static int bgp_update_receive(struct peer_connection *connection, + /* Network Layer Reachability Information. */ + update_len = end - stream_pnt(s); + +- if (update_len && attribute_len) { ++ /* If we received MP_UNREACH_NLRI attribute, but also NLRIs, then ++ * NLRIs should be handled as a new data. Though, if we received ++ * NLRIs without mandatory attributes, they should be ignored. ++ */ ++ if (update_len && attribute_len && ++ attr_parse_ret != BGP_ATTR_PARSE_MISSING_MANDATORY) { + /* Set NLRI portion to structure. */ + nlris[NLRI_UPDATE].afi = AFI_IP; + nlris[NLRI_UPDATE].safi = SAFI_UNICAST; diff --git a/SPECS/frr/CVE-2023-47235.patch b/SPECS/frr/CVE-2023-47235.patch new file mode 100644 index 00000000000..223e53f6aff --- /dev/null +++ b/SPECS/frr/CVE-2023-47235.patch @@ -0,0 +1,106 @@ +From 6814f2e0138a6ea5e1f83bdd9085d9a77999900b Mon Sep 17 00:00:00 2001 +From: Donatas Abraitis +Date: Fri, 27 Oct 2023 11:56:45 +0300 +Subject: [PATCH] bgpd: Treat EOR as withdrawn to avoid unwanted handling of + malformed attrs + +Treat-as-withdraw, otherwise if we just ignore it, we will pass it to be +processed as a normal UPDATE without mandatory attributes, that could lead +to harmful behavior. In this case, a crash for route-maps with the configuration +such as: + +``` +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 127.0.0.1 remote-as external + neighbor 127.0.0.1 passive + neighbor 127.0.0.1 ebgp-multihop + neighbor 127.0.0.1 disable-connected-check + neighbor 127.0.0.1 update-source 127.0.0.2 + neighbor 127.0.0.1 timers 3 90 + neighbor 127.0.0.1 timers connect 1 + ! + address-family ipv4 unicast + neighbor 127.0.0.1 addpath-tx-all-paths + neighbor 127.0.0.1 default-originate + neighbor 127.0.0.1 route-map RM_IN in + exit-address-family +exit +! +route-map RM_IN permit 10 + set as-path prepend 200 +exit +``` + +Send a malformed optional transitive attribute: + +``` +import socket +import time + +OPEN = (b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +b"\xff\xff\x00\x62\x01\x04\xfd\xea\x00\x5a\x0a\x00\x00\x01\x45\x02" +b"\x06\x01\x04\x00\x01\x00\x01\x02\x02\x02\x00\x02\x02\x46\x00\x02" +b"\x06\x41\x04\x00\x00\xfd\xea\x02\x02\x06\x00\x02\x06\x45\x04\x00" +b"\x01\x01\x03\x02\x0e\x49\x0c\x0a\x64\x6f\x6e\x61\x74\x61\x73\x2d" +b"\x70\x63\x00\x02\x04\x40\x02\x00\x78\x02\x09\x47\x07\x00\x01\x01" +b"\x80\x00\x00\x00") + +KEEPALIVE = (b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +b"\xff\xff\xff\xff\xff\xff\x00\x13\x04") + +UPDATE = bytearray.fromhex("ffffffffffffffffffffffffffffffff002b0200000003c0ff00010100eb00ac100b0b001ad908ac100b0b") + +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect(('127.0.0.2', 179)) +s.send(OPEN) +data = s.recv(1024) +s.send(KEEPALIVE) +data = s.recv(1024) +s.send(UPDATE) +data = s.recv(1024) +time.sleep(100) +s.close() +``` + +Reported-by: Iggy Frankovic +Signed-off-by: Donatas Abraitis +--- + bgpd/bgp_attr.c | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c +index cf2dbe65b805..1473dc772502 100644 +--- a/bgpd/bgp_attr.c ++++ b/bgpd/bgp_attr.c +@@ -3406,10 +3406,13 @@ static int bgp_attr_check(struct peer *peer, struct attr *attr, + uint8_t type = 0; + + /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an +- * empty UPDATE. */ ++ * empty UPDATE. Treat-as-withdraw, otherwise if we just ignore it, ++ * we will pass it to be processed as a normal UPDATE without mandatory ++ * attributes, that could lead to harmful behavior. ++ */ + if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag && + !length) +- return BGP_ATTR_PARSE_PROCEED; ++ return BGP_ATTR_PARSE_WITHDRAW; + + /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required + to carry any other path attributes.", though if MP_REACH_NLRI or NLRI +@@ -3843,7 +3843,13 @@ enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr, + aspath_unintern(&as4_path); + + transit = bgp_attr_get_transit(attr); +- if (ret != BGP_ATTR_PARSE_ERROR) { ++ /* If we received an UPDATE with mandatory attributes, then ++ * the unrecognized transitive optional attribute of that ++ * path MUST be passed. Otherwise, it's an error, and from ++ * security perspective it might be very harmful if we continue ++ * here with the unrecognized attributes. ++ */ ++ if (ret == BGP_ATTR_PARSE_PROCEED) { + /* Finally intern unknown attribute. */ + if (transit) + bgp_attr_set_transit(attr, transit_intern(transit)); diff --git a/SPECS/frr/frr.spec b/SPECS/frr/frr.spec index cc77cd5b193..569cbcfa375 100644 --- a/SPECS/frr/frr.spec +++ b/SPECS/frr/frr.spec @@ -3,7 +3,7 @@ Summary: Routing daemon Name: frr Version: 8.5.3 -Release: 3%{?dist} +Release: 4%{?dist} License: GPL-2.0-or-later Vendor: Microsoft Corporation Distribution: Mariner @@ -18,6 +18,8 @@ Patch3: 0003-fips-mode.patch Patch4: 0004-remove-grpc-test.patch Patch5: CVE-2023-46752.patch Patch6: CVE-2023-46753.patch +Patch7: CVE-2023-47235.patch +Patch8: CVE-2023-47234.patch BuildRequires: autoconf BuildRequires: automake BuildRequires: bison @@ -199,6 +201,9 @@ rm tests/lib/*grpc* %{_sysusersdir}/%{name}.conf %changelog +* Tue Nov 14 2023 Sam Meluch - 8.5.3-4 +- Patch CVE-2023-47234 and CVE-2023-47235 + * Mon Nov 06 2023 Rachel Menge - 8.5.3-3 - Patch CVE-2023-46752 and CVE-2023-46753 diff --git a/SPECS/gh/gh.spec b/SPECS/gh/gh.spec index 3abfa1bd838..757dec924ec 100644 --- a/SPECS/gh/gh.spec +++ b/SPECS/gh/gh.spec @@ -73,7 +73,7 @@ make test %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.13.0-16 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.13.0-15 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/git-lfs/git-lfs.spec b/SPECS/git-lfs/git-lfs.spec index ec0412b9631..9207737410f 100644 --- a/SPECS/git-lfs/git-lfs.spec +++ b/SPECS/git-lfs/git-lfs.spec @@ -78,7 +78,7 @@ git lfs uninstall %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.1.4-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.1.4-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/glide/glide.spec b/SPECS/glide/glide.spec index 2eb691ae7b2..a2f02463d95 100644 --- a/SPECS/glide/glide.spec +++ b/SPECS/glide/glide.spec @@ -54,7 +54,7 @@ popd %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.13.3-24 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.13.3-23 - Bump release to rebuild with updated version of Go. diff --git a/SPECS-EXTENDED/glog/glog.signatures.json b/SPECS/glog/glog.signatures.json similarity index 100% rename from SPECS-EXTENDED/glog/glog.signatures.json rename to SPECS/glog/glog.signatures.json diff --git a/SPECS-EXTENDED/glog/glog.spec b/SPECS/glog/glog.spec similarity index 100% rename from SPECS-EXTENDED/glog/glog.spec rename to SPECS/glog/glog.spec diff --git a/SPECS/go-md2man/go-md2man.spec b/SPECS/go-md2man/go-md2man.spec index d514c688185..88adf9a19ea 100644 --- a/SPECS/go-md2man/go-md2man.spec +++ b/SPECS/go-md2man/go-md2man.spec @@ -49,7 +49,7 @@ cp go-md2man-%{version}/LICENSE.md %{buildroot}%{_docdir}/%{name}-%{version}/LIC %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.0.1-21 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.0.1-20 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/gobject-introspection/gobject-introspection.spec b/SPECS/gobject-introspection/gobject-introspection.spec index 7b6814b1561..47cda0e9137 100644 --- a/SPECS/gobject-introspection/gobject-introspection.spec +++ b/SPECS/gobject-introspection/gobject-introspection.spec @@ -99,7 +99,7 @@ find %{buildroot} -type f -name "*.la" -delete -print %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.71.0-16 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.71.0-15 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/helm/helm.spec b/SPECS/helm/helm.spec index aaa33fcce7e..efd6dfd6e5b 100644 --- a/SPECS/helm/helm.spec +++ b/SPECS/helm/helm.spec @@ -57,7 +57,7 @@ go test -v ./cmd/helm %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.10.3-11 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.10.3-10 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/hyperv-daemons/hyperv-daemons.signatures.json b/SPECS/hyperv-daemons/hyperv-daemons.signatures.json index 0a2d58945c7..52bcf550955 100644 --- a/SPECS/hyperv-daemons/hyperv-daemons.signatures.json +++ b/SPECS/hyperv-daemons/hyperv-daemons.signatures.json @@ -7,6 +7,6 @@ "hypervkvpd.service": "c1bb207cf9f388f8f3cf5b649abbf8cfe4c4fcf74538612946e68f350d1f265f", "hypervvss.rules": "94cead44245ef6553ab79c0bbac8419e3ff4b241f01bcec66e6f508098cbedd1", "hypervvssd.service": "22270d9f0f23af4ea7905f19c1d5d5495e40c1f782cbb87a99f8aec5a011078d", - "kernel-5.15.137.1.tar.gz": "c00abd18daa5fcdf732d88bed57eb26a247473888c8aa9003897baa15d6c0e58" + "kernel-5.15.138.1.tar.gz": "7b5e3a24102b00889e44094e0e47e0901600ba9d3a0197e75e39173ddf760d4c" } } \ No newline at end of file diff --git a/SPECS/hyperv-daemons/hyperv-daemons.spec b/SPECS/hyperv-daemons/hyperv-daemons.spec index 883971afaae..aa5ffce61f0 100644 --- a/SPECS/hyperv-daemons/hyperv-daemons.spec +++ b/SPECS/hyperv-daemons/hyperv-daemons.spec @@ -8,7 +8,7 @@ %global udev_prefix 70 Summary: Hyper-V daemons suite Name: hyperv-daemons -Version: 5.15.137.1 +Version: 5.15.138.1 Release: 1%{?dist} License: GPLv2+ Vendor: Microsoft Corporation @@ -219,6 +219,9 @@ fi %{_sbindir}/lsvmbus %changelog +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS/influx-cli/influx-cli.spec b/SPECS/influx-cli/influx-cli.spec index bbf11b8cccb..e7dc3bd8444 100644 --- a/SPECS/influx-cli/influx-cli.spec +++ b/SPECS/influx-cli/influx-cli.spec @@ -82,7 +82,7 @@ bin/influx completion zsh > %{buildroot}/%{_datadir}/zsh/site-functions/_influx %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.6.1-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.6.1-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/influxdb/influxdb.spec b/SPECS/influxdb/influxdb.spec index 73249570ec4..0ad4aa3d867 100644 --- a/SPECS/influxdb/influxdb.spec +++ b/SPECS/influxdb/influxdb.spec @@ -145,7 +145,7 @@ go test ./... %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.6.1-12 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.6.1-11 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/jx/jx.spec b/SPECS/jx/jx.spec index 4b67b1edf4d..9d4336c4801 100644 --- a/SPECS/jx/jx.spec +++ b/SPECS/jx/jx.spec @@ -61,7 +61,7 @@ install -p -m 755 -t %{buildroot}%{_bindir} ./build/jx %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.2.236-14 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.2.236-13 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/kata-containers-cc/kata-containers-cc.spec b/SPECS/kata-containers-cc/kata-containers-cc.spec index 9e31a09f4c2..9e129c45974 100644 --- a/SPECS/kata-containers-cc/kata-containers-cc.spec +++ b/SPECS/kata-containers-cc/kata-containers-cc.spec @@ -296,7 +296,7 @@ install -D -m 0755 %{_builddir}/%{name}-%{version}/tools/osbuilder/image-builder - Add patch to retain UVM rootfs dependencies * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.6.1-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.6.1-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/kata-containers/kata-containers.spec b/SPECS/kata-containers/kata-containers.spec index 360a0b02feb..18225c089b3 100644 --- a/SPECS/kata-containers/kata-containers.spec +++ b/SPECS/kata-containers/kata-containers.spec @@ -231,7 +231,7 @@ ln -sf %{_bindir}/kata-runtime %{buildroot}%{_prefix}/local/bin/kata-runtime %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.1.0-9 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.1.0-8 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/keda/keda.spec b/SPECS/keda/keda.spec index 8173d62174e..b79c45ff4b0 100644 --- a/SPECS/keda/keda.spec +++ b/SPECS/keda/keda.spec @@ -56,7 +56,7 @@ cp ./bin/keda-adapter %{buildroot}%{_bindir} %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.4.0-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.4.0-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/kernel-azure/config b/SPECS/kernel-azure/config index f8232b3b85c..5e11fd08f35 100644 --- a/SPECS/kernel-azure/config +++ b/SPECS/kernel-azure/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 5.15.137.1 Kernel Configuration +# Linux/x86_64 5.15.138.1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 11.2.0" CONFIG_CC_IS_GCC=y diff --git a/SPECS/kernel-azure/config_aarch64 b/SPECS/kernel-azure/config_aarch64 index 70836f5c227..6319d0fdabf 100644 --- a/SPECS/kernel-azure/config_aarch64 +++ b/SPECS/kernel-azure/config_aarch64 @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 5.15.137.1 Kernel Configuration +# Linux/arm64 5.15.138.1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 11.2.0" CONFIG_CC_IS_GCC=y diff --git a/SPECS/kernel-azure/kernel-azure.signatures.json b/SPECS/kernel-azure/kernel-azure.signatures.json index c52d1ada0ef..fe21a936732 100644 --- a/SPECS/kernel-azure/kernel-azure.signatures.json +++ b/SPECS/kernel-azure/kernel-azure.signatures.json @@ -1,9 +1,9 @@ { "Signatures": { "cbl-mariner-ca-20211013.pem": "5ef124b0924cb1047c111a0ecff1ae11e6ad7cac8d1d9b40f98f99334121f0b0", - "config": "f363acd6ddc040dbbb4b0902d004681078fafc4c37bd936b0a33d2c739972b20", - "config_aarch64": "56fff258048924f838958c9d9036206a543f6bf0a677281e5ad97ec8611f09a5", + "config": "b37e0060445880e68a45f77dc368ba89aed58d34def7a7c01ef853e79d74a00e", + "config_aarch64": "92acf9344a963a9e64b2a909d5fc674641333dc43a312159977aacc2f63ec13d", "sha512hmac-openssl.sh": "02ab91329c4be09ee66d759e4d23ac875037c3b56e5a598e32fd1206da06a27f", - "kernel-5.15.137.1.tar.gz": "c00abd18daa5fcdf732d88bed57eb26a247473888c8aa9003897baa15d6c0e58" + "kernel-5.15.138.1.tar.gz": "7b5e3a24102b00889e44094e0e47e0901600ba9d3a0197e75e39173ddf760d4c" } } \ No newline at end of file diff --git a/SPECS/kernel-azure/kernel-azure.spec b/SPECS/kernel-azure/kernel-azure.spec index 29d04d0badb..f5e37006164 100644 --- a/SPECS/kernel-azure/kernel-azure.spec +++ b/SPECS/kernel-azure/kernel-azure.spec @@ -27,7 +27,7 @@ Summary: Linux Kernel Name: kernel-azure -Version: 5.15.137.1 +Version: 5.15.138.1 Release: 1%{?dist} License: GPLv2 Vendor: Microsoft Corporation @@ -42,6 +42,7 @@ Source4: cbl-mariner-ca-20211013.pem BuildRequires: audit-devel BuildRequires: bash BuildRequires: bc +BuildRequires: cpio BuildRequires: diffutils BuildRequires: dwarves BuildRequires: elfutils-libelf-devel @@ -419,6 +420,12 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %{_sysconfdir}/bash_completion.d/bpftool %changelog +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Add missing BuildRequires cpio + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS/kernel-hci/config b/SPECS/kernel-hci/config index 89f0570d5a9..e2aaedb4486 100644 --- a/SPECS/kernel-hci/config +++ b/SPECS/kernel-hci/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 5.15.137.1 Kernel Configuration +# Linux/x86_64 5.15.138.1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 11.2.0" CONFIG_CC_IS_GCC=y diff --git a/SPECS/kernel-hci/kernel-hci.signatures.json b/SPECS/kernel-hci/kernel-hci.signatures.json index 18b5db62a27..09f977c838e 100644 --- a/SPECS/kernel-hci/kernel-hci.signatures.json +++ b/SPECS/kernel-hci/kernel-hci.signatures.json @@ -1,7 +1,7 @@ { "Signatures": { "cbl-mariner-ca-20211013.pem": "5ef124b0924cb1047c111a0ecff1ae11e6ad7cac8d1d9b40f98f99334121f0b0", - "config": "9b9b0d3fa3d597539db6c7734ab294d6eae06cc276125a8aaa95fd15a81d91c6", - "kernel-5.15.137.1.tar.gz": "c00abd18daa5fcdf732d88bed57eb26a247473888c8aa9003897baa15d6c0e58" + "config": "aa0a098e447e6a1a9c7466ca7774f510fb1b1d6f9a6b70d2fa84e98f1fe3a859", + "kernel-5.15.138.1.tar.gz": "7b5e3a24102b00889e44094e0e47e0901600ba9d3a0197e75e39173ddf760d4c" } } \ No newline at end of file diff --git a/SPECS/kernel-hci/kernel-hci.spec b/SPECS/kernel-hci/kernel-hci.spec index faffb982aa7..783a0475998 100644 --- a/SPECS/kernel-hci/kernel-hci.spec +++ b/SPECS/kernel-hci/kernel-hci.spec @@ -17,7 +17,7 @@ %define config_source %{SOURCE1} Summary: Linux Kernel for HCI Name: kernel-hci -Version: 5.15.137.1 +Version: 5.15.138.1 Release: 1%{?dist} License: GPLv2 Vendor: Microsoft Corporation @@ -58,6 +58,7 @@ Patch27: 0028-net-mlx5-Bridge-use-debug-not-warn-if-entry-not-found.patch BuildRequires: audit-devel BuildRequires: bash BuildRequires: bc +BuildRequires: cpio BuildRequires: diffutils BuildRequires: dwarves BuildRequires: elfutils-libelf-devel @@ -434,6 +435,12 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %{_sysconfdir}/bash_completion.d/bpftool %changelog +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Add missing BuildRequires cpio + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS/kernel-headers/kernel-headers.signatures.json b/SPECS/kernel-headers/kernel-headers.signatures.json index 6b024a593eb..276c3239f8e 100644 --- a/SPECS/kernel-headers/kernel-headers.signatures.json +++ b/SPECS/kernel-headers/kernel-headers.signatures.json @@ -1,5 +1,5 @@ { "Signatures": { - "kernel-5.15.137.1.tar.gz": "c00abd18daa5fcdf732d88bed57eb26a247473888c8aa9003897baa15d6c0e58" + "kernel-5.15.138.1.tar.gz": "7b5e3a24102b00889e44094e0e47e0901600ba9d3a0197e75e39173ddf760d4c" } } \ No newline at end of file diff --git a/SPECS/kernel-headers/kernel-headers.spec b/SPECS/kernel-headers/kernel-headers.spec index a4c3a7c7518..962a60b3c94 100644 --- a/SPECS/kernel-headers/kernel-headers.spec +++ b/SPECS/kernel-headers/kernel-headers.spec @@ -1,7 +1,7 @@ Summary: Linux API header files Name: kernel-headers -Version: 5.15.137.1 -Release: 1%{?dist} +Version: 5.15.138.1 +Release: 4%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -36,6 +36,21 @@ cp -rv usr/include/* /%{buildroot}%{_includedir} %{_includedir}/* %changelog +* Tue Nov 28 2023 Juan Camposeco - 5.15.138.1-4 +- Bump release to match kernel + +* Tue Nov 28 2023 Thien Trung Vuong - 5.15.138.1-3 +- Bump release to match kernel + +* Wed Nov 22 2023 David Daney - 5.15.138.1-2 +- Bump release to match kernel + +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Bump release to match kernel + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 diff --git a/SPECS/kernel-mshv/kernel-mshv.spec b/SPECS/kernel-mshv/kernel-mshv.spec index c8e623d5a1b..54d391a40a6 100644 --- a/SPECS/kernel-mshv/kernel-mshv.spec +++ b/SPECS/kernel-mshv/kernel-mshv.spec @@ -11,7 +11,7 @@ Summary: Mariner kernel that has MSHV Host support Name: kernel-mshv Version: 5.15.126.mshv9 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2 Group: Development/Tools Vendor: Microsoft Corporation @@ -24,6 +24,7 @@ ExclusiveArch: x86_64 BuildRequires: audit-devel BuildRequires: bash BuildRequires: bc +BuildRequires: cpio BuildRequires: diffutils BuildRequires: dwarves BuildRequires: elfutils-libelf-devel @@ -247,6 +248,9 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner-mshv.cfg %{_includedir}/perf/perf_dlfilter.h %changelog +* Mon Nov 20 2023 Rachel Menge - 5.15.126.mshv9-2 +- Add cpio as BuildRequires + * Mon Nov 6 2023 Dallas Delaney - 5.15.126.mshv9-1 - Update to v5.15.126.mshv9 diff --git a/SPECS/kernel-uvm-cvm/kernel-uvm-cvm.spec b/SPECS/kernel-uvm-cvm/kernel-uvm-cvm.spec index f9a1d4f2592..dd8717030fa 100644 --- a/SPECS/kernel-uvm-cvm/kernel-uvm-cvm.spec +++ b/SPECS/kernel-uvm-cvm/kernel-uvm-cvm.spec @@ -11,7 +11,7 @@ Summary: Linux Kernel for SEV SNP enabled Kata UVMs Name: kernel-uvm-cvm Version: 6.1.0.mshv14 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -21,6 +21,7 @@ Source1: config BuildRequires: audit-devel BuildRequires: bash BuildRequires: bc +BuildRequires: cpio BuildRequires: diffutils BuildRequires: dwarves BuildRequires: elfutils-libelf-devel @@ -153,6 +154,9 @@ find %{buildroot}/lib/modules -name '*.ko' -exec chmod u+x {} + %{_prefix}/src/linux-headers-%{uname_r} %changelog +* Mon Nov 20 2023 Rachel Menge - 6.1.0.mshv14-2 +- Add cpio as BuildRequires + * Mon Nov 6 2023 Dallas Delaney - 6.1.0.mshv14-1 - Update to v6.1.0.mshv14 diff --git a/SPECS/kernel-uvm/kernel-uvm.spec b/SPECS/kernel-uvm/kernel-uvm.spec index 75ce17ae25d..26c49edef65 100644 --- a/SPECS/kernel-uvm/kernel-uvm.spec +++ b/SPECS/kernel-uvm/kernel-uvm.spec @@ -11,7 +11,7 @@ Summary: Linux Kernel for Kata UVM Name: kernel-uvm Version: 6.1.0.mshv14 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -21,6 +21,7 @@ Source1: config BuildRequires: audit-devel BuildRequires: bash BuildRequires: bc +BuildRequires: cpio BuildRequires: diffutils BuildRequires: dwarves BuildRequires: elfutils-libelf-devel @@ -153,6 +154,9 @@ find %{buildroot}/lib/modules -name '*.ko' -exec chmod u+x {} + %{_prefix}/src/linux-headers-%{uname_r} %changelog +* Mon Nov 20 2023 Rachel Menge - 6.1.0.mshv14-2 +- Add cpio as BuildRequires + * Mon Nov 6 2023 Dallas Delaney - 6.1.0.mshv14-1 - Update to v6.1.0.mshv14 diff --git a/SPECS/kernel/CVE-2023-39198.nopatch b/SPECS/kernel/CVE-2023-39198.nopatch new file mode 100644 index 00000000000..ef102c6ca43 --- /dev/null +++ b/SPECS/kernel/CVE-2023-39198.nopatch @@ -0,0 +1,2 @@ +CVE-2023-39198 - patched in 5.15.128.1 +upstream commit ID c611589b4259ed63b9b77be6872b1ce07ec0ac16 -> stable commit ID d578c919deb786b4d6ba8c7639255cb658731671 diff --git a/SPECS/kernel/CVE-2023-5178.nopatch b/SPECS/kernel/CVE-2023-5178.nopatch new file mode 100644 index 00000000000..119f1b91af7 --- /dev/null +++ b/SPECS/kernel/CVE-2023-5178.nopatch @@ -0,0 +1,3 @@ +CVE-2023-5178 - patched in 5.15.137.1 +upstream: d920abd1e7c4884f9ecd0749d1921b7ab19ddfbd +stable: 34f62612be2a7f90ab68a14154db6664a32f8db0 \ No newline at end of file diff --git a/SPECS/kernel/config b/SPECS/kernel/config index 1ce23283bc1..74be064d056 100644 --- a/SPECS/kernel/config +++ b/SPECS/kernel/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 5.15.137.1 Kernel Configuration +# Linux/x86_64 5.15.138.1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 11.2.0" CONFIG_CC_IS_GCC=y @@ -116,7 +116,7 @@ CONFIG_BPF_JIT_ALWAYS_ON=y CONFIG_BPF_JIT_DEFAULT_ON=y CONFIG_BPF_UNPRIV_DEFAULT_OFF=y # CONFIG_BPF_PRELOAD is not set -# CONFIG_BPF_LSM is not set +CONFIG_BPF_LSM=y # end of BPF subsystem CONFIG_PREEMPT_NONE=y @@ -6541,7 +6541,7 @@ CONFIG_QUOTACTL=y CONFIG_AUTOFS4_FS=m CONFIG_AUTOFS_FS=m CONFIG_FUSE_FS=m -# CONFIG_CUSE is not set +CONFIG_CUSE=m CONFIG_VIRTIO_FS=m CONFIG_FUSE_DAX=y CONFIG_OVERLAY_FS=m diff --git a/SPECS/kernel/config_aarch64 b/SPECS/kernel/config_aarch64 index e093d43607f..9ef9ca49bb3 100644 --- a/SPECS/kernel/config_aarch64 +++ b/SPECS/kernel/config_aarch64 @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 5.15.137.1 Kernel Configuration +# Linux/arm64 5.15.138.1 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 11.2.0" CONFIG_CC_IS_GCC=y @@ -98,7 +98,7 @@ CONFIG_BPF_JIT_ALWAYS_ON=y CONFIG_BPF_JIT_DEFAULT_ON=y CONFIG_BPF_UNPRIV_DEFAULT_OFF=y # CONFIG_BPF_PRELOAD is not set -# CONFIG_BPF_LSM is not set +CONFIG_BPF_LSM=y # end of BPF subsystem CONFIG_PREEMPT_NONE=y @@ -8147,6 +8147,7 @@ CONFIG_SUN6I_MSGBOX=y # CONFIG_SPRD_MBOX is not set # CONFIG_QCOM_IPCC is not set CONFIG_IOMMU_IOVA=y +CONFIG_IOASID=y CONFIG_IOMMU_API=y CONFIG_IOMMU_SUPPORT=y @@ -8165,12 +8166,17 @@ CONFIG_IOMMU_DEFAULT_DMA_STRICT=y # CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set CONFIG_OF_IOMMU=y CONFIG_IOMMU_DMA=y +CONFIG_IOMMU_SVA_LIB=y # CONFIG_ROCKCHIP_IOMMU is not set # CONFIG_SUN50I_IOMMU is not set # CONFIG_TEGRA_IOMMU_SMMU is not set # CONFIG_IPMMU_VMSA is not set -# CONFIG_ARM_SMMU is not set -# CONFIG_ARM_SMMU_V3 is not set +CONFIG_ARM_SMMU=y +# CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS is not set +CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT=y +CONFIG_ARM_SMMU_QCOM=y +CONFIG_ARM_SMMU_V3=y +CONFIG_ARM_SMMU_V3_SVA=y # CONFIG_MTK_IOMMU is not set # CONFIG_QCOM_IOMMU is not set # CONFIG_VIRTIO_IOMMU is not set @@ -8687,7 +8693,7 @@ CONFIG_QUOTACTL=y CONFIG_AUTOFS4_FS=y CONFIG_AUTOFS_FS=y CONFIG_FUSE_FS=m -# CONFIG_CUSE is not set +CONFIG_CUSE=m CONFIG_VIRTIO_FS=m CONFIG_FUSE_DAX=y CONFIG_OVERLAY_FS=m diff --git a/SPECS/kernel/kernel.signatures.json b/SPECS/kernel/kernel.signatures.json index 467157a0ab1..6cceb94488e 100644 --- a/SPECS/kernel/kernel.signatures.json +++ b/SPECS/kernel/kernel.signatures.json @@ -1,9 +1,9 @@ { "Signatures": { "cbl-mariner-ca-20211013.pem": "5ef124b0924cb1047c111a0ecff1ae11e6ad7cac8d1d9b40f98f99334121f0b0", - "config": "f529b9e9ad21c4f26edc849658bf38de43736901d8f3aabc9f3be2f0dc37497e", - "config_aarch64": "00728640d6c8bbe24667e0f63059a9bfef523962805648860e0d2e22e7fe0079", + "config": "a43681f3c9554f43c3d87f71fb6bd8e9152baa8ef9f0622df59592e6677f1ae6", + "config_aarch64": "187ce1fe40787cc12bd37e2270ebd5b096386a8a353993261c389b4243aabb83", "sha512hmac-openssl.sh": "02ab91329c4be09ee66d759e4d23ac875037c3b56e5a598e32fd1206da06a27f", - "kernel-5.15.137.1.tar.gz": "c00abd18daa5fcdf732d88bed57eb26a247473888c8aa9003897baa15d6c0e58" + "kernel-5.15.138.1.tar.gz": "7b5e3a24102b00889e44094e0e47e0901600ba9d3a0197e75e39173ddf760d4c" } -} \ No newline at end of file +} diff --git a/SPECS/kernel/kernel.spec b/SPECS/kernel/kernel.spec index abbd2d02f43..025762f5a1b 100644 --- a/SPECS/kernel/kernel.spec +++ b/SPECS/kernel/kernel.spec @@ -3,9 +3,9 @@ %define uname_r %{version}-%{release} # find_debuginfo.sh arguments are set by default in rpm's macros. -# The default arguments regenerate the build-id for vmlinux in the +# The default arguments regenerate the build-id for vmlinux in the # debuginfo package causing a mismatch with the build-id for vmlinuz in -# the kernel package. Therefore, explicilty set the relevant default +# the kernel package. Therefore, explicilty set the relevant default # settings to prevent this behavior. %undefine _unique_build_ids %undefine _unique_debug_names @@ -27,8 +27,8 @@ Summary: Linux Kernel Name: kernel -Version: 5.15.137.1 -Release: 1%{?dist} +Version: 5.15.138.1 +Release: 4%{?dist} License: GPLv2 Vendor: Microsoft Corporation Distribution: Mariner @@ -44,6 +44,7 @@ BuildRequires: audit-devel BuildRequires: bash BuildRequires: bc BuildRequires: build-essential +BuildRequires: cpio BuildRequires: diffutils BuildRequires: dwarves BuildRequires: elfutils-libelf-devel @@ -425,6 +426,21 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %{_sysconfdir}/bash_completion.d/bpftool %changelog +* Tue Nov 28 2023 Juan Camposeco - 5.15.138.1-4 +- Enable CUSE module + +* Tue Nov 28 2023 Thien Trung Vuong - 5.15.138.1-3 +- Enable CONFIG_BPF_LSM + +* Wed Nov 22 2023 David Daney - 5.15.138.1-2 +- Add IOMMU configs for aarch64 + +* Tue Nov 21 2023 CBL-Mariner Servicing Account - 5.15.138.1-1 +- Auto-upgrade to 5.15.138.1 + +* Mon Nov 20 2023 Rachel Menge - 5.15.137.1-2 +- Add missing BuildRequires cpio + * Mon Nov 06 2023 CBL-Mariner Servicing Account - 5.15.137.1-1 - Auto-upgrade to 5.15.137.1 @@ -439,8 +455,8 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg - Remove CONFIG_NET_CLS_RSVP and CONFIG_NET_CLS_RSVP6 that don't apply to the new version * Thu Sep 21 2023 Cameron Baird - 5.15.131.1-3 -- Call grub2-mkconfig to regenerate configs only if the user has - previously used grub2-mkconfig for boot configuration. +- Call grub2-mkconfig to regenerate configs only if the user has + previously used grub2-mkconfig for boot configuration. * Wed Sep 20 2023 Jon Slobodzian - 5.15.131.1-2 - Recompile with stack-protection fixed gcc version (CVE-2023-4039) diff --git a/SPECS/kube-vip-cloud-provider/kube-vip-cloud-provider.spec b/SPECS/kube-vip-cloud-provider/kube-vip-cloud-provider.spec index 3967d66fcd3..b1769da2738 100644 --- a/SPECS/kube-vip-cloud-provider/kube-vip-cloud-provider.spec +++ b/SPECS/kube-vip-cloud-provider/kube-vip-cloud-provider.spec @@ -43,7 +43,7 @@ install kube-vip-cloud-provider %{buildroot}%{_bindir}/kube-vip-cloud-provider %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.0.2-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.0.2-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/kubernetes/kubernetes.spec b/SPECS/kubernetes/kubernetes.spec index 16c6715b8b5..48024df9918 100644 --- a/SPECS/kubernetes/kubernetes.spec +++ b/SPECS/kubernetes/kubernetes.spec @@ -10,7 +10,7 @@ Summary: Microsoft Kubernetes Name: kubernetes Version: 1.28.3 -Release: 1%{?dist} +Release: 2%{?dist} License: ASL 2.0 Vendor: Microsoft Corporation Distribution: Mariner @@ -93,9 +93,10 @@ Pause component for Microsoft Kubernetes %{version}. %setup -q -c -n %{name} %build -# set version information using version file +# set version information using KUBE_GIT_VERSION # (see k8s code: hack/lib/version.sh for more detail) -export KUBE_GIT_VERSION_FILE=%{_builddir}/%{name}/version-file.sh +export KUBE_GIT_TREE_STATE=archive +export KUBE_GIT_VERSION=v%{version} # build host and container image related components echo "+++ build kubernetes components" @@ -262,6 +263,9 @@ fi %{_exec_prefix}/local/bin/pause %changelog +* Fri Nov 10 2023 Muhammad Falak - 1.28.3-2 +- Fix version subcommand for components + * Mon Oct 23 2023 Nicolas Guibourge - 1.28.3-1 - Upgrade to 1.28.3 to address CVE-2023-44487 and CVE-2023-39325. diff --git a/SPECS/kubevirt/kubevirt.spec b/SPECS/kubevirt/kubevirt.spec index 39765806e05..c3b938fe3e9 100644 --- a/SPECS/kubevirt/kubevirt.spec +++ b/SPECS/kubevirt/kubevirt.spec @@ -215,7 +215,7 @@ install -p -m 0644 cmd/virt-handler/nsswitch.conf %{buildroot}%{_datadir}/kube-v - Bump release to rebuild against glibc 2.35-6 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.59.0-10 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.59.0-9 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/kured/kured.spec b/SPECS/kured/kured.spec index 3274967c133..f942bd1f744 100644 --- a/SPECS/kured/kured.spec +++ b/SPECS/kured/kured.spec @@ -126,7 +126,7 @@ sed -i -e 's|image: .*|image: registry.opensuse.org/kubic/kured:%{version}|g' %{ - Upgrade to 1.13.2 for vendored go CVEs * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.9.1-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.9.1-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/libnvidia-container/libnvidia-container.spec b/SPECS/libnvidia-container/libnvidia-container.spec index b3814c802a2..fb5bad6a065 100644 --- a/SPECS/libnvidia-container/libnvidia-container.spec +++ b/SPECS/libnvidia-container/libnvidia-container.spec @@ -133,7 +133,7 @@ This package contains command-line tools that facilitate using the library. %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.13.5-4 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.13.5-3 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/linuxptp/clknetsim-phc2sys.patch b/SPECS/linuxptp/clknetsim-phc2sys.patch new file mode 100644 index 00000000000..1d2b6491052 --- /dev/null +++ b/SPECS/linuxptp/clknetsim-phc2sys.patch @@ -0,0 +1,22 @@ +commit 2c62b9a3d8aa61bbb45a522c47be1ff2261e9b0e +Author: Miroslav Lichvar +Date: Mon Mar 14 11:40:50 2022 +0100 + + bash: remove default options for phc2sys + + Don't set any options by default for phc2sys to avoid conflict between + -O and -a. + +diff --git a/clknetsim.bash b/clknetsim.bash +index becc94d..eed622c 100644 +--- a/clknetsim.bash ++++ b/clknetsim.bash +@@ -82,7 +82,7 @@ start_client() { + args+=($opts) + ;; + phc2sys) +- args=(-s /dev/ptp0 -O 0 $opts $config) ++ args=($opts $config) + ;; + nsm) + args=($opts) diff --git a/SPECS/linuxptp/enable-ha.patch b/SPECS/linuxptp/enable-ha.patch new file mode 100644 index 00000000000..740eec62948 --- /dev/null +++ b/SPECS/linuxptp/enable-ha.patch @@ -0,0 +1,11615 @@ +From 63b43924294da6cb177d0509120b2e957580441c Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:52 +0200 +Subject: [PATCH 1/47] clock: Reset state when switching port with same best clock. + +When the best port is changed, but the ID of the best clock doesn't +change (e.g. a passive port is activated on link failure), reset the +current delay and other master/link-specific state to avoid the switch +throwing the clock off. + +Reviewed-by: Jacob Keller +Signed-off-by: Miroslav Lichvar +[commit 7e8eba5332671abfd95d06dd191059eded1d2cca upstream] +Signed-off-by: Jim Somerville +--- + clock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/clock.c b/clock.c +index a66d189..96453f4 100644 +--- a/clock.c ++++ b/clock.c +@@ -1857,7 +1857,7 @@ static void handle_state_decision_event(struct clock *c) + cid2str(&best_id)); + } + +- if (!cid_eq(&best_id, &c->best_id)) { ++ if (!cid_eq(&best_id, &c->best_id) || best != c->best) { + clock_freq_est_reset(c); + tsproc_reset(c->tsproc, 1); + if (!tmv_is_zero(c->initial_delay)) +-- +2.25.1 + +From 1779482f39e6513995b13fdbd350f7aee8495b7e Mon Sep 17 00:00:00 2001 +Message-Id: <1779482f39e6513995b13fdbd350f7aee8495b7e.1630418391.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:53 +0200 +Subject: [PATCH 2/47] clock: Reset clock check on best clock/port change. + +Reset the clock check when the best clock or port changes, together with +the other state like current estimated delay and frequency. This avoids +false positives if the clock is controlled by an external process when +not synchronized by PTP (e.g. phc2sys -rr). + +Reviewed-by: Jacob Keller +Signed-off-by: Miroslav Lichvar +[commit 262a49b07eaccc0f0237e3cd4df01b185b8f664f upstream] +Signed-off-by: Jim Somerville +--- + clock.c | 2 ++ + clockcheck.c | 9 ++++++++- + clockcheck.h | 6 ++++++ + 3 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/clock.c b/clock.c +index d955710..49bd4a9 100644 +--- a/clock.c ++++ b/clock.c +@@ -1911,6 +1911,8 @@ static void handle_state_decision_event(struct clock *c) + + if (!cid_eq(&best_id, &c->best_id) || best != c->best) { + clock_freq_est_reset(c); ++ if (c->sanity_check) ++ clockcheck_reset(c->sanity_check); + tsproc_reset(c->tsproc, 1); + if (!tmv_is_zero(c->initial_delay)) + tsproc_set_delay(c->tsproc, c->initial_delay); +diff --git a/clockcheck.c b/clockcheck.c +index d48a578..d0b4714 100644 +--- a/clockcheck.c ++++ b/clockcheck.c +@@ -47,9 +47,16 @@ struct clockcheck *clockcheck_create(int freq_limit) + if (!cc) + return NULL; + cc->freq_limit = freq_limit; ++ clockcheck_reset(cc); ++ return cc; ++} ++ ++void clockcheck_reset(struct clockcheck *cc) ++{ ++ cc->freq_known = 0; + cc->max_freq = -CHECK_MAX_FREQ; + cc->min_freq = CHECK_MAX_FREQ; +- return cc; ++ cc->last_ts = 0; + } + + int clockcheck_sample(struct clockcheck *cc, uint64_t ts) +diff --git a/clockcheck.h b/clockcheck.h +index 78aca48..1ff86eb 100644 +--- a/clockcheck.h ++++ b/clockcheck.h +@@ -33,6 +33,12 @@ struct clockcheck; + */ + struct clockcheck *clockcheck_create(int freq_limit); + ++/** ++ * Reset a clock check. ++ * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). ++ */ ++void clockcheck_reset(struct clockcheck *cc); ++ + /** + * Perform the sanity check on a time stamp. + * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). +-- +2.29.2 + +From a1ed560a712d611edf8b47756bc56542a57bff7d Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:54 +0200 +Subject: [PATCH 3/47] port: Don't check timestamps from non-slave ports. + +Don't perform the sanity check on receive timestamps from ports in +non-slave states to avoid false positives in the jbod mode, where +the timestamps can be generated by different clocks. + +Reviewed-by: Jacob Keller +Signed-off-by: Miroslav Lichvar +[commit e117e37e379556fa23337db2518bb44d8793e039 upstream] +Signed-off-by: Jim Somerville +--- + port.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/port.c b/port.c +index 9e9d484..387d5a2 100644 +--- a/port.c ++++ b/port.c +@@ -2731,7 +2731,10 @@ static enum fsm_event bc_event(struct port *p, int fd_index) + } + if (msg_sots_valid(msg)) { + ts_add(&msg->hwts.ts, -p->rx_timestamp_offset); +- clock_check_ts(p->clock, tmv_to_nanoseconds(msg->hwts.ts)); ++ if (p->state == PS_SLAVE) { ++ clock_check_ts(p->clock, ++ tmv_to_nanoseconds(msg->hwts.ts)); ++ } + } + + switch (msg_type(msg)) { +-- +2.29.2 + +From 5caa4d0a9161e6a33e269c8e445b322e4437e6b3 Mon Sep 17 00:00:00 2001 +Message-Id: <5caa4d0a9161e6a33e269c8e445b322e4437e6b3.1630418391.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:55 +0200 +Subject: [PATCH 4/47] port: Don't renew raw transport. + +Renewing of the transport on announce/sync timeout is needed in the +client-only mode to avoid getting stuck with a broken multicast socket +when the link goes down. + +This shouldn't be necessary with the raw transport. Closing and binding +of raw sockets can apparently be so slow that it triggers a false +positive in the clock check. + +Reported-by: Amar Subramanyam +Signed-off-by: Miroslav Lichvar +Reviewed-by: Jacob Keller +[commit 6df84259647757bc53818a039734f8ff85618c02 upstream] +Signed-off-by: Jim Somerville +--- + port.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/port.c b/port.c +index 387d5a2..d26b87f 100644 +--- a/port.c ++++ b/port.c +@@ -1805,6 +1805,12 @@ static int port_renew_transport(struct port *p) + if (!port_is_enabled(p)) { + return 0; + } ++ ++ /* Closing and binding of raw sockets is too slow and unnecessary */ ++ if (transport_type(p->trp) == TRANS_IEEE_802_3) { ++ return 0; ++ } ++ + transport_close(p->trp, &p->fda); + port_clear_fda(p, FD_FIRST_TIMER); + res = transport_open(p->trp, p->iface, &p->fda, p->timestamping); +-- +2.29.2 + +From 3bf4f1784fa0a03a252961f400a78d963773f8f5 Mon Sep 17 00:00:00 2001 +Message-Id: <3bf4f1784fa0a03a252961f400a78d963773f8f5.1630611367.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630611367.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630611367.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:56 +0200 +Subject: [PATCH 5/47] clockcheck: Increase minimum interval. + +Increase the minimum check interval to 1 second to measure the frequency +offset more accurately and with default configuration make false +positives less likely due to a heavily overloaded system. + +Signed-off-by: Miroslav Lichvar +Reviewed-by: Jacob Keller +[commit a082bcd700e4955ebaa00d7039bf4bce92048ac4 upstream] +Signed-off-by: Jim Somerville +--- + clockcheck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/clockcheck.c b/clockcheck.c +index d0b4714..f0141be 100644 +--- a/clockcheck.c ++++ b/clockcheck.c +@@ -23,7 +23,7 @@ + #include "clockcheck.h" + #include "print.h" + +-#define CHECK_MIN_INTERVAL 100000000 ++#define CHECK_MIN_INTERVAL 1000000000 + #define CHECK_MAX_FREQ 900000000 + + struct clockcheck { +-- +2.29.2 + +From 3a6de7b6208ccc64a20474db15abaac08e99d10b Mon Sep 17 00:00:00 2001 +Message-Id: <3a6de7b6208ccc64a20474db15abaac08e99d10b.1630418391.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Cole Walker +Date: Wed, 23 Jun 2021 11:14:41 -0400 +Subject: [PATCH 6/47] Add option to disable default port selection in phc2sys + +This change serves to address an issue in phc2sys +where the local ptp clocks are not synced together properly if the local +time is far behind the reference time. This issue occurs when phc2sys +starts and there is no client port currently synced to a grandmaster. In +the original behaviour, phc2sys selects the first configured port and +proceeds to sync all of the other clocks to it by performing the +first_step operation. + +Then ptp4l will evenually lock to the Grandmaster clock, and that +single port will have its time updated to the correct value, but +phc2sys has already performed the first_step operation and will not +step the other clocks again. + +This solution provides an option to disable the selection of a +default port by phc2sys. When no default port is selected, phc2sys waits +for ptp4l to sync to the Grandmaster before bringing the other clocks +into sync with the first_step operation. + +This option is configured via the default_sync +parameter or the -D flag. The default_sync parameter is set to on by +default in order to keep the behaviour the same as upstream linuxptp +but can be configured by users via +system service-parameter-add ptp global default_sync=0 + +Signed-off-by: Jim Somerville +--- + config.c | 1 + + phc2sys.c | 15 ++++++++++++--- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/config.c b/config.c +index ef5e833..cab7e4f 100644 +--- a/config.c ++++ b/config.c +@@ -333,6 +333,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("utc_offset", CURRENT_UTC_OFFSET, 0, INT_MAX), + GLOB_ITEM_INT("verbose", 0, 0, 1), + GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), ++ GLOB_ITEM_INT("default_sync", 1, 0, 1), + }; + + static struct unicast_master_table *current_uc_mtab; +diff --git a/phc2sys.c b/phc2sys.c +index a36cbe0..44d6872 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -120,6 +120,7 @@ struct phc2sys_private { + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; + struct clock *master; ++ int default_sync; + }; + + static struct config *phc2sys_config; +@@ -437,7 +438,7 @@ static void reconfigure(struct phc2sys_private *priv) + } + last = c; + } +- if (dst_cnt > 1 && !src) { ++ if (dst_cnt > 1 && !src && priv->default_sync) { + if (!rt || rt->dest_only) { + priv->master = last; + /* Reset to original state in next reconfiguration. */ +@@ -1344,6 +1345,7 @@ static void usage(char *progname) + " -N [num] number of master clock readings per update (5)\n" + " -L [limit] sanity frequency limit in ppb (200000000)\n" + " -M [num] NTP SHM segment number (0)\n" ++ " -D [num] fall back to default clock in automatic mode (1)\n" + " -u [num] number of clock updates in summary stats (0)\n" + " -n [num] domain number (0)\n" + " -x apply leap seconds by servo instead of kernel\n" +@@ -1364,7 +1366,7 @@ int main(int argc, char *argv[]) + struct clock *src, *dst; + struct config *cfg; + struct option *opts; +- int autocfg = 0, c, domain_number = 0, index, ntpshm_segment; ++ int autocfg = 0, c, domain_number = 0, default_sync = 1, index, ntpshm_segment; + int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0, wait_sync = 0; + double phc_rate, tmp; + struct phc2sys_private priv = { +@@ -1388,7 +1390,7 @@ int main(int argc, char *argv[]) + progname = strrchr(argv[0], '/'); + progname = progname ? 1+progname : argv[0]; + while (EOF != (c = getopt_long(argc, argv, +- "arc:d:f:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:t:mqvh", ++ "arc:d:f:s:E:P:I:S:F:R:N:O:L:M:D:i:u:wn:xz:l:t:mqvh", + opts, &index))) { + switch (c) { + case 0: +@@ -1540,6 +1542,12 @@ int main(int argc, char *argv[]) + version_show(stdout); + config_destroy(cfg); + return 0; ++ case 'D': ++ if (get_arg_val_i(c, optarg, &default_sync, 0, 1) || ++ config_set_int(cfg, "default_sync", default_sync)) { ++ goto end; ++ } ++ break; + case 'h': + usage(progname); + config_destroy(cfg); +@@ -1588,6 +1596,7 @@ int main(int argc, char *argv[]) + } + priv.kernel_leap = config_get_int(cfg, NULL, "kernel_leap"); + priv.sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit"); ++ priv.default_sync = config_get_int(cfg, NULL, "default_sync"); + + if (autocfg) { + if (init_pmc(cfg, &priv)) +-- +2.29.2 + +From 6428c2628c013c408ec09355ad37eb12fa6bb20f Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:35 +0200 +Subject: [PATCH 7/47] sysoff: Change sysoff_measure() to return errno. + +Return -errno from failed ioctl instead of the SYSOFF_* enum from the +measurement functions to allow the callers to check for specific errors. + +Signed-off-by: Miroslav Lichvar +[commit 7824b13db9533ddebe37cf444d7aaa5d235575d3 upstream] +Signed-off-by: Douglas Henrique Koerich +--- + sysoff.c | 15 ++++++++------- + sysoff.h | 2 +- + 2 files changed, 9 insertions(+), 8 deletions(-) + +diff --git a/sysoff.c b/sysoff.c +index 2743859..5d3b907 100644 +--- a/sysoff.c ++++ b/sysoff.c +@@ -17,6 +17,7 @@ + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ ++#include + #include + #include + #include +@@ -38,11 +39,11 @@ static int sysoff_precise(int fd, int64_t *result, uint64_t *ts) + memset(&pso, 0, sizeof(pso)); + if (ioctl(fd, PTP_SYS_OFFSET_PRECISE, &pso)) { + pr_debug("ioctl PTP_SYS_OFFSET_PRECISE: %m"); +- return SYSOFF_RUN_TIME_MISSING; ++ return -errno; + } + *result = pctns(&pso.sys_realtime) - pctns(&pso.device); + *ts = pctns(&pso.sys_realtime); +- return SYSOFF_PRECISE; ++ return 0; + } + + static int64_t sysoff_estimate(struct ptp_clock_time *pct, int extended, +@@ -98,10 +99,10 @@ static int sysoff_extended(int fd, int n_samples, + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED, &pso)) { + pr_debug("ioctl PTP_SYS_OFFSET_EXTENDED: %m"); +- return SYSOFF_RUN_TIME_MISSING; ++ return -errno; + } + *result = sysoff_estimate(&pso.ts[0][0], 1, n_samples, ts, delay); +- return SYSOFF_EXTENDED; ++ return 0; + } + + static int sysoff_basic(int fd, int n_samples, +@@ -112,10 +113,10 @@ static int sysoff_basic(int fd, int n_samples, + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET, &pso)) { + perror("ioctl PTP_SYS_OFFSET"); +- return SYSOFF_RUN_TIME_MISSING; ++ return -errno; + } + *result = sysoff_estimate(pso.ts, 0, n_samples, ts, delay); +- return SYSOFF_BASIC; ++ return 0; + } + + int sysoff_measure(int fd, int method, int n_samples, +@@ -130,7 +131,7 @@ int sysoff_measure(int fd, int method, int n_samples, + case SYSOFF_BASIC: + return sysoff_basic(fd, n_samples, result, ts, delay); + } +- return SYSOFF_RUN_TIME_MISSING; ++ return -EOPNOTSUPP; + } + + int sysoff_probe(int fd, int n_samples) +diff --git a/sysoff.h b/sysoff.h +index e4de919..5480f8f 100644 +--- a/sysoff.h ++++ b/sysoff.h +@@ -44,7 +44,7 @@ int sysoff_probe(int fd, int n_samples); + * @param result The estimated offset in nanoseconds. + * @param ts The system time corresponding to the 'result'. + * @param delay The delay in reading of the clock in nanoseconds. +- * @return One of the SYSOFF_ enumeration values. ++ * @return Zero on success, negative error code otherwise. + */ + int sysoff_measure(int fd, int method, int n_samples, + int64_t *result, uint64_t *ts, int64_t *delay); +-- +2.29.2 + +From 38a530d94fc5aa73bde424d05e2e38348e64d7e5 Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:36 +0200 +Subject: [PATCH 8/47] sysoff: Change log level of ioctl error messages. + +Change the log level of ioctl error messages to the error level to make +them visible in default configuration, with the exception of EOPNOTSUPP +which is expected in probing and should stay at the debug level to avoid +confusing users. + +Signed-off-by: Miroslav Lichvar +[commit 270709323a161ff1cb83af511ce50691152c75cf upstream] +Signed-off-by: Douglas Henrique Koerich +--- + sysoff.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/sysoff.c b/sysoff.c +index 5d3b907..a425275 100644 +--- a/sysoff.c ++++ b/sysoff.c +@@ -28,6 +28,14 @@ + + #define NS_PER_SEC 1000000000LL + ++static void print_ioctl_error(const char *name) ++{ ++ if (errno == EOPNOTSUPP) ++ pr_debug("ioctl %s: %s", name, strerror(errno)); ++ else ++ pr_err("ioctl %s: %s", name, strerror(errno)); ++} ++ + static int64_t pctns(struct ptp_clock_time *t) + { + return t->sec * NS_PER_SEC + t->nsec; +@@ -38,7 +46,7 @@ static int sysoff_precise(int fd, int64_t *result, uint64_t *ts) + struct ptp_sys_offset_precise pso; + memset(&pso, 0, sizeof(pso)); + if (ioctl(fd, PTP_SYS_OFFSET_PRECISE, &pso)) { +- pr_debug("ioctl PTP_SYS_OFFSET_PRECISE: %m"); ++ print_ioctl_error("PTP_SYS_OFFSET_PRECISE"); + return -errno; + } + *result = pctns(&pso.sys_realtime) - pctns(&pso.device); +@@ -98,7 +106,7 @@ static int sysoff_extended(int fd, int n_samples, + memset(&pso, 0, sizeof(pso)); + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED, &pso)) { +- pr_debug("ioctl PTP_SYS_OFFSET_EXTENDED: %m"); ++ print_ioctl_error("PTP_SYS_OFFSET_EXTENDED"); + return -errno; + } + *result = sysoff_estimate(&pso.ts[0][0], 1, n_samples, ts, delay); +@@ -112,7 +120,7 @@ static int sysoff_basic(int fd, int n_samples, + memset(&pso, 0, sizeof(pso)); + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET, &pso)) { +- perror("ioctl PTP_SYS_OFFSET"); ++ print_ioctl_error("PTP_SYS_OFFSET"); + return -errno; + } + *result = sysoff_estimate(pso.ts, 0, n_samples, ts, delay); +-- +2.29.2 + +From 11ae077e31d9957df01aacfa17eea5b5d548b249 Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:37 +0200 +Subject: [PATCH 9/47] sysoff: Retry on EBUSY when probing supported ioctls. + +Handle EBUSY when probing support for a PTP_SYS_OFFSET ioctl. Try each +ioctl up to three times before giving up on it to make the detection +more reliable. + +Signed-off-by: Miroslav Lichvar +[commit dadd2593c7beaee9eba5828a7bd8a0b5849dd8bb upstream] +Signed-off-by: Douglas Henrique Koerich +--- + sysoff.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/sysoff.c b/sysoff.c +index a425275..fc1f7ca 100644 +--- a/sysoff.c ++++ b/sysoff.c +@@ -145,8 +145,8 @@ int sysoff_measure(int fd, int method, int n_samples, + int sysoff_probe(int fd, int n_samples) + { + int64_t junk, delay; ++ int i, j, err; + uint64_t ts; +- int i; + + if (n_samples > PTP_MAX_SAMPLES) { + fprintf(stderr, "warning: %d exceeds kernel max readings %d\n", +@@ -156,9 +156,15 @@ int sysoff_probe(int fd, int n_samples) + } + + for (i = 0; i < SYSOFF_LAST; i++) { +- if (sysoff_measure(fd, i, n_samples, &junk, &ts, &delay) < 0) +- continue; +- return i; ++ for (j = 0; j < 3; j++) { ++ err = sysoff_measure(fd, i, n_samples, &junk, &ts, ++ &delay); ++ if (err == -EBUSY) ++ continue; ++ if (err) ++ break; ++ return i; ++ } + } + + return SYSOFF_RUN_TIME_MISSING; +-- +2.29.2 + +From e4fd6a930213e6f0f009eb070d51b1e14db60d1b Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:38 +0200 +Subject: [PATCH 10/47] phc2sys: Don't exit when reading of PHC fails with EBUSY. + +Reading of the PHC can occasionally fail with some drivers, e.g. the ice +driver returns EBUSY when it fails to get a lock. Continue in the loop +instead of exiting on the error. + +Signed-off-by: Miroslav Lichvar + +[ commit e8dc364f9fd5fbdac5d2c5e433f28e9da0028d49 upstream + We drop two hunks of it, namely the stuff that applies to + clockadj_compare, because they apply to the source code + ahead of baseline currently used by StarlingX ] + +Signed-off-by: Douglas Henrique Koerich +--- + phc2sys.c | 31 +++++++++++++++++++------------ + 1 file changed, 19 insertions(+), 12 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 44d6872..7959015 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -722,6 +722,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + struct clock *clock; + uint64_t ts; + int64_t offset, delay; ++ int err; + + interval.tv_sec = priv->phc_interval; + interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; +@@ -765,28 +766,34 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + if (clock->clkid == CLOCK_REALTIME && + priv->master->sysoff_method >= 0) { + /* use sysoff */ +- if (sysoff_measure(CLOCKID_TO_FD(priv->master->clkid), +- priv->master->sysoff_method, +- priv->phc_readings, +- &offset, &ts, &delay) < 0) +- return -1; ++ err = sysoff_measure(CLOCKID_TO_FD(priv->master->clkid), ++ priv->master->sysoff_method, ++ priv->phc_readings, ++ &offset, &ts, &delay); + } else if (priv->master->clkid == CLOCK_REALTIME && + clock->sysoff_method >= 0) { + /* use reversed sysoff */ +- if (sysoff_measure(CLOCKID_TO_FD(clock->clkid), +- clock->sysoff_method, +- priv->phc_readings, +- &offset, &ts, &delay) < 0) +- return -1; +- offset = -offset; +- ts += offset; ++ err = sysoff_measure(CLOCKID_TO_FD(clock->clkid), ++ clock->sysoff_method, ++ priv->phc_readings, ++ &offset, &ts, &delay); ++ if (!err) { ++ offset = -offset; ++ ts += offset; ++ } + } else { ++ err = 0; + /* use phc */ + if (!read_phc(priv->master->clkid, clock->clkid, + priv->phc_readings, + &offset, &ts, &delay)) + continue; + } ++ if (err == -EBUSY) ++ continue; ++ if (err) ++ return -1; ++ + update_clock(priv, clock, offset, ts, delay); + } + } +-- +2.29.2 + +From 0c5c39a8cd3675d91e872a75d75854907950957d Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 13:47:47 -0300 +Subject: [PATCH 11/47] phc2sys: extract PMC functionality into a smaller + struct pmc_node + +This creates a smaller structure within phc2sys_private, which embeds +all properties related to the PMC. This structure is called "pmc_node", +which is somewhat reminiscent of the old name of phc2sys_private (struct +node). But the advantage is that struct pmc_node can be reused by other +modules. + +The phc2sys code that is executed upon a subscription update, +recv_subscribed, is now refactored into a function pointer callback. It +is imaginable that other programs might to do other things in it. +Note that putting this function pointer in struct pmc_node is, long +term, maybe not the best of choices. It is only needed from the +run_pmc_events() code path, and could be therefore passed as a more +local callback to that function only. However, for that, further +refactoring is needed inside the common run_pmc() function, so that is +being left for another time. + +Signed-off-by: Vladimir Oltean + +[commit 1ca1419ad7e6cc04cf893f5a9ca449a90f39f4e0 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 228 ++++++++++++++++++++++++++++++------------------------ + 1 file changed, 125 insertions(+), 103 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 7959015..86b9822 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -39,6 +39,7 @@ + + #include "clockadj.h" + #include "clockcheck.h" ++#include "contain.h" + #include "ds.h" + #include "fsm.h" + #include "missing.h" +@@ -99,23 +100,34 @@ struct port { + struct clock *clock; + }; + ++struct pmc_node; ++ ++typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++ struct ptp_message *msg, ++ int excluded); ++ ++struct pmc_node { ++ struct pmc *pmc; ++ int pmc_ds_requested; ++ uint64_t pmc_last_update; ++ int sync_offset; ++ int leap; ++ int utc_offset_traceable; ++ int clock_identity_set; ++ struct ClockIdentity clock_identity; ++ pmc_node_recv_subscribed_t *recv_subscribed; ++}; ++ + struct phc2sys_private { + unsigned int stats_max_count; + int sanity_freq_limit; + enum servo_type servo_type; + int phc_readings; + double phc_interval; +- int sync_offset; + int forced_sync_offset; +- int utc_offset_traceable; +- int leap; + int kernel_leap; +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; + int state_changed; +- int clock_identity_set; +- struct ClockIdentity clock_identity; ++ struct pmc_node node; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -125,16 +137,16 @@ struct phc2sys_private { + + static struct config *phc2sys_config; + +-static int update_pmc(struct phc2sys_private *priv, int subscribe); ++static int update_pmc_node(struct pmc_node *node, int subscribe); + static int clock_handle_leap(struct phc2sys_private *priv, + struct clock *clock, + int64_t offset, uint64_t ts); +-static int run_pmc_get_utc_offset(struct phc2sys_private *priv, ++static int run_pmc_get_utc_offset(struct pmc_node *node, + int timeout); +-static void run_pmc_events(struct phc2sys_private *priv); ++static void run_pmc_events(struct pmc_node *node); + + static int normalize_state(int state); +-static int run_pmc_port_properties(struct phc2sys_private *priv, ++static int run_pmc_port_properties(struct pmc_node *node, + int timeout, unsigned int port, + int *state, int *tstamping, char *iface); + +@@ -325,7 +337,7 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + + LIST_FOREACH(p, &priv->ports, list) { + if (p->clock == clock) { +- ret = run_pmc_port_properties(priv, 1000, p->number, ++ ret = run_pmc_port_properties(&priv->node, 1000, p->number, + &state, ×tamping, + iface); + if (ret > 0) +@@ -660,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + + if (src == CLOCK_INVALID) { + /* The sync offset can't be applied with PPS alone. */ +- priv->sync_offset = 0; ++ priv->node.sync_offset = 0; + } else { + enable_pps_output(priv->master->clkid); + } +@@ -691,7 +703,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc(priv, 0) < 0) ++ if (update_pmc_node(&priv->node, 0) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -729,15 +741,15 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); +- if (update_pmc(priv, subscriptions) < 0) ++ if (update_pmc_node(&priv->node, subscriptions) < 0) + continue; + + if (subscriptions) { +- run_pmc_events(priv); ++ run_pmc_events(&priv->node); + if (priv->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ +- if (run_pmc_get_utc_offset(priv, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + continue; + } +@@ -800,13 +812,12 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + return 0; + } + +-static int check_clock_identity(struct phc2sys_private *priv, +- struct ptp_message *msg) ++static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) + { +- if (!priv->clock_identity_set) ++ if (!node->clock_identity_set) + return 1; +- return cid_eq(&priv->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); ++ return cid_eq(&node->clock_identity, ++ &msg->header.sourcePortIdentity.clockIdentity); + } + + static int is_msg_mgt(struct ptp_message *msg) +@@ -876,9 +887,13 @@ static int clock_compute_state(struct phc2sys_private *priv, + return state; + } + +-static int recv_subscribed(struct phc2sys_private *priv, +- struct ptp_message *msg, int excluded) ++#define node_to_phc2sys(node) \ ++ container_of(node, struct phc2sys_private, node) ++ ++static int phc2sys_recv_subscribed(struct pmc_node *node, ++ struct ptp_message *msg, int excluded) + { ++ struct phc2sys_private *priv = node_to_phc2sys(node); + int mgt_id, state; + struct portDS *pds; + struct port *port; +@@ -913,29 +928,28 @@ static int recv_subscribed(struct phc2sys_private *priv, + return 0; + } + +-static void send_subscription(struct phc2sys_private *priv) ++static void send_subscription(struct pmc_node *node) + { + struct subscribe_events_np sen; + + memset(&sen, 0, sizeof(sen)); + sen.duration = PMC_SUBSCRIBE_DURATION; + sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; +- pmc_send_set_action(priv->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +-static int init_pmc(struct config *cfg, struct phc2sys_private *priv) ++static int init_pmc_node(struct config *cfg, struct pmc_node *node, ++ const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) + { +- char uds_local[MAX_IFNAME_SIZE + 1]; +- +- snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d", +- getpid()); +- priv->pmc = pmc_create(cfg, TRANS_UDS, uds_local, 0, ++ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, + config_get_int(cfg, NULL, "domainNumber"), + config_get_int(cfg, NULL, "transportSpecific") << 4, 1); +- if (!priv->pmc) { ++ if (!node->pmc) { + pr_err("failed to create pmc"); + return -1; + } ++ node->recv_subscribed = recv_subscribed; + + return 0; + } +@@ -946,7 +960,7 @@ static int init_pmc(struct config *cfg, struct phc2sys_private *priv) + * -1: error reported by the other side + * -2: local error, fatal + */ +-static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, ++static int run_pmc(struct pmc_node *node, int timeout, int ds_id, + struct ptp_message **msg) + { + #define N_FD 1 +@@ -954,9 +968,9 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + int cnt, res; + + while (1) { +- pollfd[0].fd = pmc_get_transport_fd(priv->pmc); ++ pollfd[0].fd = pmc_get_transport_fd(node->pmc); + pollfd[0].events = POLLIN|POLLPRI; +- if (!priv->pmc_ds_requested && ds_id >= 0) ++ if (!node->pmc_ds_requested && ds_id >= 0) + pollfd[0].events |= POLLOUT; + + cnt = poll(pollfd, N_FD, timeout); +@@ -966,7 +980,7 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + } + if (!cnt) { + /* Request the data set again in the next run. */ +- priv->pmc_ds_requested = 0; ++ node->pmc_ds_requested = 0; + return 0; + } + +@@ -975,24 +989,24 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + !(pollfd[0].revents & (POLLIN|POLLPRI))) { + switch (ds_id) { + case TLV_SUBSCRIBE_EVENTS_NP: +- send_subscription(priv); ++ send_subscription(node); + break; + default: +- pmc_send_get_action(priv->pmc, ds_id); ++ pmc_send_get_action(node->pmc, ds_id); + break; + } +- priv->pmc_ds_requested = 1; ++ node->pmc_ds_requested = 1; + } + + if (!(pollfd[0].revents & (POLLIN|POLLPRI))) + continue; + +- *msg = pmc_recv(priv->pmc); ++ *msg = pmc_recv(node->pmc); + + if (!*msg) + continue; + +- if (!check_clock_identity(priv, *msg)) { ++ if (!check_clock_identity(node, *msg)) { + msg_put(*msg); + *msg = NULL; + continue; +@@ -1000,29 +1014,29 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + + res = is_msg_mgt(*msg); + if (res < 0 && get_mgt_err_id(*msg) == ds_id) { +- priv->pmc_ds_requested = 0; ++ node->pmc_ds_requested = 0; + return -1; + } +- if (res <= 0 || recv_subscribed(priv, *msg, ds_id) || ++ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || + get_mgt_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; + continue; + } +- priv->pmc_ds_requested = 0; ++ node->pmc_ds_requested = 0; + return 1; + } + } + +-static int run_pmc_wait_sync(struct phc2sys_private *priv, int timeout) ++static int run_pmc_wait_sync(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; +- int res; +- void *data; + Enumeration8 portState; ++ void *data; ++ int res; + + while (1) { +- res = run_pmc(priv, timeout, TLV_PORT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); + if (res <= 0) + return res; + +@@ -1036,47 +1050,47 @@ static int run_pmc_wait_sync(struct phc2sys_private *priv, int timeout) + return 1; + } + /* try to get more data sets (for other ports) */ +- priv->pmc_ds_requested = 1; ++ node->pmc_ds_requested = 1; + } + } + +-static int run_pmc_get_utc_offset(struct phc2sys_private *priv, int timeout) ++static int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; + struct timePropertiesDS *tds; + +- res = run_pmc(priv, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); + if (res <= 0) + return res; + + tds = (struct timePropertiesDS *)get_mgt_data(msg); + if (tds->flags & PTP_TIMESCALE) { +- priv->sync_offset = tds->currentUtcOffset; ++ node->sync_offset = tds->currentUtcOffset; + if (tds->flags & LEAP_61) +- priv->leap = 1; ++ node->leap = 1; + else if (tds->flags & LEAP_59) +- priv->leap = -1; ++ node->leap = -1; + else +- priv->leap = 0; +- priv->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && + tds->flags & TIME_TRACEABLE; + } else { +- priv->sync_offset = 0; +- priv->leap = 0; +- priv->utc_offset_traceable = 0; ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; + } + msg_put(msg); + return 1; + } + +-static int run_pmc_get_number_ports(struct phc2sys_private *priv, int timeout) ++static int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; + struct defaultDS *dds; + +- res = run_pmc(priv, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); + if (res <= 0) + return res; + +@@ -1086,36 +1100,36 @@ static int run_pmc_get_number_ports(struct phc2sys_private *priv, int timeout) + return res; + } + +-static int run_pmc_subscribe(struct phc2sys_private *priv, int timeout) ++static int run_pmc_subscribe(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; + +- res = run_pmc(priv, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); + if (res <= 0) + return res; + msg_put(msg); + return 1; + } + +-static void run_pmc_events(struct phc2sys_private *priv) ++static void run_pmc_events(struct pmc_node *node) + { + struct ptp_message *msg; + +- run_pmc(priv, 0, -1, &msg); ++ run_pmc(node, 0, -1, &msg); + } + +-static int run_pmc_port_properties(struct phc2sys_private *priv, int timeout, +- unsigned int port, +- int *state, int *tstamping, char *iface) ++static int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) + { + struct ptp_message *msg; + int res, len; + struct port_properties_np *ppn; + +- pmc_target_port(priv->pmc, port); ++ pmc_target_port(node->pmc, port); + while (1) { +- res = run_pmc(priv, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); + if (res <= 0) + goto out; + +@@ -1138,32 +1152,35 @@ static int run_pmc_port_properties(struct phc2sys_private *priv, int timeout, + break; + } + out: +- pmc_target_all(priv->pmc); ++ pmc_target_all(node->pmc); + return res; + } + +-static int run_pmc_clock_identity(struct phc2sys_private *priv, int timeout) ++static int run_pmc_clock_identity(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + struct defaultDS *dds; + int res; + +- res = run_pmc(priv, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); + if (res <= 0) + return res; + + dds = (struct defaultDS *)get_mgt_data(msg); +- memcpy(&priv->clock_identity, &dds->clockIdentity, ++ memcpy(&node->clock_identity, &dds->clockIdentity, + sizeof(struct ClockIdentity)); +- priv->clock_identity_set = 1; ++ node->clock_identity_set = 1; + msg_put(msg); + return 1; + } + +-static void close_pmc(struct phc2sys_private *priv) ++static void close_pmc_node(struct pmc_node *node) + { +- pmc_destroy(priv->pmc); +- priv->pmc = NULL; ++ if (!node->pmc) ++ return; ++ ++ pmc_destroy(node->pmc); ++ node->pmc = NULL; + } + + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) +@@ -1178,7 +1195,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + while (1) { + if (!is_running()) + return -1; +- res = run_pmc_clock_identity(priv, 1000); ++ res = run_pmc_clock_identity(&priv->node, 1000); + if (res < 0) + return -1; + if (res > 0) +@@ -1187,20 +1204,20 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + pr_notice("Waiting for ptp4l..."); + } + +- number_ports = run_pmc_get_number_ports(priv, 1000); ++ number_ports = run_pmc_get_number_ports(&priv->node, 1000); + if (number_ports <= 0) { + pr_err("failed to get number of ports"); + return -1; + } + +- res = run_pmc_subscribe(priv, 1000); ++ res = run_pmc_subscribe(&priv->node, 1000); + if (res <= 0) { + pr_err("failed to subscribe"); + return -1; + } + + for (i = 1; i <= number_ports; i++) { +- res = run_pmc_port_properties(priv, 1000, i, &state, ++ res = run_pmc_port_properties(&priv->node, 1000, i, &state, + ×tamping, iface); + if (res == -1) { + /* port does not exist, ignore the port */ +@@ -1237,7 +1254,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (run_pmc_get_utc_offset(priv, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + return -1; + } +@@ -1245,7 +1262,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-static int update_pmc(struct phc2sys_private *priv, int subscribe) ++static int update_pmc_node(struct pmc_node *node, int subscribe) + { + struct timespec tp; + uint64_t ts; +@@ -1256,13 +1273,13 @@ static int update_pmc(struct phc2sys_private *priv, int subscribe) + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (priv->pmc && +- !(ts > priv->pmc_last_update && +- ts - priv->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (node->pmc && ++ !(ts > node->pmc_last_update && ++ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (subscribe) +- run_pmc_subscribe(priv, 0); +- if (run_pmc_get_utc_offset(priv, 0) > 0) +- priv->pmc_last_update = ts; ++ run_pmc_subscribe(node, 0); ++ if (run_pmc_get_utc_offset(node, 0) > 0) ++ node->pmc_last_update = ts; + } + + return 0; +@@ -1272,9 +1289,9 @@ static int update_pmc(struct phc2sys_private *priv, int subscribe) + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) + { +- int clock_leap, node_leap = priv->leap; ++ int clock_leap, node_leap = priv->node.leap; + +- clock->sync_offset = priv->sync_offset; ++ clock->sync_offset = priv->node.sync_offset; + + if ((node_leap || clock->leap_set) && + clock->is_utc != priv->master->is_utc) { +@@ -1315,7 +1332,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + } + } + +- if (priv->utc_offset_traceable && ++ if (priv->node.utc_offset_traceable && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); +@@ -1370,6 +1387,7 @@ static void usage(char *progname) + int main(int argc, char *argv[]) + { + char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL; ++ char uds_local[MAX_IFNAME_SIZE + 1]; + struct clock *src, *dst; + struct config *cfg; + struct option *opts; +@@ -1478,7 +1496,7 @@ int main(int argc, char *argv[]) + goto end; + break; + case 'O': +- if (get_arg_val_i(c, optarg, &priv.sync_offset, ++ if (get_arg_val_i(c, optarg, &priv.node.sync_offset, + INT_MIN, INT_MAX)) + goto end; + priv.forced_sync_offset = -1; +@@ -1605,8 +1623,12 @@ int main(int argc, char *argv[]) + priv.sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit"); + priv.default_sync = config_get_int(cfg, NULL, "default_sync"); + ++ snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d", ++ getpid()); ++ + if (autocfg) { +- if (init_pmc(cfg, &priv)) ++ if (init_pmc_node(cfg, &priv.node, uds_local, ++ phc2sys_recv_subscribed)) + goto end; + if (auto_init_ports(&priv, rt) < 0) + goto end; +@@ -1643,11 +1665,12 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- if (init_pmc(cfg, &priv)) ++ if (init_pmc_node(cfg, &priv.node, uds_local, ++ phc2sys_recv_subscribed)) + goto end; + + while (is_running()) { +- r = run_pmc_wait_sync(&priv, 1000); ++ r = run_pmc_wait_sync(&priv.node, 1000); + if (r < 0) + goto end; + if (r > 0) +@@ -1657,7 +1680,7 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = run_pmc_get_utc_offset(&priv, 1000); ++ r = run_pmc_get_utc_offset(&priv.node, 1000); + if (r <= 0) { + pr_err("failed to get UTC offset"); + goto end; +@@ -1667,7 +1690,7 @@ int main(int argc, char *argv[]) + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || + src->clkid == CLOCK_INVALID) +- close_pmc(&priv); ++ close_pmc_node(&priv.node); + } + + if (pps_fd >= 0) { +@@ -1680,8 +1703,7 @@ int main(int argc, char *argv[]) + } + + end: +- if (priv.pmc) +- close_pmc(&priv); ++ close_pmc_node(&priv.node); + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +-- +2.25.1 + +From 87d8e7281e3e66813d0c669bea0b5335a8cbb6b6 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 13:59:48 -0300 +Subject: [PATCH 12/47] phc2sys: make PMC functions non-static + +In preparation of a trivial movement of code to pmc_common.c, remove the +"static" keyword from the functions that will end up there, since they +will be still called from phc2sys.c for now. + +Signed-off-by: Vladimir Oltean + +[commit 2ccbb14450e1e96168a2604c0e8c96ae5a6a5bf0 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 40 +++++++++++++++++++--------------------- + 1 file changed, 19 insertions(+), 21 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 86b9822..d5b8e71 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -137,18 +137,17 @@ struct phc2sys_private { + + static struct config *phc2sys_config; + +-static int update_pmc_node(struct pmc_node *node, int subscribe); ++int update_pmc_node(struct pmc_node *node, int subscribe); + static int clock_handle_leap(struct phc2sys_private *priv, + struct clock *clock, + int64_t offset, uint64_t ts); +-static int run_pmc_get_utc_offset(struct pmc_node *node, +- int timeout); +-static void run_pmc_events(struct pmc_node *node); ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++void run_pmc_events(struct pmc_node *node); + + static int normalize_state(int state); +-static int run_pmc_port_properties(struct pmc_node *node, +- int timeout, unsigned int port, +- int *state, int *tstamping, char *iface); ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); + + static struct servo *servo_add(struct phc2sys_private *priv, + struct clock *clock) +@@ -838,13 +837,13 @@ static int is_msg_mgt(struct ptp_message *msg) + return 0; + } + +-static int get_mgt_id(struct ptp_message *msg) ++int get_mgt_id(struct ptp_message *msg) + { + struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; + return mgt->id; + } + +-static void *get_mgt_data(struct ptp_message *msg) ++void *get_mgt_data(struct ptp_message *msg) + { + struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; + return mgt->data; +@@ -938,9 +937,8 @@ static void send_subscription(struct pmc_node *node) + pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +-static int init_pmc_node(struct config *cfg, struct pmc_node *node, +- const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) + { + node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, + config_get_int(cfg, NULL, "domainNumber"), +@@ -1054,7 +1052,7 @@ static int run_pmc_wait_sync(struct pmc_node *node, int timeout) + } + } + +-static int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -1084,7 +1082,7 @@ static int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + return 1; + } + +-static int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -1100,7 +1098,7 @@ static int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + return res; + } + +-static int run_pmc_subscribe(struct pmc_node *node, int timeout) ++int run_pmc_subscribe(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -1112,16 +1110,16 @@ static int run_pmc_subscribe(struct pmc_node *node, int timeout) + return 1; + } + +-static void run_pmc_events(struct pmc_node *node) ++void run_pmc_events(struct pmc_node *node) + { + struct ptp_message *msg; + + run_pmc(node, 0, -1, &msg); + } + +-static int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) + { + struct ptp_message *msg; + int res, len; +@@ -1174,7 +1172,7 @@ static int run_pmc_clock_identity(struct pmc_node *node, int timeout) + return 1; + } + +-static void close_pmc_node(struct pmc_node *node) ++void close_pmc_node(struct pmc_node *node) + { + if (!node->pmc) + return; +@@ -1262,7 +1260,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-static int update_pmc_node(struct pmc_node *node, int subscribe) ++int update_pmc_node(struct pmc_node *node, int subscribe) + { + struct timespec tp; + uint64_t ts; +-- +2.25.1 + +From ba9c2ea0e1f7a96093bca1147d4745a7d0ce17b6 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:34:19 -0300 +Subject: [PATCH 13/47] phc2sys: break out pmc code into pmc_common.c + +The code through which phc2sys sends various PTP management messages to +ptp4l via pmc can be reused. + +This patch is a trivial movement of that code to a separate translation +module, outside of phc2sys. This makes it available to other programs +that want to subscribe to port state change events too, such as ts2phc. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Jacob Keller + +[commit abc75482332752b630b023178ccdf636f5fe7de7 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 354 --------------------------------------------------- + pmc_common.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++ + pmc_common.h | 35 +++++ + 3 files changed, 372 insertions(+), 354 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index d5b8e71..9184db6 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -63,12 +63,6 @@ + #define NS_PER_SEC 1000000000LL + + #define PHC_PPS_OFFSET_LIMIT 10000000 +-#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) +-#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ +-/* Note that PMC_SUBSCRIBE_DURATION has to be longer than +- * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is +- * renewed. +- */ + + struct clock { + LIST_ENTRY(clock) list; +@@ -100,24 +94,6 @@ struct port { + struct clock *clock; + }; + +-struct pmc_node; +- +-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, +- struct ptp_message *msg, +- int excluded); +- +-struct pmc_node { +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; +- int sync_offset; +- int leap; +- int utc_offset_traceable; +- int clock_identity_set; +- struct ClockIdentity clock_identity; +- pmc_node_recv_subscribed_t *recv_subscribed; +-}; +- + struct phc2sys_private { + unsigned int stats_max_count; + int sanity_freq_limit; +@@ -137,17 +113,11 @@ struct phc2sys_private { + + static struct config *phc2sys_config; + +-int update_pmc_node(struct pmc_node *node, int subscribe); + static int clock_handle_leap(struct phc2sys_private *priv, + struct clock *clock, + int64_t offset, uint64_t ts); +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); +-void run_pmc_events(struct pmc_node *node); + + static int normalize_state(int state); +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface); + + static struct servo *servo_add(struct phc2sys_private *priv, + struct clock *clock) +@@ -811,52 +781,6 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + return 0; + } + +-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) +-{ +- if (!node->clock_identity_set) +- return 1; +- return cid_eq(&node->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); +-} +- +-static int is_msg_mgt(struct ptp_message *msg) +-{ +- struct TLV *tlv; +- +- if (msg_type(msg) != MANAGEMENT) +- return 0; +- if (management_action(msg) != RESPONSE) +- return 0; +- if (msg_tlv_count(msg) != 1) +- return 0; +- tlv = (struct TLV *) msg->management.suffix; +- if (tlv->type == TLV_MANAGEMENT) +- return 1; +- if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) +- return -1; +- return 0; +-} +- +-int get_mgt_id(struct ptp_message *msg) +-{ +- struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->id; +-} +- +-void *get_mgt_data(struct ptp_message *msg) +-{ +- struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->data; +-} +- +-static int get_mgt_err_id(struct ptp_message *msg) +-{ +- struct management_error_status *mgt; +- +- mgt = (struct management_error_status *)msg->management.suffix; +- return mgt->id; +-} +- + static int normalize_state(int state) + { + if (state != PS_MASTER && state != PS_SLAVE && +@@ -927,260 +851,6 @@ static int phc2sys_recv_subscribed(struct pmc_node *node, + return 0; + } + +-static void send_subscription(struct pmc_node *node) +-{ +- struct subscribe_events_np sen; +- +- memset(&sen, 0, sizeof(sen)); +- sen.duration = PMC_SUBSCRIBE_DURATION; +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; +- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); +-} +- +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) +-{ +- node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, +- config_get_int(cfg, NULL, "domainNumber"), +- config_get_int(cfg, NULL, "transportSpecific") << 4, 1); +- if (!node->pmc) { +- pr_err("failed to create pmc"); +- return -1; +- } +- node->recv_subscribed = recv_subscribed; +- +- return 0; +-} +- +-/* Return values: +- * 1: success +- * 0: timeout +- * -1: error reported by the other side +- * -2: local error, fatal +- */ +-static int run_pmc(struct pmc_node *node, int timeout, int ds_id, +- struct ptp_message **msg) +-{ +-#define N_FD 1 +- struct pollfd pollfd[N_FD]; +- int cnt, res; +- +- while (1) { +- pollfd[0].fd = pmc_get_transport_fd(node->pmc); +- pollfd[0].events = POLLIN|POLLPRI; +- if (!node->pmc_ds_requested && ds_id >= 0) +- pollfd[0].events |= POLLOUT; +- +- cnt = poll(pollfd, N_FD, timeout); +- if (cnt < 0) { +- pr_err("poll failed"); +- return -2; +- } +- if (!cnt) { +- /* Request the data set again in the next run. */ +- node->pmc_ds_requested = 0; +- return 0; +- } +- +- /* Send a new request if there are no pending messages. */ +- if ((pollfd[0].revents & POLLOUT) && +- !(pollfd[0].revents & (POLLIN|POLLPRI))) { +- switch (ds_id) { +- case TLV_SUBSCRIBE_EVENTS_NP: +- send_subscription(node); +- break; +- default: +- pmc_send_get_action(node->pmc, ds_id); +- break; +- } +- node->pmc_ds_requested = 1; +- } +- +- if (!(pollfd[0].revents & (POLLIN|POLLPRI))) +- continue; +- +- *msg = pmc_recv(node->pmc); +- +- if (!*msg) +- continue; +- +- if (!check_clock_identity(node, *msg)) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- +- res = is_msg_mgt(*msg); +- if (res < 0 && get_mgt_err_id(*msg) == ds_id) { +- node->pmc_ds_requested = 0; +- return -1; +- } +- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || +- get_mgt_id(*msg) != ds_id) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- node->pmc_ds_requested = 0; +- return 1; +- } +-} +- +-static int run_pmc_wait_sync(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- Enumeration8 portState; +- void *data; +- int res; +- +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- data = get_mgt_data(msg); +- portState = ((struct portDS *)data)->portState; +- msg_put(msg); +- +- switch (portState) { +- case PS_MASTER: +- case PS_SLAVE: +- return 1; +- } +- /* try to get more data sets (for other ports) */ +- node->pmc_ds_requested = 1; +- } +-} +- +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct timePropertiesDS *tds; +- +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- tds = (struct timePropertiesDS *)get_mgt_data(msg); +- if (tds->flags & PTP_TIMESCALE) { +- node->sync_offset = tds->currentUtcOffset; +- if (tds->flags & LEAP_61) +- node->leap = 1; +- else if (tds->flags & LEAP_59) +- node->leap = -1; +- else +- node->leap = 0; +- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && +- tds->flags & TIME_TRACEABLE; +- } else { +- node->sync_offset = 0; +- node->leap = 0; +- node->utc_offset_traceable = 0; +- } +- msg_put(msg); +- return 1; +-} +- +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct defaultDS *dds; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- res = dds->numberPorts; +- msg_put(msg); +- return res; +-} +- +-int run_pmc_subscribe(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (res <= 0) +- return res; +- msg_put(msg); +- return 1; +-} +- +-void run_pmc_events(struct pmc_node *node) +-{ +- struct ptp_message *msg; +- +- run_pmc(node, 0, -1, &msg); +-} +- +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) +-{ +- struct ptp_message *msg; +- int res, len; +- struct port_properties_np *ppn; +- +- pmc_target_port(node->pmc, port); +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); +- if (res <= 0) +- goto out; +- +- ppn = get_mgt_data(msg); +- if (ppn->portIdentity.portNumber != port) { +- msg_put(msg); +- continue; +- } +- +- *state = ppn->port_state; +- *tstamping = ppn->timestamping; +- len = ppn->interface.length; +- if (len > IFNAMSIZ - 1) +- len = IFNAMSIZ - 1; +- memcpy(iface, ppn->interface.text, len); +- iface[len] = '\0'; +- +- msg_put(msg); +- res = 1; +- break; +- } +-out: +- pmc_target_all(node->pmc); +- return res; +-} +- +-static int run_pmc_clock_identity(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- struct defaultDS *dds; +- int res; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- memcpy(&node->clock_identity, &dds->clockIdentity, +- sizeof(struct ClockIdentity)); +- node->clock_identity_set = 1; +- msg_put(msg); +- return 1; +-} +- +-void close_pmc_node(struct pmc_node *node) +-{ +- if (!node->pmc) +- return; +- +- pmc_destroy(node->pmc); +- node->pmc = NULL; +-} +- + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + { + struct port *port; +@@ -1259,30 +929,6 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + return 0; + } + +-/* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_node *node, int subscribe) +-{ +- struct timespec tp; +- uint64_t ts; +- +- if (clock_gettime(CLOCK_MONOTONIC, &tp)) { +- pr_err("failed to read clock: %m"); +- return -1; +- } +- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; +- +- if (node->pmc && +- !(ts > node->pmc_last_update && +- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { +- if (subscribe) +- run_pmc_subscribe(node, 0); +- if (run_pmc_get_utc_offset(node, 0) > 0) +- node->pmc_last_update = ts; +- } +- +- return 0; +-} +- + /* Returns: non-zero to skip clock update */ + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) +diff --git a/pmc_common.c b/pmc_common.c +index f07f6f6..c9cdf18 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -18,6 +18,8 @@ + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include ++#include ++#include + #include + #include + #include +@@ -56,6 +58,13 @@ + /* Includes one extra byte to make length even. */ + #define EMPTY_PTP_TEXT 2 + ++#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) ++#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ ++/* Note that PMC_SUBSCRIBE_DURATION has to be longer than ++ * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is ++ * renewed. ++ */ ++ + static void do_get_action(struct pmc *pmc, int action, int index, char *str); + static void do_set_action(struct pmc *pmc, int action, int index, char *str); + static void not_supported(struct pmc *pmc, int action, int index, char *str); +@@ -711,3 +720,331 @@ int pmc_do_command(struct pmc *pmc, char *str) + + return 0; + } ++ ++static void send_subscription(struct pmc_node *node) ++{ ++ struct subscribe_events_np sen; ++ ++ memset(&sen, 0, sizeof(sen)); ++ sen.duration = PMC_SUBSCRIBE_DURATION; ++ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++} ++ ++static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) ++{ ++ if (!node->clock_identity_set) ++ return 1; ++ return cid_eq(&node->clock_identity, ++ &msg->header.sourcePortIdentity.clockIdentity); ++} ++ ++static int is_msg_mgt(struct ptp_message *msg) ++{ ++ struct TLV *tlv; ++ ++ if (msg_type(msg) != MANAGEMENT) ++ return 0; ++ if (management_action(msg) != RESPONSE) ++ return 0; ++ if (msg_tlv_count(msg) != 1) ++ return 0; ++ tlv = (struct TLV *) msg->management.suffix; ++ if (tlv->type == TLV_MANAGEMENT) ++ return 1; ++ if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) ++ return -1; ++ return 0; ++} ++ ++int get_mgt_id(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->id; ++} ++ ++void *get_mgt_data(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->data; ++} ++ ++static int get_mgt_err_id(struct ptp_message *msg) ++{ ++ struct management_error_status *mgt; ++ ++ mgt = (struct management_error_status *)msg->management.suffix; ++ return mgt->id; ++} ++ ++/* Return values: ++ * 1: success ++ * 0: timeout ++ * -1: error reported by the other side ++ * -2: local error, fatal ++ */ ++static int run_pmc(struct pmc_node *node, int timeout, int ds_id, ++ struct ptp_message **msg) ++{ ++#define N_FD 1 ++ struct pollfd pollfd[N_FD]; ++ int cnt, res; ++ ++ while (1) { ++ pollfd[0].fd = pmc_get_transport_fd(node->pmc); ++ pollfd[0].events = POLLIN|POLLPRI; ++ if (!node->pmc_ds_requested && ds_id >= 0) ++ pollfd[0].events |= POLLOUT; ++ ++ cnt = poll(pollfd, N_FD, timeout); ++ if (cnt < 0) { ++ pr_err("poll failed"); ++ return -2; ++ } ++ if (!cnt) { ++ /* Request the data set again in the next run. */ ++ node->pmc_ds_requested = 0; ++ return 0; ++ } ++ ++ /* Send a new request if there are no pending messages. */ ++ if ((pollfd[0].revents & POLLOUT) && ++ !(pollfd[0].revents & (POLLIN|POLLPRI))) { ++ switch (ds_id) { ++ case TLV_SUBSCRIBE_EVENTS_NP: ++ send_subscription(node); ++ break; ++ default: ++ pmc_send_get_action(node->pmc, ds_id); ++ break; ++ } ++ node->pmc_ds_requested = 1; ++ } ++ ++ if (!(pollfd[0].revents & (POLLIN|POLLPRI))) ++ continue; ++ ++ *msg = pmc_recv(node->pmc); ++ ++ if (!*msg) ++ continue; ++ ++ if (!check_clock_identity(node, *msg)) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ ++ res = is_msg_mgt(*msg); ++ if (res < 0 && get_mgt_err_id(*msg) == ds_id) { ++ node->pmc_ds_requested = 0; ++ return -1; ++ } ++ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || ++ get_mgt_id(*msg) != ds_id) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ node->pmc_ds_requested = 0; ++ return 1; ++ } ++} ++ ++int run_pmc_wait_sync(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ Enumeration8 portState; ++ void *data; ++ int res; ++ ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ data = get_mgt_data(msg); ++ portState = ((struct portDS *)data)->portState; ++ msg_put(msg); ++ ++ switch (portState) { ++ case PS_MASTER: ++ case PS_SLAVE: ++ return 1; ++ } ++ /* try to get more data sets (for other ports) */ ++ node->pmc_ds_requested = 1; ++ } ++} ++ ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct timePropertiesDS *tds; ++ ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ tds = (struct timePropertiesDS *)get_mgt_data(msg); ++ if (tds->flags & PTP_TIMESCALE) { ++ node->sync_offset = tds->currentUtcOffset; ++ if (tds->flags & LEAP_61) ++ node->leap = 1; ++ else if (tds->flags & LEAP_59) ++ node->leap = -1; ++ else ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ tds->flags & TIME_TRACEABLE; ++ } else { ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; ++ } ++ msg_put(msg); ++ return 1; ++} ++ ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct defaultDS *dds; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ res = dds->numberPorts; ++ msg_put(msg); ++ return res; ++} ++ ++int run_pmc_subscribe(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (res <= 0) ++ return res; ++ msg_put(msg); ++ return 1; ++} ++ ++void run_pmc_events(struct pmc_node *node) ++{ ++ struct ptp_message *msg; ++ ++ run_pmc(node, 0, -1, &msg); ++} ++ ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) ++{ ++ struct ptp_message *msg; ++ int res, len; ++ struct port_properties_np *ppn; ++ ++ pmc_target_port(node->pmc, port); ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ if (res <= 0) ++ goto out; ++ ++ ppn = get_mgt_data(msg); ++ if (ppn->portIdentity.portNumber != port) { ++ msg_put(msg); ++ continue; ++ } ++ ++ *state = ppn->port_state; ++ *tstamping = ppn->timestamping; ++ len = ppn->interface.length; ++ if (len > IFNAMSIZ - 1) ++ len = IFNAMSIZ - 1; ++ memcpy(iface, ppn->interface.text, len); ++ iface[len] = '\0'; ++ ++ msg_put(msg); ++ res = 1; ++ break; ++ } ++out: ++ pmc_target_all(node->pmc); ++ return res; ++} ++ ++int run_pmc_clock_identity(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ struct defaultDS *dds; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ memcpy(&node->clock_identity, &dds->clockIdentity, ++ sizeof(struct ClockIdentity)); ++ node->clock_identity_set = 1; ++ msg_put(msg); ++ return 1; ++} ++ ++/* Returns: -1 in case of error, 0 otherwise */ ++int update_pmc_node(struct pmc_node *node, int subscribe) ++{ ++ struct timespec tp; ++ uint64_t ts; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &tp)) { ++ pr_err("failed to read clock: %m"); ++ return -1; ++ } ++ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; ++ ++ if (node->pmc && ++ !(ts > node->pmc_last_update && ++ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (subscribe) ++ run_pmc_subscribe(node, 0); ++ if (run_pmc_get_utc_offset(node, 0) > 0) ++ node->pmc_last_update = ts; ++ } ++ ++ return 0; ++} ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) ++{ ++ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, ++ config_get_int(cfg, NULL, "domainNumber"), ++ config_get_int(cfg, NULL, "transportSpecific") << 4, 1); ++ if (!node->pmc) { ++ pr_err("failed to create pmc"); ++ return -1; ++ } ++ node->recv_subscribed = recv_subscribed; ++ ++ return 0; ++} ++ ++void close_pmc_node(struct pmc_node *node) ++{ ++ if (!node->pmc) ++ return; ++ ++ pmc_destroy(node->pmc); ++ node->pmc = NULL; ++} +diff --git a/pmc_common.h b/pmc_common.h +index 9fa72de..476ccea 100644 +--- a/pmc_common.h ++++ b/pmc_common.h +@@ -22,6 +22,7 @@ + #define HAVE_PMC_COMMON_H + + #include "config.h" ++#include "fsm.h" + #include "msg.h" + #include "transport.h" + +@@ -49,4 +50,38 @@ void pmc_target_all(struct pmc *pmc); + const char *pmc_action_string(int action); + int pmc_do_command(struct pmc *pmc, char *str); + ++struct pmc_node; ++ ++typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++ struct ptp_message *msg, ++ int excluded); ++ ++struct pmc_node { ++ struct pmc *pmc; ++ int pmc_ds_requested; ++ uint64_t pmc_last_update; ++ int sync_offset; ++ int leap; ++ int utc_offset_traceable; ++ int clock_identity_set; ++ struct ClockIdentity clock_identity; ++ pmc_node_recv_subscribed_t *recv_subscribed; ++}; ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed); ++void close_pmc_node(struct pmc_node *node); ++int update_pmc_node(struct pmc_node *node, int subscribe); ++int run_pmc_subscribe(struct pmc_node *node, int timeout); ++int run_pmc_clock_identity(struct pmc_node *node, int timeout); ++int run_pmc_wait_sync(struct pmc_node *node, int timeout); ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout); ++void run_pmc_events(struct pmc_node *node); ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++int get_mgt_id(struct ptp_message *msg); ++void *get_mgt_data(struct ptp_message *msg); ++ + #endif +-- +2.25.1 + +From c00e75286b2ad882cf8e89549ea58e438c877f95 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:40:59 -0300 +Subject: [PATCH 14/47] Introduce the PMC agent module. + +The logic for placing PTP management queries migrated out of phc2sys into +pmc_common in order to be shared with other programs in the future. This +logic uses pmc_common rather than extending it, and so it should live in +its own module stacked on top of pmc_common. + +This patch moves the code into its own file verbatim without making any +other changes. + +Signed-off-by: Richard Cochran + +[commit f266740e1a8aacc03f97205ae14fc43c59081433 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + makefile | 6 +- + phc2sys.c | 2 +- + pmc_agent.c | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++ + pmc_agent.h | 62 +++++++++ + pmc_common.c | 338 ----------------------------------------------- + pmc_common.h | 34 ----- + 6 files changed, 427 insertions(+), 376 deletions(-) + create mode 100644 pmc_agent.c + create mode 100644 pmc_agent.h + +diff --git a/makefile b/makefile +index 27c4d78..33e7ca0 100644 +--- a/makefile ++++ b/makefile +@@ -34,8 +34,8 @@ OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ + sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ + unicast_fsm.o unicast_service.o util.o version.o + +-OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ +- sysoff.o timemaster.o $(TS2PHC) ++OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \ ++ pmc_common.o sysoff.o timemaster.o $(TS2PHC) + SRC = $(OBJECTS:.o=.c) + DEPEND = $(OBJECTS:.o=.d) + srcdir := $(dir $(lastword $(MAKEFILE_LIST))) +@@ -59,7 +59,7 @@ pmc: config.o hash.o interface.o msg.o phc.o pmc.o pmc_common.o print.o sk.o \ + tlv.o $(TRANSP) util.o version.o + + phc2sys: clockadj.o clockcheck.o config.o hash.o interface.o msg.o \ +- phc.o phc2sys.o pmc_common.o print.o $(SERVOS) sk.o stats.o \ ++ phc.o phc2sys.o pmc_agent.o pmc_common.o print.o $(SERVOS) sk.o stats.o \ + sysoff.o tlv.o $(TRANSP) util.o version.o + + hwstamp_ctl: hwstamp_ctl.o version.o +diff --git a/phc2sys.c b/phc2sys.c +index 9184db6..648ba61 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -47,7 +47,7 @@ + #include "ntpshm.h" + #include "phc.h" + #include "pi.h" +-#include "pmc_common.h" ++#include "pmc_agent.h" + #include "print.h" + #include "servo.h" + #include "sk.h" +diff --git a/pmc_agent.c b/pmc_agent.c +new file mode 100644 +index 0000000..774e94d +--- /dev/null ++++ b/pmc_agent.c +@@ -0,0 +1,361 @@ ++/** ++ * @file pmc_agent.c ++ * @note Copyright (C) 2012 Richard Cochran ++ * @note Copyright (C) 2013 Miroslav Lichvar ++ * ++ * 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 2 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, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++#include ++#include ++ ++#include "notification.h" ++#include "pmc_agent.h" ++#include "print.h" ++#include "util.h" ++ ++#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) ++#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ ++/* Note that PMC_SUBSCRIBE_DURATION has to be longer than ++ * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is ++ * renewed. ++ */ ++ ++static void send_subscription(struct pmc_node *node) ++{ ++ struct subscribe_events_np sen; ++ ++ memset(&sen, 0, sizeof(sen)); ++ sen.duration = PMC_SUBSCRIBE_DURATION; ++ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++} ++ ++static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) ++{ ++ if (!node->clock_identity_set) ++ return 1; ++ return cid_eq(&node->clock_identity, ++ &msg->header.sourcePortIdentity.clockIdentity); ++} ++ ++static int is_msg_mgt(struct ptp_message *msg) ++{ ++ struct TLV *tlv; ++ ++ if (msg_type(msg) != MANAGEMENT) ++ return 0; ++ if (management_action(msg) != RESPONSE) ++ return 0; ++ if (msg_tlv_count(msg) != 1) ++ return 0; ++ tlv = (struct TLV *) msg->management.suffix; ++ if (tlv->type == TLV_MANAGEMENT) ++ return 1; ++ if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) ++ return -1; ++ return 0; ++} ++ ++int get_mgt_id(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->id; ++} ++ ++void *get_mgt_data(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->data; ++} ++ ++static int get_mgt_err_id(struct ptp_message *msg) ++{ ++ struct management_error_status *mgt; ++ ++ mgt = (struct management_error_status *)msg->management.suffix; ++ return mgt->id; ++} ++ ++/* Return values: ++ * 1: success ++ * 0: timeout ++ * -1: error reported by the other side ++ * -2: local error, fatal ++ */ ++static int run_pmc(struct pmc_node *node, int timeout, int ds_id, ++ struct ptp_message **msg) ++{ ++#define N_FD 1 ++ struct pollfd pollfd[N_FD]; ++ int cnt, res; ++ ++ while (1) { ++ pollfd[0].fd = pmc_get_transport_fd(node->pmc); ++ pollfd[0].events = POLLIN|POLLPRI; ++ if (!node->pmc_ds_requested && ds_id >= 0) ++ pollfd[0].events |= POLLOUT; ++ ++ cnt = poll(pollfd, N_FD, timeout); ++ if (cnt < 0) { ++ pr_err("poll failed"); ++ return -2; ++ } ++ if (!cnt) { ++ /* Request the data set again in the next run. */ ++ node->pmc_ds_requested = 0; ++ return 0; ++ } ++ ++ /* Send a new request if there are no pending messages. */ ++ if ((pollfd[0].revents & POLLOUT) && ++ !(pollfd[0].revents & (POLLIN|POLLPRI))) { ++ switch (ds_id) { ++ case TLV_SUBSCRIBE_EVENTS_NP: ++ send_subscription(node); ++ break; ++ default: ++ pmc_send_get_action(node->pmc, ds_id); ++ break; ++ } ++ node->pmc_ds_requested = 1; ++ } ++ ++ if (!(pollfd[0].revents & (POLLIN|POLLPRI))) ++ continue; ++ ++ *msg = pmc_recv(node->pmc); ++ ++ if (!*msg) ++ continue; ++ ++ if (!check_clock_identity(node, *msg)) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ ++ res = is_msg_mgt(*msg); ++ if (res < 0 && get_mgt_err_id(*msg) == ds_id) { ++ node->pmc_ds_requested = 0; ++ return -1; ++ } ++ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || ++ get_mgt_id(*msg) != ds_id) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ node->pmc_ds_requested = 0; ++ return 1; ++ } ++} ++ ++int run_pmc_wait_sync(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ Enumeration8 portState; ++ void *data; ++ int res; ++ ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ data = get_mgt_data(msg); ++ portState = ((struct portDS *)data)->portState; ++ msg_put(msg); ++ ++ switch (portState) { ++ case PS_MASTER: ++ case PS_SLAVE: ++ return 1; ++ } ++ /* try to get more data sets (for other ports) */ ++ node->pmc_ds_requested = 1; ++ } ++} ++ ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct timePropertiesDS *tds; ++ ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ tds = (struct timePropertiesDS *)get_mgt_data(msg); ++ if (tds->flags & PTP_TIMESCALE) { ++ node->sync_offset = tds->currentUtcOffset; ++ if (tds->flags & LEAP_61) ++ node->leap = 1; ++ else if (tds->flags & LEAP_59) ++ node->leap = -1; ++ else ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ tds->flags & TIME_TRACEABLE; ++ } else { ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; ++ } ++ msg_put(msg); ++ return 1; ++} ++ ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct defaultDS *dds; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ res = dds->numberPorts; ++ msg_put(msg); ++ return res; ++} ++ ++int run_pmc_subscribe(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (res <= 0) ++ return res; ++ msg_put(msg); ++ return 1; ++} ++ ++void run_pmc_events(struct pmc_node *node) ++{ ++ struct ptp_message *msg; ++ ++ run_pmc(node, 0, -1, &msg); ++} ++ ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) ++{ ++ struct ptp_message *msg; ++ int res, len; ++ struct port_properties_np *ppn; ++ ++ pmc_target_port(node->pmc, port); ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ if (res <= 0) ++ goto out; ++ ++ ppn = get_mgt_data(msg); ++ if (ppn->portIdentity.portNumber != port) { ++ msg_put(msg); ++ continue; ++ } ++ ++ *state = ppn->port_state; ++ *tstamping = ppn->timestamping; ++ len = ppn->interface.length; ++ if (len > IFNAMSIZ - 1) ++ len = IFNAMSIZ - 1; ++ memcpy(iface, ppn->interface.text, len); ++ iface[len] = '\0'; ++ ++ msg_put(msg); ++ res = 1; ++ break; ++ } ++out: ++ pmc_target_all(node->pmc); ++ return res; ++} ++ ++int run_pmc_clock_identity(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ struct defaultDS *dds; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ memcpy(&node->clock_identity, &dds->clockIdentity, ++ sizeof(struct ClockIdentity)); ++ node->clock_identity_set = 1; ++ msg_put(msg); ++ return 1; ++} ++ ++/* Returns: -1 in case of error, 0 otherwise */ ++int update_pmc_node(struct pmc_node *node, int subscribe) ++{ ++ struct timespec tp; ++ uint64_t ts; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &tp)) { ++ pr_err("failed to read clock: %m"); ++ return -1; ++ } ++ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; ++ ++ if (node->pmc && ++ !(ts > node->pmc_last_update && ++ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (subscribe) ++ run_pmc_subscribe(node, 0); ++ if (run_pmc_get_utc_offset(node, 0) > 0) ++ node->pmc_last_update = ts; ++ } ++ ++ return 0; ++} ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) ++{ ++ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, ++ config_get_int(cfg, NULL, "domainNumber"), ++ config_get_int(cfg, NULL, "transportSpecific") << 4, 1); ++ if (!node->pmc) { ++ pr_err("failed to create pmc"); ++ return -1; ++ } ++ node->recv_subscribed = recv_subscribed; ++ ++ return 0; ++} ++ ++void close_pmc_node(struct pmc_node *node) ++{ ++ if (!node->pmc) ++ return; ++ ++ pmc_destroy(node->pmc); ++ node->pmc = NULL; ++} +diff --git a/pmc_agent.h b/pmc_agent.h +new file mode 100644 +index 0000000..90245b1 +--- /dev/null ++++ b/pmc_agent.h +@@ -0,0 +1,62 @@ ++/** ++ * @file pmc_agent.h ++ * @brief Client code for making PTP management requests. ++ * @note Copyright (C) 2013 Miroslav Lichvar ++ * @note Copyright (C) 2020 Richard Cochran ++ * ++ * 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 2 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, write to the Free Software Foundation, Inc., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef HAVE_PMC_AGENT_H ++#define HAVE_PMC_AGENT_H ++ ++#include "pmc_common.h" ++ ++struct pmc_node; ++ ++typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++ struct ptp_message *msg, ++ int excluded); ++ ++struct pmc_node { ++ struct pmc *pmc; ++ int pmc_ds_requested; ++ uint64_t pmc_last_update; ++ int sync_offset; ++ int leap; ++ int utc_offset_traceable; ++ int clock_identity_set; ++ struct ClockIdentity clock_identity; ++ pmc_node_recv_subscribed_t *recv_subscribed; ++}; ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed); ++void close_pmc_node(struct pmc_node *node); ++int update_pmc_node(struct pmc_node *node, int subscribe); ++int run_pmc_subscribe(struct pmc_node *node, int timeout); ++int run_pmc_clock_identity(struct pmc_node *node, int timeout); ++int run_pmc_wait_sync(struct pmc_node *node, int timeout); ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout); ++void run_pmc_events(struct pmc_node *node); ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++int get_mgt_id(struct ptp_message *msg); ++void *get_mgt_data(struct ptp_message *msg); ++ ++#endif ++ +diff --git a/pmc_common.c b/pmc_common.c +index c9cdf18..a117904 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -18,8 +18,6 @@ + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include +-#include +-#include + #include + #include + #include +@@ -29,7 +27,6 @@ + #include "print.h" + #include "tlv.h" + #include "transport.h" +-#include "util.h" + #include "pmc_common.h" + + #define BAD_ACTION -1 +@@ -58,13 +55,6 @@ + /* Includes one extra byte to make length even. */ + #define EMPTY_PTP_TEXT 2 + +-#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) +-#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ +-/* Note that PMC_SUBSCRIBE_DURATION has to be longer than +- * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is +- * renewed. +- */ +- + static void do_get_action(struct pmc *pmc, int action, int index, char *str); + static void do_set_action(struct pmc *pmc, int action, int index, char *str); + static void not_supported(struct pmc *pmc, int action, int index, char *str); +@@ -720,331 +710,3 @@ int pmc_do_command(struct pmc *pmc, char *str) + + return 0; + } +- +-static void send_subscription(struct pmc_node *node) +-{ +- struct subscribe_events_np sen; +- +- memset(&sen, 0, sizeof(sen)); +- sen.duration = PMC_SUBSCRIBE_DURATION; +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; +- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); +-} +- +-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) +-{ +- if (!node->clock_identity_set) +- return 1; +- return cid_eq(&node->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); +-} +- +-static int is_msg_mgt(struct ptp_message *msg) +-{ +- struct TLV *tlv; +- +- if (msg_type(msg) != MANAGEMENT) +- return 0; +- if (management_action(msg) != RESPONSE) +- return 0; +- if (msg_tlv_count(msg) != 1) +- return 0; +- tlv = (struct TLV *) msg->management.suffix; +- if (tlv->type == TLV_MANAGEMENT) +- return 1; +- if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) +- return -1; +- return 0; +-} +- +-int get_mgt_id(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->id; +-} +- +-void *get_mgt_data(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->data; +-} +- +-static int get_mgt_err_id(struct ptp_message *msg) +-{ +- struct management_error_status *mgt; +- +- mgt = (struct management_error_status *)msg->management.suffix; +- return mgt->id; +-} +- +-/* Return values: +- * 1: success +- * 0: timeout +- * -1: error reported by the other side +- * -2: local error, fatal +- */ +-static int run_pmc(struct pmc_node *node, int timeout, int ds_id, +- struct ptp_message **msg) +-{ +-#define N_FD 1 +- struct pollfd pollfd[N_FD]; +- int cnt, res; +- +- while (1) { +- pollfd[0].fd = pmc_get_transport_fd(node->pmc); +- pollfd[0].events = POLLIN|POLLPRI; +- if (!node->pmc_ds_requested && ds_id >= 0) +- pollfd[0].events |= POLLOUT; +- +- cnt = poll(pollfd, N_FD, timeout); +- if (cnt < 0) { +- pr_err("poll failed"); +- return -2; +- } +- if (!cnt) { +- /* Request the data set again in the next run. */ +- node->pmc_ds_requested = 0; +- return 0; +- } +- +- /* Send a new request if there are no pending messages. */ +- if ((pollfd[0].revents & POLLOUT) && +- !(pollfd[0].revents & (POLLIN|POLLPRI))) { +- switch (ds_id) { +- case TLV_SUBSCRIBE_EVENTS_NP: +- send_subscription(node); +- break; +- default: +- pmc_send_get_action(node->pmc, ds_id); +- break; +- } +- node->pmc_ds_requested = 1; +- } +- +- if (!(pollfd[0].revents & (POLLIN|POLLPRI))) +- continue; +- +- *msg = pmc_recv(node->pmc); +- +- if (!*msg) +- continue; +- +- if (!check_clock_identity(node, *msg)) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- +- res = is_msg_mgt(*msg); +- if (res < 0 && get_mgt_err_id(*msg) == ds_id) { +- node->pmc_ds_requested = 0; +- return -1; +- } +- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || +- get_mgt_id(*msg) != ds_id) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- node->pmc_ds_requested = 0; +- return 1; +- } +-} +- +-int run_pmc_wait_sync(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- Enumeration8 portState; +- void *data; +- int res; +- +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- data = get_mgt_data(msg); +- portState = ((struct portDS *)data)->portState; +- msg_put(msg); +- +- switch (portState) { +- case PS_MASTER: +- case PS_SLAVE: +- return 1; +- } +- /* try to get more data sets (for other ports) */ +- node->pmc_ds_requested = 1; +- } +-} +- +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct timePropertiesDS *tds; +- +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- tds = (struct timePropertiesDS *)get_mgt_data(msg); +- if (tds->flags & PTP_TIMESCALE) { +- node->sync_offset = tds->currentUtcOffset; +- if (tds->flags & LEAP_61) +- node->leap = 1; +- else if (tds->flags & LEAP_59) +- node->leap = -1; +- else +- node->leap = 0; +- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && +- tds->flags & TIME_TRACEABLE; +- } else { +- node->sync_offset = 0; +- node->leap = 0; +- node->utc_offset_traceable = 0; +- } +- msg_put(msg); +- return 1; +-} +- +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct defaultDS *dds; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- res = dds->numberPorts; +- msg_put(msg); +- return res; +-} +- +-int run_pmc_subscribe(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (res <= 0) +- return res; +- msg_put(msg); +- return 1; +-} +- +-void run_pmc_events(struct pmc_node *node) +-{ +- struct ptp_message *msg; +- +- run_pmc(node, 0, -1, &msg); +-} +- +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) +-{ +- struct ptp_message *msg; +- int res, len; +- struct port_properties_np *ppn; +- +- pmc_target_port(node->pmc, port); +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); +- if (res <= 0) +- goto out; +- +- ppn = get_mgt_data(msg); +- if (ppn->portIdentity.portNumber != port) { +- msg_put(msg); +- continue; +- } +- +- *state = ppn->port_state; +- *tstamping = ppn->timestamping; +- len = ppn->interface.length; +- if (len > IFNAMSIZ - 1) +- len = IFNAMSIZ - 1; +- memcpy(iface, ppn->interface.text, len); +- iface[len] = '\0'; +- +- msg_put(msg); +- res = 1; +- break; +- } +-out: +- pmc_target_all(node->pmc); +- return res; +-} +- +-int run_pmc_clock_identity(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- struct defaultDS *dds; +- int res; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- memcpy(&node->clock_identity, &dds->clockIdentity, +- sizeof(struct ClockIdentity)); +- node->clock_identity_set = 1; +- msg_put(msg); +- return 1; +-} +- +-/* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_node *node, int subscribe) +-{ +- struct timespec tp; +- uint64_t ts; +- +- if (clock_gettime(CLOCK_MONOTONIC, &tp)) { +- pr_err("failed to read clock: %m"); +- return -1; +- } +- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; +- +- if (node->pmc && +- !(ts > node->pmc_last_update && +- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { +- if (subscribe) +- run_pmc_subscribe(node, 0); +- if (run_pmc_get_utc_offset(node, 0) > 0) +- node->pmc_last_update = ts; +- } +- +- return 0; +-} +- +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) +-{ +- node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, +- config_get_int(cfg, NULL, "domainNumber"), +- config_get_int(cfg, NULL, "transportSpecific") << 4, 1); +- if (!node->pmc) { +- pr_err("failed to create pmc"); +- return -1; +- } +- node->recv_subscribed = recv_subscribed; +- +- return 0; +-} +- +-void close_pmc_node(struct pmc_node *node) +-{ +- if (!node->pmc) +- return; +- +- pmc_destroy(node->pmc); +- node->pmc = NULL; +-} +diff --git a/pmc_common.h b/pmc_common.h +index 476ccea..8bea2e0 100644 +--- a/pmc_common.h ++++ b/pmc_common.h +@@ -50,38 +50,4 @@ void pmc_target_all(struct pmc *pmc); + const char *pmc_action_string(int action); + int pmc_do_command(struct pmc *pmc, char *str); + +-struct pmc_node; +- +-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, +- struct ptp_message *msg, +- int excluded); +- +-struct pmc_node { +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; +- int sync_offset; +- int leap; +- int utc_offset_traceable; +- int clock_identity_set; +- struct ClockIdentity clock_identity; +- pmc_node_recv_subscribed_t *recv_subscribed; +-}; +- +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed); +-void close_pmc_node(struct pmc_node *node); +-int update_pmc_node(struct pmc_node *node, int subscribe); +-int run_pmc_subscribe(struct pmc_node *node, int timeout); +-int run_pmc_clock_identity(struct pmc_node *node, int timeout); +-int run_pmc_wait_sync(struct pmc_node *node, int timeout); +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout); +-void run_pmc_events(struct pmc_node *node); +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface); +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); +-int get_mgt_id(struct ptp_message *msg); +-void *get_mgt_data(struct ptp_message *msg); +- + #endif +-- +2.25.1 + +From 82258917b8de7110545f3d4f99d3ac88a609f019 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:47:36 -0300 +Subject: [PATCH 15/47] pmc_agent: Rename pmc_node to something more + descriptive. + +Signed-off-by: Richard Cochran + +[commit bb6865cdf59572fcb09c11d549828269281c6841 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 4 ++-- + pmc_agent.c | 26 +++++++++++++------------- + pmc_agent.h | 26 +++++++++++++------------- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 648ba61..74ee9d1 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -103,7 +103,7 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; +- struct pmc_node node; ++ struct pmc_agent node; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -813,7 +813,7 @@ static int clock_compute_state(struct phc2sys_private *priv, + #define node_to_phc2sys(node) \ + container_of(node, struct phc2sys_private, node) + +-static int phc2sys_recv_subscribed(struct pmc_node *node, ++static int phc2sys_recv_subscribed(struct pmc_agent *node, + struct ptp_message *msg, int excluded) + { + struct phc2sys_private *priv = node_to_phc2sys(node); +diff --git a/pmc_agent.c b/pmc_agent.c +index 774e94d..e83895c 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -32,7 +32,7 @@ + * renewed. + */ + +-static void send_subscription(struct pmc_node *node) ++static void send_subscription(struct pmc_agent *node) + { + struct subscribe_events_np sen; + +@@ -42,7 +42,7 @@ static void send_subscription(struct pmc_node *node) + pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) ++static int check_clock_identity(struct pmc_agent *node, struct ptp_message *msg) + { + if (!node->clock_identity_set) + return 1; +@@ -98,7 +98,7 @@ static int get_mgt_err_id(struct ptp_message *msg) + * -1: error reported by the other side + * -2: local error, fatal + */ +-static int run_pmc(struct pmc_node *node, int timeout, int ds_id, ++static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + struct ptp_message **msg) + { + #define N_FD 1 +@@ -166,7 +166,7 @@ static int run_pmc(struct pmc_node *node, int timeout, int ds_id, + } + } + +-int run_pmc_wait_sync(struct pmc_node *node, int timeout) ++int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + Enumeration8 portState; +@@ -192,7 +192,7 @@ int run_pmc_wait_sync(struct pmc_node *node, int timeout) + } + } + +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++int run_pmc_get_utc_offset(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -222,7 +222,7 @@ int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + return 1; + } + +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -238,7 +238,7 @@ int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + return res; + } + +-int run_pmc_subscribe(struct pmc_node *node, int timeout) ++int run_pmc_subscribe(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -250,14 +250,14 @@ int run_pmc_subscribe(struct pmc_node *node, int timeout) + return 1; + } + +-void run_pmc_events(struct pmc_node *node) ++void run_pmc_events(struct pmc_agent *node) + { + struct ptp_message *msg; + + run_pmc(node, 0, -1, &msg); + } + +-int run_pmc_port_properties(struct pmc_node *node, int timeout, ++int run_pmc_port_properties(struct pmc_agent *node, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface) + { +@@ -294,7 +294,7 @@ out: + return res; + } + +-int run_pmc_clock_identity(struct pmc_node *node, int timeout) ++int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + struct defaultDS *dds; +@@ -313,7 +313,7 @@ int run_pmc_clock_identity(struct pmc_node *node, int timeout) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_node *node, int subscribe) ++int update_pmc_node(struct pmc_agent *node, int subscribe) + { + struct timespec tp; + uint64_t ts; +@@ -336,7 +336,7 @@ int update_pmc_node(struct pmc_node *node, int subscribe) + return 0; + } + +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed) + { + node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, +@@ -351,7 +351,7 @@ int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, + return 0; + } + +-void close_pmc_node(struct pmc_node *node) ++void close_pmc_node(struct pmc_agent *node) + { + if (!node->pmc) + return; +diff --git a/pmc_agent.h b/pmc_agent.h +index 90245b1..10ef4b5 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -24,13 +24,13 @@ + + #include "pmc_common.h" + +-struct pmc_node; ++struct pmc_agent; + +-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++typedef int pmc_node_recv_subscribed_t(struct pmc_agent *agent, + struct ptp_message *msg, + int excluded); + +-struct pmc_node { ++struct pmc_agent { + struct pmc *pmc; + int pmc_ds_requested; + uint64_t pmc_last_update; +@@ -42,19 +42,19 @@ struct pmc_node { + pmc_node_recv_subscribed_t *recv_subscribed; + }; + +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed); +-void close_pmc_node(struct pmc_node *node); +-int update_pmc_node(struct pmc_node *node, int subscribe); +-int run_pmc_subscribe(struct pmc_node *node, int timeout); +-int run_pmc_clock_identity(struct pmc_node *node, int timeout); +-int run_pmc_wait_sync(struct pmc_node *node, int timeout); +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout); +-void run_pmc_events(struct pmc_node *node); +-int run_pmc_port_properties(struct pmc_node *node, int timeout, ++void close_pmc_node(struct pmc_agent *agent); ++int update_pmc_node(struct pmc_agent *agent, int subscribe); ++int run_pmc_subscribe(struct pmc_agent *agent, int timeout); ++int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); ++int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); ++int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); ++void run_pmc_events(struct pmc_agent *agent); ++int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); + int get_mgt_id(struct ptp_message *msg); + void *get_mgt_data(struct ptp_message *msg); + +-- +2.25.1 + +From f6d7bb0a62f15fcca0343c42891f7e056f502949 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:55:29 -0300 +Subject: [PATCH 16/47] pmc_agent: Hide the implementation. + +The PMC agent's implementation should not be exposed to its users. This +patch hides the details and provides a method to create an instance. In +addition, the signature of the receive callback is made generic, removing +the container_of pattern meant for sub-classing modules. + +Signed-off-by: Richard Cochran + +[commit 826698791769e0ba4431fe98f02d4d09c109542e upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 76 +++++++++++++++++++++++++++++------------------------ + pmc_agent.c | 58 +++++++++++++++++++++++++++++++++++----- + pmc_agent.h | 62 +++++++++++++++++++++++++++++++------------ + 3 files changed, 138 insertions(+), 58 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 74ee9d1..037b1b9 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -103,7 +103,7 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; +- struct pmc_agent node; ++ struct pmc_agent *node; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -306,7 +306,7 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + + LIST_FOREACH(p, &priv->ports, list) { + if (p->clock == clock) { +- ret = run_pmc_port_properties(&priv->node, 1000, p->number, ++ ret = run_pmc_port_properties(priv->node, 1000, p->number, + &state, ×tamping, + iface); + if (ret > 0) +@@ -641,7 +641,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + + if (src == CLOCK_INVALID) { + /* The sync offset can't be applied with PPS alone. */ +- priv->node.sync_offset = 0; ++ pmc_agent_set_sync_offset(priv->node, 0); + } else { + enable_pps_output(priv->master->clkid); + } +@@ -672,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc_node(&priv->node, 0) < 0) ++ if (update_pmc_node(priv->node, 0) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -710,15 +710,15 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); +- if (update_pmc_node(&priv->node, subscriptions) < 0) ++ if (update_pmc_node(priv->node, subscriptions) < 0) + continue; + + if (subscriptions) { +- run_pmc_events(&priv->node); ++ run_pmc_events(priv->node); + if (priv->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ +- if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + continue; + } +@@ -810,13 +810,10 @@ static int clock_compute_state(struct phc2sys_private *priv, + return state; + } + +-#define node_to_phc2sys(node) \ +- container_of(node, struct phc2sys_private, node) +- +-static int phc2sys_recv_subscribed(struct pmc_agent *node, +- struct ptp_message *msg, int excluded) ++static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, ++ int excluded) + { +- struct phc2sys_private *priv = node_to_phc2sys(node); ++ struct phc2sys_private *priv = (struct phc2sys_private *) context; + int mgt_id, state; + struct portDS *pds; + struct port *port; +@@ -863,7 +860,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + while (1) { + if (!is_running()) + return -1; +- res = run_pmc_clock_identity(&priv->node, 1000); ++ res = run_pmc_clock_identity(priv->node, 1000); + if (res < 0) + return -1; + if (res > 0) +@@ -872,20 +869,20 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + pr_notice("Waiting for ptp4l..."); + } + +- number_ports = run_pmc_get_number_ports(&priv->node, 1000); ++ number_ports = run_pmc_get_number_ports(priv->node, 1000); + if (number_ports <= 0) { + pr_err("failed to get number of ports"); + return -1; + } + +- res = run_pmc_subscribe(&priv->node, 1000); ++ res = run_pmc_subscribe(priv->node, 1000); + if (res <= 0) { + pr_err("failed to subscribe"); + return -1; + } + + for (i = 1; i <= number_ports; i++) { +- res = run_pmc_port_properties(&priv->node, 1000, i, &state, ++ res = run_pmc_port_properties(priv->node, 1000, i, &state, + ×tamping, iface); + if (res == -1) { + /* port does not exist, ignore the port */ +@@ -922,7 +919,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + return -1; + } +@@ -933,9 +930,9 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) + { +- int clock_leap, node_leap = priv->node.leap; ++ int clock_leap, node_leap = pmc_agent_get_leap(priv->node); + +- clock->sync_offset = priv->node.sync_offset; ++ clock->sync_offset = pmc_agent_get_sync_offset(priv->node); + + if ((node_leap || clock->leap_set) && + clock->is_utc != priv->master->is_utc) { +@@ -976,7 +973,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + } + } + +- if (priv->node.utc_offset_traceable && ++ if (pmc_agent_utc_offset_traceable(priv->node) && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); +@@ -1032,11 +1029,13 @@ int main(int argc, char *argv[]) + { + char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL; + char uds_local[MAX_IFNAME_SIZE + 1]; ++ int autocfg = 0, c, domain_number = 0, index, ntpshm_segment, offset; ++ int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0; ++ int wait_sync = 0; + struct clock *src, *dst; + struct config *cfg; + struct option *opts; +- int autocfg = 0, c, domain_number = 0, default_sync = 1, index, ntpshm_segment; +- int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0, wait_sync = 0; ++ int default_sync = 1; + double phc_rate, tmp; + struct phc2sys_private priv = { + .phc_readings = 5, +@@ -1049,6 +1048,10 @@ int main(int argc, char *argv[]) + if (!cfg) { + return -1; + } ++ priv.node = pmc_agent_create(); ++ if (!priv.node) { ++ return -1; ++ } + + opts = config_long_options(cfg); + +@@ -1140,9 +1143,10 @@ int main(int argc, char *argv[]) + goto end; + break; + case 'O': +- if (get_arg_val_i(c, optarg, &priv.node.sync_offset, +- INT_MIN, INT_MAX)) ++ if (get_arg_val_i(c, optarg, &offset, INT_MIN, INT_MAX)) { + goto end; ++ } ++ pmc_agent_set_sync_offset(priv.node, offset); + priv.forced_sync_offset = -1; + break; + case 'L': +@@ -1271,8 +1275,8 @@ int main(int argc, char *argv[]) + getpid()); + + if (autocfg) { +- if (init_pmc_node(cfg, &priv.node, uds_local, +- phc2sys_recv_subscribed)) ++ if (init_pmc_node(cfg, priv.node, uds_local, ++ phc2sys_recv_subscribed, &priv)) + goto end; + if (auto_init_ports(&priv, rt) < 0) + goto end; +@@ -1309,12 +1313,12 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- if (init_pmc_node(cfg, &priv.node, uds_local, +- phc2sys_recv_subscribed)) ++ if (init_pmc_node(cfg, priv.node, uds_local, ++ phc2sys_recv_subscribed, &priv)) + goto end; + + while (is_running()) { +- r = run_pmc_wait_sync(&priv.node, 1000); ++ r = run_pmc_wait_sync(priv.node, 1000); + if (r < 0) + goto end; + if (r > 0) +@@ -1324,7 +1328,7 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = run_pmc_get_utc_offset(&priv.node, 1000); ++ r = run_pmc_get_utc_offset(priv.node, 1000); + if (r <= 0) { + pr_err("failed to get UTC offset"); + goto end; +@@ -1333,8 +1337,10 @@ int main(int argc, char *argv[]) + + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || +- src->clkid == CLOCK_INVALID) +- close_pmc_node(&priv.node); ++ src->clkid == CLOCK_INVALID) { ++ pmc_agent_destroy(priv.node); ++ priv.node = NULL; ++ } + } + + if (pps_fd >= 0) { +@@ -1347,7 +1353,9 @@ int main(int argc, char *argv[]) + } + + end: +- close_pmc_node(&priv.node); ++ if (priv.node) { ++ pmc_agent_destroy(priv.node); ++ } + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +diff --git a/pmc_agent.c b/pmc_agent.c +index e83895c..8ccafe2 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -19,6 +19,7 @@ + */ + #include + #include ++#include + + #include "notification.h" + #include "pmc_agent.h" +@@ -32,6 +33,22 @@ + * renewed. + */ + ++struct pmc_agent { ++ struct pmc *pmc; ++ uint64_t pmc_last_update; ++ ++ struct ClockIdentity clock_identity; ++ int clock_identity_set; ++ int leap; ++ int pmc_ds_requested; ++ int sync_offset; ++ int utc_offset_traceable; ++ ++ /* Callback on message reception */ ++ pmc_node_recv_subscribed_t *recv_subscribed; ++ void *recv_context; ++}; ++ + static void send_subscription(struct pmc_agent *node) + { + struct subscribe_events_np sen; +@@ -155,7 +172,8 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + node->pmc_ds_requested = 0; + return -1; + } +- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || ++ if (res <= 0 || ++ node->recv_subscribed(node->recv_context, *msg, ds_id) || + get_mgt_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; +@@ -337,7 +355,7 @@ int update_pmc_node(struct pmc_agent *node, int subscribe) + } + + int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) ++ pmc_node_recv_subscribed_t *recv_subscribed, void *context) + { + node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, + config_get_int(cfg, NULL, "domainNumber"), +@@ -347,15 +365,41 @@ int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + return -1; + } + node->recv_subscribed = recv_subscribed; ++ node->recv_context = context; + + return 0; + } + +-void close_pmc_node(struct pmc_agent *node) ++struct pmc_agent *pmc_agent_create(void) ++{ ++ struct pmc_agent *agent = calloc(1, sizeof(*agent)); ++ return agent; ++} ++ ++void pmc_agent_destroy(struct pmc_agent *agent) ++{ ++ if (agent->pmc) { ++ pmc_destroy(agent->pmc); ++ } ++ free(agent); ++} ++ ++int pmc_agent_get_leap(struct pmc_agent *agent) + { +- if (!node->pmc) +- return; ++ return agent->leap; ++} ++ ++int pmc_agent_get_sync_offset(struct pmc_agent *agent) ++{ ++ return agent->sync_offset; ++} + +- pmc_destroy(node->pmc); +- node->pmc = NULL; ++void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) ++{ ++ agent->sync_offset = offset; ++} ++ ++bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) ++{ ++ return agent->utc_offset_traceable; + } +diff --git a/pmc_agent.h b/pmc_agent.h +index 10ef4b5..c0b4525 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -22,29 +22,17 @@ + #ifndef HAVE_PMC_AGENT_H + #define HAVE_PMC_AGENT_H + ++#include ++ + #include "pmc_common.h" + + struct pmc_agent; + +-typedef int pmc_node_recv_subscribed_t(struct pmc_agent *agent, +- struct ptp_message *msg, ++typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int excluded); + +-struct pmc_agent { +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; +- int sync_offset; +- int leap; +- int utc_offset_traceable; +- int clock_identity_set; +- struct ClockIdentity clock_identity; +- pmc_node_recv_subscribed_t *recv_subscribed; +-}; +- + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed); +-void close_pmc_node(struct pmc_agent *agent); ++ pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int update_pmc_node(struct pmc_agent *agent, int subscribe); + int run_pmc_subscribe(struct pmc_agent *agent, int timeout); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); +@@ -58,5 +46,45 @@ int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); + int get_mgt_id(struct ptp_message *msg); + void *get_mgt_data(struct ptp_message *msg); + +-#endif + ++/** ++ * Creates an instance of a PMC agent. ++ * @return Pointer to a PMC instance on success, NULL otherwise. ++ */ ++struct pmc_agent *pmc_agent_create(void); ++ ++/** ++ * Destroys an instance of a PMC agent. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ */ ++void pmc_agent_destroy(struct pmc_agent *agent); ++ ++/** ++ * Gets the current leap adjustment. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return The leap adjustment in seconds, either 1, 0, or -1. ++ */ ++int pmc_agent_get_leap(struct pmc_agent *agent); ++ ++/** ++ * Gets the TAI-UTC offset. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return Current offset in seconds. ++ */ ++int pmc_agent_get_sync_offset(struct pmc_agent *agent); ++ ++/** ++ * Sets the TAI-UTC offset. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param offset Desired offset in seconds. ++ */ ++void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); ++ ++/** ++ * Tests whether the current UTC offset is traceable. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return True is the offset is traceable, false otherwise. ++ */ ++bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent); ++ ++#endif +-- +2.25.1 + +From 4ebb69f5c55e7f1f08d1a73df87d42fe70147ec9 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:58:09 -0300 +Subject: [PATCH 17/47] Find a better home for the management TLV ID helper + function. + +Signed-off-by: Richard Cochran + +[commit d95bb9f9d62f4f372934905e97e052aa68dcfc58 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + msg.h | 12 ++++++++++++ + phc2sys.c | 2 +- + pmc_agent.c | 10 +--------- + pmc_agent.h | 1 - + 4 files changed, 14 insertions(+), 11 deletions(-) + +diff --git a/msg.h b/msg.h +index e71d3ce..b600ff0 100644 +--- a/msg.h ++++ b/msg.h +@@ -247,6 +247,18 @@ static inline uint8_t management_action(struct ptp_message *m) + return m->management.flags & 0x0f; + } + ++/** ++ * Obtain the ID field from the TLV in a management message. ++ * @param m A management message. ++ * @return The value of the ID field. ++ */ ++static inline int management_tlv_id(struct ptp_message *m) ++{ ++ struct management_tlv *mgt; ++ mgt = (struct management_tlv *) m->management.suffix; ++ return mgt->id; ++} ++ + /** + * Test a given bit in a message's flag field. + * @param m Message to test. +diff --git a/phc2sys.c b/phc2sys.c +index 037b1b9..1f74f27 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -819,7 +819,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + struct port *port; + struct clock *clock; + +- mgt_id = get_mgt_id(msg); ++ mgt_id = management_tlv_id(msg); + if (mgt_id == excluded) + return 0; + switch (mgt_id) { +diff --git a/pmc_agent.c b/pmc_agent.c +index 8ccafe2..6dfb3ca 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -85,14 +85,6 @@ static int is_msg_mgt(struct ptp_message *msg) + return 0; + } + +-int get_mgt_id(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->id; +-} +- + void *get_mgt_data(struct ptp_message *msg) + { + struct management_tlv *mgt; +@@ -174,7 +166,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + } + if (res <= 0 || + node->recv_subscribed(node->recv_context, *msg, ds_id) || +- get_mgt_id(*msg) != ds_id) { ++ management_tlv_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; + continue; +diff --git a/pmc_agent.h b/pmc_agent.h +index c0b4525..09249ff 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -43,7 +43,6 @@ int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); + int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); +-int get_mgt_id(struct ptp_message *msg); + void *get_mgt_data(struct ptp_message *msg); + + +-- +2.25.1 + +From 6e4f8ea8531b7678a44a9b3ed021fda94eccdc27 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:59:57 -0300 +Subject: [PATCH 18/47] Find a better home for the management TLV data helper + function. + +Signed-off-by: Richard Cochran + +[commit 5dd47c873cae8e0a2815b43c1ef3a86b9aca9dac upstream] +Signed-off-by: Andre Mauricio Zelak +--- + msg.h | 12 ++++++++++++ + phc2sys.c | 2 +- + pmc_agent.c | 18 +++++------------- + pmc_agent.h | 1 - + 4 files changed, 18 insertions(+), 15 deletions(-) + +diff --git a/msg.h b/msg.h +index b600ff0..84380da 100644 +--- a/msg.h ++++ b/msg.h +@@ -247,6 +247,18 @@ static inline uint8_t management_action(struct ptp_message *m) + return m->management.flags & 0x0f; + } + ++/** ++ * Obtain the data field from the TLV in a management message. ++ * @param m A management message. ++ * @return A pointer to the TLV data field. ++ */ ++static inline void *management_tlv_data(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->data; ++} ++ + /** + * Obtain the ID field from the TLV in a management message. + * @param m A management message. +diff --git a/phc2sys.c b/phc2sys.c +index 1f74f27..280e249 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -824,7 +824,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + return 0; + switch (mgt_id) { + case TLV_PORT_DATA_SET: +- pds = get_mgt_data(msg); ++ pds = management_tlv_data(msg); + port = port_get(priv, pds->portIdentity.portNumber); + if (!port) { + pr_info("received data for unknown port %s", +diff --git a/pmc_agent.c b/pmc_agent.c +index 6dfb3ca..6e9c023 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -85,14 +85,6 @@ static int is_msg_mgt(struct ptp_message *msg) + return 0; + } + +-void *get_mgt_data(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->data; +-} +- + static int get_mgt_err_id(struct ptp_message *msg) + { + struct management_error_status *mgt; +@@ -188,7 +180,7 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- data = get_mgt_data(msg); ++ data = management_tlv_data(msg); + portState = ((struct portDS *)data)->portState; + msg_put(msg); + +@@ -212,7 +204,7 @@ int run_pmc_get_utc_offset(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- tds = (struct timePropertiesDS *)get_mgt_data(msg); ++ tds = (struct timePropertiesDS *) management_tlv_data(msg); + if (tds->flags & PTP_TIMESCALE) { + node->sync_offset = tds->currentUtcOffset; + if (tds->flags & LEAP_61) +@@ -242,7 +234,7 @@ int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- dds = (struct defaultDS *)get_mgt_data(msg); ++ dds = (struct defaultDS *) management_tlv_data(msg); + res = dds->numberPorts; + msg_put(msg); + return res; +@@ -281,7 +273,7 @@ int run_pmc_port_properties(struct pmc_agent *node, int timeout, + if (res <= 0) + goto out; + +- ppn = get_mgt_data(msg); ++ ppn = management_tlv_data(msg); + if (ppn->portIdentity.portNumber != port) { + msg_put(msg); + continue; +@@ -314,7 +306,7 @@ int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- dds = (struct defaultDS *)get_mgt_data(msg); ++ dds = (struct defaultDS *) management_tlv_data(msg); + memcpy(&node->clock_identity, &dds->clockIdentity, + sizeof(struct ClockIdentity)); + node->clock_identity_set = 1; +diff --git a/pmc_agent.h b/pmc_agent.h +index 09249ff..f3a26fe 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -43,7 +43,6 @@ int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); + int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); +-void *get_mgt_data(struct ptp_message *msg); + + + /** +-- +2.25.1 + +From 95e4983c9ab517b9dda1faf171721f0dd877e076 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:04:11 -0300 +Subject: [PATCH 19/47] Introduce error codes for the run_pmc method. + +The run_pmc function is used by several of the PMC agent methods, but it +breaks the pattern of returning zero on success. However, the user facing +PMC agent methods will need to conform to the return code convention used +throughout the stack. + +In order to migrate to proper return codes, this patch replaces the hard +coded result values with macros so that the interface methods can translate +them to the required semantics of zero on success. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 802259bbe40faa5f8bdebab36e6fbcbc51c3c2a2 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index 6e9c023..22d9c5b 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -93,12 +93,11 @@ static int get_mgt_err_id(struct ptp_message *msg) + return mgt->id; + } + +-/* Return values: +- * 1: success +- * 0: timeout +- * -1: error reported by the other side +- * -2: local error, fatal +- */ ++#define RUN_PMC_OKAY 1 ++#define RUN_PMC_TMO 0 ++#define RUN_PMC_NODEV -1 ++#define RUN_PMC_INTR -2 ++ + static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + struct ptp_message **msg) + { +@@ -115,12 +114,12 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + cnt = poll(pollfd, N_FD, timeout); + if (cnt < 0) { + pr_err("poll failed"); +- return -2; ++ return RUN_PMC_INTR; + } + if (!cnt) { + /* Request the data set again in the next run. */ + node->pmc_ds_requested = 0; +- return 0; ++ return RUN_PMC_TMO; + } + + /* Send a new request if there are no pending messages. */ +@@ -154,7 +153,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + res = is_msg_mgt(*msg); + if (res < 0 && get_mgt_err_id(*msg) == ds_id) { + node->pmc_ds_requested = 0; +- return -1; ++ return RUN_PMC_NODEV; + } + if (res <= 0 || + node->recv_subscribed(node->recv_context, *msg, ds_id) || +@@ -164,7 +163,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + continue; + } + node->pmc_ds_requested = 0; +- return 1; ++ return RUN_PMC_OKAY; + } + } + +-- +2.25.1 + +From 8c1dd261683d27acba49e047d9f6da52dada3c98 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:08:01 -0300 +Subject: [PATCH 20/47] pmc_agent: Convert the subscribe method into the + canonical form. + +This patch renames the function to have the module prefix and corrects the +return code semantics. + +Signed-off-by: Richard Cochran +Reviewed-by: Jacob Keller +Reviewed-by: Vladimir Oltean + +[commit cc98d39f58adc1fd05db0038acfdcc5669f2ba8c upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 4 ++-- + pmc_agent.c | 48 +++++++++++++++++++++++++++++++++++------------- + pmc_agent.h | 9 ++++++++- + 3 files changed, 45 insertions(+), 16 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 280e249..f61e699 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -875,8 +875,8 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + return -1; + } + +- res = run_pmc_subscribe(priv->node, 1000); +- if (res <= 0) { ++ res = pmc_agent_subscribe(priv->node, 1000); ++ if (res) { + pr_err("failed to subscribe"); + return -1; + } +diff --git a/pmc_agent.c b/pmc_agent.c +index 22d9c5b..9c5eb71 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -17,6 +17,7 @@ + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ ++#include + #include + #include + #include +@@ -98,6 +99,26 @@ static int get_mgt_err_id(struct ptp_message *msg) + #define RUN_PMC_NODEV -1 + #define RUN_PMC_INTR -2 + ++static bool is_run_pmc_error(int code) ++{ ++ return code != RUN_PMC_OKAY; ++} ++ ++static int run_pmc_err2errno(int code) ++{ ++ switch (code) { ++ case RUN_PMC_TMO: ++ return -ETIMEDOUT; ++ case RUN_PMC_NODEV: ++ return -ENODEV; ++ case RUN_PMC_INTR: ++ return -EINTR; ++ case RUN_PMC_OKAY: ++ default: ++ return 0; ++ } ++} ++ + static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + struct ptp_message **msg) + { +@@ -239,18 +260,6 @@ int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + return res; + } + +-int run_pmc_subscribe(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (res <= 0) +- return res; +- msg_put(msg); +- return 1; +-} +- + void run_pmc_events(struct pmc_agent *node) + { + struct ptp_message *msg; +@@ -329,7 +338,7 @@ int update_pmc_node(struct pmc_agent *node, int subscribe) + !(ts > node->pmc_last_update && + ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (subscribe) +- run_pmc_subscribe(node, 0); ++ pmc_agent_subscribe(node, 0); + if (run_pmc_get_utc_offset(node, 0) > 0) + node->pmc_last_update = ts; + } +@@ -382,6 +391,19 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + agent->sync_offset = offset; + } + ++int pmc_agent_subscribe(struct pmc_agent *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ msg_put(msg); ++ return 0; ++} ++ + bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) + { + return agent->utc_offset_traceable; +diff --git a/pmc_agent.h b/pmc_agent.h +index f3a26fe..9dc684e 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -34,7 +34,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int update_pmc_node(struct pmc_agent *agent, int subscribe); +-int run_pmc_subscribe(struct pmc_agent *agent, int timeout); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); +@@ -78,6 +77,14 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent); + */ + void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); + ++/** ++ * Subscribes to push notifications of changes in port state. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_subscribe(struct pmc_agent *agent, int timeout); ++ + /** + * Tests whether the current UTC offset is traceable. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From 82a369b4fe44a7cea41fb0ccf408c02b1b6aa694 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:17:26 -0300 +Subject: [PATCH 21/47] pmc_agent: Simplify the update method. + +The main method that causes the PMC agent to update its status takes a flag +that results in different behavior when push notifications are active. +This patch simplifies the interface by letting the agent remember whether +or not the caller subscribed to the notifications in the first place. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 1126f8f67e853199f05a7c993c910ebc7807bd3d upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 6 ++++-- + pmc_agent.c | 32 ++++++++++++++++++++------------ + pmc_agent.h | 2 +- + 3 files changed, 25 insertions(+), 15 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index f61e699..b155961 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -672,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc_node(priv->node, 0) < 0) ++ if (update_pmc_node(priv->node) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -710,8 +710,10 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); +- if (update_pmc_node(priv->node, subscriptions) < 0) ++ ++ if (update_pmc_node(priv->node) < 0) { + continue; ++ } + + if (subscriptions) { + run_pmc_events(priv->node); +diff --git a/pmc_agent.c b/pmc_agent.c +index 9c5eb71..dd509af 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -42,6 +42,7 @@ struct pmc_agent { + int clock_identity_set; + int leap; + int pmc_ds_requested; ++ bool stay_subscribed; + int sync_offset; + int utc_offset_traceable; + +@@ -188,6 +189,19 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + } + } + ++static int renew_subscription(struct pmc_agent *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ msg_put(msg); ++ return 0; ++} ++ + int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +@@ -323,7 +337,7 @@ int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_agent *node, int subscribe) ++int update_pmc_node(struct pmc_agent *node) + { + struct timespec tp; + uint64_t ts; +@@ -337,8 +351,9 @@ int update_pmc_node(struct pmc_agent *node, int subscribe) + if (node->pmc && + !(ts > node->pmc_last_update && + ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { +- if (subscribe) +- pmc_agent_subscribe(node, 0); ++ if (node->stay_subscribed) { ++ renew_subscription(node, 0); ++ } + if (run_pmc_get_utc_offset(node, 0) > 0) + node->pmc_last_update = ts; + } +@@ -393,15 +408,8 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + + int pmc_agent_subscribe(struct pmc_agent *node, int timeout) + { +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (is_run_pmc_error(res)) { +- return run_pmc_err2errno(res); +- } +- msg_put(msg); +- return 0; ++ node->stay_subscribed = true; ++ return renew_subscription(node, timeout); + } + + bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) +diff --git a/pmc_agent.h b/pmc_agent.h +index 9dc684e..743818f 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -33,7 +33,7 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); +-int update_pmc_node(struct pmc_agent *agent, int subscribe); ++int update_pmc_node(struct pmc_agent *agent); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); +-- +2.25.1 + +From 731e8938953e56578007a679dbaa29e9471650ac Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:18:36 -0300 +Subject: [PATCH 22/47] pmc_agent: Simplify logic in update method. + +If the pmc pointer is not set, then there is no need to read the time only +to later discard the result. This patch simplifies the flow by returning +early if there is no work to be done. + +Signed-off-by: Richard Cochran +Reviewed-by: Jacob Keller +Reviewed-by: Vladimir Oltean + +[commit 956b7eeb8247e3f0658b1205dfd3bea3e1011ee2 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index dd509af..f30f174 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -342,14 +342,16 @@ int update_pmc_node(struct pmc_agent *node) + struct timespec tp; + uint64_t ts; + ++ if (!node->pmc) { ++ return 0; ++ } + if (clock_gettime(CLOCK_MONOTONIC, &tp)) { + pr_err("failed to read clock: %m"); + return -1; + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (node->pmc && +- !(ts > node->pmc_last_update && ++ if (!(ts > node->pmc_last_update && + ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (node->stay_subscribed) { + renew_subscription(node, 0); +-- +2.25.1 + +From 357e24c897e1e2d29cf011b3a38c3a6b2a7943c3 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:33:43 -0300 +Subject: [PATCH 23/47] pmc_agent: Remove bogus comparison between last update + and now. + +The monotonic clock can never go backwards. If you take T1 and later T2 +from that clock, then (T2 > T1) is always true. + +This patch removes the useless test. + +[ This test evolved over the years. Originally the time stamp in question + came from a PHC. ] + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 2f2f7fc5881a88295350430edaf4505dc03b1602 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index f30f174..df3a562 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -351,8 +351,7 @@ int update_pmc_node(struct pmc_agent *node) + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (!(ts > node->pmc_last_update && +- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (!(ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +-- +2.25.1 + +From d5421e4d4d86907648a59810ab9c27e739591971 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:35:23 -0300 +Subject: [PATCH 24/47] pmc_agent: Perform time comparison using positive + logic. + +In the update_pmc_node() method, reduce the expression +!(x < y) to (x >= y). + +While we're at it, clean the coding style as well. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit fb92fec7cef9ee3345950c2633a7781b8bd3ca08 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index df3a562..ea6b3b7 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -351,12 +351,13 @@ int update_pmc_node(struct pmc_agent *node) + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (!(ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (ts - node->pmc_last_update >= PMC_UPDATE_INTERVAL) { + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +- if (run_pmc_get_utc_offset(node, 0) > 0) ++ if (run_pmc_get_utc_offset(node, 0) > 0) { + node->pmc_last_update = ts; ++ } + } + + return 0; +-- +2.25.1 + +From a304d4df86a76c187fc7074755fe9b5ad349efbe Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:36:38 -0300 +Subject: [PATCH 25/47] pmc_agent: Rename the update method and attempt to + document it. + +This patch renames the function to have the module prefix and tries to +put into words what it does. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 9a2dae984e0d355d751913e3308f9a954da11aa3 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 4 ++-- + pmc_agent.c | 53 ++++++++++++++++++++++++++--------------------------- + pmc_agent.h | 21 ++++++++++++++++++++- + 3 files changed, 48 insertions(+), 30 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index b155961..cbe80f2 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -672,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc_node(priv->node) < 0) ++ if (pmc_agent_update(priv->node) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -711,7 +711,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + +- if (update_pmc_node(priv->node) < 0) { ++ if (pmc_agent_update(priv->node) < 0) { + continue; + } + +diff --git a/pmc_agent.c b/pmc_agent.c +index ea6b3b7..22af306 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -336,33 +336,6 @@ int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + return 1; + } + +-/* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_agent *node) +-{ +- struct timespec tp; +- uint64_t ts; +- +- if (!node->pmc) { +- return 0; +- } +- if (clock_gettime(CLOCK_MONOTONIC, &tp)) { +- pr_err("failed to read clock: %m"); +- return -1; +- } +- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; +- +- if (ts - node->pmc_last_update >= PMC_UPDATE_INTERVAL) { +- if (node->stay_subscribed) { +- renew_subscription(node, 0); +- } +- if (run_pmc_get_utc_offset(node, 0) > 0) { +- node->pmc_last_update = ts; +- } +- } +- +- return 0; +-} +- + int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context) + { +@@ -414,6 +387,32 @@ int pmc_agent_subscribe(struct pmc_agent *node, int timeout) + return renew_subscription(node, timeout); + } + ++int pmc_agent_update(struct pmc_agent *node) ++{ ++ struct timespec tp; ++ uint64_t ts; ++ ++ if (!node->pmc) { ++ return 0; ++ } ++ if (clock_gettime(CLOCK_MONOTONIC, &tp)) { ++ pr_err("failed to read clock: %m"); ++ return -errno; ++ } ++ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; ++ ++ if (ts - node->pmc_last_update >= PMC_UPDATE_INTERVAL) { ++ if (node->stay_subscribed) { ++ renew_subscription(node, 0); ++ } ++ if (run_pmc_get_utc_offset(node, 0) > 0) { ++ node->pmc_last_update = ts; ++ } ++ } ++ ++ return 0; ++} ++ + bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) + { + return agent->utc_offset_traceable; +diff --git a/pmc_agent.h b/pmc_agent.h +index 743818f..483a21b 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -33,7 +33,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); +-int update_pmc_node(struct pmc_agent *agent); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); +@@ -85,6 +84,26 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); + */ + int pmc_agent_subscribe(struct pmc_agent *agent, int timeout); + ++/** ++ * Queries the local ptp4l instance to update the TAI-UTC offset and ++ * the current leap second flags. ++ * ++ * In addition: ++ * ++ * - Any active port state subscription will be renewed. ++ * - The port state notification callback might be invoked. ++ * ++ * This function should be called periodically at least once per ++ * minute to keep both the port state and the leap second flags up to ++ * date. Note that the PMC agent rate limits the query to once per ++ * minute, and so the caller may safely invoke this method more often ++ * than that. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_update(struct pmc_agent *agent); ++ + /** + * Tests whether the current UTC offset is traceable. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From 5aacbe319db97907a15741005e2790bbf4c742a0 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:37:46 -0300 +Subject: [PATCH 26/47] phc2sys: Fix null pointer de-reference in manual mode. + +If both the -w and -O command line options are specified (or when +using -w when both source and destination clocks are PHCs), then +pointer to the PMC agent will be incorrectly freed. + +Fix the segfault by introducing a method to "disable" the agent as was +done before the PMC agent code was introduced. + +Unfortunately the resulting PMC agent API now has both create/destroy +and init/disable methods. This clunky arrangement can be cleaned up +later on, but it entails re-factoring the phc2sys program even more. + +Signed-off-by: Richard Cochran +Fixes: 8266987 ("pmc_agent: Hide the implementation.") + +[commit 68fd0b010e9761e3dc580026eb6f2366c7c8e82d upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 7 ++----- + pmc_agent.c | 8 ++++++++ + pmc_agent.h | 6 ++++++ + 3 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index cbe80f2..3cafbb2 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1340,8 +1340,7 @@ int main(int argc, char *argv[]) + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || + src->clkid == CLOCK_INVALID) { +- pmc_agent_destroy(priv.node); +- priv.node = NULL; ++ pmc_agent_disable(priv.node); + } + } + +@@ -1355,9 +1354,7 @@ int main(int argc, char *argv[]) + } + + end: +- if (priv.node) { +- pmc_agent_destroy(priv.node); +- } ++ pmc_agent_destroy(priv.node); + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +diff --git a/pmc_agent.c b/pmc_agent.c +index 22af306..833d1c1 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -366,6 +366,14 @@ void pmc_agent_destroy(struct pmc_agent *agent) + free(agent); + } + ++void pmc_agent_disable(struct pmc_agent *agent) ++{ ++ if (agent->pmc) { ++ pmc_destroy(agent->pmc); ++ } ++ agent->pmc = NULL; ++} ++ + int pmc_agent_get_leap(struct pmc_agent *agent) + { + return agent->leap; +diff --git a/pmc_agent.h b/pmc_agent.h +index 483a21b..0ed10f8 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -55,6 +55,12 @@ struct pmc_agent *pmc_agent_create(void); + */ + void pmc_agent_destroy(struct pmc_agent *agent); + ++/** ++ * Disconnects the PMC agent from the ptp4l service. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ */ ++void pmc_agent_disable(struct pmc_agent *agent); ++ + /** + * Gets the current leap adjustment. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From b8188a4fd51bc8983e5d19f18fe37b8ca39d03a6 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:20:04 -0300 +Subject: [PATCH 27/47] pmc_agent: Convert the method that queries TAI-UTC + offset into the canonical form. + +This patch renames the function to have the module prefix and corrects the +return code semantics. + +The active word in the function's name is "query" rather that "get" in +order to distinguish methods that send and receive over the network +from those that merely return a cached value. + +Signed-off-by: Richard Cochran +Reviewed-by: Jacob Keller + +[commit 943c8f51c56acb72277d1a9459bbf7b7a5ac5fe7 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 8 +++---- + pmc_agent.c | 63 +++++++++++++++++++++++++++-------------------------- + pmc_agent.h | 16 ++++++++++++-- + 3 files changed, 50 insertions(+), 37 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 3cafbb2..78d662b 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -720,7 +720,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + if (priv->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ +- if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { ++ if (pmc_agent_query_utc_offset(priv->node, 1000)) { + pr_err("failed to get UTC offset"); + continue; + } +@@ -921,7 +921,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { ++ if (pmc_agent_query_utc_offset(priv->node, 1000)) { + pr_err("failed to get UTC offset"); + return -1; + } +@@ -1330,8 +1330,8 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = run_pmc_get_utc_offset(priv.node, 1000); +- if (r <= 0) { ++ r = pmc_agent_query_utc_offset(priv.node, 1000); ++ if (r) { + pr_err("failed to get UTC offset"); + goto end; + } +diff --git a/pmc_agent.c b/pmc_agent.c +index 833d1c1..7a57a2f 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -228,36 +228,6 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + } + } + +-int run_pmc_get_utc_offset(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct timePropertiesDS *tds; +- +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- tds = (struct timePropertiesDS *) management_tlv_data(msg); +- if (tds->flags & PTP_TIMESCALE) { +- node->sync_offset = tds->currentUtcOffset; +- if (tds->flags & LEAP_61) +- node->leap = 1; +- else if (tds->flags & LEAP_59) +- node->leap = -1; +- else +- node->leap = 0; +- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && +- tds->flags & TIME_TRACEABLE; +- } else { +- node->sync_offset = 0; +- node->leap = 0; +- node->utc_offset_traceable = 0; +- } +- msg_put(msg); +- return 1; +-} +- + int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +@@ -384,6 +354,37 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) ++{ ++ struct timePropertiesDS *tds; ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ ++ tds = (struct timePropertiesDS *) management_tlv_data(msg); ++ if (tds->flags & PTP_TIMESCALE) { ++ node->sync_offset = tds->currentUtcOffset; ++ if (tds->flags & LEAP_61) ++ node->leap = 1; ++ else if (tds->flags & LEAP_59) ++ node->leap = -1; ++ else ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ tds->flags & TIME_TRACEABLE; ++ } else { ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; ++ } ++ msg_put(msg); ++ return 0; ++} ++ + void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + { + agent->sync_offset = offset; +@@ -413,7 +414,7 @@ int pmc_agent_update(struct pmc_agent *node) + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +- if (run_pmc_get_utc_offset(node, 0) > 0) { ++ if (!pmc_agent_query_utc_offset(node, 0)) { + node->pmc_last_update = ts; + } + } +diff --git a/pmc_agent.h b/pmc_agent.h +index 0ed10f8..44326d2 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -40,8 +40,6 @@ void run_pmc_events(struct pmc_agent *agent); + int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); +-int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); +- + + /** + * Creates an instance of a PMC agent. +@@ -75,6 +73,20 @@ int pmc_agent_get_leap(struct pmc_agent *agent); + */ + int pmc_agent_get_sync_offset(struct pmc_agent *agent); + ++/** ++ * Queries the TAI-UTC offset and the current leap adjustment from the ++ * ptp4l service. ++ * ++ * In addition: ++ * ++ * - The port state notification callback might be invoked. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_utc_offset(struct pmc_agent *agent, int timeout); ++ + /** + * Sets the TAI-UTC offset. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From acdf74df9fa69b81c1e9332f10d4efcd3e9bae48 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:23:29 -0300 +Subject: [PATCH 28/47] pmc_agent: Convert the method that queries the port + properties. + +Prefix the function with the module name and correct the return code +semantics. + +The active word in the function's name is "query" rather that "get" in +order to distinguish methods that send and receive over the network +from those that merely return a cached value. + +Signed-off-by: Richard Cochran + +[commit ac7d69bbc476b94d76e5cee4992b9682f003feaf upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 41 +++++++++++++++-------------- + pmc_agent.c | 74 ++++++++++++++++++++++++++--------------------------- + pmc_agent.h | 22 +++++++++++++--- + 3 files changed, 78 insertions(+), 59 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 78d662b..32e6e13 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -296,8 +296,7 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + int new_state) + { +- int phc_index = -1, phc_switched = 0; +- int state, timestamping, ret = -1; ++ int err = -1, phc_index = -1, phc_switched = 0, state, timestamping; + struct port *p; + struct servo *servo; + struct sk_ts_info ts_info; +@@ -305,16 +304,19 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + clockid_t clkid = CLOCK_INVALID; + + LIST_FOREACH(p, &priv->ports, list) { +- if (p->clock == clock) { +- ret = run_pmc_port_properties(priv->node, 1000, p->number, +- &state, ×tamping, +- iface); +- if (ret > 0) +- p->state = normalize_state(state); ++ if (p->clock != clock) { ++ continue; ++ } ++ err = pmc_agent_query_port_properties(priv->node, 1000, ++ p->number, &state, ++ ×tamping, iface); ++ if (!err) { ++ p->state = normalize_state(state); + } ++ break; + } + +- if (ret > 0 && timestamping != TS_SOFTWARE) { ++ if (!err && timestamping != TS_SOFTWARE) { + /* Check if device changed */ + if (strcmp(clock->device, iface)) { + free(clock->device); +@@ -852,12 +854,12 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + { +- struct port *port; +- struct clock *clock; +- int number_ports, res; +- unsigned int i; ++ int err, number_ports, res; + int state, timestamping; + char iface[IFNAMSIZ]; ++ struct clock *clock; ++ struct port *port; ++ unsigned int i; + + while (1) { + if (!is_running()) +@@ -877,20 +879,21 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + return -1; + } + +- res = pmc_agent_subscribe(priv->node, 1000); +- if (res) { ++ err = pmc_agent_subscribe(priv->node, 1000); ++ if (err) { + pr_err("failed to subscribe"); + return -1; + } + + for (i = 1; i <= number_ports; i++) { +- res = run_pmc_port_properties(priv->node, 1000, i, &state, +- ×tamping, iface); +- if (res == -1) { ++ err = pmc_agent_query_port_properties(priv->node, 1000, i, ++ &state, ×tamping, ++ iface); ++ if (err == -ENODEV) { + /* port does not exist, ignore the port */ + continue; + } +- if (res <= 0) { ++ if (err) { + pr_err("failed to get port properties"); + return -1; + } +diff --git a/pmc_agent.c b/pmc_agent.c +index 7a57a2f..cc729ab 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -251,43 +251,6 @@ void run_pmc_events(struct pmc_agent *node) + run_pmc(node, 0, -1, &msg); + } + +-int run_pmc_port_properties(struct pmc_agent *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) +-{ +- struct ptp_message *msg; +- int res, len; +- struct port_properties_np *ppn; +- +- pmc_target_port(node->pmc, port); +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); +- if (res <= 0) +- goto out; +- +- ppn = management_tlv_data(msg); +- if (ppn->portIdentity.portNumber != port) { +- msg_put(msg); +- continue; +- } +- +- *state = ppn->port_state; +- *tstamping = ppn->timestamping; +- len = ppn->interface.length; +- if (len > IFNAMSIZ - 1) +- len = IFNAMSIZ - 1; +- memcpy(iface, ppn->interface.text, len); +- iface[len] = '\0'; +- +- msg_put(msg); +- res = 1; +- break; +- } +-out: +- pmc_target_all(node->pmc); +- return res; +-} +- + int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +@@ -354,6 +317,43 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) ++{ ++ struct port_properties_np *ppn; ++ struct ptp_message *msg; ++ int res, len; ++ ++ pmc_target_port(node->pmc, port); ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ if (is_run_pmc_error(res)) { ++ goto out; ++ } ++ ppn = management_tlv_data(msg); ++ if (ppn->portIdentity.portNumber != port) { ++ msg_put(msg); ++ continue; ++ } ++ *state = ppn->port_state; ++ *tstamping = ppn->timestamping; ++ len = ppn->interface.length; ++ if (len > IFNAMSIZ - 1) { ++ len = IFNAMSIZ - 1; ++ } ++ memcpy(iface, ppn->interface.text, len); ++ iface[len] = '\0'; ++ ++ msg_put(msg); ++ res = 0; ++ break; ++ } ++out: ++ pmc_target_all(node->pmc); ++ return run_pmc_err2errno(res); ++} ++ + int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + { + struct timePropertiesDS *tds; +diff --git a/pmc_agent.h b/pmc_agent.h +index 44326d2..ea37bf9 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -37,9 +37,6 @@ int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); + void run_pmc_events(struct pmc_agent *agent); +-int run_pmc_port_properties(struct pmc_agent *agent, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface); + + /** + * Creates an instance of a PMC agent. +@@ -73,6 +70,25 @@ int pmc_agent_get_leap(struct pmc_agent *agent); + */ + int pmc_agent_get_sync_offset(struct pmc_agent *agent); + ++/** ++ * Queries the port properties of a given port from the ptp4l service. ++ * ++ * In addition: ++ * ++ * - The port state notification callback might be invoked. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @param port The port index of interest. ++ * @param state Buffer to hold the returned port state. ++ * @param tstamping Buffer to hold the returned time stamping flavor. ++ * @param iface Buffer to hold the returned interface name. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_port_properties(struct pmc_agent *agent, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); ++ + /** + * Queries the TAI-UTC offset and the current leap adjustment from the + * ptp4l service. +-- +2.25.1 + +From 3e6dd047083625ca03df9b4bbdc781e7dd079ff2 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:29:30 -0300 +Subject: [PATCH 29/47] pmc_agent: Generalize the method that queries the local + clock identity. + +When started in automatic mode, the phc2sys program first queries the +local clock identification and then the number of ports immediately +afterwords. However, both of those values come from the default data +set. Make code both simpler and more efficient by caching the entire +data set inside of the agent. + +A subsequent patch will fix the run_pmc_get_number_ports() method to +return the cached result. + +The active word in the function's name is "query" rather that "get" in +order to distinguish methods that send and receive over the network +from those that merely return a cached value. + +Signed-off-by: Richard Cochran + +[commit 919703eb06b7ee9679308597e01e1da0162736d7 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 20 +++++++++++--------- + pmc_agent.c | 46 +++++++++++++++++++++++----------------------- + pmc_agent.h | 15 ++++++++++++++- + 3 files changed, 48 insertions(+), 33 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 32e6e13..0f33630 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -854,23 +854,25 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + { +- int err, number_ports, res; +- int state, timestamping; ++ int err, number_ports, state, timestamping; + char iface[IFNAMSIZ]; + struct clock *clock; + struct port *port; + unsigned int i; + + while (1) { +- if (!is_running()) ++ if (!is_running()) { + return -1; +- res = run_pmc_clock_identity(priv->node, 1000); +- if (res < 0) +- return -1; +- if (res > 0) ++ } ++ err = pmc_agent_query_dds(priv->node, 1000); ++ if (!err) { + break; +- /* res == 0, timeout */ +- pr_notice("Waiting for ptp4l..."); ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ } else { ++ return -1; ++ } + } + + number_ports = run_pmc_get_number_ports(priv->node, 1000); +diff --git a/pmc_agent.c b/pmc_agent.c +index cc729ab..51023d1 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -38,8 +38,8 @@ struct pmc_agent { + struct pmc *pmc; + uint64_t pmc_last_update; + +- struct ClockIdentity clock_identity; +- int clock_identity_set; ++ struct defaultDS dds; ++ bool dds_valid; + int leap; + int pmc_ds_requested; + bool stay_subscribed; +@@ -63,10 +63,11 @@ static void send_subscription(struct pmc_agent *node) + + static int check_clock_identity(struct pmc_agent *node, struct ptp_message *msg) + { +- if (!node->clock_identity_set) ++ if (!node->dds_valid) { + return 1; +- return cid_eq(&node->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); ++ } ++ return cid_eq(&node->dds.clockIdentity, ++ &msg->header.sourcePortIdentity.clockIdentity); + } + + static int is_msg_mgt(struct ptp_message *msg) +@@ -251,24 +252,6 @@ void run_pmc_events(struct pmc_agent *node) + run_pmc(node, 0, -1, &msg); + } + +-int run_pmc_clock_identity(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- struct defaultDS *dds; +- int res; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *) management_tlv_data(msg); +- memcpy(&node->clock_identity, &dds->clockIdentity, +- sizeof(struct ClockIdentity)); +- node->clock_identity_set = 1; +- msg_put(msg); +- return 1; +-} +- + int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context) + { +@@ -317,6 +300,23 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_query_dds(struct pmc_agent *node, int timeout) ++{ ++ struct ptp_message *msg; ++ struct defaultDS *dds; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ dds = (struct defaultDS *) management_tlv_data(msg); ++ memcpy(&node->dds, dds, sizeof(node->dds)); ++ node->dds_valid = true; ++ msg_put(msg); ++ return 0; ++} ++ + int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface) +diff --git a/pmc_agent.h b/pmc_agent.h +index ea37bf9..9d8bd1c 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -33,7 +33,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); +-int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); + void run_pmc_events(struct pmc_agent *agent); +@@ -70,6 +69,20 @@ int pmc_agent_get_leap(struct pmc_agent *agent); + */ + int pmc_agent_get_sync_offset(struct pmc_agent *agent); + ++/** ++ * Queries the local clock's default data set from the ptp4l service. ++ * The result of the query will be cached inside of the agent. ++ * ++ * In addition: ++ * ++ * - The port state notification callback might be invoked. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_dds(struct pmc_agent *agent, int timeout); ++ + /** + * Queries the port properties of a given port from the ptp4l service. + * +-- +2.25.1 + +From d3b877cae9576beddb00d4c5db67bf49c944b222 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:30:57 -0300 +Subject: [PATCH 30/47] pmc_agent: Simplify the method that gets of the number + of local ports. + +The number of ports is already available in the cached default data +set. Use it directly. + +Signed-off-by: Richard Cochran + +[commit 6bc9eb81dd254d90b5fe059684271b9beebf6b9b upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 2 +- + pmc_agent.c | 24 ++++++++---------------- + pmc_agent.h | 11 ++++++++++- + 3 files changed, 19 insertions(+), 18 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 0f33630..569544e 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -875,7 +875,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + } + +- number_ports = run_pmc_get_number_ports(priv->node, 1000); ++ number_ports = pmc_agent_get_number_ports(priv->node); + if (number_ports <= 0) { + pr_err("failed to get number of ports"); + return -1; +diff --git a/pmc_agent.c b/pmc_agent.c +index 51023d1..aa2347d 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -229,22 +229,6 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + } + } + +-int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct defaultDS *dds; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *) management_tlv_data(msg); +- res = dds->numberPorts; +- msg_put(msg); +- return res; +-} +- + void run_pmc_events(struct pmc_agent *node) + { + struct ptp_message *msg; +@@ -300,6 +284,14 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_get_number_ports(struct pmc_agent *node) ++{ ++ if (!node->dds_valid) { ++ return -1; ++ } ++ return node->dds.numberPorts; ++} ++ + int pmc_agent_query_dds(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +diff --git a/pmc_agent.h b/pmc_agent.h +index 9d8bd1c..f0e2c7a 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -34,7 +34,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); +-int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); + void run_pmc_events(struct pmc_agent *agent); + + /** +@@ -62,6 +61,16 @@ void pmc_agent_disable(struct pmc_agent *agent); + */ + int pmc_agent_get_leap(struct pmc_agent *agent); + ++/** ++ * Gets the number of local ports from the default data set. Users ++ * should first call pmc_agent_query_dds() before invoking this ++ * function. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return The non-negative number of ports, or -1 if unknown. ++ */ ++int pmc_agent_get_number_ports(struct pmc_agent *agent); ++ + /** + * Gets the TAI-UTC offset. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From 156728d14591dd2b3131bcff49959e806523c1bb Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:32:27 -0300 +Subject: [PATCH 31/47] pmc_agent: Let the update method poll for push events. + +Signed-off-by: Richard Cochran + +[commit c4a5eef1f4763805e6e2a2d25eb1d436018d4745 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 3 +++ + pmc_agent.h | 5 +++-- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index aa2347d..6e6627d 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -390,6 +390,7 @@ int pmc_agent_subscribe(struct pmc_agent *node, int timeout) + + int pmc_agent_update(struct pmc_agent *node) + { ++ struct ptp_message *msg; + struct timespec tp; + uint64_t ts; + +@@ -411,6 +412,8 @@ int pmc_agent_update(struct pmc_agent *node) + } + } + ++ run_pmc(node, 0, -1, &msg); ++ + return 0; + } + +diff --git a/pmc_agent.h b/pmc_agent.h +index f0e2c7a..dd34d30 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -141,11 +141,12 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); + int pmc_agent_subscribe(struct pmc_agent *agent, int timeout); + + /** +- * Queries the local ptp4l instance to update the TAI-UTC offset and +- * the current leap second flags. ++ * Polls for push notifications from the local ptp4l service. + * + * In addition: + * ++ * - Queries the local ptp4l instance to update the TAI-UTC offset and ++ * the current leap second flags. + * - Any active port state subscription will be renewed. + * - The port state notification callback might be invoked. + * +-- +2.25.1 + +From 0e504e57af6c576202bbe1abe5a99eb24a981b73 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:51:10 -0300 +Subject: [PATCH 32/47] phc2sys: Fix regression in the automatic mode. + +Commit ac7d69bbc476 ("pmc_agent: Convert the method that queries the +port properties.") had the well meant intention of the cleaning up the +error code semantics of the port properties query function. However, +that commit mixed up the normal, external semantics of zero meaning +success with the internal semantics where zero is an error. Correct +the issue by replacing the hard coded number with the proper macro. + +Signed-off-by: Richard Cochran +Fixes: ac7d69bbc476 ("pmc_agent: Convert the method that queries the port properties.") + +[commit 0fb1be2f5c4d6905f33a2b1c31e7496d52296748 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index 6e6627d..623f300 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -338,7 +338,7 @@ int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, + iface[len] = '\0'; + + msg_put(msg); +- res = 0; ++ res = RUN_PMC_OKAY; + break; + } + out: +-- +2.25.1 + +From 06a6734e3350e4020b4bb7b24a15d43aa42b4ca7 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:57:11 -0300 +Subject: [PATCH 33/47] Implement push notification for TIME_STATUS_NP + +Subscribers to NOTIFY_TIME_SYNC will be notified on every clock +synchronization. + +[ RC: + - Don't subscribe this in pmc_agent. + - Use stdbool/stdint types in event_bitmask_get/set. ] + +Signed-off-by: Juergen Werner +Signed-off-by: Richard Cochran + +[commit 6d7c090706e76af334185ffcec9cc56d0570e215 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 14 +++++++++----- + notification.h | 22 ++++++++++++++++++++++ + pmc.c | 6 ++++-- + pmc_agent.c | 2 +- + pmc_common.c | 23 +++++++++++++++-------- + 5 files changed, 51 insertions(+), 16 deletions(-) + +diff --git a/clock.c b/clock.c +index 437cd1c..f048771 100644 +--- a/clock.c ++++ b/clock.c +@@ -243,13 +243,11 @@ static void clock_prune_subscriptions(struct clock *c) + void clock_send_notification(struct clock *c, struct ptp_message *msg, + enum notification event) + { +- unsigned int event_pos = event / 8; +- uint8_t mask = 1 << (event % 8); + struct port *uds = c->uds_port; + struct clock_subscriber *s; + + LIST_FOREACH(s, &c->subscribers, list) { +- if (!(s->events[event_pos] & mask)) ++ if (!event_bitmask_get(s->events, event)) + continue; + /* send event */ + msg->header.sequenceId = htons(s->sequenceId); +@@ -1501,7 +1499,9 @@ void clock_notify_event(struct clock *c, enum notification event) + int id; + + switch (event) { +- /* set id */ ++ case NOTIFY_TIME_SYNC: ++ id = TLV_TIME_STATUS_NP; ++ break; + default: + return; + } +@@ -1731,7 +1731,9 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin) + c->cur.offsetFromMaster = tmv_to_TimeInterval(c->master_offset); + + if (c->free_running) { +- return clock_no_adjust(c, ingress, origin); ++ state = clock_no_adjust(c, ingress, origin); ++ clock_notify_event(c, NOTIFY_TIME_SYNC); ++ return state; + } + + offset = tmv_to_nanoseconds(c->master_offset); +@@ -1777,6 +1779,8 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin) + tmv_to_nanoseconds(c->path_delay)); + } + ++ clock_notify_event(c, NOTIFY_TIME_SYNC); ++ + return state; + } + +diff --git a/notification.h b/notification.h +index 47c9b56..115f864 100644 +--- a/notification.h ++++ b/notification.h +@@ -20,8 +20,30 @@ + #ifndef HAVE_NOTIFICATION_H + #define HAVE_NOTIFICATION_H + ++#include ++#include ++ ++static inline void event_bitmask_set(uint8_t *bitmask, unsigned int event, ++ bool value) ++{ ++ unsigned int event_pos = event / 8; ++ uint8_t event_bit = 1 << (event % 8); ++ ++ if (value) { ++ bitmask[event_pos] |= event_bit; ++ } else { ++ bitmask[event_pos] &= ~(event_bit); ++ } ++} ++ ++static inline bool event_bitmask_get(uint8_t *bitmask, unsigned int event) ++{ ++ return (bitmask[event / 8] & (1 << (event % 8))) ? true : false; ++} ++ + enum notification { + NOTIFY_PORT_STATE, ++ NOTIFY_TIME_SYNC, + }; + + #endif +diff --git a/pmc.c b/pmc.c +index 65d1d61..3678800 100644 +--- a/pmc.c ++++ b/pmc.c +@@ -387,9 +387,11 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + sen = (struct subscribe_events_np *) mgt->data; + fprintf(fp, "SUBSCRIBE_EVENTS_NP " + IFMT "duration %hu" +- IFMT "NOTIFY_PORT_STATE %s", ++ IFMT "NOTIFY_PORT_STATE %s" ++ IFMT "NOTIFY_TIME_SYNC %s", + sen->duration, +- (sen->bitmask[0] & 1 << NOTIFY_PORT_STATE) ? "on" : "off"); ++ event_bitmask_get(sen->bitmask, NOTIFY_PORT_STATE) ? "on" : "off", ++ event_bitmask_get(sen->bitmask, NOTIFY_TIME_SYNC) ? "on" : "off"); + break; + case TLV_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) mgt->data; +diff --git a/pmc_agent.c b/pmc_agent.c +index 623f300..37910b3 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -57,7 +57,7 @@ static void send_subscription(struct pmc_agent *node) + + memset(&sen, 0, sizeof(sen)); + sen.duration = PMC_SUBSCRIBE_DURATION; +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ event_bitmask_set(sen.bitmask, NOTIFY_PORT_STATE, TRUE); + pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +diff --git a/pmc_common.c b/pmc_common.c +index a117904..c5cd992 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -149,7 +149,8 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + struct management_tlv_datum mtd; + struct subscribe_events_np sen; + struct port_ds_np pnp; +- char onoff[4] = {0}; ++ char onoff_port_state[4] = "off"; ++ char onoff_time_status[4] = "off"; + + switch (action) { + case GET: +@@ -223,16 +224,22 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + case TLV_SUBSCRIBE_EVENTS_NP: + memset(&sen, 0, sizeof(sen)); + cnt = sscanf(str, " %*s %*s " +- "duration %hu " +- "NOTIFY_PORT_STATE %3s ", +- &sen.duration, onoff); +- if (cnt != 2) { +- fprintf(stderr, "%s SET needs 2 values\n", ++ "duration %hu " ++ "NOTIFY_PORT_STATE %3s " ++ "NOTIFY_TIME_SYNC %3s ", ++ &sen.duration, ++ onoff_port_state, ++ onoff_time_status); ++ if (cnt != 3) { ++ fprintf(stderr, "%s SET needs 3 values\n", + idtab[index].name); + break; + } +- if (!strcasecmp(onoff, "on")) { +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ if (!strcasecmp(onoff_port_state, "on")) { ++ event_bitmask_set(sen.bitmask, NOTIFY_PORT_STATE, TRUE); ++ } ++ if (!strcasecmp(onoff_time_status, "on")) { ++ event_bitmask_set(sen.bitmask, NOTIFY_TIME_SYNC, TRUE); + } + pmc_send_set_action(pmc, code, &sen, sizeof(sen)); + break; +-- +2.25.1 + +From babbe47ab091071e16fcd527bf1aad06e5aec377 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 18:16:31 -0300 +Subject: [PATCH 34/47] clock: Rename UDS variables to read-write. + +In preparation for a new read-only UDS port, rename variables of the +current UDS port to make it clear it is read-write, as opposed to +read-only. + +Signed-off-by: Miroslav Lichvar + +[commit 1b781a5a086571859b0cfba687706d8fdc764d7f upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 52 +++++++++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 23 deletions(-) + +diff --git a/clock.c b/clock.c +index f048771..d653c33 100644 +--- a/clock.c ++++ b/clock.c +@@ -95,7 +95,7 @@ struct clock { + struct foreign_clock *best; + struct ClockIdentity best_id; + LIST_HEAD(ports_head, port) ports; +- struct port *uds_port; ++ struct port *uds_rw_port; + struct pollfd *pollfd; + int pollfd_valid; + int nports; /* does not include the UDS port */ +@@ -129,7 +129,7 @@ struct clock { + struct clock_stats stats; + int stats_interval; + struct clockcheck *sanity_check; +- struct interface *udsif; ++ struct interface *uds_rw_if; + LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers; + struct monitor *slave_event_monitor; + }; +@@ -243,7 +243,7 @@ static void clock_prune_subscriptions(struct clock *c) + void clock_send_notification(struct clock *c, struct ptp_message *msg, + enum notification event) + { +- struct port *uds = c->uds_port; ++ struct port *uds = c->uds_rw_port; + struct clock_subscriber *s; + + LIST_FOREACH(s, &c->subscribers, list) { +@@ -265,13 +265,13 @@ void clock_destroy(struct clock *c) + { + struct port *p, *tmp; + +- interface_destroy(c->udsif); ++ interface_destroy(c->uds_rw_if); + clock_flush_subscriptions(c); + LIST_FOREACH_SAFE(p, &c->ports, list, tmp) { + clock_remove_port(c, p); + } + monitor_destroy(c->slave_event_monitor); +- port_close(c->uds_port); ++ port_close(c->uds_rw_port); + free(c->pollfd); + if (c->clkid != CLOCK_REALTIME) { + phc_close(c->clkid); +@@ -440,7 +440,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + datalen = sizeof(*gsn); + break; + case TLV_SUBSCRIBE_EVENTS_NP: +- if (p != c->uds_port) { ++ if (p != c->uds_rw_port) { + /* Only the UDS port allowed. */ + break; + } +@@ -782,7 +782,7 @@ static int forwarding(struct clock *c, struct port *p) + default: + break; + } +- if (p == c->uds_port && ps != PS_FAULTY) { ++ if (p == c->uds_rw_port && ps != PS_FAULTY) { + return 1; + } + return 0; +@@ -1042,20 +1042,20 @@ struct clock *clock_create(enum clock_type type, struct config *config, + + /* Configure the UDS. */ + uds_ifname = config_get_string(config, NULL, "uds_address"); +- c->udsif = interface_create(uds_ifname); +- if (config_set_section_int(config, interface_name(c->udsif), ++ c->uds_rw_if = interface_create(uds_ifname); ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "announceReceiptTimeout", 0)) { + return NULL; + } +- if (config_set_section_int(config, interface_name(c->udsif), ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "delay_mechanism", DM_AUTO)) { + return NULL; + } +- if (config_set_section_int(config, interface_name(c->udsif), ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "network_transport", TRANS_UDS)) { + return NULL; + } +- if (config_set_section_int(config, interface_name(c->udsif), ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "delay_filter_length", 1)) { + return NULL; + } +@@ -1178,14 +1178,15 @@ struct clock *clock_create(enum clock_type type, struct config *config, + } + + /* Create the UDS interface. */ +- c->uds_port = port_open(phc_device, phc_index, timestamping, 0, c->udsif, c); +- if (!c->uds_port) { ++ c->uds_rw_port = port_open(phc_device, phc_index, timestamping, 0, ++ c->uds_rw_if, c); ++ if (!c->uds_rw_port) { + pr_err("failed to open the UDS port"); + return NULL; + } + clock_fda_changed(c); + +- c->slave_event_monitor = monitor_create(config, c->uds_port); ++ c->slave_event_monitor = monitor_create(config, c->uds_rw_port); + if (!c->slave_event_monitor) { + pr_err("failed to create slave event monitor"); + return NULL; +@@ -1204,7 +1205,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + LIST_FOREACH(p, &c->ports, list) { + port_dispatch(p, EV_INITIALIZE, 0); + } +- port_dispatch(c->uds_port, EV_INITIALIZE, 0); ++ port_dispatch(c->uds_rw_port, EV_INITIALIZE, 0); + + return c; + } +@@ -1312,7 +1313,7 @@ static void clock_check_pollfd(struct clock *c) + clock_fill_pollfd(dest, p); + dest += N_CLOCK_PFD; + } +- clock_fill_pollfd(dest, c->uds_port); ++ clock_fill_pollfd(dest, c->uds_rw_port); + c->pollfd_valid = 1; + } + +@@ -1329,7 +1330,7 @@ static int clock_do_forward_mgmt(struct clock *c, + return 0; + + /* Don't forward any requests to the UDS port. */ +- if (out == c->uds_port) { ++ if (out == c->uds_rw_port) { + switch (management_action(msg)) { + case GET: + case SET: +@@ -1360,7 +1361,7 @@ static void clock_forward_mgmt_msg(struct clock *c, struct port *p, struct ptp_m + pr_err("port %d: management forward failed", + port_number(piter)); + } +- if (clock_do_forward_mgmt(c, p, c->uds_port, msg, &msg_ready)) ++ if (clock_do_forward_mgmt(c, p, c->uds_rw_port, msg, &msg_ready)) + pr_err("uds port: management forward failed"); + if (msg_ready) { + msg_post_recv(msg, pdulen); +@@ -1412,7 +1413,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + clock_management_send_error(p, msg, TLV_WRONG_LENGTH); + return changed; + } +- if (p != c->uds_port) { ++ if (p != c->uds_rw_port) { + /* Sorry, only allowed on the UDS port. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return changed; +@@ -1421,6 +1422,11 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + return changed; + break; + case COMMAND: ++ if (p != c->uds_rw_port) { ++ /* Sorry, only allowed on the UDS port. */ ++ clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ return changed; ++ } + break; + default: + return changed; +@@ -1428,7 +1434,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + + switch (mgt->id) { + case TLV_PORT_PROPERTIES_NP: +- if (p != c->uds_port) { ++ if (p != c->uds_rw_port) { + /* Only the UDS port allowed. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return 0; +@@ -1493,7 +1499,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + + void clock_notify_event(struct clock *c, enum notification event) + { +- struct port *uds = c->uds_port; ++ struct port *uds = c->uds_rw_port; + struct PortIdentity pid = port_identity(uds); + struct ptp_message *msg; + int id; +@@ -1599,7 +1605,7 @@ int clock_poll(struct clock *c) + /* Check the UDS port. */ + for (i = 0; i < N_POLLFD; i++) { + if (cur[i].revents & (POLLIN|POLLPRI)) { +- event = port_event(c->uds_port, i); ++ event = port_event(c->uds_rw_port, i); + if (EV_STATE_DECISION_EVENT == event) { + c->sde = 1; + } +-- +2.25.1 + +From 4af24949b94eda84b4b74d77b9164cf3fe0eccf9 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 18:18:29 -0300 +Subject: [PATCH 35/47] clock: Add read-only UDS port for monitoring. + +Add a second UDS port to allow untrusted applications to monitor ptp4l. +On this "read-only" UDS port disable non-GET actions and forwarding. +The path can be configured with the uds_ro_address option (default is +/var/run/ptp4lro). + +Forwarding is disabled to limit the access to the local ptp4l instance. + +Subscriptions are not enabled to prevent the applications from making a +large number of subscriptions or interfere with applications that have +access to the read-write UDS port. + +Signed-off-by: Miroslav Lichvar + +[commit 6823e077b2466dcc3c7cbce8ab384b0ef9a62811 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 72 +++++++++++++++++++++++++++++++++++++-------- + config.c | 1 + + configs/default.cfg | 1 + + ptp4l.8 | 6 ++++ + 4 files changed, 67 insertions(+), 13 deletions(-) + +diff --git a/clock.c b/clock.c +index d653c33..869e35d 100644 +--- a/clock.c ++++ b/clock.c +@@ -96,9 +96,10 @@ struct clock { + struct ClockIdentity best_id; + LIST_HEAD(ports_head, port) ports; + struct port *uds_rw_port; ++ struct port *uds_ro_port; + struct pollfd *pollfd; + int pollfd_valid; +- int nports; /* does not include the UDS port */ ++ int nports; /* does not include the two UDS ports */ + int last_port_number; + int sde; + int free_running; +@@ -130,6 +131,7 @@ struct clock { + int stats_interval; + struct clockcheck *sanity_check; + struct interface *uds_rw_if; ++ struct interface *uds_ro_if; + LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers; + struct monitor *slave_event_monitor; + }; +@@ -266,12 +268,14 @@ void clock_destroy(struct clock *c) + struct port *p, *tmp; + + interface_destroy(c->uds_rw_if); ++ interface_destroy(c->uds_ro_if); + clock_flush_subscriptions(c); + LIST_FOREACH_SAFE(p, &c->ports, list, tmp) { + clock_remove_port(c, p); + } + monitor_destroy(c->slave_event_monitor); + port_close(c->uds_rw_port); ++ port_close(c->uds_ro_port); + free(c->pollfd); + if (c->clkid != CLOCK_REALTIME) { + phc_close(c->clkid); +@@ -441,7 +445,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + break; + case TLV_SUBSCRIBE_EVENTS_NP: + if (p != c->uds_rw_port) { +- /* Only the UDS port allowed. */ ++ /* Only the UDS-RW port allowed. */ + break; + } + sen = (struct subscribe_events_np *)tlv->data; +@@ -772,6 +776,10 @@ static int clock_utc_correct(struct clock *c, tmv_t ingress) + static int forwarding(struct clock *c, struct port *p) + { + enum port_state ps = port_state(p); ++ ++ if (p == c->uds_ro_port) ++ return 0; ++ + switch (ps) { + case PS_MASTER: + case PS_GRAND_MASTER: +@@ -816,7 +824,7 @@ static int clock_add_port(struct clock *c, const char *phc_device, + { + struct port *p, *piter, *lastp = NULL; + +- if (clock_resize_pollfd(c, c->nports + 1)) { ++ if (clock_resize_pollfd(c, c->nports + 2)) { + return -1; + } + p = port_open(phc_device, phc_index, timestamping, +@@ -1041,6 +1049,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + } + + /* Configure the UDS. */ ++ + uds_ifname = config_get_string(config, NULL, "uds_address"); + c->uds_rw_if = interface_create(uds_ifname); + if (config_set_section_int(config, interface_name(c->uds_rw_if), +@@ -1060,6 +1069,25 @@ struct clock *clock_create(enum clock_type type, struct config *config, + return NULL; + } + ++ uds_ifname = config_get_string(config, NULL, "uds_ro_address"); ++ c->uds_ro_if = interface_create(uds_ifname); ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "announceReceiptTimeout", 0)) { ++ return NULL; ++ } ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "delay_mechanism", DM_AUTO)) { ++ return NULL; ++ } ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "network_transport", TRANS_UDS)) { ++ return NULL; ++ } ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "delay_filter_length", 1)) { ++ return NULL; ++ } ++ + c->config = config; + c->free_running = config_get_int(config, NULL, "free_running"); + c->freq_est_interval = config_get_int(config, NULL, "freq_est_interval"); +@@ -1177,11 +1205,18 @@ struct clock *clock_create(enum clock_type type, struct config *config, + return NULL; + } + +- /* Create the UDS interface. */ ++ /* Create the UDS interfaces. */ ++ + c->uds_rw_port = port_open(phc_device, phc_index, timestamping, 0, + c->uds_rw_if, c); + if (!c->uds_rw_port) { +- pr_err("failed to open the UDS port"); ++ pr_err("failed to open the UDS-RW port"); ++ return NULL; ++ } ++ c->uds_ro_port = port_open(phc_device, phc_index, timestamping, 0, ++ c->uds_ro_if, c); ++ if (!c->uds_ro_port) { ++ pr_err("failed to open the UDS-RO port"); + return NULL; + } + clock_fda_changed(c); +@@ -1206,6 +1241,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + port_dispatch(p, EV_INITIALIZE, 0); + } + port_dispatch(c->uds_rw_port, EV_INITIALIZE, 0); ++ port_dispatch(c->uds_ro_port, EV_INITIALIZE, 0); + + return c; + } +@@ -1276,9 +1312,9 @@ static int clock_resize_pollfd(struct clock *c, int new_nports) + { + struct pollfd *new_pollfd; + +- /* Need to allocate one whole extra block of fds for UDS. */ ++ /* Need to allocate two whole extra blocks of fds for UDS ports. */ + new_pollfd = realloc(c->pollfd, +- (new_nports + 1) * N_CLOCK_PFD * ++ (new_nports + 2) * N_CLOCK_PFD * + sizeof(struct pollfd)); + if (!new_pollfd) { + return -1; +@@ -1314,6 +1350,8 @@ static void clock_check_pollfd(struct clock *c) + dest += N_CLOCK_PFD; + } + clock_fill_pollfd(dest, c->uds_rw_port); ++ dest += N_CLOCK_PFD; ++ clock_fill_pollfd(dest, c->uds_ro_port); + c->pollfd_valid = 1; + } + +@@ -1329,7 +1367,8 @@ static int clock_do_forward_mgmt(struct clock *c, + if (in == out || !forwarding(c, out)) + return 0; + +- /* Don't forward any requests to the UDS port. */ ++ /* Don't forward any requests to the UDS-RW port ++ (the UDS-RO port doesn't allow any forwarding). */ + if (out == c->uds_rw_port) { + switch (management_action(msg)) { + case GET: +@@ -1414,7 +1453,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + return changed; + } + if (p != c->uds_rw_port) { +- /* Sorry, only allowed on the UDS port. */ ++ /* Sorry, only allowed on the UDS-RW port. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return changed; + } +@@ -1423,7 +1462,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + break; + case COMMAND: + if (p != c->uds_rw_port) { +- /* Sorry, only allowed on the UDS port. */ ++ /* Sorry, only allowed on the UDS-RW port. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return changed; + } +@@ -1435,7 +1474,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + switch (mgt->id) { + case TLV_PORT_PROPERTIES_NP: + if (p != c->uds_rw_port) { +- /* Only the UDS port allowed. */ ++ /* Only the UDS-RW port allowed. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return 0; + } +@@ -1548,7 +1587,7 @@ int clock_poll(struct clock *c) + struct port *p; + + clock_check_pollfd(c); +- cnt = poll(c->pollfd, (c->nports + 1) * N_CLOCK_PFD, -1); ++ cnt = poll(c->pollfd, (c->nports + 2) * N_CLOCK_PFD, -1); + if (cnt < 0) { + if (EINTR == errno) { + return 0; +@@ -1602,7 +1641,7 @@ int clock_poll(struct clock *c) + cur += N_CLOCK_PFD; + } + +- /* Check the UDS port. */ ++ /* Check the UDS ports. */ + for (i = 0; i < N_POLLFD; i++) { + if (cur[i].revents & (POLLIN|POLLPRI)) { + event = port_event(c->uds_rw_port, i); +@@ -1611,6 +1650,13 @@ int clock_poll(struct clock *c) + } + } + } ++ cur += N_CLOCK_PFD; ++ for (i = 0; i < N_POLLFD; i++) { ++ if (cur[i].revents & (POLLIN|POLLPRI)) { ++ event = port_event(c->uds_ro_port, i); ++ /* sde is not expected on the UDS-RO port */ ++ } ++ } + + if (c->sde) { + handle_state_decision_event(c); +diff --git a/config.c b/config.c +index fea7f67..d45e948 100644 +--- a/config.c ++++ b/config.c +@@ -323,6 +323,7 @@ struct config_item config_tab[] = { + PORT_ITEM_INT("udp_ttl", 1, 1, 255), + PORT_ITEM_INT("udp6_scope", 0x0E, 0x00, 0x0F), + GLOB_ITEM_STR("uds_address", "/var/run/ptp4l"), ++ GLOB_ITEM_STR("uds_ro_address", "/var/run/ptp4lro"), + PORT_ITEM_INT("unicast_listen", 0, 0, 1), + PORT_ITEM_INT("unicast_master_table", 0, 0, INT_MAX), + PORT_ITEM_INT("unicast_req_duration", 3600, 10, INT_MAX), +diff --git a/configs/default.cfg b/configs/default.cfg +index 8c19129..d5bab7d 100644 +--- a/configs/default.cfg ++++ b/configs/default.cfg +@@ -90,6 +90,7 @@ p2p_dst_mac 01:80:C2:00:00:0E + udp_ttl 1 + udp6_scope 0x0E + uds_address /var/run/ptp4l ++uds_ro_address /var/run/ptp4lro + # + # Default interface options + # +diff --git a/ptp4l.8 b/ptp4l.8 +index b179b81..f9bd228 100644 +--- a/ptp4l.8 ++++ b/ptp4l.8 +@@ -615,6 +615,12 @@ is only relevant with IPv6 transport. See RFC 4291. The default is + Specifies the address of the UNIX domain socket for receiving local + management messages. The default is /var/run/ptp4l. + .TP ++.B uds_ro_address ++Specifies the address of the second UNIX domain socket for receiving local ++management messages, which is restricted to GET actions and does not forward ++messages to other ports. Access to this socket can be given to untrusted ++applications for monitoring purposes. The default is /var/run/ptp4lro. ++.TP + .B dscp_event + Defines the Differentiated Services Codepoint (DSCP) to be used for PTP + event messages. Must be a value between 0 and 63. There are several media +-- +2.25.1 + +From 019f50868bc4300c591025d364898035ea9817b9 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 18:20:50 -0300 +Subject: [PATCH 36/47] Rename management ID macros. + +The management ID macros are prefixed with TLV. This is confusing, +because the true TLV tags have the same prefix. Make the code more +readable by using an appropriate prefix the for management IDs. + +Signed-off-by: Richard Cochran + +[commit d86eaa157a0af7d807fc44ce6d91c34084e6902f upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 130 +++++++++++++++++++------------------- + phc2sys.c | 2 +- + pmc.c | 61 +++++++++--------- + pmc_agent.c | 14 ++--- + pmc_common.c | 172 ++++++++++++++++++++++++++------------------------- + port.c | 72 +++++++++++---------- + tlv.c | 62 +++++++++---------- + tlv.h | 132 +++++++++++++++++++-------------------- + 8 files changed, 329 insertions(+), 316 deletions(-) + +diff --git a/clock.c b/clock.c +index 869e35d..534b7e1 100644 +--- a/clock.c ++++ b/clock.c +@@ -361,64 +361,64 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + tlv->id = id; + + switch (id) { +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + text = (struct PTPText *) tlv->data; + text->length = c->desc.userDescription.length; + memcpy(text->text, c->desc.userDescription.text, text->length); + datalen = 1 + text->length; + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + memcpy(tlv->data, &c->dds, sizeof(c->dds)); + datalen = sizeof(c->dds); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + memcpy(tlv->data, &c->cur, sizeof(c->cur)); + datalen = sizeof(c->cur); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + memcpy(tlv->data, &c->dad.pds, sizeof(c->dad.pds)); + datalen = sizeof(c->dad.pds); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + memcpy(tlv->data, &c->tds, sizeof(c->tds)); + datalen = sizeof(c->tds); + break; +- case TLV_PRIORITY1: ++ case MID_PRIORITY1: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.priority1; + datalen = sizeof(*mtd); + break; +- case TLV_PRIORITY2: ++ case MID_PRIORITY2: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.priority2; + datalen = sizeof(*mtd); + break; +- case TLV_DOMAIN: ++ case MID_DOMAIN: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.domainNumber; + datalen = sizeof(*mtd); + break; +- case TLV_SLAVE_ONLY: ++ case MID_SLAVE_ONLY: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.flags & DDS_SLAVE_ONLY; + datalen = sizeof(*mtd); + break; +- case TLV_CLOCK_ACCURACY: ++ case MID_CLOCK_ACCURACY: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.clockQuality.clockAccuracy; + datalen = sizeof(*mtd); + break; +- case TLV_TRACEABILITY_PROPERTIES: ++ case MID_TRACEABILITY_PROPERTIES: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->tds.flags & (TIME_TRACEABLE|FREQ_TRACEABLE); + datalen = sizeof(*mtd); + break; +- case TLV_TIMESCALE_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->tds.flags & PTP_TIMESCALE; + datalen = sizeof(*mtd); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + tsn = (struct time_status_np *) tlv->data; + tsn->master_offset = tmv_to_nanoseconds(c->master_offset); + tsn->ingress_time = tmv_to_nanoseconds(c->ingress_ts); +@@ -435,7 +435,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + tsn->gmIdentity = c->dad.pds.grandmasterIdentity; + datalen = sizeof(*tsn); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) tlv->data; + gsn->clockQuality = c->dds.clockQuality; + gsn->utc_offset = c->utc_offset; +@@ -443,7 +443,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + gsn->time_source = c->time_source; + datalen = sizeof(*gsn); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + if (p != c->uds_rw_port) { + /* Only the UDS-RW port allowed. */ + break; +@@ -452,7 +452,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + clock_get_subscription(c, req, sen->bitmask, &sen->duration); + datalen = sizeof(*sen); + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->local_sync_uncertain; + datalen = sizeof(*mtd); +@@ -504,19 +504,19 @@ static int clock_management_set(struct clock *c, struct port *p, + tlv = (struct management_tlv *) req->management.suffix; + + switch (id) { +- case TLV_PRIORITY1: ++ case MID_PRIORITY1: + mtd = (struct management_tlv_datum *) tlv->data; + c->dds.priority1 = mtd->val; + *changed = 1; + respond = 1; + break; +- case TLV_PRIORITY2: ++ case MID_PRIORITY2: + mtd = (struct management_tlv_datum *) tlv->data; + c->dds.priority2 = mtd->val; + *changed = 1; + respond = 1; + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) tlv->data; + c->dds.clockQuality = gsn->clockQuality; + c->utc_offset = gsn->utc_offset; +@@ -525,12 +525,12 @@ static int clock_management_set(struct clock *c, struct port *p, + *changed = 1; + respond = 1; + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + sen = (struct subscribe_events_np *)tlv->data; + clock_update_subscription(c, req, sen->bitmask, sen->duration); + respond = 1; + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) tlv->data; + switch (mtd->val) { + case SYNC_UNCERTAIN_DONTCARE: +@@ -1448,13 +1448,13 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + return changed; + break; + case SET: +- if (mgt->length == 2 && mgt->id != TLV_NULL_MANAGEMENT) { +- clock_management_send_error(p, msg, TLV_WRONG_LENGTH); ++ if (mgt->length == 2 && mgt->id != MID_NULL_MANAGEMENT) { ++ clock_management_send_error(p, msg, MID_WRONG_LENGTH); + return changed; + } + if (p != c->uds_rw_port) { + /* Sorry, only allowed on the UDS-RW port. */ +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + return changed; + } + if (clock_management_set(c, p, mgt->id, msg, &changed)) +@@ -1463,7 +1463,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + case COMMAND: + if (p != c->uds_rw_port) { + /* Sorry, only allowed on the UDS-RW port. */ +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + return changed; + } + break; +@@ -1472,50 +1472,50 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + } + + switch (mgt->id) { +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + if (p != c->uds_rw_port) { + /* Only the UDS-RW port allowed. */ +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + return 0; + } + } + + switch (mgt->id) { +- case TLV_USER_DESCRIPTION: +- case TLV_SAVE_IN_NON_VOLATILE_STORAGE: +- case TLV_RESET_NON_VOLATILE_STORAGE: +- case TLV_INITIALIZE: +- case TLV_FAULT_LOG: +- case TLV_FAULT_LOG_RESET: +- case TLV_DEFAULT_DATA_SET: +- case TLV_CURRENT_DATA_SET: +- case TLV_PARENT_DATA_SET: +- case TLV_TIME_PROPERTIES_DATA_SET: +- case TLV_PRIORITY1: +- case TLV_PRIORITY2: +- case TLV_DOMAIN: +- case TLV_SLAVE_ONLY: +- case TLV_TIME: +- case TLV_CLOCK_ACCURACY: +- case TLV_UTC_PROPERTIES: +- case TLV_TRACEABILITY_PROPERTIES: +- case TLV_TIMESCALE_PROPERTIES: +- case TLV_PATH_TRACE_LIST: +- case TLV_PATH_TRACE_ENABLE: +- case TLV_GRANDMASTER_CLUSTER_TABLE: +- case TLV_ACCEPTABLE_MASTER_TABLE: +- case TLV_ACCEPTABLE_MASTER_MAX_TABLE_SIZE: +- case TLV_ALTERNATE_TIME_OFFSET_ENABLE: +- case TLV_ALTERNATE_TIME_OFFSET_NAME: +- case TLV_ALTERNATE_TIME_OFFSET_MAX_KEY: +- case TLV_ALTERNATE_TIME_OFFSET_PROPERTIES: +- case TLV_TRANSPARENT_CLOCK_DEFAULT_DATA_SET: +- case TLV_PRIMARY_DOMAIN: +- case TLV_TIME_STATUS_NP: +- case TLV_GRANDMASTER_SETTINGS_NP: +- case TLV_SUBSCRIBE_EVENTS_NP: +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ case MID_USER_DESCRIPTION: ++ case MID_SAVE_IN_NON_VOLATILE_STORAGE: ++ case MID_RESET_NON_VOLATILE_STORAGE: ++ case MID_INITIALIZE: ++ case MID_FAULT_LOG: ++ case MID_FAULT_LOG_RESET: ++ case MID_DEFAULT_DATA_SET: ++ case MID_CURRENT_DATA_SET: ++ case MID_PARENT_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: ++ case MID_PRIORITY1: ++ case MID_PRIORITY2: ++ case MID_DOMAIN: ++ case MID_SLAVE_ONLY: ++ case MID_TIME: ++ case MID_CLOCK_ACCURACY: ++ case MID_UTC_PROPERTIES: ++ case MID_TRACEABILITY_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: ++ case MID_PATH_TRACE_LIST: ++ case MID_PATH_TRACE_ENABLE: ++ case MID_GRANDMASTER_CLUSTER_TABLE: ++ case MID_ACCEPTABLE_MASTER_TABLE: ++ case MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE: ++ case MID_ALTERNATE_TIME_OFFSET_ENABLE: ++ case MID_ALTERNATE_TIME_OFFSET_NAME: ++ case MID_ALTERNATE_TIME_OFFSET_MAX_KEY: ++ case MID_ALTERNATE_TIME_OFFSET_PROPERTIES: ++ case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET: ++ case MID_PRIMARY_DOMAIN: ++ case MID_TIME_STATUS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + break; + default: + answers = 0; +@@ -1528,8 +1528,8 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + } + if (!answers) { + /* IEEE 1588 Interpretation #21 suggests to use +- * TLV_WRONG_VALUE for ports that do not exist */ +- clock_management_send_error(p, msg, TLV_WRONG_VALUE); ++ * MID_WRONG_VALUE for ports that do not exist */ ++ clock_management_send_error(p, msg, MID_WRONG_VALUE); + } + break; + } +@@ -1545,7 +1545,7 @@ void clock_notify_event(struct clock *c, enum notification event) + + switch (event) { + case NOTIFY_TIME_SYNC: +- id = TLV_TIME_STATUS_NP; ++ id = MID_TIME_STATUS_NP; + break; + default: + return; +diff --git a/phc2sys.c b/phc2sys.c +index 569544e..c9fabd7 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -827,7 +827,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + if (mgt_id == excluded) + return 0; + switch (mgt_id) { +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + pds = management_tlv_data(msg); + port = port_get(priv, pds->portIdentity.portNumber); + if (!port) { +diff --git a/pmc.c b/pmc.c +index 3678800..0881178 100644 +--- a/pmc.c ++++ b/pmc.c +@@ -186,12 +186,12 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + goto out; + } + mgt = (struct management_tlv *) msg->management.suffix; +- if (mgt->length == 2 && mgt->id != TLV_NULL_MANAGEMENT) { ++ if (mgt->length == 2 && mgt->id != MID_NULL_MANAGEMENT) { + fprintf(fp, "empty-tlv "); + goto out; + } + switch (mgt->id) { +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + cd = &extra->cd; + fprintf(fp, "CLOCK_DESCRIPTION " + IFMT "clockType 0x%hx" +@@ -215,12 +215,12 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + text2str(cd->userDescription), + bin2str(cd->profileIdentity, PROFILE_ID_LEN)); + break; +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + fprintf(fp, "USER_DESCRIPTION " + IFMT "userDescription %s", + text2str(extra->cd.userDescription)); + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + dds = (struct defaultDS *) mgt->data; + fprintf(fp, "DEFAULT_DATA_SET " + IFMT "twoStepFlag %d" +@@ -244,7 +244,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + cid2str(&dds->clockIdentity), + dds->domainNumber); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + cds = (struct currentDS *) mgt->data; + fprintf(fp, "CURRENT_DATA_SET " + IFMT "stepsRemoved %hd" +@@ -253,7 +253,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + cds->stepsRemoved, cds->offsetFromMaster / 65536.0, + cds->meanPathDelay / 65536.0); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + pds = (struct parentDS *) mgt->data; + fprintf(fp, "PARENT_DATA_SET " + IFMT "parentPortIdentity %s" +@@ -277,7 +277,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + pds->grandmasterPriority2, + cid2str(&pds->grandmasterIdentity)); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + tp = (struct timePropertiesDS *) mgt->data; + fprintf(fp, "TIME_PROPERTIES_DATA_SET " + IFMT "currentUtcOffset %hd" +@@ -297,32 +297,32 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + tp->flags & FREQ_TRACEABLE ? 1 : 0, + tp->timeSource); + break; +- case TLV_PRIORITY1: ++ case MID_PRIORITY1: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "PRIORITY1 " + IFMT "priority1 %hhu", mtd->val); + break; +- case TLV_PRIORITY2: ++ case MID_PRIORITY2: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "PRIORITY2 " + IFMT "priority2 %hhu", mtd->val); + break; +- case TLV_DOMAIN: ++ case MID_DOMAIN: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "DOMAIN " + IFMT "domainNumber %hhu", mtd->val); + break; +- case TLV_SLAVE_ONLY: ++ case MID_SLAVE_ONLY: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "SLAVE_ONLY " + IFMT "slaveOnly %d", mtd->val & DDS_SLAVE_ONLY ? 1 : 0); + break; +- case TLV_CLOCK_ACCURACY: ++ case MID_CLOCK_ACCURACY: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "CLOCK_ACCURACY " + IFMT "clockAccuracy 0x%02hhx", mtd->val); + break; +- case TLV_TRACEABILITY_PROPERTIES: ++ case MID_TRACEABILITY_PROPERTIES: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "TRACEABILITY_PROPERTIES " + IFMT "timeTraceable %d" +@@ -330,12 +330,17 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + mtd->val & TIME_TRACEABLE ? 1 : 0, + mtd->val & FREQ_TRACEABLE ? 1 : 0); + break; +- case TLV_TIMESCALE_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "TIMESCALE_PROPERTIES " + IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_MASTER_ONLY: ++ mtd = (struct management_tlv_datum *) mgt->data; ++ fprintf(fp, "MASTER_ONLY " ++ IFMT "masterOnly %d", mtd->val); ++ break; ++ case MID_TIME_STATUS_NP: + tsn = (struct time_status_np *) mgt->data; + fprintf(fp, "TIME_STATUS_NP " + IFMT "master_offset %" PRId64 +@@ -357,7 +362,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + tsn->gmPresent ? "true" : "false", + cid2str(&tsn->gmIdentity)); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) mgt->data; + fprintf(fp, "GRANDMASTER_SETTINGS_NP " + IFMT "clockClass %hhu" +@@ -383,7 +388,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + gsn->time_flags & FREQ_TRACEABLE ? 1 : 0, + gsn->time_source); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + sen = (struct subscribe_events_np *) mgt->data; + fprintf(fp, "SUBSCRIBE_EVENTS_NP " + IFMT "duration %hu" +@@ -393,12 +398,12 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + event_bitmask_get(sen->bitmask, NOTIFY_PORT_STATE) ? "on" : "off", + event_bitmask_get(sen->bitmask, NOTIFY_TIME_SYNC) ? "on" : "off"); + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "SYNCHRONIZATION_UNCERTAIN_NP " + IFMT "uncertain %hhu", mtd->val); + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + p = (struct portDS *) mgt->data; + if (p->portState > PS_SLAVE) { + p->portState = 0; +@@ -420,7 +425,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + p->logSyncInterval, p->delayMechanism, + p->logMinPdelayReqInterval, p->versionNumber); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pnp = (struct port_ds_np *) mgt->data; + fprintf(fp, "PORT_DATA_SET_NP " + IFMT "neighborPropDelayThresh %u" +@@ -428,7 +433,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + pnp->neighborPropDelayThresh, + pnp->asCapable ? 1 : 0); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + ppn = (struct port_properties_np *) mgt->data; + if (ppn->port_state > PS_SLAVE) { + ppn->port_state = 0; +@@ -443,7 +448,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + ts_str(ppn->timestamping), + text2str(&ppn->interface)); + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + pcp = (struct port_stats_np *) mgt->data; + fprintf(fp, "PORT_STATS_NP " + IFMT "portIdentity %s" +@@ -489,32 +494,32 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + pcp->stats.txMsgType[SIGNALING], + pcp->stats.txMsgType[MANAGEMENT]); + break; +- case TLV_LOG_ANNOUNCE_INTERVAL: ++ case MID_LOG_ANNOUNCE_INTERVAL: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "LOG_ANNOUNCE_INTERVAL " + IFMT "logAnnounceInterval %hhd", mtd->val); + break; +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "ANNOUNCE_RECEIPT_TIMEOUT " + IFMT "announceReceiptTimeout %hhu", mtd->val); + break; +- case TLV_LOG_SYNC_INTERVAL: ++ case MID_LOG_SYNC_INTERVAL: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "LOG_SYNC_INTERVAL " + IFMT "logSyncInterval %hhd", mtd->val); + break; +- case TLV_VERSION_NUMBER: ++ case MID_VERSION_NUMBER: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "VERSION_NUMBER " + IFMT "versionNumber %hhu", mtd->val); + break; +- case TLV_DELAY_MECHANISM: ++ case MID_DELAY_MECHANISM: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "DELAY_MECHANISM " + IFMT "delayMechanism %hhu", mtd->val); + break; +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "LOG_MIN_PDELAY_REQ_INTERVAL " + IFMT "logMinPdelayReqInterval %hhd", mtd->val); +diff --git a/pmc_agent.c b/pmc_agent.c +index 37910b3..3034f65 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -58,7 +58,7 @@ static void send_subscription(struct pmc_agent *node) + memset(&sen, 0, sizeof(sen)); + sen.duration = PMC_SUBSCRIBE_DURATION; + event_bitmask_set(sen.bitmask, NOTIFY_PORT_STATE, TRUE); +- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++ pmc_send_set_action(node->pmc, MID_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + + static int check_clock_identity(struct pmc_agent *node, struct ptp_message *msg) +@@ -149,7 +149,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + if ((pollfd[0].revents & POLLOUT) && + !(pollfd[0].revents & (POLLIN|POLLPRI))) { + switch (ds_id) { +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + send_subscription(node); + break; + default: +@@ -195,7 +195,7 @@ static int renew_subscription(struct pmc_agent *node, int timeout) + struct ptp_message *msg; + int res; + +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ res = run_pmc(node, timeout, MID_SUBSCRIBE_EVENTS_NP, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } +@@ -211,7 +211,7 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + int res; + + while (1) { +- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, MID_PORT_DATA_SET, &msg); + if (res <= 0) + return res; + +@@ -298,7 +298,7 @@ int pmc_agent_query_dds(struct pmc_agent *node, int timeout) + struct defaultDS *dds; + int res; + +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, MID_DEFAULT_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } +@@ -319,7 +319,7 @@ int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, + + pmc_target_port(node->pmc, port); + while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ res = run_pmc(node, timeout, MID_PORT_PROPERTIES_NP, &msg); + if (is_run_pmc_error(res)) { + goto out; + } +@@ -352,7 +352,7 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + struct ptp_message *msg; + int res; + +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ res = run_pmc(node, timeout, MID_TIME_PROPERTIES_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } +diff --git a/pmc_common.c b/pmc_common.c +index c5cd992..7a1dbb4 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -76,61 +76,62 @@ struct management_id { + + struct management_id idtab[] = { + /* Clock management ID values */ +- { "USER_DESCRIPTION", TLV_USER_DESCRIPTION, do_get_action }, +- { "SAVE_IN_NON_VOLATILE_STORAGE", TLV_SAVE_IN_NON_VOLATILE_STORAGE, not_supported }, +- { "RESET_NON_VOLATILE_STORAGE", TLV_RESET_NON_VOLATILE_STORAGE, not_supported }, +- { "INITIALIZE", TLV_INITIALIZE, not_supported }, +- { "FAULT_LOG", TLV_FAULT_LOG, not_supported }, +- { "FAULT_LOG_RESET", TLV_FAULT_LOG_RESET, not_supported }, +- { "DEFAULT_DATA_SET", TLV_DEFAULT_DATA_SET, do_get_action }, +- { "CURRENT_DATA_SET", TLV_CURRENT_DATA_SET, do_get_action }, +- { "PARENT_DATA_SET", TLV_PARENT_DATA_SET, do_get_action }, +- { "TIME_PROPERTIES_DATA_SET", TLV_TIME_PROPERTIES_DATA_SET, do_get_action }, +- { "PRIORITY1", TLV_PRIORITY1, do_set_action }, +- { "PRIORITY2", TLV_PRIORITY2, do_set_action }, +- { "DOMAIN", TLV_DOMAIN, do_get_action }, +- { "SLAVE_ONLY", TLV_SLAVE_ONLY, do_get_action }, +- { "TIME", TLV_TIME, not_supported }, +- { "CLOCK_ACCURACY", TLV_CLOCK_ACCURACY, do_get_action }, +- { "UTC_PROPERTIES", TLV_UTC_PROPERTIES, not_supported }, +- { "TRACEABILITY_PROPERTIES", TLV_TRACEABILITY_PROPERTIES, do_get_action }, +- { "TIMESCALE_PROPERTIES", TLV_TIMESCALE_PROPERTIES, do_get_action }, +- { "PATH_TRACE_LIST", TLV_PATH_TRACE_LIST, not_supported }, +- { "PATH_TRACE_ENABLE", TLV_PATH_TRACE_ENABLE, not_supported }, +- { "GRANDMASTER_CLUSTER_TABLE", TLV_GRANDMASTER_CLUSTER_TABLE, not_supported }, +- { "ACCEPTABLE_MASTER_TABLE", TLV_ACCEPTABLE_MASTER_TABLE, not_supported }, +- { "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", TLV_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported }, +- { "ALTERNATE_TIME_OFFSET_ENABLE", TLV_ALTERNATE_TIME_OFFSET_ENABLE, not_supported }, +- { "ALTERNATE_TIME_OFFSET_NAME", TLV_ALTERNATE_TIME_OFFSET_NAME, not_supported }, +- { "ALTERNATE_TIME_OFFSET_MAX_KEY", TLV_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported }, +- { "ALTERNATE_TIME_OFFSET_PROPERTIES", TLV_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported }, +- { "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", TLV_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported }, +- { "PRIMARY_DOMAIN", TLV_PRIMARY_DOMAIN, not_supported }, +- { "TIME_STATUS_NP", TLV_TIME_STATUS_NP, do_get_action }, +- { "GRANDMASTER_SETTINGS_NP", TLV_GRANDMASTER_SETTINGS_NP, do_set_action }, +- { "SUBSCRIBE_EVENTS_NP", TLV_SUBSCRIBE_EVENTS_NP, do_set_action }, +- { "SYNCHRONIZATION_UNCERTAIN_NP", TLV_SYNCHRONIZATION_UNCERTAIN_NP, do_set_action }, ++ { "USER_DESCRIPTION", MID_USER_DESCRIPTION, do_get_action }, ++ { "SAVE_IN_NON_VOLATILE_STORAGE", MID_SAVE_IN_NON_VOLATILE_STORAGE, not_supported }, ++ { "RESET_NON_VOLATILE_STORAGE", MID_RESET_NON_VOLATILE_STORAGE, not_supported }, ++ { "INITIALIZE", MID_INITIALIZE, not_supported }, ++ { "FAULT_LOG", MID_FAULT_LOG, not_supported }, ++ { "FAULT_LOG_RESET", MID_FAULT_LOG_RESET, not_supported }, ++ { "DEFAULT_DATA_SET", MID_DEFAULT_DATA_SET, do_get_action }, ++ { "CURRENT_DATA_SET", MID_CURRENT_DATA_SET, do_get_action }, ++ { "PARENT_DATA_SET", MID_PARENT_DATA_SET, do_get_action }, ++ { "TIME_PROPERTIES_DATA_SET", MID_TIME_PROPERTIES_DATA_SET, do_get_action }, ++ { "PRIORITY1", MID_PRIORITY1, do_set_action }, ++ { "PRIORITY2", MID_PRIORITY2, do_set_action }, ++ { "DOMAIN", MID_DOMAIN, do_get_action }, ++ { "SLAVE_ONLY", MID_SLAVE_ONLY, do_get_action }, ++ { "TIME", MID_TIME, not_supported }, ++ { "CLOCK_ACCURACY", MID_CLOCK_ACCURACY, do_get_action }, ++ { "UTC_PROPERTIES", MID_UTC_PROPERTIES, not_supported }, ++ { "TRACEABILITY_PROPERTIES", MID_TRACEABILITY_PROPERTIES, do_get_action }, ++ { "TIMESCALE_PROPERTIES", MID_TIMESCALE_PROPERTIES, do_get_action }, ++ { "PATH_TRACE_LIST", MID_PATH_TRACE_LIST, not_supported }, ++ { "PATH_TRACE_ENABLE", MID_PATH_TRACE_ENABLE, not_supported }, ++ { "GRANDMASTER_CLUSTER_TABLE", MID_GRANDMASTER_CLUSTER_TABLE, not_supported }, ++ { "ACCEPTABLE_MASTER_TABLE", MID_ACCEPTABLE_MASTER_TABLE, not_supported }, ++ { "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported }, ++ { "MASTER_ONLY", MID_MASTER_ONLY, do_get_action }, ++ { "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported }, ++ { "PRIMARY_DOMAIN", MID_PRIMARY_DOMAIN, not_supported }, ++ { "TIME_STATUS_NP", MID_TIME_STATUS_NP, do_get_action }, ++ { "GRANDMASTER_SETTINGS_NP", MID_GRANDMASTER_SETTINGS_NP, do_set_action }, ++ { "SUBSCRIBE_EVENTS_NP", MID_SUBSCRIBE_EVENTS_NP, do_set_action }, ++ { "SYNCHRONIZATION_UNCERTAIN_NP", MID_SYNCHRONIZATION_UNCERTAIN_NP, do_set_action }, + /* Port management ID values */ +- { "NULL_MANAGEMENT", TLV_NULL_MANAGEMENT, null_management }, +- { "CLOCK_DESCRIPTION", TLV_CLOCK_DESCRIPTION, do_get_action }, +- { "PORT_DATA_SET", TLV_PORT_DATA_SET, do_get_action }, +- { "LOG_ANNOUNCE_INTERVAL", TLV_LOG_ANNOUNCE_INTERVAL, do_get_action }, +- { "ANNOUNCE_RECEIPT_TIMEOUT", TLV_ANNOUNCE_RECEIPT_TIMEOUT, do_get_action }, +- { "LOG_SYNC_INTERVAL", TLV_LOG_SYNC_INTERVAL, do_get_action }, +- { "VERSION_NUMBER", TLV_VERSION_NUMBER, do_get_action }, +- { "ENABLE_PORT", TLV_ENABLE_PORT, not_supported }, +- { "DISABLE_PORT", TLV_DISABLE_PORT, not_supported }, +- { "UNICAST_NEGOTIATION_ENABLE", TLV_UNICAST_NEGOTIATION_ENABLE, not_supported }, +- { "UNICAST_MASTER_TABLE", TLV_UNICAST_MASTER_TABLE, not_supported }, +- { "UNICAST_MASTER_MAX_TABLE_SIZE", TLV_UNICAST_MASTER_MAX_TABLE_SIZE, not_supported }, +- { "ACCEPTABLE_MASTER_TABLE_ENABLED", TLV_ACCEPTABLE_MASTER_TABLE_ENABLED, not_supported }, +- { "ALTERNATE_MASTER", TLV_ALTERNATE_MASTER, not_supported }, +- { "TRANSPARENT_CLOCK_PORT_DATA_SET", TLV_TRANSPARENT_CLOCK_PORT_DATA_SET, not_supported }, +- { "DELAY_MECHANISM", TLV_DELAY_MECHANISM, do_get_action }, +- { "LOG_MIN_PDELAY_REQ_INTERVAL", TLV_LOG_MIN_PDELAY_REQ_INTERVAL, do_get_action }, +- { "PORT_DATA_SET_NP", TLV_PORT_DATA_SET_NP, do_set_action }, +- { "PORT_STATS_NP", TLV_PORT_STATS_NP, do_get_action }, +- { "PORT_PROPERTIES_NP", TLV_PORT_PROPERTIES_NP, do_get_action }, ++ { "NULL_MANAGEMENT", MID_NULL_MANAGEMENT, null_management }, ++ { "CLOCK_DESCRIPTION", MID_CLOCK_DESCRIPTION, do_get_action }, ++ { "PORT_DATA_SET", MID_PORT_DATA_SET, do_get_action }, ++ { "LOG_ANNOUNCE_INTERVAL", MID_LOG_ANNOUNCE_INTERVAL, do_get_action }, ++ { "ANNOUNCE_RECEIPT_TIMEOUT", MID_ANNOUNCE_RECEIPT_TIMEOUT, do_get_action }, ++ { "LOG_SYNC_INTERVAL", MID_LOG_SYNC_INTERVAL, do_get_action }, ++ { "VERSION_NUMBER", MID_VERSION_NUMBER, do_get_action }, ++ { "ENABLE_PORT", MID_ENABLE_PORT, not_supported }, ++ { "DISABLE_PORT", MID_DISABLE_PORT, not_supported }, ++ { "UNICAST_NEGOTIATION_ENABLE", MID_UNICAST_NEGOTIATION_ENABLE, not_supported }, ++ { "UNICAST_MASTER_TABLE", MID_UNICAST_MASTER_TABLE, not_supported }, ++ { "UNICAST_MASTER_MAX_TABLE_SIZE", MID_UNICAST_MASTER_MAX_TABLE_SIZE, not_supported }, ++ { "ACCEPTABLE_MASTER_TABLE_ENABLED", MID_ACCEPTABLE_MASTER_TABLE_ENABLED, not_supported }, ++ { "ALTERNATE_MASTER", MID_ALTERNATE_MASTER, not_supported }, ++ { "TRANSPARENT_CLOCK_PORT_DATA_SET", MID_TRANSPARENT_CLOCK_PORT_DATA_SET, not_supported }, ++ { "DELAY_MECHANISM", MID_DELAY_MECHANISM, do_get_action }, ++ { "LOG_MIN_PDELAY_REQ_INTERVAL", MID_LOG_MIN_PDELAY_REQ_INTERVAL, do_get_action }, ++ { "PORT_DATA_SET_NP", MID_PORT_DATA_SET_NP, do_set_action }, ++ { "PORT_STATS_NP", MID_PORT_STATS_NP, do_get_action }, ++ { "PORT_PROPERTIES_NP", MID_PORT_PROPERTIES_NP, do_get_action }, + }; + + static void do_get_action(struct pmc *pmc, int action, int index, char *str) +@@ -167,8 +168,8 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + return; + } + switch (code) { +- case TLV_PRIORITY1: +- case TLV_PRIORITY2: ++ case MID_PRIORITY1: ++ case MID_PRIORITY2: + cnt = sscanf(str, " %*s %*s %hhu", &mtd.val); + if (cnt != 1) { + fprintf(stderr, "%s SET needs 1 value\n", +@@ -177,7 +178,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + } + pmc_send_set_action(pmc, code, &mtd, sizeof(mtd)); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + cnt = sscanf(str, " %*s %*s " + "clockClass %hhu " + "clockAccuracy %hhx " +@@ -221,7 +222,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + gsn.time_flags |= FREQ_TRACEABLE; + pmc_send_set_action(pmc, code, &gsn, sizeof(gsn)); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + memset(&sen, 0, sizeof(sen)); + cnt = sscanf(str, " %*s %*s " + "duration %hu " +@@ -243,7 +244,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + } + pmc_send_set_action(pmc, code, &sen, sizeof(sen)); + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + cnt = sscanf(str, " %*s %*s %hhu", &mtd.val); + if (cnt != 1) { + fprintf(stderr, "%s SET needs 1 value\n", +@@ -264,7 +265,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + SYNC_UNCERTAIN_DONTCARE); + } + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + cnt = sscanf(str, " %*s %*s " + "neighborPropDelayThresh %u " + "asCapable %d ", +@@ -488,53 +489,54 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id) + return len; + + switch (id) { +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + len += EMPTY_PTP_TEXT; + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + len += sizeof(struct defaultDS); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + len += sizeof(struct currentDS); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + len += sizeof(struct parentDS); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + len += sizeof(struct timePropertiesDS); + break; +- case TLV_PRIORITY1: +- case TLV_PRIORITY2: +- case TLV_DOMAIN: +- case TLV_SLAVE_ONLY: +- case TLV_CLOCK_ACCURACY: +- case TLV_TRACEABILITY_PROPERTIES: +- case TLV_TIMESCALE_PROPERTIES: ++ case MID_PRIORITY1: ++ case MID_PRIORITY2: ++ case MID_DOMAIN: ++ case MID_SLAVE_ONLY: ++ case MID_CLOCK_ACCURACY: ++ case MID_TRACEABILITY_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: ++ case MID_MASTER_ONLY: + len += sizeof(struct management_tlv_datum); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + len += sizeof(struct time_status_np); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + len += sizeof(struct grandmaster_settings_np); + break; +- case TLV_NULL_MANAGEMENT: ++ case MID_NULL_MANAGEMENT: + break; +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + len += EMPTY_CLOCK_DESCRIPTION; + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + len += sizeof(struct portDS); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + len += sizeof(struct port_ds_np); + break; +- case TLV_LOG_ANNOUNCE_INTERVAL: +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: +- case TLV_LOG_SYNC_INTERVAL: +- case TLV_VERSION_NUMBER: +- case TLV_DELAY_MECHANISM: +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: ++ case MID_LOG_ANNOUNCE_INTERVAL: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_LOG_SYNC_INTERVAL: ++ case MID_VERSION_NUMBER: ++ case MID_DELAY_MECHANISM: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: + len += sizeof(struct management_tlv_datum); + break; + } +@@ -574,7 +576,7 @@ int pmc_send_get_action(struct pmc *pmc, int id) + extra->tlv = (struct TLV *) msg->management.suffix; + msg_tlv_attach(msg, extra); + +- if (id == TLV_CLOCK_DESCRIPTION && !pmc->zero_length_gets) { ++ if (id == MID_CLOCK_DESCRIPTION && !pmc->zero_length_gets) { + /* + * Make sure the tlv_extra pointers dereferenced in + * mgt_pre_send() do point to something. +diff --git a/port.c b/port.c +index f22bff4..b0e4ef8 100644 +--- a/port.c ++++ b/port.c +@@ -810,10 +810,10 @@ static int port_management_fill_response(struct port *target, + tlv->id = id; + + switch (id) { +- case TLV_NULL_MANAGEMENT: ++ case MID_NULL_MANAGEMENT: + datalen = 0; + break; +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + cd = &extra->cd; + buf = tlv->data; + cd->clockType = (UInteger16 *) buf; +@@ -873,7 +873,7 @@ static int port_management_fill_response(struct port *target, + buf += PROFILE_ID_LEN; + datalen = buf - tlv->data; + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + pds = (struct portDS *) tlv->data; + pds->portIdentity = target->portIdentity; + if (target->state == PS_GRAND_MASTER) { +@@ -895,27 +895,32 @@ static int port_management_fill_response(struct port *target, + pds->versionNumber = target->versionNumber; + datalen = sizeof(*pds); + break; +- case TLV_LOG_ANNOUNCE_INTERVAL: ++ case MID_LOG_ANNOUNCE_INTERVAL: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->logAnnounceInterval; + datalen = sizeof(*mtd); + break; +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->announceReceiptTimeout; + datalen = sizeof(*mtd); + break; +- case TLV_LOG_SYNC_INTERVAL: ++ case MID_LOG_SYNC_INTERVAL: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->logSyncInterval; + datalen = sizeof(*mtd); + break; +- case TLV_VERSION_NUMBER: ++ case MID_VERSION_NUMBER: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->versionNumber; + datalen = sizeof(*mtd); + break; +- case TLV_DELAY_MECHANISM: ++ case MID_MASTER_ONLY: ++ mtd = (struct management_tlv_datum *) tlv->data; ++ mtd->val = target->master_only; ++ datalen = sizeof(*mtd); ++ break; ++ case MID_DELAY_MECHANISM: + mtd = (struct management_tlv_datum *) tlv->data; + if (target->delayMechanism) + mtd->val = target->delayMechanism; +@@ -923,18 +928,18 @@ static int port_management_fill_response(struct port *target, + mtd->val = DM_E2E; + datalen = sizeof(*mtd); + break; +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->logMinPdelayReqInterval; + datalen = sizeof(*mtd); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pdsnp = (struct port_ds_np *) tlv->data; + pdsnp->neighborPropDelayThresh = target->neighborPropDelayThresh; + pdsnp->asCapable = target->asCapable; + datalen = sizeof(*pdsnp); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + ppn = (struct port_properties_np *)tlv->data; + ppn->portIdentity = target->portIdentity; + if (target->state == PS_GRAND_MASTER) +@@ -946,7 +951,7 @@ static int port_management_fill_response(struct port *target, + ptp_text_set(&ppn->interface, ts_label); + datalen = sizeof(*ppn) + ppn->interface.length; + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + psn = (struct port_stats_np *)tlv->data; + psn->portIdentity = target->portIdentity; + psn->stats = target->stats; +@@ -1000,7 +1005,7 @@ static int port_management_set(struct port *target, + tlv = (struct management_tlv *) req->management.suffix; + + switch (id) { +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pdsnp = (struct port_ds_np *) tlv->data; + target->neighborPropDelayThresh = pdsnp->neighborPropDelayThresh; + respond = 1; +@@ -2858,27 +2863,28 @@ int port_manage(struct port *p, struct port *ingress, struct ptp_message *msg) + } + + switch (mgt->id) { +- case TLV_NULL_MANAGEMENT: +- case TLV_CLOCK_DESCRIPTION: +- case TLV_PORT_DATA_SET: +- case TLV_LOG_ANNOUNCE_INTERVAL: +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: +- case TLV_LOG_SYNC_INTERVAL: +- case TLV_VERSION_NUMBER: +- case TLV_ENABLE_PORT: +- case TLV_DISABLE_PORT: +- case TLV_UNICAST_NEGOTIATION_ENABLE: +- case TLV_UNICAST_MASTER_TABLE: +- case TLV_UNICAST_MASTER_MAX_TABLE_SIZE: +- case TLV_ACCEPTABLE_MASTER_TABLE_ENABLED: +- case TLV_ALTERNATE_MASTER: +- case TLV_TRANSPARENT_CLOCK_PORT_DATA_SET: +- case TLV_DELAY_MECHANISM: +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: +- port_management_send_error(p, ingress, msg, TLV_NOT_SUPPORTED); ++ case MID_NULL_MANAGEMENT: ++ case MID_CLOCK_DESCRIPTION: ++ case MID_PORT_DATA_SET: ++ case MID_LOG_ANNOUNCE_INTERVAL: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_LOG_SYNC_INTERVAL: ++ case MID_VERSION_NUMBER: ++ case MID_ENABLE_PORT: ++ case MID_DISABLE_PORT: ++ case MID_UNICAST_NEGOTIATION_ENABLE: ++ case MID_UNICAST_MASTER_TABLE: ++ case MID_UNICAST_MASTER_MAX_TABLE_SIZE: ++ case MID_ACCEPTABLE_MASTER_TABLE_ENABLED: ++ case MID_ALTERNATE_MASTER: ++ case MID_MASTER_ONLY: ++ case MID_TRANSPARENT_CLOCK_PORT_DATA_SET: ++ case MID_DELAY_MECHANISM: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: ++ port_management_send_error(p, ingress, msg, MID_NOT_SUPPORTED); + break; + default: +- port_management_send_error(p, ingress, msg, TLV_NO_SUCH_ID); ++ port_management_send_error(p, ingress, msg, MID_NO_SUCH_ID); + return -1; + } + return 1; +@@ -2983,7 +2989,7 @@ void port_notify_event(struct port *p, enum notification event) + + switch (event) { + case NOTIFY_PORT_STATE: +- id = TLV_PORT_DATA_SET; ++ id = MID_PORT_DATA_SET; + break; + default: + return; +diff --git a/tlv.c b/tlv.c +index 738e404..2526394 100644 +--- a/tlv.c ++++ b/tlv.c +@@ -129,7 +129,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + uint8_t *buf; + uint16_t u16; + switch (m->id) { +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + cd = &extra->cd; + buf = m->data; + len = data_len; +@@ -228,14 +228,14 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + + extra_len = buf - m->data; + break; +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + if (data_len < sizeof(struct PTPText)) + goto bad_length; + extra->cd.userDescription = (struct PTPText *) m->data; + extra_len = sizeof(struct PTPText); + extra_len += extra->cd.userDescription->length; + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + if (data_len != sizeof(struct defaultDS)) + goto bad_length; + dds = (struct defaultDS *) m->data; +@@ -243,7 +243,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + dds->clockQuality.offsetScaledLogVariance = + ntohs(dds->clockQuality.offsetScaledLogVariance); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + if (data_len != sizeof(struct currentDS)) + goto bad_length; + cds = (struct currentDS *) m->data; +@@ -251,7 +251,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + cds->offsetFromMaster = net2host64(cds->offsetFromMaster); + cds->meanPathDelay = net2host64(cds->meanPathDelay); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + if (data_len != sizeof(struct parentDS)) + goto bad_length; + pds = (struct parentDS *) m->data; +@@ -264,20 +264,20 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + pds->grandmasterClockQuality.offsetScaledLogVariance = + ntohs(pds->grandmasterClockQuality.offsetScaledLogVariance); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + if (data_len != sizeof(struct timePropertiesDS)) + goto bad_length; + tp = (struct timePropertiesDS *) m->data; + tp->currentUtcOffset = ntohs(tp->currentUtcOffset); + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + if (data_len != sizeof(struct portDS)) + goto bad_length; + p = (struct portDS *) m->data; + p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber); + p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + if (data_len != sizeof(struct time_status_np)) + goto bad_length; + tsn = (struct time_status_np *) m->data; +@@ -289,7 +289,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + scaled_ns_n2h(&tsn->lastGmPhaseChange); + tsn->gmPresent = ntohl(tsn->gmPresent); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + if (data_len != sizeof(struct grandmaster_settings_np)) + goto bad_length; + gsn = (struct grandmaster_settings_np *) m->data; +@@ -297,20 +297,20 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + ntohs(gsn->clockQuality.offsetScaledLogVariance); + gsn->utc_offset = ntohs(gsn->utc_offset); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + if (data_len != sizeof(struct port_ds_np)) + goto bad_length; + pdsnp = (struct port_ds_np *) m->data; + pdsnp->neighborPropDelayThresh = ntohl(pdsnp->neighborPropDelayThresh); + pdsnp->asCapable = ntohl(pdsnp->asCapable); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + if (data_len != sizeof(struct subscribe_events_np)) + goto bad_length; + sen = (struct subscribe_events_np *)m->data; + sen->duration = ntohs(sen->duration); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + if (data_len < sizeof(struct port_properties_np)) + goto bad_length; + ppn = (struct port_properties_np *)m->data; +@@ -318,7 +318,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + extra_len = sizeof(struct port_properties_np); + extra_len += ppn->interface.length; + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + if (data_len < sizeof(struct port_stats_np)) + goto bad_length; + psn = (struct port_stats_np *)m->data; +@@ -326,12 +326,12 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + ntohs(psn->portIdentity.portNumber); + extra_len = sizeof(struct port_stats_np); + break; +- case TLV_SAVE_IN_NON_VOLATILE_STORAGE: +- case TLV_RESET_NON_VOLATILE_STORAGE: +- case TLV_INITIALIZE: +- case TLV_FAULT_LOG_RESET: +- case TLV_ENABLE_PORT: +- case TLV_DISABLE_PORT: ++ case MID_SAVE_IN_NON_VOLATILE_STORAGE: ++ case MID_RESET_NON_VOLATILE_STORAGE: ++ case MID_INITIALIZE: ++ case MID_FAULT_LOG_RESET: ++ case MID_ENABLE_PORT: ++ case MID_DISABLE_PORT: + if (data_len != 0) + goto bad_length; + break; +@@ -362,7 +362,7 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + struct port_stats_np *psn; + struct mgmt_clock_description *cd; + switch (m->id) { +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + if (extra) { + cd = &extra->cd; + flip16(cd->clockType); +@@ -371,19 +371,19 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + flip16(&cd->protocolAddress->addressLength); + } + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + dds = (struct defaultDS *) m->data; + dds->numberPorts = htons(dds->numberPorts); + dds->clockQuality.offsetScaledLogVariance = + htons(dds->clockQuality.offsetScaledLogVariance); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + cds = (struct currentDS *) m->data; + cds->stepsRemoved = htons(cds->stepsRemoved); + cds->offsetFromMaster = host2net64(cds->offsetFromMaster); + cds->meanPathDelay = host2net64(cds->meanPathDelay); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + pds = (struct parentDS *) m->data; + pds->parentPortIdentity.portNumber = + htons(pds->parentPortIdentity.portNumber); +@@ -394,16 +394,16 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + pds->grandmasterClockQuality.offsetScaledLogVariance = + htons(pds->grandmasterClockQuality.offsetScaledLogVariance); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + tp = (struct timePropertiesDS *) m->data; + tp->currentUtcOffset = htons(tp->currentUtcOffset); + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + p = (struct portDS *) m->data; + p->portIdentity.portNumber = htons(p->portIdentity.portNumber); + p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + tsn = (struct time_status_np *) m->data; + tsn->master_offset = host2net64(tsn->master_offset); + tsn->ingress_time = host2net64(tsn->ingress_time); +@@ -413,26 +413,26 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + scaled_ns_h2n(&tsn->lastGmPhaseChange); + tsn->gmPresent = htonl(tsn->gmPresent); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) m->data; + gsn->clockQuality.offsetScaledLogVariance = + htons(gsn->clockQuality.offsetScaledLogVariance); + gsn->utc_offset = htons(gsn->utc_offset); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pdsnp = (struct port_ds_np *) m->data; + pdsnp->neighborPropDelayThresh = htonl(pdsnp->neighborPropDelayThresh); + pdsnp->asCapable = htonl(pdsnp->asCapable); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + sen = (struct subscribe_events_np *)m->data; + sen->duration = htons(sen->duration); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + ppn = (struct port_properties_np *)m->data; + ppn->portIdentity.portNumber = htons(ppn->portIdentity.portNumber); + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + psn = (struct port_stats_np *)m->data; + psn->portIdentity.portNumber = + htons(psn->portIdentity.portNumber); +diff --git a/tlv.h b/tlv.h +index a205119..97615fd 100644 +--- a/tlv.h ++++ b/tlv.h +@@ -64,76 +64,76 @@ enum management_action { + }; + + /* Clock management ID values */ +-#define TLV_USER_DESCRIPTION 0x0002 +-#define TLV_SAVE_IN_NON_VOLATILE_STORAGE 0x0003 +-#define TLV_RESET_NON_VOLATILE_STORAGE 0x0004 +-#define TLV_INITIALIZE 0x0005 +-#define TLV_FAULT_LOG 0x0006 +-#define TLV_FAULT_LOG_RESET 0x0007 +-#define TLV_DEFAULT_DATA_SET 0x2000 +-#define TLV_CURRENT_DATA_SET 0x2001 +-#define TLV_PARENT_DATA_SET 0x2002 +-#define TLV_TIME_PROPERTIES_DATA_SET 0x2003 +-#define TLV_PRIORITY1 0x2005 +-#define TLV_PRIORITY2 0x2006 +-#define TLV_DOMAIN 0x2007 +-#define TLV_SLAVE_ONLY 0x2008 +-#define TLV_TIME 0x200F +-#define TLV_CLOCK_ACCURACY 0x2010 +-#define TLV_UTC_PROPERTIES 0x2011 +-#define TLV_TRACEABILITY_PROPERTIES 0x2012 +-#define TLV_TIMESCALE_PROPERTIES 0x2013 +-#define TLV_PATH_TRACE_LIST 0x2015 +-#define TLV_PATH_TRACE_ENABLE 0x2016 +-#define TLV_GRANDMASTER_CLUSTER_TABLE 0x2017 +-#define TLV_ACCEPTABLE_MASTER_TABLE 0x201A +-#define TLV_ACCEPTABLE_MASTER_MAX_TABLE_SIZE 0x201C +-#define TLV_ALTERNATE_TIME_OFFSET_ENABLE 0x201E +-#define TLV_ALTERNATE_TIME_OFFSET_NAME 0x201F +-#define TLV_ALTERNATE_TIME_OFFSET_MAX_KEY 0x2020 +-#define TLV_ALTERNATE_TIME_OFFSET_PROPERTIES 0x2021 +-#define TLV_EXTERNAL_PORT_CONFIGURATION_ENABLED 0x3000 +-#define TLV_HOLDOVER_UPGRADE_ENABLE 0x3002 +-#define TLV_TRANSPARENT_CLOCK_DEFAULT_DATA_SET 0x4000 +-#define TLV_PRIMARY_DOMAIN 0x4002 +-#define TLV_TIME_STATUS_NP 0xC000 +-#define TLV_GRANDMASTER_SETTINGS_NP 0xC001 +-#define TLV_SUBSCRIBE_EVENTS_NP 0xC003 +-#define TLV_SYNCHRONIZATION_UNCERTAIN_NP 0xC006 ++#define MID_USER_DESCRIPTION 0x0002 ++#define MID_SAVE_IN_NON_VOLATILE_STORAGE 0x0003 ++#define MID_RESET_NON_VOLATILE_STORAGE 0x0004 ++#define MID_INITIALIZE 0x0005 ++#define MID_FAULT_LOG 0x0006 ++#define MID_FAULT_LOG_RESET 0x0007 ++#define MID_DEFAULT_DATA_SET 0x2000 ++#define MID_CURRENT_DATA_SET 0x2001 ++#define MID_PARENT_DATA_SET 0x2002 ++#define MID_TIME_PROPERTIES_DATA_SET 0x2003 ++#define MID_PRIORITY1 0x2005 ++#define MID_PRIORITY2 0x2006 ++#define MID_DOMAIN 0x2007 ++#define MID_SLAVE_ONLY 0x2008 ++#define MID_TIME 0x200F ++#define MID_CLOCK_ACCURACY 0x2010 ++#define MID_UTC_PROPERTIES 0x2011 ++#define MID_TRACEABILITY_PROPERTIES 0x2012 ++#define MID_TIMESCALE_PROPERTIES 0x2013 ++#define MID_PATH_TRACE_LIST 0x2015 ++#define MID_PATH_TRACE_ENABLE 0x2016 ++#define MID_GRANDMASTER_CLUSTER_TABLE 0x2017 ++#define MID_ACCEPTABLE_MASTER_TABLE 0x201A ++#define MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE 0x201C ++#define MID_ALTERNATE_TIME_OFFSET_ENABLE 0x201E ++#define MID_ALTERNATE_TIME_OFFSET_NAME 0x201F ++#define MID_ALTERNATE_TIME_OFFSET_MAX_KEY 0x2020 ++#define MID_ALTERNATE_TIME_OFFSET_PROPERTIES 0x2021 ++#define MID_EXTERNAL_PORT_CONFIGURATION_ENABLED 0x3000 ++#define MID_HOLDOVER_UPGRADE_ENABLE 0x3002 ++#define MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET 0x4000 ++#define MID_PRIMARY_DOMAIN 0x4002 ++#define MID_TIME_STATUS_NP 0xC000 ++#define MID_GRANDMASTER_SETTINGS_NP 0xC001 ++#define MID_SUBSCRIBE_EVENTS_NP 0xC003 ++#define MID_SYNCHRONIZATION_UNCERTAIN_NP 0xC006 + + /* Port management ID values */ +-#define TLV_NULL_MANAGEMENT 0x0000 +-#define TLV_CLOCK_DESCRIPTION 0x0001 +-#define TLV_PORT_DATA_SET 0x2004 +-#define TLV_LOG_ANNOUNCE_INTERVAL 0x2009 +-#define TLV_ANNOUNCE_RECEIPT_TIMEOUT 0x200A +-#define TLV_LOG_SYNC_INTERVAL 0x200B +-#define TLV_VERSION_NUMBER 0x200C +-#define TLV_ENABLE_PORT 0x200D +-#define TLV_DISABLE_PORT 0x200E +-#define TLV_UNICAST_NEGOTIATION_ENABLE 0x2014 +-#define TLV_UNICAST_MASTER_TABLE 0x2018 +-#define TLV_UNICAST_MASTER_MAX_TABLE_SIZE 0x2019 +-#define TLV_ACCEPTABLE_MASTER_TABLE_ENABLED 0x201B +-#define TLV_ALTERNATE_MASTER 0x201D +-#define TLV_MASTER_ONLY 0x3001 +-#define TLV_EXT_PORT_CONFIG_PORT_DATA_SET 0x3003 +-#define TLV_SLAVE_EVENT_MONITORING 0x3004 // TODO - proposed value, missing in 1588 v2.1 +-#define TLV_TRANSPARENT_CLOCK_PORT_DATA_SET 0x4001 +-#define TLV_DELAY_MECHANISM 0x6000 +-#define TLV_LOG_MIN_PDELAY_REQ_INTERVAL 0x6001 +-#define TLV_PORT_DATA_SET_NP 0xC002 +-#define TLV_PORT_PROPERTIES_NP 0xC004 +-#define TLV_PORT_STATS_NP 0xC005 ++#define MID_NULL_MANAGEMENT 0x0000 ++#define MID_CLOCK_DESCRIPTION 0x0001 ++#define MID_PORT_DATA_SET 0x2004 ++#define MID_LOG_ANNOUNCE_INTERVAL 0x2009 ++#define MID_ANNOUNCE_RECEIPT_TIMEOUT 0x200A ++#define MID_LOG_SYNC_INTERVAL 0x200B ++#define MID_VERSION_NUMBER 0x200C ++#define MID_ENABLE_PORT 0x200D ++#define MID_DISABLE_PORT 0x200E ++#define MID_UNICAST_NEGOTIATION_ENABLE 0x2014 ++#define MID_UNICAST_MASTER_TABLE 0x2018 ++#define MID_UNICAST_MASTER_MAX_TABLE_SIZE 0x2019 ++#define MID_ACCEPTABLE_MASTER_TABLE_ENABLED 0x201B ++#define MID_ALTERNATE_MASTER 0x201D ++#define MID_MASTER_ONLY 0x3001 ++#define MID_EXT_PORT_CONFIG_PORT_DATA_SET 0x3003 ++#define MID_SLAVE_EVENT_MONITORING 0x3004 // TODO - proposed value, missing in 1588 v2.1 ++#define MID_TRANSPARENT_CLOCK_PORT_DATA_SET 0x4001 ++#define MID_DELAY_MECHANISM 0x6000 ++#define MID_LOG_MIN_PDELAY_REQ_INTERVAL 0x6001 ++#define MID_PORT_DATA_SET_NP 0xC002 ++#define MID_PORT_PROPERTIES_NP 0xC004 ++#define MID_PORT_STATS_NP 0xC005 + + /* Management error ID values */ +-#define TLV_RESPONSE_TOO_BIG 0x0001 +-#define TLV_NO_SUCH_ID 0x0002 +-#define TLV_WRONG_LENGTH 0x0003 +-#define TLV_WRONG_VALUE 0x0004 +-#define TLV_NOT_SETABLE 0x0005 +-#define TLV_NOT_SUPPORTED 0x0006 +-#define TLV_GENERAL_ERROR 0xFFFE ++#define MID_RESPONSE_TOO_BIG 0x0001 ++#define MID_NO_SUCH_ID 0x0002 ++#define MID_WRONG_LENGTH 0x0003 ++#define MID_WRONG_VALUE 0x0004 ++#define MID_NOT_SETABLE 0x0005 ++#define MID_NOT_SUPPORTED 0x0006 ++#define MID_GENERAL_ERROR 0xFFFE + + /* Values for the SYNCHRONIZATION_UNCERTAIN_NP management TLV */ + #define SYNC_UNCERTAIN_DONTCARE 0xff +-- +2.25.1 + +From 2a6ddfe1b9700ce8e0c62da8a7a4f2edcd4e1cad Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Sun, 18 Jun 2023 20:58:34 -0300 +Subject: [PATCH 37/47] Enhance phc2sys to accept multiple ptp4l inputs + +A new configuration option called ha_enabled was created. When it is set 1 +multiple ptp4l inputs are accepted and the high availability algorithms +are enabled. + +In addition to ha_enabled 1 a set of interfaces must also be provided. +Each interface is one-to-one mapped to a clock source, and must be +associated to an unique ptp4l instance using the ha_uds_address +configuration option. + +For example: + +ha_enabled 1 + +[ens1f1] +ha_uds_address /var/run/ptp4l-ptp-inst1 + +[ens1f2] +ha_uds_address /var/run/ptp4l-ptp-inst2 + +A maximum of 128 interfaces is supported. + +Regression: verify non HA phc2sys configuration +PASS: Verify auto configuration is still accepted. +PASS: Verify manual configuration with a single clock is still accepted. +PASS: Verify mix of manual and auto configuration is denied. +PASS: Verify manual configuration with zero clock sources is denied. + +Test Plan: verify HA configuration +PASS: Verify HA is disabled by default. +PASS: Verify HA configuration with 1 or more clock source is accepted. +PASS: Verify ha_uds_address default value. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek +Signed-off-by: Andre Mauricio Zelak + +[commit 705fe12b294216c7b5797f48d83ff97fcc076294 upstream] +[commit e730f006cb56ac55932220c1afff5470de875200 upstream] +[commit df8fa0492771f6babb75254619337edb6041daea upstream] +[commit 0201340fa5abc17634bfb4d0b2a386d218d3095b upstream] +[commit dd7400f4eb548dfb2acfb6ebaf53a6d77b9c5da2 upstream] +[commit 904fb44ecebd448f9b2952dd287ac2b5db8249db upstream] +[commit 56dcd671d5241b589dc44b776fec9b2752496477 upstream] +[commit 7e5617afe8837b77629cc04c9e3abb38ae64678c upstream] +[commit 5ea8af40b8b5e4680d8a8e1a19482c28f95ce6b3 upstream] +[commit 3d38367a3151845ec543ab9125e2d0a0aefa2f56 upstream] +[commit 17a4d6805597fd6ddb911b8246e7b131a42f9191 upstream] +[commit 1650d972f4fe9bb39807536df2594d1a85aabf9c upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 17 +++ + config.h | 2 + + phc2sys.c | 337 +++++++++++++++++++++++++++++++++++++--------------- + pmc_agent.c | 17 --- + pmc_agent.h | 21 +++- + uds.c | 19 ++- + 6 files changed, 294 insertions(+), 119 deletions(-) + +diff --git a/config.c b/config.c +index d45e948..b97e5d7 100644 +--- a/config.c ++++ b/config.c +@@ -250,6 +250,8 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("G.8275.defaultDS.localPriority", 128, 1, UINT8_MAX), + PORT_ITEM_INT("G.8275.portDS.localPriority", 128, 1, UINT8_MAX), + GLOB_ITEM_INT("gmCapable", 1, 0, 1), ++ GLOB_ITEM_INT("ha_enabled", 0, 0, 1), ++ PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), + GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu), + PORT_ITEM_INT("hybrid_e2e", 0, 0, 1), + PORT_ITEM_INT("ignore_source_id", 0, 0, 1), +@@ -996,6 +998,21 @@ char *config_get_string(struct config *cfg, const char *section, + return ci->val.s; + } + ++unsigned int config_get_interfaces(struct config *cfg, char *interfaces[], unsigned int max) ++{ ++ struct interface *iface = NULL; ++ unsigned int counter = 0; ++ ++ STAILQ_FOREACH(iface, &cfg->interfaces, list) { ++ if (counter == max) { ++ pr_err("bug: too many interfaces!"); ++ return (unsigned int)-1; ++ } ++ interfaces[counter++] = interface_name(iface); ++ } ++ return counter; ++} ++ + int config_harmonize_onestep(struct config *cfg) + { + enum timestamp_type tstype = config_get_int(cfg, NULL, "time_stamping"); +diff --git a/config.h b/config.h +index 14d2f64..645fb42 100644 +--- a/config.h ++++ b/config.h +@@ -64,6 +64,8 @@ int config_get_int(struct config *cfg, const char *section, + char *config_get_string(struct config *cfg, const char *section, + const char *option); + ++unsigned int config_get_interfaces(struct config *cfg, char *interfaces[], unsigned int max); ++ + int config_harmonize_onestep(struct config *cfg); + + static inline struct option *config_long_options(struct config *cfg) +diff --git a/phc2sys.c b/phc2sys.c +index c9fabd7..a4afe01 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -64,6 +64,12 @@ + + #define PHC_PPS_OFFSET_LIMIT 10000000 + ++#define MAX_SRC_CLOCKS 128 ++ ++#define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) || (((unsigned int) index) & 0xFF)) ++#define PORT_ID_TO_PORT(id) ((((unsigned int) id) >> 8) & 0xFF) ++#define PORT_ID_TO_INDEX(id) (((unsigned int) id) & 0xFF) ++ + struct clock { + LIST_ENTRY(clock) list; + LIST_ENTRY(clock) dst_list; +@@ -85,6 +91,7 @@ struct clock { + struct stats *freq_stats; + struct stats *delay_stats; + struct clockcheck *sanity_check; ++ struct pmc_agent *node; + }; + + struct port { +@@ -103,7 +110,7 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; +- struct pmc_agent *node; ++ LIST_HEAD(pmc_agent_head, pmc_agent) pmc_agents; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -260,6 +267,18 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number) + return NULL; + } + ++static struct port *port_get_by_clock(struct phc2sys_private *priv, struct clock * clock) ++{ ++ struct port *p, *port = NULL; ++ LIST_FOREACH(p, &priv->ports, list) { ++ if (p->clock == clock) { ++ port = p; ++ break; ++ } ++ } ++ return port; ++} ++ + static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + char *device) + { +@@ -293,6 +312,42 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + return p; + } + ++static struct pmc_agent *pmc_agent_get(struct phc2sys_private *priv, unsigned int index) ++{ ++ struct pmc_agent *node; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ if (node->index == index) { ++ break; ++ } ++ } ++ return node; ++} ++ ++static struct pmc_agent *pmc_agent_add(struct phc2sys_private *priv, unsigned int index) ++{ ++ struct pmc_agent *node = pmc_agent_get(priv, index); ++ if (node) ++ return node; ++ ++ node = pmc_agent_create(); ++ if (!node) { ++ pr_err("failed to allocate memory for a pmc agent"); ++ return NULL; ++ } ++ ++ node->index = index; ++ LIST_INSERT_HEAD(&priv->pmc_agents, node, list); ++ return node; ++} ++ ++static void pmc_agent_cleanup(struct phc2sys_private *priv) ++{ ++ struct pmc_agent *node, *tmp; ++ LIST_FOREACH_SAFE(node, &priv->pmc_agents, list, tmp) { ++ pmc_agent_destroy(node); ++ } ++} ++ + static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + int new_state) + { +@@ -302,12 +357,17 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + struct sk_ts_info ts_info; + char iface[IFNAMSIZ]; + clockid_t clkid = CLOCK_INVALID; ++ struct pmc_agent *node; ++ unsigned int pmc_index; + + LIST_FOREACH(p, &priv->ports, list) { + if (p->clock != clock) { + continue; + } +- err = pmc_agent_query_port_properties(priv->node, 1000, ++ ++ pmc_index = PORT_ID_TO_INDEX(p->number); ++ node = pmc_agent_get(priv, pmc_index); ++ err = pmc_agent_query_port_properties(node, 1000, + p->number, &state, + ×tamping, iface); + if (!err) { +@@ -638,12 +698,13 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + int64_t pps_offset, phc_offset, phc_delay; + uint64_t pps_ts, phc_ts; + clockid_t src = priv->master->clkid; ++ struct pmc_agent *node = LIST_FIRST(&priv->pmc_agents); + + priv->master->source_label = "pps"; + + if (src == CLOCK_INVALID) { + /* The sync offset can't be applied with PPS alone. */ +- pmc_agent_set_sync_offset(priv->node, 0); ++ pmc_agent_set_sync_offset(node, 0); + } else { + enable_pps_output(priv->master->clkid); + } +@@ -674,7 +735,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (pmc_agent_update(priv->node) < 0) ++ if (pmc_agent_update(node) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -706,6 +767,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + uint64_t ts; + int64_t offset, delay; + int err; ++ struct pmc_agent *node = NULL; + + interval.tv_sec = priv->phc_interval; + interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; +@@ -713,22 +775,28 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + +- if (pmc_agent_update(priv->node) < 0) { +- continue; +- } ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ if (pmc_agent_update(node) < 0) { ++ continue; ++ } + +- if (subscriptions) { +- run_pmc_events(priv->node); +- if (priv->state_changed) { +- /* force getting offset, as it may have +- * changed after the port state change */ +- if (pmc_agent_query_utc_offset(priv->node, 1000)) { +- pr_err("failed to get UTC offset"); +- continue; ++ if (subscriptions) { ++ run_pmc_events(node); ++ if (priv->state_changed) { ++ /* force getting offset, as it may have ++ * changed after the port state change */ ++ if (pmc_agent_query_utc_offset(node, 1000)) { ++ pr_err("failed to get UTC offset"); ++ continue; ++ } + } +- reconfigure(priv); + } + } ++ ++ if (subscriptions && priv->state_changed) { ++ reconfigure(priv); ++ } ++ + if (!priv->master) + continue; + +@@ -859,55 +927,65 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + struct clock *clock; + struct port *port; + unsigned int i; ++ struct pmc_agent *node = NULL; ++ unsigned int retries, port_number; + +- while (1) { +- if (!is_running()) { +- return -1; +- } +- err = pmc_agent_query_dds(priv->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- } else { +- return -1; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_dds(node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } + } +- } + +- number_ports = pmc_agent_get_number_ports(priv->node); +- if (number_ports <= 0) { +- pr_err("failed to get number of ports"); +- return -1; +- } +- +- err = pmc_agent_subscribe(priv->node, 1000); +- if (err) { +- pr_err("failed to subscribe"); +- return -1; +- } +- +- for (i = 1; i <= number_ports; i++) { +- err = pmc_agent_query_port_properties(priv->node, 1000, i, +- &state, ×tamping, +- iface); +- if (err == -ENODEV) { +- /* port does not exist, ignore the port */ ++ number_ports = pmc_agent_get_number_ports(node); ++ if (number_ports <= 0) { ++ pr_err("failed to get number of ports"); + continue; + } ++ ++ err = pmc_agent_subscribe(node, 1000); + if (err) { +- pr_err("failed to get port properties"); +- return -1; +- } +- if (timestamping == TS_SOFTWARE) { +- /* ignore ports with software time stamping */ ++ pr_err("failed to subscribe"); + continue; + } +- port = port_add(priv, i, iface); +- if (!port) +- return -1; +- port->state = normalize_state(state); ++ ++ for (i = 1; i <= number_ports; i++) { ++ err = pmc_agent_query_port_properties(node, 1000, i, ++ &state, ×tamping, ++ iface); ++ if (err == -ENODEV) { ++ /* port does not exist, ignore the port */ ++ continue; ++ } ++ if (err) { ++ pr_err("failed to get port properties"); ++ break; ++ } ++ if (timestamping == TS_SOFTWARE) { ++ /* ignore ports with software time stamping */ ++ continue; ++ } ++ port_number = PORT_INDEX_TO_PORT_ID(i, node->index); ++ port = port_add(priv, port_number, iface); ++ if (!port) ++ return -1; ++ port->state = normalize_state(state); ++ /* map clock to pmc agent node */ ++ port->clock->node = node; ++ } + } ++ + if (LIST_EMPTY(&priv->clocks)) { + pr_err("no suitable ports available"); + return -1; +@@ -926,9 +1004,11 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (pmc_agent_query_utc_offset(priv->node, 1000)) { +- pr_err("failed to get UTC offset"); +- return -1; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ if (pmc_agent_query_utc_offset(node, 1000)) { ++ pr_err("failed to get UTC offset"); ++ continue; ++ } + } + return 0; + } +@@ -937,9 +1017,12 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) + { +- int clock_leap, node_leap = pmc_agent_get_leap(priv->node); ++ int clock_leap, node_leap; ++ struct pmc_agent *node = priv->master->node; ++ ++ node_leap = pmc_agent_get_leap(node); + +- clock->sync_offset = pmc_agent_get_sync_offset(priv->node); ++ clock->sync_offset = pmc_agent_get_sync_offset(node); + + if ((node_leap || clock->leap_set) && + clock->is_utc != priv->master->is_utc) { +@@ -980,7 +1063,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + } + } + +- if (pmc_agent_utc_offset_traceable(priv->node) && ++ if (pmc_agent_utc_offset_traceable(node) && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); +@@ -1034,7 +1117,8 @@ static void usage(char *progname) + + int main(int argc, char *argv[]) + { +- char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL; ++ char *config = NULL, *dst_name = NULL, *progname; ++ char *src_names[MAX_SRC_CLOCKS]; + char uds_local[MAX_IFNAME_SIZE + 1]; + int autocfg = 0, c, domain_number = 0, index, ntpshm_segment, offset; + int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0; +@@ -1047,7 +1131,11 @@ int main(int argc, char *argv[]) + struct phc2sys_private priv = { + .phc_readings = 5, + .phc_interval = 1.0, ++ .master = NULL, + }; ++ struct pmc_agent *node = NULL; ++ unsigned int i, src_cnt = 0; ++ int ha_enabled = 0; + + handle_term_signals(); + +@@ -1055,8 +1143,8 @@ int main(int argc, char *argv[]) + if (!cfg) { + return -1; + } +- priv.node = pmc_agent_create(); +- if (!priv.node) { ++ node = pmc_agent_add(&priv, 0); ++ if (!node) { + return -1; + } + +@@ -1102,7 +1190,11 @@ int main(int argc, char *argv[]) + "'-i' has been deprecated. please use '-s' instead.\n"); + /* fallthrough */ + case 's': +- src_name = strdup(optarg); ++ if (src_cnt == MAX_SRC_CLOCKS) { ++ fprintf(stderr, "too many source clocks\n"); ++ goto bad_usage; ++ } ++ src_names[src_cnt++] = optarg; + break; + case 'E': + if (!strcasecmp(optarg, "pi")) { +@@ -1153,7 +1245,7 @@ int main(int argc, char *argv[]) + if (get_arg_val_i(c, optarg, &offset, INT_MIN, INT_MAX)) { + goto end; + } +- pmc_agent_set_sync_offset(priv.node, offset); ++ pmc_agent_set_sync_offset(node, offset); + priv.forced_sync_offset = -1; + break; + case 'L': +@@ -1241,12 +1333,22 @@ int main(int argc, char *argv[]) + return c; + } + +- if (autocfg && (src_name || dst_name || pps_fd >= 0 || wait_sync || priv.forced_sync_offset)) { ++ if (src_cnt == 0) { ++ /* get the source interface list from configuration file */ ++ src_cnt = config_get_interfaces(cfg, src_names, MAX_SRC_CLOCKS); ++ if (src_cnt == (unsigned int)-1) { ++ fprintf(stderr, "too many interfaces in configuration file\n"); ++ fprintf(stderr, "maximum number of interfaces is %u\n", MAX_SRC_CLOCKS); ++ goto bad_usage; ++ } ++ } ++ ++ if (autocfg && (src_cnt > 0 || dst_name || pps_fd >= 0 || wait_sync || priv.forced_sync_offset)) { + fprintf(stderr, + "autoconfiguration cannot be mixed with manual config options.\n"); + goto bad_usage; + } +- if (!autocfg && pps_fd < 0 && !src_name) { ++ if (!autocfg && pps_fd < 0 && src_cnt == 0) { + fprintf(stderr, + "autoconfiguration or valid source clock must be selected.\n"); + goto bad_usage; +@@ -1282,7 +1384,7 @@ int main(int argc, char *argv[]) + getpid()); + + if (autocfg) { +- if (init_pmc_node(cfg, priv.node, uds_local, ++ if (init_pmc_node(cfg, node, uds_local, + phc2sys_recv_subscribed, &priv)) + goto end; + if (auto_init_ports(&priv, rt) < 0) +@@ -1291,15 +1393,26 @@ int main(int argc, char *argv[]) + goto end; + } + +- src = clock_add(&priv, src_name); +- free(src_name); +- if (!src) { +- fprintf(stderr, +- "valid source clock must be selected.\n"); ++ ha_enabled = config_get_int(cfg, NULL, "ha_enabled"); ++ if (!ha_enabled && src_cnt > 1) { ++ fprintf(stderr, "too many source clocks\n"); ++ fprintf(stderr, "Use 'ha_enabled 1' to accept more than one source clocks\n"); + goto bad_usage; + } +- src->state = PS_SLAVE; +- priv.master = src; ++ ++ for (i = 0; i < src_cnt; ++i) { ++ src = clock_add(&priv, src_names[i]); ++ if (!src) { ++ fprintf(stderr, ++ "invalid source clock '%s'.\n", src_names[i]); ++ goto bad_usage; ++ } ++ src->state = PS_SLAVE; ++ /* select the first clock */ ++ if (priv.master == NULL) { ++ priv.master = src; ++ } ++ } + + dst = clock_add(&priv, dst_name ? dst_name : "CLOCK_REALTIME"); + free(dst_name); +@@ -1320,32 +1433,58 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- if (init_pmc_node(cfg, priv.node, uds_local, +- phc2sys_recv_subscribed, &priv)) +- goto end; ++ i = 0; ++ for (src = LIST_FIRST(&priv.clocks); ++ src != NULL; ++ src = LIST_NEXT(src, list)) { + +- while (is_running()) { +- r = run_pmc_wait_sync(priv.node, 1000); +- if (r < 0) +- goto end; +- if (r > 0) +- break; +- else +- pr_notice("Waiting for ptp4l..."); +- } ++ /* skip dst clock */ ++ if (src == dst) { ++ continue; ++ } + +- if (!priv.forced_sync_offset) { +- r = pmc_agent_query_utc_offset(priv.node, 1000); +- if (r) { +- pr_err("failed to get UTC offset"); ++ if (i > 0) { ++ node = pmc_agent_add(&priv, i); ++ if (!node) ++ goto end; ++ } ++ ++ /* uds local is formated '/var/run/phc2sys..' */ ++ snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d.%s", ++ getpid(), src->device); ++ ++ if (init_pmc_node(cfg, node, uds_local, ++ phc2sys_recv_subscribed, &priv)) + goto end; ++ ++ /* map clock to pmc agent node */ ++ src->node = node; ++ ++ while (is_running()) { ++ r = run_pmc_wait_sync(node, 1000); ++ if (r < 0) ++ goto end; ++ if (r > 0) ++ break; ++ else ++ pr_notice("Waiting for ptp4l..."); ++ } ++ ++ if (!priv.forced_sync_offset) { ++ r = pmc_agent_query_utc_offset(node, 1000); ++ if (r) { ++ pr_err("failed to get UTC offset"); ++ goto end; ++ } ++ } ++ ++ if (priv.forced_sync_offset || ++ (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || ++ src->clkid == CLOCK_INVALID) { ++ pmc_agent_disable(node); + } +- } + +- if (priv.forced_sync_offset || +- (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || +- src->clkid == CLOCK_INVALID) { +- pmc_agent_disable(priv.node); ++ ++i; + } + } + +@@ -1359,7 +1498,7 @@ int main(int argc, char *argv[]) + } + + end: +- pmc_agent_destroy(priv.node); ++ pmc_agent_cleanup(&priv); + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +diff --git a/pmc_agent.c b/pmc_agent.c +index 3034f65..d13f569 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -34,23 +34,6 @@ + * renewed. + */ + +-struct pmc_agent { +- struct pmc *pmc; +- uint64_t pmc_last_update; +- +- struct defaultDS dds; +- bool dds_valid; +- int leap; +- int pmc_ds_requested; +- bool stay_subscribed; +- int sync_offset; +- int utc_offset_traceable; +- +- /* Callback on message reception */ +- pmc_node_recv_subscribed_t *recv_subscribed; +- void *recv_context; +-}; +- + static void send_subscription(struct pmc_agent *node) + { + struct subscribe_events_np sen; +diff --git a/pmc_agent.h b/pmc_agent.h +index dd34d30..5f25984 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -26,11 +26,28 @@ + + #include "pmc_common.h" + +-struct pmc_agent; +- + typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int excluded); + ++struct pmc_agent { ++ LIST_ENTRY(pmc_agent) list; ++ struct pmc *pmc; ++ uint64_t pmc_last_update; ++ ++ struct defaultDS dds; ++ bool dds_valid; ++ int leap; ++ int pmc_ds_requested; ++ bool stay_subscribed; ++ int sync_offset; ++ int utc_offset_traceable; ++ unsigned int index; ++ ++ /* Callback on message reception */ ++ pmc_node_recv_subscribed_t *recv_subscribed; ++ void *recv_context; ++}; ++ + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); +diff --git a/uds.c b/uds.c +index 641a672..57d4796 100644 +--- a/uds.c ++++ b/uds.c +@@ -55,11 +55,13 @@ static int uds_close(struct transport *t, struct fdarray *fda) + static int uds_open(struct transport *t, struct interface *iface, struct fdarray *fda, + enum timestamp_type tt) + { +- char *uds_path = config_get_string(t->cfg, NULL, "uds_address"); ++ char *uds_path = NULL; + struct uds *uds = container_of(t, struct uds, t); + const char *name = interface_name(iface); + struct sockaddr_un sa; + int fd, err; ++ char *point = NULL, *source = NULL; ++ int ha_enabled = config_get_int(t->cfg, NULL, "ha_enabled"); + + fd = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (fd < 0) { +@@ -82,6 +84,21 @@ static int uds_open(struct transport *t, struct interface *iface, struct fdarray + /* For client use, pre load the server path. */ + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_LOCAL; ++ ++ if (!ha_enabled) { ++ uds_path = config_get_string(t->cfg, NULL, "uds_address"); ++ } else { ++ /* The interface name is formated as '/var/run/phc2sys..'. ++ The last item is the source interface. */ ++ point = strtok(name, "."); ++ while(point != NULL) { ++ source = point; ++ point = strtok(NULL, "."); ++ } ++ ++ uds_path = config_get_string(t->cfg, source, "ha_uds_address"); ++ } ++ + strncpy(sa.sun_path, uds_path, sizeof(sa.sun_path) - 1); + uds->address.sun = sa; + uds->address.len = sizeof(sa); +-- +2.25.1 + +From 142b30b1f996a5bd48f0edc9b5fb0f51af0b97fd Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Tue, 4 Jul 2023 17:27:50 -0300 +Subject: [PATCH 38/47] Best source selection algorithm + +An algorithm to select the best available clock and use it +as clock source. + +A new set of configuration options was introduced to control +the clock requirements. The clock which fails to match the +requirements is discarded. + +If a single clock matches the requirements, it is selected +as source. + +If one or more clock match requirements, the clock with the highest +priority is selected. In case of tie, the 1st configured clock is +selected. + +And if no clock match requirements, the clock with the best local +clock class is selected. + +The ha_priority option is an interface setting used to configure +the clock priority. The lowest priority is 0 and the highest is 254, +and the default value is 0. + +The ha_min_local_clockClass option is a global setting for the minimal +local clock class requirement. It ranges from 6 to 255 and its default +is 135. + +The ha_min_clockAccuracy option is a global setting for the minimal +clock accuracy requirement. It ranges from 0x00 to 0xff and its default +is 0xfe. + +The ha_min_offsetScaledLogVariance is a global setting for the minimal +offset scaled log variance. It ranges from 0 to 65535 and its default +is 65535. + +The ha_timeTraceable is a global setting to enable or disable +the time traceable verification. When it's set the clock +which time is not traceable is discarded. + +The ha_frequencyTraceable is a global setting to enable or disable +the frequency traceable verification. When it's set the clock +which frequency is not traceable is discarded. + +The ha_min_gm_ClockClass is a global setting for the minimal +GM clock class requirement. It ranges from 6 to 255 and its +default is 135. + +Test Plan: clock selection algorithm +PASS: Verify clock is discarded when local clock class doesn't match +requirements. +PASS: Verify clock is discarded when local clock accuracy doesn't match +requirements. +PASS: Verify clock is discarded when local offset scaled log variance doesn't +match requirements. +PASS: Verify clock is discarded when time traceable verification is set 1 and +the clock hasn't time traceable flag set. +PASS: Verify clock is discarded when frequency traceable verification is set 1 +and the clock hasn't frequency traceable flag set. +PASS: Verify clock is discarded when GM clock class doesn't match requirements. +PASS: Verify clock is accepted when time traceable verification is set 0 even +when clock hasn't time traceable flag set. +PASS: Verify clock is accepted when frequency traceable verification is set 0 +even when clock hasn't frequency traceable flag set. +PASS: Verify the highest priority clock is selected when one or more clock +match the requirements. +PASS: Verify the 1st configured clock is selected when one or more clock of the +same priority match the requirements. +PASS: Verify the clock with highest local clock class is selected when no clock +match the requirements. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit 1c10dd42b32388c2e708ad249dd1f193e7208155 upstream] +[commit 373c4fd50aaf52540d3eeb8f38f3e07307dea3a3 upstream] +[commit 279d5b6e7f88876ce00f1e87faba65c7cd6a90b0 upstream] +[commit 00d9ad798b1f700faefa0b5d4074c46f8ae87ef4 upstream] +[commit 1407a51d8000ca7df18ba67d611a761abb6f77f8 upstream] +[commit e0c1c7b64f7af8002092c01e023f524bfcc39f8b upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 7 ++ + phc2sys.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + pmc_agent.c | 20 +++++ + pmc_agent.h | 13 +++ + 4 files changed, 271 insertions(+) + +diff --git a/config.c b/config.c +index b97e5d7..8ce5f6c 100644 +--- a/config.c ++++ b/config.c +@@ -250,7 +250,14 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("G.8275.defaultDS.localPriority", 128, 1, UINT8_MAX), + PORT_ITEM_INT("G.8275.portDS.localPriority", 128, 1, UINT8_MAX), + GLOB_ITEM_INT("gmCapable", 1, 0, 1), ++ GLOB_ITEM_INT("ha_frequencyTraceable", 0, 0, 1), + GLOB_ITEM_INT("ha_enabled", 0, 0, 1), ++ GLOB_ITEM_INT("ha_min_clockAccuracy", 0xfe, 0, 0xff), ++ GLOB_ITEM_INT("ha_min_gm_ClockClass", 135, 6, 255), ++ GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), ++ GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), ++ PORT_ITEM_INT("ha_priority", 0, 0, 255), ++ GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), + PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), + GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu), + PORT_ITEM_INT("hybrid_e2e", 0, 0, 1), +diff --git a/phc2sys.c b/phc2sys.c +index a4afe01..d148d62 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -73,6 +73,7 @@ + struct clock { + LIST_ENTRY(clock) list; + LIST_ENTRY(clock) dst_list; ++ LIST_ENTRY(clock) good_list; + clockid_t clkid; + int phc_index; + int sysoff_method; +@@ -1073,6 +1074,232 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + return 0; + } + ++static struct clock* startup_select_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ struct clock *clock = NULL, *best = NULL; ++ LIST_HEAD(head, clock) good_clocks; ++ int clock_priority, highest_priority; ++ int min_local_clock_class, min_gm_clock_class, clock_class, lowest_clock_class; ++ int err; ++ unsigned int min_clock_accuracy, min_offset_scaled_log_variance, retries; ++ bool check_time_traceable, check_freq_traceable; ++ ++ LIST_INIT(&good_clocks); ++ ++ /* get requirements */ ++ min_local_clock_class = config_get_int(cfg, NULL, "ha_min_local_clockClass"); ++ min_clock_accuracy = config_get_int(cfg, NULL, "ha_min_clockAccuracy"); ++ min_offset_scaled_log_variance = config_get_int(cfg, NULL, "ha_min_offsetScaledLogVariance"); ++ check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); ++ check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); ++ min_gm_clock_class = config_get_int(cfg, NULL, "ha_min_gm_ClockClass"); ++ ++ /* save a list of available source clocks that matches requirements */ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* check matching parameters */ ++ pr_debug("clock %s state %d", clock->device, clock->state); ++ ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ pr_debug("clock %s discarded because state is PS_MASTER", clock->device); ++ continue; ++ } ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s discarded because node is (null)", clock->device); ++ continue; ++ } ++ ++ /* get Default Data Set */ ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return NULL; ++ } ++ err = pmc_agent_query_dds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return NULL; ++ } ++ } ++ ++ if (!clock->node->dds_valid) { ++ pr_debug("clock %s discarded because dds is invalid", clock->device); ++ continue; ++ } ++ ++ /* min clockClass ++ as lower clock class is better, accept sources which clock class ++ is lower then or equal to min local clock class and discard ++ the sources which clock class is higher than min local clock class. ++ */ ++ clock_class = clock->node->dds.clockQuality.clockClass; ++ pr_debug("clock %s local clockClass %d", clock->device, clock_class); ++ if (clock_class > min_local_clock_class) { ++ pr_debug("clock %s discarded because local clock class %d > min clock class %d", ++ clock->device, clock_class, min_local_clock_class); ++ continue; ++ } ++ ++ /* min clockAccuracy (lower is better) */ ++ pr_debug("clock %s clockAccuracy 0x%x", clock->device, ++ clock->node->dds.clockQuality.clockAccuracy); ++ if (clock->node->dds.clockQuality.clockAccuracy > min_clock_accuracy) { ++ pr_debug("clock %s discarded because clock accuracy %d > min clock accuracy %d", ++ clock->device, clock->node->dds.clockQuality.clockAccuracy, ++ min_clock_accuracy); ++ continue; ++ } ++ ++ /* min offset scaled log variance */ ++ pr_debug("clock %s offsetScaledLogVariance 0x%x", clock->device, ++ clock->node->dds.clockQuality.offsetScaledLogVariance); ++ if (clock->node->dds.clockQuality.offsetScaledLogVariance > min_offset_scaled_log_variance) { ++ pr_debug("clock %s discarded because offset scaled log variance 0x%x > min offset 0x%x", ++ clock->device, clock->node->dds.clockQuality.offsetScaledLogVariance, ++ min_offset_scaled_log_variance); ++ continue; ++ } ++ ++ if (check_time_traceable || check_freq_traceable) { ++ /* get Time Properties Data Set */ ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return NULL; ++ } ++ err = pmc_agent_query_utc_offset(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return NULL; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because tds is invalid", clock->device); ++ continue; ++ } ++ } ++ ++ /* is time traceable */ ++ pr_debug("clock %s is time traceable %s", clock->device, ++ clock->node->utc_offset_traceable ? "yes" : "no"); ++ if (check_time_traceable && !clock->node->utc_offset_traceable) { ++ pr_debug("clock %s discarded because time is not traceable", clock->device); ++ continue; ++ } ++ ++ /* is frequency traceable */ ++ pr_debug("clock %s is frequency traceable %s", clock->device, ++ clock->node->freq_traceable ? "yes" : "no"); ++ if (check_freq_traceable && !clock->node->freq_traceable) { ++ pr_debug("clock %s discarded because frequency is not traceable", clock->device); ++ continue; ++ } ++ ++ retries = 0; ++ while (retries < 10) { ++ if (!is_running()) { ++ return NULL; ++ } ++ err = pmc_agent_query_pds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return NULL; ++ } ++ } ++ ++ if (!clock->node->pds_valid) { ++ pr_debug("clock %s discarded because pds is invalid", clock->device); ++ continue; ++ } ++ ++ /* min gm clock class - lower is better */ ++ clock_class = clock->node->pds.grandmasterClockQuality.clockClass; ++ pr_debug("clock %s GM clockClass %d", clock->device, clock_class); ++ if (clock_class > min_gm_clock_class) { ++ pr_debug("clock %s discarded because GM clock class %d > min clock class %d", ++ clock->device, clock_class, min_gm_clock_class); ++ continue; ++ } ++ ++ pr_notice("clock %s matched requirements", clock->device); ++ ++ clock->good_list.le_next = NULL; ++ clock->good_list.le_prev = NULL; ++ LIST_INSERT_HEAD(&good_clocks, clock, good_list); ++ } ++ ++ /* one or more sources match requirements, select highest priority */ ++ highest_priority = 0; ++ LIST_FOREACH(clock, &good_clocks, good_list) { ++ clock_priority = config_get_int(cfg, clock->device, "ha_priority"); ++ ++ /* select highest priority clock ++ more than one clock with same priority, select first ++ don't select clocks with ha_priority 0 */ ++ if (clock_priority > highest_priority) { ++ pr_notice("new highest ha priority clock %s ha_priority %d", ++ clock->device, clock_priority); ++ best = clock; ++ highest_priority = clock_priority; ++ } ++ } ++ ++ /* no sources match requirements, choose best available clockClass */ ++ if (!best) { ++ lowest_clock_class = 256; ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ continue; ++ } ++ ++ /* get clock class */ ++ clock_class = clock->node->dds.clockQuality.clockClass; ++ if (clock_class <= lowest_clock_class) { ++ pr_notice("new better clock class clock %s clock class %d", ++ clock->device, clock_class); ++ best = clock; ++ lowest_clock_class = clock_class; ++ } ++ } ++ } ++ ++ /* no clock selected, select first clock configured (last in list) */ ++ if (!best) { ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ continue; ++ } ++ ++ best = clock; ++ } ++ } ++ ++ if (best) ++ pr_notice("Best clock selected %s", best->device); ++ ++ return best; ++}; ++ + static void usage(char *progname) + { + fprintf(stderr, +@@ -1486,6 +1713,10 @@ int main(int argc, char *argv[]) + + ++i; + } ++ ++ if (ha_enabled) { ++ startup_select_clock(&priv, cfg); ++ } + } + + if (pps_fd >= 0) { +diff --git a/pmc_agent.c b/pmc_agent.c +index d13f569..534f483 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -351,15 +351,35 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + node->leap = 0; + node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && + tds->flags & TIME_TRACEABLE; ++ node->freq_traceable = tds->flags & FREQ_TRACEABLE; + } else { + node->sync_offset = 0; + node->leap = 0; + node->utc_offset_traceable = 0; ++ node->freq_traceable = 0; + } + msg_put(msg); + return 0; + } + ++int pmc_agent_query_pds(struct pmc_agent *node, int timeout) ++{ ++ struct parentDS *pds; ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, MID_PARENT_DATA_SET, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ ++ pds = (struct parentDS *) management_tlv_data(msg); ++ memcpy(&node->pds, pds, sizeof(node->pds)); ++ node->pds_valid = true; ++ msg_put(msg); ++ return 0; ++} ++ + void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + { + agent->sync_offset = offset; +diff --git a/pmc_agent.h b/pmc_agent.h +index 5f25984..2bd7f02 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -41,7 +41,10 @@ struct pmc_agent { + bool stay_subscribed; + int sync_offset; + int utc_offset_traceable; ++ int freq_traceable; + unsigned int index; ++ struct parentDS pds; ++ bool pds_valid; + + /* Callback on message reception */ + pmc_node_recv_subscribed_t *recv_subscribed; +@@ -142,6 +145,16 @@ int pmc_agent_query_port_properties(struct pmc_agent *agent, int timeout, + */ + int pmc_agent_query_utc_offset(struct pmc_agent *agent, int timeout); + ++/** ++ * Queries the parent data set from the ptp4l service. ++ * The result of the query will be cached inside of the agent. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_pds(struct pmc_agent *agent, int timeout); ++ + /** + * Sets the TAI-UTC offset. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From c61a91f5da1c07a783b0922e713c9f1d32adfa80 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Sat, 8 Jul 2023 19:02:50 -0300 +Subject: [PATCH 39/47] Select best source clock after state changes + +During operation, the clock states might change and require a new clock +to be selected. For example, the local clock class of the current active +clock has changed and it doesn't match requirements any more. + +Every 60 seconds the state of every configured clock is updated. The +clock state includes all the parameters used in the clock selection +algorithm. + +When the active clock degrades, the clock selection algorithm +is used to immediatialy promote a better source clock. + +When a higher priority clock recovers or starts to match the requirements, +a stability timer is started, and the active candidate clock is selected +when timer reaches threshold. If the stability timer is not configured +the switch is done immediatialy. + +The stability timer is retriggarable. It is started when a clock with higher +priority than the active becomes available, becaming the active candidate. +The timer is restarted when another clock becames the active candidate. + +The ha_stability_timer option is a global setting used to configure the +stability timer. Its value is expressed in seconds, and the value 0 +disables the timer. In other words, when ha_stability_timer is set +0 the clock change is done immediatialy on clock state change. + +When a clock with equal priority than the active becomes available, +the active clock must not be switched. Only if the active degrades +the other clock can be selected active. + +Test plan: equal priority clocks +PASS: Verify when the active clock state changes but still matches +requirements, the active clock doesn't change. +PASS: Verify when the active clock degrades and secondary clock becomes active. +PASS: Verify when the primary recovers the active clock doesn't change. +PASS: Verify when the secondary and active clock degrades, the primary becomes +active. + +Test plan: different priority clock +PASS: Verify the higher priority clock is selected active at startup. +PASS: Verify when the active and primary degrades the secondary is selected +active. +PASS: Verify when the primary recovers it is selected active. + +Test plan: stability timer +PASS: Verify when primary and active clock degrades the secondary becomes +active. +PASS: Verify when primary recovers the secondary is kept active until +stability timer has elapsed, then the primary becomes active. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit de9976fc57d3e8212f51f4c509da27c88d0a39d8 upstream] +[commit 44c06ab00d81245d4dfeb159c61f3c0dbf148d81 upstream] +[commit 44de5cf877cbe18dbec0341931b4bc745e61746e upstream] +[commit 61cf557b56805a1af9b7cbd0344f22c4acd9400c upstream] +[commit f7d915c89b949122d9cb9eef82588b73e6171619 upstream] +[commit 2aec3fc46e82375e9e48da9f5aa227ee3d885308 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 1 + + phc2sys.c | 672 ++++++++++++++++++++++++++++++++++------------------ + pmc_agent.c | 39 ++- + pmc_agent.h | 6 +- + 4 files changed, 481 insertions(+), 237 deletions(-) + +diff --git a/config.c b/config.c +index 8ce5f6c..1ad5157 100644 +--- a/config.c ++++ b/config.c +@@ -257,6 +257,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), + PORT_ITEM_INT("ha_priority", 0, 0, 255), ++ PORT_ITEM_INT("ha_stability_timer", 0, 0, INT_MAX), + GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), + PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), + GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu), +diff --git a/phc2sys.c b/phc2sys.c +index d148d62..152e783 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -66,14 +66,14 @@ + + #define MAX_SRC_CLOCKS 128 + +-#define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) || (((unsigned int) index) & 0xFF)) ++#define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) | (((unsigned int) index) & 0xFF)) + #define PORT_ID_TO_PORT(id) ((((unsigned int) id) >> 8) & 0xFF) + #define PORT_ID_TO_INDEX(id) (((unsigned int) id) & 0xFF) + + struct clock { + LIST_ENTRY(clock) list; + LIST_ENTRY(clock) dst_list; +- LIST_ENTRY(clock) good_list; ++ LIST_ENTRY(clock) ha_list; + clockid_t clkid; + int phc_index; + int sysoff_method; +@@ -94,6 +94,7 @@ struct clock { + struct clockcheck *sanity_check; + struct pmc_agent *node; + }; ++typedef LIST_HEAD(head, clock) clock_list_head_t; + + struct port { + LIST_ENTRY(port) list; +@@ -111,11 +112,14 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; ++ int clock_state_changed; + LIST_HEAD(pmc_agent_head, pmc_agent) pmc_agents; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; + struct clock *master; ++ struct clock *better; ++ struct timespec stability_timer; + int default_sync; + }; + +@@ -248,6 +252,235 @@ static void clock_cleanup(struct phc2sys_private *priv) + } + } + ++static struct clock *clock_get(struct phc2sys_private *priv, struct pmc_agent *node) ++{ ++ struct clock * clock = NULL; ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ if (clock->node == node) { ++ break; ++ } ++ } ++ return clock; ++} ++ ++static bool clock_match_ha_dds_requirements(struct clock *clock, struct config *cfg) ++{ ++ /* get requirements */ ++ int local_clock_class, min_local_clock_class = config_get_int(cfg, NULL, "ha_min_local_clockClass"); ++ unsigned int clock_accuracy, min_clock_accuracy = config_get_int(cfg, NULL, "ha_min_clockAccuracy"); ++ unsigned int offset, min_offset_scaled_log_variance = config_get_int(cfg, NULL, "ha_min_offsetScaledLogVariance"); ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s node is (null)", clock->device); ++ return false; ++ } ++ ++ if (!clock->node->dds_valid) { ++ pr_debug("clock %s dds is invalid", clock->device); ++ return false; ++ } ++ ++ /* min local clock class (lower is better) */ ++ local_clock_class = clock->node->dds.clockQuality.clockClass; ++ if (local_clock_class > min_local_clock_class) { ++ pr_debug("clock %s local clock class %d > min local clock class %d", ++ clock->device, local_clock_class, min_local_clock_class); ++ return false; ++ } ++ ++ /* min clock accuracy (lower is better) */ ++ clock_accuracy = clock->node->dds.clockQuality.clockAccuracy; ++ if (clock_accuracy > min_clock_accuracy) { ++ pr_debug("clock %s clock accuracy %d > min clock accuracy %d", ++ clock->device, clock_accuracy, min_clock_accuracy); ++ return false; ++ } ++ ++ /* min offset scaled log variance (lower is better) */ ++ offset = clock->node->dds.clockQuality.offsetScaledLogVariance; ++ if (offset > min_offset_scaled_log_variance) { ++ pr_debug("clock %s offset scaled log variance 0x%x > min offset 0x%x", ++ clock->device, offset, min_offset_scaled_log_variance); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool clock_match_ha_tpds_requirements(struct clock *clock, struct config *cfg) ++{ ++ /* get requirements */ ++ bool check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); ++ bool check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s node is (null)", clock->device); ++ return false; ++ } ++ ++ /* is time traceable */ ++ if (check_time_traceable && !clock->node->utc_offset_traceable) { ++ pr_debug("clock %s time is not traceable", clock->device); ++ return false; ++ } ++ ++ /* is frequency traceable */ ++ if (check_freq_traceable && !clock->node->freq_traceable) { ++ pr_debug("clock %s frequency is not traceable", clock->device); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool clock_match_ha_pds_requirements(struct clock *clock, struct config *cfg) ++{ ++ /* get requirements */ ++ int gm_clock_class, min_gm_clock_class = config_get_int(cfg, NULL, "ha_min_gm_ClockClass"); ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s node is (null)", clock->device); ++ return false; ++ } ++ ++ if (!clock->node->pds_valid) { ++ pr_debug("clock %s pds is invalid", clock->device); ++ return false; ++ } ++ ++ /* min gm clock class (lower is better) */ ++ gm_clock_class = clock->node->pds.grandmasterClockQuality.clockClass; ++ if (gm_clock_class > min_gm_clock_class) { ++ pr_debug("clock %s GM clock class %d > min clock class %d", ++ clock->device, gm_clock_class, min_gm_clock_class); ++ return false; ++ } ++ ++ return true; ++} ++ ++/* save a list of available source clocks that matches ha requirements */ ++static int clock_available_ha_src_clocks(struct phc2sys_private *priv, struct config *cfg, clock_list_head_t *available_clocks) ++{ ++ int err, retries; ++ struct clock *clock; ++ bool check_time_traceable, check_freq_traceable; ++ ++ LIST_INIT(available_clocks); ++ ++ check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); ++ check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ pr_debug("clock %s state %d", clock->device, clock->state); ++ ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ pr_debug("clock %s discarded because state is PS_MASTER", clock->device); ++ continue; ++ } ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s discarded because node is (null)", clock->device); ++ continue; ++ } ++ ++ /* get Default Data Set */ ++ if (!clock->node->dds_valid) { ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_dds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because tds is invalid", clock->device); ++ continue; ++ } ++ } ++ ++ if (!clock_match_ha_dds_requirements(clock, cfg)) ++ continue; ++ ++ if (check_time_traceable || check_freq_traceable) { ++ /* get Time Properties Data Set */ ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_utc_offset(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because tds is invalid", clock->device); ++ continue; ++ } ++ ++ if (!clock_match_ha_tpds_requirements(clock, cfg)) ++ continue; ++ } ++ ++ /* get Parent Data Set */ ++ if (!clock->node->pds_valid) { ++ retries = 0; ++ while (retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_pds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because pds is invalid", clock->device); ++ continue; ++ } ++ } ++ ++ if (!clock_match_ha_pds_requirements(clock, cfg)) ++ continue; ++ ++ clock->ha_list.le_next = NULL; ++ clock->ha_list.le_prev = NULL; ++ LIST_INSERT_HEAD(available_clocks, clock, ha_list); ++ } ++ ++ return 0; ++} ++ + static void port_cleanup(struct phc2sys_private *priv) + { + struct port *p, *tmp; +@@ -368,6 +601,10 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + + pmc_index = PORT_ID_TO_INDEX(p->number); + node = pmc_agent_get(priv, pmc_index); ++ if (!node) { ++ pr_warning("pmc node associated to port number %d not found", p->number); ++ continue; ++ } + err = pmc_agent_query_port_properties(node, 1000, + p->number, &state, + ×tamping, iface); +@@ -761,7 +998,159 @@ static int update_needed(struct clock *c) + return 0; + } + +-static int do_loop(struct phc2sys_private *priv, int subscriptions) ++static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ int clock_priority, highest_priority; ++ int clock_class, lowest_clock_class; ++ struct clock *clock = NULL, *best = NULL; ++ clock_list_head_t ha_available_clocks; ++ ++ /* save a list of available source clocks that matches requirements */ ++ if (clock_available_ha_src_clocks(priv, cfg, &ha_available_clocks) < 0) { ++ pr_err("failed to create ha available clock list"); ++ return NULL; ++ } ++ ++ /* one or more sources match requirements, select highest priority */ ++ highest_priority = 0; ++ LIST_FOREACH(clock, &ha_available_clocks, ha_list) { ++ clock_priority = config_get_int(cfg, clock->device, "ha_priority"); ++ ++ /* select highest priority clock ++ more than one clock with same priority, select first ++ don't select clocks with ha_priority 0 */ ++ if (clock_priority > highest_priority) { ++ pr_notice("new highest ha priority clock %s ha_priority %d", ++ clock->device, clock_priority); ++ best = clock; ++ highest_priority = clock_priority; ++ } ++ } ++ ++ /* no sources match requirements, choose best available clockClass */ ++ if (!best) { ++ lowest_clock_class = 256; ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (clock->node == NULL) ++ continue; ++ ++ /* get clock class */ ++ clock_class = clock->node->dds.clockQuality.clockClass; ++ if (clock_class <= lowest_clock_class) { ++ pr_notice("new better clock class clock %s clock class %d", ++ clock->device, clock_class); ++ best = clock; ++ lowest_clock_class = clock_class; ++ } ++ } ++ } ++ ++ /* no clock selected, select first clock configured (last in list) */ ++ if (!best) { ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (clock->node == NULL) ++ continue; ++ ++ best = clock; ++ } ++ } ++ ++ if (best) ++ pr_notice("Best clock selected %s", best->device); ++ ++ return best; ++} ++ ++static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ struct clock *active = priv->master, *candidate = NULL; ++ int stability_timer = 0; ++ struct timespec now; ++ int active_priority, candidate_priority; ++ int active_clock_class, candidate_clock_class; ++ ++ /* Active source degrades - re-run ha_select_clock algorithm */ ++ if ((active->node->new_dds && !clock_match_ha_dds_requirements(active, cfg)) || ++ (active->node->new_tpds && !clock_match_ha_tpds_requirements(active, cfg)) || ++ (active->node->new_pds && !clock_match_ha_pds_requirements(active, cfg))) { ++ ++ pr_notice("active clock %s has degraded", active->device); ++ ++ active->node->new_dds = false; ++ active->node->new_tpds = false; ++ active->node->new_pds = false; ++ ++ candidate = ha_select_clock(priv, cfg); ++ if (active != candidate) { ++ pr_notice("new source clock selected %s", candidate->device); ++ return candidate; ++ } ++ } ++ ++ /* Primary clock is active, secondary clock becomes better quality */ ++ /* Secondary clock is active, primary clock becomes better quality */ ++ ++ /* select best clock available */ ++ candidate = ha_select_clock(priv, cfg); ++ ++ if (active == candidate) { ++ /* active source still is or became the best clock available again */ ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } else { ++ /* new clock candidate */ ++ ++ /* candidate has equal priority and clockClass than active - don't change active */ ++ active_priority = config_get_int(cfg, active->device, "ha_priority"); ++ candidate_priority = config_get_int(cfg, candidate->device, "ha_priority"); ++ active_clock_class = active->node->dds.clockQuality.clockClass; ++ candidate_clock_class = candidate->node->dds.clockQuality.clockClass; ++ if ((active_priority == candidate_priority) && ++ (active_clock_class == candidate_clock_class)) { ++ return NULL; ++ } ++ ++ /* stability timer = 0 - change active */ ++ stability_timer = config_get_int(cfg, NULL, "ha_stability_timer"); ++ if (stability_timer == 0) { ++ pr_notice("new source clock selected %s", candidate->device); ++ return candidate; ++ } ++ ++ if (candidate != priv->better) { ++ priv->better = candidate; ++ /* start/restart stability timer */ ++ clock_gettime(CLOCK_REALTIME, &now); ++ priv->stability_timer.tv_sec = now.tv_sec + stability_timer; ++ priv->stability_timer.tv_nsec = now.tv_nsec; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void reset_new_dataset_flags(struct phc2sys_private *priv) ++{ ++ struct pmc_agent *node; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ node->new_dds = false; ++ node->new_tpds = false; ++ node->new_pds = false; ++ } ++} ++ ++static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscriptions) + { + struct timespec interval; + struct clock *clock; +@@ -769,6 +1158,8 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + int64_t offset, delay; + int err; + struct pmc_agent *node = NULL; ++ int ha_enabled = config_get_int(cfg, NULL, "ha_enabled"); ++ struct timespec now; + + interval.tv_sec = priv->phc_interval; + interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; +@@ -781,6 +1172,10 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + continue; + } + ++ if (node->new_dds || node->new_tpds || node->new_pds) { ++ priv->clock_state_changed = 1; ++ } ++ + if (subscriptions) { + run_pmc_events(node); + if (priv->state_changed) { +@@ -798,6 +1193,35 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + reconfigure(priv); + } + ++ if (ha_enabled) { ++ if (priv->clock_state_changed) { ++ clock = check_and_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } ++ ++ priv->clock_state_changed = 0; ++ reset_new_dataset_flags(priv); ++ } ++ ++ if (priv->better) { ++ /* has stability timer expired? */ ++ clock_gettime(CLOCK_REALTIME, &now); ++ if ((now.tv_sec > priv->stability_timer.tv_sec) || ++ (now.tv_sec == priv->stability_timer.tv_sec && ++ now.tv_nsec > priv->stability_timer.tv_nsec)) { ++ pr_notice("new source clock selected %s", priv->better->device); ++ priv->master = priv->better; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } ++ } ++ } ++ + if (!priv->master) + continue; + +@@ -883,21 +1307,25 @@ static int clock_compute_state(struct phc2sys_private *priv, + return state; + } + +-static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, ++static int phc2sys_recv_subscribed(struct pmc_agent *node, void *context, struct ptp_message *msg, + int excluded) + { + struct phc2sys_private *priv = (struct phc2sys_private *) context; + int mgt_id, state; + struct portDS *pds; ++ struct defaultDS *dds; ++ struct parentDS *parentds; ++ struct timePropertiesDS *tds; + struct port *port; + struct clock *clock; ++ int utc_offset_traceable, freq_traceable; + + mgt_id = management_tlv_id(msg); + if (mgt_id == excluded) + return 0; + switch (mgt_id) { + case MID_PORT_DATA_SET: +- pds = management_tlv_data(msg); ++ pds = (struct portDS *)management_tlv_data(msg); + port = port_get(priv, pds->portIdentity.portNumber); + if (!port) { + pr_info("received data for unknown port %s", +@@ -1074,232 +1502,6 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + return 0; + } + +-static struct clock* startup_select_clock(struct phc2sys_private *priv, struct config *cfg) +-{ +- struct clock *clock = NULL, *best = NULL; +- LIST_HEAD(head, clock) good_clocks; +- int clock_priority, highest_priority; +- int min_local_clock_class, min_gm_clock_class, clock_class, lowest_clock_class; +- int err; +- unsigned int min_clock_accuracy, min_offset_scaled_log_variance, retries; +- bool check_time_traceable, check_freq_traceable; +- +- LIST_INIT(&good_clocks); +- +- /* get requirements */ +- min_local_clock_class = config_get_int(cfg, NULL, "ha_min_local_clockClass"); +- min_clock_accuracy = config_get_int(cfg, NULL, "ha_min_clockAccuracy"); +- min_offset_scaled_log_variance = config_get_int(cfg, NULL, "ha_min_offsetScaledLogVariance"); +- check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); +- check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); +- min_gm_clock_class = config_get_int(cfg, NULL, "ha_min_gm_ClockClass"); +- +- /* save a list of available source clocks that matches requirements */ +- LIST_FOREACH(clock, &priv->clocks, list) { +- /* check matching parameters */ +- pr_debug("clock %s state %d", clock->device, clock->state); +- +- /* ignore the dst clock */ +- if (clock->state == PS_MASTER) { +- pr_debug("clock %s discarded because state is PS_MASTER", clock->device); +- continue; +- } +- +- /* sanity check */ +- if (clock->node == NULL) { +- pr_debug("clock %s discarded because node is (null)", clock->device); +- continue; +- } +- +- /* get Default Data Set */ +- retries = 0; +- while(retries < 10) { +- if (!is_running()) { +- return NULL; +- } +- err = pmc_agent_query_dds(clock->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- retries++; +- } else { +- return NULL; +- } +- } +- +- if (!clock->node->dds_valid) { +- pr_debug("clock %s discarded because dds is invalid", clock->device); +- continue; +- } +- +- /* min clockClass +- as lower clock class is better, accept sources which clock class +- is lower then or equal to min local clock class and discard +- the sources which clock class is higher than min local clock class. +- */ +- clock_class = clock->node->dds.clockQuality.clockClass; +- pr_debug("clock %s local clockClass %d", clock->device, clock_class); +- if (clock_class > min_local_clock_class) { +- pr_debug("clock %s discarded because local clock class %d > min clock class %d", +- clock->device, clock_class, min_local_clock_class); +- continue; +- } +- +- /* min clockAccuracy (lower is better) */ +- pr_debug("clock %s clockAccuracy 0x%x", clock->device, +- clock->node->dds.clockQuality.clockAccuracy); +- if (clock->node->dds.clockQuality.clockAccuracy > min_clock_accuracy) { +- pr_debug("clock %s discarded because clock accuracy %d > min clock accuracy %d", +- clock->device, clock->node->dds.clockQuality.clockAccuracy, +- min_clock_accuracy); +- continue; +- } +- +- /* min offset scaled log variance */ +- pr_debug("clock %s offsetScaledLogVariance 0x%x", clock->device, +- clock->node->dds.clockQuality.offsetScaledLogVariance); +- if (clock->node->dds.clockQuality.offsetScaledLogVariance > min_offset_scaled_log_variance) { +- pr_debug("clock %s discarded because offset scaled log variance 0x%x > min offset 0x%x", +- clock->device, clock->node->dds.clockQuality.offsetScaledLogVariance, +- min_offset_scaled_log_variance); +- continue; +- } +- +- if (check_time_traceable || check_freq_traceable) { +- /* get Time Properties Data Set */ +- retries = 0; +- while(retries < 10) { +- if (!is_running()) { +- return NULL; +- } +- err = pmc_agent_query_utc_offset(clock->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- retries++; +- } else { +- return NULL; +- } +- } +- +- if (err != 0) { +- pr_debug("clock %s discarded because tds is invalid", clock->device); +- continue; +- } +- } +- +- /* is time traceable */ +- pr_debug("clock %s is time traceable %s", clock->device, +- clock->node->utc_offset_traceable ? "yes" : "no"); +- if (check_time_traceable && !clock->node->utc_offset_traceable) { +- pr_debug("clock %s discarded because time is not traceable", clock->device); +- continue; +- } +- +- /* is frequency traceable */ +- pr_debug("clock %s is frequency traceable %s", clock->device, +- clock->node->freq_traceable ? "yes" : "no"); +- if (check_freq_traceable && !clock->node->freq_traceable) { +- pr_debug("clock %s discarded because frequency is not traceable", clock->device); +- continue; +- } +- +- retries = 0; +- while (retries < 10) { +- if (!is_running()) { +- return NULL; +- } +- err = pmc_agent_query_pds(clock->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- retries++; +- } else { +- return NULL; +- } +- } +- +- if (!clock->node->pds_valid) { +- pr_debug("clock %s discarded because pds is invalid", clock->device); +- continue; +- } +- +- /* min gm clock class - lower is better */ +- clock_class = clock->node->pds.grandmasterClockQuality.clockClass; +- pr_debug("clock %s GM clockClass %d", clock->device, clock_class); +- if (clock_class > min_gm_clock_class) { +- pr_debug("clock %s discarded because GM clock class %d > min clock class %d", +- clock->device, clock_class, min_gm_clock_class); +- continue; +- } +- +- pr_notice("clock %s matched requirements", clock->device); +- +- clock->good_list.le_next = NULL; +- clock->good_list.le_prev = NULL; +- LIST_INSERT_HEAD(&good_clocks, clock, good_list); +- } +- +- /* one or more sources match requirements, select highest priority */ +- highest_priority = 0; +- LIST_FOREACH(clock, &good_clocks, good_list) { +- clock_priority = config_get_int(cfg, clock->device, "ha_priority"); +- +- /* select highest priority clock +- more than one clock with same priority, select first +- don't select clocks with ha_priority 0 */ +- if (clock_priority > highest_priority) { +- pr_notice("new highest ha priority clock %s ha_priority %d", +- clock->device, clock_priority); +- best = clock; +- highest_priority = clock_priority; +- } +- } +- +- /* no sources match requirements, choose best available clockClass */ +- if (!best) { +- lowest_clock_class = 256; +- LIST_FOREACH(clock, &priv->clocks, list) { +- /* ignore the dst clock */ +- if (clock->state == PS_MASTER) { +- continue; +- } +- +- /* get clock class */ +- clock_class = clock->node->dds.clockQuality.clockClass; +- if (clock_class <= lowest_clock_class) { +- pr_notice("new better clock class clock %s clock class %d", +- clock->device, clock_class); +- best = clock; +- lowest_clock_class = clock_class; +- } +- } +- } +- +- /* no clock selected, select first clock configured (last in list) */ +- if (!best) { +- LIST_FOREACH(clock, &priv->clocks, list) { +- /* ignore the dst clock */ +- if (clock->state == PS_MASTER) { +- continue; +- } +- +- best = clock; +- } +- } +- +- if (best) +- pr_notice("Best clock selected %s", best->device); +- +- return best; +-}; +- + static void usage(char *progname) + { + fprintf(stderr, +@@ -1359,6 +1561,8 @@ int main(int argc, char *argv[]) + .phc_readings = 5, + .phc_interval = 1.0, + .master = NULL, ++ .better = NULL, ++ .stability_timer.tv_sec = 0, + }; + struct pmc_agent *node = NULL; + unsigned int i, src_cnt = 0; +@@ -1616,7 +1820,7 @@ int main(int argc, char *argv[]) + goto end; + if (auto_init_ports(&priv, rt) < 0) + goto end; +- r = do_loop(&priv, 1); ++ r = do_loop(&priv, cfg, 1); + goto end; + } + +@@ -1715,7 +1919,7 @@ int main(int argc, char *argv[]) + } + + if (ha_enabled) { +- startup_select_clock(&priv, cfg); ++ priv.master = ha_select_clock(&priv, cfg); + } + } + +@@ -1725,7 +1929,7 @@ int main(int argc, char *argv[]) + servo_sync_interval(dst->servo, 1.0); + r = do_pps_loop(&priv, dst, pps_fd); + } else { +- r = do_loop(&priv, 0); ++ r = do_loop(&priv, cfg, 0); + } + + end: +diff --git a/pmc_agent.c b/pmc_agent.c +index 534f483..af15710 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -162,7 +162,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + return RUN_PMC_NODEV; + } + if (res <= 0 || +- node->recv_subscribed(node->recv_context, *msg, ds_id) || ++ node->recv_subscribed(node, node->recv_context, *msg, ds_id) || + management_tlv_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; +@@ -280,12 +280,21 @@ int pmc_agent_query_dds(struct pmc_agent *node, int timeout) + struct ptp_message *msg; + struct defaultDS *dds; + int res; ++ struct ClockQuality *current, *new; + + res = run_pmc(node, timeout, MID_DEFAULT_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } + dds = (struct defaultDS *) management_tlv_data(msg); ++ current = &node->dds.clockQuality; ++ new = &dds->clockQuality; ++ ++ if ((current->clockClass != new->clockClass) || ++ (current->clockAccuracy != new->clockAccuracy) || ++ (current->offsetScaledLogVariance != new->offsetScaledLogVariance)) { ++ node->new_dds = true; ++ } + memcpy(&node->dds, dds, sizeof(node->dds)); + node->dds_valid = true; + msg_put(msg); +@@ -334,12 +343,19 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + struct timePropertiesDS *tds; + struct ptp_message *msg; + int res; ++ int sync_offset, leap, utc_offset_traceable, freq_traceable; + + res = run_pmc(node, timeout, MID_TIME_PROPERTIES_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } + ++ /* save current state */ ++ sync_offset = node->sync_offset; ++ leap = node->leap; ++ utc_offset_traceable = node->utc_offset_traceable; ++ freq_traceable = node->freq_traceable; ++ + tds = (struct timePropertiesDS *) management_tlv_data(msg); + if (tds->flags & PTP_TIMESCALE) { + node->sync_offset = tds->currentUtcOffset; +@@ -358,6 +374,15 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + node->utc_offset_traceable = 0; + node->freq_traceable = 0; + } ++ ++ /* compare to new tpds */ ++ if ((sync_offset != node->sync_offset) || ++ (leap != node->leap) || ++ (utc_offset_traceable != node->utc_offset_traceable) || ++ (freq_traceable != node->freq_traceable)) { ++ node->new_tpds = true; ++ } ++ + msg_put(msg); + return 0; + } +@@ -367,6 +392,7 @@ int pmc_agent_query_pds(struct pmc_agent *node, int timeout) + struct parentDS *pds; + struct ptp_message *msg; + int res; ++ struct ClockQuality *current, *new; + + res = run_pmc(node, timeout, MID_PARENT_DATA_SET, &msg); + if (is_run_pmc_error(res)) { +@@ -374,6 +400,11 @@ int pmc_agent_query_pds(struct pmc_agent *node, int timeout) + } + + pds = (struct parentDS *) management_tlv_data(msg); ++ current = &node->pds.grandmasterClockQuality; ++ new = &pds->grandmasterClockQuality; ++ if (current->clockClass != new->clockClass) { ++ node->new_pds = true; ++ } + memcpy(&node->pds, pds, sizeof(node->pds)); + node->pds_valid = true; + msg_put(msg); +@@ -396,6 +427,7 @@ int pmc_agent_update(struct pmc_agent *node) + struct ptp_message *msg; + struct timespec tp; + uint64_t ts; ++ int r; + + if (!node->pmc) { + return 0; +@@ -410,7 +442,10 @@ int pmc_agent_update(struct pmc_agent *node) + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +- if (!pmc_agent_query_utc_offset(node, 0)) { ++ r = pmc_agent_query_utc_offset(node, 0); ++ r += pmc_agent_query_dds(node, 0); ++ r += pmc_agent_query_pds(node, 0); ++ if (!r) { + node->pmc_last_update = ts; + } + } +diff --git a/pmc_agent.h b/pmc_agent.h +index 2bd7f02..8207c46 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -26,7 +26,8 @@ + + #include "pmc_common.h" + +-typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, ++struct pmc_agent; ++typedef int pmc_node_recv_subscribed_t(struct pmc_agent* node, void *context, struct ptp_message *msg, + int excluded); + + struct pmc_agent { +@@ -36,15 +37,18 @@ struct pmc_agent { + + struct defaultDS dds; + bool dds_valid; ++ bool new_dds; + int leap; + int pmc_ds_requested; + bool stay_subscribed; + int sync_offset; + int utc_offset_traceable; + int freq_traceable; ++ bool new_tpds; + unsigned int index; + struct parentDS pds; + bool pds_valid; ++ bool new_pds; + + /* Callback on message reception */ + pmc_node_recv_subscribed_t *recv_subscribed; +-- +2.25.1 + +From 7d5061d971a8abc2ba8443edccde38e9a7a6f0ce Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Wed, 26 Jul 2023 15:08:15 -0300 +Subject: [PATCH 40/47] Forced lock a clock source in configuration + +To help on maintenance and debuging tasks was implemented a configuration +to forced lock to a single clock. It disables the automatic clock +selection algorithm and lock to a source interface. + +When an interface is configured with maximum ha_priority (254) +the source selection is locked to it, regardless of its clock +status. + +When more than one source clock is configured with ha_priority 254 +selects the 1st interface in the configuration file. + +Test plan: forced lock by configuration +PASS: Verify the clock source is forced lock to an interface, regardless +its state. +PASS: Verify the clock source remains locked event after change the clock +state. +PASS: Verify the 1st configured interface with priority 254 is selected +when multiple interfaces has the same priority. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit 9563a04ef76cda55f9f014150270dbd320ca4bc4 upstream] +[commit 655fe5e304386b4494d864638ca972c4bd892e52 upstream] +[commit 3200a16f4cbe2d125bf301827a24d3d01e7f1c70 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 2 +- + phc2sys.c | 105 ++++++++++++++++++++++++++++++++++++++---------------- + 2 files changed, 75 insertions(+), 32 deletions(-) + +diff --git a/config.c b/config.c +index 1ad5157..dba1eef 100644 +--- a/config.c ++++ b/config.c +@@ -256,7 +256,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("ha_min_gm_ClockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), +- PORT_ITEM_INT("ha_priority", 0, 0, 255), ++ PORT_ITEM_INT("ha_priority", 0, 0, 254), + PORT_ITEM_INT("ha_stability_timer", 0, 0, INT_MAX), + GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), + PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), +diff --git a/phc2sys.c b/phc2sys.c +index 152e783..0b3f724 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -64,6 +64,7 @@ + + #define PHC_PPS_OFFSET_LIMIT 10000000 + ++#define FORCED_SOURCE_CLOCK_PRIORITY 254 + #define MAX_SRC_CLOCKS 128 + + #define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) | (((unsigned int) index) & 0xFF)) +@@ -121,6 +122,7 @@ struct phc2sys_private { + struct clock *better; + struct timespec stability_timer; + int default_sync; ++ int forced_source_clock; + }; + + static struct config *phc2sys_config; +@@ -998,6 +1000,29 @@ static int update_needed(struct clock *c) + return 0; + } + ++/* check configuration if one of the source clocks is force locked to be active */ ++static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ int clock_priority; ++ struct clock *clock = NULL, *best = NULL; ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ continue; ++ } ++ ++ clock_priority = config_get_int(cfg, clock->device, "ha_priority"); ++ if (FORCED_SOURCE_CLOCK_PRIORITY == clock_priority) { ++ pr_info("HA automatic source selection is disabled by configuration"); ++ priv->forced_source_clock = 1; ++ best = clock; ++ } ++ } ++ ++ return best; ++} ++ + static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config *cfg) + { + int clock_priority, highest_priority; +@@ -1066,7 +1091,7 @@ static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config + } + + if (best) +- pr_notice("Best clock selected %s", best->device); ++ pr_notice("best clock available %s", best->device); + + return best; + } +@@ -1121,7 +1146,7 @@ static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct + return NULL; + } + +- /* stability timer = 0 - change active */ ++ /* stability timer equal 0 - change active */ + stability_timer = config_get_int(cfg, NULL, "ha_stability_timer"); + if (stability_timer == 0) { + pr_notice("new source clock selected %s", candidate->device); +@@ -1173,6 +1198,10 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (node->new_dds || node->new_tpds || node->new_pds) { ++ pr_debug("pmc agent index %d clock state changed by %s%s%s", ++ node->index, node->new_dds ? "new dds " : "", ++ node->new_tpds ? "new tpds " : "", ++ node->new_pds ? "new pds " : ""); + priv->clock_state_changed = 1; + } + +@@ -1194,30 +1223,38 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (ha_enabled) { +- if (priv->clock_state_changed) { +- clock = check_and_select_clock(priv, cfg); +- if (clock && clock != priv->master) { +- priv->master = clock; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ if (priv->forced_source_clock) { ++ /* HA automatic clock selection is disabled */ ++ if (priv->clock_state_changed) { ++ priv->clock_state_changed = 0; ++ reset_new_dataset_flags(priv); + } ++ } else { ++ if (priv->clock_state_changed) { ++ clock = check_and_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } + +- priv->clock_state_changed = 0; +- reset_new_dataset_flags(priv); +- } ++ priv->clock_state_changed = 0; ++ reset_new_dataset_flags(priv); ++ } + +- if (priv->better) { +- /* has stability timer expired? */ +- clock_gettime(CLOCK_REALTIME, &now); +- if ((now.tv_sec > priv->stability_timer.tv_sec) || +- (now.tv_sec == priv->stability_timer.tv_sec && +- now.tv_nsec > priv->stability_timer.tv_nsec)) { +- pr_notice("new source clock selected %s", priv->better->device); +- priv->master = priv->better; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ if (priv->better) { ++ /* has stability timer expired? */ ++ clock_gettime(CLOCK_REALTIME, &now); ++ if ((now.tv_sec > priv->stability_timer.tv_sec) || ++ (now.tv_sec == priv->stability_timer.tv_sec && ++ now.tv_nsec > priv->stability_timer.tv_nsec)) { ++ pr_notice("new source clock selected %s", priv->better->device); ++ priv->master = priv->better; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } + } + } + } +@@ -1313,12 +1350,8 @@ static int phc2sys_recv_subscribed(struct pmc_agent *node, void *context, struct + struct phc2sys_private *priv = (struct phc2sys_private *) context; + int mgt_id, state; + struct portDS *pds; +- struct defaultDS *dds; +- struct parentDS *parentds; +- struct timePropertiesDS *tds; + struct port *port; + struct clock *clock; +- int utc_offset_traceable, freq_traceable; + + mgt_id = management_tlv_id(msg); + if (mgt_id == excluded) +@@ -1563,6 +1596,7 @@ int main(int argc, char *argv[]) + .master = NULL, + .better = NULL, + .stability_timer.tv_sec = 0, ++ .forced_source_clock = 0, + }; + struct pmc_agent *node = NULL; + unsigned int i, src_cnt = 0; +@@ -1861,13 +1895,19 @@ int main(int argc, char *argv[]) + goto bad_usage; + } + ++ if (ha_enabled) { ++ src = ha_forced_source_clock(&priv, cfg); ++ if (src != NULL) { ++ pr_info("Only interface %s will be used as source clock", src->device); ++ priv.master = src; ++ } ++ } ++ + r = -1; + + if (wait_sync) { + i = 0; +- for (src = LIST_FIRST(&priv.clocks); +- src != NULL; +- src = LIST_NEXT(src, list)) { ++ LIST_FOREACH(src, &priv.clocks, list) { + + /* skip dst clock */ + if (src == dst) { +@@ -1890,6 +1930,8 @@ int main(int argc, char *argv[]) + + /* map clock to pmc agent node */ + src->node = node; ++ pr_debug("pmc node index %d source clock %s initialized", ++ node->index, src->device); + + while (is_running()) { + r = run_pmc_wait_sync(node, 1000); +@@ -1918,8 +1960,9 @@ int main(int argc, char *argv[]) + ++i; + } + +- if (ha_enabled) { ++ if (ha_enabled && !priv.forced_source_clock) { + priv.master = ha_select_clock(&priv, cfg); ++ pr_info("interface %s will be used as source clock", priv.master->device); + } + } + +-- +2.25.1 + +From fce993dd36e481aace337a62ff81331cd2411bec Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Thu, 27 Jul 2023 14:22:47 -0300 +Subject: [PATCH 41/47] HA phc2sys com socket + +A new communication path was created to retrieve status and to control +the high availability algorithm. + +The ha_phc2sys_com_socket option is a global setting to configure +the socket path. Its default value is /var/run/phc2sys. + +The command 'status' was created to retrieve the current HA clock status. +The answer is a table of configured clocks and its status. + +act interface priority clockClass clockAcc offset time freq gm. + + * ens2f1 200 248 0xfe 0xffff no no 6 + ens1f2 100 248 0xfe 0xffff no no 6 + +Source forced? no + +The * sign marks the active source clock. +The - sign marks the active candidate source clock, which will be set active +after the stability timer expiration. +The x sign marks the disabled interfaces (see 'disable source' command). + +The 'Source forced?' field shows if the active source is forced lock or not. + +The 'clock source' command can be used to retrive the active +clock source. It returns the interface name of the active +clock source or "None" when there is no one select. + +The 'forced lock' command can be used to retrieve if the active +clock source is forced lock, and the clock source selection +algorithm is disabled. It returns "True" when is forced lock +and "False" otherwise. + +Test plan: socket path configuration +PASS Verify the socket using the default path. +PASS Verify the socket using a given socket path. + +Test plan: status command +PASS: Verify the 'status' command after start up. +PASS: Verify the 'status' command while stability timer is running. +PASS: Verify the 'status' command with a forced lock interface by +configuring ha_priority 254. + +Test plan: clock source command +PASS: Verify the 'clock source' command response is the highest priority +interface after start up. +PASS: Verify the 'clock source' command response is the active interface +after the primary has degraded. +PASS: Verify the 'clock source' command response is the forced lock +interface, when ha_priority 254 is configured in one of them. + +Test plan: forced lock command +PASS: Verify the 'forced lock' command response is 'False' when no +interface is configured with ha_priority 254. +PASS: Verify the 'forced lock' command response is 'True' when one +interface is configured with ha_priority 254. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit 0cfcbb78485a83d324963130f9558fd0a1962a79 upstream] +[commit 73b9afa33a0d8dcfd9c4ebb7bceacee40af8eb2b upstream] +[commit 6e93059d34639a3c2aac6b56dcf94ddf1e48e9b4 upstream] +[commit 4f118cf954bc3543582765bc039c42aeac05caf5 upstream] +[commit 6387ddf644afcb880b67368be8416b8ce906e029 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 1 + + phc2sys.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 216 insertions(+), 16 deletions(-) + +diff --git a/config.c b/config.c +index dba1eef..6a1bfb4 100644 +--- a/config.c ++++ b/config.c +@@ -256,6 +256,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("ha_min_gm_ClockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), ++ GLOB_ITEM_STR("ha_phc2sys_com_socket", "/var/run/phc2sys-phc-inst1"), + PORT_ITEM_INT("ha_priority", 0, 0, 254), + PORT_ITEM_INT("ha_stability_timer", 0, 0, INT_MAX), + GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), +diff --git a/phc2sys.c b/phc2sys.c +index 0b3f724..0bc3709 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -66,6 +66,9 @@ + + #define FORCED_SOURCE_CLOCK_PRIORITY 254 + #define MAX_SRC_CLOCKS 128 ++#define HA_SCK_N_FD 1 ++#define HA_SCK_BUFFER_SIZE 1024 ++#define HA_SCK_FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) /*0660*/ + + #define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) | (((unsigned int) index) & 0xFF)) + #define PORT_ID_TO_PORT(id) ((((unsigned int) id) >> 8) & 0xFF) +@@ -94,6 +97,7 @@ struct clock { + struct stats *delay_stats; + struct clockcheck *sanity_check; + struct pmc_agent *node; ++ int ha_priority; + }; + typedef LIST_HEAD(head, clock) clock_list_head_t; + +@@ -123,6 +127,7 @@ struct phc2sys_private { + struct timespec stability_timer; + int default_sync; + int forced_source_clock; ++ int ha_socket_fd; + }; + + static struct config *phc2sys_config; +@@ -1003,7 +1008,6 @@ static int update_needed(struct clock *c) + /* check configuration if one of the source clocks is force locked to be active */ + static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct config *cfg) + { +- int clock_priority; + struct clock *clock = NULL, *best = NULL; + + LIST_FOREACH(clock, &priv->clocks, list) { +@@ -1012,8 +1016,7 @@ static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct + continue; + } + +- clock_priority = config_get_int(cfg, clock->device, "ha_priority"); +- if (FORCED_SOURCE_CLOCK_PRIORITY == clock_priority) { ++ if (FORCED_SOURCE_CLOCK_PRIORITY == clock->ha_priority) { + pr_info("HA automatic source selection is disabled by configuration"); + priv->forced_source_clock = 1; + best = clock; +@@ -1025,7 +1028,7 @@ static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct + + static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config *cfg) + { +- int clock_priority, highest_priority; ++ int highest_priority; + int clock_class, lowest_clock_class; + struct clock *clock = NULL, *best = NULL; + clock_list_head_t ha_available_clocks; +@@ -1038,17 +1041,14 @@ static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config + + /* one or more sources match requirements, select highest priority */ + highest_priority = 0; +- LIST_FOREACH(clock, &ha_available_clocks, ha_list) { +- clock_priority = config_get_int(cfg, clock->device, "ha_priority"); +- +- /* select highest priority clock ++ LIST_FOREACH(clock, &ha_available_clocks, ha_list) {/* select highest priority clock + more than one clock with same priority, select first + don't select clocks with ha_priority 0 */ +- if (clock_priority > highest_priority) { ++ if (clock->ha_priority > highest_priority) { + pr_notice("new highest ha priority clock %s ha_priority %d", +- clock->device, clock_priority); ++ clock->device, clock->ha_priority); + best = clock; +- highest_priority = clock_priority; ++ highest_priority = clock->ha_priority; + } + } + +@@ -1101,7 +1101,6 @@ static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct + struct clock *active = priv->master, *candidate = NULL; + int stability_timer = 0; + struct timespec now; +- int active_priority, candidate_priority; + int active_clock_class, candidate_clock_class; + + /* Active source degrades - re-run ha_select_clock algorithm */ +@@ -1137,11 +1136,9 @@ static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct + /* new clock candidate */ + + /* candidate has equal priority and clockClass than active - don't change active */ +- active_priority = config_get_int(cfg, active->device, "ha_priority"); +- candidate_priority = config_get_int(cfg, candidate->device, "ha_priority"); + active_clock_class = active->node->dds.clockQuality.clockClass; + candidate_clock_class = candidate->node->dds.clockQuality.clockClass; +- if ((active_priority == candidate_priority) && ++ if ((active->ha_priority == candidate->ha_priority) && + (active_clock_class == candidate_clock_class)) { + return NULL; + } +@@ -1175,6 +1172,196 @@ static void reset_new_dataset_flags(struct phc2sys_private *priv) + } + } + ++static int ha_com_socket_close(int fd) ++{ ++ struct sockaddr_un sa; ++ socklen_t len = sizeof(sa); ++ ++ // if (fd < 0) ++ // return -1; ++ ++ if (!getsockname(fd, (struct sockaddr *) &sa, &len) && ++ sa.sun_family == AF_LOCAL) { ++ unlink(sa.sun_path); ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++static int ha_com_socket_open(int *fd_out, struct config *cfg) ++{ ++ int fd, err; ++ struct sockaddr_un sa; ++ const char *name = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); ++ ++ fd = socket(AF_LOCAL, SOCK_DGRAM, 0); ++ if (fd < 0) { ++ pr_err("ha_com_socket: failed to create socket: %m"); ++ return -1; ++ } ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.sun_family = AF_LOCAL; ++ strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1); ++ ++ err = bind(fd, (struct sockaddr *) &sa, sizeof(sa)); ++ if (err < 0) { ++ pr_err("ha_com_socket: bind failed: %m"); ++ close(fd); ++ return -1; ++ } ++ ++ *fd_out = fd; ++ chmod(name, HA_SCK_FILEMODE); ++ ++ return 0; ++} ++ ++static int ha_com_socket_recv(int fd, void *buf, size_t buflen, ++ struct address *addr) ++{ ++ int cnt; ++ ++ addr->len = sizeof(addr->sun); ++ cnt = recvfrom(fd, buf, buflen, 0, &addr->sa, &addr->len); ++ if (cnt <= 0) { ++ pr_err("ha_com_socket: recvfrom failed: %m"); ++ return cnt; ++ } ++ ++ ((char*)buf)[cnt] = '\0'; ++ ++ return 0; ++} ++ ++static int ha_com_socket_send(int fd, struct address *addr, void *buf, ++ size_t buflen) ++{ ++ int cnt; ++ ++ cnt = sendto(fd, buf, buflen, 0, &addr->sa, addr->len); ++ if (cnt < 1) { ++ return -errno; ++ } ++ return cnt; ++} ++ ++static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, ++ size_t resplen) ++{ ++ struct clock *clock; ++ size_t curlen = 0; ++ ++ /* header */ ++ curlen = snprintf(response, resplen, ++ "act interface priority clockClass clockAcc offset time freq " ++ "gm.clockClass\n\n"); ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (clock->node == NULL) ++ continue; ++ ++ curlen += snprintf(response + curlen, resplen - curlen, ++ " %c %9s %8d %10d 0x%2x 0x%4x %s %s %d\n", ++ (priv->master == clock) ? '*' : ++ (priv->better == clock) ? '-' : ' ', ++ clock->device, clock->ha_priority, ++ clock->node->dds.clockQuality.clockClass, ++ clock->node->dds.clockQuality.clockAccuracy, ++ clock->node->dds.clockQuality.offsetScaledLogVariance, ++ clock->node->utc_offset_traceable ? "yes" : "no ", ++ clock->node->freq_traceable ? "yes" : "no ", ++ clock->node->pds.grandmasterClockQuality.clockClass); ++ } ++ ++ curlen += snprintf(response + curlen, resplen - curlen, ++ "\n\nSource forced? %s\n", priv->forced_source_clock ? "yes" : "no"); ++ ++ return curlen; ++} ++ ++static int ha_com_socket_handle_msg(struct phc2sys_private *priv) ++{ ++ struct pollfd pollfd[HA_SCK_N_FD]; ++ struct address sender; ++ int cnt, res = 0; ++ int timeout = 0; ++ void * buffer = NULL; ++ void * response = NULL; ++ ++ while(1) { ++ pollfd[0].fd = priv->ha_socket_fd; ++ pollfd[0].events = POLLIN|POLLPRI; ++ ++ cnt = poll(pollfd, HA_SCK_N_FD, timeout); ++ if (cnt < 0) { ++ pr_err("ha_com_socket: poll failed: %m"); ++ res = -1; ++ break; ++ } ++ if (!cnt) { ++ /* timeout and fd wasn't ready */ ++ break; ++ } ++ ++ if (!(pollfd[0].revents & (POLLIN|POLLPRI))) ++ break; ++ ++ buffer = malloc(HA_SCK_BUFFER_SIZE); ++ if (!buffer) { ++ pr_err("ha_com_socket: failed to allocate memory for message"); ++ res = -1; ++ break; ++ } ++ ++ res = ha_com_socket_recv(pollfd[0].fd, buffer, HA_SCK_BUFFER_SIZE, &sender); ++ if (res < 0) ++ break; ++ ++ fprintf(stderr, "ha_com_socket: received: %s\n", (char*)buffer); ++ fprintf(stderr, "ha_com_socket: recvd from: %s\n", ((struct sockaddr_un*)&sender.sa)->sun_path); ++ ++ response = malloc(HA_SCK_BUFFER_SIZE); ++ if (!response) { ++ pr_err("ha_com_socket: failed to allocate memory for response message"); ++ res = -1; ++ break; ++ } ++ ++ /* handle messages and create responses */ ++ if (strcmp((const char*)buffer, "status") == 0) { ++ cnt = ha_handle_status_msg(priv, response, HA_SCK_BUFFER_SIZE); ++ } else if (strcmp((const char*)buffer, "clock source") == 0) { ++ if (priv->master) { ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", ++ priv->master->device); ++ } else { ++ cnt = snprintf((char*)buffer, HA_SCK_BUFFER_SIZE, "None"); ++ } ++ } else if (strcmp((const char*)buffer, "forced lock") == 0) { ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", ++ priv->forced_source_clock ? "True" : "False"); ++ } else { ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "error: invalid command"); ++ } ++ ++ fprintf(stderr, "ha_com_socket: response: \n%s", (char*)response); ++ ++ res = ha_com_socket_send(pollfd[0].fd, &sender, response, cnt); ++ } ++ ++ free(buffer); ++ free(response); ++ return res; ++} ++ + static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscriptions) + { + struct timespec interval; +@@ -1223,6 +1410,8 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (ha_enabled) { ++ ha_com_socket_handle_msg(priv); ++ + if (priv->forced_source_clock) { + /* HA automatic clock selection is disabled */ + if (priv->clock_state_changed) { +@@ -1312,6 +1501,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + update_clock(priv, clock, offset, ts, delay); + } + } ++ + return 0; + } + +@@ -1597,6 +1787,7 @@ int main(int argc, char *argv[]) + .better = NULL, + .stability_timer.tv_sec = 0, + .forced_source_clock = 0, ++ .ha_socket_fd = -1, + }; + struct pmc_agent *node = NULL; + unsigned int i, src_cnt = 0; +@@ -1861,7 +2052,7 @@ int main(int argc, char *argv[]) + ha_enabled = config_get_int(cfg, NULL, "ha_enabled"); + if (!ha_enabled && src_cnt > 1) { + fprintf(stderr, "too many source clocks\n"); +- fprintf(stderr, "Use 'ha_enabled 1' to accept more than one source clocks\n"); ++ fprintf(stderr, "Use 'ha_enabled 1' to accept more than one source clock\n"); + goto bad_usage; + } + +@@ -1877,6 +2068,9 @@ int main(int argc, char *argv[]) + if (priv.master == NULL) { + priv.master = src; + } ++ if (ha_enabled) { ++ src->ha_priority = config_get_int(cfg, src->device, "ha_priority"); ++ } + } + + dst = clock_add(&priv, dst_name ? dst_name : "CLOCK_REALTIME"); +@@ -1966,6 +2160,10 @@ int main(int argc, char *argv[]) + } + } + ++ if (ha_enabled) { ++ ha_com_socket_open(&priv.ha_socket_fd, cfg); ++ } ++ + if (pps_fd >= 0) { + /* only one destination clock allowed with PPS until we + * implement a mean to specify PTP port to PPS mapping */ +@@ -1976,6 +2174,7 @@ int main(int argc, char *argv[]) + } + + end: ++ ha_com_socket_close(priv.ha_socket_fd); + pmc_agent_cleanup(&priv); + clock_cleanup(&priv); + port_cleanup(&priv); +-- +2.25.1 + +From e77783a9873baeeda277cfa59059021ce121a693 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Fri, 4 Aug 2023 15:44:12 -0300 +Subject: [PATCH 42/47] Commands 'enable lock' and 'disable lock. + +The 'enable lock' command is used to lock to a single clock +source and disable the HA clock selection algorithm. The +interface of the clock source must be specified in the +command. For example: + +'enable lock ' + +It returns "Success" or an error message. The error message +"Error: Usage 'enable lock '" is returned when +no interface was provided in the command. The error message +"Error: Interface not found!" is returned when the interface +provided is not found in the phc2sys configuration. + +The command 'disable lock' is used to unlock the clock source +and re-enable the HA clock selection algorithm. It returns +"Success" even when the clock source was not locked. + +Test plan: enable lock and disable lock commands +PASS: Verify the enable lock changes the clock source to the given +interface. +PASS: Verify that regardless the interface state, the clock source +remains locked. +PASS: Verify that disable lock command makes the better available +clock to be selected again. + +[commit 704d9ed2e22b89308c7f0149d7fde86d456bc4e3 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 110 +++++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 93 insertions(+), 17 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 0bc3709..f89dc23 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -259,13 +259,21 @@ static void clock_cleanup(struct phc2sys_private *priv) + } + } + +-static struct clock *clock_get(struct phc2sys_private *priv, struct pmc_agent *node) ++static struct clock * clock_get_by_device(struct phc2sys_private *priv, ++ const char * device) + { + struct clock * clock = NULL; + LIST_FOREACH(clock, &priv->clocks, list) { +- if (clock->node == node) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (!clock->device) ++ continue; ++ ++ if (strcmp(device, clock->device) == 0) + break; +- } + } + return clock; + } +@@ -508,18 +516,6 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number) + return NULL; + } + +-static struct port *port_get_by_clock(struct phc2sys_private *priv, struct clock * clock) +-{ +- struct port *p, *port = NULL; +- LIST_FOREACH(p, &priv->ports, list) { +- if (p->clock == clock) { +- port = p; +- break; +- } +- } +- return port; +-} +- + static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + char *device) + { +@@ -1287,7 +1283,82 @@ static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, + return curlen; + } + +-static int ha_com_socket_handle_msg(struct phc2sys_private *priv) ++static bool startsWith(const char *prefix, const char *str) ++{ ++ return 0 == strncmp(prefix, str, strlen(prefix) - 1); ++} ++ ++static char * strAtColumn(char *msg, size_t column) ++{ ++ int i; ++ char * str = NULL; ++ ++ /* split and walk over the columns */ ++ strtok(msg, " "); ++ for (i = 1; i < column; i++) { ++ str = strtok(NULL, " "); ++ } ++ ++ return str; ++} ++ ++static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, ++ char *response, size_t resplen) ++{ ++ size_t curlen = 0; ++ char *interface = NULL; ++ struct clock *clock = NULL; ++ ++ interface = strAtColumn(msg, 3); ++ if (strlen(interface) == 0) { ++ return snprintf(response, resplen, "Error: Usage 'enable lock '"); ++ } ++ ++ clock = clock_get_by_device(priv, interface); ++ if (!clock) { ++ return snprintf(response, resplen, "Error: Interface not found!"); ++ } ++ ++ pr_info("HA automatic source selection is disabled by command"); ++ pr_info("Only interface %s will be used as source clock", clock->device); ++ ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ ++ priv->forced_source_clock = 1; ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_handle_disable_lock_msg(struct phc2sys_private *priv, ++ struct config *cfg, char *response, size_t resplen) ++{ ++ size_t curlen = 0; ++ struct clock *clock = NULL; ++ ++ if (priv->forced_source_clock) { ++ pr_info("HA automatic source selection is enabled by command"); ++ /* re-enable HA source selection algorithm */ ++ priv->forced_source_clock = 0; ++ /* select the best clock available */ ++ clock = ha_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ priv->master = clock; ++ pr_notice("new source clock selected %s", clock->device); ++ } ++ } ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_com_socket_handle_msg(struct phc2sys_private *priv, ++ struct config *cfg) + { + struct pollfd pollfd[HA_SCK_N_FD]; + struct address sender; +@@ -1348,6 +1419,11 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv) + } else if (strcmp((const char*)buffer, "forced lock") == 0) { + cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", + priv->forced_source_clock ? "True" : "False"); ++ } else if (startsWith("enable lock", buffer)) { ++ cnt = ha_handle_enable_lock_msg(priv, buffer, response, ++ HA_SCK_BUFFER_SIZE); ++ } else if (strcmp((const char*)buffer, "disable lock") == 0) { ++ cnt = ha_handle_disable_lock_msg(priv, cfg, response, HA_SCK_BUFFER_SIZE); + } else { + cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "error: invalid command"); + } +@@ -1410,7 +1486,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (ha_enabled) { +- ha_com_socket_handle_msg(priv); ++ ha_com_socket_handle_msg(priv, cfg); + + if (priv->forced_source_clock) { + /* HA automatic clock selection is disabled */ +-- +2.25.1 + +From 27b5c6afff470053b30ade14537be43f1c1c376d Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Fri, 4 Aug 2023 19:01:57 -0300 +Subject: [PATCH 43/47] Commands 'enable source' and 'disable source'. + +These commands controls the list of clocks available to clock +selection algorithm. + +At startup all sources are enabled and can be selected as clock +source. The 'disable source' command removes a given interface +from the available list and it can't be selected any more. The +'enable source' command re-enables the interface. + +The last interface can't be disable. The disable command fails and +returns an error indicating the given interface is the last one. + +If the active clock source interface is disabled than a new one +will be selected. + +Every time the enable command is executed the clock selection +algorithm is executed and the best available clock is selected. + +The enable and disable source commands won't affect the active +clock if one interface is forced lock as active. + +The disabled interface is market with 'x' sign in the status +command. + +Test plan: enable source and disable source commands +PASS: Verify a new interface is selected when the active one +is disabled. +PASS: Verify the primary interface is re-selected active after +it is enabled back. +PASS: Verify the disable source command fails when attempt to +disable the last enabled interface. +PASS: Verify the active interface don't change while one of them +are forced lock as active. +PASS: Verify the active interface dont't change after enabling +an interface while in forced lock mode. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit 55ac3f4131aaa999b1b7b9eec50b7cb7cebbf0d4 upstream] +[commit c77de0acd3641833d2705e3929be2152bd5fb519 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 127 insertions(+), 19 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index f89dc23..035ee21 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -98,6 +98,7 @@ struct clock { + struct clockcheck *sanity_check; + struct pmc_agent *node; + int ha_priority; ++ int enabled; + }; + typedef LIST_HEAD(head, clock) clock_list_head_t; + +@@ -228,6 +229,8 @@ static struct clock *clock_add(struct phc2sys_private *priv, char *device) + c->sysoff_method = sysoff_probe(CLOCKID_TO_FD(clkid), + priv->phc_readings); + ++ c->enabled = 1; ++ + LIST_INSERT_HEAD(&priv->clocks, c, list); + return c; + } +@@ -278,6 +281,28 @@ static struct clock * clock_get_by_device(struct phc2sys_private *priv, + return clock; + } + ++static size_t clock_count_enabled_sources(struct phc2sys_private *priv, ++ struct clock *ignore) ++{ ++ size_t count = 0; ++ struct clock * clock = NULL; ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ if (clock == ignore) ++ continue; ++ ++ if (!clock->enabled) ++ continue; ++ ++ count++; ++ } ++ return count; ++} ++ + static bool clock_match_ha_dds_requirements(struct clock *clock, struct config *cfg) + { + /* get requirements */ +@@ -404,6 +429,11 @@ static int clock_available_ha_src_clocks(struct phc2sys_private *priv, struct co + continue; + } + ++ if (!clock->enabled) { ++ pr_debug("clock %s is disabled", clock->device); ++ continue; ++ } ++ + /* get Default Data Set */ + if (!clock->node->dds_valid) { + retries = 0; +@@ -1267,7 +1297,8 @@ static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, + curlen += snprintf(response + curlen, resplen - curlen, + " %c %9s %8d %10d 0x%2x 0x%4x %s %s %d\n", + (priv->master == clock) ? '*' : +- (priv->better == clock) ? '-' : ' ', ++ (priv->better == clock) ? '-' : ++ (!clock->enabled) ? 'x' : ' ', + clock->device, clock->ha_priority, + clock->node->dds.clockQuality.clockClass, + clock->node->dds.clockQuality.clockAccuracy, +@@ -1302,6 +1333,16 @@ static char * strAtColumn(char *msg, size_t column) + return str; + } + ++static void ha_set_clock_source(struct phc2sys_private *priv, struct clock *clock) ++{ ++ pr_notice("new clock source selected %s", clock->device); ++ ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++} ++ + static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, + char *response, size_t resplen) + { +@@ -1316,16 +1357,13 @@ static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, + + clock = clock_get_by_device(priv, interface); + if (!clock) { +- return snprintf(response, resplen, "Error: Interface not found!"); ++ return snprintf(response, resplen, "Error: Interface not found"); + } + + pr_info("HA automatic source selection is disabled by command"); + pr_info("Only interface %s will be used as source clock", clock->device); + +- priv->master = clock; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ ha_set_clock_source(priv, clock); + + priv->forced_source_clock = 1; + +@@ -1347,8 +1385,77 @@ static int ha_handle_disable_lock_msg(struct phc2sys_private *priv, + /* select the best clock available */ + clock = ha_select_clock(priv, cfg); + if (clock && clock != priv->master) { +- priv->master = clock; +- pr_notice("new source clock selected %s", clock->device); ++ ha_set_clock_source(priv, clock); ++ } ++ } ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_handle_enable_source_msg(struct phc2sys_private *priv, ++ struct config *cfg, char *msg, char *response, size_t resplen) ++{ ++ size_t curlen; ++ char *interface = NULL; ++ struct clock *clock = NULL; ++ ++ interface = strAtColumn(msg, 3); ++ if (strlen(interface) == 0) { ++ return snprintf(response, resplen, "Error: Usage 'enable source '"); ++ } ++ ++ clock = clock_get_by_device(priv, interface); ++ if (!clock) { ++ return snprintf(response, resplen, "Error: Interface not found"); ++ } ++ ++ clock->enabled = 1; ++ ++ if (!priv->forced_source_clock) { ++ /* select the best clock available */ ++ clock = ha_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ ha_set_clock_source(priv, clock); ++ } ++ } ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_handle_disable_source_msg(struct phc2sys_private *priv, ++ struct config *cfg, char *msg, char *response, size_t resplen) ++{ ++ size_t curlen; ++ char *interface = NULL; ++ struct clock *clock = NULL; ++ ++ interface = strAtColumn(msg, 3); ++ if (strlen(interface) == 0) { ++ return snprintf(response, resplen, "Error: Usage 'disable source '"); ++ } ++ ++ clock = clock_get_by_device(priv, interface); ++ if (!clock) { ++ return snprintf(response, resplen, "Error: Interface not found"); ++ } ++ ++ /* check if is the last clock enabled */ ++ if (clock_count_enabled_sources(priv, clock) == 0) { ++ return snprintf(response, resplen, "Error: Last interface enabled"); ++ } ++ ++ clock->enabled = 0; ++ ++ /* disabling clock source */ ++ if (clock == priv->master && !priv->forced_source_clock) { ++ /* select the best clock available */ ++ clock = ha_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ ha_set_clock_source(priv, clock); + } + } + +@@ -1423,9 +1530,17 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + cnt = ha_handle_enable_lock_msg(priv, buffer, response, + HA_SCK_BUFFER_SIZE); + } else if (strcmp((const char*)buffer, "disable lock") == 0) { +- cnt = ha_handle_disable_lock_msg(priv, cfg, response, HA_SCK_BUFFER_SIZE); ++ cnt = ha_handle_disable_lock_msg(priv, cfg, response, ++ HA_SCK_BUFFER_SIZE); ++ } else if (startsWith("enable source", buffer)) { ++ cnt = ha_handle_enable_source_msg(priv, cfg, buffer, response, ++ HA_SCK_BUFFER_SIZE); ++ } else if (startsWith("disable source", buffer)) { ++ cnt = ha_handle_disable_source_msg(priv, cfg, buffer, response, ++ HA_SCK_BUFFER_SIZE); + } else { +- cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "error: invalid command"); ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, ++ "Error: Invalid command"); + } + + fprintf(stderr, "ha_com_socket: response: \n%s", (char*)response); +@@ -1498,10 +1613,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + if (priv->clock_state_changed) { + clock = check_and_select_clock(priv, cfg); + if (clock && clock != priv->master) { +- priv->master = clock; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ ha_set_clock_source(priv, clock); + } + + priv->clock_state_changed = 0; +@@ -1514,11 +1626,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + if ((now.tv_sec > priv->stability_timer.tv_sec) || + (now.tv_sec == priv->stability_timer.tv_sec && + now.tv_nsec > priv->stability_timer.tv_nsec)) { +- pr_notice("new source clock selected %s", priv->better->device); +- priv->master = priv->better; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ ha_set_clock_source(priv, priv->better); + } + } + } +-- +2.25.1 + +From 2d40cc7cf52bbf054856c34902e4bda9f13ebb79 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 7 Aug 2023 14:55:12 -0300 +Subject: [PATCH 44/47] Stream type phc2sys com socket + +The type of the socket was changed from datagram to stream. + +Test plan: status/show commands +PASS: Verify status command response +PASS: Verify forced lock command response +PASS: Verify clock source command response + +Test plan: enable lock and disable lock commands +PASS: Verify the enable lock changes the clock source to the given +interface. +PASS: Verify that disable lock command makes the better available +clock to be selected again. + +Test plan: disable source and enable source commands +PASS: Verify a new interface is selected when the active one +is disabled. +PASS: Verify the primary interface is re-selected active after +it is enabled back. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit b4f79cb626d6e40cf1d5aa2c5d5fba89e2c2e340 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 76 +++++++++++++++++++++++++++---------------------------- + 1 file changed, 38 insertions(+), 38 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 035ee21..a597014 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1203,14 +1203,12 @@ static int ha_com_socket_close(int fd) + struct sockaddr_un sa; + socklen_t len = sizeof(sa); + +- // if (fd < 0) +- // return -1; +- + if (!getsockname(fd, (struct sockaddr *) &sa, &len) && + sa.sun_family == AF_LOCAL) { + unlink(sa.sun_path); + } + ++ shutdown(fd, SHUT_RDWR); + close(fd); + return 0; + } +@@ -1219,9 +1217,10 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + { + int fd, err; + struct sockaddr_un sa; ++ const int backlog = 50; + const char *name = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); + +- fd = socket(AF_LOCAL, SOCK_DGRAM, 0); ++ fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd < 0) { + pr_err("ha_com_socket: failed to create socket: %m"); + return -1; +@@ -1238,22 +1237,27 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + return -1; + } + ++ err = listen(fd, backlog); ++ if (err < 0) { ++ pr_err("ha_com_socket: listen failed: %m"); ++ close(fd); ++ return -1; ++ } ++ + *fd_out = fd; + chmod(name, HA_SCK_FILEMODE); + + return 0; + } + +-static int ha_com_socket_recv(int fd, void *buf, size_t buflen, +- struct address *addr) ++static int ha_com_socket_recv(int fd, void *buf, size_t buflen) + { + int cnt; + +- addr->len = sizeof(addr->sun); +- cnt = recvfrom(fd, buf, buflen, 0, &addr->sa, &addr->len); ++ cnt = read(fd, buf, buflen); + if (cnt <= 0) { +- pr_err("ha_com_socket: recvfrom failed: %m"); +- return cnt; ++ pr_err("ha_com_socket: read failed: %m"); ++ return -errno; + } + + ((char*)buf)[cnt] = '\0'; +@@ -1261,13 +1265,13 @@ static int ha_com_socket_recv(int fd, void *buf, size_t buflen, + return 0; + } + +-static int ha_com_socket_send(int fd, struct address *addr, void *buf, +- size_t buflen) ++static int ha_com_socket_send(int fd, void *buf, size_t buflen) + { + int cnt; + +- cnt = sendto(fd, buf, buflen, 0, &addr->sa, addr->len); +- if (cnt < 1) { ++ cnt = send(fd, buf, buflen, 0); ++ if (cnt < 0) { ++ pr_err("ha_com_socket: send failed: %m"); + return -errno; + } + return cnt; +@@ -1467,48 +1471,42 @@ static int ha_handle_disable_source_msg(struct phc2sys_private *priv, + static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + struct config *cfg) + { +- struct pollfd pollfd[HA_SCK_N_FD]; +- struct address sender; +- int cnt, res = 0; +- int timeout = 0; ++ int cnt, res = 0, fd; + void * buffer = NULL; + void * response = NULL; + + while(1) { +- pollfd[0].fd = priv->ha_socket_fd; +- pollfd[0].events = POLLIN|POLLPRI; +- +- cnt = poll(pollfd, HA_SCK_N_FD, timeout); +- if (cnt < 0) { +- pr_err("ha_com_socket: poll failed: %m"); +- res = -1; +- break; +- } +- if (!cnt) { +- /* timeout and fd wasn't ready */ ++ fd = accept(priv->ha_socket_fd, NULL, NULL); ++ if (fd < 0) { ++ if (errno == EAGAIN || errno == EWOULDBLOCK) { ++ /* no msg available */ ++ } else { ++ pr_err("ha_com_socket: accept failed: %m"); ++ res = -1; ++ } + break; + } + +- if (!(pollfd[0].revents & (POLLIN|POLLPRI))) +- break; +- + buffer = malloc(HA_SCK_BUFFER_SIZE); + if (!buffer) { + pr_err("ha_com_socket: failed to allocate memory for message"); ++ close(fd); + res = -1; + break; + } + +- res = ha_com_socket_recv(pollfd[0].fd, buffer, HA_SCK_BUFFER_SIZE, &sender); +- if (res < 0) ++ res = ha_com_socket_recv(fd, buffer, HA_SCK_BUFFER_SIZE); ++ if (res < 0) { ++ close(fd); + break; ++ } + +- fprintf(stderr, "ha_com_socket: received: %s\n", (char*)buffer); +- fprintf(stderr, "ha_com_socket: recvd from: %s\n", ((struct sockaddr_un*)&sender.sa)->sun_path); ++ pr_debug("ha_com_socket: command received: %s", (char*)buffer); + + response = malloc(HA_SCK_BUFFER_SIZE); + if (!response) { + pr_err("ha_com_socket: failed to allocate memory for response message"); ++ close(fd); + res = -1; + break; + } +@@ -1543,9 +1541,11 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + "Error: Invalid command"); + } + +- fprintf(stderr, "ha_com_socket: response: \n%s", (char*)response); ++ pr_debug("ha_com_socket: response: %s", (char*)response); + +- res = ha_com_socket_send(pollfd[0].fd, &sender, response, cnt); ++ res = ha_com_socket_send(fd, response, cnt); ++ ++ close(fd); + } + + free(buffer); +-- +2.25.1 + +From 2896553d6dfa975102cba4cc45105b000ec0ae52 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Tue, 8 Aug 2023 13:10:50 -0300 +Subject: [PATCH 45/47] Functions starts_with and str_at_column + +Renaming starts_with and str_at_column functions to match ptp4l code +style. + +Test plan: commands +PASS: Verify 'enable lock ', 'disabel source ' and +'enable source ' still work. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit f43f86eab5f8f5d2c9895d290d4bdfd6f60853f8 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index a597014..6965162 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1318,12 +1318,12 @@ static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, + return curlen; + } + +-static bool startsWith(const char *prefix, const char *str) ++static bool starts_with(const char *prefix, const char *str) + { + return 0 == strncmp(prefix, str, strlen(prefix) - 1); + } + +-static char * strAtColumn(char *msg, size_t column) ++static char * str_at_column(char *msg, size_t column) + { + int i; + char * str = NULL; +@@ -1354,7 +1354,7 @@ static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, + char *interface = NULL; + struct clock *clock = NULL; + +- interface = strAtColumn(msg, 3); ++ interface = str_at_column(msg, 3); + if (strlen(interface) == 0) { + return snprintf(response, resplen, "Error: Usage 'enable lock '"); + } +@@ -1405,7 +1405,7 @@ static int ha_handle_enable_source_msg(struct phc2sys_private *priv, + char *interface = NULL; + struct clock *clock = NULL; + +- interface = strAtColumn(msg, 3); ++ interface = str_at_column(msg, 3); + if (strlen(interface) == 0) { + return snprintf(response, resplen, "Error: Usage 'enable source '"); + } +@@ -1437,7 +1437,7 @@ static int ha_handle_disable_source_msg(struct phc2sys_private *priv, + char *interface = NULL; + struct clock *clock = NULL; + +- interface = strAtColumn(msg, 3); ++ interface = str_at_column(msg, 3); + if (strlen(interface) == 0) { + return snprintf(response, resplen, "Error: Usage 'disable source '"); + } +@@ -1524,16 +1524,16 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + } else if (strcmp((const char*)buffer, "forced lock") == 0) { + cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", + priv->forced_source_clock ? "True" : "False"); +- } else if (startsWith("enable lock", buffer)) { ++ } else if (starts_with("enable lock", buffer)) { + cnt = ha_handle_enable_lock_msg(priv, buffer, response, + HA_SCK_BUFFER_SIZE); + } else if (strcmp((const char*)buffer, "disable lock") == 0) { + cnt = ha_handle_disable_lock_msg(priv, cfg, response, + HA_SCK_BUFFER_SIZE); +- } else if (startsWith("enable source", buffer)) { ++ } else if (starts_with("enable source", buffer)) { + cnt = ha_handle_enable_source_msg(priv, cfg, buffer, response, + HA_SCK_BUFFER_SIZE); +- } else if (startsWith("disable source", buffer)) { ++ } else if (starts_with("disable source", buffer)) { + cnt = ha_handle_disable_source_msg(priv, cfg, buffer, response, + HA_SCK_BUFFER_SIZE); + } else { +-- +2.25.1 + +From f480fb54182da36baeb35bac90154abafcaf854a Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Tue, 8 Aug 2023 14:06:55 -0300 +Subject: [PATCH 46/47] Robustness improvements to phc2sys socket + +When phc2sys abnormally exits the socket file might remain created. +To avoid error when phc2sys is relaunched, the exixting file is +deleted before recriating the socket. + +If the peer application closes the socket before sending the +response completely, it will cause a broken pipe error. The +send function generates a SIGPIPE on broken pipe errors, +killing the phc2sys process unless MSG_NOSIGNAL flag is set. + +Test plan: socket file +PASS: Verify that phc2sys can restart normally after killing it. + +Test plan: SIGPIPE +PASS: Verify the phc2sys application don't exit when client socket +is closed before the respose is sent. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit 8b3765b3f104a90a487fbcb0f61074c7677c215e upstream] +[commit 50ad1c6f81a706b8be6689bea2ba2db215cf3dc3 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 6965162..edc626f 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1218,7 +1218,9 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + int fd, err; + struct sockaddr_un sa; + const int backlog = 50; +- const char *name = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); ++ const char *path = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); ++ ++ unlink(path); + + fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd < 0) { +@@ -1228,7 +1230,7 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_LOCAL; +- strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1); ++ strncpy(sa.sun_path, path, sizeof(sa.sun_path) - 1); + + err = bind(fd, (struct sockaddr *) &sa, sizeof(sa)); + if (err < 0) { +@@ -1245,7 +1247,7 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + } + + *fd_out = fd; +- chmod(name, HA_SCK_FILEMODE); ++ chmod(path, HA_SCK_FILEMODE); + + return 0; + } +@@ -1269,7 +1271,7 @@ static int ha_com_socket_send(int fd, void *buf, size_t buflen) + { + int cnt; + +- cnt = send(fd, buf, buflen, 0); ++ cnt = send(fd, buf, buflen, MSG_NOSIGNAL); + if (cnt < 0) { + pr_err("ha_com_socket: send failed: %m"); + return -errno; +-- +2.25.1 + +From c5e1599748877f16bfd1dea6910f6b8b57be7ddd Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 7 Aug 2023 18:19:37 -0300 +Subject: [PATCH 47/47] phc2sys without -w option. + +Fix bad clock and pmc initialization when -w command argument +is not provided. + +The pmc agent must be created and mapped to a clock source even +in cases the -w (wait for ptp4l) option is not used. + +Test plan: +PASS: Verify phc2sys initializes without -w argument. +PASS: Verify phc2sys initializes with two ptp4l interfaces +configured. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit 10fa27f5829787c15e9ae59c45703328ca4e644f upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 34 ++++++++++++++-------------------- + 1 file changed, 14 insertions(+), 20 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index edc626f..065b7f0 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -2254,6 +2254,15 @@ int main(int argc, char *argv[]) + if (priv.master == NULL) { + priv.master = src; + } ++ if (i > 0) { ++ node = pmc_agent_add(&priv, i); ++ if (!node) ++ goto end; ++ } ++ /* map clock to pmc agent node */ ++ src->node = node; ++ pr_debug("pmc node index %d assigned to source interface %s", ++ node->index, src->device); + if (ha_enabled) { + src->ha_priority = config_get_int(cfg, src->device, "ha_priority"); + } +@@ -2286,35 +2295,22 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- i = 0; + LIST_FOREACH(src, &priv.clocks, list) { + + /* skip dst clock */ +- if (src == dst) { ++ if (src->state == PS_MASTER) + continue; +- } +- +- if (i > 0) { +- node = pmc_agent_add(&priv, i); +- if (!node) +- goto end; +- } + + /* uds local is formated '/var/run/phc2sys..' */ + snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d.%s", + getpid(), src->device); + +- if (init_pmc_node(cfg, node, uds_local, ++ if (init_pmc_node(cfg, src->node, uds_local, + phc2sys_recv_subscribed, &priv)) + goto end; + +- /* map clock to pmc agent node */ +- src->node = node; +- pr_debug("pmc node index %d source clock %s initialized", +- node->index, src->device); +- + while (is_running()) { +- r = run_pmc_wait_sync(node, 1000); ++ r = run_pmc_wait_sync(src->node, 1000); + if (r < 0) + goto end; + if (r > 0) +@@ -2324,7 +2320,7 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = pmc_agent_query_utc_offset(node, 1000); ++ r = pmc_agent_query_utc_offset(src->node, 1000); + if (r) { + pr_err("failed to get UTC offset"); + goto end; +@@ -2334,10 +2330,8 @@ int main(int argc, char *argv[]) + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || + src->clkid == CLOCK_INVALID) { +- pmc_agent_disable(node); ++ pmc_agent_disable(src->node); + } +- +- ++i; + } + + if (ha_enabled && !priv.forced_source_clock) { +-- +2.25.1 + diff --git a/SPECS/linuxptp/linuxptp-zerolength.patch b/SPECS/linuxptp/linuxptp-zerolength.patch new file mode 100644 index 00000000000..0ab5ed46020 --- /dev/null +++ b/SPECS/linuxptp/linuxptp-zerolength.patch @@ -0,0 +1,37 @@ +commit 9633ab52460f58c92c6daa35e9d24e4ce9c5ab1c +Author: Miroslav Lichvar +Date: Tue Feb 23 11:01:43 2021 +0100 + + sk: Don't return error for zero-length messages. + + The recvmsg() call can return zero for a zero-length UDP message, which + should be handled as a bad message and not a fault of the port. This was + addressed in commit 6b61ba29c78e ("Avoid fault when receiving zero + length packets"), but later regressed in commit a6e0b83bd503 + ("sk: Convey transmit path errors to the caller."). + + Signed-off-by: Miroslav Lichvar + Fixes: a6e0b83bd503 ("sk: Convey transmit path errors to the caller.") + +diff --git a/sk.c b/sk.c +index c9ef4d2..8be0708 100644 +--- a/sk.c ++++ b/sk.c +@@ -391,7 +391,7 @@ int sk_receive(int fd, void *buf, int buflen, + + if (!ts) { + memset(&hwts->ts, 0, sizeof(hwts->ts)); +- return cnt < 1 ? -errno : cnt; ++ return cnt < 0 ? -errno : cnt; + } + + switch (hwts->type) { +@@ -407,7 +407,7 @@ int sk_receive(int fd, void *buf, int buflen, + hwts->ts = timespec_to_tmv(ts[1]); + break; + } +- return cnt < 1 ? -errno : cnt; ++ return cnt < 0 ? -errno : cnt; + } + + int sk_set_priority(int fd, int family, uint8_t dscp) diff --git a/SPECS/linuxptp/linuxptp.signatures.json b/SPECS/linuxptp/linuxptp.signatures.json new file mode 100644 index 00000000000..80f77173f17 --- /dev/null +++ b/SPECS/linuxptp/linuxptp.signatures.json @@ -0,0 +1,12 @@ +{ + "Signatures": { + "clknetsim-9ed48d.tar.gz": "4e464ddb20c6436e8dfbc82305ae5187831a4b6b60b89dcdc92f59a0d3539496", + "linuxptp-3.1.1.tgz": "94d6855f9b7f2d8e9b0ca6d384e3fae6226ce6fc012dbad02608bdef3be1c0d9", + "linuxptp-testsuite-ff37e2.tar.gz": "038b1bb07ce5f03b3207bbfabe6f85d2cbbb16ef54b121a35749ca7e01d54648", + "phc2sys.service": "4bab3fe8ba6b801d6092d820c4fa4514973f441af5967f4c597ecd60d863c752", + "ptp4l.conf": "c3ae7d8d845619f41f3743da9e46ea6f690d218383f127e0bf2b64de2e69c283", + "ptp4l.service": "2d85b55077bb99091ddf66917b86044f75fe31584b647a7df06994eee5ecf6bd", + "timemaster.conf": "068104a097f468aaeb12bcf67eab2705736665a5546cda29be62be3c4593ddd5", + "timemaster.service": "01af35467d2400f12e7c95df94e069bba89ad06c048b6f346fc26676db2e6b42" + } +} \ No newline at end of file diff --git a/SPECS-EXTENDED/linuxptp/linuxptp.spec b/SPECS/linuxptp/linuxptp.spec similarity index 73% rename from SPECS-EXTENDED/linuxptp/linuxptp.spec rename to SPECS/linuxptp/linuxptp.spec index 50cf173b167..98d4013219e 100644 --- a/SPECS-EXTENDED/linuxptp/linuxptp.spec +++ b/SPECS/linuxptp/linuxptp.spec @@ -1,32 +1,38 @@ Vendor: Microsoft Corporation Distribution: Mariner -%global gitfullver e0580929f451e685d92cd10d80b76f39e9b09a97 -%global gitver %(c=%{gitfullver}; echo ${c:0:6}) %global _hardened_build 1 -%global testsuite_ver a7f6e1 -%global clknetsim_ver 79ffe4 +%global testsuite_ver ff37e2 +%global clknetsim_ver 9ed48d Name: linuxptp -Version: 2.0 -Release: 8%{?dist} +Version: 3.1.1 +Release: 1%{?dist} Summary: PTP implementation for Linux License: GPLv2+ URL: http://linuxptp.sourceforge.net/ -#Source0: https://downloads.sourceforge.net/%{name}/%{name}-%{version}.tgz -Source0: https://github.com/richardcochran/%{name}/archive/%{gitver}/%{name}-%{gitver}.tar.gz +Source0: https://sourceforge.net/projects/%{name}/files/v3.1/%{name}-%{version}.tgz Source1: phc2sys.service Source2: ptp4l.service Source3: timemaster.service Source4: timemaster.conf +Source5: ptp4l.conf # external test suite -Source10: https://github.com/mlichvar/linuxptp-testsuite/archive/%{testsuite_ver}/linuxptp-testsuite-%{testsuite_ver}.tar.gz +Source10: linuxptp-testsuite-%{testsuite_ver}.tar.gz # simulator for test suite -Source11: https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/clknetsim-%{clknetsim_ver}.tar.gz +Source11: clknetsim-%{clknetsim_ver}.tar.gz -BuildRequires: gcc gcc-c++ systemd -BuildRequires: net-snmp-devel +# fix handling of zero-length messages +Patch0: linuxptp-zerolength.patch +# revert phc2sys options needed by the older version of test suite +Patch1: clknetsim-phc2sys.patch + +# The following patch is a combination of multiple patches to enable HA in linuxptp +# https://review.opendev.org/c/starlingx/integ/+/891638 +Patch2: enable-ha.patch + +BuildRequires: gcc gcc-c++ make systemd %{?systemd_requires} @@ -39,11 +45,18 @@ Supporting legacy APIs and other platforms is not a goal. %prep %setup -q -a 10 -a 11 -n %{name}-%{!?gitfullver:%{version}}%{?gitfullver} +%patch0 -p1 -b .zerolength mv linuxptp-testsuite-%{testsuite_ver}* testsuite mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim +pushd testsuite/clknetsim +%patch1 -p1 -R -b .phc2sys +popd + +%patch2 -p1 -b .pre-ha + %build -make %{?_smp_mflags} \ +%{make_build} \ EXTRA_CFLAGS="$RPM_OPT_FLAGS" \ EXTRA_LDFLAGS="$RPM_LD_FLAGS" @@ -51,11 +64,10 @@ make %{?_smp_mflags} \ %makeinstall mkdir -p $RPM_BUILD_ROOT{%{_sysconfdir}/sysconfig,%{_unitdir},%{_mandir}/man5} -install -m 644 -p configs/default.cfg $RPM_BUILD_ROOT%{_sysconfdir}/ptp4l.conf install -m 644 -p %{SOURCE1} %{SOURCE2} %{SOURCE3} $RPM_BUILD_ROOT%{_unitdir} -install -m 644 -p %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir} +install -m 644 -p %{SOURCE4} %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir} -echo 'OPTIONS="-f /etc/ptp4l.conf -i eth0"' > \ +echo 'OPTIONS="-f /etc/ptp4l.conf"' > \ $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/ptp4l echo 'OPTIONS="-a -r"' > $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/phc2sys @@ -66,7 +78,7 @@ echo '.so man8/timemaster.8' > $RPM_BUILD_ROOT%{_mandir}/man5/timemaster.conf.5 cd testsuite # set random seed to get deterministic results export CLKNETSIM_RANDOM_SEED=26743 -make %{?_smp_mflags} -C clknetsim +%{make_build} -C clknetsim PATH=..:$PATH ./run %post @@ -93,15 +105,53 @@ PATH=..:$PATH ./run %{_sbindir}/phc_ctl %{_sbindir}/pmc %{_sbindir}/ptp4l -%{_sbindir}/snmp4lptp %{_sbindir}/timemaster +%{_sbindir}/ts2phc %{_mandir}/man5/*.5* %{_mandir}/man8/*.8* %changelog -* Thu Oct 14 2021 Pawel Winogrodzki - 2.0-8 -- Initial CBL-Mariner import from Fedora 32 (license: MIT). -- Converting the 'Release' tag to the '[number].[distribution]' format. +* Thu Nov 16 2023 Harshit Gupta - 3.1.1-1 +- Initial CBL-Mariner import from Fedora 37 (license: MIT). +- License Verified. +- Upstream linuxptp 3.1.1-6 has been imported into Azure Linux with package 3.1.1-1 +- Remove SELinux policy + +* Wed Jan 11 2023 Miroslav Lichvar 3.1.1-6 +- update selinux policy (#2159919) + +* Thu Jul 21 2022 Fedora Release Engineering - 3.1.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Tue Apr 26 2022 Miroslav Lichvar 3.1.1-4 +- fix tests on ppc64le (#2046706) + +* Thu Jan 20 2022 Fedora Release Engineering - 3.1.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Thu Jul 22 2021 Miroslav Lichvar 3.1.1-2 +- package selinux policy + +* Wed Jul 07 2021 Miroslav Lichvar 3.1.1-1 +- update to 3.1.1 (CVE-2021-3570, CVE-2021-3571) + +* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek - 3.1-4 +- Rebuilt for updated systemd-rpm-macros + See https://pagure.io/fesco/issue/2583. + +* Thu Feb 25 2021 Miroslav Lichvar 3.1-3 +- fix handling of zero-length messages +- minimize default configuration +- remove obsolete build requirement + +* Tue Jan 26 2021 Fedora Release Engineering - 3.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Sep 29 2020 Miroslav Lichvar 3.1-1 +- update to 3.1 + +* Mon Jul 27 2020 Miroslav Lichvar 3.0-1 +- update to 3.0 * Mon Feb 03 2020 Miroslav Lichvar 2.0-7.20191225gite05809 - update to 20191225gite05809 diff --git a/SPECS-EXTENDED/linuxptp/phc2sys.service b/SPECS/linuxptp/phc2sys.service similarity index 100% rename from SPECS-EXTENDED/linuxptp/phc2sys.service rename to SPECS/linuxptp/phc2sys.service diff --git a/SPECS/linuxptp/ptp4l.conf b/SPECS/linuxptp/ptp4l.conf new file mode 100644 index 00000000000..4b4b1db475b --- /dev/null +++ b/SPECS/linuxptp/ptp4l.conf @@ -0,0 +1,14 @@ +# For more information about this file, see the ptp4l(8) man page. +# Examples are available in /usr/share/doc/linuxptp/configs. + +[global] +domainNumber 0 +slaveOnly 1 +time_stamping hardware +tx_timestamp_timeout 1 +logging_level 6 +summary_interval 0 + +[eth0] +network_transport UDPv4 +hybrid_e2e 0 diff --git a/SPECS-EXTENDED/linuxptp/ptp4l.service b/SPECS/linuxptp/ptp4l.service similarity index 100% rename from SPECS-EXTENDED/linuxptp/ptp4l.service rename to SPECS/linuxptp/ptp4l.service diff --git a/SPECS-EXTENDED/linuxptp/timemaster.conf b/SPECS/linuxptp/timemaster.conf similarity index 78% rename from SPECS-EXTENDED/linuxptp/timemaster.conf rename to SPECS/linuxptp/timemaster.conf index fd8e77e0a48..14762099e07 100644 --- a/SPECS-EXTENDED/linuxptp/timemaster.conf +++ b/SPECS/linuxptp/timemaster.conf @@ -14,18 +14,11 @@ ntp_program chronyd [chrony.conf] include /etc/chrony.conf -[ntp.conf] -includefile /etc/ntp.conf - [ptp4l.conf] [chronyd] path /usr/sbin/chronyd -[ntpd] -path /usr/sbin/ntpd -options -u ntp:ntp -g - [phc2sys] path /usr/sbin/phc2sys diff --git a/SPECS-EXTENDED/linuxptp/timemaster.service b/SPECS/linuxptp/timemaster.service similarity index 100% rename from SPECS-EXTENDED/linuxptp/timemaster.service rename to SPECS/linuxptp/timemaster.service diff --git a/SPECS/local-path-provisioner/local-path-provisioner.spec b/SPECS/local-path-provisioner/local-path-provisioner.spec index 3c2169dcbc4..a106b0a32b5 100644 --- a/SPECS/local-path-provisioner/local-path-provisioner.spec +++ b/SPECS/local-path-provisioner/local-path-provisioner.spec @@ -31,7 +31,7 @@ install local-path-provisioner %{buildroot}%{_bindir}/local-path-provisioner %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.0.21-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.0.21-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/mariner-release/mariner-release.spec b/SPECS/mariner-release/mariner-release.spec index 137082b2705..2c62f773810 100644 --- a/SPECS/mariner-release/mariner-release.spec +++ b/SPECS/mariner-release/mariner-release.spec @@ -1,7 +1,7 @@ Summary: CBL-Mariner release files Name: mariner-release Version: 2.0 -Release: 54%{?dist} +Release: 55%{?dist} License: MIT Vendor: Microsoft Corporation Distribution: Mariner @@ -62,6 +62,9 @@ EOF %config(noreplace) %{_sysconfdir}/issue.net %changelog +* Thu Nov 30 2023 CBL-Mariner Servicing Account - 2.0-55 +- Bump release for December 2023 Release + * Sat Nov 11 2023 CBL-Mariner Servicing Account - 2.0-54 - Bump release for November 2023 Release diff --git a/SPECS/mariner-repos/mariner-cloud-native-preview.repo b/SPECS/mariner-repos/mariner-cloud-native-preview.repo new file mode 100644 index 00000000000..bac5b6096f9 --- /dev/null +++ b/SPECS/mariner-repos/mariner-cloud-native-preview.repo @@ -0,0 +1,29 @@ +[mariner-official-cloud-native-preview] +name=CBL-Mariner Official Cloud Native Preview $releasever $basearch +baseurl=https://packages.microsoft.com/cbl-mariner/$releasever/preview/cloud-native/$basearch +gpgkey=file:///etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY file:///etc/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY +gpgcheck=1 +repo_gpgcheck=1 +enabled=1 +skip_if_unavailable=True +sslverify=1 + +[mariner-official-cloud-native-preview-debuginfo] +name=CBL-Mariner Official Cloud Native Preview $releasever $basearch Debuginfo +baseurl=https://packages.microsoft.com/cbl-mariner/$releasever/preview/cloud-native/debuginfo/$basearch +gpgkey=file:///etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY file:///etc/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY +gpgcheck=1 +repo_gpgcheck=1 +enabled=0 +skip_if_unavailable=True +sslverify=1 + +[mariner-official-cloud-native-preview-source] +name=CBL-Mariner Official Cloud Native Preview $releasever Source +baseurl=https://packages.microsoft.com/cbl-mariner/$releasever/preview/cloud-native/srpms +gpgkey=file:///etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY file:///etc/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY +gpgcheck=1 +repo_gpgcheck=1 +enabled=0 +skip_if_unavailable=True +sslverify=1 diff --git a/SPECS/mariner-repos/mariner-cloud-native.repo b/SPECS/mariner-repos/mariner-cloud-native.repo new file mode 100644 index 00000000000..67f840e790e --- /dev/null +++ b/SPECS/mariner-repos/mariner-cloud-native.repo @@ -0,0 +1,29 @@ +[mariner-official-cloud-native] +name=CBL-Mariner Official Cloud Native $releasever $basearch +baseurl=https://packages.microsoft.com/cbl-mariner/$releasever/prod/cloud-native/$basearch +gpgkey=file:///etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY file:///etc/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY +gpgcheck=1 +repo_gpgcheck=1 +enabled=1 +skip_if_unavailable=True +sslverify=1 + +[mariner-official-cloud-native-debuginfo] +name=CBL-Mariner Official Cloud Native $releasever $basearch Debuginfo +baseurl=https://packages.microsoft.com/cbl-mariner/$releasever/prod/cloud-native/debuginfo/$basearch +gpgkey=file:///etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY file:///etc/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY +gpgcheck=1 +repo_gpgcheck=1 +enabled=0 +skip_if_unavailable=True +sslverify=1 + +[mariner-official-cloud-native-source] +name=CBL-Mariner Official Cloud Native $releasever Source +baseurl=https://packages.microsoft.com/cbl-mariner/$releasever/prod/cloud-native/srpms +gpgkey=file:///etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY file:///etc/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY +gpgcheck=1 +repo_gpgcheck=1 +enabled=0 +skip_if_unavailable=True +sslverify=1 diff --git a/SPECS/mariner-repos/mariner-repos.signatures.json b/SPECS/mariner-repos/mariner-repos.signatures.json index 6ba07b82f38..7bf32ec7683 100644 --- a/SPECS/mariner-repos/mariner-repos.signatures.json +++ b/SPECS/mariner-repos/mariner-repos.signatures.json @@ -2,6 +2,8 @@ "Signatures": { "MICROSOFT-METADATA-GPG-KEY": "1824ecffeda90cfe4178a99bddde450f09fd40e8faf4f0124fba16ea79998c4c", "MICROSOFT-RPM-GPG-KEY": "1092f37ec429e58bf9c7f898df17c3c32eb2ce3c4c037afb8ffe2d2b42e16e89", + "mariner-cloud-native.repo": "62adec4e53f3a3b9917861422fd6cc6dd83564369c9aaa67452dd4d9470961b6", + "mariner-cloud-native-preview.repo": "acdb2c35e00cc596df3c16cd100707d6596080cae3302293801d030cfb72a334", "mariner-debuginfo-preview.repo": "1fea397ab17fc801351174626639c4a5e38df7393053bc419f192d49530d9f92", "mariner-debuginfo.repo": "5502de8668a12802c46f45b51e47e54472e371b02984c641d757a3905322bc7d", "mariner-extended-debuginfo-preview.repo": "2897ae5134cddb357a17348308a02f3f3ef7032109687fa9dbbfa017abc87933", diff --git a/SPECS/mariner-repos/mariner-repos.spec b/SPECS/mariner-repos/mariner-repos.spec index a3167a36bc9..c4fbabb9443 100644 --- a/SPECS/mariner-repos/mariner-repos.spec +++ b/SPECS/mariner-repos/mariner-repos.spec @@ -1,7 +1,7 @@ Summary: CBL-Mariner repo files, gpg keys Name: mariner-repos Version: 2.0 -Release: 8%{?dist} +Release: 9%{?dist} License: MIT Vendor: Microsoft Corporation Distribution: Mariner @@ -21,6 +21,8 @@ Source10: mariner-official-base.repo Source11: mariner-official-preview.repo Source12: mariner-extended-debuginfo.repo Source13: mariner-extended-debuginfo-preview.repo +Source14: mariner-cloud-native.repo +Source15: mariner-cloud-native-preview.repo Requires: %{name}-shared = %{version}-%{release} @@ -29,6 +31,22 @@ BuildArch: noarch %description CBL-Mariner repo files and gpg keys +%package cloud-native +Summary: CBL-Mariner cloud-native repo file. +Group: System Environment/Base +Requires: %{name}-shared = %{version}-%{release} + +%description cloud-native +%{summary} + +%package cloud-native-preview +Summary: CBL-Mariner cloud-native preview repo file. +Group: System Environment/Base +Requires: %{name}-shared = %{version}-%{release} + +%description cloud-native-preview +%{summary} + %package debug Summary: CBL-Mariner Debuginfo repo file. Group: System Environment/Base @@ -143,6 +161,8 @@ install -m 644 %{SOURCE10} $REPO_DIRECTORY install -m 644 %{SOURCE11} $REPO_DIRECTORY install -m 644 %{SOURCE12} $REPO_DIRECTORY install -m 644 %{SOURCE13} $REPO_DIRECTORY +install -m 644 %{SOURCE14} $REPO_DIRECTORY +install -m 644 %{SOURCE15} $REPO_DIRECTORY export RPM_GPG_DIRECTORY="%{buildroot}%{_sysconfdir}/pki/rpm-gpg" @@ -164,6 +184,14 @@ gpg --batch --yes --delete-keys 2BC94FFF7015A5F28F1537AD0CD9FED33135CE90 %defattr(-,root,root,-) %config(noreplace) %{_sysconfdir}/yum.repos.d/mariner-official-base.repo +%files cloud-native +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/yum.repos.d/mariner-cloud-native.repo + +%files cloud-native-preview +%defattr(-,root,root,-) +%config(noreplace) %{_sysconfdir}/yum.repos.d/mariner-cloud-native-preview.repo + %files debug %defattr(-,root,root,-) %config(noreplace) %{_sysconfdir}/yum.repos.d/mariner-debuginfo.repo @@ -214,6 +242,9 @@ gpg --batch --yes --delete-keys 2BC94FFF7015A5F28F1537AD0CD9FED33135CE90 %{_sysconfdir}/pki/rpm-gpg/MICROSOFT-METADATA-GPG-KEY %changelog +* Wed Nov 29 2023 Jon Slobodzian - 2.0-9 +- Add cloud native repos. + * Thu Jul 14 2022 Andrew Phelps - 2.0-8 - Add SRPM and Debuginfo repos to existing base, extended, and preview subpackages diff --git a/SPECS/moby-buildx/moby-buildx.spec b/SPECS/moby-buildx/moby-buildx.spec index e7d1103965f..82961c13a3e 100644 --- a/SPECS/moby-buildx/moby-buildx.spec +++ b/SPECS/moby-buildx/moby-buildx.spec @@ -43,7 +43,7 @@ cp -aT buildx "%{buildroot}/%{_libexecdir}/docker/cli-plugins/docker-buildx" %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.7.1-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.7.1-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/moby-cli/moby-cli.spec b/SPECS/moby-cli/moby-cli.spec index 49933fdbd36..f099652c64f 100644 --- a/SPECS/moby-cli/moby-cli.spec +++ b/SPECS/moby-cli/moby-cli.spec @@ -81,7 +81,7 @@ install -p -m 644 contrib/completion/fish/docker.fish %{buildroot}%{_datadir}/fi %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 20.10.25-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 20.10.25-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/moby-compose/moby-compose.spec b/SPECS/moby-compose/moby-compose.spec index 5f24ed68f55..21ff35240e3 100644 --- a/SPECS/moby-compose/moby-compose.spec +++ b/SPECS/moby-compose/moby-compose.spec @@ -45,7 +45,7 @@ install -D -m0755 bin/build/docker-compose %{buildroot}/%{_libexecdir}/docker/cl %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.17.2-6 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.17.2-5 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/moby-containerd-cc/moby-containerd-cc.spec b/SPECS/moby-containerd-cc/moby-containerd-cc.spec index 09a833e04d9..a80ce26cf3c 100644 --- a/SPECS/moby-containerd-cc/moby-containerd-cc.spec +++ b/SPECS/moby-containerd-cc/moby-containerd-cc.spec @@ -81,7 +81,7 @@ fi - Always add TargetLayerDigestLabel label to snapshots * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.7.1-6 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.7.1-5 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/moby-containerd/moby-containerd.spec b/SPECS/moby-containerd/moby-containerd.spec index bcd13fe9c4a..5c21d161447 100644 --- a/SPECS/moby-containerd/moby-containerd.spec +++ b/SPECS/moby-containerd/moby-containerd.spec @@ -94,7 +94,7 @@ fi - Precreate /opt/containerd/{bin,lib} to ensure correct SELinux labeling. * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.6.22-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.6.22-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/moby-engine/moby-engine.spec b/SPECS/moby-engine/moby-engine.spec index 3dd39bf3d91..5509d76f662 100644 --- a/SPECS/moby-engine/moby-engine.spec +++ b/SPECS/moby-engine/moby-engine.spec @@ -127,7 +127,7 @@ fi %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 20.10.25-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 20.10.25-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/moby-runc/moby-runc.spec b/SPECS/moby-runc/moby-runc.spec index 5b74f13d492..5a2df82425a 100644 --- a/SPECS/moby-runc/moby-runc.spec +++ b/SPECS/moby-runc/moby-runc.spec @@ -58,7 +58,7 @@ make install-man DESTDIR="%{buildroot}" PREFIX="%{_prefix}" %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.1.9-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.1.9-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/msft-golang/msft-golang.signatures.json b/SPECS/msft-golang/msft-golang.signatures.json index b0e414fbd4b..aa5b3bc111a 100644 --- a/SPECS/msft-golang/msft-golang.signatures.json +++ b/SPECS/msft-golang/msft-golang.signatures.json @@ -1,6 +1,7 @@ { "Signatures": { "go.20230802.5.src.tar.gz": "56b9e0e0c3c13ca95d5efa6de4e7d49a9d190eca77919beff99d33cd3fa74e95", + "go.20231107.4.src.tar.gz": "91f65dd825d40f7da7fb53e8713c4f25545feb49eb66f4e491bf5540bcec7287", "go1.4-bootstrap-20171003.tar.gz": "f4ff5b5eb3a3cae1c993723f3eab519c5bae18866b5e5f96fe1102f0cb5c3e52" } } \ No newline at end of file diff --git a/SPECS/msft-golang/msft-golang.spec b/SPECS/msft-golang/msft-golang.spec index b1d9828a33a..da89aee2aeb 100644 --- a/SPECS/msft-golang/msft-golang.spec +++ b/SPECS/msft-golang/msft-golang.spec @@ -1,3 +1,4 @@ +%global bootstrap_compiler_version 20230802.5 %global goroot %{_libdir}/golang %global gopath %{_datadir}/gocode %ifarch aarch64 @@ -12,15 +13,16 @@ %define __find_requires %{nil} Summary: Go Name: msft-golang -Version: 1.19.12 +Version: 1.20.11 Release: 1%{?dist} License: BSD Vendor: Microsoft Corporation Distribution: Mariner Group: System Environment/Security URL: https://github.com/microsoft/go -Source0: https://github.com/microsoft/go/releases/download/v1.19.12-1/go.20230802.5.src.tar.gz +Source0: https://github.com/microsoft/go/releases/download/v1.20.11-1/go.20231107.4.src.tar.gz Source1: https://dl.google.com/go/go1.4-bootstrap-20171003.tar.gz +Source2: https://github.com/microsoft/go/releases/download/v1.19.12-1/go.%{bootstrap_compiler_version}.src.tar.gz Patch0: go14_bootstrap_aarch64.patch Conflicts: go Conflicts: golang @@ -37,6 +39,12 @@ mv -v go go-bootstrap %setup -q -n go %build +# (go >= 1.20 bootstraps with go >= 1.17) +# This condition makes go compiler >= 1.20 build a 3 step process: +# - Build the bootstrap compiler 1.4 (bootstrap bits in c) +# - Use the 1.4 compiler to build %{bootstrap_compiler_version} +# - Use the %{bootstrap_compiler_version} compiler to build go >= 1.20 compiler + # Build go 1.4 bootstrap pushd %{_topdir}/BUILD/go-bootstrap/src CGO_ENABLED=0 ./make.bash @@ -44,6 +52,20 @@ popd mv -v %{_topdir}/BUILD/go-bootstrap %{_libdir}/golang export GOROOT=%{_libdir}/golang +# Use go1.4 bootstrap to compile go.%{bootstrap_compiler_version} (C bootstrap) +export GOROOT_BOOTSTRAP=%{_libdir}/golang +mkdir -p %{_topdir}/BUILD/go.%{bootstrap_compiler_version} +tar xf %{SOURCE2} -C %{_topdir}/BUILD/go.%{bootstrap_compiler_version} --strip-components=1 +pushd %{_topdir}/BUILD/go.%{bootstrap_compiler_version}/src +CGO_ENABLED=0 ./make.bash +popd + +# Nuke the older go 1.4 bootstrap +rm -rf %{_libdir}/golang + +# Make go.%{bootstrap_compiler_version} as the new bootstrapper (Go boostrap) +mv -v %{_topdir}/BUILD/go.%{bootstrap_compiler_version} %{_libdir}/golang + # Build current go version export GOHOSTOS=linux export GOHOSTARCH=%{gohostarch} @@ -115,6 +137,10 @@ fi %{_bindir}/* %changelog +* Wed Nov 22 2023 Andrew Phelps - 1.20.11-1 +- Upgrade to 1.20.11 +- Keep go 1.19.12 source to provide additional go boostrap + * Wed Aug 16 2023 Brian Fjeldstad - 1.19.12-1 - Upgrade to 1.19.12 to fix CVE-2023-39533 diff --git a/SPECS/multus/multus.spec b/SPECS/multus/multus.spec index b84fc604a2c..125dfeb1f80 100644 --- a/SPECS/multus/multus.spec +++ b/SPECS/multus/multus.spec @@ -76,7 +76,7 @@ install -D -m0644 deployments/multus-daemonset.yml %{buildroot}%{_datadir}/k8s-y - Upgrade to v4.0.2 * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.8-13 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.8-12 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/mysql/mysql.signatures.json b/SPECS/mysql/mysql.signatures.json index 52459f1afd7..f8b7bdb045c 100644 --- a/SPECS/mysql/mysql.signatures.json +++ b/SPECS/mysql/mysql.signatures.json @@ -1,5 +1,5 @@ { "Signatures": { - "mysql-boost-8.0.34.tar.gz": "0b881a19bcef732cd4dbbfc8dfeb84eff61f5dfe0d9788d015d699733e0adf1f" + "mysql-boost-8.0.35.tar.gz": "41253c3a99cefcf6d806040c6687692eb0c37b4c7aae5882417dfb9c5d3ce4ce" } } \ No newline at end of file diff --git a/SPECS/mysql/mysql.spec b/SPECS/mysql/mysql.spec index b75472f1564..d74d9319bf4 100644 --- a/SPECS/mysql/mysql.spec +++ b/SPECS/mysql/mysql.spec @@ -1,6 +1,6 @@ Summary: MySQL. Name: mysql -Version: 8.0.34 +Version: 8.0.35 Release: 1%{?dist} License: GPLv2 with exceptions AND LGPLv2 AND BSD Vendor: Microsoft Corporation @@ -83,6 +83,9 @@ make test %{_libdir}/pkgconfig/mysqlclient.pc %changelog +* Tue Nov 28 2023 CBL-Mariner Servicing Account - 8.0.35-1 +- Auto-upgrade to 8.0.35 - address CVE-2023-22078, CVE-2023-22068, CVE-2023-22084, CVE-2023-22070, CVE-2023-22092, CVE-2023-22079, CVE-2023-22032, CVE-2023-22103, CVE-2023-22112, CVE-2023-22059, CVE-2023-22114, CVE-2023-22097, CVE-2023-22064, CVE-2023-22066, etc. + * Wed Nov 1 2023 CBL-Mariner Servicing Account - 8.0.34-1 - Auto-upgrade to 8.0.34 - address CVE-2023-22053, CVE-2023-22054, CVE-2023-22056, CVE-2023-22058, CVE-2023-22065, CVE-2023-22110, CVE-2023-22111, CVE-2023-22113, CVE-2023-22115 diff --git a/SPECS/ncurses/ncurses.signatures.json b/SPECS/ncurses/ncurses.signatures.json index 5055a483009..79b42138de2 100644 --- a/SPECS/ncurses/ncurses.signatures.json +++ b/SPECS/ncurses/ncurses.signatures.json @@ -1,5 +1,5 @@ { "Signatures": { - "ncurses-6.4-20230408.tgz": "46030c04cfb60433db05631e8124640acff284b1d3b9c897ff661686d885e0e8" + "ncurses-6.4-20230423.tgz": "7d77f95eff470099cfb7cabd6d3b8766ee1c4bda33755dd5a4d73fcc85315a4f" } } \ No newline at end of file diff --git a/SPECS/ncurses/ncurses.spec b/SPECS/ncurses/ncurses.spec index 36212e6cd84..38301799bef 100644 --- a/SPECS/ncurses/ncurses.spec +++ b/SPECS/ncurses/ncurses.spec @@ -1,9 +1,9 @@ -%global patchlevel 20230408 +%global patchlevel 20230423 Summary: Libraries for terminal handling of character screens Name: ncurses Version: 6.4 -Release: 1%{?dist} +Release: 2%{?dist} License: MIT Vendor: Microsoft Corporation Distribution: Mariner @@ -233,6 +233,9 @@ xz NEWS %files term -f terms.term %changelog +* Thu Nov 16 2023 Tobias Brick - 6.4-2 +- Update to version 6.4-20230423 to fix crash in tmux + * Wed Apr 26 2023 Sindhu Karri - 6.4-1 - Update to version 6.4-20230408 to fix CVE-2023-29491 diff --git a/SPECS/nmi/nmi.spec b/SPECS/nmi/nmi.spec index 5267a33783e..3be7886aac5 100644 --- a/SPECS/nmi/nmi.spec +++ b/SPECS/nmi/nmi.spec @@ -64,7 +64,7 @@ popd %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.8.7-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.8.7-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/node-problem-detector/node-problem-detector.spec b/SPECS/node-problem-detector/node-problem-detector.spec index 80607103fc0..d1b277ca80b 100644 --- a/SPECS/node-problem-detector/node-problem-detector.spec +++ b/SPECS/node-problem-detector/node-problem-detector.spec @@ -65,7 +65,7 @@ make test %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.8.10-17 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.8.10-16 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/nvidia-container-toolkit/nvidia-container-toolkit.spec b/SPECS/nvidia-container-toolkit/nvidia-container-toolkit.spec index 472d2d013f7..bbbf3415fcd 100644 --- a/SPECS/nvidia-container-toolkit/nvidia-container-toolkit.spec +++ b/SPECS/nvidia-container-toolkit/nvidia-container-toolkit.spec @@ -95,7 +95,7 @@ rm -f %{_bindir}/nvidia-container-toolkit %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.13.5-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.13.5-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/opa/opa.spec b/SPECS/opa/opa.spec index 91e75499ce9..727b2c96dd1 100644 --- a/SPECS/opa/opa.spec +++ b/SPECS/opa/opa.spec @@ -54,7 +54,7 @@ install -D -p -m 0644 man/* %{buildroot}%{_mandir}/man1/ %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.50.2-7 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.50.2-6 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/opensc/CVE-2023-4535.patch b/SPECS/opensc/CVE-2023-4535.patch new file mode 100644 index 00000000000..0202220683e --- /dev/null +++ b/SPECS/opensc/CVE-2023-4535.patch @@ -0,0 +1,53 @@ +diff -urN a/opensc-0.23.0/src/libopensc/card-myeid.c b/opensc-0.23.0/src/libopensc/card-myeid.c +--- opensc-0.23.0/src/libopensc/card-myeid.c 2022-11-29 00:36:01.000000000 -0800 ++++ opensc-0.23.0/src/libopensc/card-myeid.c 2023-11-17 14:29:07.476071900 -0800 +@@ -1966,15 +1966,20 @@ + sc_log(ctx, "Found padding byte %02x", pad_byte); + if (pad_byte == 0 || pad_byte > block_size) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); +- sdata = priv->sym_plain_buffer + block_size - pad_byte; ++ sdata = priv->sym_plain_buffer + block_size; + for (i = 0; i < pad_byte; i++) +- if (sdata[i] != pad_byte) ++ if (*(--sdata) != pad_byte) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_PADDING); + return_len = block_size - pad_byte; + } +- *outlen = return_len; ++ /* application can request buffer size or actual buffer size is too small */ ++ if (out == NULL) { ++ *outlen = return_len; ++ LOG_FUNC_RETURN(ctx, SC_SUCCESS); ++ } + if (return_len > *outlen) + LOG_FUNC_RETURN(ctx, SC_ERROR_BUFFER_TOO_SMALL); ++ *outlen = return_len; + memcpy(out, priv->sym_plain_buffer, return_len); + sc_log(ctx, "C_DecryptFinal %zu bytes", *outlen); + return SC_SUCCESS; +@@ -2042,10 +2047,11 @@ + priv->sym_crypt_buffer_len = 0; + rest_len = 0; + } +- memcpy(sdata, data, apdu_datalen); +- data += apdu_datalen; +- datalen -= apdu_datalen; +- ++ if (data) { ++ memcpy(sdata, data, apdu_datalen); ++ data += apdu_datalen; ++ datalen -= apdu_datalen; ++ } + r = sc_transmit_apdu(card, &apdu); + LOG_TEST_RET(ctx, r, "APDU transmit failed"); + r = sc_check_sw(card, apdu.sw1, apdu.sw2); +@@ -2084,7 +2090,8 @@ + /* save rest of data for next run */ + priv->sym_crypt_buffer_len = datalen; + sc_log(ctx, "rest data len = %zu", datalen); +- memcpy(priv->sym_crypt_buffer, data, datalen); ++ if (data) ++ memcpy(priv->sym_crypt_buffer, data, datalen); + sc_log(ctx, "return data len = %zu", return_len); + *outlen = return_len; + return SC_SUCCESS; diff --git a/SPECS/opensc/opensc.spec b/SPECS/opensc/opensc.spec index 6ba12cd4e84..e528a47c506 100644 --- a/SPECS/opensc/opensc.spec +++ b/SPECS/opensc/opensc.spec @@ -3,7 +3,7 @@ Summary: Smart card library and applications Name: opensc Version: 0.23.0 -Release: 1%{?dist} +Release: 2%{?dist} License: LGPLv2+ Vendor: Microsoft Corporation Distribution: Mariner @@ -11,6 +11,7 @@ URL: https://github.com/OpenSC/OpenSC Source0: https://github.com/OpenSC/OpenSC/releases/download/%{version}/%{name}-%{version}.tar.gz Source1: opensc.module Patch1: opensc-0.23.0-pinpad.patch +Patch2: CVE-2023-4535.patch BuildRequires: autoconf BuildRequires: automake BuildRequires: bash-completion @@ -140,6 +141,9 @@ rm %{buildroot}%{_mandir}/man1/opensc-notify.1* %{_mandir}/man5/* %changelog +* Fri Nov 17 2023 Riken Maharjan - 0.23.0-2 +- Fix CVE-2023-4535 + * Thu Aug 31 2023 Henry Beberman - 0.23.0-1 - Upgrade to 0.23.0 to fix CVE-2021-34193 - Update pinpad.patch to be compatible with 0.23.0 diff --git a/SPECS/packer/packer.spec b/SPECS/packer/packer.spec index 8c54e372250..1bc4640b8ce 100644 --- a/SPECS/packer/packer.spec +++ b/SPECS/packer/packer.spec @@ -61,7 +61,7 @@ go test -mod=vendor %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.8.1-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.8.1-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/prometheus-adapter/prometheus-adapter.spec b/SPECS/prometheus-adapter/prometheus-adapter.spec index 7868243db27..2d67974b475 100644 --- a/SPECS/prometheus-adapter/prometheus-adapter.spec +++ b/SPECS/prometheus-adapter/prometheus-adapter.spec @@ -42,7 +42,7 @@ make test %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.10.0-10 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.10.0-9 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/prometheus-node-exporter/prometheus-node-exporter.spec b/SPECS/prometheus-node-exporter/prometheus-node-exporter.spec index f3971da4075..b1c4c64baf2 100644 --- a/SPECS/prometheus-node-exporter/prometheus-node-exporter.spec +++ b/SPECS/prometheus-node-exporter/prometheus-node-exporter.spec @@ -108,7 +108,7 @@ getent passwd 'prometheus' >/dev/null || useradd -r -g 'prometheus' -d '%{_share %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.3.1-21 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.3.1-20 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/prometheus-process-exporter/prometheus-process-exporter.spec b/SPECS/prometheus-process-exporter/prometheus-process-exporter.spec index 97f6adc2563..610e549de3c 100644 --- a/SPECS/prometheus-process-exporter/prometheus-process-exporter.spec +++ b/SPECS/prometheus-process-exporter/prometheus-process-exporter.spec @@ -98,7 +98,7 @@ getent passwd 'prometheus' >/dev/null || useradd -r -g 'prometheus' -d '%{_share %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 0.7.10-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 0.7.10-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/prometheus/prometheus.spec b/SPECS/prometheus/prometheus.spec index c600ccb0e0f..2afd56a924a 100644 --- a/SPECS/prometheus/prometheus.spec +++ b/SPECS/prometheus/prometheus.spec @@ -132,7 +132,7 @@ fi %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 2.37.0-11 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 2.37.0-10 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/python-junit-xml/python-junit-xml.signatures.json b/SPECS/python-junit-xml/python-junit-xml.signatures.json new file mode 100644 index 00000000000..c8324e4f030 --- /dev/null +++ b/SPECS/python-junit-xml/python-junit-xml.signatures.json @@ -0,0 +1,5 @@ +{ + "Signatures": { + "python-junit-xml-1.9.tar.gz": "fc3c243666574ce2db66a8502793d3d420556b7ff5123b63a63a199287d753d8" + } +} \ No newline at end of file diff --git a/SPECS/python-junit-xml/python-junit-xml.spec b/SPECS/python-junit-xml/python-junit-xml.spec new file mode 100644 index 00000000000..7df1d6bf00f --- /dev/null +++ b/SPECS/python-junit-xml/python-junit-xml.spec @@ -0,0 +1,174 @@ +%global commit 856414648cbab3f64e69b856bc25cea8b9aa0377 +%global shortcommit %(c=%{commit}; echo ${c:0:7}) + +%global pypi_name junit-xml + +%global common_description %{expand: +A Python module for creating JUnit XML test result documents that can be read +by tools such as Jenkins or Bamboo. If you are ever working with test tool or +test suite written in Python and want to take advantage of Jenkins’ or Bamboo’s +pretty graphs and test reporting capabilities, this module will let you +generate the XML test reports.} + +Summary: Python module for creating JUnit XML test result documents +Name: python-%{pypi_name} +Version: 1.9 +Release: 1%{?dist} +License: MIT +Vendor: Microsoft Corporation +Distribution: Mariner +URL: https://github.com/kyrus/python-junit-xml +# No GitHub release, have to use commit ID. +# Pypi's tarball is missing the tests. +Source0: https://github.com/kyrus/python-junit-xml/tarball/%{commit}#/%{name}-%{version}.tar.gz +BuildArch: noarch + +BuildRequires: python3-devel +BuildRequires: python3-pip +BuildRequires: python3-six +BuildRequires: python3-wheel + +%description %{common_description} + +%package -n python3-%{pypi_name} +Summary: %{summary} +Provides: python3-junit_xml = %{version}-%{release} + +%description -n python3-%{pypi_name} %{common_description} + +%prep +%autosetup -n kyrus-%{name}-%{shortcommit} -p1 + +%generate_buildrequires +%pyproject_buildrequires -t + +%build +%pyproject_wheel + +%install +%pyproject_install +%pyproject_save_files junit_xml + +%check +# Freezing 'pytest' to a known working version as updates tend to introduce regressions. +pip3 install pytest==7.4.3 tox tox-current-env virtualenv +%tox + +%files -n python3-%{pypi_name} -f %{pyproject_files} +%license LICENSE.txt +%doc README.rst + +%changelog +* Fri Nov 17 2023 Pawel Winogrodzki - 1.9-1 +- Initial CBL-Mariner import from Fedora 38 (license: MIT). +- License verified. + +* Fri Jul 22 2022 Fedora Release Engineering 1.9^20200222gitba89b41-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Mon Jun 13 2022 Python Maint 1.9^20200222gitba89b41-2 +- Rebuilt for Python 3.11 + +* Wed Apr 20 2022 Benjamin A. Beasley 1.9^20200222gitba89b41-1 +- Drop “forge” macros and use “modern” snapshot versioning + +* Fri Jan 21 2022 Fedora Release Engineering 1.9-18 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Mon Sep 13 2021 Benjamin A. Beasley 1.9-17 +- Let pyproject-rpm-macros handle the license file + +* Sun Sep 12 2021 Benjamin A. Beasley 1.9-16 +- Drop BR on pyproject-rpm-macros, now implied by python3-devel + +* Sun Sep 12 2021 Benjamin A. Beasley 1.9-15 +- Add Python provides for junit-xml name + +* Sun Sep 12 2021 Benjamin A. Beasley 1.9-14 +- Drop BR on pyproject-rpm-macros, now implied by python3-devel + +* Tue Jul 27 2021 Benjamin A. Beasley 1.9-13 +- Move %%generate_buildrequires after %%prep to make the spec file easier + to follow + +* Fri Jul 23 2021 Fedora Release Engineering 1.9-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Fri Jul 09 2021 Benjamin A. Beasley - 1.9-9 +- Merged PR#1; drop patch for RHBZ#1935212 + +* Fri Jun 04 2021 Python Maint - 1.9-8 +- Rebuilt for Python 3.10 + +* Wed May 12 2021 Benjamin A. Beasley - 1.9-7 +- Move “forge” macros to the top of the spec file + +* Tue Mar 16 2021 Benjamin A. Beasley - 1.9-6 +- Drop python3dist(setuptools) BR, redundant with %%pyproject_buildrequires + +* Mon Mar 08 2021 Benjamin A. Beasley - 1.9-5 +- Replace ' with ’ in description + +* Thu Feb 11 2021 Benjamin A. Beasley - 1.9-4 +- Rebuilt for pyproject-rpm-macros-0-38 to fix unowned nested __pycache__ + directories (RHBZ#1925963) + +* Wed Jan 27 2021 Fedora Release Engineering - 1.9-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Thu Jan 14 2021 Benjamin A. Beasley - 1.9-2 +- Drop conditionals for Fedora 32 + +* Thu Jan 14 2021 Benjamin A. Beasley - 1.9-1 +- Update to 1.9 (RHBZ#1486729) + +* Thu Jan 14 2021 Benjamin A. Beasley - 1.8-13 +- Drop EPEL compatibility and unnecessary macros; EPEL7/8 will be supported by + a forked spec file instead of conditional macros +- Use pyproject-rpm-macros, including generated BR’s +- Fix banned %%{python3_sitelib}/* in %%files +- Use %%pytest, %%pypi_source macros +- Update summary and description from upstream + +* Wed Jul 29 2020 Fedora Release Engineering - 1.8-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue May 26 2020 Miro Hrončok - 1.8-11 +- Rebuilt for Python 3.9 + +* Thu Jan 30 2020 Fedora Release Engineering - 1.8-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Wed Sep 11 2019 Adrian Reber - 1.8-9 +- Apply adapted upstream fix for test failures + +* Mon Aug 19 2019 Miro Hrončok - 1.8-8 +- Rebuilt for Python 3.8 + +* Fri Jul 26 2019 Fedora Release Engineering - 1.8-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Sat Feb 02 2019 Fedora Release Engineering - 1.8-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Wed Oct 17 2018 Zbigniew Jędrzejewski-Szmek - 1.8-5 +- Subpackage python2-junit_xml has been removed + See https://fedoraproject.org/wiki/Changes/Mass_Python_2_Package_Removal + +* Sat Jul 14 2018 Fedora Release Engineering - 1.8-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Tue Jun 19 2018 Miro Hrončok - 1.8-3 +- Rebuilt for Python 3.7 + +* Fri Feb 09 2018 Fedora Release Engineering - 1.8-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Wed Aug 30 2017 James Hogarth - 1.8-1 +- update to 1.8 + +* Thu Jul 27 2017 Fedora Release Engineering - 1.7-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Feb 15 2017 James Hogarth - 1.7-1 +- Initial package diff --git a/SPECS/python-werkzeug/0001-enable-tests-in-rpm-env.patch b/SPECS/python-werkzeug/0001-enable-tests-in-rpm-env.patch new file mode 100644 index 00000000000..52ab41dc192 --- /dev/null +++ b/SPECS/python-werkzeug/0001-enable-tests-in-rpm-env.patch @@ -0,0 +1,32 @@ +From 7bd6337181f1964d3a0203be2faf49f335984402 Mon Sep 17 00:00:00 2001 +From: Nick Samson +Date: Mon, 13 Nov 2023 17:02:11 -0800 +Subject: [PATCH] Added pythonpath fix for RPM build testing + +--- + tests/conftest.py | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/tests/conftest.py b/tests/conftest.py +index b73202cd..0fb9657d 100644 +--- a/tests/conftest.py ++++ b/tests/conftest.py +@@ -105,7 +105,14 @@ def dev_server(xprocess, request, tmp_path): + # Extend the existing env, otherwise Windows and CI fails. + # Modules will be imported from tmp_path for the reloader. + # Unbuffered output so the logs update immediately. +- env = {**os.environ, "PYTHONPATH": str(tmp_path), "PYTHONUNBUFFERED": "1"} ++ pypath = os.environ.get("PYTHONPATH", "") ++ if len(pypath) > 0: ++ pypath += os.pathsep ++ env = { ++ **os.environ, ++ "PYTHONPATH": f"{pypath}{str(tmp_path)}", ++ "PYTHONUNBUFFERED": "1", ++ } + + @cached_property + def pattern(self): +-- +2.34.1 + diff --git a/SPECS/python-werkzeug/0002-disable-stat-test.patch b/SPECS/python-werkzeug/0002-disable-stat-test.patch new file mode 100644 index 00000000000..f17bc09c2d2 --- /dev/null +++ b/SPECS/python-werkzeug/0002-disable-stat-test.patch @@ -0,0 +1,25 @@ +From ae808cb894699826acaa28c24c716caacc21a101 Mon Sep 17 00:00:00 2001 +From: Nick Samson +Date: Thu, 16 Nov 2023 13:14:37 -0800 +Subject: [PATCH] Removed stat test due to environmental concerns + +--- + tests/test_serving.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tests/test_serving.py b/tests/test_serving.py +index 4abc755d..eb7d161f 100644 +--- a/tests/test_serving.py ++++ b/tests/test_serving.py +@@ -148,7 +148,7 @@ def test_windows_get_args_for_reloading(monkeypatch, tmp_path): + + + @pytest.mark.filterwarnings("ignore::pytest.PytestUnraisableExceptionWarning") +-@pytest.mark.parametrize("find", [_find_stat_paths, _find_watchdog_paths]) ++@pytest.mark.parametrize("find", [_find_watchdog_paths]) + def test_exclude_patterns(find): + # Select a path to exclude from the unfiltered list, assert that it is present and + # then gets excluded. +-- +2.34.1 + diff --git a/SPECS/python-werkzeug/CVE-2023-46136.patch b/SPECS/python-werkzeug/CVE-2023-46136.patch new file mode 100644 index 00000000000..681803c736c --- /dev/null +++ b/SPECS/python-werkzeug/CVE-2023-46136.patch @@ -0,0 +1,36 @@ +From b1916c0c083e0be1c9d887ee2f3d696922bfc5c1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pawe=C5=82=20Srokosz?= +Date: Thu, 12 Oct 2023 18:50:04 +0200 +Subject: [PATCH 1/2] Fix: slow multipart parsing for huge files with few CR/LF + characters + +--- + src/werkzeug/sansio/multipart.py | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py +index 380993af7..fc8735378 100644 +--- a/src/werkzeug/sansio/multipart.py ++++ b/src/werkzeug/sansio/multipart.py +@@ -251,12 +251,20 @@ def _parse_data(self, data: bytes, *, start: bool) -> tuple[bytes, int, bool]: + else: + data_start = 0 + +- if self.buffer.find(b"--" + self.boundary) == -1: ++ boundary = b"--" + self.boundary ++ ++ if self.buffer.find(boundary) == -1: + # No complete boundary in the buffer, but there may be + # a partial boundary at the end. As the boundary + # starts with either a nl or cr find the earliest and + # return up to that as data. + data_end = del_index = self.last_newline(data[data_start:]) + data_start ++ # If amount of data after last newline is far from ++ # possible length of partial boundary, we should ++ # assume that there is no partial boundary in the buffer ++ # and return all pending data. ++ if (len(data) - data_end) > len(b"\n" + boundary): ++ data_end = del_index = len(data) + more_data = True + else: + match = self.boundary_re.search(data) diff --git a/SPECS/python-werkzeug/python-werkzeug.signatures.json b/SPECS/python-werkzeug/python-werkzeug.signatures.json index 2bfdf9b7a93..5a46311edff 100644 --- a/SPECS/python-werkzeug/python-werkzeug.signatures.json +++ b/SPECS/python-werkzeug/python-werkzeug.signatures.json @@ -1,5 +1,5 @@ { "Signatures": { - "werkzeug-2.2.3.tar.gz": "8b5729f88b3e18b8fbb5a722e374bf00a1d9b77da447e846e2c64b8108c0522a" + "werkzeug-2.3.7.tar.gz": "d9a68679b430e099b668a61130f1eb6e6768ac663a8667745ad637955ca1dd9d" } } \ No newline at end of file diff --git a/SPECS/python-werkzeug/python-werkzeug.spec b/SPECS/python-werkzeug/python-werkzeug.spec index 9b9d7189b5e..9ccaab6ccc3 100644 --- a/SPECS/python-werkzeug/python-werkzeug.spec +++ b/SPECS/python-werkzeug/python-werkzeug.spec @@ -1,6 +1,6 @@ Summary: The Swiss Army knife of Python web development Name: python-werkzeug -Version: 2.2.3 +Version: 2.3.7 Release: 1%{?dist} License: BSD Vendor: Microsoft Corporation @@ -8,6 +8,16 @@ Distribution: Mariner Group: Development/Languages/Python URL: https://github.com/pallets/werkzeug Source0: https://github.com/pallets/werkzeug/archive/%{version}.tar.gz#/werkzeug-%{version}.tar.gz +Patch0: 0001-enable-tests-in-rpm-env.patch +# Werkzeug really doesn't like running tests in the RPM build environment. +# The %%tox macro explicitly sets PYTHONPATH rather than using a virtualenv and, +# crucially, also puts Werkzeug in a directory considered to be a system directory. +# Normally, Werkzeug is tested using an editable installation, where the source +# directory is added to PYTHONPATH but not installed in a system directory. +# This means all files this function would normally find are considered system files +# and are excluded. +Patch1: 0002-disable-stat-test.patch +Patch2: CVE-2023-46136.patch BuildArch: noarch %description @@ -17,14 +27,19 @@ The Swiss Army knife of Python web development Summary: The Swiss Army knife of Python web development BuildRequires: python3-devel BuildRequires: python3-libs +BuildRequires: pyproject-rpm-macros BuildRequires: python3-setuptools BuildRequires: python3-xml +BuildRequires: python3-flit-core +BuildRequires: python3-pip +BuildRequires: python3-wheel Requires: python3 +Requires: python3-markupsafe %if %{with_check} BuildRequires: curl-devel BuildRequires: openssl-devel -BuildRequires: python3-pip BuildRequires: python3-requests +BuildRequires: python3-markupsafe %endif %description -n python3-werkzeug @@ -33,22 +48,34 @@ Werkzeug started as simple collection of various utilities for WSGI applications %prep %autosetup -n werkzeug-%{version} -p1 +%generate_buildrequires +%pyproject_buildrequires %{?with_tests:-t} + %build -%py3_build +%pyproject_wheel %install -%py3_install +%pyproject_install +%pyproject_save_files werkzeug %check -pip3 install pytest hypothesis -LANG=en_US.UTF-8 PYTHONPATH=./ python3 setup.py test +pip3 install tox==4.6.3 tox-current-env +pip3 install -r requirements/tests.txt +%tox -%files -n python3-werkzeug +%files -n python3-werkzeug -f %{pyproject_files} %defattr(-,root,root) +%doc README.rst +%doc CHANGES.rst %license LICENSE.rst -%{python3_sitelib}/* %changelog +* Mon Nov 06 2023 Nick Samson - 2.3.7-1 +- Upgraded to version 2.3.7 +- Migrated to pyproject build +- Added required MarkupSafe dependency +- Added patch for CVE-2023-46136 + * Tue Mar 14 2023 Rakshaa Viswanathan - 2.2.3-1 - Updated to version 2.2.3 for CVE-2023-23934 adn CVE-2023-25577 - Remove patch for CVE-2023-25577 diff --git a/SPECS/qemu/CVE-2023-3354.patch b/SPECS/qemu/CVE-2023-3354.patch new file mode 100644 index 00000000000..413219d5e76 --- /dev/null +++ b/SPECS/qemu/CVE-2023-3354.patch @@ -0,0 +1,83 @@ +From 10be627d2b5ec2d6b3dce045144aa739eef678b4 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Tue, 20 Jun 2023 09:45:34 +0100 +Subject: [PATCH] io: remove io watch if TLS channel is closed during handshake +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The TLS handshake make take some time to complete, during which time an +I/O watch might be registered with the main loop. If the owner of the +I/O channel invokes qio_channel_close() while the handshake is waiting +to continue the I/O watch must be removed. Failing to remove it will +later trigger the completion callback which the owner is not expecting +to receive. In the case of the VNC server, this results in a SEGV as +vnc_disconnect_start() tries to shutdown a client connection that is +already gone / NULL. + +CVE-2023-3354 +Reported-by: jiangyegen +Signed-off-by: Daniel P. Berrangé +--- + include/io/channel-tls.h | 1 + + io/channel-tls.c | 18 ++++++++++++------ + 2 files changed, 13 insertions(+), 6 deletions(-) + +diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h +index 5672479e9e..26c67f17e2 100644 +--- a/include/io/channel-tls.h ++++ b/include/io/channel-tls.h +@@ -48,6 +48,7 @@ struct QIOChannelTLS { + QIOChannel *master; + QCryptoTLSSession *session; + QIOChannelShutdown shutdown; ++ guint hs_ioc_tag; + }; + + /** +diff --git a/io/channel-tls.c b/io/channel-tls.c +index 9805dd0a3f..847d5297c3 100644 +--- a/io/channel-tls.c ++++ b/io/channel-tls.c +@@ -198,12 +198,13 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc, + } + + trace_qio_channel_tls_handshake_pending(ioc, status); +- qio_channel_add_watch_full(ioc->master, +- condition, +- qio_channel_tls_handshake_io, +- data, +- NULL, +- context); ++ ioc->hs_ioc_tag = ++ qio_channel_add_watch_full(ioc->master, ++ condition, ++ qio_channel_tls_handshake_io, ++ data, ++ NULL, ++ context); + } + } + +@@ -218,6 +219,7 @@ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc, + QIOChannelTLS *tioc = QIO_CHANNEL_TLS( + qio_task_get_source(task)); + ++ tioc->hs_ioc_tag = 0; + g_free(data); + qio_channel_tls_handshake_task(tioc, task, context); + +@@ -378,6 +380,10 @@ static int qio_channel_tls_close(QIOChannel *ioc, + { + QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + ++ if (tioc->hs_ioc_tag) { ++ g_clear_handle_id(&tioc->hs_ioc_tag, g_source_remove); ++ } ++ + return qio_channel_close(tioc->master, errp); + } + +-- +2.34.1 + diff --git a/SPECS/qemu/qemu.spec b/SPECS/qemu/qemu.spec index ec531ee55fe..3fb48cc50cc 100644 --- a/SPECS/qemu/qemu.spec +++ b/SPECS/qemu/qemu.spec @@ -217,7 +217,7 @@ Obsoletes: %{name}-system-unicore32-core <= %{version}-%{release} Summary: QEMU is a FAST! processor emulator Name: qemu Version: 6.2.0 -Release: 18%{?dist} +Release: 19%{?dist} License: BSD AND CC-BY AND GPLv2+ AND LGPLv2+ AND MIT Vendor: Microsoft Corporation Distribution: Mariner @@ -274,6 +274,7 @@ Patch1013: CVE-2022-3165.patch # https://gitlab.com/qemu-project/qemu/-/issues/541 Patch1014: CVE-2021-3750.patch Patch1015: CVE-2022-36648.patch +Patch1016: CVE-2023-3354.patch # alsa audio output BuildRequires: alsa-lib-devel @@ -2308,6 +2309,9 @@ useradd -r -u 107 -g qemu -G kvm -d / -s %{_sbindir}/nologin \ %changelog +* Mon Oct 30 2023 Jonathan Behrens - 6.2.0-19 +- Address CVE-2023-3354 + * Wed Sep 20 2023 Jon Slobodzian - 6.2.0-18 - Recompile with stack-protection fixed gcc version (CVE-2023-4039) diff --git a/SPECS/rook/rook.spec b/SPECS/rook/rook.spec index 6c9cb5d89df..3235566a0e0 100644 --- a/SPECS/rook/rook.spec +++ b/SPECS/rook/rook.spec @@ -249,7 +249,7 @@ sed -i -e "s|\(.*tag: \)VERSION|\1%{helm_appVersion}|" %{values_yaml} %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.6.2-15 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.6.2-14 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/skopeo/skopeo.spec b/SPECS/skopeo/skopeo.spec index 922a528f557..b6e616c7e18 100644 --- a/SPECS/skopeo/skopeo.spec +++ b/SPECS/skopeo/skopeo.spec @@ -50,7 +50,7 @@ make test-unit-local - Update to v1.13.3 to fix CVE-2023-33199 in rekor. * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.12.0-5 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.12.0-4 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/sriov-network-device-plugin/sriov-network-device-plugin.spec b/SPECS/sriov-network-device-plugin/sriov-network-device-plugin.spec index bed532bf657..161505c4f48 100644 --- a/SPECS/sriov-network-device-plugin/sriov-network-device-plugin.spec +++ b/SPECS/sriov-network-device-plugin/sriov-network-device-plugin.spec @@ -35,7 +35,7 @@ install -D -m0755 images/ddptool-1.0.1.12.tar.gz %{buildroot}%{_datadir}/%{name} %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 3.5.1-3 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 3.5.1-2 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/syslog-ng/CVE-2022-38725.patch b/SPECS/syslog-ng/CVE-2022-38725.patch new file mode 100644 index 00000000000..8e6c01bf943 --- /dev/null +++ b/SPECS/syslog-ng/CVE-2022-38725.patch @@ -0,0 +1,778 @@ +From 41171e96b533ec20e0ab25196e10a10468d9b816 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sat, 20 Aug 2022 12:26:05 +0200 +Subject: [PATCH 1/8] syslogformat: fix out-of-bounds reading of data buffer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +--- + modules/syslogformat/syslog-format.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/modules/syslogformat/syslog-format.c b/modules/syslogformat/syslog-format.c +index 6625b87..cc1f3eb 100644 +--- a/modules/syslogformat/syslog-format.c ++++ b/modules/syslogformat/syslog-format.c +@@ -223,6 +223,9 @@ log_msg_parse_cisco_timestamp_attributes(LogMessage *self, const guchar **data, + const guchar *src = *data; + gint left = *length; + ++ if (!left) ++ return; ++ + /* Cisco timestamp extensions, the first '*' indicates that the clock is + * unsynced, '.' if it is known to be synced */ + if (G_UNLIKELY(src[0] == '*')) +@@ -562,7 +565,7 @@ log_msg_parse_sd(LogMessage *self, const guchar **data, gint *length, const MsgF + open_sd++; + do + { +- if (!isascii(*src) || *src == '=' || *src == ' ' || *src == ']' || *src == '"') ++ if (!left || !isascii(*src) || *src == '=' || *src == ' ' || *src == ']' || *src == '"') + goto error; + /* read sd_id */ + pos = 0; +@@ -595,7 +598,8 @@ log_msg_parse_sd(LogMessage *self, const guchar **data, gint *length, const MsgF + sd_id_len = pos; + strcpy(sd_value_name, logmsg_sd_prefix); + strncpy(sd_value_name + logmsg_sd_prefix_len, sd_id_name, sizeof(sd_value_name) - logmsg_sd_prefix_len); +- if (*src == ']') ++ ++ if (left && *src == ']') + { + log_msg_set_value_by_name(self, sd_value_name, "", 0); + } +@@ -612,7 +616,7 @@ log_msg_parse_sd(LogMessage *self, const guchar **data, gint *length, const MsgF + else + goto error; + +- if (!isascii(*src) || *src == '=' || *src == ' ' || *src == ']' || *src == '"') ++ if (!left || !isascii(*src) || *src == '=' || *src == ' ' || *src == ']' || *src == '"') + goto error; + + /* read sd-param */ +-- +2.38.0.windows.1 + + +From 04d584b8cf2233eee96678fc32dd9e23eb4b04e8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sat, 20 Aug 2022 12:22:44 +0200 +Subject: [PATCH 2/8] syslogformat: add bug reproducer test for non-zero + terminated input +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +--- + modules/syslogformat/CMakeLists.txt | 1 + + modules/syslogformat/tests/CMakeLists.txt | 1 + + .../syslogformat/tests/test_syslog_format.c | 72 +++++++++++++++++++ + 5 files changed, 85 insertions(+) + create mode 100644 modules/syslogformat/tests/CMakeLists.txt + create mode 100644 modules/syslogformat/tests/test_syslog_format.c + +diff --git a/modules/syslogformat/CMakeLists.txt b/modules/syslogformat/CMakeLists.txt +index 94ee01a..64848ef 100644 +--- a/modules/syslogformat/CMakeLists.txt ++++ b/modules/syslogformat/CMakeLists.txt +@@ -14,3 +14,4 @@ add_module( + SOURCES ${SYSLOGFORMAT_SOURCES} + ) + ++add_test_subdirectory(tests) +diff --git a/modules/syslogformat/tests/CMakeLists.txt b/modules/syslogformat/tests/CMakeLists.txt +new file mode 100644 +index 0000000..2e45b71 +--- /dev/null ++++ b/modules/syslogformat/tests/CMakeLists.txt +@@ -0,0 +1 @@ ++add_unit_test(CRITERION TARGET test_syslog_format DEPENDS syslogformat) +diff --git a/modules/syslogformat/tests/test_syslog_format.c b/modules/syslogformat/tests/test_syslog_format.c +new file mode 100644 +index 0000000..b247fe3 +--- /dev/null ++++ b/modules/syslogformat/tests/test_syslog_format.c +@@ -0,0 +1,72 @@ ++/* ++ * Copyright (c) 2022 One Identity ++ * Copyright (c) 2022 László Várady ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation, 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, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ * ++ * As an additional exemption you are allowed to compile & link against the ++ * OpenSSL libraries as published by the OpenSSL project. See the file ++ * COPYING for details. ++ * ++ */ ++ ++#include ++ ++#include "apphook.h" ++#include "cfg.h" ++#include "syslog-format.h" ++#include "logmsg/logmsg.h" ++#include "msg-format.h" ++#include "scratch-buffers.h" ++ ++#include ++ ++GlobalConfig *cfg; ++MsgFormatOptions parse_options; ++ ++static void ++setup(void) ++{ ++ app_startup(); ++ syslog_format_init(); ++ ++ cfg = cfg_new_snippet(); ++ msg_format_options_defaults(&parse_options); ++} ++ ++static void ++teardown(void) ++{ ++ scratch_buffers_explicit_gc(); ++ app_shutdown(); ++ cfg_free(cfg); ++} ++ ++TestSuite(syslog_format, .init = setup, .fini = teardown); ++ ++Test(syslog_format, parser_should_not_spin_on_non_zero_terminated_input, .timeout = 10) ++{ ++ const gchar *data = "<182>2022-08-17T05:02:28.217 mymachine su: 'su root' failed for lonvick on /dev/pts/8"; ++ /* chosen carefully to reproduce a bug */ ++ gsize data_length = 27; ++ ++ msg_format_options_init(&parse_options, cfg); ++ LogMessage *msg = msg_format_construct_message(&parse_options, (const guchar *) data, data_length); ++ ++ gsize problem_position; ++ cr_assert(syslog_format_handler(&parse_options, msg, (const guchar *) data, data_length, &problem_position)); ++ ++ msg_format_options_destroy(&parse_options); ++ log_msg_unref(msg); ++} +-- +2.38.0.windows.1 + + +From 1466f25f0bf334f9fc5a7e4a125adbbfa3339719 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sun, 21 Aug 2022 18:44:28 +0200 +Subject: [PATCH 3/8] syslogformat: fix reading cisco sequence id out of bounds +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +--- + modules/syslogformat/syslog-format.c | 2 +- + .../syslogformat/tests/test_syslog_format.c | 32 +++++++++++++++++++ + 2 files changed, 33 insertions(+), 1 deletion(-) + +diff --git a/modules/syslogformat/syslog-format.c b/modules/syslogformat/syslog-format.c +index cc1f3eb..513c42b 100644 +--- a/modules/syslogformat/syslog-format.c ++++ b/modules/syslogformat/syslog-format.c +@@ -207,7 +207,7 @@ log_msg_parse_cisco_sequence_id(LogMessage *self, const guchar **data, gint *len + + /* if the next char is not space, then we may try to read a date */ + +- if (*src != ' ') ++ if (!left || *src != ' ') + return; + + log_msg_set_value(self, handles.cisco_seqid, (gchar *) *data, *length - left - 1); +diff --git a/modules/syslogformat/tests/test_syslog_format.c b/modules/syslogformat/tests/test_syslog_format.c +index b247fe3..d0f5b40 100644 +--- a/modules/syslogformat/tests/test_syslog_format.c ++++ b/modules/syslogformat/tests/test_syslog_format.c +@@ -70,3 +70,35 @@ Test(syslog_format, parser_should_not_spin_on_non_zero_terminated_input, .timeou + msg_format_options_destroy(&parse_options); + log_msg_unref(msg); + } ++ ++Test(syslog_format, cisco_sequence_id_non_zero_termination) ++{ ++ const gchar *data = "<189>65536: "; ++ gsize data_length = strlen(data); ++ ++ msg_format_options_init(&parse_options, cfg); ++ LogMessage *msg = msg_format_construct_message(&parse_options, (const guchar *) data, data_length); ++ ++ gsize problem_position; ++ cr_assert(syslog_format_handler(&parse_options, msg, (const guchar *) data, data_length, &problem_position)); ++ cr_assert_str_eq(log_msg_get_value_by_name(msg, ".SDATA.meta.sequenceId", NULL), "65536"); ++ ++ msg_format_options_destroy(&parse_options); ++ log_msg_unref(msg); ++} ++ ++Test(syslog_format, minimal_non_zero_terminated_numeric_message_is_parsed_as_program_name) ++{ ++ const gchar *data = "<189>65536"; ++ gsize data_length = strlen(data); ++ ++ msg_format_options_init(&parse_options, cfg); ++ LogMessage *msg = msg_format_construct_message(&parse_options, (const guchar *) data, data_length); ++ ++ gsize problem_position; ++ cr_assert(syslog_format_handler(&parse_options, msg, (const guchar *) data, data_length, &problem_position)); ++ cr_assert_str_eq(log_msg_get_value_by_name(msg, "PROGRAM", NULL), "65536"); ++ ++ msg_format_options_destroy(&parse_options); ++ log_msg_unref(msg); ++} +-- +2.38.0.windows.1 + + +From 4e0aedee7c5caae45714cbbe7f08fc34552adc59 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sat, 20 Aug 2022 12:42:38 +0200 +Subject: [PATCH 4/8] timeutils: fix iterating out of the range of timestamp + buffer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +Signed-off-by: Balazs Scheidler +--- + lib/timeutils/scan-timestamp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/timeutils/scan-timestamp.c b/lib/timeutils/scan-timestamp.c +index 2f6a6b7..cb6802d 100644 +--- a/lib/timeutils/scan-timestamp.c ++++ b/lib/timeutils/scan-timestamp.c +@@ -328,7 +328,7 @@ __parse_usec(const guchar **data, gint *length) + src++; + (*length)--; + } +- while (isdigit(*src)) ++ while (*length > 0 && isdigit(*src)) + { + src++; + (*length)--; +-- +2.38.0.windows.1 + + +From a9aebf3431a210dbae252a5fec64a2b3e6fcf602 Mon Sep 17 00:00:00 2001 +From: Balazs Scheidler +Date: Sat, 20 Aug 2022 12:43:42 +0200 +Subject: [PATCH 5/8] timeutils: add tests for non-zero terminated inputs + +Signed-off-by: Balazs Scheidler +--- + lib/timeutils/tests/test_scan-timestamp.c | 126 +++++++++++++++++++--- + 1 file changed, 113 insertions(+), 13 deletions(-) + +diff --git a/lib/timeutils/tests/test_scan-timestamp.c b/lib/timeutils/tests/test_scan-timestamp.c +index e655425..3c2101d 100644 +--- a/lib/timeutils/tests/test_scan-timestamp.c ++++ b/lib/timeutils/tests/test_scan-timestamp.c +@@ -49,17 +49,21 @@ fake_time_add(time_t diff) + } + + static gboolean +-_parse_rfc3164(const gchar *ts, gchar isotimestamp[32]) ++_parse_rfc3164(const gchar *ts, gint len, gchar isotimestamp[32]) + { + UnixTime stamp; +- const guchar *data = (const guchar *) ts; +- gint length = strlen(ts); ++ const guchar *tsu = (const guchar *) ts; ++ gint tsu_len = len < 0 ? strlen(ts) : len; + GString *result = g_string_new(""); + WallClockTime wct = WALL_CLOCK_TIME_INIT; + +- ++ const guchar *data = tsu; ++ gint length = tsu_len; + gboolean success = scan_rfc3164_timestamp(&data, &length, &wct); + ++ cr_assert(length >= 0); ++ cr_assert(data == &tsu[tsu_len - length]); ++ + unix_time_unset(&stamp); + convert_wall_clock_time_to_unix_time(&wct, &stamp); + +@@ -70,16 +74,21 @@ _parse_rfc3164(const gchar *ts, gchar isotimestamp[32]) + } + + static gboolean +-_parse_rfc5424(const gchar *ts, gchar isotimestamp[32]) ++_parse_rfc5424(const gchar *ts, gint len, gchar isotimestamp[32]) + { + UnixTime stamp; +- const guchar *data = (const guchar *) ts; +- gint length = strlen(ts); ++ const guchar *tsu = (const guchar *) ts; ++ gint tsu_len = len < 0 ? strlen(ts) : len; + GString *result = g_string_new(""); + WallClockTime wct = WALL_CLOCK_TIME_INIT; + ++ const guchar *data = tsu; ++ gint length = tsu_len; + gboolean success = scan_rfc5424_timestamp(&data, &length, &wct); + ++ cr_assert(length >= 0); ++ cr_assert(data == &tsu[tsu_len - length]); ++ + unix_time_unset(&stamp); + convert_wall_clock_time_to_unix_time(&wct, &stamp); + +@@ -90,31 +99,60 @@ _parse_rfc5424(const gchar *ts, gchar isotimestamp[32]) + } + + static gboolean +-_rfc3164_timestamp_eq(const gchar *ts, const gchar *expected, gchar converted[32]) ++_rfc3164_timestamp_eq(const gchar *ts, gint len, const gchar *expected, gchar converted[32]) + { +- cr_assert(_parse_rfc3164(ts, converted)); ++ cr_assert(_parse_rfc3164(ts, len, converted)); + return strcmp(converted, expected) == 0; + } + + static gboolean +-_rfc5424_timestamp_eq(const gchar *ts, const gchar *expected, gchar converted[32]) ++_rfc5424_timestamp_eq(const gchar *ts, gint len, const gchar *expected, gchar converted[32]) + { +- cr_assert(_parse_rfc5424(ts, converted)); ++ cr_assert(_parse_rfc5424(ts, len, converted)); + return strcmp(converted, expected) == 0; + } + + #define _expect_rfc3164_timestamp_eq(ts, expected) \ + ({ \ + gchar converted[32]; \ +- cr_expect(_rfc3164_timestamp_eq(ts, expected, converted), "Parsed RFC3164 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \ ++ cr_expect(_rfc3164_timestamp_eq(ts, -1, expected, converted), "Parsed RFC3164 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \ ++ }) ++ ++#define _expect_rfc3164_timestamp_len_eq(ts, len, expected) \ ++ ({ \ ++ gchar converted[32]; \ ++ cr_expect(_rfc3164_timestamp_eq(ts, len, expected, converted), "Parsed RFC3164 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \ ++ }) ++ ++#define _expect_rfc3164_fails(ts, len) \ ++ ({ \ ++ WallClockTime wct = WALL_CLOCK_TIME_INIT; \ ++ const guchar *data = (guchar *) ts; \ ++ gint length = len < 0 ? strlen(ts) : len; \ ++ cr_assert_not(scan_rfc3164_timestamp(&data, &length, &wct)); \ + }) + + #define _expect_rfc5424_timestamp_eq(ts, expected) \ + ({ \ + gchar converted[32]; \ +- cr_expect(_rfc5424_timestamp_eq(ts, expected, converted), "Parsed RFC5424 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \ ++ cr_expect(_rfc5424_timestamp_eq(ts, -1, expected, converted), "Parsed RFC5424 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \ ++ }) ++ ++#define _expect_rfc5424_timestamp_len_eq(ts, len, expected) \ ++ ({ \ ++ gchar converted[32]; \ ++ cr_expect(_rfc5424_timestamp_eq(ts, len, expected, converted), "Parsed RFC5424 timestamp does not equal expected, ts=%s, converted=%s, expected=%s", ts, converted, expected); \ ++ }) ++ ++#define _expect_rfc5424_fails(ts, len) \ ++ ({ \ ++ WallClockTime wct = WALL_CLOCK_TIME_INIT; \ ++ const guchar *data = (guchar *) ts; \ ++ gint length = len < 0 ? strlen(ts) : len; \ ++ cr_assert_not(scan_rfc5424_timestamp(&data, &length, &wct)); \ + }) + ++ + Test(parse_timestamp, standard_bsd_format) + { + _expect_rfc3164_timestamp_eq("Oct 1 17:46:12", "2017-10-01T17:46:12.000+02:00"); +@@ -148,6 +186,68 @@ Test(parse_timestamp, standard_bsd_format_year_in_the_past) + _expect_rfc3164_timestamp_eq("Dec 31 17:46:12", "2017-12-31T17:46:12.000+01:00"); + } + ++Test(parse_timestamp, non_zero_terminated_rfc3164_iso_input_is_handled_properly) ++{ ++ gchar *ts = "2022-08-17T05:02:28.417Z whatever"; ++ gint ts_len = 24; ++ ++ _expect_rfc3164_timestamp_len_eq(ts, strlen(ts), "2022-08-17T05:02:28.417+00:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len + 5, "2022-08-17T05:02:28.417+00:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len, "2022-08-17T05:02:28.417+00:00"); ++ ++ /* no "Z" parsed, timezone defaults to local, forced CET */ ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len - 1, "2022-08-17T05:02:28.417+02:00"); ++ ++ /* msec is partially parsed as we trim the string from the right */ ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len - 2, "2022-08-17T05:02:28.410+02:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len - 3, "2022-08-17T05:02:28.400+02:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len - 4, "2022-08-17T05:02:28.000+02:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len - 5, "2022-08-17T05:02:28.000+02:00"); ++ ++ for (gint i = 6; i < ts_len; i++) ++ _expect_rfc3164_fails(ts, ts_len - i); ++ ++} ++ ++Test(parse_timestamp, non_zero_terminated_rfc3164_bsd_pix_or_asa_input_is_handled_properly) ++{ ++ gchar *ts = "Aug 17 2022 05:02:28: whatever"; ++ gint ts_len = 21; ++ ++ _expect_rfc3164_timestamp_len_eq(ts, strlen(ts), "2022-08-17T05:02:28.000+02:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len + 5, "2022-08-17T05:02:28.000+02:00"); ++ _expect_rfc3164_timestamp_len_eq(ts, ts_len, "2022-08-17T05:02:28.000+02:00"); ++ ++ /* no ":" at the end, that's a problem, unrecognized */ ++ _expect_rfc3164_fails(ts, ts_len - 1); ++ ++ for (gint i = 1; i < ts_len; i++) ++ _expect_rfc3164_fails(ts, ts_len - i); ++} ++ ++Test(parse_timestamp, non_zero_terminated_rfc5424_input_is_handled_properly) ++{ ++ gchar *ts = "2022-08-17T05:02:28.417Z whatever"; ++ gint ts_len = 24; ++ ++ _expect_rfc5424_timestamp_len_eq(ts, strlen(ts), "2022-08-17T05:02:28.417+00:00"); ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len + 5, "2022-08-17T05:02:28.417+00:00"); ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len, "2022-08-17T05:02:28.417+00:00"); ++ ++ /* no "Z" parsed, timezone defaults to local, forced CET */ ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len - 1, "2022-08-17T05:02:28.417+02:00"); ++ ++ /* msec is partially parsed as we trim the string from the right */ ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len - 2, "2022-08-17T05:02:28.410+02:00"); ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len - 3, "2022-08-17T05:02:28.400+02:00"); ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len - 4, "2022-08-17T05:02:28.000+02:00"); ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len - 5, "2022-08-17T05:02:28.000+02:00"); ++ ++ for (gint i = 6; i < ts_len; i++) ++ _expect_rfc5424_fails(ts, ts_len - i); ++ ++} ++ + + Test(parse_timestamp, daylight_saving_behavior_at_spring_with_explicit_timezones) + { +-- +2.38.0.windows.1 + + +From 48d441a6ac6522e66b3ee0b7bd1cbd5b0ee2fc38 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sat, 20 Aug 2022 14:29:43 +0200 +Subject: [PATCH 6/8] timeutils: name repeating constant +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +--- + lib/timeutils/scan-timestamp.c | 54 ++++++++++++++++++---------------- + 1 file changed, 29 insertions(+), 25 deletions(-) + +diff --git a/lib/timeutils/scan-timestamp.c b/lib/timeutils/scan-timestamp.c +index cb6802d..197e3ad 100644 +--- a/lib/timeutils/scan-timestamp.c ++++ b/lib/timeutils/scan-timestamp.c +@@ -34,41 +34,43 @@ scan_day_abbrev(const gchar **buf, gint *left, gint *wday) + { + *wday = -1; + +- if (*left < 3) ++ const gsize abbrev_length = 3; ++ ++ if (*left < abbrev_length) + return FALSE; + + switch (**buf) + { + case 'S': +- if (strncasecmp(*buf, "Sun", 3) == 0) ++ if (strncasecmp(*buf, "Sun", abbrev_length) == 0) + *wday = 0; +- else if (strncasecmp(*buf, "Sat", 3) == 0) ++ else if (strncasecmp(*buf, "Sat", abbrev_length) == 0) + *wday = 6; + else + return FALSE; + break; + case 'M': +- if (strncasecmp(*buf, "Mon", 3) == 0) ++ if (strncasecmp(*buf, "Mon", abbrev_length) == 0) + *wday = 1; + else + return FALSE; + break; + case 'T': +- if (strncasecmp(*buf, "Tue", 3) == 0) ++ if (strncasecmp(*buf, "Tue", abbrev_length) == 0) + *wday = 2; +- else if (strncasecmp(*buf, "Thu", 3) == 0) ++ else if (strncasecmp(*buf, "Thu", abbrev_length) == 0) + *wday = 4; + else + return FALSE; + break; + case 'W': +- if (strncasecmp(*buf, "Wed", 3) == 0) ++ if (strncasecmp(*buf, "Wed", abbrev_length) == 0) + *wday = 3; + else + return FALSE; + break; + case 'F': +- if (strncasecmp(*buf, "Fri", 3) == 0) ++ if (strncasecmp(*buf, "Fri", abbrev_length) == 0) + *wday = 5; + else + return FALSE; +@@ -77,8 +79,8 @@ scan_day_abbrev(const gchar **buf, gint *left, gint *wday) + return FALSE; + } + +- (*buf) += 3; +- (*left) -= 3; ++ (*buf) += abbrev_length; ++ (*left) -= abbrev_length; + return TRUE; + } + +@@ -87,63 +89,65 @@ scan_month_abbrev(const gchar **buf, gint *left, gint *mon) + { + *mon = -1; + +- if (*left < 3) ++ const gsize abbrev_length = 3; ++ ++ if (*left < abbrev_length) + return FALSE; + + switch (**buf) + { + case 'J': +- if (strncasecmp(*buf, "Jan", 3) == 0) ++ if (strncasecmp(*buf, "Jan", abbrev_length) == 0) + *mon = 0; +- else if (strncasecmp(*buf, "Jun", 3) == 0) ++ else if (strncasecmp(*buf, "Jun", abbrev_length) == 0) + *mon = 5; +- else if (strncasecmp(*buf, "Jul", 3) == 0) ++ else if (strncasecmp(*buf, "Jul", abbrev_length) == 0) + *mon = 6; + else + return FALSE; + break; + case 'F': +- if (strncasecmp(*buf, "Feb", 3) == 0) ++ if (strncasecmp(*buf, "Feb", abbrev_length) == 0) + *mon = 1; + else + return FALSE; + break; + case 'M': +- if (strncasecmp(*buf, "Mar", 3) == 0) ++ if (strncasecmp(*buf, "Mar", abbrev_length) == 0) + *mon = 2; +- else if (strncasecmp(*buf, "May", 3) == 0) ++ else if (strncasecmp(*buf, "May", abbrev_length) == 0) + *mon = 4; + else + return FALSE; + break; + case 'A': +- if (strncasecmp(*buf, "Apr", 3) == 0) ++ if (strncasecmp(*buf, "Apr", abbrev_length) == 0) + *mon = 3; +- else if (strncasecmp(*buf, "Aug", 3) == 0) ++ else if (strncasecmp(*buf, "Aug", abbrev_length) == 0) + *mon = 7; + else + return FALSE; + break; + case 'S': +- if (strncasecmp(*buf, "Sep", 3) == 0) ++ if (strncasecmp(*buf, "Sep", abbrev_length) == 0) + *mon = 8; + else + return FALSE; + break; + case 'O': +- if (strncasecmp(*buf, "Oct", 3) == 0) ++ if (strncasecmp(*buf, "Oct", abbrev_length) == 0) + *mon = 9; + else + return FALSE; + break; + case 'N': +- if (strncasecmp(*buf, "Nov", 3) == 0) ++ if (strncasecmp(*buf, "Nov", abbrev_length) == 0) + *mon = 10; + else + return FALSE; + break; + case 'D': +- if (strncasecmp(*buf, "Dec", 3) == 0) ++ if (strncasecmp(*buf, "Dec", abbrev_length) == 0) + *mon = 11; + else + return FALSE; +@@ -152,8 +156,8 @@ scan_month_abbrev(const gchar **buf, gint *left, gint *mon) + return FALSE; + } + +- (*buf) += 3; +- (*left) -= 3; ++ (*buf) += abbrev_length; ++ (*left) -= abbrev_length; + return TRUE; + } + +-- +2.38.0.windows.1 + + +From 928663b7fea9c7e8c74fb3a3e44d32131cd4b069 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sat, 20 Aug 2022 14:30:22 +0200 +Subject: [PATCH 7/8] timeutils: fix invalid calculation of ISO timestamp + length +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +--- + lib/timeutils/scan-timestamp.c | 8 ++++++-- + lib/timeutils/tests/test_scan-timestamp.c | 7 +++++++ + 2 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/lib/timeutils/scan-timestamp.c b/lib/timeutils/scan-timestamp.c +index 197e3ad..4e618e4 100644 +--- a/lib/timeutils/scan-timestamp.c ++++ b/lib/timeutils/scan-timestamp.c +@@ -346,19 +346,21 @@ __parse_usec(const guchar **data, gint *length) + static gboolean + __has_iso_timezone(const guchar *src, gint length) + { +- return (length >= 5) && ++ return (length >= 6) && + (*src == '+' || *src == '-') && + isdigit(*(src+1)) && + isdigit(*(src+2)) && + *(src+3) == ':' && + isdigit(*(src+4)) && + isdigit(*(src+5)) && +- !isdigit(*(src+6)); ++ (length < 7 || !isdigit(*(src+6))); + } + + static guint32 + __parse_iso_timezone(const guchar **data, gint *length) + { ++ g_assert(*length >= 6); ++ + gint hours, mins; + const guchar *src = *data; + guint32 tz = 0; +@@ -368,8 +370,10 @@ __parse_iso_timezone(const guchar **data, gint *length) + hours = (*(src + 1) - '0') * 10 + *(src + 2) - '0'; + mins = (*(src + 4) - '0') * 10 + *(src + 5) - '0'; + tz = sign * (hours * 3600 + mins * 60); ++ + src += 6; + (*length) -= 6; ++ + *data = src; + return tz; + } +diff --git a/lib/timeutils/tests/test_scan-timestamp.c b/lib/timeutils/tests/test_scan-timestamp.c +index 3c2101d..50edb6a 100644 +--- a/lib/timeutils/tests/test_scan-timestamp.c ++++ b/lib/timeutils/tests/test_scan-timestamp.c +@@ -248,6 +248,13 @@ Test(parse_timestamp, non_zero_terminated_rfc5424_input_is_handled_properly) + + } + ++Test(parse_timestamp, non_zero_terminated_rfc5424_timestamp_only) ++{ ++ const gchar *ts = "2022-08-17T05:02:28.417+03:00"; ++ gint ts_len = strlen(ts); ++ _expect_rfc5424_timestamp_len_eq(ts, ts_len, ts); ++} ++ + + Test(parse_timestamp, daylight_saving_behavior_at_spring_with_explicit_timezones) + { +-- +2.38.0.windows.1 + + +From bee470d56113d9b8676e10c37146b9c565c30f51 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?L=C3=A1szl=C3=B3=20V=C3=A1rady?= + +Date: Sat, 20 Aug 2022 14:30:51 +0200 +Subject: [PATCH 8/8] timeutils: fix out-of-bounds reading of data buffer +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: László Várady +--- + lib/timeutils/scan-timestamp.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/timeutils/scan-timestamp.c b/lib/timeutils/scan-timestamp.c +index 4e618e4..0f7f52e 100644 +--- a/lib/timeutils/scan-timestamp.c ++++ b/lib/timeutils/scan-timestamp.c +@@ -427,7 +427,7 @@ __parse_bsd_timestamp(const guchar **data, gint *length, WallClockTime *wct) + if (!scan_pix_timestamp((const gchar **) &src, &left, wct)) + return FALSE; + +- if (*src == ':') ++ if (left && *src == ':') + { + src++; + left--; +@@ -478,7 +478,7 @@ scan_rfc3164_timestamp(const guchar **data, gint *length, WallClockTime *wct) + * looking at you, skip that as well, so we can reliably detect IPv6 + * addresses as hostnames, which would be using ":" as well. */ + +- if (*src == ':') ++ if (left && *src == ':') + { + ++src; + --left; +-- +2.38.0.windows.1 + diff --git a/SPECS/syslog-ng/syslog-ng.spec b/SPECS/syslog-ng/syslog-ng.spec index d3950180847..6bfb7929d04 100644 --- a/SPECS/syslog-ng/syslog-ng.spec +++ b/SPECS/syslog-ng/syslog-ng.spec @@ -1,7 +1,7 @@ Summary: Next generation system logger facilty Name: syslog-ng Version: 3.33.2 -Release: 4%{?dist} +Release: 7%{?dist} License: BSD AND GPLv2+ AND LGPLv2+ Vendor: Microsoft Corporation Distribution: Mariner @@ -10,6 +10,7 @@ URL: https://syslog-ng.org/ Source0: https://github.com/balabit/%{name}/releases/download/%{name}-%{version}/%{name}-%{version}.tar.gz Source1: 60-syslog-ng-journald.conf Source2: syslog-ng.service +Patch0: CVE-2022-38725.patch BuildRequires: glib-devel BuildRequires: json-c-devel BuildRequires: json-glib-devel @@ -50,7 +51,7 @@ Requires: %{name} = %{version}-%{release} needed to build applications using syslog-ng APIs. %prep -%setup -q +%autosetup -p1 rm -rf ../p3dir cp -a . ../p3dir @@ -85,9 +86,10 @@ sed -i 's/eventlog//g' %{buildroot}%{_libdir}/pkgconfig/syslog-ng.pc install -vdm755 %{buildroot}%{_libdir}/systemd/system-preset echo "disable syslog-ng.service" > %{buildroot}%{_libdir}/systemd/system-preset/50-syslog-ng.preset -%check -pip3 install unittest2 nose ply pep8 -make %{?_smp_mflags} check +# TODO: fix tests. Look at comments in https://github.com/microsoft/CBL-Mariner/pull/6431 +# %check +# pip3 install unittest2 nose ply pep8 +# make %{?_smp_mflags} check %post if [ $1 -eq 1 ] ; then @@ -147,6 +149,15 @@ fi %{_libdir}/pkgconfig/* %changelog +* Mon Nov 27 2023 Saul Paredes - 3.33.2-7 +- Comment %check section + +* Wed Nov 22 2023 Saul Paredes - 3.33.2-6 +- Remove 'make check' from %check section + +* Fri Oct 13 2023 Mykhailo Bykhovtsev - 3.33.2-5 +- Patched CVE-2022-38725 + * Wed Sep 20 2023 Jon Slobodzian - 3.33.2-4 - Recompile with stack-protection fixed gcc version (CVE-2023-4039) diff --git a/SPECS/telegraf/telegraf.spec b/SPECS/telegraf/telegraf.spec index 5176a2efede..a95abcb119b 100644 --- a/SPECS/telegraf/telegraf.spec +++ b/SPECS/telegraf/telegraf.spec @@ -83,7 +83,7 @@ fi - Removed invalid, outdated patch. * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.27.3-4 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.27.3-3 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/terraform/terraform.spec b/SPECS/terraform/terraform.spec index 53a01836bb2..a485dbc3b39 100644 --- a/SPECS/terraform/terraform.spec +++ b/SPECS/terraform/terraform.spec @@ -58,7 +58,7 @@ install -p -m 755 -t %{buildroot}%{_bindir} ./terraform %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 1.3.2-12 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 1.3.2-11 - Bump release to rebuild with updated version of Go. diff --git a/SPECS/tmux/manual-patch-to-fix-crash-due-to-change-to-ncurses.patch b/SPECS/tmux/manual-patch-to-fix-crash-due-to-change-to-ncurses.patch new file mode 100644 index 00000000000..e1bdfa68d9d --- /dev/null +++ b/SPECS/tmux/manual-patch-to-fix-crash-due-to-change-to-ncurses.patch @@ -0,0 +1,90 @@ +From 8bec5e2d2e5a6c77ce3c2ec2c38e658efc6fc26f Mon Sep 17 00:00:00 2001 +From: Tobias Brick +Date: Thu, 26 Oct 2023 17:23:48 +0000 +Subject: [PATCH] Manual patch to fix crash due to change to ncurses + +ncurses-6.4-20230408 change tparm to require cur_term, which broke tmux usage of it. + +ncurses-6.4-20230423 then added tiparm_s that allows usage without cur_term. + +tmux change https://github.com/tmux/tmux/commit/39d41d0810d4e8ae6ce8d27776dfbb96722d9319 uses tiparm_s if it exists, but cannot be cleanly applied to tmux tag 3.2a. + +That change uses a config setting to created #defines to determine which version of tparm it should use, and only conditionally uses tiparm_s, because it needs to be backwards compatible with previous versions of ncurses. + +But to use that, we would need to get the actual source as it appears in github, rather than the released version (they are different downloads: see https://github.com/tmux/tmux/releases). + +Fortunately, we have the luxury of forcing tmux to use a version of ncurses that has the function we want (see above). + +Given all this, this patch takes the change to use tiparm_s, removes the conditional compilation portion so it always uses tiparm_s and applies it to the code as it exists in 3.2a. + +It has both a build-time and run-time dependency on ncurses-6.4-20230423 or later. +--- + tty-term.c | 30 +++++++++++++++++++++++++----- + 1 file changed, 25 insertions(+), 5 deletions(-) + +diff --git a/tty-term.c b/tty-term.c +index add71d89..a5ed1d77 100644 +--- a/tty-term.c ++++ b/tty-term.c +@@ -761,33 +761,53 @@ tty_term_string(struct tty_term *term, enum tty_code_code code) + const char * + tty_term_string1(struct tty_term *term, enum tty_code_code code, int a) + { +- return (tparm((char *) tty_term_string(term, code), a, 0, 0, 0, 0, 0, 0, 0, 0)); ++ const char *x = tty_term_string(term, code), *s; ++ s = tiparm_s(1, 0, x, a); ++ if (s == NULL) ++ fatalx("could not expand %s", tty_term_codes[code].name); ++ return (s); + } + + const char * + tty_term_string2(struct tty_term *term, enum tty_code_code code, int a, int b) + { +- return (tparm((char *) tty_term_string(term, code), a, b, 0, 0, 0, 0, 0, 0, 0)); ++ const char *x = tty_term_string(term, code), *s; ++ s = tiparm_s(2, 0, x, a, b); ++ if (s == NULL) ++ fatalx("could not expand %s", tty_term_codes[code].name); ++ return (s); + } + + const char * + tty_term_string3(struct tty_term *term, enum tty_code_code code, int a, int b, + int c) + { +- return (tparm((char *) tty_term_string(term, code), a, b, c, 0, 0, 0, 0, 0, 0)); ++ const char *x = tty_term_string(term, code), *s; ++ s = tiparm_s(3, 0, x, a, b, c); ++ if (s == NULL) ++ fatalx("could not expand %s", tty_term_codes[code].name); ++ return (s); + } + + const char * + tty_term_ptr1(struct tty_term *term, enum tty_code_code code, const void *a) + { +- return (tparm((char *) tty_term_string(term, code), (long)a, 0, 0, 0, 0, 0, 0, 0, 0)); ++ const char *x = tty_term_string(term, code), *s; ++ s = tiparm_s(1, 1, x, a); ++ if (s == NULL) ++ fatalx("could not expand %s", tty_term_codes[code].name); ++ return (s); + } + + const char * + tty_term_ptr2(struct tty_term *term, enum tty_code_code code, const void *a, + const void *b) + { +- return (tparm((char *) tty_term_string(term, code), (long)a, (long)b, 0, 0, 0, 0, 0, 0, 0)); ++ const char *x = tty_term_string(term, code), *s; ++ s = tiparm_s(2, 3, x, a, b); ++ if (s == NULL) ++ fatalx("could not expand %s", tty_term_codes[code].name); ++ return (s); + } + + int +-- +2.33.8 + diff --git a/SPECS/tmux/tmux.spec b/SPECS/tmux/tmux.spec index af94042e01d..76dade73748 100644 --- a/SPECS/tmux/tmux.spec +++ b/SPECS/tmux/tmux.spec @@ -1,7 +1,7 @@ Summary: Terminal multiplexer Name: tmux Version: 3.2a -Release: 3%{?dist} +Release: 4%{?dist} License: ISC and BSD URL: https://tmux.github.io/ Group: Applications/System @@ -9,8 +9,11 @@ Vendor: Microsoft Corporation Distribution: Mariner Source0: https://github.com/tmux/tmux/releases/download/%{version}/%{name}-%{version}.tar.gz Patch0: CVE-2022-47016.patch -Requires: libevent ncurses -BuildRequires: libevent-devel ncurses-devel +Patch1: manual-patch-to-fix-crash-due-to-change-to-ncurses.patch +Requires: libevent +Requires: ncurses >= 6.4-2 +BuildRequires: libevent-devel +BuildRequires: ncurses-devel >= 6.4-2 %description Terminal multiplexer @@ -38,6 +41,10 @@ make %{?_smp_mflags} check %exclude /usr/src %changelog +* Thu Nov 16 2023 Tobias Brick - 3.2a-4 +- Add dependency on ncurses >= 6.4-2 +- Patch to fix crash due to kprevious change to ncurses + * Fri Feb 10 2023 Rachel Menge - 3.2a-3 - Patch CVE-2022-47016 diff --git a/SPECS/valgrind/valgrind.signatures.json b/SPECS/valgrind/valgrind.signatures.json index 713c0d28217..1b13217b01c 100644 --- a/SPECS/valgrind/valgrind.signatures.json +++ b/SPECS/valgrind/valgrind.signatures.json @@ -1,5 +1,5 @@ { "Signatures": { - "valgrind-3.18.1.tar.bz2": "00859aa13a772eddf7822225f4b46ee0d39afbe071d32778da4d99984081f7f5" + "valgrind-3.22.0.tar.bz2": "c811db5add2c5f729944caf47c4e7a65dcaabb9461e472b578765dd7bf6d2d4c" } -} \ No newline at end of file +} diff --git a/SPECS/valgrind/valgrind.spec b/SPECS/valgrind/valgrind.spec index 6d635c0de0a..85cd54fca39 100644 --- a/SPECS/valgrind/valgrind.spec +++ b/SPECS/valgrind/valgrind.spec @@ -1,8 +1,8 @@ %global security_hardening none Summary: Memory Management Debugger. Name: valgrind -Version: 3.18.1 -Release: 3%{?dist} +Version: 3.22.0 +Release: 1%{?dist} License: GPL-2.0-or-later Vendor: Microsoft Corporation Distribution: Mariner @@ -50,6 +50,9 @@ make %{?_smp_mflags} -k check %{_libexecdir}/valgrind/* %changelog +* Thu Nov 16 2023 Sriram Nambakam - 3.22.0-1 +- Update to 3.22.0 + * Mon Aug 08 2023 Sam Meluch - 3.18.1-3 - Add glibc-debuginfo to Requires diff --git a/SPECS/vim/vim.signatures.json b/SPECS/vim/vim.signatures.json index e190f5948bc..bc346525d8c 100644 --- a/SPECS/vim/vim.signatures.json +++ b/SPECS/vim/vim.signatures.json @@ -1,5 +1,5 @@ { - "Signatures": { - "vim-9.0.2010.tar.gz": "7295de193f7c78128ae43d8c7e55851ee1d85c5723a40fedb054147d9be82acf" - } + "Signatures": { + "vim-9.0.2112.tar.gz": "1967127c825a6c32dfd21f419a0e11cbcc20b796d861b1483507ddf19b2c5df0" + } } \ No newline at end of file diff --git a/SPECS/vim/vim.spec b/SPECS/vim/vim.spec index 597df75dfc6..19c6a20e811 100644 --- a/SPECS/vim/vim.spec +++ b/SPECS/vim/vim.spec @@ -1,7 +1,7 @@ %define debug_package %{nil} Summary: Text editor Name: vim -Version: 9.0.2010 +Version: 9.0.2112 Release: 1%{?dist} License: Vim Vendor: Microsoft Corporation @@ -196,6 +196,12 @@ fi %{_bindir}/vimdiff %changelog +* Mon Nov 27 2023 CBL-Mariner Servicing Account - 9.0.2112-1 +- Auto-upgrade to 9.0.2112 - CVEs + +* Tue Nov 14 2023 CBL-Mariner Servicing Account - 9.0.2068-1 +- Auto-upgrade to 9.0.2068 - CVE-2023-46246 + * Tue Oct 17 2023 Neha Agarwal - 9.0.2010-1 - Update version to 9.0.2010 to fix CVE-2023-5535. - Remove patches that no longer apply in the new version. diff --git a/SPECS/vitess/vitess.spec b/SPECS/vitess/vitess.spec index 360923998ce..79a5afcc78e 100644 --- a/SPECS/vitess/vitess.spec +++ b/SPECS/vitess/vitess.spec @@ -105,7 +105,7 @@ go check -t go/cmd \ %changelog * Mon Oct 16 2023 CBL-Mariner Servicing Account - 16.0.2-6 -- Bump release to rebuild with go 1.20.10 +- Bump release to rebuild with go 1.20.9 * Tue Oct 10 2023 Dan Streetman - 16.0.2-5 - Bump release to rebuild with updated version of Go. diff --git a/cgmanifest.json b/cgmanifest.json index 0723e5097bf..cc24bcd17bc 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -1147,8 +1147,8 @@ "type": "other", "other": { "name": "blobfuse2", - "version": "2.1.1", - "downloadUrl": "https://github.com/Azure/azure-storage-fuse/archive/blobfuse2-2.1.1.tar.gz" + "version": "2.1.2", + "downloadUrl": "https://github.com/Azure/azure-storage-fuse/archive/blobfuse2-2.1.2.tar.gz" } } }, @@ -2913,6 +2913,16 @@ } } }, + { + "component": { + "type": "other", + "other": { + "name": "double-conversion", + "version": "3.1.5", + "downloadUrl": "https://github.com/google/double-conversion/archive/v3.1.5/double-conversion-3.1.5.tar.gz" + } + } + }, { "component": { "type": "other", @@ -6530,8 +6540,8 @@ "type": "other", "other": { "name": "hyperv-daemons", - "version": "5.15.137.1", - "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.137.1.tar.gz" + "version": "5.15.138.1", + "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.138.1.tar.gz" } } }, @@ -8111,8 +8121,8 @@ "type": "other", "other": { "name": "kernel", - "version": "5.15.137.1", - "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.137.1.tar.gz" + "version": "5.15.138.1", + "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.138.1.tar.gz" } } }, @@ -8121,8 +8131,8 @@ "type": "other", "other": { "name": "kernel-azure", - "version": "5.15.137.1", - "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.137.1.tar.gz" + "version": "5.15.138.1", + "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.138.1.tar.gz" } } }, @@ -8131,8 +8141,8 @@ "type": "other", "other": { "name": "kernel-hci", - "version": "5.15.137.1", - "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.137.1.tar.gz" + "version": "5.15.138.1", + "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.138.1.tar.gz" } } }, @@ -8141,8 +8151,8 @@ "type": "other", "other": { "name": "kernel-headers", - "version": "5.15.137.1", - "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.137.1.tar.gz" + "version": "5.15.138.1", + "downloadUrl": "https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/5.15.138.1.tar.gz" } } }, @@ -12021,8 +12031,8 @@ "type": "other", "other": { "name": "linuxptp", - "version": "2.0", - "downloadUrl": "https://github.com/richardcochran/linuxptp/archive/e05809/linuxptp-e05809.tar.gz" + "version": "3.1.1", + "downloadUrl": "https://sourceforge.net/projects/linuxptp/files/v3.1/linuxptp-3.1.1.tgz" } } }, @@ -13618,6 +13628,16 @@ } } }, + { + "component": { + "type": "other", + "other": { + "name": "msft-golang", + "version": "1.20.11", + "downloadUrl": "https://github.com/microsoft/go/releases/download/v1.20.11-1/go.20231107.4.src.tar.gz" + } + } + }, { "component": { "type": "other", @@ -13753,8 +13773,8 @@ "type": "other", "other": { "name": "mysql", - "version": "8.0.34", - "downloadUrl": "https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.34.tar.gz" + "version": "8.0.35", + "downloadUrl": "https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-boost-8.0.35.tar.gz" } } }, @@ -14054,7 +14074,7 @@ "other": { "name": "ncurses", "version": "6.4", - "downloadUrl": "https://invisible-mirror.net/archives/ncurses/current/ncurses-6.4-20230408.tgz" + "downloadUrl": "https://invisible-mirror.net/archives/ncurses/current/ncurses-6.4-20230423.tgz" } } }, @@ -23019,6 +23039,16 @@ } } }, + { + "component": { + "type": "other", + "other": { + "name": "python-junit-xml", + "version": "1.9", + "downloadUrl": "https://github.com/kyrus/python-junit-xml/tarball/856414648cbab3f64e69b856bc25cea8b9aa0377" + } + } + }, { "component": { "type": "other", @@ -24954,8 +24984,8 @@ "type": "other", "other": { "name": "python-werkzeug", - "version": "2.2.3", - "downloadUrl": "https://github.com/pallets/werkzeug/archive/2.2.3.tar.gz" + "version": "2.3.7", + "downloadUrl": "https://github.com/pallets/werkzeug/archive/2.3.7.tar.gz" } } }, @@ -29347,8 +29377,8 @@ "type": "other", "other": { "name": "valgrind", - "version": "3.18.1", - "downloadUrl": "https://sourceware.org/pub/valgrind/valgrind-3.18.1.tar.bz2" + "version": "3.22.0", + "downloadUrl": "https://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2" } } }, @@ -29387,8 +29417,8 @@ "type": "other", "other": { "name": "vim", - "version": "9.0.2010", - "downloadUrl": "https://github.com/vim/vim/archive/v9.0.2010.tar.gz" + "version": "9.0.2112", + "downloadUrl": "https://github.com/vim/vim/archive/v9.0.2112.tar.gz" } } }, @@ -30877,4 +30907,4 @@ } ], "Version": 1 -} +} \ No newline at end of file diff --git a/toolkit/resources/manifests/package/ccache-configuration.json b/toolkit/resources/manifests/package/ccache-configuration.json index 38e358b8950..910b12bec76 100644 --- a/toolkit/resources/manifests/package/ccache-configuration.json +++ b/toolkit/resources/manifests/package/ccache-configuration.json @@ -32,7 +32,7 @@ "name": "kernel", "comment": "", "enabled": true, - "packageNames": [ "kernel", "kernel-azure", "kernel-hci", "kernel-mshv", "kernel-uvm" ] + "packageNames": [ "kernel", "kernel-azure", "kernel-hci", "kernel-mshv", "kernel-uvm", "kernel-mos" ] }, { "name": "libdb", diff --git a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt index 4b4d7a16506..66db2ea2801 100644 --- a/toolkit/resources/manifests/package/pkggen_core_aarch64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_aarch64.txt @@ -1,5 +1,5 @@ filesystem-1.1-17.cm2.aarch64.rpm -kernel-headers-5.15.137.1-1.cm2.noarch.rpm +kernel-headers-5.15.138.1-4.cm2.noarch.rpm glibc-2.35-6.cm2.aarch64.rpm glibc-devel-2.35-6.cm2.aarch64.rpm glibc-i18n-2.35-6.cm2.aarch64.rpm @@ -33,11 +33,11 @@ libpkgconf-1.8.0-3.cm2.aarch64.rpm pkgconf-1.8.0-3.cm2.aarch64.rpm pkgconf-m4-1.8.0-3.cm2.noarch.rpm pkgconf-pkg-config-1.8.0-3.cm2.aarch64.rpm -ncurses-6.4-1.cm2.aarch64.rpm -ncurses-compat-6.4-1.cm2.aarch64.rpm -ncurses-devel-6.4-1.cm2.aarch64.rpm -ncurses-libs-6.4-1.cm2.aarch64.rpm -ncurses-term-6.4-1.cm2.aarch64.rpm +ncurses-6.4-2.cm2.aarch64.rpm +ncurses-compat-6.4-2.cm2.aarch64.rpm +ncurses-devel-6.4-2.cm2.aarch64.rpm +ncurses-libs-6.4-2.cm2.aarch64.rpm +ncurses-term-6.4-2.cm2.aarch64.rpm readline-8.1-1.cm2.aarch64.rpm readline-devel-8.1-1.cm2.aarch64.rpm coreutils-8.32-7.cm2.aarch64.rpm @@ -224,8 +224,8 @@ pinentry-1.2.0-1.cm2.aarch64.rpm gnupg2-2.4.0-2.cm2.aarch64.rpm gnupg2-lang-2.4.0-2.cm2.aarch64.rpm gpgme-1.16.0-2.cm2.aarch64.rpm -mariner-repos-shared-2.0-8.cm2.noarch.rpm -mariner-repos-2.0-8.cm2.noarch.rpm +mariner-repos-shared-2.0-9.cm2.noarch.rpm +mariner-repos-2.0-9.cm2.noarch.rpm libffi-3.4.2-3.cm2.aarch64.rpm libffi-devel-3.4.2-3.cm2.aarch64.rpm libtasn1-4.19.0-1.cm2.aarch64.rpm diff --git a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt index ca8d153ae93..e524e9dc838 100644 --- a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt @@ -1,5 +1,5 @@ filesystem-1.1-17.cm2.x86_64.rpm -kernel-headers-5.15.137.1-1.cm2.noarch.rpm +kernel-headers-5.15.138.1-4.cm2.noarch.rpm glibc-2.35-6.cm2.x86_64.rpm glibc-devel-2.35-6.cm2.x86_64.rpm glibc-i18n-2.35-6.cm2.x86_64.rpm @@ -33,11 +33,11 @@ libpkgconf-1.8.0-3.cm2.x86_64.rpm pkgconf-1.8.0-3.cm2.x86_64.rpm pkgconf-m4-1.8.0-3.cm2.noarch.rpm pkgconf-pkg-config-1.8.0-3.cm2.x86_64.rpm -ncurses-6.4-1.cm2.x86_64.rpm -ncurses-compat-6.4-1.cm2.x86_64.rpm -ncurses-devel-6.4-1.cm2.x86_64.rpm -ncurses-libs-6.4-1.cm2.x86_64.rpm -ncurses-term-6.4-1.cm2.x86_64.rpm +ncurses-6.4-2.cm2.x86_64.rpm +ncurses-compat-6.4-2.cm2.x86_64.rpm +ncurses-devel-6.4-2.cm2.x86_64.rpm +ncurses-libs-6.4-2.cm2.x86_64.rpm +ncurses-term-6.4-2.cm2.x86_64.rpm readline-8.1-1.cm2.x86_64.rpm readline-devel-8.1-1.cm2.x86_64.rpm coreutils-8.32-7.cm2.x86_64.rpm @@ -224,8 +224,8 @@ pinentry-1.2.0-1.cm2.x86_64.rpm gnupg2-2.4.0-2.cm2.x86_64.rpm gnupg2-lang-2.4.0-2.cm2.x86_64.rpm gpgme-1.16.0-2.cm2.x86_64.rpm -mariner-repos-shared-2.0-8.cm2.noarch.rpm -mariner-repos-2.0-8.cm2.noarch.rpm +mariner-repos-shared-2.0-9.cm2.noarch.rpm +mariner-repos-2.0-9.cm2.noarch.rpm libffi-3.4.2-3.cm2.x86_64.rpm libffi-devel-3.4.2-3.cm2.x86_64.rpm libtasn1-4.19.0-1.cm2.x86_64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_aarch64.txt b/toolkit/resources/manifests/package/toolchain_aarch64.txt index 0bd3e09f9e4..0d726b1c0b5 100644 --- a/toolkit/resources/manifests/package/toolchain_aarch64.txt +++ b/toolkit/resources/manifests/package/toolchain_aarch64.txt @@ -136,7 +136,7 @@ intltool-0.51.0-7.cm2.noarch.rpm itstool-2.0.6-4.cm2.noarch.rpm kbd-2.2.0-1.cm2.aarch64.rpm kbd-debuginfo-2.2.0-1.cm2.aarch64.rpm -kernel-headers-5.15.137.1-1.cm2.noarch.rpm +kernel-headers-5.15.138.1-4.cm2.noarch.rpm kmod-29-2.cm2.aarch64.rpm kmod-debuginfo-29-2.cm2.aarch64.rpm kmod-devel-29-2.cm2.aarch64.rpm @@ -230,31 +230,33 @@ m4-debuginfo-1.4.19-2.cm2.aarch64.rpm make-4.3-3.cm2.aarch64.rpm make-debuginfo-4.3-3.cm2.aarch64.rpm mariner-check-macros-2.0-24.cm2.noarch.rpm -mariner-repos-2.0-8.cm2.noarch.rpm -mariner-repos-debug-2.0-8.cm2.noarch.rpm -mariner-repos-debug-preview-2.0-8.cm2.noarch.rpm -mariner-repos-extended-2.0-8.cm2.noarch.rpm -mariner-repos-extended-debug-2.0-8.cm2.noarch.rpm -mariner-repos-extended-debug-preview-2.0-8.cm2.noarch.rpm -mariner-repos-extended-preview-2.0-8.cm2.noarch.rpm -mariner-repos-extras-2.0-8.cm2.noarch.rpm -mariner-repos-extras-preview-2.0-8.cm2.noarch.rpm -mariner-repos-microsoft-2.0-8.cm2.noarch.rpm -mariner-repos-microsoft-preview-2.0-8.cm2.noarch.rpm -mariner-repos-preview-2.0-8.cm2.noarch.rpm -mariner-repos-shared-2.0-8.cm2.noarch.rpm +mariner-repos-2.0-9.cm2.noarch.rpm +mariner-repos-cloud-native-2.0-9.cm2.noarch.rpm +mariner-repos-cloud-native-preview-2.0-9.cm2.noarch.rpm +mariner-repos-debug-2.0-9.cm2.noarch.rpm +mariner-repos-debug-preview-2.0-9.cm2.noarch.rpm +mariner-repos-extended-2.0-9.cm2.noarch.rpm +mariner-repos-extended-debug-2.0-9.cm2.noarch.rpm +mariner-repos-extended-debug-preview-2.0-9.cm2.noarch.rpm +mariner-repos-extended-preview-2.0-9.cm2.noarch.rpm +mariner-repos-extras-2.0-9.cm2.noarch.rpm +mariner-repos-extras-preview-2.0-9.cm2.noarch.rpm +mariner-repos-microsoft-2.0-9.cm2.noarch.rpm +mariner-repos-microsoft-preview-2.0-9.cm2.noarch.rpm +mariner-repos-preview-2.0-9.cm2.noarch.rpm +mariner-repos-shared-2.0-9.cm2.noarch.rpm mariner-rpm-macros-2.0-24.cm2.noarch.rpm meson-0.60.2-2.cm2.noarch.rpm mpfr-4.1.0-2.cm2.aarch64.rpm mpfr-debuginfo-4.1.0-2.cm2.aarch64.rpm mpfr-devel-4.1.0-2.cm2.aarch64.rpm msopenjdk-11-11.0.18-1.aarch64.rpm -ncurses-6.4-1.cm2.aarch64.rpm -ncurses-compat-6.4-1.cm2.aarch64.rpm -ncurses-debuginfo-6.4-1.cm2.aarch64.rpm -ncurses-devel-6.4-1.cm2.aarch64.rpm -ncurses-libs-6.4-1.cm2.aarch64.rpm -ncurses-term-6.4-1.cm2.aarch64.rpm +ncurses-6.4-2.cm2.aarch64.rpm +ncurses-compat-6.4-2.cm2.aarch64.rpm +ncurses-debuginfo-6.4-2.cm2.aarch64.rpm +ncurses-devel-6.4-2.cm2.aarch64.rpm +ncurses-libs-6.4-2.cm2.aarch64.rpm +ncurses-term-6.4-2.cm2.aarch64.rpm newt-0.52.21-5.cm2.aarch64.rpm newt-debuginfo-0.52.21-5.cm2.aarch64.rpm newt-devel-0.52.21-5.cm2.aarch64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_x86_64.txt b/toolkit/resources/manifests/package/toolchain_x86_64.txt index e168b9fb0fa..8ca901674ba 100644 --- a/toolkit/resources/manifests/package/toolchain_x86_64.txt +++ b/toolkit/resources/manifests/package/toolchain_x86_64.txt @@ -136,7 +136,7 @@ intltool-0.51.0-7.cm2.noarch.rpm itstool-2.0.6-4.cm2.noarch.rpm kbd-2.2.0-1.cm2.x86_64.rpm kbd-debuginfo-2.2.0-1.cm2.x86_64.rpm -kernel-headers-5.15.137.1-1.cm2.noarch.rpm +kernel-headers-5.15.138.1-4.cm2.noarch.rpm kmod-29-2.cm2.x86_64.rpm kmod-debuginfo-29-2.cm2.x86_64.rpm kmod-devel-29-2.cm2.x86_64.rpm @@ -230,31 +230,33 @@ m4-debuginfo-1.4.19-2.cm2.x86_64.rpm make-4.3-3.cm2.x86_64.rpm make-debuginfo-4.3-3.cm2.x86_64.rpm mariner-check-macros-2.0-24.cm2.noarch.rpm -mariner-repos-2.0-8.cm2.noarch.rpm -mariner-repos-debug-2.0-8.cm2.noarch.rpm -mariner-repos-debug-preview-2.0-8.cm2.noarch.rpm -mariner-repos-extended-2.0-8.cm2.noarch.rpm -mariner-repos-extended-debug-2.0-8.cm2.noarch.rpm -mariner-repos-extended-debug-preview-2.0-8.cm2.noarch.rpm -mariner-repos-extended-preview-2.0-8.cm2.noarch.rpm -mariner-repos-extras-2.0-8.cm2.noarch.rpm -mariner-repos-extras-preview-2.0-8.cm2.noarch.rpm -mariner-repos-microsoft-2.0-8.cm2.noarch.rpm -mariner-repos-microsoft-preview-2.0-8.cm2.noarch.rpm -mariner-repos-preview-2.0-8.cm2.noarch.rpm -mariner-repos-shared-2.0-8.cm2.noarch.rpm +mariner-repos-2.0-9.cm2.noarch.rpm +mariner-repos-cloud-native-2.0-9.cm2.noarch.rpm +mariner-repos-cloud-native-preview-2.0-9.cm2.noarch.rpm +mariner-repos-debug-2.0-9.cm2.noarch.rpm +mariner-repos-debug-preview-2.0-9.cm2.noarch.rpm +mariner-repos-extended-2.0-9.cm2.noarch.rpm +mariner-repos-extended-debug-2.0-9.cm2.noarch.rpm +mariner-repos-extended-debug-preview-2.0-9.cm2.noarch.rpm +mariner-repos-extended-preview-2.0-9.cm2.noarch.rpm +mariner-repos-extras-2.0-9.cm2.noarch.rpm +mariner-repos-extras-preview-2.0-9.cm2.noarch.rpm +mariner-repos-microsoft-2.0-9.cm2.noarch.rpm +mariner-repos-microsoft-preview-2.0-9.cm2.noarch.rpm +mariner-repos-preview-2.0-9.cm2.noarch.rpm +mariner-repos-shared-2.0-9.cm2.noarch.rpm mariner-rpm-macros-2.0-24.cm2.noarch.rpm meson-0.60.2-2.cm2.noarch.rpm mpfr-4.1.0-2.cm2.x86_64.rpm mpfr-debuginfo-4.1.0-2.cm2.x86_64.rpm mpfr-devel-4.1.0-2.cm2.x86_64.rpm msopenjdk-11-11.0.18-1.x86_64.rpm -ncurses-6.4-1.cm2.x86_64.rpm -ncurses-compat-6.4-1.cm2.x86_64.rpm -ncurses-debuginfo-6.4-1.cm2.x86_64.rpm -ncurses-devel-6.4-1.cm2.x86_64.rpm -ncurses-libs-6.4-1.cm2.x86_64.rpm -ncurses-term-6.4-1.cm2.x86_64.rpm +ncurses-6.4-2.cm2.x86_64.rpm +ncurses-compat-6.4-2.cm2.x86_64.rpm +ncurses-debuginfo-6.4-2.cm2.x86_64.rpm +ncurses-devel-6.4-2.cm2.x86_64.rpm +ncurses-libs-6.4-2.cm2.x86_64.rpm +ncurses-term-6.4-2.cm2.x86_64.rpm newt-0.52.21-5.cm2.x86_64.rpm newt-debuginfo-0.52.21-5.cm2.x86_64.rpm newt-devel-0.52.21-5.cm2.x86_64.rpm diff --git a/toolkit/scripts/containerized-build/create_container_build.sh b/toolkit/scripts/containerized-build/create_container_build.sh index 8d37638141f..417546f63f6 100755 --- a/toolkit/scripts/containerized-build/create_container_build.sh +++ b/toolkit/scripts/containerized-build/create_container_build.sh @@ -60,7 +60,7 @@ build_worker_chroot() { build_tools() { pushd $toolkit_root echo "Building required tools..." - make go-srpmpacker go-depsearch go-grapher go-specreader REBUILD_TOOLS=y > /dev/null + make go-depsearch go-downloader go-grapher go-specreader go-srpmpacker REBUILD_TOOLS=y > /dev/null popd } @@ -146,7 +146,7 @@ fi if [[ "${mode}" == "build" ]]; then pushd $toolkit_root echo "Populating Intermediate SRPMs..." - if [[ ( ! -f "$TOOL_BINS_DIR/srpmpacker" ) ]]; then build_tools; fi + if [[ ( ! -f "$TOOL_BINS_DIR/srpmpacker" ) || ( ! -f "$TOOL_BINS_DIR/downloader" ) ]]; then build_tools; fi make input-srpms SRPM_FILE_SIGNATURE_HANDLING="update" > /dev/null popd fi diff --git a/toolkit/scripts/mariner-required-configs.json b/toolkit/scripts/mariner-required-configs.json index cc2719d98db..ae70649eac8 100644 --- a/toolkit/scripts/mariner-required-configs.json +++ b/toolkit/scripts/mariner-required-configs.json @@ -1103,6 +1103,19 @@ "https://github.com/microsoft/CBL-Mariner/pull/5972" ] }, + "CONFIG_BPF_LSM": { + "value": [ + "y" + ], + "arch": [ + "AMD64", + "ARM64" + ], + "comment": "Allow customers to use BPF LSM programs", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6846" + ] + }, "CONFIG_MCTP": { "value": [ "is not set" @@ -1213,6 +1226,116 @@ "PR": [ "https://github.com/microsoft/CBL-Mariner/pull/6574" ] + }, + "CONFIG_CUSE": { + "value": [ + "m" + ], + "arch": [ + "AMD64", + "ARM64" + ], + "comment": "Enable CUSE to support Nvidia rshim interface for Mariner hosts", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6853" + ] + }, + "CONFIG_IOASID": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "Needed for CONFIG_ARM_SMMU", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_IOMMU_SVA_LIB": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "Needed for CONFIG_ARM_SMMU_V3_SVA", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_ARM_SMMU": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "Needed for VFIO to work", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_ARM_SMMU_LEGACY_DT_BINDINGS": { + "value": [ + "", + "is not set" + ], + "arch": [ + "ARM64" + ], + "comment": "No device tree support needed", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_ARM_SMMU_DISABLE_BYPASS_BY_DEFAULT": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "More secure when set", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_ARM_SMMU_QCOM": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "Allow support of QCom SMMU, because why not?", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_ARM_SMMU_V3": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "Needed for VFIO to work", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] + }, + "CONFIG_ARM_SMMU_V3_SVA": { + "value": [ + "y" + ], + "arch": [ + "ARM64" + ], + "comment": "Needed for CONFIG_ARM_SMMU_V3", + "PR": [ + "https://github.com/microsoft/CBL-Mariner/pull/6829" + ] } } } diff --git a/toolkit/tools/imagecustomizer/docs/configuration.md b/toolkit/tools/imagecustomizer/docs/configuration.md index 428baf46f3a..59a4d6fa771 100644 --- a/toolkit/tools/imagecustomizer/docs/configuration.md +++ b/toolkit/tools/imagecustomizer/docs/configuration.md @@ -1,7 +1,6 @@ # Mariner Image Customizer configuration The Mariner Image Customizer is configured using a YAML (or JSON) file. -The top level type for this YAML file is the [Config](#config-type) type. ### Operation ordering @@ -63,19 +62,86 @@ SystemConfig: - kernel-hci ``` +## Top-level + +The top level type for the YAML file is the [Config](#config-type) type. + ## Config type The top-level type of the configuration. +### Disks [[Disk](#disk-type)[]] + +Contains the options for provisioning disks and their partitions. + +If the Disks field isn't specified, then the partitions of the base image aren't +changed. + +If Disks is specified, then [SystemConfig.BootType](#boottype-boottype) must also be +specified. + +While Disks is a list, only 1 disk is supported at the moment. +Support for multiple disks may (or may not) be added in the future. + +```yaml +Disks: +- PartitionTableType: gpt + MaxSize: 4096 + Partitions: + - ID: esp + Flags: + - esp + - boot + Start: 1 + End: 9 + FsType: fat32 + + - ID: rootfs + Start: 9 + FsType: ext4 + +SystemConfig: + BootType: efi + PartitionSettings: + - ID: esp + MountPoint: /boot/efi + MountOptions: umask=0077 + + - ID: rootfs + MountPoint: / +``` + ### SystemConfig [[SystemConfig](#systemconfig-type)] Contains the configuration options for the OS. +Example: + ```yaml SystemConfig: Hostname: example-image ``` +## Disk type + +Specifies the properties of a disk, including its partitions. + +### PartitionTableType [string] + +Specifies how the partition tables are laid out. + +Supported options: + +- `gpt`: Use the GUID Partition Table (GPT) format. + +### MaxSize [uint64] + +The size of the disk, specified in mebibytes (MiB). + +### Partitions [[Partition](#partition-type)] + +The partitions to provision on the disk. + ## FileConfig type Specifies options for placing a file in the OS. @@ -180,6 +246,124 @@ Packages: - openssh-server ``` +## Partition type + +### ID [string] + +Required. + +The ID of the partition. +This is used correlate Partition objects with [PartitionSetting](#partitionsetting-type) +objects. + +### FsType [string] + +Required. + +The filesystem type of the partition. + +Supported options: + +- `ext4` +- `fat32` +- `xfs` + +### Name [string] + +The label to assign to the partition. + +### Start [uint64] + +Required. + +The start location (inclusive) of the partition, specified in MiBs. + +### End [uint64] + +The end location (exclusive) of the partition, specified in MiBs. + +The End and Size fields cannot be specified at the same time. + +Either the Size or End field is required for all partitions except for the last +partition. +When both the Size and End fields are omitted, the last partition will fill the +remainder of the disk (based on the disk's [MaxSize](#maxsize-uint64) field). + +### Size [uint64] + +The size of the partition, specified in MiBs. + +### Flags [string[]] + +Specifies options for the partition. + +Supported options: + +- `esp`: The UEFI System Partition (ESP). + The partition must have a `FsType` of `fat32`. + + When specified on a GPT formatted disk, the `boot` flag must also be added. + +- `bios_grub`: Specifies this partition is the BIOS boot partition. + This is required for GPT disks that wish to be bootable using legacy BIOS mode. + + This partition must start at block 1. + + This flag is only supported on GPT formatted disks. + + For further details, see: https://en.wikipedia.org/wiki/BIOS_boot_partition + +- `boot`: Specifies that this partition contains the boot loader. + + When specified on a GPT formatted disk, the `esp` flag must also be added. + +These options mirror those in +[parted](https://www.gnu.org/software/parted/manual/html_node/set.html). + +## PartitionSetting type + +Specifies the mount options for a partition. + +### ID [string] + +Required. + +The ID of the partition. +This is used correlate [Partition](#partition-type) objects with PartitionSetting +objects. + +### MountIdentifier [string] + +Default: `partuuid` + +The partition ID type that should be used to recognize the partition on the disk. + +Supported options: + +- `uuid`: The filesystem's partition UUID. + +- `partuuid`: The partition UUID specified in the partition table. + +- `partlabel`: The partition label specified in the partition table. + +### MountOptions [string] + +The additional options used when mounting the file system. + +These options are in the same format as [mount](https://linux.die.net/man/8/mount)'s +`-o` option (or the `fs_mntops` field of the +[fstab](https://man7.org/linux/man-pages/man5/fstab.5.html) file). + +### MountPoint [string] + +Required. + +The absolute path of where the partition should be mounted. + +The mounts will be sorted to ensure that parent directories are mounted before child +directories. +For example, `/boot` will be mounted before `/boot/efi`. + ## Script type Points to a script file (typically a Bash script) to be run during customization. @@ -248,6 +432,22 @@ SystemConfig: Contains the configuration options for the OS. +### BootType [string] + +Specifies the boot system that the image supports. + +Supported options: + +- `legacy`: Support booting from BIOS firmware. + + When this option is specified, the partition layout must contain a partition with the + `bios_grub` flag. + +- `efi`: Support booting from UEFI firmware. + + When this option is specified, the partition layout must contain a partition with the + `esp` flag. + ### Hostname [string] Specifies the hostname for the OS. @@ -393,6 +593,10 @@ SystemConfig: Permissions: "664" ``` +### PartitionSettings [[PartitionSetting](#partitionsetting-type)[]] + +Specifies the mount options of the partitions. + ### PostInstallScripts [[Script](#script-type)[]] Scripts to run against the image after the packages have been added and removed. diff --git a/toolkit/tools/imagecustomizerapi/boottype.go b/toolkit/tools/imagecustomizerapi/boottype.go new file mode 100644 index 00000000000..e8931ace1b3 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/boottype.go @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "fmt" +) + +type BootType string + +const ( + BootTypeEfi BootType = "efi" + BootTypeLegacy BootType = "legacy" + BootTypeUnset BootType = "" +) + +func (t BootType) IsValid() error { + switch t { + case BootTypeEfi, BootTypeLegacy, BootTypeUnset: + // All good. + return nil + + default: + return fmt.Errorf("invalid BootType value (%v)", t) + } +} diff --git a/toolkit/tools/imagecustomizerapi/boottype_test.go b/toolkit/tools/imagecustomizerapi/boottype_test.go new file mode 100644 index 00000000000..7514bb9624f --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/boottype_test.go @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestBootTypeIsValid(t *testing.T) { + err := BootTypeEfi.IsValid() + assert.NoError(t, err) +} + +func TestBootTypeIsValidBadValue(t *testing.T) { + err := BootType("bad").IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid BootType value") +} diff --git a/toolkit/tools/imagecustomizerapi/config.go b/toolkit/tools/imagecustomizerapi/config.go index a070877c1a8..f387de301b8 100644 --- a/toolkit/tools/imagecustomizerapi/config.go +++ b/toolkit/tools/imagecustomizerapi/config.go @@ -3,15 +3,82 @@ package imagecustomizerapi +import ( + "fmt" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/sliceutils" +) + type Config struct { + Disks *[]Disk `yaml:"Disks"` SystemConfig SystemConfig `yaml:"SystemConfig"` } func (c *Config) IsValid() error { + if c.Disks != nil { + disks := *c.Disks + if len(disks) < 1 { + return fmt.Errorf("at least 1 disk must be specified (or the Disks field should be ommited)") + } + if len(disks) > 1 { + return fmt.Errorf("multiple disks is not currently supported") + } + + for i, disk := range disks { + err := disk.IsValid() + if err != nil { + return fmt.Errorf("invalid disk at index %d:\n%w", i, err) + } + } + } + err := c.SystemConfig.IsValid() if err != nil { return err } + hasDisks := c.Disks != nil + hasBootType := c.SystemConfig.BootType != BootTypeUnset + + if hasDisks != hasBootType { + return fmt.Errorf("SystemConfig.BootType and Disks must be specified together") + } + + // Ensure the correct partitions exist to support the specified the boot type. + switch c.SystemConfig.BootType { + case BootTypeEfi: + hasEsp := sliceutils.ContainsFunc(*c.Disks, func(disk Disk) bool { + return sliceutils.ContainsFunc(disk.Partitions, func(partition Partition) bool { + return sliceutils.ContainsValue(partition.Flags, PartitionFlagESP) + }) + }) + if !hasEsp { + return fmt.Errorf("'esp' partition must be provided for 'efi' boot type") + } + + case BootTypeLegacy: + hasBiosBoot := sliceutils.ContainsFunc(*c.Disks, func(disk Disk) bool { + return sliceutils.ContainsFunc(disk.Partitions, func(partition Partition) bool { + return sliceutils.ContainsValue(partition.Flags, PartitionFlagBiosGrub) + }) + }) + if !hasBiosBoot { + return fmt.Errorf("'bios_grub' partition must be provided for 'legacy' boot type") + } + } + + // Ensure all the partition settings object have an equivalent partition object. + for i, partitionSetting := range c.SystemConfig.PartitionSettings { + diskExists := sliceutils.ContainsFunc(*c.Disks, func(disk Disk) bool { + return sliceutils.ContainsFunc(disk.Partitions, func(partition Partition) bool { + return partition.ID == partitionSetting.ID + }) + }) + if !diskExists { + return fmt.Errorf("invalid PartitionSetting at index %d:\nno partition with matching ID (%s)", i, + partitionSetting.ID) + } + } + return nil } diff --git a/toolkit/tools/imagecustomizerapi/config_test.go b/toolkit/tools/imagecustomizerapi/config_test.go new file mode 100644 index 00000000000..2b0a2017973 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/config_test.go @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConfigIsValid(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{ + { + ID: "esp", + FsType: "fat32", + Start: 1, + Flags: []PartitionFlag{ + "esp", + "boot", + }, + }, + }, + }}, + SystemConfig: SystemConfig{ + BootType: "efi", + Hostname: "test", + PartitionSettings: []PartitionSetting{ + { + ID: "esp", + MountPoint: "/boot/efi", + }, + }, + }, + } + + err := config.IsValid() + assert.NoError(t, err) +} + +func TestConfigIsValidLegacy(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{ + { + ID: "boot", + FsType: "fat32", + Start: 1, + Flags: []PartitionFlag{ + "bios_grub", + }, + }, + }, + }}, + SystemConfig: SystemConfig{ + BootType: "legacy", + Hostname: "test", + }, + } + + err := config.IsValid() + assert.NoError(t, err) +} + +func TestConfigIsValidNoBootType(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + }, + }, + }}, + SystemConfig: SystemConfig{ + Hostname: "test", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "BootType") + assert.ErrorContains(t, err, "Disks") +} + +func TestConfigIsValidMultipleDisks(t *testing.T) { + config := &Config{ + Disks: &[]Disk{ + { + PartitionTableType: "gpt", + MaxSize: 1, + }, + { + PartitionTableType: "gpt", + MaxSize: 1, + }, + }, + SystemConfig: SystemConfig{ + Hostname: "test", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "multiple disks") +} + +func TestConfigIsValidZeroDisks(t *testing.T) { + config := &Config{ + Disks: &[]Disk{}, + SystemConfig: SystemConfig{ + Hostname: "test", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "1 disk") +} + +func TestConfigIsValidBadHostname(t *testing.T) { + config := &Config{ + SystemConfig: SystemConfig{ + Hostname: "test_", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid hostname") +} + +func TestConfigIsValidBadDisk(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 0, + }}, + SystemConfig: SystemConfig{ + Hostname: "test", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "MaxSize") +} + +func TestConfigIsValidMissingEsp(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{}, + }}, + SystemConfig: SystemConfig{ + BootType: "efi", + Hostname: "test", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "esp") + assert.ErrorContains(t, err, "efi") +} + +func TestConfigIsValidMissingBiosBoot(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{}, + }}, + SystemConfig: SystemConfig{ + BootType: "legacy", + Hostname: "test", + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "bios_grub") + assert.ErrorContains(t, err, "legacy") +} + +func TestConfigIsValidInvalidMountPoint(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{ + { + ID: "esp", + FsType: "fat32", + Start: 1, + Flags: []PartitionFlag{ + "esp", + "boot", + }, + }, + }, + }}, + SystemConfig: SystemConfig{ + BootType: "efi", + Hostname: "test", + PartitionSettings: []PartitionSetting{ + { + ID: "esp", + MountPoint: "boot/efi", + }, + }, + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "MountPoint") + assert.ErrorContains(t, err, "absolute path") +} + +func TestConfigIsValidInvalidPartitionId(t *testing.T) { + config := &Config{ + Disks: &[]Disk{{ + PartitionTableType: "gpt", + MaxSize: 2, + Partitions: []Partition{ + { + ID: "esp", + FsType: "fat32", + Start: 1, + Flags: []PartitionFlag{ + "esp", + "boot", + }, + }, + }, + }}, + SystemConfig: SystemConfig{ + BootType: "efi", + Hostname: "test", + PartitionSettings: []PartitionSetting{ + { + ID: "boot", + MountPoint: "/boot/efi", + }, + }, + }, + } + + err := config.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "partition") + assert.ErrorContains(t, err, "ID") +} diff --git a/toolkit/tools/imagecustomizerapi/disk.go b/toolkit/tools/imagecustomizerapi/disk.go new file mode 100644 index 00000000000..17980fd86d0 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/disk.go @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "fmt" + "sort" + "strconv" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/sliceutils" +) + +type Disk struct { + // The type of partition table to use (e.g. mbr, gpt) + PartitionTableType PartitionTableType `yaml:"PartitionTableType"` + + // The virtual size of the disk. + MaxSize uint64 `yaml:"MaxSize"` + + // The partitions to allocate on the disk. + Partitions []Partition `yaml:"Partitions"` +} + +func (d *Disk) IsValid() error { + err := d.PartitionTableType.IsValid() + if err != nil { + return err + } + + if d.MaxSize <= 0 { + return fmt.Errorf("a disk's MaxSize value (%d) must be a positive non-zero number", d.MaxSize) + } + + partitionIDSet := make(map[string]bool) + for i, partition := range d.Partitions { + err := partition.IsValid() + if err != nil { + return fmt.Errorf("invalid partition at index %d:\n%w", i, err) + } + + if _, existingName := partitionIDSet[partition.ID]; existingName { + return fmt.Errorf("duplicate partition ID used (%s) at index %d", partition.ID, i) + } + + partitionIDSet[partition.ID] = false // dummy value + + if d.PartitionTableType == PartitionTableTypeGpt { + isESP := sliceutils.ContainsValue(partition.Flags, PartitionFlagESP) + isBoot := sliceutils.ContainsValue(partition.Flags, PartitionFlagBoot) + + if isESP != isBoot { + return fmt.Errorf( + "invalid partition at index %d:\n'esp' and 'boot' flags must be specified together on GPT disks", i) + } + } + } + + // Check for overlapping partitions. + // First, sort partitions by start index. + sortedPartitions := append([]Partition(nil), d.Partitions...) + sort.Slice(sortedPartitions, func(i, j int) bool { + return sortedPartitions[i].Start < sortedPartitions[j].Start + }) + + // Then, confirm each partition ends before the next starts. + for i := 0; i < len(sortedPartitions)-1; i++ { + a := &sortedPartitions[i] + b := &sortedPartitions[i+1] + + aEnd, aHasEnd := a.GetEnd() + if !aHasEnd { + return fmt.Errorf("partition (%s) is not last partition but ommitted End value", a.ID) + } + if aEnd > b.Start { + bEnd, bHasEnd := b.GetEnd() + bEndStr := "" + if bHasEnd { + bEndStr = strconv.FormatUint(bEnd, 10) + } + return fmt.Errorf("partition's (%s) range [%d, %d) overlaps partition's (%s) range [%d, %s)", + a.ID, a.Start, aEnd, b.ID, b.Start, bEndStr) + } + } + + if len(sortedPartitions) > 0 { + // Make sure the first block isn't used. + firstPartition := sortedPartitions[0] + if firstPartition.Start == 0 { + return fmt.Errorf("block 0 must be reserved for the MBR header (%s)", firstPartition.ID) + } + + // Check that the disk is big enough for the partition layout. + lastPartition := sortedPartitions[len(sortedPartitions)-1] + + lastPartitionEnd, lastPartitionHasEnd := lastPartition.GetEnd() + + var requiredSize uint64 + if !lastPartitionHasEnd { + requiredSize = lastPartition.Start + 1 + } else { + requiredSize = lastPartitionEnd + } + + if requiredSize > d.MaxSize { + return fmt.Errorf("disk's partitions need %d MiB but MaxSize is only %d MiB", requiredSize, d.MaxSize) + } + } + + return nil +} diff --git a/toolkit/tools/imagecustomizerapi/disk_test.go b/toolkit/tools/imagecustomizerapi/disk_test.go new file mode 100644 index 00000000000..dd8aa91c58e --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/disk_test.go @@ -0,0 +1,323 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "testing" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/ptrutils" + "github.com/stretchr/testify/assert" +) + +func TestDiskIsValid(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + }, + }, + } + + err := disk.IsValid() + assert.NoError(t, err) +} + +func TestDiskIsValidWithEnd(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + End: ptrutils.PtrTo(uint64(2)), + }, + }, + } + + err := disk.IsValid() + assert.NoError(t, err) +} + +func TestDiskIsValidWithSize(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + Size: ptrutils.PtrTo(uint64(1)), + }, + }, + } + + err := disk.IsValid() + assert.NoError(t, err) +} + +func TestDiskIsValidStartAt0(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 0, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "block 0") + assert.ErrorContains(t, err, "MBR header") +} + +func TestDiskIsValidInvalidTableType(t *testing.T) { + disk := &Disk{ + PartitionTableType: "a", + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "PartitionTableType") +} + +func TestDiskIsValidInvalidPartition(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 2, + End: ptrutils.PtrTo(uint64(0)), + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid partition") +} + +func TestDiskIsValidTwoExpanding(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 4, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + }, + { + ID: "b", + FsType: "ext4", + Start: 2, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "is not last partition") +} + +func TestDiskIsValidOverlaps(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 4, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + End: ptrutils.PtrTo(uint64(3)), + }, + { + ID: "b", + FsType: "ext4", + Start: 2, + End: ptrutils.PtrTo(uint64(4)), + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "overlaps") +} + +func TestDiskIsValidOverlapsExpanding(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 4, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + End: ptrutils.PtrTo(uint64(3)), + }, + { + ID: "b", + FsType: "ext4", + Start: 2, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "overlaps") +} + +func TestDiskIsValidTooSmall(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 3, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + End: ptrutils.PtrTo(uint64(2)), + }, + { + ID: "b", + FsType: "ext4", + Start: 3, + End: ptrutils.PtrTo(uint64(4)), + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "MaxSize") +} + +func TestDiskIsValidTooSmallExpanding(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 3, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + End: ptrutils.PtrTo(uint64(3)), + }, + { + ID: "b", + FsType: "ext4", + Start: 3, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "MaxSize") +} + +func TestDiskIsValidZeroSize(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 0, + Partitions: []Partition{}, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "MaxSize") +} + +func TestDiskIsValidMissingEspFlag(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 3, + Partitions: []Partition{ + { + ID: "a", + FsType: "fat32", + Start: 1, + Flags: []PartitionFlag{ + "boot", + }, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "esp") + assert.ErrorContains(t, err, "boot") + assert.ErrorContains(t, err, "flag") +} + +func TestDiskIsValidMissingBootFlag(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 3, + Partitions: []Partition{ + { + ID: "a", + FsType: "fat32", + Start: 1, + Flags: []PartitionFlag{ + "esp", + }, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "esp") + assert.ErrorContains(t, err, "boot") + assert.ErrorContains(t, err, "flag") +} + +func TestDiskIsValidDuplicatePartitionId(t *testing.T) { + disk := &Disk{ + PartitionTableType: PartitionTableTypeGpt, + MaxSize: 2, + Partitions: []Partition{ + { + ID: "a", + FsType: "ext4", + Start: 1, + End: ptrutils.PtrTo(uint64(2)), + }, + { + ID: "a", + FsType: "ext4", + Start: 2, + }, + }, + } + + err := disk.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "duplicate partition ID") +} diff --git a/toolkit/tools/imagecustomizerapi/filesystemtype.go b/toolkit/tools/imagecustomizerapi/filesystemtype.go new file mode 100644 index 00000000000..0a8e29ecc17 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/filesystemtype.go @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import "fmt" + +// FileSystemType is a type of file system (e.g. ext4, xfs, etc.) +type FileSystemType string + +const ( + FileSystemTypeExt4 FileSystemType = "ext4" + FileSystemTypeXfs FileSystemType = "xfs" + FileSystemTypeFat32 FileSystemType = "fat32" +) + +func (t FileSystemType) IsValid() error { + switch t { + case FileSystemTypeExt4, FileSystemTypeXfs, FileSystemTypeFat32: + // All good. + return nil + + default: + return fmt.Errorf("invalid FileSystemType value (%s)", t) + } +} diff --git a/toolkit/tools/imagecustomizerapi/mountidentifier.go b/toolkit/tools/imagecustomizerapi/mountidentifier.go new file mode 100644 index 00000000000..30d92c4d042 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/mountidentifier.go @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import "fmt" + +// MountIdentifierType indicates how a partition should be identified in the fstab file +type MountIdentifierType string + +const ( + // MountIdentifierTypeUuid mounts this partition via the filesystem UUID + MountIdentifierTypeUuid MountIdentifierType = "uuid" + + // MountIdentifierTypePartUuid mounts this partition via the GPT/MBR PARTUUID + MountIdentifierTypePartUuid MountIdentifierType = "partuuid" + + // MountIdentifierTypePartLabel mounts this partition via the GPT PARTLABEL + MountIdentifierTypePartLabel MountIdentifierType = "partlabel" + + // MountIdentifierTypeDefault uses the default type, which is PARTUUID. + MountIdentifierTypeDefault MountIdentifierType = "" +) + +func (m MountIdentifierType) IsValid() error { + switch m { + case MountIdentifierTypeUuid, MountIdentifierTypePartUuid, MountIdentifierTypePartLabel, MountIdentifierTypeDefault: + // All good. + return nil + + default: + return fmt.Errorf("invalid MountIdentifierType value (%v)", m) + } +} diff --git a/toolkit/tools/imagecustomizerapi/partition.go b/toolkit/tools/imagecustomizerapi/partition.go new file mode 100644 index 00000000000..3941d642534 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/partition.go @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "fmt" + "unicode" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/sliceutils" +) + +type Partition struct { + // ID is used to correlate `Partition` objects with `PartitionSetting` objects. + ID string `yaml:"ID"` + // FsType is the type of file system to use on the partition. + FsType FileSystemType `yaml:"FsType"` + // Name is the label to assign to the partition. + Name string `yaml:"Name"` + // Start is the offset where the partition begins (inclusive), in MiBs. + Start uint64 `yaml:"Start"` + // End is the offset where the partition ends (exclusive), in MiBs. + End *uint64 `yaml:"End"` + // Size is the size of the partition in MiBs. + Size *uint64 `yaml:"Size"` + // Flags assigns features to the partition. + Flags []PartitionFlag `yaml:"Flags"` +} + +func (p *Partition) IsValid() error { + err := p.FsType.IsValid() + if err != nil { + return fmt.Errorf("invalid partition (%s) FsType value:\n%w", p.ID, err) + } + + err = isGPTNameValid(p.Name) + if err != nil { + return err + } + + if p.End != nil && p.Size != nil { + return fmt.Errorf("cannot specify both End and Size on partition (%s)", p.ID) + } + + if (p.End != nil && p.Start >= *p.End) || (p.Size != nil && *p.Size <= 0) { + return fmt.Errorf("partition's (%s) size can't be 0 or negative", p.ID) + } + + for _, f := range p.Flags { + err := f.IsValid() + if err != nil { + return err + } + } + + isESP := sliceutils.ContainsValue(p.Flags, PartitionFlagESP) + if isESP { + if p.FsType != FileSystemTypeFat32 { + return fmt.Errorf("ESP partition must have 'fat32' filesystem type") + } + } + + isBiosBoot := sliceutils.ContainsValue(p.Flags, PartitionFlagBiosGrub) + if isBiosBoot { + if p.Start != 1 { + return fmt.Errorf("BIOS boot partition must start at block 1") + } + + if p.FsType != FileSystemTypeFat32 { + return fmt.Errorf("BIOS boot partition must have 'fat32' filesystem type") + } + } + + return nil +} + +func (p *Partition) GetEnd() (uint64, bool) { + if p.End != nil { + return *p.End, true + } + + if p.Size != nil { + return p.Start + *p.Size, true + } + + return 0, false +} + +// isGPTNameValid checks if a GPT partition name is valid. +func isGPTNameValid(name string) error { + // The max partition name length is 36 UTF-16 code units, including a null terminator. + // Since we are also restricting the name to ASCII, this means 35 ASCII characters. + const maxLength = 35 + + // Restrict the name to only ASCII characters as some tools (e.g. parted) work better + // with only ASCII characters. + for _, char := range name { + if char > unicode.MaxASCII { + return fmt.Errorf("partition name (%s) contains a non-ASCII character (%c)", name, char) + } + } + + if len(name) > maxLength { + return fmt.Errorf("partition name (%s) is too long", name) + } + + return nil +} diff --git a/toolkit/tools/imagecustomizerapi/partition_test.go b/toolkit/tools/imagecustomizerapi/partition_test.go new file mode 100644 index 00000000000..63d5a935b80 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/partition_test.go @@ -0,0 +1,220 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "testing" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/ptrutils" + "github.com/stretchr/testify/assert" +) + +func TestPartitionIsValidExpanding(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + } + + err := partition.IsValid() + assert.NoError(t, err) +} + +func TestPartitionIsValidFixedSize(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: ptrutils.PtrTo(uint64(1)), + } + + err := partition.IsValid() + assert.NoError(t, err) +} + +func TestPartitionIsValidZeroSize(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: ptrutils.PtrTo(uint64(0)), + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "partition") + assert.ErrorContains(t, err, "size") +} + +func TestPartitionIsValidZeroSizeV2(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + Size: ptrutils.PtrTo(uint64(0)), + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "partition") + assert.ErrorContains(t, err, "size") +} + +func TestPartitionIsValidNegativeSize(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 2, + End: ptrutils.PtrTo(uint64(1)), + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "partition") + assert.ErrorContains(t, err, "size") +} + +func TestPartitionIsValidBothEndAndSize(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 2, + End: ptrutils.PtrTo(uint64(3)), + Size: ptrutils.PtrTo(uint64(1)), + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "End") + assert.ErrorContains(t, err, "Size") +} + +func TestPartitionIsValidGoodName(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: nil, + Name: "a", + } + + err := partition.IsValid() + assert.NoError(t, err) +} + +func TestPartitionIsValidNameTooLong(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: nil, + Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "name") + assert.ErrorContains(t, err, "too long") +} + +func TestPartitionIsValidNameNonASCII(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: nil, + Name: "❤️", + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "name") + assert.ErrorContains(t, err, "ASCII") +} + +func TestPartitionIsValidGoodFlag(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "fat32", + Start: 0, + End: nil, + Flags: []PartitionFlag{"esp"}, + } + + err := partition.IsValid() + assert.NoError(t, err) +} + +func TestPartitionIsValidBadFlag(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: nil, + Flags: []PartitionFlag{"a"}, + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "PartitionFlag") +} + +func TestPartitionIsValidUnsupportedFileSystem(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ntfs", + Start: 0, + End: nil, + Flags: []PartitionFlag{"a"}, + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "FileSystemType") +} + +func TestPartitionIsValidBadEspFsType(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 0, + End: nil, + Flags: []PartitionFlag{"esp"}, + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "ESP") + assert.ErrorContains(t, err, "fat32") +} + +func TestPartitionIsValidBadBiosBootFsType(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 1, + End: nil, + Flags: []PartitionFlag{"bios_grub"}, + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "BIOS boot") + assert.ErrorContains(t, err, "fat32") +} + +func TestPartitionIsValidBadBiosBootStart(t *testing.T) { + partition := Partition{ + ID: "a", + FsType: "ext4", + Start: 2, + End: nil, + Flags: []PartitionFlag{"bios_grub"}, + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "BIOS boot") + assert.ErrorContains(t, err, "start") +} diff --git a/toolkit/tools/imagecustomizerapi/partitionflag.go b/toolkit/tools/imagecustomizerapi/partitionflag.go new file mode 100644 index 00000000000..032287ac142 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/partitionflag.go @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "fmt" +) + +// PartitionFlag describes the features of a partition. +type PartitionFlag string + +const ( + // PartitionFlagEsp indicates this is a UEFI System Partition (ESP). + // + // On GPT disks, "boot" and "esp" must always be specified together. + PartitionFlagESP PartitionFlag = "esp" + + // PartitionFlagBiosGrub indicates this is the BIOS boot partition. + // This is required for GPT disks that wish to be bootable using legacy BIOS mode. + // This partition must start at block 1. + // + // See, https://en.wikipedia.org/wiki/BIOS_boot_partition + PartitionFlagBiosGrub PartitionFlag = "bios_grub" + + // PartitionFlagBoot indicates this is a boot partition. + // + // On GPT disks, "boot" and "esp" must always be specified together. + PartitionFlagBoot PartitionFlag = "boot" +) + +func (p PartitionFlag) IsValid() (err error) { + switch p { + case PartitionFlagBoot, PartitionFlagBiosGrub, PartitionFlagESP: + // All good. + return nil + + default: + return fmt.Errorf("unknown PartitionFlag value (%s)", p) + } +} diff --git a/toolkit/tools/imagecustomizerapi/partitionsetting.go b/toolkit/tools/imagecustomizerapi/partitionsetting.go new file mode 100644 index 00000000000..5bccc6bebf6 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/partitionsetting.go @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "fmt" + "path" +) + +// PartitionSetting holds the mounting information for each partition. +type PartitionSetting struct { + ID string `yaml:"ID"` + MountIdentifier MountIdentifierType `yaml:"MountIdentifier"` + MountOptions string `yaml:"MountOptions"` + MountPoint string `yaml:"MountPoint"` +} + +// IsValid returns an error if the PartitionSetting is not valid +func (p *PartitionSetting) IsValid() error { + err := p.MountIdentifier.IsValid() + if err != nil { + return err + } + + if p.MountPoint != "" && !path.IsAbs(p.MountPoint) { + return fmt.Errorf("MountPoint (%s) must be an absolute path", p.MountPoint) + } + + return nil +} diff --git a/toolkit/tools/imagecustomizerapi/partitionsetting_test.go b/toolkit/tools/imagecustomizerapi/partitionsetting_test.go new file mode 100644 index 00000000000..851c091c144 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/partitionsetting_test.go @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPartitionIsValidInvalidMountIdentifier(t *testing.T) { + partition := PartitionSetting{ + ID: "a", + MountIdentifier: "bad", + } + + err := partition.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "invalid") + assert.ErrorContains(t, err, "MountIdentifierType") +} diff --git a/toolkit/tools/imagecustomizerapi/partitiontabletype.go b/toolkit/tools/imagecustomizerapi/partitiontabletype.go new file mode 100644 index 00000000000..4f17d48a0d8 --- /dev/null +++ b/toolkit/tools/imagecustomizerapi/partitiontabletype.go @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerapi + +import ( + "fmt" +) + +// PartitionTableType is either gpt, mbr, or none +type PartitionTableType string + +const ( + PartitionTableTypeGpt PartitionTableType = "gpt" +) + +func (t PartitionTableType) IsValid() error { + switch t { + case PartitionTableTypeGpt: + // All good. + return nil + + default: + return fmt.Errorf("invalid PartitionTableType value (%s)", t) + } +} diff --git a/toolkit/tools/imagecustomizerapi/systemconfig.go b/toolkit/tools/imagecustomizerapi/systemconfig.go index 415161a0d85..e2ed6bedefb 100644 --- a/toolkit/tools/imagecustomizerapi/systemconfig.go +++ b/toolkit/tools/imagecustomizerapi/systemconfig.go @@ -12,6 +12,7 @@ import ( // SystemConfig defines how each system present on the image is supposed to be configured. type SystemConfig struct { + BootType BootType `yaml:"BootType"` Hostname string `yaml:"Hostname"` UpdateBaseImagePackages bool `yaml:"UpdateBaseImagePackages"` PackageListsInstall []string `yaml:"PackageListsInstall"` @@ -21,6 +22,7 @@ type SystemConfig struct { PackageListsUpdate []string `yaml:"PackageListsUpdate"` PackagesUpdate []string `yaml:"PackagesUpdate"` AdditionalFiles map[string]FileConfigList `yaml:"AdditionalFiles"` + PartitionSettings []PartitionSetting `yaml:"PartitionSettings"` PostInstallScripts []Script `yaml:"PostInstallScripts"` FinalizeImageScripts []Script `yaml:"FinalizeImageScripts"` Users []User `yaml:"Users"` @@ -31,6 +33,11 @@ type SystemConfig struct { func (s *SystemConfig) IsValid() error { var err error + err = s.BootType.IsValid() + if err != nil { + return err + } + if s.Hostname != "" { if !govalidator.IsDNSName(s.Hostname) || strings.Contains(s.Hostname, "_") { return fmt.Errorf("invalid hostname: %s", s.Hostname) @@ -44,6 +51,20 @@ func (s *SystemConfig) IsValid() error { } } + partitionIDSet := make(map[string]bool) + for i, partition := range s.PartitionSettings { + err = partition.IsValid() + if err != nil { + return fmt.Errorf("invalid PartitionSettings item at index %d: %w", i, err) + } + + if _, existingName := partitionIDSet[partition.ID]; existingName { + return fmt.Errorf("duplicate PartitionSettings ID used (%s) at index %d", partition.ID, i) + } + + partitionIDSet[partition.ID] = false // dummy value + } + for i, script := range s.PostInstallScripts { err = script.IsValid() if err != nil { diff --git a/toolkit/tools/imagecustomizerapi/systemconfig_test.go b/toolkit/tools/imagecustomizerapi/systemconfig_test.go index f08ac90eae1..cada72e43e7 100644 --- a/toolkit/tools/imagecustomizerapi/systemconfig_test.go +++ b/toolkit/tools/imagecustomizerapi/systemconfig_test.go @@ -5,6 +5,8 @@ package imagecustomizerapi import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestSystemConfigValidEmpty(t *testing.T) { @@ -22,3 +24,20 @@ func TestSystemConfigInvalidHostname(t *testing.T) { func TestSystemConfigInvalidAdditionalFiles(t *testing.T) { testInvalidYamlValue[*SystemConfig](t, "{ \"AdditionalFiles\": { \"a.txt\": [] } }") } + +func TestSystemConfigIsValidDuplicatePartitionID(t *testing.T) { + value := SystemConfig{ + PartitionSettings: []PartitionSetting{ + { + ID: "a", + }, + { + ID: "a", + }, + }, + } + + err := value.IsValid() + assert.Error(t, err) + assert.ErrorContains(t, err, "duplicate PartitionSettings ID") +} diff --git a/toolkit/tools/imagegen/diskutils/diskutils.go b/toolkit/tools/imagegen/diskutils/diskutils.go index 6f89e38b20e..9188d983a2a 100644 --- a/toolkit/tools/imagegen/diskutils/diskutils.go +++ b/toolkit/tools/imagegen/diskutils/diskutils.go @@ -241,6 +241,7 @@ func CreateSparseDisk(diskPath string, size uint64, perm os.FileMode) (err error // SetupLoopbackDevice creates a /dev/loop device for the given disk file func SetupLoopbackDevice(diskFilePath string) (devicePath string, err error) { + logger.Log.Debugf("Attaching Loopback: %v", diskFilePath) stdout, stderr, err := shell.Execute("losetup", "--show", "-f", "-P", diskFilePath) if err != nil { logger.Log.Warnf("Failed to create loopback device using losetup: %v", stderr) @@ -301,7 +302,7 @@ func BlockOnDiskIOByIds(debugName string, maj string, min string) (err error) { outstandingOpsIdx = 11 ) - logger.Log.Infof("Flushing all IO to disk") + logger.Log.Debugf("Flushing all IO to disk") _, _, err = shell.Execute("sync") if err != nil { return @@ -351,7 +352,7 @@ func BlockOnDiskIOByIds(debugName string, maj string, min string) (err error) { // DetachLoopbackDevice detaches the specified disk func DetachLoopbackDevice(diskDevPath string) (err error) { - logger.Log.Infof("Detaching Loopback Device Path: %v", diskDevPath) + logger.Log.Debugf("Detaching Loopback Device Path: %v", diskDevPath) _, stderr, err := shell.Execute("losetup", "-d", diskDevPath) if err != nil { logger.Log.Warnf("Failed to detach loopback device using losetup: %v", stderr) diff --git a/toolkit/tools/imagepkgfetcher/imagepkgfetcher.go b/toolkit/tools/imagepkgfetcher/imagepkgfetcher.go index d1bf565bfee..1ccec3c626e 100644 --- a/toolkit/tools/imagepkgfetcher/imagepkgfetcher.go +++ b/toolkit/tools/imagepkgfetcher/imagepkgfetcher.go @@ -160,7 +160,12 @@ func cloneSystemConfigs(cloner repocloner.RepoCloner, configFile, baseDirPath st if err != nil { // Fallback to legacy flow with multiple transactions in case we get a OOM error from a large transaction. logger.Log.Warnf("Failed to clone packages in a single transaction, will retry with individual transactions... (%s)", err) + logger.Log.Warnf("\tCheck log file '%s' for more details from package manager.", *logFile) _, err = cloner.CloneByPackageVer(cloneDeps, packageVersionsInConfig...) + if err != nil { + logger.Log.Errorf("Also failed to clone packages with individual transactions. Error: %s", err) + logger.Log.Errorf("\tCheck log file '%s' for more details from package manager.", *logFile) + } } return } diff --git a/toolkit/tools/internal/pkggraph/pkggraph.go b/toolkit/tools/internal/pkggraph/pkggraph.go index 65825ee8581..4336fee8630 100644 --- a/toolkit/tools/internal/pkggraph/pkggraph.go +++ b/toolkit/tools/internal/pkggraph/pkggraph.go @@ -608,6 +608,21 @@ func (g *PkgGraph) FindBestPkgNode(pkgVer *pkgjson.PackageVer) (lookupEntry *Loo return } +// HasNode returns true if pkgNode points to a node that is present in the graph. +// If the object is not the same, but the ID is the same, it will return false (i.e., the node is a copy) +func (g *PkgGraph) HasNode(pkgNode *PkgNode) bool { + if pkgNode == nil { + return false + } + nodeWithSameId := g.Node(pkgNode.ID()) + if nodeWithSameId == nil { + return false + } else { + // Check if they are the same node object + return nodeWithSameId.(*PkgNode) == pkgNode + } +} + // AllNodes returns a list of all nodes in the graph. func (g *PkgGraph) AllNodes() []*PkgNode { count := g.Nodes().Len() @@ -636,13 +651,9 @@ func (g *PkgGraph) AllNodesFrom(rootNode *PkgNode) []*PkgNode { // It traverses the graph and returns all nodes of type TypeLocalRun and // TypeRemoteRun. func (g *PkgGraph) AllRunNodes() []*PkgNode { - nodes := make([]*PkgNode, 0, g.Nodes().Len()) - for _, n := range g.AllNodes() { - if n.Type == TypeLocalRun || n.Type == TypeRemoteRun { - nodes = append(nodes, n) - } - } - return nodes + return g.NodesMatchingFilter(func(n *PkgNode) bool { + return n.Type == TypeLocalRun || n.Type == TypeRemoteRun + }) } // AllPreferredRunNodes returns all RunNodes in the LookupTable @@ -653,22 +664,38 @@ func (g *PkgGraph) AllRunNodes() []*PkgNode { // 3. LocalRun Node if both LocalRun and RemoteRun nodes are present in the graph // This function will return all RunNodes in the LookupTable. func (g *PkgGraph) AllPreferredRunNodes() []*PkgNode { - return g.allNodesOfType(func(n *LookupNode) *PkgNode { - return n.RunNode - }) + // We can estimate there will be ~1 run node per package. + foundNodes := 0 + nodes := make([]*PkgNode, 0, len(g.lookupTable())) + for _, versionList := range g.lookupTable() { + for _, n := range versionList { + if n.RunNode != nil { + nodes = append(nodes, n.RunNode) + foundNodes++ + } + } + } + return nodes[:foundNodes] } // AllBuildNodes returns a list of all build nodes in the graph func (g *PkgGraph) AllBuildNodes() []*PkgNode { - return g.allNodesOfType(func(n *LookupNode) *PkgNode { - return n.BuildNode + return g.NodesMatchingFilter(func(n *PkgNode) bool { + return n.Type == TypeLocalBuild }) } // AllTestNodes returns a list of all test nodes in the graph func (g *PkgGraph) AllTestNodes() []*PkgNode { - return g.allNodesOfType(func(n *LookupNode) *PkgNode { - return n.TestNode + return g.NodesMatchingFilter(func(n *PkgNode) bool { + return n.Type == TypeTest + }) +} + +// AllImplicitNodes returns a list of all implicit remote nodes in the graph +func (g *PkgGraph) AllImplicitNodes() []*PkgNode { + return g.NodesMatchingFilter(func(n *PkgNode) bool { + return n.Implicit }) } @@ -1101,19 +1128,76 @@ func (g *PkgGraph) AddGoalNodeWithExtraLayers(goalName string, packages, tests [ err = g.safeAddNode(goalNode) if err != nil { + err = fmt.Errorf("failed to add goal node '%s': %s", goalName, err.Error()) return } err = g.connectGoalEdges(goalNode, packagesGoalSet, strict, TypeLocalRun) if err != nil { + err = fmt.Errorf("failed to connect goal node '%s' to packages: %s", goalName, err.Error()) return } err = g.connectGoalEdges(goalNode, testsGoalSet, strict, TypeTest) if err != nil { + err = fmt.Errorf("failed to connect goal node '%s' to tests: %s", goalName, err.Error()) + return + } + + // Expand the goal node if requested + if extraLayers > 0 { + g.addGoalNodeLayers(goalNode, extraLayers) + } + + return +} + +// AddGoalNodeToNodes behaves similarly to AddGoalNodeWithExtraLayers, but instead of using a list of package versions (via +// graph lookup) to create the goal node, it uses a list of existing nodes in the graph. +// - goalName: The name of the goal node to add +// - existingNodes: A list of nodes to link the goal node to. +// - extraLayers: The number of levels to expand the goal node. Each level will add one more layer of packages beyond +// the goal node. For example, if the goal node is "x" and extraLevels is 1, the goal node will link to all nodes +// which depend on "x" as well as "x" itself (Specifically run nodes, all other nodes are stepped over) +func (g *PkgGraph) AddGoalNodeToNodes(goalName string, existingNodes []*PkgNode, extraLayers int) (goalNode *PkgNode, err error) { + // Check if we already have a goal node with the requested name + if g.FindGoalNode(goalName) != nil { + err = fmt.Errorf("can't have two goal nodes named %s", goalName) + return + } + + logger.Log.Debugf("Adding a goal node '%s'.", goalName) + + // Create goal node and add an edge to all the other requested nodes + goalNode = &PkgNode{ + State: StateMeta, + Type: TypeGoal, + SrpmPath: NoSRPMPath, + RpmPath: NoRPMPath, + SourceRepo: NoSourceRepo, + nodeID: g.NewNode().ID(), + GoalName: goalName, + } + goalNode.This = goalNode + + err = g.safeAddNode(goalNode) + if err != nil { + err = fmt.Errorf("failed to add goal node '%s': %s", goalName, err.Error()) return } + for _, node := range existingNodes { + if !g.HasNode(node) { + err = fmt.Errorf("can't add goal node '%s' from node '%s' which is not in the graph", goalName, node.FriendlyName()) + return nil, err + } + err = g.AddEdge(goalNode, node) + if err != nil { + err = fmt.Errorf("failed to add edge from goal node '%s' to node '%s': %s", goalName, node.FriendlyName(), err.Error()) + return nil, err + } + } + // Expand the goal node if requested if extraLayers > 0 { g.addGoalNodeLayers(goalNode, extraLayers) @@ -1363,25 +1447,6 @@ func (g *PkgGraph) CloneNode(pkgNode *PkgNode) (newNode *PkgNode) { return } -// allNodesOfType returns a list of all non-null nodes returned by the getter. -func (g *PkgGraph) allNodesOfType(nodeGetter func(node *LookupNode) *PkgNode) []*PkgNode { - count := 0 - for _, list := range g.lookupTable() { - count += len(list) - } - - nodes := make([]*PkgNode, 0, count) - for _, list := range g.lookupTable() { - for _, n := range list { - if node := nodeGetter(n); node != nil { - nodes = append(nodes, node) - } - } - } - - return nodes -} - // buildGoalSet returns a set of package versions that are the goal of the graph. func (g *PkgGraph) buildGoalSet(packageVers []*pkgjson.PackageVer, nodeType NodeType) (goalSet map[*pkgjson.PackageVer]bool) { if len(packageVers) > 0 { diff --git a/toolkit/tools/internal/pkggraph/pkggraph_test.go b/toolkit/tools/internal/pkggraph/pkggraph_test.go index bc0c3882150..f04401937be 100644 --- a/toolkit/tools/internal/pkggraph/pkggraph_test.go +++ b/toolkit/tools/internal/pkggraph/pkggraph_test.go @@ -132,6 +132,24 @@ func buildBuildNodeHelper(pkg *pkgjson.PackageVer) (node *PkgNode) { return } +// buildTestNodeHelper creates a new 'Test' PkgNode based on a PackageVer struct +func buildTestNodeHelper(pkg *pkgjson.PackageVer) (node *PkgNode) { + pkgCopy := *pkg + node = &PkgNode{ + VersionedPkg: &pkgCopy, + State: StateBuild, + Type: TypeTest, + SrpmPath: pkgCopy.Name + ".src.rpm", + RpmPath: pkgCopy.Name + ".rpm", + SpecPath: pkgCopy.Name + ".spec", + SourceDir: pkgCopy.Name + "/src/", + Architecture: "test_arch", + SourceRepo: "test_repo", + } + node.This = node + return +} + // buildBuildNode creates a new 'Unresolved' PkgNode based on a PackageVer struct func buildUnresolvedNodeHelper(pkg *pkgjson.PackageVer) (node *PkgNode) { pkgCopy := *pkg @@ -279,6 +297,30 @@ func checkTestGraph(t *testing.T, g *PkgGraph) { checkEqualComponents(t, component2, g.AllNodesFrom(c2.RunNode)) } +// Given a (possibly duplicated) node 'n', find the real node in the graph that corresponds to the package +func getRealNodeFromGraphHelper(t *testing.T, g *PkgGraph, n *PkgNode) (realN *PkgNode) { + t.Helper() + realNode, err := g.FindExactPkgNodeFromPkg(n.VersionedPkg) + assert.NoError(t, err) + assert.NotNil(t, realNode) + + switch n.Type { + case TypeLocalBuild: + realN = realNode.BuildNode + case TypeTest: + realN = realNode.TestNode + case TypeLocalRun: + fallthrough + case TypeRemoteRun: + realN = realNode.RunNode + default: + assert.Fail(t, "Unknown node type") + return nil + } + assert.NotNil(t, realN) + return +} + // Validate the test graph is well formed func TestCreateTestGraph(t *testing.T) { g, err := buildTestGraphHelper() @@ -761,6 +803,52 @@ func TestStrictGoalNodes(t *testing.T) { assert.Error(t, err) } +// Basic test of adding a goal node using two package node objects. +func TestGoalWithNodes(t *testing.T) { + g := NewPkgGraph() + err := addNodesHelper(g, allNodes) + assert.NoError(t, err) + assert.NotNil(t, g) + + nodeList := []*PkgNode{ + getRealNodeFromGraphHelper(t, g, pkgARun), + getRealNodeFromGraphHelper(t, g, pkgBRun), + } + + goal, err := g.AddGoalNodeToNodes("test", nodeList, 0) + assert.NoError(t, err) + assert.NotNil(t, goal) + assert.Equal(t, len(allNodes)+1, len(g.AllNodes())) + goalNodes := graph.NodesOf(g.From(goal.ID())) + assert.Equal(t, 2, len(goalNodes)) +} + +// Make sure we can't add a duplicate goal node with AddGoalNodeToNodes(). +func TestDuplicateGoalWithNodes(t *testing.T) { + g := NewPkgGraph() + goal, err := g.AddGoalNode("test", nil, nil, false) + assert.NoError(t, err) + assert.NotNil(t, goal) + assert.Equal(t, "test", goal.GoalName) + + _, err = g.AddGoalNodeToNodes("test", nil, 0) + assert.Error(t, err) +} + +// Ensure nodes that are outside the graph can't be added to a goal +func TestGoalWithNodeOutsideGraph(t *testing.T) { + g := NewPkgGraph() + err := addNodesHelper(g, allNodes) + assert.NoError(t, err) + assert.NotNil(t, g) + + nodeList := []*PkgNode{pkgARun, pkgBRun} + + goal, err := g.AddGoalNodeToNodes("test", nodeList, 0) + assert.Error(t, err) + assert.Nil(t, goal) +} + func TestGoalWithLevelZero(t *testing.T) { g, err := buildTestGraphHelper() assert.NoError(t, err) @@ -848,6 +936,41 @@ func TestGoalWithLevelTwo(t *testing.T) { checkEqualComponents(t, expectedGoalPackages, g.AllNodesFrom(goal)) } +// Check if AddGoalNodeToNodes() works with a levels +func TestGoalWithNodesWithLevelTwo(t *testing.T) { + g, err := buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + goalNode := getRealNodeFromGraphHelper(t, g, pkgCRun) + goal, err := g.AddGoalNodeToNodes("test_2", []*PkgNode{goalNode}, 2) + assert.NoError(t, err) + assert.NotNil(t, goal) + nodesInGoal := []*PkgNode{} + for _, n := range graph.NodesOf(g.From(goal.ID())) { + nodesInGoal = append(nodesInGoal, n.(*PkgNode)) + } + expectedGoalNodes := []*PkgNode{ + pkgARun, + pkgCRun, + pkgBRun, + } + checkEqualComponents(t, expectedGoalNodes, nodesInGoal) + expectedGoalPackages := []*PkgNode{ + pkgARun, + pkgABuild, + pkgBRun, + pkgBBuild, + pkgCRun, + pkgCBuild, + pkgD1Unresolved, + pkgD2Unresolved, + pkgD3Unresolved, + goal, + } + checkEqualComponents(t, expectedGoalPackages, g.AllNodesFrom(goal)) +} + // Add a meta node which should link the two disconnected graph components in the test graph func TestMetaNode(t *testing.T) { g, err := buildTestGraphHelper() @@ -1188,6 +1311,94 @@ func TestEncodingSubGraph(t *testing.T) { assert.Equal(t, len(component), len(gCopy.AllNodes())) } +func TestHasNode(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + assert.True(t, g.HasNode(n)) + assert.False(t, g.HasNode(pkgBRun)) +} + +func TestHasNodeNil(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + assert.False(t, g.HasNode(nil)) +} + +func TestHasNodeCopyGraph(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + // A deep copy should have different objects, and should return false + gCopy, err := g.DeepCopy() + assert.NoError(t, err) + assert.False(t, gCopy.HasNode(n)) +} + +func TestHasNodeCopyNode(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + // A deep copy should have different objects, and should return false + nCopy := n.Copy() + assert.False(t, g.HasNode(nCopy)) +} + +// Ensure that HasNode functions as expected when we create a subgraph. The subgraph should only "have" the nodes +// that make up the subgraph. +func TestHasNodeSubgraph(t *testing.T) { + g, err := buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + root, err := g.FindBestPkgNode(&pkgjson.PackageVer{Name: "B"}) + assert.NoError(t, err) + subGraph, err := g.CreateSubGraph(root.RunNode) + assert.NoError(t, err) + assert.NotNil(t, subGraph) + + inSubgraph := []*PkgNode{ + pkgBRun, + pkgBBuild, + pkgCRun, + pkgCBuild, + pkgD2Unresolved, + pkgD3Unresolved, + } + + outsideSubgraph := []*PkgNode{ + pkgARun, + pkgABuild, + pkgD1Unresolved, + pkgD4Unresolved, + pkgD5Unresolved, + pkgD6Unresolved, + pkgC2Run, + pkgC2Build, + } + + for _, n := range inSubgraph { + assert.True(t, subGraph.HasNode(getRealNodeFromGraphHelper(t, g, n))) + } + + for _, n := range outsideSubgraph { + assert.False(t, subGraph.HasNode(getRealNodeFromGraphHelper(t, g, n))) + } +} + func TestShouldSucceedMakeDAGWithGoalNode(t *testing.T) { gOut, err := buildTestGraphHelper() assert.NoError(t, err) @@ -1251,6 +1462,25 @@ func TestShouldGetSRPMNameFromEmptySRPMPath(t *testing.T) { assert.Equal(t, ".", node.SRPMFileName()) } +func TestAllBuildNodes(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + n, err = addNodeToGraphHelper(g, pkgABuild) + assert.NotNil(t, n) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{pkgABuild}, g.AllBuildNodes()) + + g, err = buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + checkEqualComponents(t, buildNodes, g.AllBuildNodes()) +} + func TestShouldGetAllBuildNodesWithFilter(t *testing.T) { gOut, err := buildTestGraphHelper() assert.NoError(t, err) @@ -1262,6 +1492,22 @@ func TestShouldGetAllBuildNodesWithFilter(t *testing.T) { checkEqualComponents(t, buildNodes, foundNodes) } +func TestAllNodes(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{pkgARun}, g.AllNodes()) + + g, err = buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + checkEqualComponents(t, allNodes, g.AllNodes()) +} + func TestShouldGetAllNodesWithFilter(t *testing.T) { gOut, err := buildTestGraphHelper() assert.NoError(t, err) @@ -1273,6 +1519,42 @@ func TestShouldGetAllNodesWithFilter(t *testing.T) { checkEqualComponents(t, allNodes, foundNodes) } +func TestAllRunNodes(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{pkgARun}, g.AllRunNodes()) + + g, err = buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + checkEqualComponents(t, append(runNodes, unresolvedNodes...), g.AllRunNodes()) +} + +func TestAllPreferredRunNodes(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{pkgARun}, g.AllRunNodes()) + checkEqualComponents(t, []*PkgNode{pkgARun}, g.AllPreferredRunNodes()) + + duplicateRemote, err := addNodeToGraphHelper(g, buildUnresolvedNodeHelper(&pkgA)) + assert.NotNil(t, duplicateRemote) + assert.NoError(t, err) + + // Duplicate node should show here + checkEqualComponents(t, []*PkgNode{pkgARun, duplicateRemote}, g.AllRunNodes()) + // But not here + checkEqualComponents(t, []*PkgNode{pkgARun}, g.AllPreferredRunNodes()) +} + func TestShouldGetAllRunNodesWithFilter(t *testing.T) { gOut, err := buildTestGraphHelper() assert.NoError(t, err) @@ -1294,3 +1576,57 @@ func TestShouldGetAllUnresolvedNodesWithFilter(t *testing.T) { }) checkEqualComponents(t, unresolvedNodes, foundNodes) } + +func TestAllTestNodes(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + testNode := buildTestNodeHelper(&pkgA) + + n, err = addNodeToGraphHelper(g, testNode) + assert.NotNil(t, n) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{testNode}, g.AllTestNodes()) + + g, err = buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + n, err = addNodeToGraphHelper(g, testNode) + assert.NotNil(t, n) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{testNode}, g.AllTestNodes()) +} + +func TestAllImplicitNodes(t *testing.T) { + g := NewPkgGraph() + assert.NotNil(t, g) + n, err := addNodeToGraphHelper(g, pkgARun) + assert.NotNil(t, n) + assert.NoError(t, err) + + implicitVersion := pkgjson.PackageVer{Name: "/path/to/implicit"} + implicitNode := buildUnresolvedNodeHelper(&implicitVersion) + + actualImplicitNode, err := addNodeToGraphHelper(g, implicitNode) + assert.NotNil(t, actualImplicitNode) + assert.NoError(t, err) + assert.True(t, actualImplicitNode.Implicit) + + checkEqualComponents(t, []*PkgNode{actualImplicitNode}, g.AllImplicitNodes()) + + g, err = buildTestGraphHelper() + assert.NoError(t, err) + assert.NotNil(t, g) + + actualImplicitNode, err = addNodeToGraphHelper(g, implicitNode) + assert.NotNil(t, actualImplicitNode) + assert.NoError(t, err) + + checkEqualComponents(t, []*PkgNode{actualImplicitNode}, g.AllImplicitNodes()) +} diff --git a/toolkit/tools/internal/rpm/rpm.go b/toolkit/tools/internal/rpm/rpm.go index c0ae71a7d13..ff22e350ff9 100644 --- a/toolkit/tools/internal/rpm/rpm.go +++ b/toolkit/tools/internal/rpm/rpm.go @@ -137,6 +137,25 @@ func SetMacroDir(newMacroDir string) (origenv []string, err error) { return } +// ExtractNameFromRPMPath strips the version from an RPM file name. i.e. pkg-name-1.2.3-4.cm2.x86_64.rpm -> pkg-name +func ExtractNameFromRPMPath(rpmFilePath string) (packageName string, err error) { + baseName := filepath.Base(rpmFilePath) + + // If the path is invalid, return empty string. We consider any string that has at least 1 '-' characters valid. + if !strings.Contains(baseName, "-") { + err = fmt.Errorf("invalid RPM file path '%s', can't extract name", rpmFilePath) + return + } + + rpmFileSplit := strings.Split(baseName, "-") + packageName = strings.Join(rpmFileSplit[:len(rpmFileSplit)-2], "-") + if packageName == "" { + err = fmt.Errorf("invalid RPM file path '%s', can't extract name", rpmFilePath) + return + } + return +} + // getCommonBuildArgs will generate arguments to pass to 'rpmbuild'. func getCommonBuildArgs(outArch, srpmFile string, defines map[string]string) (buildArgs []string, err error) { const ( diff --git a/toolkit/tools/internal/rpm/rpm_test.go b/toolkit/tools/internal/rpm/rpm_test.go index 2f7b41c3879..2fa1e96de45 100644 --- a/toolkit/tools/internal/rpm/rpm_test.go +++ b/toolkit/tools/internal/rpm/rpm_test.go @@ -151,3 +151,51 @@ func TestShouldNotFindCheckSectionInSpecWithoutCheckSection(t *testing.T) { assert.NoError(t, err) assert.False(t, hasCheckSection) } + +func TestExtractNameFromRPMPath(t *testing.T) { + tests := []struct { + name string + rpmFile string + expected string + err error + }{ + { + name: "valid rpm file", + rpmFile: "/path/to/pkg-1.0.0-1.noarch.rpm", + expected: "pkg", + err: nil, + }, + { + name: "valid rpm file with complex name", + rpmFile: "/path/to/pkg-name-1.0.0-1.noarch.rpm", + expected: "pkg-name", + err: nil, + }, + { + name: "invalid rpm file", + rpmFile: "/path/to/garbage.rpm", + expected: "", + err: fmt.Errorf("invalid RPM file path '%s', can't extract name", "/path/to/garbage.rpm"), + }, + { + name: "empty rpm file", + rpmFile: "", + expected: "", + err: fmt.Errorf("invalid RPM file path '%s', can't extract name", ""), + }, + { + name: "just a hyphen", + rpmFile: "-", + expected: "", + err: fmt.Errorf("invalid RPM file path '%s', can't extract name", "-"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual, err := ExtractNameFromRPMPath(tt.rpmFile) + assert.Equal(t, tt.err, err) + assert.Equal(t, tt.expected, actual) + }) + } +} diff --git a/toolkit/tools/internal/shell/shell.go b/toolkit/tools/internal/shell/shell.go index c4281c547d1..21d0244a423 100644 --- a/toolkit/tools/internal/shell/shell.go +++ b/toolkit/tools/internal/shell/shell.go @@ -141,6 +141,30 @@ func ExecuteLive(squashErrors bool, program string, args ...string) (err error) return ExecuteLiveWithCallback(onStdout, onStderr, false, program, args...) } +// ExecuteLiveWithErr runs a command in the shell and logs it in real-time. +// In addition, if there is an error, the last x lines of stderr will be attached to the err object. +func ExecuteLiveWithErr(stderrLines int, program string, args ...string) (err error) { + stderrChan := make(chan string, stderrLines) + + err = ExecuteLiveWithCallbackAndChannels(logger.Log.Debug, logger.Log.Debug, nil, stderrChan, program, args...) + close(stderrChan) + if err != nil { + errLines := "" + for errLine := range stderrChan { + if errLines != "" { + errLines += "\n" + } + errLines += errLine + } + + if errLines != "" { + err = fmt.Errorf("%s\n%w", errLines, err) + } + return + } + return nil +} + // ExecuteLiveWithCallback runs a command in the shell and invokes the provided callbacks in real-time on each line of stdout and stderr. // If printOutputOnError is true, the full output of the command will be printed after completion if the command returns an error. In the event // the buffer becomes full the oldest buffered output is discarded. @@ -148,6 +172,33 @@ func ExecuteLiveWithCallback(onStdout, onStderr func(...interface{}), printOutpu var outputChan chan string const outputChanBufferSize = 1500 + if printOutputOnError { + outputChan = make(chan string, outputChanBufferSize) + } + + err = ExecuteLiveWithCallbackAndChannels(onStdout, onStderr, outputChan, outputChan, program, args...) + if err != nil { + return + } + + // Optionally dump the output in the event of an error + if outputChan != nil { + close(outputChan) + } + if err != nil && printOutputOnError { + logger.Log.Errorf("Call to %s returned error, last %d lines of output:", program, outputChanBufferSize) + for line := range outputChan { + logger.Log.Warn(line) + } + } + + return +} + +func ExecuteLiveWithCallbackAndChannels(onStdout, onStderr func(...interface{}), + stdoutChannel, stderrChannel chan string, + program string, args ...string, +) (err error) { cmd := exec.Command(program, args...) stdoutPipe, err := cmd.StdoutPipe() @@ -174,26 +225,12 @@ func ExecuteLiveWithCallback(onStdout, onStderr func(...interface{}), printOutpu wg := new(sync.WaitGroup) wg.Add(2) - if printOutputOnError { - outputChan = make(chan string, outputChanBufferSize) - } - go logger.StreamOutput(stdoutPipe, onStdout, wg, outputChan) - go logger.StreamOutput(stderrPipe, onStderr, wg, outputChan) + go logger.StreamOutput(stdoutPipe, onStdout, wg, stdoutChannel) + go logger.StreamOutput(stderrPipe, onStderr, wg, stderrChannel) wg.Wait() err = cmd.Wait() - // Optionally dump the output in the event of an error - if outputChan != nil { - close(outputChan) - } - if err != nil && printOutputOnError { - logger.Log.Errorf("Call to %s returned error, last %d lines of output:", cmd.Args, outputChanBufferSize) - for line := range outputChan { - logger.Log.Warn(line) - } - } - return } diff --git a/toolkit/tools/internal/sliceutils/sliceutils.go b/toolkit/tools/internal/sliceutils/sliceutils.go index 0d5fdc83287..c9612153adb 100644 --- a/toolkit/tools/internal/sliceutils/sliceutils.go +++ b/toolkit/tools/internal/sliceutils/sliceutils.go @@ -89,3 +89,23 @@ func RemoveDuplicatesFromSlice[K comparable](inputSlice []K) (outputSlice []K) { func nilCheck(expected interface{}, given interface{}) (checkValid, checkResult bool) { return (expected == nil || given == nil), (expected == nil && given == nil) } + +// Can be replaced by slices.Contains in Go 1.21. +func ContainsValue[K comparable](inputSlice []K, value K) bool { + for _, item := range inputSlice { + if item == value { + return true + } + } + return false +} + +// Can be replaced by slices.ContainsFunc in Go 1.21. +func ContainsFunc[K any](inputSlice []K, fn func(K) bool) bool { + for _, item := range inputSlice { + if fn(item) { + return true + } + } + return false +} diff --git a/toolkit/tools/internal/timestamp/timestamp_mgr.go b/toolkit/tools/internal/timestamp/timestamp_mgr.go index 3675e581759..906beb8d9e5 100644 --- a/toolkit/tools/internal/timestamp/timestamp_mgr.go +++ b/toolkit/tools/internal/timestamp/timestamp_mgr.go @@ -138,7 +138,7 @@ func ensureManagerExists() error { func BeginTiming(toolName, outputFile string) (*TimeStamp, error) { if outputFile == "" { err := fmt.Errorf("timestamp output file is not specified, the feature will be turned off for %s", toolName) - logger.Log.Info(err.Error()) + logger.Log.Debug(err.Error()) return &TimeStamp{}, err } @@ -171,7 +171,7 @@ func BeginTiming(toolName, outputFile string) (*TimeStamp, error) { func ResumeTiming(toolName, outputFile string) error { if outputFile == "" { err := fmt.Errorf("timestamp output file is not specified, the feature will be turned off for %s", toolName) - logger.Log.Info(err.Error()) + logger.Log.Debug(err.Error()) return err } diff --git a/toolkit/tools/pkg/imagecustomizerlib/customizepackages.go b/toolkit/tools/pkg/imagecustomizerlib/customizepackages.go index 653a0844ff9..18e5010840d 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/customizepackages.go +++ b/toolkit/tools/pkg/imagecustomizerlib/customizepackages.go @@ -17,8 +17,6 @@ import ( func addRemoveAndUpdatePackages(buildDir string, baseConfigPath string, config *imagecustomizerapi.SystemConfig, imageChroot *safechroot.Chroot, rpmsSources []string, useBaseImageRpmRepos bool, ) error { - var err error - allPackagesRemove, err := collectPackagesList(baseConfigPath, config.PackageListsRemove, config.PackagesRemove) if err != nil { return err @@ -62,11 +60,13 @@ func addRemoveAndUpdatePackages(buildDir string, baseConfigPath string, config * } } + logger.Log.Infof("Installing packages: %v", allPackagesInstall) err = installOrUpdatePackages("install", allPackagesInstall, imageChroot) if err != nil { return err } + logger.Log.Infof("Updating packages: %v", allPackagesUpdate) err = installOrUpdatePackages("update", allPackagesUpdate, imageChroot) if err != nil { return err @@ -105,7 +105,7 @@ func collectPackagesList(baseConfigPath string, packageLists []string, packages } func removePackages(allPackagesToRemove []string, imageChroot *safechroot.Chroot) error { - var err error + logger.Log.Infof("Removing packages: %v", allPackagesToRemove) tnfRemoveArgs := []string{ "-v", "remove", "--assumeyes", "--disablerepo", "*", @@ -118,10 +118,9 @@ func removePackages(allPackagesToRemove []string, imageChroot *safechroot.Chroot for _, packageName := range allPackagesToRemove { tnfRemoveArgs[len(tnfRemoveArgs)-1] = packageName - err = imageChroot.Run(func() error { - err := shell.ExecuteLiveWithCallback(tdnfRemoveStdoutFilter, logger.Log.Warn, false, "tdnf", + err := imageChroot.Run(func() error { + return shell.ExecuteLiveWithCallback(tdnfRemoveStdoutFilter, logger.Log.Debug, false, "tdnf", tnfRemoveArgs...) - return err }) if err != nil { return fmt.Errorf("failed to remove package (%s):\n%w", packageName, err) @@ -148,17 +147,16 @@ func tdnfRemoveStdoutFilter(args ...interface{}) { } func updateAllPackages(imageChroot *safechroot.Chroot) error { - var err error + logger.Log.Infof("Updating base image packages") tnfUpdateArgs := []string{ "-v", "update", "--nogpgcheck", "--assumeyes", "--setopt", fmt.Sprintf("reposdir=%s", rpmsMountParentDirInChroot), } - err = imageChroot.Run(func() error { - err := shell.ExecuteLiveWithCallback(tdnfInstallOrUpdateStdoutFilter, logger.Log.Warn, false, "tdnf", + err := imageChroot.Run(func() error { + return shell.ExecuteLiveWithCallback(tdnfInstallOrUpdateStdoutFilter, logger.Log.Debug, false, "tdnf", tnfUpdateArgs...) - return err }) if err != nil { return fmt.Errorf("failed to update packages:\n%w", err) @@ -168,8 +166,6 @@ func updateAllPackages(imageChroot *safechroot.Chroot) error { } func installOrUpdatePackages(action string, allPackagesToAdd []string, imageChroot *safechroot.Chroot) error { - var err error - // Create tdnf command args. // Note: When using `--repofromdir`, tdnf will not use any default repos and will only use the last // `--repofromdir` specified. @@ -185,10 +181,9 @@ func installOrUpdatePackages(action string, allPackagesToAdd []string, imageChro for _, packageName := range allPackagesToAdd { tnfInstallArgs[len(tnfInstallArgs)-1] = packageName - err = imageChroot.Run(func() error { - err := shell.ExecuteLiveWithCallback(tdnfInstallOrUpdateStdoutFilter, logger.Log.Warn, false, "tdnf", + err := imageChroot.Run(func() error { + return shell.ExecuteLiveWithCallback(tdnfInstallOrUpdateStdoutFilter, logger.Log.Debug, false, "tdnf", tnfInstallArgs...) - return err }) if err != nil { return fmt.Errorf("failed to %s package (%s):\n%w", action, packageName, err) diff --git a/toolkit/tools/pkg/imagecustomizerlib/customizepartitions.go b/toolkit/tools/pkg/imagecustomizerlib/customizepartitions.go new file mode 100644 index 00000000000..f909e5eb812 --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/customizepartitions.go @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerlib + +import ( + "path/filepath" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" +) + +func customizePartitions(buildDir string, baseConfigPath string, config *imagecustomizerapi.Config, + buildImageFile string, +) (string, error) { + if config.Disks == nil && config.SystemConfig.BootType == imagecustomizerapi.BootTypeUnset { + // No changes to make to the partitions. + // So, just use the original disk. + return buildImageFile, nil + } + + newBuildImageFile := filepath.Join(buildDir, PartitionCustomizedImageName) + + // If there is no known way to create the new partition layout from the old one, + // then fallback to creating the new partitions from scratch and doing a file copy. + err := customizePartitionsUsingFileCopy(buildDir, baseConfigPath, config, buildImageFile, newBuildImageFile) + if err != nil { + return "", err + } + + return newBuildImageFile, nil +} diff --git a/toolkit/tools/pkg/imagecustomizerlib/customizepartitionsfilecopy.go b/toolkit/tools/pkg/imagecustomizerlib/customizepartitionsfilecopy.go new file mode 100644 index 00000000000..e7bb64e0919 --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/customizepartitionsfilecopy.go @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerlib + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/logger" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/shell" +) + +func customizePartitionsUsingFileCopy(buildDir string, baseConfigPath string, config *imagecustomizerapi.Config, + buildImageFile string, newBuildImageFile string, +) error { + existingImageConnection, err := connectToExistingImage(buildImageFile, buildDir, "imageroot") + if err != nil { + return err + } + defer existingImageConnection.Close() + + diskConfig := (*config.Disks)[0] + + installOSFunc := func(imageChroot *safechroot.Chroot) error { + return copyFilesIntoNewDisk(existingImageConnection.Chroot(), imageChroot) + } + + newImageConnection, err := createNewImage(newBuildImageFile, diskConfig, config.SystemConfig.PartitionSettings, + config.SystemConfig.BootType, buildDir, "newimageroot", installOSFunc) + if err != nil { + return err + } + defer newImageConnection.Close() + + err = newImageConnection.CleanClose() + if err != nil { + return err + } + + err = existingImageConnection.CleanClose() + if err != nil { + return err + } + + return nil +} + +func copyFilesIntoNewDisk(existingImageChroot *safechroot.Chroot, newImageChroot *safechroot.Chroot) error { + err := copyFilesIntoNewDiskHelper(existingImageChroot, newImageChroot) + if err != nil { + return fmt.Errorf("failed to copy files into new partition layout:\n%w", err) + } + return nil +} + +func copyFilesIntoNewDiskHelper(existingImageChroot *safechroot.Chroot, newImageChroot *safechroot.Chroot) error { + // Notes: + // `-a` ensures unix permissions, extended attributes (including SELinux), and sub-directories (-r) are copied. + // `--no-dereference` ensures that symlinks are copied as symlinks. + copyArgs := []string{"--verbose", "--no-clobber", "-a", "--no-dereference", "--sparse", "always", "-t", newImageChroot.RootDir()} + + files, err := os.ReadDir(existingImageChroot.RootDir()) + if err != nil { + return fmt.Errorf("failed to read base image root directory:\n%w", err) + } + + for _, file := range files { + switch file.Name() { + case "dev", "proc", "sys", "run", "tmp": + // Exclude special directories. + // + // Note: Under /var, there are symlinks to a couple of these special directories. + // However, the `cp` command is called with `--no-dereference`. So, the symlinks will be copied as symlinks. + continue + } + + fullFileName := filepath.Join(existingImageChroot.RootDir(), file.Name()) + copyArgs = append(copyArgs, fullFileName) + } + + err = shell.ExecuteLiveWithCallback(func(...interface{}) {}, logger.Log.Warn, false, + "cp", copyArgs...) + if err != nil { + return fmt.Errorf("failed to copy files:\n%w", err) + } + + return nil +} diff --git a/toolkit/tools/pkg/imagecustomizerlib/customizeutils.go b/toolkit/tools/pkg/imagecustomizerlib/customizeutils.go index 9f57d54b1ca..03fef5f832a 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/customizeutils.go +++ b/toolkit/tools/pkg/imagecustomizerlib/customizeutils.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "strconv" + "time" "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/installutils" @@ -34,6 +35,8 @@ func doCustomizations(buildDir string, baseConfigPath string, config *imagecusto // Note: The ordering of the customization steps here should try to mirror the order of the equivalent steps in imager // tool as closely as possible. + buildTime := time.Now().Format("2006-01-02T15:04:05Z") + err = overrideResolvConf(imageChroot) if err != nil { return err @@ -69,6 +72,11 @@ func doCustomizations(buildDir string, baseConfigPath string, config *imagecusto return err } + err = addCustomizerRelease(imageChroot, ToolVersion, buildTime) + if err != nil { + return err + } + err = runScripts(baseConfigPath, config.SystemConfig.PostInstallScripts, imageChroot) if err != nil { return err @@ -127,14 +135,14 @@ func deleteResolvConf(imageChroot *safechroot.Chroot) error { } func updateHostname(hostname string, imageChroot *safechroot.Chroot) error { - var err error - if hostname == "" { return nil } + logger.Log.Infof("Setting hostname (%s)", hostname) + hostnameFilePath := filepath.Join(imageChroot.RootDir(), "etc/hostname") - err = file.Write(hostname, hostnameFilePath) + err := file.Write(hostname, hostnameFilePath) if err != nil { return fmt.Errorf("failed to write hostname file: %w", err) } @@ -143,17 +151,17 @@ func updateHostname(hostname string, imageChroot *safechroot.Chroot) error { } func copyAdditionalFiles(baseConfigPath string, additionalFiles map[string]imagecustomizerapi.FileConfigList, imageChroot *safechroot.Chroot) error { - var err error - for sourceFile, fileConfigs := range additionalFiles { for _, fileConfig := range fileConfigs { + logger.Log.Infof("Copying: %s", fileConfig.Path) + fileToCopy := safechroot.FileToCopy{ Src: filepath.Join(baseConfigPath, sourceFile), Dest: fileConfig.Path, Permissions: (*fs.FileMode)(fileConfig.Permissions), } - err = imageChroot.AddFiles(fileToCopy) + err := imageChroot.AddFiles(fileToCopy) if err != nil { return err } @@ -180,18 +188,14 @@ func runScripts(baseConfigPath string, scripts []imagecustomizerapi.Script, imag for _, script := range scripts { scriptPathInChroot := filepath.Join(configDirMountPathInChroot, script.Path) command := fmt.Sprintf("%s %s", scriptPathInChroot, script.Args) + logger.Log.Infof("Running script (%s)", script.Path) // Run the script. err = imageChroot.UnsafeRun(func() error { - err := shell.ExecuteLive(false, shell.ShellProgram, "-c", command) - if err != nil { - return err - } - - return nil + return shell.ExecuteLiveWithErr(1, shell.ShellProgram, "-c", command) }) if err != nil { - return err + return fmt.Errorf("script (%s) failed:\n%w", script.Path, err) } } @@ -303,15 +307,10 @@ func enableOrDisableServices(services imagecustomizerapi.Services, imageChroot * logger.Log.Infof("Enabling service (%s)", service.Name) err = imageChroot.UnsafeRun(func() error { - err := shell.ExecuteLive(false, "systemctl", "enable", service.Name) - if err != nil { - return fmt.Errorf("failed to enable service (%s): \n%w", service.Name, err) - } - - return nil + return shell.ExecuteLiveWithErr(1, "systemctl", "enable", service.Name) }) if err != nil { - return err + return fmt.Errorf("failed to enable service (%s):\n%w", service.Name, err) } } @@ -320,15 +319,10 @@ func enableOrDisableServices(services imagecustomizerapi.Services, imageChroot * logger.Log.Infof("Disabling service (%s)", service.Name) err = imageChroot.UnsafeRun(func() error { - err := shell.ExecuteLive(false, "systemctl", "disable", service.Name) - if err != nil { - return fmt.Errorf("failed to disable service (%s): %w", service.Name, err) - } - - return nil + return shell.ExecuteLiveWithErr(1, "systemctl", "disable", service.Name) }) if err != nil { - return err + return fmt.Errorf("failed to disable service (%s):\n%w", service.Name, err) } } @@ -361,3 +355,23 @@ func loadOrDisableModules(modules imagecustomizerapi.Modules, imageChroot *safec return nil } + +func addCustomizerRelease(imageChroot *safechroot.Chroot, toolVersion string, buildTime string) error { + var err error + + logger.Log.Infof("Creating image customizer release file") + + customizerReleaseFilePath := filepath.Join(imageChroot.RootDir(), "/etc/mariner-customizer-release") + lines := []string{ + fmt.Sprintf("%s=\"%s\"", "TOOL_VERSION", toolVersion), + fmt.Sprintf("%s=\"%s\"", "BUILD_DATE", buildTime), + "", + } + + err = file.WriteLines(lines, customizerReleaseFilePath) + if err != nil { + return fmt.Errorf("error writing customizer release file (%s): %w", customizerReleaseFilePath, err) + } + + return nil +} diff --git a/toolkit/tools/pkg/imagecustomizerlib/customizeutils_test.go b/toolkit/tools/pkg/imagecustomizerlib/customizeutils_test.go index 9414ca87b52..62871d513fe 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/customizeutils_test.go +++ b/toolkit/tools/pkg/imagecustomizerlib/customizeutils_test.go @@ -4,9 +4,12 @@ package imagecustomizerlib import ( + "bufio" "os" "path/filepath" + "strings" "testing" + "time" "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/ptrutils" @@ -98,3 +101,47 @@ func TestCopyAdditionalFiles(t *testing.T) { assert.Equal(t, orig_contents, copy_1_contents) assert.Equal(t, orig_contents, copy_2_contents) } + +func TestAddCustomizerRelease(t *testing.T) { + if os.Geteuid() != 0 { + t.Skip("Test must be run as root because it uses a chroot") + } + + proposedDir := filepath.Join(tmpDir, "TestAddCustomizerRelease") + chroot := safechroot.NewChroot(proposedDir, false) + err := chroot.Initialize("", []string{}, []*safechroot.MountPoint{}) + assert.NoError(t, err) + defer chroot.Close(false) + + err = os.MkdirAll(filepath.Join(chroot.RootDir(), "etc"), os.ModePerm) + assert.NoError(t, err) + + expectedVersion := "0.1.0" + expectedDate := time.Now().Format("2006-01-02T15:04:05Z") + err = addCustomizerRelease(chroot, expectedVersion, expectedDate) + assert.NoError(t, err) + + releaseFilePath := filepath.Join(chroot.RootDir(), "etc/mariner-customizer-release") + + file, err := os.Open(releaseFilePath) + if err != nil { + t.Fatalf("Failed to open file: %v", err) + } + defer file.Close() + + scanner := bufio.NewScanner(file) + config := make(map[string]string) + for scanner.Scan() { + line := scanner.Text() + if line == "" { + continue + } + parts := strings.Split(line, "=") + key := parts[0] + value := strings.Trim(parts[1], "\"") + config[key] = value + } + + assert.Equal(t, expectedVersion, config["TOOL_VERSION"]) + assert.Equal(t, expectedDate, config["BUILD_DATE"]) +} diff --git a/toolkit/tools/pkg/imagecustomizerlib/imageConnection.go b/toolkit/tools/pkg/imagecustomizerlib/imageConnection.go new file mode 100644 index 00000000000..d6e31e12ba2 --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/imageConnection.go @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerlib + +import ( + "fmt" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safeloopback" +) + +type ImageConnection struct { + loopback *safeloopback.Loopback + chroot *safechroot.Chroot + chrootIsExistingDir bool +} + +func NewImageConnection() *ImageConnection { + return &ImageConnection{} +} + +func (c *ImageConnection) ConnectLoopback(diskFilePath string) error { + if c.loopback != nil { + return fmt.Errorf("loopback already connected") + } + + loopback, err := safeloopback.NewLoopback(diskFilePath) + if err != nil { + return fmt.Errorf("failed to mount raw disk (%s) as a loopback device:\n%w", diskFilePath, err) + } + c.loopback = loopback + return nil +} + +func (c *ImageConnection) ConnectChroot(rootDir string, isExistingDir bool, extraDirectories []string, + extraMountPoints []*safechroot.MountPoint, +) error { + if c.chroot != nil { + return fmt.Errorf("chroot already connected") + } + + chroot := safechroot.NewChroot(rootDir, isExistingDir) + err := chroot.Initialize("", extraDirectories, extraMountPoints) + if err != nil { + return err + } + c.chroot = chroot + c.chrootIsExistingDir = isExistingDir + + return nil +} + +func (c *ImageConnection) Chroot() *safechroot.Chroot { + return c.chroot +} + +func (c *ImageConnection) Loopback() *safeloopback.Loopback { + return c.loopback +} + +func (c *ImageConnection) Close() { + if c.chroot != nil { + c.chroot.Close(c.chrootIsExistingDir) + } + + if c.loopback != nil { + c.loopback.Close() + } +} + +func (c *ImageConnection) CleanClose() error { + err := c.chroot.Close(c.chrootIsExistingDir) + if err != nil { + return err + } + + err = c.loopback.CleanClose() + if err != nil { + return err + } + + return nil +} diff --git a/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go b/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go index bd72cdeee58..474772759be 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go +++ b/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer.go @@ -10,13 +10,15 @@ import ( "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/file" - "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot" - "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safeloopback" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/logger" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/shell" ) const ( tmpParitionDirName = "tmppartition" + + BaseImageName = "image.raw" + PartitionCustomizedImageName = "image2.raw" ) var ( @@ -83,13 +85,20 @@ func CustomizeImage(buildDir string, baseConfigPath string, config *imagecustomi } // Convert image file to raw format, so that a kernel loop device can be used to make changes to the image. - buildImageFile := filepath.Join(buildDirAbs, "image.raw") + buildImageFile := filepath.Join(buildDirAbs, BaseImageName) - _, _, err = shell.Execute("qemu-img", "convert", "-O", "raw", imageFile, buildImageFile) + logger.Log.Infof("Mounting base image: %s", buildImageFile) + err = shell.ExecuteLiveWithErr(1, "qemu-img", "convert", "-O", "raw", imageFile, buildImageFile) if err != nil { return fmt.Errorf("failed to convert image file to raw format:\n%w", err) } + // Customize the partitions. + buildImageFile, err = customizePartitions(buildDirAbs, baseConfigPath, config, buildImageFile) + if err != nil { + return err + } + // Customize the raw image file. err = customizeImageHelper(buildDirAbs, baseConfigPath, config, buildImageFile, rpmsSources, useBaseImageRpmRepos) if err != nil { @@ -97,14 +106,18 @@ func CustomizeImage(buildDir string, baseConfigPath string, config *imagecustomi } // Create final output image file. + logger.Log.Infof("Writing: %s", outputImageFile) + outDir := filepath.Dir(outputImageFile) os.MkdirAll(outDir, os.ModePerm) - _, _, err = shell.Execute("qemu-img", "convert", "-O", qemuOutputImageFormat, buildImageFile, outputImageFile) + err = shell.ExecuteLiveWithErr(1, "qemu-img", "convert", "-O", qemuOutputImageFormat, buildImageFile, outputImageFile) if err != nil { return fmt.Errorf("failed to convert image file to format: %s:\n%w", outputImageFormat, err) } + logger.Log.Infof("Success!") + return nil } @@ -190,43 +203,19 @@ func validateScript(baseConfigPath string, script *imagecustomizerapi.Script) er func customizeImageHelper(buildDir string, baseConfigPath string, config *imagecustomizerapi.Config, buildImageFile string, rpmsSources []string, useBaseImageRpmRepos bool, ) error { - // Mount the raw disk image file. - loopback, err := safeloopback.NewLoopback(buildImageFile) - if err != nil { - return fmt.Errorf("failed to mount raw disk (%s) as a loopback device:\n%w", buildImageFile, err) - } - defer loopback.Close() - - // Look for all the partitions on the image. - newMountDirectories, mountPoints, err := findPartitions(buildDir, loopback.DevicePath()) - if err != nil { - return fmt.Errorf("failed to find disk partitions:\n%w", err) - } - - // Create chroot environment. - imageChrootDir := filepath.Join(buildDir, "imageroot") - - chrootLeaveOnDisk := false - imageChroot := safechroot.NewChroot(imageChrootDir, chrootLeaveOnDisk) - err = imageChroot.Initialize("", newMountDirectories, mountPoints) + imageConnection, err := connectToExistingImage(buildImageFile, buildDir, "imageroot") if err != nil { return err } - defer imageChroot.Close(chrootLeaveOnDisk) + defer imageConnection.Close() // Do the actual customizations. - err = doCustomizations(buildDir, baseConfigPath, config, imageChroot, rpmsSources, useBaseImageRpmRepos) - if err != nil { - return err - } - - // Close. - err = imageChroot.Close(chrootLeaveOnDisk) + err = doCustomizations(buildDir, baseConfigPath, config, imageConnection.Chroot(), rpmsSources, useBaseImageRpmRepos) if err != nil { return err } - err = loopback.CleanClose() + err = imageConnection.CleanClose() if err != nil { return err } diff --git a/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer_test.go b/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer_test.go index 723616a948e..dbcb31523b4 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer_test.go +++ b/toolkit/tools/pkg/imagecustomizerlib/imagecustomizer_test.go @@ -11,10 +11,8 @@ import ( "testing" "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" - "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/configuration" - "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/diskutils" - "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/installutils" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/buildpipeline" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/ptrutils" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safeloopback" "github.com/stretchr/testify/assert" @@ -206,130 +204,55 @@ func createFakeEfiImage(buildDir string) (string, error) { } // Use a prototypical Mariner image partition config. - diskConfig := configuration.Disk{ - PartitionTableType: configuration.PartitionTableTypeGpt, + diskConfig := imagecustomizerapi.Disk{ + PartitionTableType: imagecustomizerapi.PartitionTableTypeGpt, MaxSize: 4096, - Partitions: []configuration.Partition{ + Partitions: []imagecustomizerapi.Partition{ { ID: "boot", - Flags: []configuration.PartitionFlag{"esp", "boot"}, + Flags: []imagecustomizerapi.PartitionFlag{"esp", "boot"}, Start: 1, - End: 9, + End: ptrutils.PtrTo(uint64(9)), FsType: "fat32", }, { ID: "rootfs", Start: 9, - End: 0, + End: nil, FsType: "ext4", }, }, } - partitionSettings := []configuration.PartitionSetting{ + partitionSettings := []imagecustomizerapi.PartitionSetting{ { ID: "boot", MountPoint: "/boot/efi", MountOptions: "umask=0077", - MountIdentifier: configuration.MountIdentifierDefault, + MountIdentifier: imagecustomizerapi.MountIdentifierTypeDefault, }, { ID: "rootfs", MountPoint: "/", - MountIdentifier: configuration.MountIdentifierDefault, + MountIdentifier: imagecustomizerapi.MountIdentifierTypeDefault, }, } - // Create raw disk image file. - rawDisk, err := diskutils.CreateEmptyDisk(buildDir, "disk.raw", diskConfig.MaxSize) - if err != nil { - return "", fmt.Errorf("failed to create empty disk file in (%s):\n%w", buildDir, err) - } - - // Connect raw disk image file. - loopback, err := safeloopback.NewLoopback(rawDisk) - if err != nil { - return "", fmt.Errorf("failed to mount raw disk (%s) as a loopback device:\n%w", rawDisk, err) - } - defer loopback.Close() - - // Set up partitions. - partIDToDevPathMap, partIDToFsTypeMap, _, _, err := diskutils.CreatePartitions(loopback.DevicePath(), diskConfig, - configuration.RootEncryption{}, configuration.ReadOnlyVerityRoot{}) - if err != nil { - return "", fmt.Errorf("failed to create partitions on disk (%s):\n%w", loopback.DevicePath(), err) - } - - // Create partition mount config. - bootPartitionDevPath := fmt.Sprintf("%sp1", loopback.DevicePath()) - osPartitionDevPath := fmt.Sprintf("%sp2", loopback.DevicePath()) - - newMountDirectories := []string{} - mountPoints := []*safechroot.MountPoint{ - safechroot.NewPreDefaultsMountPoint(osPartitionDevPath, "/", "ext4", 0, ""), - safechroot.NewMountPoint(bootPartitionDevPath, "/boot/efi", "vfat", 0, ""), - } - - // Mount the partitions. - chrootLeaveOnDisk := false - imageChroot := safechroot.NewChroot(filepath.Join(buildDir, "imageroot"), chrootLeaveOnDisk) - err = imageChroot.Initialize("", newMountDirectories, mountPoints) - if err != nil { - return "", err - } - defer imageChroot.Close(chrootLeaveOnDisk) - - // Write a fake grub.cfg file so that the partition discovery logic works. - bootPrefix := "/boot" - - osUuid, err := installutils.GetUUID(osPartitionDevPath) - if err != nil { - return "", fmt.Errorf("failed get OS partition UUID:\n%w", err) - } - - rootDevice, err := installutils.FormatMountIdentifier(configuration.MountIdentifierUuid, osPartitionDevPath) - if err != nil { - return "", fmt.Errorf("failed to format mount identifier:\n%w", err) - } - - err = installutils.InstallBootloader(imageChroot, false, "efi", osUuid, bootPrefix, "") - if err != nil { - return "", fmt.Errorf("failed to install bootloader:\n%w", err) - } + rawDisk := filepath.Join(buildDir, "disk.raw") - err = installutils.InstallGrubCfg(imageChroot.RootDir(), rootDevice, osUuid, bootPrefix, - diskutils.EncryptedRootDevice{}, configuration.KernelCommandLine{}, diskutils.VerityDevice{}, false) - if err != nil { - return "", fmt.Errorf("failed to install main grub config file:\n%w", err) - } - - err = installutils.InstallGrubEnv(imageChroot.RootDir()) - if err != nil { - return "", fmt.Errorf("failed to install grubenv file:\n%w", err) - } - - // Write a fake fstab file so that the partition discovery logic works. - mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, _ := installutils.CreateMountPointPartitionMap( - partIDToDevPathMap, partIDToFsTypeMap, partitionSettings, - ) - - err = installutils.UpdateFstab(imageChroot.RootDir(), partitionSettings, mountPointMap, mountPointToFsTypeMap, - mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, false, /*hidepidEnabled*/ - ) - if err != nil { - return "", fmt.Errorf("failed to install fstab file:\n%w", err) - } - - // Close. - err = imageChroot.Close(chrootLeaveOnDisk) - if err != nil { - return "", err + installOS := func(imageChroot *safechroot.Chroot) error { + // Don't write anything for the OS. + // The createNewImage function will still write the bootloader and fstab file, which will allow the partition + // discovery logic to work. This allows for a limited set of tests to run without needing any of the RPM files. + return nil } - err = loopback.CleanClose() + imageConnection, err := createNewImage(rawDisk, diskConfig, partitionSettings, "efi", buildDir, "imageroot", + installOS) if err != nil { return "", err } + defer imageConnection.Close() return rawDisk, nil } diff --git a/toolkit/tools/pkg/imagecustomizerlib/imageutils.go b/toolkit/tools/pkg/imagecustomizerlib/imageutils.go new file mode 100644 index 00000000000..ae573e6db23 --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/imageutils.go @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerlib + +import ( + "fmt" + "path/filepath" + "sort" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/configuration" + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/diskutils" + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/installutils" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/file" + "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/safechroot" +) + +type installOSFunc func(imageChroot *safechroot.Chroot) error + +func connectToExistingImage(imageFilePath string, buildDir string, chrootDirName string) (*ImageConnection, error) { + imageConnection := NewImageConnection() + + err := connectToExistingImageHelper(imageConnection, imageFilePath, buildDir, chrootDirName) + if err != nil { + imageConnection.Close() + return nil, err + } + + return imageConnection, nil + +} + +func connectToExistingImageHelper(imageConnection *ImageConnection, imageFilePath string, + buildDir string, chrootDirName string, +) error { + // Connect to image file using loopback device. + err := imageConnection.ConnectLoopback(imageFilePath) + if err != nil { + return err + } + + // Look for all the partitions on the image. + newMountDirectories, mountPoints, err := findPartitions(buildDir, imageConnection.Loopback().DevicePath()) + if err != nil { + return fmt.Errorf("failed to find disk partitions:\n%w", err) + } + + // Create chroot environment. + imageChrootDir := filepath.Join(buildDir, chrootDirName) + + err = imageConnection.ConnectChroot(imageChrootDir, false, newMountDirectories, mountPoints) + if err != nil { + return err + } + + return nil +} + +func createNewImage(filename string, diskConfig imagecustomizerapi.Disk, + partitionSettings []imagecustomizerapi.PartitionSetting, bootType imagecustomizerapi.BootType, buildDir string, + chrootDirName string, installOS installOSFunc, +) (*ImageConnection, error) { + imageConnection := &ImageConnection{} + + err := createNewImageHelper(imageConnection, filename, diskConfig, partitionSettings, bootType, buildDir, + chrootDirName, installOS, + ) + if err != nil { + imageConnection.Close() + return nil, fmt.Errorf("failed to create new image:\n%w", err) + } + + return imageConnection, nil +} + +func createNewImageHelper(imageConnection *ImageConnection, filename string, diskConfig imagecustomizerapi.Disk, + partitionSettings []imagecustomizerapi.PartitionSetting, bootType imagecustomizerapi.BootType, buildDir string, + chrootDirName string, installOS installOSFunc, +) error { + // Convert config to image config types, so that the imager's utils can be used. + imagerBootType, err := bootTypeToImager(bootType) + if err != nil { + return err + } + + imagerDiskConfig, err := diskConfigToImager(diskConfig) + if err != nil { + return err + } + + imagerPartitionSettings, err := partitionSettingsToImager(partitionSettings) + if err != nil { + return err + } + + // Sort the partitions so that they are mounted in the correct oder. + sort.Slice(imagerPartitionSettings, func(i, j int) bool { + return imagerPartitionSettings[i].MountPoint < imagerPartitionSettings[j].MountPoint + }) + + // Create imager boilerplate. + mountPointMap, err := createImageBoilerplate(imageConnection, filename, buildDir, chrootDirName, imagerDiskConfig, + imagerPartitionSettings) + if err != nil { + return err + } + + // Install the OS. + err = installOS(imageConnection.Chroot()) + if err != nil { + return err + } + + // Configure the boot loader. + err = installutils.ConfigureDiskBootloader(imagerBootType, false, false, imagerPartitionSettings, + configuration.KernelCommandLine{}, imageConnection.Chroot(), imageConnection.Loopback().DevicePath(), + mountPointMap, diskutils.EncryptedRootDevice{}, diskutils.VerityDevice{}) + if err != nil { + return fmt.Errorf("failed to install bootloader:\n%w", err) + } + + return nil +} + +func createImageBoilerplate(imageConnection *ImageConnection, filename string, buildDir string, chrootDirName string, + imagerDiskConfig configuration.Disk, imagerPartitionSettings []configuration.PartitionSetting, +) (map[string]string, error) { + // Create raw disk image file. + err := diskutils.CreateSparseDisk(filename, imagerDiskConfig.MaxSize, 0o644) + if err != nil { + return nil, fmt.Errorf("failed to create empty disk file (%s):\n%w", filename, err) + } + + // Connect raw disk image file. + err = imageConnection.ConnectLoopback(filename) + if err != nil { + return nil, err + } + + // Set up partitions. + partIDToDevPathMap, partIDToFsTypeMap, _, _, err := diskutils.CreatePartitions( + imageConnection.Loopback().DevicePath(), imagerDiskConfig, configuration.RootEncryption{}, + configuration.ReadOnlyVerityRoot{}) + if err != nil { + return nil, fmt.Errorf("failed to create partitions on disk (%s):\n%w", imageConnection.Loopback().DevicePath(), err) + } + + // Read the disk partitions. + diskPartitions, err := diskutils.GetDiskPartitions(imageConnection.Loopback().DevicePath()) + if err != nil { + return nil, err + } + + // Create the fstab file. + // This is done so that we can read back the file using findmnt, which conveniently splits the vfs and fs mount + // options for us. If we wanted to handle this more directly, we could create a golang wrapper around libmount + // (which is what findmnt uses). But we are already using the findmnt in other places. + tmpFstabFile := filepath.Join(buildDir, chrootDirName+"_fstab") + + mountPointMap, mountPointToFsTypeMap, mountPointToMountArgsMap, _ := installutils.CreateMountPointPartitionMap( + partIDToDevPathMap, partIDToFsTypeMap, imagerPartitionSettings, + ) + + err = installutils.UpdateFstabFile(tmpFstabFile, imagerPartitionSettings, mountPointMap, mountPointToFsTypeMap, + mountPointToMountArgsMap, partIDToDevPathMap, partIDToFsTypeMap, false, /*hidepidEnabled*/ + ) + if err != nil { + return nil, fmt.Errorf("failed to write temp fstab file:\n%w", err) + } + + // Read back the fstab file. + mountPoints, err := findMountsFromFstabFile(tmpFstabFile, diskPartitions) + if err != nil { + return nil, err + } + + // Create chroot environment. + imageChrootDir := filepath.Join(buildDir, chrootDirName) + + err = imageConnection.ConnectChroot(imageChrootDir, false, nil, mountPoints) + if err != nil { + return nil, err + } + + // Move the fstab file into the image. + imageFstabFilePath := filepath.Join(imageConnection.Chroot().RootDir(), "etc/fstab") + + err = file.Move(tmpFstabFile, imageFstabFilePath) + if err != nil { + return nil, fmt.Errorf("failed to move fstab into new image:\n%w", err) + } + + return mountPointMap, nil +} diff --git a/toolkit/tools/pkg/imagecustomizerlib/partitionutils.go b/toolkit/tools/pkg/imagecustomizerlib/partitionutils.go index 30d08faba8b..407c6233a24 100644 --- a/toolkit/tools/pkg/imagecustomizerlib/partitionutils.go +++ b/toolkit/tools/pkg/imagecustomizerlib/partitionutils.go @@ -273,7 +273,8 @@ func findMountsFromRootfs(rootfsPartition *diskutils.PartitionInfo, diskPartitio tmpDir := filepath.Join(buildDir, tmpParitionDirName) // Temporarily mount the rootfs partition so that the fstab file can be read. - rootfsPartitionMount, err := safemount.NewMount(rootfsPartition.Path, tmpDir, rootfsPartition.FileSystemType, 0, "", true) + rootfsPartitionMount, err := safemount.NewMount(rootfsPartition.Path, tmpDir, rootfsPartition.FileSystemType, 0, "", + true) if err != nil { return nil, fmt.Errorf("failed to mount rootfs partition (%s):\n%w", rootfsPartition.Path, err) } @@ -281,7 +282,8 @@ func findMountsFromRootfs(rootfsPartition *diskutils.PartitionInfo, diskPartitio // Read the fstab file. fstabPath := filepath.Join(tmpDir, "/etc/fstab") - fstabEntries, err := diskutils.ReadFstabFile(fstabPath) + + mountPoints, err := findMountsFromFstabFile(fstabPath, diskPartitions) if err != nil { return nil, err } @@ -292,6 +294,17 @@ func findMountsFromRootfs(rootfsPartition *diskutils.PartitionInfo, diskPartitio return nil, fmt.Errorf("failed to close rootfs partition mount (%s):\n%w", rootfsPartition.Path, err) } + return mountPoints, nil +} + +func findMountsFromFstabFile(fstabPath string, diskPartitions []diskutils.PartitionInfo, +) ([]*safechroot.MountPoint, error) { + // Read the fstab file. + fstabEntries, err := diskutils.ReadFstabFile(fstabPath) + if err != nil { + return nil, err + } + mountPoints, err := fstabEntriesToMountPoints(fstabEntries, diskPartitions) if err != nil { return nil, err @@ -300,7 +313,8 @@ func findMountsFromRootfs(rootfsPartition *diskutils.PartitionInfo, diskPartitio return mountPoints, nil } -func fstabEntriesToMountPoints(fstabEntries []diskutils.FstabEntry, diskPartitions []diskutils.PartitionInfo) ([]*safechroot.MountPoint, error) { +func fstabEntriesToMountPoints(fstabEntries []diskutils.FstabEntry, diskPartitions []diskutils.PartitionInfo, +) ([]*safechroot.MountPoint, error) { // Convert fstab entries into mount points. var mountPoints []*safechroot.MountPoint var foundRoot bool diff --git a/toolkit/tools/pkg/imagecustomizerlib/testdata/legacyboot-config.yaml b/toolkit/tools/pkg/imagecustomizerlib/testdata/legacyboot-config.yaml new file mode 100644 index 00000000000..b17a3857e06 --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/testdata/legacyboot-config.yaml @@ -0,0 +1,22 @@ +Disks: +- PartitionTableType: gpt + MaxSize: 4096 + Partitions: + - ID: boot + Flags: + - bios_grub + Start: 1 + Size: 8 + FsType: fat32 + + - ID: rootfs + Start: 9 + FsType: ext4 + +SystemConfig: + BootType: legacy + PartitionSettings: + - ID: boot + + - ID: rootfs + MountPoint: / diff --git a/toolkit/tools/pkg/imagecustomizerlib/testdata/partitions-config.yaml b/toolkit/tools/pkg/imagecustomizerlib/testdata/partitions-config.yaml new file mode 100644 index 00000000000..922612591a4 --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/testdata/partitions-config.yaml @@ -0,0 +1,41 @@ +Disks: +- PartitionTableType: gpt + MaxSize: 4096 + Partitions: + - ID: esp + Flags: + - esp + - boot + Start: 1 + End: 9 + FsType: fat32 + + - ID: boot + Start: 9 + End: 108 + FsType: ext4 + + - ID: rootfs + Start: 108 + End: 2048 + FsType: xfs + + - ID: var + Start: 2048 + FsType: xfs + +SystemConfig: + BootType: efi + PartitionSettings: + - ID: esp + MountPoint: /boot/efi + MountOptions: umask=0077 + + - ID: boot + MountPoint: /boot + + - ID: rootfs + MountPoint: / + + - ID: var + MountPoint: /var diff --git a/toolkit/tools/pkg/imagecustomizerlib/typeConversion.go b/toolkit/tools/pkg/imagecustomizerlib/typeConversion.go new file mode 100644 index 00000000000..00305b536aa --- /dev/null +++ b/toolkit/tools/pkg/imagecustomizerlib/typeConversion.go @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package imagecustomizerlib + +import ( + "fmt" + + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagecustomizerapi" + "github.com/microsoft/CBL-Mariner/toolkit/tools/imagegen/configuration" +) + +func bootTypeToImager(bootType imagecustomizerapi.BootType) (string, error) { + switch bootType { + case imagecustomizerapi.BootTypeEfi: + return "efi", nil + + case imagecustomizerapi.BootTypeLegacy: + return "legacy", nil + + default: + return "", fmt.Errorf("invalid BootType value (%s)", bootType) + } +} + +func diskConfigToImager(diskConfig imagecustomizerapi.Disk) (configuration.Disk, error) { + imagerPartitionTableType, err := partitionTableTypeToImager(diskConfig.PartitionTableType) + if err != nil { + return configuration.Disk{}, err + } + + imagerPartitions, err := partitionsToImager(diskConfig.Partitions) + if err != nil { + return configuration.Disk{}, err + } + + imagerDisk := configuration.Disk{ + PartitionTableType: imagerPartitionTableType, + MaxSize: diskConfig.MaxSize, + Partitions: imagerPartitions, + } + return imagerDisk, err +} + +func partitionTableTypeToImager(partitionTableType imagecustomizerapi.PartitionTableType, +) (configuration.PartitionTableType, error) { + switch partitionTableType { + case imagecustomizerapi.PartitionTableTypeGpt: + return configuration.PartitionTableTypeGpt, nil + + default: + return "", fmt.Errorf("unknown partition table type (%s)", partitionTableType) + } +} + +func partitionsToImager(partitions []imagecustomizerapi.Partition) ([]configuration.Partition, error) { + imagerPartitions := []configuration.Partition(nil) + for _, partition := range partitions { + imagerPartition, err := partitionToImager(partition) + if err != nil { + return nil, err + } + + imagerPartitions = append(imagerPartitions, imagerPartition) + } + + return imagerPartitions, nil +} + +func partitionToImager(partition imagecustomizerapi.Partition) (configuration.Partition, error) { + imagerEnd, _ := partition.GetEnd() + + imagerFlags, err := partitionFlagsToImager(partition.Flags) + if err != nil { + return configuration.Partition{}, err + } + + imagerPartition := configuration.Partition{ + ID: partition.ID, + FsType: string(partition.FsType), + Name: partition.Name, + Start: partition.Start, + End: imagerEnd, + Flags: imagerFlags, + } + return imagerPartition, nil +} + +func partitionFlagsToImager(flags []imagecustomizerapi.PartitionFlag) ([]configuration.PartitionFlag, error) { + imagerFlags := []configuration.PartitionFlag(nil) + for _, flag := range flags { + imagerFlag, err := partitionFlagToImager(flag) + if err != nil { + return nil, err + } + + imagerFlags = append(imagerFlags, imagerFlag) + } + + return imagerFlags, nil +} + +func partitionFlagToImager(flag imagecustomizerapi.PartitionFlag) (configuration.PartitionFlag, error) { + switch flag { + case imagecustomizerapi.PartitionFlagESP: + return configuration.PartitionFlagESP, nil + + case imagecustomizerapi.PartitionFlagBiosGrub: + return configuration.PartitionFlagBiosGrub, nil + + case imagecustomizerapi.PartitionFlagBoot: + return configuration.PartitionFlagBoot, nil + + default: + return "", fmt.Errorf("unknown partition flag (%s)", flag) + } +} + +func partitionSettingsToImager(partitionSettings []imagecustomizerapi.PartitionSetting, +) ([]configuration.PartitionSetting, error) { + imagerPartitionSettings := []configuration.PartitionSetting(nil) + for _, partitionSetting := range partitionSettings { + imagerPartitionSetting, err := partitionSettingToImager(partitionSetting) + if err != nil { + return nil, err + } + imagerPartitionSettings = append(imagerPartitionSettings, imagerPartitionSetting) + } + return imagerPartitionSettings, nil +} + +func partitionSettingToImager(partitionSettings imagecustomizerapi.PartitionSetting, +) (configuration.PartitionSetting, error) { + imagerMountIdentifierType, err := mountIdentifierTypeToImager(partitionSettings.MountIdentifier) + if err != nil { + return configuration.PartitionSetting{}, err + } + + imagerPartitionSetting := configuration.PartitionSetting{ + ID: partitionSettings.ID, + MountIdentifier: imagerMountIdentifierType, + MountOptions: partitionSettings.MountOptions, + MountPoint: partitionSettings.MountPoint, + } + return imagerPartitionSetting, nil +} + +func mountIdentifierTypeToImager(mountIdentifierType imagecustomizerapi.MountIdentifierType, +) (configuration.MountIdentifier, error) { + switch mountIdentifierType { + case imagecustomizerapi.MountIdentifierTypeUuid: + return configuration.MountIdentifierUuid, nil + + case imagecustomizerapi.MountIdentifierTypePartUuid, imagecustomizerapi.MountIdentifierTypeDefault: + return configuration.MountIdentifierPartUuid, nil + + case imagecustomizerapi.MountIdentifierTypePartLabel: + return configuration.MountIdentifierPartLabel, nil + + default: + return "", fmt.Errorf("unknwon MountIdentifierType value (%s)", mountIdentifierType) + } +} diff --git a/toolkit/tools/pkggen/worker/create_worker_chroot.sh b/toolkit/tools/pkggen/worker/create_worker_chroot.sh index df3fc6e8a5e..26b1c6dd274 100755 --- a/toolkit/tools/pkggen/worker/create_worker_chroot.sh +++ b/toolkit/tools/pkggen/worker/create_worker_chroot.sh @@ -22,10 +22,25 @@ chroot_builder_folder=$chroot_base/$chroot_name chroot_archive=$chroot_base/$chroot_name.tar.gz chroot_log="$log_path"/$chroot_name.log +# We have two major steps per entry in the packages file: install the RPM, then add to database +total_steps=$(wc -l < "$packages") +total_steps=$((total_steps * 2)) +current_step=0 +# Print "%" and increment current_step +function increment_progress() { + # Increment global counter current_step + current_step=$((current_step + 1)) +} +function format_progress() { + progress=$((current_step * 100 / total_steps)) + printf "%s%%" "$progress" +} + install_one_toolchain_rpm () { error_msg_tail="Inspect $chroot_log for more info. Did you hydrate the toolchain?" - echo "Adding RPM to worker chroot: $1." | tee -a "$chroot_log" + echo "Adding RPM to worker chroot $(format_progress): $1." | tee -a "$chroot_log" + increment_progress full_rpm_path=$(find "$rpm_path" -name "$1" -type f 2>>"$chroot_log") if [ ! $? -eq 0 ] || [ -z "$full_rpm_path" ] @@ -83,7 +98,8 @@ else while read -r package || [ -n "$package" ]; do full_rpm_path=$(find "$rpm_path" -name "$package" -type f 2>>"$chroot_log") cp $full_rpm_path $chroot_builder_folder/$package - echo "Adding RPM DB entry to worker chroot: $package." | tee -a "$chroot_log" + echo "Adding RPM DB entry to worker chroot $(format_progress): $package." | tee -a "$chroot_log" + increment_progress chroot "$chroot_builder_folder" rpm -i -v --nodeps --noorder --force --dbpath="$TEMP_DB_PATH" --justdb "$package" &>> "$chroot_log" chroot "$chroot_builder_folder" rm $package done < "$packages" @@ -113,8 +129,8 @@ fi echo "Done installing all packages, creating $chroot_archive." | tee -a "$chroot_log" if command -v pigz &>/dev/null ; then - tar -I pigz -cvf "$chroot_archive" -C "$chroot_base/$chroot_name" . >> "$chroot_log" + tar --warning='no-file-ignored' -I pigz -cvf "$chroot_archive" -C "$chroot_base/$chroot_name" . >> "$chroot_log" else - tar -I gzip -cvf "$chroot_archive" -C "$chroot_base/$chroot_name" . >> "$chroot_log" + tar --warning='no-file-ignored' -I gzip -cvf "$chroot_archive" -C "$chroot_base/$chroot_name" . >> "$chroot_log" fi -echo "Done creating $chroot_archive." | tee -a "$chroot_log" \ No newline at end of file +echo "Done creating $chroot_archive." | tee -a "$chroot_log" diff --git a/toolkit/tools/scheduler/schedulerutils/buildworker.go b/toolkit/tools/scheduler/schedulerutils/buildworker.go index 936693e4f18..48c0676d59c 100644 --- a/toolkit/tools/scheduler/schedulerutils/buildworker.go +++ b/toolkit/tools/scheduler/schedulerutils/buildworker.go @@ -299,7 +299,7 @@ func testSRPMFile(agent buildagents.BuildAgent, checkAttempts int, basePackageNa _, logFile, buildErr = agent.BuildPackage(basePackageName, srpmFile, logBaseName, outArch, runCheck, dependencies) if buildErr != nil { - logger.Log.Warnf("Test build for '%s' failed on a non-test build issue. Error: %s", srpmFile, buildErr) + logger.Log.Warnf("Test build for '%s' failed on a non-test build issue. Error: %s, for details see: %s", srpmFile, buildErr, logFile) return } @@ -309,7 +309,7 @@ func testSRPMFile(agent buildagents.BuildAgent, checkAttempts int, basePackageNa }, checkAttempts, retryDuration) if err != nil && checkFailed { - logger.Log.Warnf("Tests failed for '%s'. Error: %s", srpmFile, err) + logger.Log.Warnf("Tests failed for '%s'. Error: %s, for details see: %s", srpmFile, err, logFile) err = nil } return diff --git a/toolkit/tools/scheduler/schedulerutils/depsolver.go b/toolkit/tools/scheduler/schedulerutils/depsolver.go index 56dd586eacd..1071327bfbb 100644 --- a/toolkit/tools/scheduler/schedulerutils/depsolver.go +++ b/toolkit/tools/scheduler/schedulerutils/depsolver.go @@ -4,7 +4,9 @@ package schedulerutils import ( + "fmt" "math" + "strings" "sync" "github.com/microsoft/CBL-Mariner/toolkit/tools/internal/logger" @@ -22,6 +24,7 @@ func CanSubGraph(pkgGraph *pkggraph.PkgGraph, node *pkggraph.PkgNode, useCachedI search := traverse.BreadthFirst{} foundUnsolvableNode := false + unsolvedNodes := make([]*pkggraph.PkgNode, 0) // Walk entire graph and print list of any/all unsolvable nodes search.Walk(pkgGraph, node, func(n graph.Node, d int) (stopSearch bool) { @@ -43,7 +46,8 @@ func CanSubGraph(pkgGraph *pkggraph.PkgGraph, node *pkggraph.PkgNode, useCachedI } // This node is not yet solvable - logger.Log.Warnf("Could not subgraph due to node: %v", pkgNode) + logger.Log.Debugf("Could not subgraph due to node: %v", pkgNode) + unsolvedNodes = append(unsolvedNodes, pkgNode) // If we are in trace mode, print the path from the root node to the unsolvable node if logger.Log.IsLevelEnabled(logrus.TraceLevel) { @@ -64,7 +68,24 @@ func CanSubGraph(pkgGraph *pkggraph.PkgGraph, node *pkggraph.PkgNode, useCachedI return }) - return foundUnsolvableNode == false + // Print a summary of the nodes causing the subgraph to be unsolvable + if len(unsolvedNodes) > 0 { + var warningString strings.Builder + warningString.WriteString(fmt.Sprintf("Found %d unsolved implicit nodes, cannot optimize subgraph yet...\n", len(unsolvedNodes))) + printCount := 5 + if len(unsolvedNodes) <= 5 { + printCount = len(unsolvedNodes) + } + for _, node := range unsolvedNodes[:printCount] { + warningString.WriteString(fmt.Sprintf("\tUnsolvable node: %v\n", node)) + } + if len(unsolvedNodes) > 5 { + warningString.WriteString(fmt.Sprintf("\t...and %d more\n", len(unsolvedNodes)-printCount)) + } + logger.Log.Warn(warningString.String()) + } + + return !foundUnsolvableNode } // LeafNodes returns a slice of all leaf nodes in the graph. diff --git a/toolkit/tools/srpmpacker/srpmpacker.go b/toolkit/tools/srpmpacker/srpmpacker.go index b06c1117358..338f79e672a 100644 --- a/toolkit/tools/srpmpacker/srpmpacker.go +++ b/toolkit/tools/srpmpacker/srpmpacker.go @@ -838,12 +838,11 @@ func hydrateFiles(fileTypeToHydrate fileType, specFile, workingDir string, srcCo fileHydrationState[fileNeeded] = false } - // If the user provided an existing source dir, prefer it over remote sources. + // If the user provided an existing source dir, try it first before using remote sources. if srcConfig.localSourceDir != "" { - err = hydrateFromLocalSource(fileHydrationState, newSourceDir, srcConfig, skipSignatureHandling, currentSignatures) - // On error warn and default to hydrating from an external server. + err = tryToHydrateFromLocalSource(fileHydrationState, newSourceDir, srcConfig, skipSignatureHandling, currentSignatures) if err != nil { - logger.Log.Warnf("Error hydrating from local source directory (%s): %v", srcConfig.localSourceDir, err) + return } } @@ -854,20 +853,31 @@ func hydrateFiles(fileTypeToHydrate fileType, specFile, workingDir string, srcCo } } + missingFiles := []string{} for fileNeeded, alreadyHydrated := range fileHydrationState { if !alreadyHydrated { - err = fmt.Errorf("unable to hydrate file: %s", fileNeeded) - logger.Log.Error(err) + missingFiles = append(missingFiles, fileNeeded) + logger.Log.Errorf("Unable to hydrate file: %s", fileNeeded) } } + if len(missingFiles) != 0 { + err = fmt.Errorf("unable to hydrate files: %v", missingFiles) + } + return } -// hydrateFromLocalSource will update fileHydrationState. -// Will alter currentSignatures. -func hydrateFromLocalSource(fileHydrationState map[string]bool, newSourceDir string, srcConfig sourceRetrievalConfiguration, skipSignatureHandling bool, currentSignatures map[string]string) (err error) { - err = filepath.Walk(srcConfig.localSourceDir, func(path string, info os.FileInfo, err error) error { +// tryToHydrateFromLocalSource tries to find the required sources inside srcConfig.localSourceDir. +// Will skip files in fileHydrationState that are not present under srcConfig.localSourceDir. +// Will update fileHydrationState if a source is found. +// May alter currentSignatures depending on value of srcConfig.signatureHandling. +func tryToHydrateFromLocalSource(fileHydrationState map[string]bool, newSourceDir string, srcConfig sourceRetrievalConfiguration, skipSignatureHandling bool, currentSignatures map[string]string) (err error) { + return filepath.Walk(srcConfig.localSourceDir, func(path string, info os.FileInfo, walkErr error) (internalErr error) { + if walkErr != nil { + return walkErr + } + isFile, _ := file.IsFile(path) if !isFile { return nil @@ -875,8 +885,8 @@ func hydrateFromLocalSource(fileHydrationState map[string]bool, newSourceDir str fileName := filepath.Base(path) - isHydrated, found := fileHydrationState[fileName] - if !found { + isHydrated, fileRequiredBySpec := fileHydrationState[fileName] + if !fileRequiredBySpec { return nil } @@ -886,17 +896,15 @@ func hydrateFromLocalSource(fileHydrationState map[string]bool, newSourceDir str } if !skipSignatureHandling { - err = validateSignature(path, srcConfig, currentSignatures) - if err != nil { - logger.Log.Warn(err.Error()) - return nil + internalErr = validateSignature(path, srcConfig, currentSignatures) + if internalErr != nil { + return internalErr } } - err = file.Copy(path, filepath.Join(newSourceDir, fileName)) - if err != nil { - logger.Log.Warnf("Failed to copy file (%s), skipping. Error: %s", path, err) - return nil + internalErr = file.Copy(path, filepath.Join(newSourceDir, fileName)) + if internalErr != nil { + return internalErr } logger.Log.Debugf("Hydrated (%s) from (%s)", fileName, path) @@ -904,8 +912,6 @@ func hydrateFromLocalSource(fileHydrationState map[string]bool, newSourceDir str fileHydrationState[fileName] = true return nil }) - - return } // hydrateFromRemoteSource will update fileHydrationState. @@ -918,7 +924,7 @@ func hydrateFromRemoteSource(fileHydrationState map[string]bool, newSourceDir st failureBackoffBase = 2.0 downloadRetryDuration = time.Second ) - var errPackerCancelReceived = fmt.Errorf("packer cancel signal received") + errPackerCancelReceived := fmt.Errorf("packer cancel signal received") for fileName, alreadyHydrated := range fileHydrationState { if alreadyHydrated { @@ -941,14 +947,13 @@ func hydrateFromRemoteSource(fileHydrationState map[string]bool, newSourceDir st } } - cancelled := false - cancelled, err = retry.RunWithExpBackoff(func() error { - err := network.DownloadFile(url, destinationFile, srcConfig.caCerts, srcConfig.tlsCerts) - if err != nil { - logger.Log.Warnf("Failed to download (%s). Error: %s", url, err) + cancelled, internalErr := retry.RunWithExpBackoff(func() error { + downloadErr := network.DownloadFile(url, destinationFile, srcConfig.caCerts, srcConfig.tlsCerts) + if downloadErr != nil { + logger.Log.Debugf("Failed an attempt to download (%s). Error: %s.", url, downloadErr) } - return err + return downloadErr }, downloadRetryAttempts, downloadRetryDuration, failureBackoffBase, cancel) if netOpsSemaphore != nil { @@ -956,28 +961,28 @@ func hydrateFromRemoteSource(fileHydrationState map[string]bool, newSourceDir st <-netOpsSemaphore } + // We may intentionally fail early due to a cancellation signal, stop immediately if that is the case. if cancelled { err = errPackerCancelReceived return } - if err != nil { - // We may intentionally fail early due to a cancellation signal, stop immediately if that is the case. + if internalErr != nil { + logger.Log.Errorf("Failed to download (%s). Error: %s.", url, internalErr) continue } if !skipSignatureHandling { - err = validateSignature(destinationFile, srcConfig, currentSignatures) - if err != nil { - logger.Log.Warn(err.Error()) + internalErr = validateSignature(destinationFile, srcConfig, currentSignatures) + if internalErr != nil { + logger.Log.Errorf("Signature validation for (%s) failed. Error: %s.", destinationFile, internalErr) // If the delete fails, just warn as there will be another cleanup // attempt when exiting the program. - err = os.Remove(destinationFile) - if err != nil { - logger.Log.Warnf("Failed to delete file (%s). Error: %s", destinationFile, err) + internalErr = os.Remove(destinationFile) + if internalErr != nil { + logger.Log.Warnf("Failed to delete file (%s) after signature validation failure. Error: %s.", destinationFile, internalErr) } - continue } }