Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ Licensed under the Apache License, Version 2.0

- [rfcapi API Reference](rfcapi/docs/README.md)
- [tr181api API Reference](tr181api/docs/README.md)
- [RFC Parameter Runtime Priority](docs/rfc-parameter-runtime-priority.md)
- [Build System Instructions](.github/instructions/build-system.instructions.md)
- [C Embedded Standards](.github/instructions/c-embedded.instructions.md)
- [L2 Test Runner Agent](.github/agents/l2-test-runner.agent.md)
158 changes: 158 additions & 0 deletions docs/rfc-parameter-runtime-priority.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,106 @@ If duplicate keys exist across files, effective precedence is determined by merg

---

## Path 4: XConf Write Path and Firmware Upgrade Behavior

This is the path used by `rfcMgr` when it applies a fresh XConf response to the device.
Understanding this path is essential for explaining why parameter values set by XConf
persist across firmware upgrades.

### How XConf Writes Persist

When `rfcMgr` successfully downloads an XConf response, it calls
`processXconfResponseConfigDataPart()`, which writes every parameter received from XConf
directly into `/opt/secure/RFC/tr181store.ini`.

`/opt/secure/` is a **persistent storage partition** that survives firmware upgrades.
A firmware upgrade does not erase this partition.

This means any value XConf has ever pushed to a device remains at **priority 2** in the
runtime stack until XConf explicitly sends a different value or the parameter is manually
cleared.

### Firmware Upgrade Sequence

On every `rfcMgr` startup, `IsNewFirmwareFirstRequest()` compares the firmware string
stored in `/opt/secure/RFC/.version` against the currently running firmware:

```cpp
// rfc_xconf_handler.cpp
bool RuntimeFeatureControlProcessor::IsNewFirmwareFirstRequest(void)
{
if ((_last_firmware.empty()) ||
(!_firmware_version.empty() &&
(_last_firmware.compare(_firmware_version) != 0)))
{
return true; // new firmware detected
}
return false;
}
```

When this returns `true`, `clearDB()` is called **before** the XConf response is
processed. `clearDB()` is not a factory reset — it truncates `tr181store.ini` and signals
`tr69hostif` to flush its in-memory state, then immediately re-populates the store from
the fresh XConf response.

```mermaid
sequenceDiagram
participant rfcMgr
participant VersionFile as /opt/secure/RFC/.version
participant XConf as XConf Server
participant Store as tr181store.ini

rfcMgr->>VersionFile: Read _last_firmware
rfcMgr->>rfcMgr: IsNewFirmwareFirstRequest()
alt firmware changed
rfcMgr->>XConf: GET featureControl/getSettings
XConf-->>rfcMgr: { param: value, ... }
rfcMgr->>Store: clearDB() — truncate
rfcMgr->>Store: processXconfResponseConfigDataPart() — write XConf values
rfcMgr->>VersionFile: WriteFile(".version", new_fw)
else same firmware
rfcMgr->>XConf: GET featureControl/getSettings
XConf-->>rfcMgr: { param: value, ... }
rfcMgr->>Store: processXconfResponseConfigDataPart() — write XConf values
end
```

### Why a Parameter Value Is the Same After Upgrade

A firmware upgrade does not notify XConf. XConf continues to send the same value for a
parameter until an operator changes the XConf server-side configuration for that
device/account combination. The sequence is:

1. XConf sends `param=value` → written to `tr181store.ini`
2. Firmware is upgraded
3. `clearDB()` truncates `tr181store.ini`
4. XConf is queried again and **sends the same `param=value`**
5. `param=value` is written back into `tr181store.ini`

The XML `<default>` element in `data-model.xml` is at priority 5 and is never consulted
because the XConf response at priority 2 fills `tr181store.ini` before any fallback is
needed.

### Key Files Involved

| File | Partition | Survives upgrade | Purpose |
|---|---|---|---|
| `/opt/secure/RFC/tr181store.ini` | `/opt/secure/` (persistent) | **Yes** | XConf-applied parameter values |
| `/opt/secure/RFC/.version` | `/opt/secure/` (persistent) | **Yes** | Last firmware that processed XConf |
| `/opt/secure/RFC/bootstrap.ini` | `/opt/secure/` (persistent) | **Yes** | Bootstrap-backed overrides |
| `/tmp/data-model.xml` | tmpfs | No | XML factory defaults — lowest priority |

### Changing an XConf-Applied Value

| Method | Command | Effect |
|---|---|---|
| Update XConf rule | Change device/account rule in XConf server | Permanent; takes effect on next `rfcMgr` run |
| Local override | `tr181 -s <param> -v <value> -n string` | Temporary; overwritten on next successful XConf fetch |
| Clear the parameter | `tr181 -c <param>` | Removes entry from store; XML default takes effect until XConf runs again |

---

## Combined Conceptual Priority

If you want one combined conceptual ordering across all runtime sources, the safest summary is:
Expand Down Expand Up @@ -298,6 +398,64 @@ Why:
- `validateAgainstDataModel()` had already captured XML `defaultValue`
- the live request path returns the XML default, not `/etc/rfcdefaults`

### Example F: parameter value is the same after firmware upgrade

Consider:

```text
Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.TR069support.Enable
```

State before upgrade:

- XConf has a rule pushing `Enable=true` for this device
- `/opt/secure/RFC/tr181store.ini` contains `...TR069support.Enable=true`
- `data-model.xml` has `<default type="factory" value="false"/>`

After upgrading from one firmware version to another:

