1#!/bin/bash -eu
2
3# SPDX-License-Identifier: Apache-2.0
4# Copyright 2020-2021 YADRO
5
6log_msg() {
7    echo "$@"
8}
9
10log_err() {
11    echo "$@" >&2
12}
13
14read_hw_mac() {
15    local iface="$1"
16    cat /sys/class/net/"${iface}"/address 2>/dev/null ||:
17}
18
19set_hw_mac() {
20    local iface="$1"
21    local mac="$2"
22    local up=""
23    if ip link show dev "${iface}" | grep -q "${iface}:.*\<UP\>" 2>/dev/null; then
24        up=true
25    fi
26
27    if [ "${up}" = true ]; then
28        ip link set dev "${iface}" down ||:
29    fi
30    ip link set dev "${iface}" address "${mac}" ||:
31    if [ "${up}" = true ]; then
32        ip link set dev "${iface}" up ||:
33    fi
34}
35
36set_fw_env_mac() {
37    local iface="$1"
38    local mac="$2"
39    local envname=""
40    case "${iface}" in
41        eth0)
42            envname="ethaddr"
43        ;;
44        eth1)
45            envname="eth1addr"
46        ;;
47        *)
48            return 1
49        ;;
50    esac
51    if ! fw_setenv "$envname" "$mac"; then
52        return 1
53    fi
54}
55
56######## MAIN ########
57FRU_DBUS_SERVICE="xyz.openbmc_project.FruDevice"
58FRU_DBUS_OBJECT_TEMPLATE="\/xyz.*_Motherboard"
59FRU_DBUS_INTERFACE="xyz.openbmc_project.FruDevice"
60fru_dbus_object=""
61
62######## Parse D-bus data ########
63n=1
64while true; do
65    fru_dbus_object=$(busctl tree ${FRU_DBUS_SERVICE} 2>/dev/null |
66        sed -n "s/^.*\(${FRU_DBUS_OBJECT_TEMPLATE}\).*$/\1/p"
67      ) && [ -n "${fru_dbus_object}" ] && break
68    if [ ${n} -lt 15 ]; then
69        n=$((n+1))
70        sleep 1
71    else
72        log_err "Failed to find baseboard FRU object"
73        exit 1
74    fi
75done
76
77dbusData=$(dbus-send --system --print-reply=literal \
78            --dest=${FRU_DBUS_SERVICE} \
79            "${fru_dbus_object}" \
80            org.freedesktop.DBus.Properties.GetAll \
81            string:${FRU_DBUS_INTERFACE} 2>/dev/null ||:)
82
83if [ -z "${dbusData}" ]; then
84    log_err "Failed to get data from D-Bus"
85    exit 1
86fi
87
88# This awk script matches strings 010xyyyyyyyyyyyy, where x is interface index
89# and yyyyyyyyyyyy - interface mac address.
90# The output would be in form ethx=yy:yy:yy:yy:yy:yy
91macsList=$(echo "${dbusData}" | \
92    awk '/BOARD_INFO_AM[0-9]+\s+variant\s+010[0-9a-f]{13}/{
93        totalMacDigits=12
94        singleOctet=2
95        offset=4
96        printf "eth%s=", substr($3, offset, 1)
97        for(i = 1 + offset; i < totalMacDigits + offset; i += singleOctet) {
98            printf "%s", substr($3, i, singleOctet)
99            if (i <= totalMacDigits + offset - singleOctet) printf ":"
100        }
101        printf "\n"
102    }'||:)
103
104if [ -z "${macsList}" ]; then
105    log_err "Failed to get MAC address"
106    exit 1
107fi
108
109# Get hardware model name from PRODUCT_PRODUCT_NAME
110# Examples:
111#  'VEGMAN N110 Server' -> 'VEGMAN-N110'
112#  'TATLIN.ARCHIVE.XS' -> 'TATLIN-ARCHIVE-XS'
113modelName=$(echo "${dbusData}" | \
114        awk '/PRODUCT_PRODUCT_NAME\s+variant\s+/{
115                if ($3 ~ /^VEGMAN/) {
116                    printf "%s-%s", $3, $4
117                } else if ($3 ~ /^TATLIN/) {
118                    print gensub(/\./, "-", "g", $3)
119                }
120             }'||:)
121
122serialNumber=$(echo "${dbusData}" | \
123     awk '/PRODUCT_SERIAL_NUMBER\s+variant\s+/{print $3}'||:)
124if [ -z "${serialNumber}" ]; then
125    log_err "Failed to get product Serial Number"
126    exit 1
127fi
128
129# shellcheck disable=SC1091
130source /etc/os-release
131
132
133######## Check and set MAC addresses ########
134retCode=0
135IFS='
136'
137for line in ${macsList} ; do
138    ifaceName=$(echo "${line}" | cut -f 1 -d '=' || :)
139    macAddr=$(echo "${line}" | cut -f 2- -d '=' || :)
140    if [ -n "${ifaceName}" ] && [ -n "${macAddr}" ]; then
141        curMacAddr=$(read_hw_mac "${ifaceName}")
142        if [ "${macAddr}" != "${curMacAddr}" ] ; then
143            log_msg "Changing MAC address for ${ifaceName}: ${curMacAddr} -> ${macAddr}"
144            # A factory assigned address was found, and it is newly assigned.
145            # Update the active interface and save the new value to the u-boot
146            # environment.
147            if ! set_hw_mac "${ifaceName}" "${macAddr}"; then
148                log_err "Failed to set address"
149                retCode=1
150            fi
151            if ! set_fw_env_mac "${ifaceName}" "${macAddr}"; then
152                log_err "Failed to set boot env for ${ifaceName}"
153                retCode=1
154            fi
155        fi
156    fi
157
158    [ ${retCode} -eq 0 ] || exit ${retCode}
159done
160
161
162######## Check and set hostname ########
163hostname=$(hostname)
164if [ "${OPENBMC_TARGET_MACHINE}" = "${hostname}" ] ; then
165    hostname="${modelName,,}-${serialNumber}"
166    log_msg "Changing hostname to ${hostname}"
167
168    if ! hostnamectl set-hostname "${hostname}"; then
169        log_err "Failed to set new hostname"
170        exit 1
171    fi
172fi
173
174######## Run optional scripts ########
175snmp_handler="/usr/sbin/snmpd-generate-conf.sh"
176if [ -x ${snmp_handler} ]; then
177    if ! ${snmp_handler} "${modelName}"; then
178        log_err "Failed to setup snmp"
179    fi
180fi
181