Commands
cli
cli öffnet die eigentliche Switch-CLI, also die FastPath-/Switching-Ebene.
Dort arbeitest du mit Dingen wie:
show vlan
show running-config
configure
interface 0/1
switchport mode access
vlan database
write memoryDas ist die Cisco-ähnliche Oberfläche für:
- VLANs
- Ports
- Trunks
- Layer-2/teilweise Layer-3
- laufende Switch-Konfiguration
Das Problem: Diese CLI verwaltet hauptsächlich den laufenden FastPath-Zustand. write memory meldet zwar Erfolg, aber die UniFi-Firmware baut FastPath beim Start wieder aus /tmp/system.cfg beziehungsweise der von ubntconf erzeugten Startup-Konfiguration auf. Deshalb verschwinden die Änderungen nach einem Reboot.
mca-cli-op
mca-cli-op gehört zur UniFi-Management-Ebene.
Auf deinem Switch ist es nur ein Symlink:
/usr/bin/mca-cli-op -> mcadEs startet also das Programm mcad in einem speziellen Betriebsmodus.
mcad kümmert sich um Dinge wie:
- Adoption
- Controller-Kommunikation
- Inform-Nachrichten
- Provisionierung
- Managementstatus
- Werksreset
- Speichern und Laden von UniFi-Konfiguration
mgmt.is_defaultmgmt.is_setup_completed
Mit Argumenten wird es normalerweise als interner Einzelbefehl verwendet, zum Beispiel:
mca-cli-op inform
mca-cli-op set-inform http://controller:8080/inform
mca-cli-op set-defaultDer Firmware-Wrapper ruft solche Befehle selbst auf. set-inform wird beispielsweise direkt an mca-cli-op weitergereicht, während set-default einen Werksreset einleitet.
Ohne Argument:
mca-cli-opöffnet es die interaktive Management-CLI:
UniFi#Das ist keine Linux-Shell und auch nicht dieselbe CLI wie cli. Deshalb kennt sie kein echo, awk, find oder Shell-Pipes.
Vereinfacht dargestellt
SSH-Shell
│
├── cli
│ └── FastPath-Switch-CLI
│ ├── VLANs
│ ├── Ports
│ ├── Trunks
│ └── running-config
│
└── mca-cli-op / mcad
└── UniFi-Management-Stack
├── Adoption
├── Provisionierung
├── Controller/Inform
├── mgmt-Konfiguration
└── Default-/Setup-StatusUSW-Pro-48-US.7.5.6# mca-cli-op help 2>&1
UniFi Command Line Interface - Ubiquiti Networks
info display device information
set-default restore to factory default
set-inform <inform_url> attempt inform URL (e.g. set-inform http://192.168.0.8:8080/inform)
upgrade <firmware_url> upgrade firmware (e.g. upgrade http://192.168.0.8/unifi_fw.bin)
fwupdate --url <firmware_url|firmware_name> [--dl-only] [--md5sum <sum_of_fw>]
[--keep-firmware] [--keep-running] [--reboot-sys]
new firmware update command
reboot reboot the device
USW-Pro-48-US.7.5.6# printf 'Rückgabecode: %s\n' "$?"
Rückgabecode: 0
USW-Pro-48-US.7.5.6# mca-cli-op info
Model: USW-Pro-48
Version: 7.5.6.17090
MAC Address: f4:e2:c6:c0:ec:22
IP Address: 172.16.1.22
Hostname: USW-Pro-48
Uptime: 3893 seconds
NTP: Not synchronized
Status: Unable to resolve (http://unifi:8080/inform)
USW-Pro-48-US.7.5.6# echo '=== Managementwerte ==='
=== Managementwerte ===
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# for datei in \
> /tmp/system.cfg \
> /tmp/default.cfg \
> /etc/persistent/cfg/mgmt
> do
> if [ -r "$datei" ]; then
> echo
> echo "--- $datei ---"
>
> awk -F= '
> $1 == "mgmt.is_default" ||
> $1 == "mgmt.is_setup_completed" ||
> $1 == "mgmt.cfgversion" ||
> $1 == "mgmt.selfrun" ||
> $1 ~ /^mgmt\.servers\.[0-9]+\.url$/ ||
> $1 ~ /^mgmt\.led_/ {
> printf "%d: %s\n", NR, $0
> }
> ' "$datei"
> else
> echo
> echo "--- $datei fehlt oder ist nicht lesbar ---"
> fi
> done
--- /tmp/system.cfg ---
2: mgmt.is_default=true
--- /tmp/default.cfg ---
2: mgmt.is_default=true
--- /etc/persistent/cfg/mgmt fehlt oder ist nicht lesbar ---
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# awk '
> {
> text[NR] = $0
> }
>
> /set_cfg_save/ {
> for (nummer = NR - 15; nummer <= NR + 20; nummer++) {
> if (nummer > 0) {
> zeigen[nummer] = 1
> }
> }
> }
>
> END {
> for (nummer = 1; nummer <= NR; nummer++) {
> if (zeigen[nummer]) {
> printf "%5d: %s\n", nummer, text[nummer]
> }
> }
> }
> ' /usr/etc/syswrapper.sh
1030: export SSH_CLIENT=""
1031: export UI_SSH_SAFE_CONTEXT="$LOG $TAIL_PID"
1032: setsid "$SCRIPT" "$cmd" "$@" >>$LOG 2>&1 </dev/null 3<&- &
1033: wait $TAIL_PID
1034: cat <&3 # Dump anything that tail might have missed
1035: exit 0
1036: fi
1037:
1038: set -- $UI_SSH_SAFE_CONTEXT
1039: [ $# -eq 2 ] || return
1040: local LOG=$1
1041: local TAIL_PID=$2
1042: local CLEAN_UP="kill $TAIL_PID 2>/dev/null; rm -f $LOG"
1043: trap "$CLEAN_UP; exit 0" HUP INT QUIT TERM EXIT
1044: }
1045: set_cfg_save() {
1046: if [ ! -e "$NEED_CFG_SAVE_FILE" ]; then
1047: log "Creating $NEED_CFG_SAVE_FILE"
1048: touch "$NEED_CFG_SAVE_FILE"
1049: fi
1050:
1051: pidof $SYSMON || syswrapper.sh cfg_save_check
1052:
1053: state_reload
1054: }
1055:
1056: # mac2serial <mac>
1057: mac2serial() {
1058: echo $1 | sed -e 's/://g' -e 'y/ABCDEF/abcdef/'
1059: }
1060:
1061: # pkill_generic <process_name> [signal] [args]
1062: pkill_generic() {
1063: local process=$1
1064: local signal=$2
1065: shift; shift;
2975: if type 'is_support_stacking' 2> /dev/null | grep -q 'function' && is_support_stacking ; then
2976: role="$(swctrl mgmt stack-info | sed -n 's/role=\([a-z]*\),.*/\1/p')"
2977:
2978: if [ "$role" = "controller" ]; then
2979: log "[Stack] $cmd remote unit $unit_number"
2980: swctrl mgmt stack-run -b "$unit_number" "syswrapper.sh $cmd"
2981: fi
2982: fi
2983: ;;
2984: set-etherlight-mode)
2985: exit_if_busy $cmd $*
2986: exit_if_state_lock_failed $cmd $*
2987: _set_etherlight_mode "$1"
2988: rc=$?
2989: if [ $rc -eq 1 ]; then
2990: set_cfg_save
2991: fi
2992: state_unlock
2993: ;;
2994: set-stack-unit-etherlight-mode)
2995: exit_if_busy $cmd $*
2996: exit_if_state_lock_failed $cmd $*
2997: _set_etherlight_mode "$1"
2998: rc=$?
2999: if [ $rc -eq 1 ]; then
3000: set_cfg_save
3001: fi
3002: state_unlock
3003: ;;
3004: set-etherlight-device-type)
3005: exit_if_busy $cmd $*
3006: exit_if_state_lock_failed $cmd $*
3007: pkill_generic led_locate.sh -f
3008:
3009: led_device_type_mode=3
3010: _set_etherlight_mode $led_device_type_mode
3011: _set_port_bitmap_color $led_device_type_mode $@
3012:
3013: state_unlock
3014: ;;
3015: set-port-locate)
3016: exit_if_busy $cmd $*
3017: exit_if_state_lock_failed $cmd $*
3018: pkill_generic led_locate.sh -f
3019:
3020: led_port_locate_mode=4
3136: if [ "$2" = "nopersist" ]; then
3137: persist=0
3138: log "[apply-config] persist=false - config will not be saved to flash"
3139: fi
3140: state_lock
3141: before=$(sed "s/\..*//" /proc/uptime)
3142: before_mca_pid=$(cat_file /var/run/mcad.0.pid)
3143: full_provision=0
3144: if ! do_fast_apply; then
3145: lockfile_retry 60 "$NEED_CFG_SAVE_LOCK_FILE" || exit 1
3146: full_provision=1
3147: /usr/etc/rc.d/rc restart
3148: unlockfile "$NEED_CFG_SAVE_LOCK_FILE"
3149: fi
3150: if [ "$persist" = 1 ]; then
3151: pidof $SYSMON && set_cfg_save
3152: fi
3153: state_reload
3154: now=$(sed "s/\..*//" /proc/uptime)
3155: delta=$((now - before))
3156: log "Provision took $delta sec, full=$full_provision, persist=$persist"
3157: # Remove deltas that may be due to time syncs, etc.
3158: if [ "$delta" -ge 0 -a "$delta" -lt 300 ]; then
3159: if [ "$full_provision" = 1 ]; then
3160: add_stat /var/run/provision_time.total $delta
3161: add_stat /var/run/provision_time.count 1
3162: else
3163: add_stat /var/run/fast_apply_time.total $delta
3164: add_stat /var/run/fast_apply_time.count 1
3165: fi
3166: fi
3167: # Update fw auto-rollback flag based on controller config
3168: # Default is enabled — only disable when explicitly set to "disabled"
3169: if [ "$(get_config_value /tmp/system.cfg switch.fw_auto_rollback.status)" = "disabled" ]; then
3170: touch /var/run/fw_auto_rollback_disabled
3171: else
3195: if [ ! -f /tmp/previous.cfg ]; then
3196: log "[apply-previous-config] previous.cfg not found, skipping rollback"
3197: state_unlock
3198: exit 1
3199: fi
3200: log "[apply-previous-config] reverting to previous configuration"
3201: cp -f /tmp/previous.cfg /tmp/system.cfg
3202: before=$(sed "s/\..*//" /proc/uptime)
3203: full_provision=0
3204: if ! do_fast_apply; then
3205: lockfile_retry 60 "$NEED_CFG_SAVE_LOCK_FILE" || exit 1
3206: full_provision=1
3207: /usr/etc/rc.d/rc restart
3208: unlockfile "$NEED_CFG_SAVE_LOCK_FILE"
3209: fi
3210: pidof $SYSMON && set_cfg_save
3211: state_reload
3212: now=$(sed "s/\..*//" /proc/uptime)
3213: delta=$((now - before))
3214: log "[apply-previous-config] Rollback took $delta sec, full=$full_provision"
3215: state_unlock
3216: ;;
3217: apply-stack-unit-config)
3218: run_ssh_safe "$@"
3219: if type 'is_support_stacking' 2> /dev/null | grep -q 'function' && is_support_stacking ; then
3220: unit="$1"
3221: systemcfg_path="$2"
3222: role="$(swctrl mgmt stack-info | sed -n 's/role=\([a-z]*\),.*/\1/p')"
3223: before=$(sed "s/\..*//" /proc/uptime)
3224: STACKING_PATH="/tmp/stacking"
3225:
3226: if [ ! -f "$systemcfg_path" ]; then
3227: log "[error] Configuration file not found: $systemcfg_path."
3228: exit 1
3229: fi
3230:
3269: /bin/sh /var/run/if_up_hook.$ifname
3270: fi
3271: ;;
3272: soft-restart)
3273: exit_if_busy $cmd $*
3274: run_ssh_safe "$@"
3275: state_lock
3276: lockfile_retry 30 "$NEED_CFG_SAVE_LOCK_FILE" || exit 1
3277: /usr/etc/rc.d/rc restart
3278: unlockfile "$NEED_CFG_SAVE_LOCK_FILE"
3279: state_reload
3280: state_unlock
3281: ;;
3282: save-config)
3283: state_lock
3284: set_cfg_save
3285: state_unlock
3286: ;;
3287: reload)
3288: exit_if_busy $cmd $*
3289: exit_if_state_lock_failed $cmd $*
3290: state_reload
3291: state_unlock
3292: ;;
3293: set-ready)
3294: # called by mcagent
3295: state_lock
3296: set_state_ready
3297: state_unlock
3298: # Mark boot as successful for auto-rollback (once per boot)
3299: if [ ! -f /var/run/boot_success_marked ] && [ -x /sbin/ubntbox ] && \
3300: grep -q "ubnt_active_slot=" /proc/cmdline 2>/dev/null; then
3301: ubntbox bootsel mark-success && touch /var/run/boot_success_marked
3302: fi
3303: # Update fw auto-rollback flag based on controller config
3304: # Default is enabled — only disable when explicitly set to "disabled"
3656: ble_stp $@
3657: ;;
3658: ble-check)
3659: ble_check $@
3660: ;;
3661: kick-sta)
3662: kick_sta $1
3663: ;;
3664: kick-sta-on)
3665: kick_sta_on $2 $1 $3
3666: ;;
3667: block-sta)
3668: driver_kick_block_sta $1
3669: add_mac /etc/persistent/cfg/blocked_sta "$1"
3670: state_lock
3671: set_cfg_save
3672: state_unlock
3673: ;;
3674: unblock-sta)
3675: del_mac /etc/persistent/cfg/blocked_sta "$1"
3676: driver_unblock_sta $1
3677: state_lock
3678: set_cfg_save
3679: state_unlock
3680: ;;
3681: apply-blocked-sta)
3682: driver_apply_blocklist
3683: ;;
3684: apply-blocked-sta-ifup)
3685: driver_apply_blocklist_ifup $1
3686: ;;
3687: redirector-init)
3688: guest_redirector_init_iptables "$@"
3689: ;;
3690: redirector-deinit)
3691: guest_redirector_deinit_iptables "$@"
3692: ;;
3693: authorize-guest)
3694: guest_authorize "$@"
3695: ;;
3696: unauthorize-guest)
3697: guest_unauthorize "$@"
3698: kick_sta $1
USW-Pro-48-US.7.5.6#Das ist der bisher wichtigste Fund. Die Firmware besitzt tatsächlich einen vorgesehenen internen Speicherweg:
syswrapper.sh save-config
↓
set_cfg_save
↓
/var/run/need_cfg_save
↓
sysmon
↓
syswrapper.sh cfg_save_check
↓
sanitize_cfg
↓
cfgmtd -w -p /etc /tmp/system.cfgsave-config ist also etwas anderes als FastPaths write memory. Es speichert die UniFi-Systemkonfiguration, aus der beim Boot anschließend /tmp/.startup-config für den Switch erzeugt wird. Genau diese Bootkette wurde in deinen bisherigen Tests sichtbar.
Der Haken bleibt: cfg_save_check schreibt nur, wenn nach sanitize_cfg eine nichtleere Datei /etc/persistent/cfg/mgmt existiert. Ohne sie versucht es lediglich, eine alte Managementdatei aus dem Flash wiederherzustellen.
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# rm -rf /tmp/usw-flash-read
USW-Pro-48-US.7.5.6# mkdir -p /tmp/usw-flash-read
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# cfgmtd -r \
> -p /tmp/usw-flash-read \
> -f /tmp/usw-flash-read/system.cfg
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# printf 'cfgmtd Rückgabecode: %s\n' "$?"
cfgmtd Rückgabecode: 0
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# echo
USW-Pro-48-US.7.5.6# echo '=== Gelesene Dateien ==='
=== Gelesene Dateien ===
USW-Pro-48-US.7.5.6# find /tmp/usw-flash-read \
> -maxdepth 4 \
> -type f \
> -exec ls -l {} \;
-rw------- 1 ui admin 1018 Jun 22 10:03 /tmp/usw-flash-read/system.cfg
-rw------- 1 ui admin 83 Jan 1 1970 /tmp/usw-flash-read/persistent/dropbear_ed25519_host_key
-rw------- 1 ui admin 89 Jan 1 1970 /tmp/usw-flash-read/persistent/dropbear_ed25519_host_key.pub
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# echo
USW-Pro-48-US.7.5.6# echo '=== Managementdatei ==='
=== Managementdatei ===
USW-Pro-48-US.7.5.6# if [ -r /tmp/usw-flash-read/persistent/cfg/mgmt ]; then
> awk -F= '
> $1 == "mgmt.is_default" ||
> $1 == "mgmt.is_setup_completed" ||
> $1 == "mgmt.cfgversion" ||
> $1 == "mgmt.selfrun" ||
> $1 ~ /^mgmt\.servers\.[0-9]+\.url$/ ||
> $1 ~ /^mgmt\.led_/ {
> printf "%d: %s\n", NR, $0
> }
> ' /tmp/usw-flash-read/persistent/cfg/mgmt
> else
> echo 'Keine persistente Managementdatei im gelesenen Flash-Stand'
> fi
Keine persistente Managementdatei im gelesenen Flash-Stand
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# echo
USW-Pro-48-US.7.5.6# echo '=== Managementwerte aus system.cfg ==='
=== Managementwerte aus system.cfg ===
USW-Pro-48-US.7.5.6# if [ -r /tmp/usw-flash-read/system.cfg ]; then
> awk -F= '
> $1 == "mgmt.is_default" ||
> $1 == "mgmt.is_setup_completed" ||
> $1 == "mgmt.cfgversion" ||
> $1 == "mgmt.selfrun" ||
> $1 ~ /^mgmt\.servers\.[0-9]+\.url$/ ||
> $1 ~ /^mgmt\.led_/ {
> printf "%d: %s\n", NR, $0
> }
> ' /tmp/usw-flash-read/system.cfg
> else
> echo 'Keine system.cfg gelesen'
> fi
2: mgmt.is_default=true
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# rm -rf /tmp/usw-mgmt-runtime-test
USW-Pro-48-US.7.5.6# mkdir -p /tmp/usw-mgmt-runtime-test
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# if [ -d /etc/persistent/cfg ]; then
> cp -a /etc/persistent/cfg /tmp/usw-mgmt-runtime-test/cfg.before
> else
> echo 'cfg war nicht vorhanden' > /tmp/usw-mgmt-runtime-test/cfg-was-missing
> fi
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# mkdir -p /etc/persistent/cfg
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# cat > /etc/persistent/cfg/mgmt <<'EOF'
> mgmt.is_default=false
> mgmt.is_setup_completed=true
> EOF
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# chmod 600 /etc/persistent/cfg/mgmt
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# echo '=== Angelegte Laufzeitdatei ==='
=== Angelegte Laufzeitdatei ===
USW-Pro-48-US.7.5.6# cat /etc/persistent/cfg/mgmt
mgmt.is_default=false
mgmt.is_setup_completed=true
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# echo
USW-Pro-48-US.7.5.6# echo '=== Interner Adoptionstest ==='
=== Interner Adoptionstest ===
USW-Pro-48-US.7.5.6# /sbin/syswrapper.sh check-is-adopted
sh: false: unknown operand
USW-Pro-48-US.7.5.6# printf 'Rückgabecode: %s\n' "$?"
Rückgabecode: 2
USW-Pro-48-US.7.5.6#
USW-Pro-48-US.7.5.6# echo
USW-Pro-48-US.7.5.6# echo '=== Laufzeitstatus ==='
=== Laufzeitstatus ===
USW-Pro-48-US.7.5.6# printf 'IsDefault='
IsDefault=USW-Pro-48-US.7.5.6# cat /proc/ubnthal/status/IsDefault
true
USW-Pro-48-US.7.5.6# printf 'LED-Pattern='
LED-Pattern=USW-Pro-48-US.7.5.6# cat /proc/gpio/led_pattern
2
USW-Pro-48-US.7.5.6#