Returned value:

```text
true
```

Why:

- `/opt/secure/` is persistent — `tr181store.ini` survives the upgrade
- `rfcMgr` detects a new firmware via `/opt/secure/RFC/.version`, calls `clearDB()` which
truncates `tr181store.ini`, then immediately queries XConf
- XConf still has the same rule and returns `Enable=true`
- `processXconfResponseConfigDataPart()` writes `true` back into `tr181store.ini`
- `tr181store.ini` is at priority 2; the XML default of `false` at priority 5 is never reached

**Diagnostic check:**

```bash
# Confirm XConf set the value
grep -i "TR069support\|Feature Name" /opt/logs/rfcscript.log | tail -20

# Confirm clearDB ran on the upgrade
grep -i "Clearing DB\|last_firmware\|different" /opt/logs/rfcscript.log | head -20

# Confirm version file was updated
cat /opt/secure/RFC/.version
```

Expected log evidence of firmware-change detection and re-apply:

```
GetLastProcessedFirmware: [<previous-firmware-version>]
Last Image version <previous-firmware-version> and current image version \
<new-firmware-version> are different
[clearDB] Clearing DB
[processXconfResponseConfigDataPart] Feature Name \
[Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.TR069support.Enable] Value[true]
```

To change the value, update the XConf server-side rule — not the firmware. See
[Path 4](#path-4-xconf-write-path-and-firmware-upgrade-behavior) for the options.

---

## See Also
Expand Down
48 changes: 36 additions & 12 deletions rfcapi/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

`librfcapi` is a C/C++ library that provides the canonical interface for reading and writing RFC (Remote Feature Control) parameters on RDK devices. It resolves parameter values from a layered file store: XConf-applied overrides first, then component defaults. All other RDK components use this library instead of accessing the INI files directly.
`librfcapi` is a C/C++ library that provides the canonical interface for reading and writing RFC (Remote Feature Control) parameters on RDK devices. When the TR-181 data model service (`tr69hostif`) is ready, reads are served from the live data model. Before that service is ready, the library falls back to layered file-backed state: XConf-applied overrides first, bootstrap values where applicable, and component defaults last. All other RDK components use this library instead of accessing the INI files directly.

---

Expand All @@ -27,20 +27,37 @@ graph TB

### Lookup Priority

For TR181-style RFC keys (`Device.*`), the effective priority is:

1. Live TR181 data model via `tr69hostif` when `/tmp/.tr69hostif_http_server_ready` exists
2. `/opt/secure/RFC/tr181store.ini` when the host interface is not ready
3. `/opt/secure/RFC/bootstrap.ini` when the host interface is not ready and the key is not present in `tr181store.ini`
4. `/tmp/rfcdefaults.ini` as the final fallback

For legacy `RFC_xxxx` keys without a dot, the lookup remains file-based and reads `/opt/secure/RFC/rfcVariable.ini` directly.

Comment on lines +30 to +38
```mermaid
flowchart LR
A[getRFCParameter called] --> B{Key starts with RFC_\nand no dot?}
B -->|Yes| C[Read rfcVariable.ini]
B -->|No| D[Read tr181store.ini]
D --> E{Found?}
E -->|Yes| F[Return value]
E -->|No| G[Read rfcdefaults.ini\n merged from /etc/rfcdefaults/]
G --> H{Found?}
H -->|Yes| F
H -->|No| I[Return WDMP_FAILURE]
C --> J{Found?}
J -->|Yes| F
J -->|No| I
B -->|No| D{tr69hostif ready?}
D -->|Yes| E[Read live data model\nvia localhost HTTP]
D -->|No| F[Read tr181store.ini]
F --> G{Found?}
G -->|Yes| H[Return value]
G -->|No| I[Read bootstrap.ini]
I --> J{Found?}
J -->|Yes| H
J -->|No| K[Read rfcdefaults.ini\nmerged from /etc/rfcdefaults/]
K --> L{Found?}
L -->|Yes| H
L -->|No| M[Return WDMP_FAILURE]
C --> N{Found?}
N -->|Yes| H
N -->|No| M
E --> O{Found?}
O -->|Yes| H
O -->|No| M
```

---
Expand Down Expand Up @@ -78,7 +95,7 @@ typedef enum {

### `getRFCParameter()`

Reads a single RFC parameter value from the local file store.
Reads a single RFC parameter value from the live TR181 data model when available, otherwise from the local fallback stores.

**Signature (non-RDKB):**
```c
Expand Down Expand Up @@ -230,6 +247,13 @@ bool isFileInDirectory(const char *filename, const char *directory);

`getRFCParameter` merges all `.ini` files under `/etc/rfcdefaults/` into `/tmp/rfcdefaults.ini` on first access if the merged file does not exist. Component default files must be named `<componentname>.ini` and placed in `/etc/rfcdefaults/`.

`/etc/rfcdefaults/*.ini` only affects the result when a requested key was not resolved from a higher-priority source. In practice that means:

1. If `tr69hostif` is up, the live data model wins and defaults are not consulted.
2. If `tr69hostif` is not up, `tr181store.ini` wins over defaults.
3. `bootstrap.ini` also wins over defaults during the pre-hostif phase.
4. Defaults are used only as the final fallback for missing keys.

```mermaid
graph TD
A["/etc/rfcdefaults/\nauth.ini\ntelemetry.ini\nip.ini\n..."] -->|"concat at runtime"| B["/tmp/rfcdefaults.ini"]
Expand Down
Loading
Loading