mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-26 18:53:01 +02:00
Compare commits
+4
-64
@@ -1,67 +1,7 @@
|
||||
aarch64
|
||||
=======
|
||||
|
||||
Microchip SparX-5i PCB135 (eMMC)
|
||||
--------------------------------
|
||||
|
||||
At present, only FIT images are supported via the SparX-5i eval
|
||||
board's U-Boot, which must contain a valid load address. If you are
|
||||
using this board, after selecting your base configuration, run the
|
||||
following command to supplement the existing config with the required
|
||||
FIT options:
|
||||
|
||||
make board-enable-sparx-fit
|
||||
|
||||
### Unbricking
|
||||
|
||||
1. Don't load a corrupt bootloader to begin with
|
||||
2. Done
|
||||
|
||||
If, for some reason, you didn't manage to follow step 1, you can use
|
||||
the `FLASH_PROG`(`J4`) connector on the board to access the SPI flash
|
||||
directly.
|
||||
|
||||
Using a [Bus Blaster][BB3] in combination with [dangerspi][dangerspi],
|
||||
you can load a new bootloader. The schematic below details how to
|
||||
connect the Bus Blaster to the board, but the concept should be
|
||||
portable to any debugger/device based around an FTDI2232 chip.
|
||||
|
||||
```
|
||||
Bus Blaster: FLASH_PROG (J4):
|
||||
.---. .---.
|
||||
VTG >o o| VTG SCK >o o| GND
|
||||
TRST |o o| GND MISO |o o| #RESET_FLASH
|
||||
TDI |o o| GND o o| 3V3
|
||||
TMS |o o| GND #CS |o o| #SYSRESET
|
||||
TCK o o| GND MOSI |o o| GND
|
||||
RTCK o o| GND '---'
|
||||
TDO |o o| GND
|
||||
TSRST |o o| GND
|
||||
DBGRQ |o o| GND
|
||||
DBGACK |o o| GND
|
||||
'---'
|
||||
```
|
||||
|
||||
| Bus Blaster | FLASH_PROG |
|
||||
|-------------|----------------|
|
||||
| `VTG` | `3V3` |
|
||||
| `GND` | `GND` |
|
||||
| | |
|
||||
| `VTG` | `#RESET_FLASH` |
|
||||
| `GND` | `#SYSRESET` |
|
||||
| | |
|
||||
| `TDI` | `MOSI` |
|
||||
| `TMS` | `#CS` |
|
||||
| `TCK` | `SCK` |
|
||||
| `TDO` | `MISO` |
|
||||
|
||||
With the cable in place, build U-Boot with the correct configuration:
|
||||
|
||||
make fireant_boot_defconfig && make
|
||||
|
||||
To flash it to the board, connect the cable to `J4` and run:
|
||||
|
||||
make board-sparx-flash-uboot
|
||||
|
||||
[BB3]: http://dangerousprototypes.com/docs/Bus_Blaster#Bus_Blaster_v3
|
||||
[dangerspi]: https://github.com/wkz/dangerspi
|
||||
Board Specific Documentation
|
||||
----------------------------
|
||||
- [Marvell CN9130-CRB](cn9130-crb/)
|
||||
- [Microchip SparX-5i PCB135 (eMMC)](sparx5-pcb135/)
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
Marvell CN9130-CRB
|
||||
==================
|
||||
|
||||
## Build instructions
|
||||
|
||||
### Bootloader
|
||||
|
||||
Build the bootloader from the supplied `defconfig`. It might be useful
|
||||
to build in a separate output directory if you want to build Infix
|
||||
from the same working tree later:
|
||||
|
||||
make O=$(pwd)/x-cn9130-boot cn9130_crb_boot_defconfig
|
||||
cd x-cn9130-boot
|
||||
make
|
||||
|
||||
The artifact of interest is called `flash-image.bin`, which will be
|
||||
located in the `images/` directory once the build completes.
|
||||
|
||||
|
||||
### Infix
|
||||
|
||||
> If you do not want to build Infix from source, feel free to use a pre-built [release]
|
||||
|
||||
The standard `aarch64_defconfig` is compatible with this board:
|
||||
|
||||
make O=$(pwd)/x-aarch64 aarch64_defconfig
|
||||
cd x-aarch64
|
||||
make
|
||||
|
||||
Two artifacts from the `images/` directory of this build are required
|
||||
to provision a new board:
|
||||
|
||||
- `rootfs.itb`: Netbootable image
|
||||
- `infix-aarch64.pkg`: Standard upgrade bundle
|
||||
|
||||
## Provisioning
|
||||
|
||||
The overall provisioning flow, in which each step in described in
|
||||
details in the following sections, is as follows:
|
||||
|
||||
- Strap board to boot from SPI FLASH
|
||||
- Load `flash-image.bin` over UART
|
||||
- Burn `flash-image.bin` to SPI FLASH
|
||||
- Netboot `rootfs.itb`
|
||||
- Run Infix's built-in provisioning script
|
||||
- Reboot, now booting from the primary partition of the SD-card
|
||||
|
||||
#### Strap Board to Boot from SPI FLASH
|
||||
|
||||
By default, the board is strapped to boot from eMMC. However, Infix
|
||||
assumes that the board will boot from SPI FLASH. Therefore we have to
|
||||
ensure that the DIP switches of `SW2` on the board selects a
|
||||
`BOOT_MODE` of `0x32`, meaning that the switches on positions 4, 3,
|
||||
and 1 should be enabled (i.e. these need to be tied to ground):
|
||||
|
||||
```
|
||||
.-----. .-----. .-----.
|
||||
| | | | | PWR |
|
||||
| SIM | | uSD | | |
|
||||
| | | | '-----'
|
||||
'-----' '-----'
|
||||
|
||||
|
|
||||
v
|
||||
.-----. .-----.
|
||||
| SW1 | | SW2 |
|
||||
'-----' '-----'
|
||||
.--------------------------.
|
||||
| |
|
||||
| |
|
||||
| CN9130 |
|
||||
```
|
||||
|
||||
#### UART Boot U-Boot
|
||||
|
||||
Make sure that:
|
||||
- [mvebu64boot] is installed and available in your shell's `$PATH`
|
||||
- No other program is attached to `ttyUSB0`
|
||||
- No power is applied to the board
|
||||
|
||||
1. Start `mvebu64boot`:
|
||||
|
||||
mvebu64boot -b /path/to/flash-image.bin /dev/ttyUSB0
|
||||
|
||||
2. Apply power
|
||||
|
||||
As soon as `mvebu64boot` completes, attach to the serial port,
|
||||
e.g. using `screen(1)`, or `console(1)` and stop the normal boot
|
||||
process by hitting any key.
|
||||
|
||||
#### Burn U-Boot to SPI FLASH
|
||||
|
||||
Make sure that:
|
||||
- `eth1` is connected to a machine which serves `flash-image.bin` over
|
||||
TFTP
|
||||
- U-Boot can reach the TFTP server. If the neighboring machine is also
|
||||
set up as a DHCP server, simply run the command `dhcp -`
|
||||
|
||||
To burn the bootloader to SPI FLASH, run the `bubt` command:
|
||||
|
||||
bubt flash-image.bin
|
||||
|
||||
Once the command completes, reset the board to verify that it can now
|
||||
boot unassisted:
|
||||
|
||||
reset
|
||||
|
||||
#### Boot up `rootfs.itb`
|
||||
|
||||
U-Boot will automatically fallback to netboot since the SD-card is
|
||||
still blank. Make sure that the PC provides the path to `rootfs.itb`
|
||||
in DHCP option 67 ("bootfile").
|
||||
|
||||
#### Install Firmware
|
||||
|
||||
Login as `admin`/`admin`, setup networking to the PC, ensure that the date on
|
||||
the device is reasonably correct, and run the provisioning script:
|
||||
|
||||
admin@infix:~$ sudo -i
|
||||
root@infix:~$ udhcpc -i e28
|
||||
root@infix:~$ date -us YYYY-MM-DD
|
||||
root@infix:~$ /libexec/infix/prod/provision tftp://<PC-IP>/infix-aarch64.pkg /dev/mmcblk0
|
||||
|
||||
After successful completion, the device is fully provisioned. On the
|
||||
next boot, the device will boot of its own accord from the primary
|
||||
SD-card partition.
|
||||
|
||||
> If possible, serve `infix-aarch64.pkg` over HTTP instead, as
|
||||
> libcurl's TFTP implementation is quite slow.
|
||||
|
||||
[release]: https://github.com/kernelkit/infix/releases
|
||||
[mvebu64boot]: https://github.com/addiva-elektronik/mvebu64boot
|
||||
@@ -16,36 +16,7 @@ run ixboot
|
||||
";
|
||||
boot_targets = "mmc1";
|
||||
ethprime = "eth1";
|
||||
|
||||
/* Uncomment this if you're debugging U-Boot
|
||||
*
|
||||
* This will allow you to break out of the
|
||||
* normal boot flow and into the interactive
|
||||
* console.
|
||||
*
|
||||
* To upgrade U-Boot itself, simply set the
|
||||
* `bootfile` variable to the path of
|
||||
* `flash-image.bin` on your TFTP server, then
|
||||
* issue `run ixupgradeboot`.
|
||||
*/
|
||||
/* bootdelay = "2"; */
|
||||
/* ixupgradeboot = " */
|
||||
/* dhcp */
|
||||
/* setexpr fileblks ${filesize} + 0x1ff */
|
||||
/* setexpr fileblks ${fileblks} / 0x200 */
|
||||
|
||||
/* mmc dev 1 */
|
||||
/* part start mmc 1 boot bootstart */
|
||||
/* part size mmc 1 boot bootsize */
|
||||
|
||||
|
||||
/* mmc erase ${bootstart} ${bootsize} */
|
||||
/* mmc write ${fileaddr} ${bootstart} ${fileblks} */
|
||||
/* "; */
|
||||
bootdelay = "1";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/* &cp0_eth0 { */
|
||||
/* phy-mode = "10gbase-r"; */
|
||||
/* }; */
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
CONFIG_DEVICE_TREE_INCLUDES="infix-env.dtsi infix-key.dtsi cn9130-crb-env.dtsi"
|
||||
CONFIG_SYS_PROMPT="(cn9130-crb) "
|
||||
|
||||
CONFIG_ENV_IS_NOWHERE=y
|
||||
# CONFIG_ENV_IS_IN_MMC is not set
|
||||
CONFIG_MVEBU_SPI_BOOT=y
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2020 Marvell International Ltd.
|
||||
*/
|
||||
|
||||
#include "cn9130-crb.dtsi"
|
||||
|
||||
/ {
|
||||
model = "Marvell Armada CN9130-CRB-A";
|
||||
|
||||
chosen {
|
||||
infix {
|
||||
/* "admin" */
|
||||
factory-password-hash = "$5$mI/zpOAqZYKLC2WU$i7iPzZiIjOjrBF3NyftS9CCq8dfYwHwrmUK097Jca9A";
|
||||
usb-ports = <&cp0_usb3_1>;
|
||||
usb-port-names = "USB";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&cp0_pcie0 {
|
||||
status = "okay";
|
||||
num-lanes = <4>;
|
||||
num-viewport = <8>;
|
||||
/* Generic PHY, providing serdes lanes */
|
||||
phys = <&cp0_comphy0 0
|
||||
&cp0_comphy1 0
|
||||
&cp0_comphy2 0
|
||||
&cp0_comphy3 0>;
|
||||
iommu-map =
|
||||
<0x0 &smmu 0x480 0x20>,
|
||||
<0x100 &smmu 0x4a0 0x20>,
|
||||
<0x200 &smmu 0x4c0 0x20>;
|
||||
iommu-map-mask = <0x031f>;
|
||||
};
|
||||
|
||||
&cp0_usb3_0 {
|
||||
status = "okay";
|
||||
usb-phy = <&cp0_usb3_0_phy0>;
|
||||
phy-names = "usb";
|
||||
};
|
||||
|
||||
&cp0_usb3_1 {
|
||||
status = "okay";
|
||||
usb-phy = <&cp0_usb3_0_phy1>;
|
||||
phy-names = "usb";
|
||||
};
|
||||
|
||||
&cp0_eth2 {
|
||||
status = "okay";
|
||||
phy-mode = "2500base-x";
|
||||
};
|
||||
|
||||
&cp0_pinctrl {
|
||||
cp0_expander0_pins: cp0-expander-pins-0 {
|
||||
marvell,pins = "mpp39";
|
||||
marvell,function = "gpio";
|
||||
};
|
||||
};
|
||||
|
||||
&expander0 {
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&cp0_expander0_pins>;
|
||||
interrupt-parent = <&cp0_gpio2>;
|
||||
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells=<2>;
|
||||
microchip,irq-mirror;
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
Microchip SparX-5i PCB135 (eMMC)
|
||||
================================
|
||||
|
||||
At present, only FIT images are supported via the SparX-5i eval
|
||||
board's U-Boot, which must contain a valid load address. If you are
|
||||
using this board, after selecting your base configuration, run the
|
||||
following command to supplement the existing config with the required
|
||||
FIT options:
|
||||
|
||||
make board-enable-sparx-fit
|
||||
|
||||
## Unbricking
|
||||
|
||||
1. Don't load a corrupt bootloader to begin with
|
||||
2. Done
|
||||
|
||||
If, for some reason, you didn't manage to follow step 1, you can use
|
||||
the `FLASH_PROG`(`J4`) connector on the board to access the SPI flash
|
||||
directly.
|
||||
|
||||
Using a [Bus Blaster][BB3] in combination with [dangerspi][dangerspi],
|
||||
you can load a new bootloader. The schematic below details how to
|
||||
connect the Bus Blaster to the board, but the concept should be
|
||||
portable to any debugger/device based around an FTDI2232 chip.
|
||||
|
||||
```
|
||||
Bus Blaster: FLASH_PROG (J4):
|
||||
.---. .---.
|
||||
VTG >o o| VTG SCK >o o| GND
|
||||
TRST |o o| GND MISO |o o| #RESET_FLASH
|
||||
TDI |o o| GND o o| 3V3
|
||||
TMS |o o| GND #CS |o o| #SYSRESET
|
||||
TCK o o| GND MOSI |o o| GND
|
||||
RTCK o o| GND '---'
|
||||
TDO |o o| GND
|
||||
TSRST |o o| GND
|
||||
DBGRQ |o o| GND
|
||||
DBGACK |o o| GND
|
||||
'---'
|
||||
```
|
||||
|
||||
| Bus Blaster | FLASH_PROG |
|
||||
|-------------|----------------|
|
||||
| `VTG` | `3V3` |
|
||||
| `GND` | `GND` |
|
||||
| | |
|
||||
| `VTG` | `#RESET_FLASH` |
|
||||
| `GND` | `#SYSRESET` |
|
||||
| | |
|
||||
| `TDI` | `MOSI` |
|
||||
| `TMS` | `#CS` |
|
||||
| `TCK` | `SCK` |
|
||||
| `TDO` | `MISO` |
|
||||
|
||||
With the cable in place, build U-Boot with the correct configuration:
|
||||
|
||||
make fireant_boot_defconfig && make
|
||||
|
||||
To flash it to the board, connect the cable to `J4` and run:
|
||||
|
||||
make board-sparx-flash-uboot
|
||||
|
||||
[BB3]: http://dangerousprototypes.com/docs/Bus_Blaster#Bus_Blaster_v3
|
||||
[dangerspi]: https://github.com/wkz/dangerspi
|
||||
@@ -0,0 +1,10 @@
|
||||
Reserved facilities for logging in various subsystems:
|
||||
|
||||
- local0: rauc
|
||||
- local1: containers
|
||||
- local2:
|
||||
- local3:
|
||||
- local5:
|
||||
- local6: reserved
|
||||
- local7: nginx
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
. /etc/partion-uuid
|
||||
. /etc/partition-uuid
|
||||
|
||||
disk=$1
|
||||
bootoffs=$2
|
||||
@@ -28,11 +28,11 @@ else
|
||||
fi
|
||||
|
||||
sgdisk \
|
||||
-o \
|
||||
-Z \
|
||||
-n1:${bootoffs}:+${bootsize} -t1:8301 -c1:boot \
|
||||
-n2::+${auxsize} -t2:8301 -c2:aux -u2:${AUX_UUID}\
|
||||
-n2::+${auxsize} -t2:8301 -c2:aux -u2:${AUX_UUID} \
|
||||
-n3::+${imgsize} -t3:8300 -c3:primary -u3:${PRIMARY_UUID} \
|
||||
-n4::+${imgsize} -t4:8300 -c4:secondary -u4:${SECONDARY_UUID}\
|
||||
-n4::+${imgsize} -t4:8300 -c4:secondary -u4:${SECONDARY_UUID} \
|
||||
-n5::+${cfgsize} -t5:8302 -c5:cfg \
|
||||
-n6:: -t6:8310 -c6:var \
|
||||
-p \
|
||||
|
||||
@@ -16,6 +16,8 @@ BR2_ROOTFS_POST_IMAGE_SCRIPT="${BR2_EXTERNAL_INFIX_PATH}/board/common/post-image
|
||||
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
|
||||
# BR2_TARGET_ROOTFS_TAR is not set
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE=y
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION=y
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_CUSTOM_VERSION_VALUE="v2.9"
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_PLATFORM="t9130"
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_FIP=y
|
||||
BR2_TARGET_ARM_TRUSTED_FIRMWARE_UBOOT_AS_BL33=y
|
||||
@@ -36,8 +38,8 @@ BR2_PACKAGE_HOST_RAUC=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
|
||||
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
# SIGN_ENABLED is not set
|
||||
DISK_IMAGE=y
|
||||
TRUSTED_KEYS=y
|
||||
TRUSTED_KEYS_DEVELOPMENT=y
|
||||
DISK_IMAGE_BOOT_BIN=y
|
||||
DISK_IMAGE_BOOT_DATA="${BINARIES_DIR}/flash-image.bin"
|
||||
DISK_IMAGE_BOOT_OFFSET=0x00200000
|
||||
|
||||
@@ -4,6 +4,25 @@ Change Log
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
|
||||
[v24.04.2][] - 2024-05-15
|
||||
-------------------------
|
||||
|
||||
### Changes
|
||||
- Add small delay in U-Boot to allow stopping boot on reference boards
|
||||
- Document how to provision the bootloader and Infix on a blank board
|
||||
- Use initial hostname from `/etc/os-release` as configuration fallback
|
||||
|
||||
### Fixes
|
||||
- Fix build regressions in `cn9130_crb_boot_defconfig` caused by upgrade
|
||||
to Buildroot v2024.02 and recent multi-key support in RAUC and U-Boot
|
||||
- Fix provisioning script after changes to make GRUB loading more robust
|
||||
- Fix missing `/etc/resolv.conf`, as noticed by `avahi-daemon`, when a
|
||||
user calls `no system` from the CLI
|
||||
- Fix #428: loss of admin account after upgrade to v24.04
|
||||
- Fix #429: failing to load `startup-config` does not trigger the fail
|
||||
secure mode, causing the system to end up in an undefined state
|
||||
|
||||
|
||||
[v24.04.1][] - 2024-05-03
|
||||
-------------------------
|
||||
|
||||
@@ -862,6 +881,7 @@ Supported YANG models in addition to those used by sysrepo and netopeer:
|
||||
|
||||
[buildroot]: https://buildroot.org/
|
||||
[UNRELEASED]: https://github.com/kernelkit/infix/compare/v24.04.0...HEAD
|
||||
[v24.04.2]: https://github.com/kernelkit/infix/compare/v24.04.1...v24.04.2
|
||||
[v24.04.1]: https://github.com/kernelkit/infix/compare/v24.04.0...v24.04.1
|
||||
[v24.04.0]: https://github.com/kernelkit/infix/compare/v24.02.0...v24.04.0
|
||||
[v24.02.0]: https://github.com/kernelkit/infix/compare/v23.11.0...v24.02.0
|
||||
|
||||
+454
-7
@@ -331,6 +331,58 @@ admin@infix.local%eth0's password: *****
|
||||
~$
|
||||
```
|
||||
|
||||
### Controlling LEDs for Production Tests
|
||||
|
||||
As part of production testing, LED verification is often expected to
|
||||
be performed. Infix uses standard [Linux support for LED
|
||||
management][6], where LEDs appear in the file system under
|
||||
/sys/class/leds and can be controlled using *echo* command. `sudo`
|
||||
privileges are required.
|
||||
|
||||
When interacting with LEDs this way, first disable the Infix *iitod*
|
||||
daemon to avoid conflicting LED control.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'initctl stop iitod'
|
||||
```
|
||||
|
||||
Then run the test, e.g., visually control that a red LED labeled
|
||||
'LAN' is working.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\:lan/trigger'
|
||||
~$ ssh admin@example.local 'echo 1 | sudo tee /sys/class/leds/red\:lan/brightness'
|
||||
```
|
||||
|
||||
To turn off the same LED, run the following commands.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'echo none | sudo tee /sys/class/leds/red\:lan/trigger'
|
||||
~$ ssh admin@example.local 'echo 0 | sudo tee /sys/class/leds/red\:lan/brightness'
|
||||
```
|
||||
When done with LED testing, enable Infix *iitod* daemon again.
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'initctl start iitod'
|
||||
```
|
||||
|
||||
### Reading Power Feed Status for Production Tests
|
||||
|
||||
As part of production tests, verification of Power Feed sensors is
|
||||
often expected to be performed. Infix uses standard [Linux support for
|
||||
Power management][7], where power sources appear in the file system
|
||||
under /sys/class/power_supply. The following example reads status of
|
||||
two power supplies named *pwr1* and *pwr2*.
|
||||
|
||||
```
|
||||
~$ ssh admin@example 'cat /sys/class/power_supply/pwr1/online'
|
||||
1
|
||||
~$ ssh admin@example 'cat /sys/class/power_supply/pwr2/online'
|
||||
0
|
||||
~$
|
||||
```
|
||||
Here, only *pwr1* happened to have power.
|
||||
|
||||
|
||||
## Examples using SSH and sysrepocfg
|
||||
|
||||
@@ -346,10 +398,10 @@ See [sysrepocfg][4] for information. Examples below will utilize
|
||||
|
||||
|
||||
- `sysrepocfg -I FILE -fjson -d DATABASE` to import/write a JSON
|
||||
formatted configuration file to the specificed database.
|
||||
formatted configuration file to the specified database.
|
||||
- `sysrepocfg -E FILE -fjson -d DATABASE` to edit/merge JSON formatted
|
||||
configuration in FILE with the specificed database.
|
||||
- `sysrepocfg -R FILE -fjson` to execute remote procdure call (RPC) defined in
|
||||
configuration in FILE with the specified database.
|
||||
- `sysrepocfg -R FILE -fjson` to execute remote procedure call (RPC) defined in
|
||||
FILE (JSON formatted).
|
||||
- `sysrepocfg -X -fjson -d DATABASE -x xpath` to read configuration or
|
||||
status from specified database.
|
||||
@@ -360,7 +412,7 @@ configuration. Exporting (-X) could operate on configuration (e.g.,
|
||||
`-d running`) or status (`-d operational`).
|
||||
|
||||
Some commands require a file as input. In examples below we assume
|
||||
it been transfered to Infix in advance, e.g. using `scp` as shown below.
|
||||
it been transferred to Infix in advance, e.g. using `scp` as shown below.
|
||||
|
||||
```
|
||||
~$ cat file.json
|
||||
@@ -388,7 +440,7 @@ it been transfered to Infix in advance, e.g. using `scp` as shown below.
|
||||
```
|
||||
See [Factory Reset](#factory-reset) for another (simpler) alternative.
|
||||
|
||||
If you only wish to copy factory config to running config the
|
||||
If it is only wished to copy factory config to running config the
|
||||
following RPC is available
|
||||
|
||||
```
|
||||
@@ -601,7 +653,7 @@ on interface *e0*.
|
||||
~$
|
||||
```
|
||||
|
||||
### Backup Configuration Using sysrepocfg And scp
|
||||
### <a id="backup"></a> Backup Configuration Using sysrepocfg And scp
|
||||
|
||||
Displaying running or startup configuration is possible with
|
||||
`sysrepocfg -X`, as shown below.
|
||||
@@ -637,7 +689,7 @@ startup configuration (not running).
|
||||
~$
|
||||
```
|
||||
|
||||
### Restore Configuration Using sysrepocfg and ssh/scp
|
||||
### <a id="restore"></a> Restore Configuration Using sysrepocfg and ssh/scp
|
||||
|
||||
|
||||
To restore a backup configuration to startup, the simplest way is to
|
||||
@@ -688,7 +740,402 @@ An alternative is to write it to a temporary file, and use `sysrepocfg
|
||||
~$
|
||||
```
|
||||
|
||||
### Read Out Hardware Information Using sysrepocfg
|
||||
|
||||
Infix supports IETF Hardware YANG with augments for ONIE formatted
|
||||
production data stored in EEPROMs, if available. See Infix [VPD
|
||||
documentation][5], as well as *ietf-hardware* and *infix-hardware* YANG
|
||||
models for details.
|
||||
|
||||
|
||||
```
|
||||
~$ ssh admin@example.local 'sysrepocfg -X -fjson -d operational -x /ietf-hardware:hardware'
|
||||
{
|
||||
"ietf-hardware:hardware": {
|
||||
"component": [
|
||||
{
|
||||
"name": "product",
|
||||
"class": "infix-hardware:vpd",
|
||||
"serial-num": "12345",
|
||||
"model-name": "Switch2010",
|
||||
"mfg-date": "2024-01-30T16:42:37+00:00",
|
||||
"infix-hardware:vpd-data": {
|
||||
"product-name": "Switch2010",
|
||||
"part-number": "ABC123-001",
|
||||
"serial-number": "007",
|
||||
"mac-address": "00:53:00:01:23:45",
|
||||
"manufacture-date": "01/30/2024 16:42:37",
|
||||
"num-macs": 11,
|
||||
"manufacturer": "ACME Production",
|
||||
"vendor": "SanFran Networks"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "USB",
|
||||
"class": "infix-hardware:usb",
|
||||
"state": {
|
||||
"admin-state": "unlocked",
|
||||
"oper-state": "enabled"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
~$
|
||||
```
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
### <a id="port-test-intro"></a> Port Configuration Example for Production Tests
|
||||
|
||||
As part of production tests, verification Ethernet ports are expected
|
||||
to be performed. A common way is to connect a test PC to two ports and
|
||||
send a *ping* traversing all ports. This can be achieved by using
|
||||
VLANs on the switch as described in this section. The resulting
|
||||
configuration file can be applied to the running configuration of the
|
||||
produced unit, e.g, use config file restore as described
|
||||
[above](#restore).
|
||||
|
||||
In this example we assume a 10 port switch, with ports e1-e10.
|
||||
|
||||
The following VLAN configuration and cable connections will be used:
|
||||
|
||||
| VLAN & Ports | Connect |
|
||||
|:------------------|:----------|
|
||||
| VLAN 10: e1 & e2 | e2 <=> e3 |
|
||||
| VLAN 20: e3 & e4 | e4 <=> e5 |
|
||||
| VLAN 30: e5 & e6 | e6 <=> e7 |
|
||||
| VLAN 40: e7 & e8 | e8 <=> e9 |
|
||||
| VLAN 50: e9 & e10 | |
|
||||
|
||||
The test PC is connected to e1 and e10 via different interfaces
|
||||
(alternatively, two different PCs are used).
|
||||
|
||||
> Configuration here is done via console. When configuring remotely
|
||||
> over SSH, remember to keep one IP address (the one used for the SSH
|
||||
> connection)! I.e., set a static IP address first, then perform the
|
||||
> VLAN configuration step."
|
||||
|
||||
#### Configuration at Start
|
||||
|
||||
Starting out, we assume a configuration where all ports are network
|
||||
interfaces (possibly with IPv6 enabled).
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
e1 ethernet LOWER-DOWN 00:53:00:06:11:01
|
||||
e2 ethernet LOWER-DOWN 00:53:00:06:11:02
|
||||
e3 ethernet LOWER-DOWN 00:53:00:06:11:03
|
||||
e4 ethernet LOWER-DOWN 00:53:00:06:11:04
|
||||
e5 ethernet LOWER-DOWN 00:53:00:06:11:05
|
||||
e6 ethernet LOWER-DOWN 00:53:00:06:11:06
|
||||
e7 ethernet LOWER-DOWN 00:53:00:06:11:07
|
||||
e8 ethernet LOWER-DOWN 00:53:00:06:11:08
|
||||
e9 ethernet LOWER-DOWN 00:53:00:06:11:09
|
||||
e10 ethernet UP 00:53:00:06:11:0a
|
||||
ipv6 fe80::0053:00ff:fe06:110a/64 (link-layer)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
#### Creating Bridge and Adding Ports
|
||||
|
||||
The example below uses Infix documentation on [creating bridges][8].
|
||||
|
||||
``` shell
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> edit interface br0
|
||||
admin@example:/config/interface/br0/> end
|
||||
admin@example:/config/> set interface e1 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e2 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e3 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e4 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e5 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e6 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e7 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e8 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e9 bridge-port bridge br0
|
||||
admin@example:/config/> set interface e10 bridge-port bridge br0
|
||||
admin@example:/config/>
|
||||
```
|
||||
|
||||
The interface status can be viewed using "show interface" after
|
||||
leaving configuration context. If configuration via SSH, first assign
|
||||
an IP address to br0 before *leaving* configuration context, e.g.,
|
||||
`set interface br0 ipv6 enabled` to get auto-configured IPv6
|
||||
address. Or skip 'leave' and stay in configuration context until done
|
||||
with all sections, including the one on [Add IP on
|
||||
Switch](#ip-on-switch).
|
||||
|
||||
``` shell
|
||||
admin@example:/config/> leave
|
||||
admin@example:/>
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge LOWER-DOWN
|
||||
├ e2 bridge LOWER-DOWN
|
||||
├ e3 bridge LOWER-DOWN
|
||||
├ e4 bridge LOWER-DOWN
|
||||
├ e5 bridge LOWER-DOWN
|
||||
├ e6 bridge LOWER-DOWN
|
||||
├ e7 bridge LOWER-DOWN
|
||||
├ e8 bridge LOWER-DOWN
|
||||
├ e9 bridge LOWER-DOWN
|
||||
└ e10 bridge FORWARDING
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
#### Assign VLANs to Ports
|
||||
|
||||
Then configure VLANs as outlined [above](#port-test-intro):
|
||||
default VID for ingress (PVID), which is done per port, and egress
|
||||
mode (untagged), which is done at the bridge level. See Infix
|
||||
[documentation for VLAN bridges][9] for more information.
|
||||
|
||||
|
||||
``` shell
|
||||
admin@example:/>
|
||||
admin@example:/> configure
|
||||
admin@example:/config/> set interface e1 bridge-port pvid 10
|
||||
admin@example:/config/> set interface e2 bridge-port pvid 10
|
||||
admin@example:/config/> set interface e3 bridge-port pvid 20
|
||||
admin@example:/config/> set interface e4 bridge-port pvid 20
|
||||
admin@example:/config/> set interface e5 bridge-port pvid 30
|
||||
admin@example:/config/> set interface e6 bridge-port pvid 30
|
||||
admin@example:/config/> set interface e7 bridge-port pvid 40
|
||||
admin@example:/config/> set interface e8 bridge-port pvid 40
|
||||
admin@example:/config/> set interface e9 bridge-port pvid 50
|
||||
admin@example:/config/> set interface e10 bridge-port pvid 50
|
||||
admin@example:/config/> edit interface br0
|
||||
admin@example:/config/interface/br0/> edit bridge vlans
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e1
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 10 untagged e2
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e3
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 20 untagged e4
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e5
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 30 untagged e6
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e7
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 40 untagged e8
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e9
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 50 untagged e10
|
||||
admin@example:/config/interface/br0/bridge/vlans/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
Interface status would now should something like the following
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge LOWER-DOWN vlan:10u pvid:10
|
||||
├ e2 bridge LOWER-DOWN vlan:10u pvid:10
|
||||
├ e3 bridge LOWER-DOWN vlan:20u pvid:20
|
||||
├ e4 bridge LOWER-DOWN vlan:20u pvid:20
|
||||
├ e5 bridge LOWER-DOWN vlan:30u pvid:30
|
||||
├ e6 bridge LOWER-DOWN vlan:30u pvid:30
|
||||
├ e7 bridge LOWER-DOWN vlan:40u pvid:40
|
||||
├ e8 bridge LOWER-DOWN vlan:40u pvid:40
|
||||
├ e9 bridge LOWER-DOWN vlan:50u pvid:50
|
||||
└ e10 bridge FORWARDING vlan:50u pvid:50
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
#### Connect Cables and Test
|
||||
|
||||
We can now connect the PC to e1 and e10, and the other ports are
|
||||
patched according to plan [above](#port-test-intro). We should get
|
||||
link up on all ports.
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e2 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e3 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e4 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e5 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e6 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e7 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e8 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e9 bridge FORWARDING vlan:50u pvid:50
|
||||
└ e10 bridge FORWARDING vlan:50u pvid:50
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
Here we use IPv6 ping all hosts (ff02::1) on PC interface eth1 to
|
||||
check reachability to the other interface of the PC.
|
||||
|
||||
> A recommendation is to use network name spaces on PC to ensure
|
||||
> traffic really goes out to switch, instead of being looped
|
||||
> internally. Or use two PCs.
|
||||
|
||||
|
||||
``` shell
|
||||
~ $ ping -L ff02::1%eth1
|
||||
PING ff02::1%eth1(ff02::1%eth1) 56 data bytes
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.496 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.514 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.473 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.736 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.563 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.507 ms
|
||||
^C
|
||||
--- ff02::1%eth1 ping statistics ---
|
||||
6 packets transmitted, 6 received, 0% packet loss, time 5108ms
|
||||
rtt min/avg/max/mdev = 0.473/0.548/0.736/0.088 ms
|
||||
~ $
|
||||
```
|
||||
|
||||
We can verify that traffic goes through the switch by disconnecting
|
||||
one of the patch cables, e.g., between e4 and e5
|
||||
|
||||
``` shell
|
||||
~ $ ping -L ff02::1%eth1
|
||||
PING ff02::1%eth1(ff02::1%eth1) 56 data bytes
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.510 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.448 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.583 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.515 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=5 ttl=64 time=0.521 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=6 ttl=64 time=0.495 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=7 ttl=64 time=0.743 ms
|
||||
... Disconnecting patch cable, thus losing packets
|
||||
... and reconnecting again. Connectivity resumes.
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=16 ttl=64 time=0.961 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=17 ttl=64 time=0.513 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=18 ttl=64 time=0.794 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=19 ttl=64 time=0.755 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=20 ttl=64 time=0.779 ms
|
||||
^C
|
||||
--- ff02::1%eth1 ping statistics ---
|
||||
20 packets transmitted, 12 received, 40% packet loss, time 19432ms
|
||||
rtt min/avg/max/mdev = 0.448/0.634/0.961/0.156 ms
|
||||
~ $
|
||||
```
|
||||
|
||||
#### <a id="ip-on-switch"></a> Add IP Address on Switch
|
||||
|
||||
The configuration so far does not provide a means to connect to the
|
||||
switch management via SSH or NETCONF, as the switch has no IP
|
||||
address. The example below shows how to add the switch to VLAN 10 (as
|
||||
used for ports e1 and e2) and enables IPv6.
|
||||
|
||||
|
||||
``` shell
|
||||
admin@example:/config/> edit interface vlan10
|
||||
admin@example:/config/interface/vlan10/> set vlan lower-layer-if br0
|
||||
admin@example:/config/interface/vlan10/> set ipv6 enabled
|
||||
admin@example:/config/interface/vlan10/> show
|
||||
type vlan;
|
||||
ipv6 {
|
||||
enabled true;
|
||||
}
|
||||
vlan {
|
||||
tag-type c-vlan;
|
||||
id 10;
|
||||
lower-layer-if br0;
|
||||
}
|
||||
admin@example:/config/interface/vlan10/>
|
||||
admin@example:/config/interface/vlan10/> end
|
||||
admin@example:/config/> edit interface br0 bridge vlans
|
||||
admin@example:/config/interface/br0/bridge/vlans/> set vlan 10 tagged br0
|
||||
admin@example:/config/interface/br0/bridge/vlans/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
Interface *vlan10* with an auto-configured IPv6 address should appear.
|
||||
|
||||
``` shell
|
||||
admin@example:/> show interfaces
|
||||
INTERFACE PROTOCOL STATE DATA
|
||||
br0 bridge vlan:10t
|
||||
│ ethernet UP 00:53:00:06:11:01
|
||||
├ e1 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e2 bridge FORWARDING vlan:10u pvid:10
|
||||
├ e3 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e4 bridge FORWARDING vlan:20u pvid:20
|
||||
├ e5 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e6 bridge FORWARDING vlan:30u pvid:30
|
||||
├ e7 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e8 bridge FORWARDING vlan:40u pvid:40
|
||||
├ e9 bridge FORWARDING vlan:50u pvid:50
|
||||
└ e10 bridge FORWARDING vlan:50u pvid:50
|
||||
lo ethernet UP 00:00:00:00:00:00
|
||||
ipv4 127.0.0.1/8 (static)
|
||||
ipv6 ::1/128 (static)
|
||||
vlan10 ethernet UP 00:53:00:06:11:01
|
||||
│ ipv6 fe80::0053:00ff:fe06:1101/64 (link-layer)
|
||||
└ br0 ethernet UP 00:53:00:06:11:01
|
||||
admin@example:/>
|
||||
```
|
||||
|
||||
When pinging "IPv6 all hosts" from the PC, there should be two
|
||||
responses for every ping, one from the switch and one from the PC
|
||||
attached to e10.
|
||||
|
||||
``` shell
|
||||
~ $ ping -L ff02::1%eth1
|
||||
PING ff02::1%eth1(ff02::1%eth1) 56 data bytes
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=1 ttl=64 time=0.508 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=1 ttl=64 time=0.968 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=2 ttl=64 time=0.866 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=2 ttl=64 time=0.867 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=3 ttl=64 time=0.467 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=3 ttl=64 time=0.469 ms
|
||||
64 bytes from fe80::488a:a35f:9d41:ac9c%eth1: icmp_seq=4 ttl=64 time=0.452 ms
|
||||
64 bytes from fe80::0053:00ff:fe06:1101%eth1: icmp_seq=4 ttl=64 time=0.453 ms
|
||||
^C
|
||||
--- ff02::1%eth1 ping statistics ---
|
||||
4 packets transmitted, 4 received, +4 duplicates, 0% packet loss, time 3031ms
|
||||
rtt min/avg/max/mdev = 0.452/0.631/0.968/0.211 ms
|
||||
~ $
|
||||
```
|
||||
|
||||
It should now be possible to access the switch from the PC via SSH (or NETCONF).
|
||||
|
||||
``` shell
|
||||
~ $ ssh admin@fe80::0053:00ff:fe06:1101%eth1
|
||||
admin@fe80::0053:00ff:fe06:1101%eth1's password:
|
||||
.-------.
|
||||
| . . | Infix -- a Network Operating System
|
||||
|-. v .-| https://kernelkit.github.io
|
||||
'-'---'-'
|
||||
|
||||
Run the command 'cli' for interactive OAM
|
||||
|
||||
admin@example:~$ exit
|
||||
~ $
|
||||
```
|
||||
|
||||
See previous sections on [backup](#backup) and [restore](#restore) of
|
||||
the created configuration.
|
||||
|
||||
|
||||
|
||||
|
||||
[1]: discovery.md
|
||||
[2]: https://rauc.io/
|
||||
[3]: boot.md#system-upgrade
|
||||
[4]: https://netopeer.liberouter.org/doc/sysrepo/libyang1/html/sysrepocfg.html
|
||||
[5]: vpd.md
|
||||
[6]: https://docs.kernel.org/leds/leds-class.html
|
||||
[7]: https://docs.kernel.org/power/power_supply_class.html
|
||||
[8]: networking.md#bridging
|
||||
[9]: networking.md#vlan-filtering-bridge
|
||||
|
||||
@@ -7,14 +7,13 @@ run name:bootstrap log:prio:user.notice norestart <pid/syslogd> \
|
||||
run name:error :1 log:console norestart if:<run/bootstrap/failure> \
|
||||
[S] /usr/libexec/confd/error --
|
||||
|
||||
service name:confd log <run/bootstrap/success> \
|
||||
service name:confd log:prio:daemon.err <run/bootstrap/success> \
|
||||
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon
|
||||
|
||||
# Bootstrap system with startup-config, ensure correct ownership
|
||||
# Bootstrap system with startup-config
|
||||
run name:startup log:prio:user.notice norestart <pid/confd> \
|
||||
[S] /usr/libexec/confd/load startup-config \
|
||||
-- Loading startup-config
|
||||
task [S] <pid/confd> chown admin:wheel /cfg/startup-config.cfg --
|
||||
|
||||
# Run if loading startup-config fails for some reason
|
||||
run name:failure log:prio:user.critical norestart <pid/confd> if:<run/startup/failure> \
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
#set DEBUG=1
|
||||
|
||||
service name:statd log [S12345] <pid/confd> statd -f -p /run/statd.pid -n -- Status daemon
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 49c660c412342e1af76735e75cd3f286ae655b82 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 28 Mar 2023 10:37:53 +0200
|
||||
Subject: [PATCH 1/4] sysrepo-plugind: add support for running in foreground
|
||||
Subject: [PATCH 1/5] sysrepo-plugind: add support for running in foreground
|
||||
with syslog
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 87224ddce4ed1a118b3efb24e0dc7167f8754c22 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Wed, 7 Jun 2023 15:19:13 +0200
|
||||
Subject: [PATCH 2/4] Refactor _sr_install_modules() to use
|
||||
Subject: [PATCH 2/5] Refactor _sr_install_modules() to use
|
||||
ly_ctx_load_module()
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
From 2d3b9524b1448dd6a8ef83e4e101e509d2552dcc Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Fri, 16 Jun 2023 12:13:16 +0200
|
||||
Subject: [PATCH 3/4] Add support for loading /etc/sysrepo/factory-default
|
||||
Subject: [PATCH 3/5] Add support for loading /etc/sysrepo/factory-default
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Follow-up to 3d07270 which adds support for compile-time defaults, this
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 7726017815243818f07936e4ff3bb979e9291f04 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Fri, 16 Jun 2023 16:17:27 +0200
|
||||
Subject: [PATCH 4/4] sysrepoctl: add support for -Cfactory -d running
|
||||
Subject: [PATCH 4/5] sysrepoctl: add support for -Cfactory -d running
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
From 49b558067ab7c6f59ec84f14cc89201b4a7bde57 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Tue, 7 May 2024 15:41:53 +0200
|
||||
Subject: [PATCH 5/5] Allow SR_EV_DONE to return any error to sysrepocfg
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Importing a system configuration with sysrepocfg the model callbacks do
|
||||
their best to validate the configuration and prepare the transaction in
|
||||
SR_EV_CHANGE. In cases when dealing with a complex system, like adding
|
||||
a route, address, or other change using iproute2, we may still get into
|
||||
trouble when actually sending off the transaction in SR_EV_DONE.
|
||||
|
||||
If something does go wrong we'd like to know this by the exit code of
|
||||
sysrepocfg, not by inspecting logs or expected system state.
|
||||
|
||||
This patch is a clumsy way of forcing the (first) error to bubble up to
|
||||
the surface and cause a non-zero exit code from sysrepocfg.
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
src/shm_sub.c | 36 ++++++++++++++++++++++++++++--------
|
||||
src/shm_sub.h | 2 +-
|
||||
src/sysrepo.c | 2 +-
|
||||
3 files changed, 30 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/src/shm_sub.c b/src/shm_sub.c
|
||||
index 99b2be37..298d7dd3 100644
|
||||
--- a/src/shm_sub.c
|
||||
+++ b/src/shm_sub.c
|
||||
@@ -1586,7 +1586,7 @@ cleanup:
|
||||
|
||||
sr_error_info_t *
|
||||
sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *orig_name, const void *orig_data,
|
||||
- uint32_t timeout_ms)
|
||||
+ uint32_t timeout_ms, sr_error_info_t **cb_err_info)
|
||||
{
|
||||
sr_error_info_t *err_info = NULL;
|
||||
struct sr_mod_info_mod_s *mod = NULL;
|
||||
@@ -1717,12 +1717,19 @@ sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *
|
||||
sr_rwunlock(&nsub->sub_shm->lock, 0, SR_LOCK_WRITE, cid, __func__);
|
||||
nsub->lock = SR_LOCK_NONE;
|
||||
|
||||
- /* we do not care about an error */
|
||||
- sr_errinfo_free(&nsub->cb_err_info);
|
||||
-
|
||||
SR_LOG_INF("EV ORIGIN: \"%s\" \"%s\" ID %" PRIu32 " priority %" PRIu32 " succeeded.",
|
||||
nsub->mod->ly_mod->name, sr_ev2str(SR_SUB_EV_DONE), nsub->mod->request_id, nsub->cur_priority);
|
||||
|
||||
+ /*
|
||||
+ * unexpected critical error, merge and cleanup, let
|
||||
+ * sysrepocfg caller handle undefined system state.
|
||||
+ */
|
||||
+ if (nsub->cb_err_info) {
|
||||
+ sr_errinfo_merge(cb_err_info, nsub->cb_err_info);
|
||||
+ nsub->cb_err_info = NULL;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
nsub->pending_event = 0;
|
||||
}
|
||||
} while (1);
|
||||
@@ -3167,7 +3174,7 @@ sr_shmsub_change_listen_check_update_edit(sr_session_ctx_t *ev_sess, const char
|
||||
sr_error_info_t *
|
||||
sr_shmsub_change_listen_process_module_events(struct modsub_change_s *change_subs, sr_conn_ctx_t *conn)
|
||||
{
|
||||
- sr_error_info_t *err_info = NULL;
|
||||
+ sr_error_info_t *err_info = NULL, *err_tmp;
|
||||
uint32_t i, data_len = 0, valid_subscr_count;
|
||||
char *data = NULL, *shm_data_ptr;
|
||||
int ret = SR_ERR_OK;
|
||||
@@ -3289,6 +3296,11 @@ process_event:
|
||||
}
|
||||
break;
|
||||
}
|
||||
+ } else if (sub_info.event == SR_SUB_EV_DONE) {
|
||||
+ if (ret && err_code == SR_ERR_OK) {
|
||||
+ /* unexpected, callback actually failed, save for later. */
|
||||
+ err_code = ret;
|
||||
+ }
|
||||
}
|
||||
|
||||
/* subscription processed this event */
|
||||
@@ -3330,6 +3342,11 @@ process_event:
|
||||
}
|
||||
break;
|
||||
case SR_SUB_EV_DONE:
|
||||
+ if (err_code) {
|
||||
+ /* prepare unexepected error from session to be written to SHM */
|
||||
+ sr_errinfo_new(&err_info, err_code, "Oups, error detected in SR_EV_DONE");
|
||||
+ }
|
||||
+ break;
|
||||
case SR_SUB_EV_ABORT:
|
||||
/* nothing to do */
|
||||
break;
|
||||
@@ -3349,14 +3366,17 @@ process_event:
|
||||
|
||||
/* SUB WRITE URGE LOCK */
|
||||
if (sr_shmsub_change_listen_relock(multi_sub_shm, SR_LOCK_WRITE_URGE, &sub_info, change_sub, change_subs->module_name,
|
||||
- ret, ev_sess, &err_info)) {
|
||||
+ ret, ev_sess, &err_tmp)) {
|
||||
+ if (err_tmp)
|
||||
+ err_info = err_tmp;
|
||||
goto cleanup;
|
||||
}
|
||||
sub_lock = SR_LOCK_WRITE_URGE;
|
||||
|
||||
/* finish event */
|
||||
- if ((err_info = sr_shmsub_multi_listen_write_event(multi_sub_shm, valid_subscr_count, err_code, &shm_data_sub, data,
|
||||
+ if ((err_tmp = sr_shmsub_multi_listen_write_event(multi_sub_shm, valid_subscr_count, err_code, &shm_data_sub, data,
|
||||
data_len, change_subs->module_name, err_code ? "fail" : "success"))) {
|
||||
+ err_info = err_tmp;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@@ -3886,7 +3906,7 @@ finish_iter:
|
||||
sr_errinfo_free(&cb_err_info);
|
||||
|
||||
/* publish "done" event */
|
||||
- if ((err_info = sr_shmsub_change_notify_change_done(&mod_info, NULL, NULL, SR_CHANGE_CB_TIMEOUT))) {
|
||||
+ if ((err_info = sr_shmsub_change_notify_change_done(&mod_info, NULL, NULL, SR_CHANGE_CB_TIMEOUT, &cb_err_info))) {
|
||||
goto cleanup_unlock;
|
||||
}
|
||||
|
||||
diff --git a/src/shm_sub.h b/src/shm_sub.h
|
||||
index 3056e4c5..460ad927 100644
|
||||
--- a/src/shm_sub.h
|
||||
+++ b/src/shm_sub.h
|
||||
@@ -144,7 +144,7 @@ sr_error_info_t *sr_shmsub_change_notify_change(struct sr_mod_info_s *mod_info,
|
||||
* @return err_info, NULL on success.
|
||||
*/
|
||||
sr_error_info_t *sr_shmsub_change_notify_change_done(struct sr_mod_info_s *mod_info, const char *orig_name,
|
||||
- const void *orig_data, uint32_t timeout_ms);
|
||||
+ const void *orig_data, uint32_t timeout_ms, sr_error_info_t **cb_err_info);
|
||||
|
||||
/**
|
||||
* @brief Notify about (generate) a change "abort" event.
|
||||
diff --git a/src/sysrepo.c b/src/sysrepo.c
|
||||
index ca2a20b7..e7c1fd0e 100644
|
||||
--- a/src/sysrepo.c
|
||||
+++ b/src/sysrepo.c
|
||||
@@ -3801,7 +3801,7 @@ store:
|
||||
}
|
||||
|
||||
/* publish "done" event, all changes were applied */
|
||||
- if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms))) {
|
||||
+ if ((err_info = sr_shmsub_change_notify_change_done(mod_info, orig_name, orig_data, timeout_ms, cb_err_info))) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -72,7 +72,7 @@ collate()
|
||||
# Report error on console, syslog, and set login banners for getty + ssh
|
||||
console_error()
|
||||
{
|
||||
logger -p user.crit -t "BOOTSTRAP" "$1"
|
||||
logger -p user.crit -t bootstrap "$1"
|
||||
|
||||
# shellcheck disable=SC3037
|
||||
/bin/echo -e "\n\n\e[31mCRITICAL BOOTSTRAP ERROR\n$1\e[0m\n" > /dev/console
|
||||
|
||||
+34
-4
@@ -19,6 +19,16 @@ banner_append()
|
||||
return 0
|
||||
}
|
||||
|
||||
note()
|
||||
{
|
||||
logger -s -I $$ -p user.notice -t load "$*"
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
logger -s -I $$ -p user.error -t load "$*"
|
||||
}
|
||||
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/confdrc
|
||||
@@ -35,17 +45,31 @@ else
|
||||
fi
|
||||
|
||||
if [ ! -f "$fn" ]; then
|
||||
logger -sik -p user.error "No such file, $fn, aborting!"
|
||||
err "No such file, $fn, aborting!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
note "Loading $config ..."
|
||||
if ! sysrepocfg -v3 -I"$fn" -f json; then
|
||||
case "$config" in
|
||||
startup-config)
|
||||
logger -sik -p user.error "Failed loading $fn, reverting to Fail Secure mode!"
|
||||
err "Failed loading $fn, reverting to Fail Secure mode!"
|
||||
# On failure to load startup-config the system is in an undefined state
|
||||
cat <<-EOF >/tmp/factory.json
|
||||
{
|
||||
"infix-factory-default:factory-default": {}
|
||||
}
|
||||
EOF
|
||||
# Default timeout is 2 seconds, allow time to clean up shm
|
||||
if ! sysrepocfg -f json -t 10 -R /tmp/factory.json; then
|
||||
rm -f /etc/sysrepo/data/*startup*
|
||||
rm -f /etc/sysrepo/data/*running*
|
||||
rm -f /dev/shm/sr_*
|
||||
killall sysrepo-plugind
|
||||
fi
|
||||
;;
|
||||
failure-config)
|
||||
logger -sik -p user.error "Failed loading $fn, aborting!"
|
||||
err "Failed loading $fn, aborting!"
|
||||
banner_append "CRITICAL ERROR: Logins are disabled, no credentials available"
|
||||
initctl -nbq runlevel 9
|
||||
;;
|
||||
@@ -53,7 +77,13 @@ if ! sysrepocfg -v3 -I"$fn" -f json; then
|
||||
|
||||
exit 1
|
||||
fi
|
||||
logger -sik -p user.notice "Loaded $fn successfully."
|
||||
|
||||
note "Loaded $fn successfully."
|
||||
if [ "$config" = "failure-config" ]; then
|
||||
banner_append "ERROR: Corrupt startup-config, system has reverted to default login credentials"
|
||||
else
|
||||
# Ensure correct ownership and permissions, in particular after factory reset
|
||||
# Created by the system, writable by any user in the admin group.
|
||||
chown root:wheel "$fn"
|
||||
chmod 0660 "$fn"
|
||||
fi
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
factorydir = $(pkgdatadir)/factory.d
|
||||
dist_factory_DATA = 10-nacm.json 10-netconf-server.json \
|
||||
10-infix-system.json 10-system.json
|
||||
10-infix-services.json 10-system.json
|
||||
|
||||
+1
-1
@@ -434,7 +434,7 @@ int cni_ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
err = sr_get_data(session, path, 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err;
|
||||
|
||||
node = lydx_get_descendant(cfg->tree, "interfaces", "interface", NULL);
|
||||
|
||||
@@ -196,7 +196,7 @@ static int change_hardware(sr_session_ctx_t *session, uint32_t sub_id, const cha
|
||||
return SR_ERR_OK;
|
||||
|
||||
rc = sr_get_data(session, XPATH_BASE_ "//.", 0, 0, 0, &cfg);
|
||||
if (rc)
|
||||
if (rc || !cfg)
|
||||
goto err;
|
||||
|
||||
rc = srx_get_diff(session, &diff);
|
||||
|
||||
@@ -1699,7 +1699,7 @@ static int ifchange(sr_session_ctx_t *session, uint32_t sub_id, const char *modu
|
||||
return err;
|
||||
|
||||
err = sr_get_data(session, "/interfaces/interface", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, (struct lyd_node **)&diff);
|
||||
|
||||
@@ -348,6 +348,11 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
}
|
||||
|
||||
rc = sr_get_data(session, "/ietf-routing:routing/control-plane-protocols//.", 0, 0, 0, &cfg);
|
||||
if (rc || !cfg) {
|
||||
NOTE("No control-plane-protocols available.");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
LY_LIST_FOR(lyd_child(cfg->tree), tmp) {
|
||||
LY_LIST_FOR(lyd_child(tmp), cplane) {
|
||||
const char *type;
|
||||
@@ -360,11 +365,13 @@ static int change_control_plane_protocols(sr_session_ctx_t *session, uint32_t su
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
sr_release_data(cfg);
|
||||
|
||||
err_close:
|
||||
fclose(fp);
|
||||
if (!staticd_enabled)
|
||||
(void)remove(STATICD_CONF_NEXT);
|
||||
sr_release_data(cfg);
|
||||
|
||||
err_abandon:
|
||||
return rc;
|
||||
}
|
||||
|
||||
+122
-80
@@ -4,6 +4,8 @@
|
||||
#include <ctype.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
@@ -442,7 +444,7 @@ static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
continue;
|
||||
|
||||
name = sr_val_to_str(old);
|
||||
DEBUG("Removing NTP server %s\n", name);
|
||||
DEBUG("Removing NTP server %s", name);
|
||||
erasef("/etc/chrony/sources.d/%s.sources", name);
|
||||
free(name);
|
||||
changes++;
|
||||
@@ -542,12 +544,11 @@ static int change_dns(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
|
||||
case SR_EV_DONE:
|
||||
/* Check if passed validation in previous event */
|
||||
if (access(RESOLV_NEXT, F_OK))
|
||||
return SR_ERR_OK;
|
||||
|
||||
(void)remove(RESOLV_PREV);
|
||||
(void)rename(RESOLV_CONF, RESOLV_PREV);
|
||||
(void)rename(RESOLV_NEXT, RESOLV_CONF);
|
||||
if (!access(RESOLV_NEXT, F_OK)) {
|
||||
(void)remove(RESOLV_PREV);
|
||||
(void)rename(RESOLV_CONF, RESOLV_PREV);
|
||||
(void)rename(RESOLV_NEXT, RESOLV_CONF);
|
||||
}
|
||||
|
||||
/* in bootstrap, another resolvconf will soon take your call */
|
||||
if (systemf("initctl -bq cond get hook/sys/up"))
|
||||
@@ -594,11 +595,10 @@ static int change_dns(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
goto fail;
|
||||
|
||||
for (size_t i = 0; i < cnt; i++) {
|
||||
const char *xpath = val[i].xpath;
|
||||
char *ptr;
|
||||
|
||||
/* Get /ietf-system:system/dns-resolver/server[name='foo'] */
|
||||
ptr = srx_get_str(session, "%s/udp-and-tcp/address", xpath);
|
||||
ptr = srx_get_str(session, "%s/udp-and-tcp/address", val[i].xpath);
|
||||
if (ptr)
|
||||
/* XXX: add support also for udp-and-tcp/port */
|
||||
fprintf(fp, "nameserver %s\n", ptr);
|
||||
@@ -637,7 +637,7 @@ static char *sys_find_usable_shell(sr_session_ctx_t *sess, char *name)
|
||||
sr_data_t *cfg;
|
||||
|
||||
snprintf(xpath, sizeof(xpath), XPATH_AUTH_"/user[name='%s']/infix-system:shell", name);
|
||||
if (!sr_get_data(sess, xpath, 0, 0, 0, &cfg)) {
|
||||
if (!sr_get_data(sess, xpath, 0, 0, 0, &cfg) && cfg) {
|
||||
struct lyd_node *node;
|
||||
|
||||
if (!lyd_find_path(cfg->tree, xpath, 0, &node))
|
||||
@@ -666,41 +666,18 @@ static char *sys_find_usable_shell(sr_session_ctx_t *sess, char *name)
|
||||
shell = LOGIN_SHELL;
|
||||
|
||||
shell = strdup(shell);
|
||||
sr_release_data(cfg);
|
||||
if (cfg)
|
||||
sr_release_data(cfg);
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
static uid_t sys_home_exists(char *user)
|
||||
{
|
||||
char path[strlen(user) + 10];
|
||||
struct stat st;
|
||||
|
||||
snprintf(path, sizeof(path), "/home/%s", user);
|
||||
if (stat(path, &st))
|
||||
return 0;
|
||||
|
||||
return st.st_uid;
|
||||
}
|
||||
|
||||
static int sys_uid_busy(char *user, uid_t uid)
|
||||
{
|
||||
struct passwd *pw;
|
||||
int rc = 0;
|
||||
|
||||
setpwent();
|
||||
while ((pw = getpwent())) {
|
||||
if (pw->pw_uid != uid)
|
||||
continue;
|
||||
if (strcmp(pw->pw_name, user))
|
||||
rc = -1;
|
||||
}
|
||||
endpwent();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sys_del_user(char *user)
|
||||
/*
|
||||
* Used both when deleting a user from the system configuration, and
|
||||
* when cleaning up stale users while adding a new user to the system.
|
||||
* In the latter case we don't need to log failure/success.
|
||||
*/
|
||||
static int sys_del_user(char *user, bool silent)
|
||||
{
|
||||
char *args[] = {
|
||||
"deluser", "--remove-home", user, NULL
|
||||
@@ -710,49 +687,71 @@ static int sys_del_user(char *user)
|
||||
erasef("/var/run/sshd/%s.keys", user);
|
||||
err = systemv_silent(args);
|
||||
if (err) {
|
||||
ERROR("Error deleting user \"%s\"\n", user);
|
||||
if (!silent)
|
||||
ERROR("Error deleting user \"%s\"", user);
|
||||
|
||||
/* Ensure $HOME is removed at least. */
|
||||
systemf("rm -rf /home/%s", user);
|
||||
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
NOTE("User \"%s\" deleted\n", user);
|
||||
|
||||
if (!silent)
|
||||
NOTE("User \"%s\" deleted", user);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int sys_add_new_user(sr_session_ctx_t *sess, char *name)
|
||||
/*
|
||||
* Helper for sys_add_user(), notice how uid:gid must never be 0:0, it
|
||||
* is reserved for the system root user which is never created/deleted.
|
||||
*
|
||||
* This function balances both /etc/passwd and /etc/group, so to make
|
||||
* things a little bit easier, we simplify the world and reserve the
|
||||
* same group name as the username. This means we can always remove
|
||||
* any stale group entries first.
|
||||
*
|
||||
* When uid:gid is non-zero we create the user and re-attach them with
|
||||
* their $HOME. When uid:gid *is* zero, however, we must make sure to
|
||||
* clean up any stale user + $HOME before calling adduser to allocate
|
||||
* a new uid:gid pair for the new user.
|
||||
*
|
||||
* If this function fails, it is up to the callee to retry the operation
|
||||
* with a zero uid:gid pair, which is the most iron clad form.
|
||||
*/
|
||||
static int sys_call_adduser(sr_session_ctx_t *sess, char *name, uid_t uid, gid_t gid)
|
||||
{
|
||||
char *shell = sys_find_usable_shell(sess, name);
|
||||
char uid_str[10];
|
||||
char *eargs[] = {
|
||||
"adduser", "-d", "-s", shell, "-u", NULL, "-H", name, NULL
|
||||
"adduser", "-d", "-s", shell, "-u", NULL, "-G", NULL, "-H", name, NULL
|
||||
};
|
||||
char *nargs[] = {
|
||||
"adduser", "-d", "-s", shell, name, NULL
|
||||
};
|
||||
bool do_chown = false;
|
||||
char uid_str[10], grp_str[strlen(name) + 1];
|
||||
char **args;
|
||||
uid_t uid;
|
||||
int err;
|
||||
|
||||
uid = sys_home_exists(name);
|
||||
if (uid) {
|
||||
if (sys_uid_busy(name, uid)) {
|
||||
/* Exists but owned by someone else. */
|
||||
ERROR("Creating user %s, /home/%s existed but was owned by another uid (%d)",
|
||||
name, name, uid);
|
||||
ERROR("Cleaning up stale /home/%s", name);
|
||||
systemf("rm -rf /home/%s", name);
|
||||
args = nargs;
|
||||
} else {
|
||||
/* Not in passwd or owned by us already */
|
||||
do_chown = true;
|
||||
DEBUG("Adding new user %s, cleaning up any stale group.", name);
|
||||
systemf("delgroup %s 2>/dev/null", name);
|
||||
|
||||
/* reusing existing uid:gid from $HOME */
|
||||
if (uid && gid) {
|
||||
/* recreate group first */
|
||||
systemf("addgroup -g %d %s", gid, name);
|
||||
|
||||
snprintf(uid_str, sizeof(uid_str), "%d", uid);
|
||||
snprintf(grp_str, sizeof(grp_str), "%s", name);
|
||||
eargs[5] = uid_str;
|
||||
eargs[7] = grp_str;
|
||||
|
||||
args = eargs;
|
||||
} else {
|
||||
DEBUG("Cleaning up any stale user %s and /home/%s", name, name);
|
||||
sys_del_user(name, true);
|
||||
|
||||
NOTE("Reusing uid %d and /home/%s for new user %s", uid, name, name);
|
||||
snprintf(uid_str, sizeof(uid_str), "%d", uid);
|
||||
eargs[5] = uid_str;
|
||||
args = eargs;
|
||||
}
|
||||
} else
|
||||
args = nargs;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Busybox implementation of 'adduser -d' sets the password
|
||||
@@ -761,14 +760,51 @@ static int sys_add_new_user(sr_session_ctx_t *sess, char *name)
|
||||
*/
|
||||
err = systemv_silent(args);
|
||||
free(shell);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new (locked) user, if they have a matching $HOME we use the
|
||||
* uid:gid from that, if they are free. In all cases of conflict with
|
||||
* any (type of) user we remove the existing $HOME and start with a new
|
||||
* uid:gid pair.
|
||||
*/
|
||||
static int sys_add_user(sr_session_ctx_t *sess, char *name)
|
||||
{
|
||||
char home[strlen(name) + 10];
|
||||
bool reused = false;
|
||||
struct stat st;
|
||||
int err;
|
||||
|
||||
/* Map users to their existing $HOME */
|
||||
snprintf(home, sizeof(home), "/home/%s", name);
|
||||
if (!stat(home, &st)) {
|
||||
/* Verify IDs aren't already used, like BusyBox adduser */
|
||||
if (getpwuid(st.st_uid) || getgrgid(st.st_uid) || getgrgid(st.st_gid)) {
|
||||
/* Exists but owned by someone else. */
|
||||
ERROR("Failed mapping user %s to /home/%s, uid:gid (%d:%d) already exists.",
|
||||
name, name, st.st_uid, st.st_gid);
|
||||
err = sys_call_adduser(sess, name, 0, 0);
|
||||
} else {
|
||||
DEBUG("Reusing uid:gid %d:%d and /home/%s for new user %s",
|
||||
st.st_uid, st.st_gid, name, name);
|
||||
err = sys_call_adduser(sess, name, st.st_uid, st.st_gid);
|
||||
if (err) {
|
||||
ERROR("Failed reusing uid:gid from /home/%s, retrying create user ...", name);
|
||||
err = sys_call_adduser(sess, name, 0, 0);
|
||||
} else
|
||||
reused = true;
|
||||
}
|
||||
} else
|
||||
err = sys_call_adduser(sess, name, 0, 0);
|
||||
|
||||
if (err) {
|
||||
ERROR("Failed creating new user \"%s\"\n", name);
|
||||
ERROR("Failed creating new user \"%s\"", name);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
NOTE("New user \"%s\" created\n", name);
|
||||
if (do_chown)
|
||||
systemf("chown -R %s:%s /home/%s", name, name, name);
|
||||
NOTE("User \"%s\" created%s.", name, reused ? ", mapped to existing home directory" : "");
|
||||
|
||||
/*
|
||||
* OpenSSH in Infix has been set up to use /var/run/sshd/%s.keys
|
||||
@@ -776,6 +812,7 @@ static int sys_add_new_user(sr_session_ctx_t *sess, char *name)
|
||||
* /home/%s/.ssh/authorized_keys file. This creates a both the
|
||||
* directory and the symlink owned by root to prevent tampering.
|
||||
*/
|
||||
DEBUG("Adding secure /home/%s/.ssh directory.", name);
|
||||
fmkpath(0750, "/home/%s/.ssh", name);
|
||||
systemf("ln -sf /var/run/sshd/%s.keys /home/%s/.ssh/authorized_keys", name, name);
|
||||
|
||||
@@ -803,7 +840,7 @@ static char *change_get_user(struct sr_change *change)
|
||||
if (user) {
|
||||
pw = getpwnam(user);
|
||||
if (!pw) {
|
||||
ERROR("Skipping attribute for missing user (%s)", user);
|
||||
/* Skipping, user probably deleted. */
|
||||
free(user);
|
||||
user = NULL;
|
||||
}
|
||||
@@ -828,7 +865,7 @@ static sr_error_t handle_sr_passwd_update(augeas *aug, sr_session_ctx_t *, struc
|
||||
assert(change->new);
|
||||
|
||||
if (change->new->type != SR_STRING_T) {
|
||||
ERROR("Internal error, expected pass to be string type\n");
|
||||
ERROR("Internal error, expected pass to be string type.");
|
||||
err = SR_ERR_INTERNAL;
|
||||
break;
|
||||
}
|
||||
@@ -841,7 +878,7 @@ static sr_error_t handle_sr_passwd_update(augeas *aug, sr_session_ctx_t *, struc
|
||||
* it as "*", meaning the user can log in with SSH keys.
|
||||
*/
|
||||
if (!hash || !strlen(hash)) {
|
||||
ERROR("Empty passwords are not allowed, disabling password login.\n");
|
||||
ERROR("Empty passwords are not allowed, disabling password login.");
|
||||
hash = "*";
|
||||
}
|
||||
if (aug_set_dynpath(aug, hash, "etc/shadow/%s/password", user))
|
||||
@@ -882,6 +919,9 @@ static sr_error_t handle_sr_shell_update(augeas *aug, sr_session_ctx_t *sess, st
|
||||
char *user;
|
||||
int err;
|
||||
|
||||
if (change->op == SR_OP_DELETED)
|
||||
return SR_ERR_OK;
|
||||
|
||||
user = change_get_user(change);
|
||||
if (!user)
|
||||
return SR_ERR_OK;
|
||||
@@ -930,7 +970,7 @@ static sr_error_t handle_sr_user_update(augeas *aug, sr_session_ctx_t *sess, str
|
||||
assert(change->new);
|
||||
|
||||
name = sr_xpath_key_value(change->new->xpath, "user", "name", &state);
|
||||
err = sys_add_new_user(sess, name);
|
||||
err = sys_add_user(sess, name);
|
||||
if (err) {
|
||||
sr_xpath_recover(&state);
|
||||
return err;
|
||||
@@ -941,7 +981,7 @@ static sr_error_t handle_sr_user_update(augeas *aug, sr_session_ctx_t *sess, str
|
||||
assert(change->old);
|
||||
|
||||
name = sr_xpath_key_value(change->old->xpath, "user", "name", &state);
|
||||
err = sys_del_user(name);
|
||||
err = sys_del_user(name, false);
|
||||
if (err) {
|
||||
sr_xpath_recover(&state);
|
||||
return err;
|
||||
@@ -973,8 +1013,9 @@ static sr_error_t generate_auth_keys(sr_session_ctx_t *session, const char *xpat
|
||||
sr_error_t err = 0;
|
||||
sr_data_t *cfg;
|
||||
|
||||
/* err may be OK and cfg NULL if 'no system' */
|
||||
err = sr_get_data(session, xpath, 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
return err;
|
||||
|
||||
auth = lydx_get_descendant(cfg->tree, "system", "authentication", NULL);
|
||||
@@ -1043,7 +1084,7 @@ static sr_error_t change_auth_done(augeas *aug, sr_session_ctx_t *session)
|
||||
*/
|
||||
err = aug_load(aug);
|
||||
if (err) {
|
||||
ERROR("Error loading files into aug tree\n");
|
||||
ERROR("Error loading files into aug tree.");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
@@ -1079,7 +1120,7 @@ static sr_error_t change_auth_done(augeas *aug, sr_session_ctx_t *session)
|
||||
return err;
|
||||
}
|
||||
|
||||
DEBUG("Changes to authentication saved\n");
|
||||
DEBUG("Changes to authentication saved.");
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
@@ -1326,8 +1367,9 @@ static int change_hostname(sr_session_ctx_t *session, uint32_t sub_id, const cha
|
||||
|
||||
nm = srx_get_str(session, "%s", xpath);
|
||||
if (!nm) {
|
||||
/* XXX: derive from global "options.h" or /usr/share/factory/ */
|
||||
nm = strdup("infix");
|
||||
nm = fgetkey("/etc/os-release", "DEFAULT_HOSTNAME");
|
||||
if (nm)
|
||||
nm = strdup(nm);
|
||||
if (!nm) {
|
||||
err = SR_ERR_NO_MEMORY;
|
||||
goto err;
|
||||
|
||||
@@ -226,7 +226,7 @@ static int change(sr_session_ctx_t *session, uint32_t sub_id, const char *module
|
||||
}
|
||||
|
||||
err = sr_get_data(session, CFG_XPATH "//.", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, &diff);
|
||||
|
||||
@@ -278,7 +278,7 @@ static int change(sr_session_ctx_t *session, uint32_t sub_id, const char *module
|
||||
}
|
||||
|
||||
err = sr_get_data(session, XPATH "//.", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
if (err || !cfg)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, &diff);
|
||||
|
||||
@@ -104,7 +104,7 @@ static sr_data_t *get(sr_session_ctx_t *session, sr_event_t event, const char *x
|
||||
return NULL; /* Don't care about CHANGE, ABORT, etc. */
|
||||
|
||||
snprintf(path, sizeof(path), "%s//.", xpath);
|
||||
if (sr_get_data(session, path, 0, 0, 0, &cfg)) {
|
||||
if (sr_get_data(session, path, 0, 0, 0, &cfg) || !cfg) {
|
||||
ERROR("no data for %s", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,13 @@ int asprintf(char **strp, const char *fmt, ...);
|
||||
|
||||
/*
|
||||
* Only NOTICE and above are logged by default, see setlogmask() for details.
|
||||
* Use `set DEBUG=1` at top of package/confd/confd.conf for logmask = debug
|
||||
*/
|
||||
#define DEBUG(fmt, ...) do { if (debug) syslog(LOG_DEBUG, fmt, ##__VA_ARGS__); } while (0)
|
||||
#define INFO(fmt, ...) syslog(LOG_INFO, fmt, ##__VA_ARGS__)
|
||||
#define NOTE(fmt, ...) syslog(LOG_NOTICE, fmt, ##__VA_ARGS__)
|
||||
#define WARN(fmt, ...) syslog(LOG_WARNING, "%s: " fmt, __func__, ##__VA_ARGS__)
|
||||
#define ERROR(fmt, ...) syslog(LOG_ERR, "%s: " fmt, __func__, ##__VA_ARGS__)
|
||||
#define ERRNO(fmt, ...) syslog(LOG_ERR, "%s: " fmt ": %s", __func__, ##__VA_ARGS__, strerror(errno))
|
||||
#define WARN(fmt, ...) syslog(LOG_WARNING, fmt, ##__VA_ARGS__)
|
||||
#define ERROR(fmt, ...) syslog(LOG_ERR, fmt, ##__VA_ARGS__)
|
||||
#define ERRNO(fmt, ...) syslog(LOG_ERR, fmt ": %s", ##__VA_ARGS__, strerror(errno))
|
||||
|
||||
#endif /* CONFD_COMMON_H_ */
|
||||
|
||||
+3
-1
@@ -526,9 +526,11 @@ int main(int argc, char *argv[])
|
||||
struct statd statd = {};
|
||||
int log_opts = LOG_USER;
|
||||
sr_conn_ctx_t *sr_conn;
|
||||
char *env;
|
||||
int err;
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "-d")) {
|
||||
env = getenv("DEBUG");
|
||||
if (env || (argc > 1 && !strcmp(argv[1], "-d"))) {
|
||||
log_opts |= LOG_PERROR;
|
||||
debug = 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user