pdr: Add pldm_pdr_delete_by_sensor_id() API

Adds a new libpldm API to delete the PDR record from the PDR repo
based on the sensor ID of the PDR. This API is used when a state
sensor PDR record needs to be deleted based on the sensor ID
and return the corresponding record handle deleted.

Change-Id: Ia78c24467f6460fdcf456a31ac851fa7b0c0b385
Signed-off-by: Pavithra Barithaya <pavithrabarithaya07@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0e093f6..36ca07f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 - utils: Introduce `pldm_edac_crc8()`
 - pdr: Add pldm_pdr_delete_by_effecter_id() API
 - platform: Add encode req for GetPDRRepositoryInfo
+- pdr: Add pldm_pdr_delete_by_sensor_id() API
 
 - oem: ibm: Add boot side rename state set and enum
 
diff --git a/include/libpldm/pdr.h b/include/libpldm/pdr.h
index 08ab38b..25faf6f 100644
--- a/include/libpldm/pdr.h
+++ b/include/libpldm/pdr.h
@@ -305,6 +305,21 @@
 	uint16_t *entity_type, uint16_t *entity_instance_num,
 	uint16_t *container_id);
 
+/** @brief delete the state sensor PDR by sensor id
+ *
+ *  @param[in] repo - opaque pointer acting as a PDR repo handle
+ *  @param[in] sensor_id - sensor ID of the PDR
+ *  @param[in] is_remote - if true, then the PDR is not from this terminus
+ *  @param[out] record_handle - if non-NULL, then record handle of the
+ *  sensor PDR deleted
+ *
+ *  @return 0 on success, with the record handle of the deleted sensor PDR
+ *  stored in record_handle if record_handle is non-NULL, or -EINVAL when
+ *  repo is NULL, or -ENOENT if the  sensor id is not found in the repo.
+ */
+int pldm_pdr_delete_by_sensor_id(pldm_pdr *repo, uint16_t sensor_id,
+				 bool is_remote, uint32_t *record_handle);
+
 /* =========================== */
 /* Entity Association PDR APIs */
 /* =========================== */
diff --git a/src/dsp/pdr.c b/src/dsp/pdr.c
index e549fbb..69229d5 100644
--- a/src/dsp/pdr.c
+++ b/src/dsp/pdr.c
@@ -403,6 +403,80 @@
 	       record_handle <= last_record_handle;
 }
 
+LIBPLDM_CC_NONNULL
+static int decode_pldm_state_sensor_pdr(uint8_t *data, uint32_t size,
+					struct pldm_state_sensor_pdr *pdr)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc = 0;
+	rc = pldm_msgbuf_init_errno(buf, sizeof(struct pldm_state_sensor_pdr),
+				    (uint8_t *)data, size);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, pdr->hdr.record_handle);
+	pldm_msgbuf_extract(buf, pdr->hdr.version);
+	pldm_msgbuf_extract(buf, pdr->hdr.type);
+	pldm_msgbuf_extract(buf, pdr->hdr.record_change_num);
+	pldm_msgbuf_extract(buf, pdr->hdr.length);
+	pldm_msgbuf_extract(buf, pdr->terminus_handle);
+	pldm_msgbuf_extract(buf, pdr->sensor_id);
+	pldm_msgbuf_extract(buf, pdr->entity_type);
+	pldm_msgbuf_extract(buf, pdr->entity_instance);
+	pldm_msgbuf_extract(buf, pdr->container_id);
+	pldm_msgbuf_extract(buf, pdr->sensor_init);
+	pldm_msgbuf_extract(buf, pdr->sensor_auxiliary_names_pdr);
+	pldm_msgbuf_extract(buf, pdr->composite_sensor_count);
+
+	return pldm_msgbuf_complete(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int pldm_pdr_delete_by_sensor_id(pldm_pdr *repo, uint16_t sensor_id,
+				 bool is_remote, uint32_t *record_handle)
+{
+	pldm_pdr_record *record;
+	pldm_pdr_record *prev = NULL;
+	int rc = 0;
+	int found;
+	struct pldm_state_sensor_pdr pdr;
+
+	if (!repo) {
+		return -EINVAL;
+	}
+
+	record = repo->first;
+
+	while (record != NULL) {
+		if (!record->data) {
+			continue;
+		}
+
+		rc = decode_pldm_state_sensor_pdr(record->data, record->size,
+						  &pdr);
+		if (rc) {
+			return rc;
+		}
+
+		if (record->is_remote != is_remote ||
+		    pdr.hdr.type != PLDM_STATE_SENSOR_PDR) {
+			record = record->next;
+			continue;
+		}
+		found = pdr.sensor_id == sensor_id;
+		if (found) {
+			if (record_handle) {
+				*record_handle = record->record_handle;
+			}
+			prev = pldm_pdr_get_prev_record(repo, record);
+			return pldm_pdr_remove_record(repo, record, prev);
+		}
+		record = record->next;
+	}
+	return -ENOENT;
+}
+
 LIBPLDM_ABI_TESTING
 int pldm_pdr_find_child_container_id_index_range_exclude(
 	const pldm_pdr *repo, uint16_t entity_type, uint16_t entity_instance,
diff --git a/tests/dsp/pdr.cpp b/tests/dsp/pdr.cpp
index 6fe8d9c..0d19de2 100644
--- a/tests/dsp/pdr.cpp
+++ b/tests/dsp/pdr.cpp
@@ -719,6 +719,97 @@
 #endif
 
 #ifdef LIBPLDM_API_TESTING
+TEST(PDRAccess, testRemoveBySensorIDDecodeFailure)
+{
+    auto repo = pldm_pdr_init();
+    ASSERT_NE(repo, nullptr);
+
+    // Create a deliberately undersized PDR record
+    size_t invalidPdrSize = sizeof(pldm_state_sensor_pdr) - 4; // Invalid size
+    std::vector<uint8_t> entry(invalidPdrSize, 0);
+    pldm_state_sensor_pdr* pdr = new (entry.data()) pldm_state_sensor_pdr;
+    pdr->hdr.type = PLDM_STATE_SENSOR_PDR;
+    pdr->sensor_id = 50; // random ID
+
+    uint32_t record_handle = 0;
+    EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1,
+                           &record_handle),
+              0);
+    // Attempt to delete the malformed record by effecter_id
+    uint32_t removed_record_handle = 0;
+    int rc =
+        pldm_pdr_delete_by_sensor_id(repo, 50, false, &removed_record_handle);
+
+    // We expect a failure from decode_pldm_state_effecter_pdr
+    EXPECT_NE(rc, 0);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo),
+              1u); // Record remains in the repo
+
+    pldm_pdr_destroy(repo);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(PDRAccess, testRemoveBySensorID)
+{
+    auto repo = pldm_pdr_init();
+    ASSERT_NE(repo, nullptr);
+
+    size_t pdrSize = 0;
+    pdrSize =
+        sizeof(pldm_state_sensor_pdr) + sizeof(state_sensor_possible_states);
+    std::vector<uint8_t> entry{};
+    entry.resize(pdrSize);
+
+    pldm_state_sensor_pdr* pdr = new (entry.data()) pldm_state_sensor_pdr;
+
+    pdr->hdr.type = PLDM_STATE_SENSOR_PDR;
+    pdr->sensor_id = 1;
+    uint32_t handle = 1;
+    EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &handle),
+              0);
+
+    pdr->sensor_id = 2;
+    handle = 2;
+    EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &handle),
+              0);
+
+    pdr->sensor_id = 10;
+    handle = 0;
+    EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &handle),
+              0);
+
+    pdr->sensor_id = 20;
+    handle = 10;
+    EXPECT_EQ(pldm_pdr_add(repo, entry.data(), entry.size(), false, 1, &handle),
+              0);
+
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 4u);
+
+    uint32_t removed_record_handle{};
+    int rc =
+        pldm_pdr_delete_by_sensor_id(repo, 1, false, &removed_record_handle);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(removed_record_handle, 1);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 3u);
+
+    // Error case where the effceter ID is not present in the repo
+    uint32_t removed_rec_handle{};
+    rc = pldm_pdr_delete_by_sensor_id(repo, 15, false, &removed_rec_handle);
+    EXPECT_EQ(rc, -ENOENT);
+    EXPECT_EQ(removed_rec_handle, 0);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 3u);
+
+    rc = pldm_pdr_delete_by_sensor_id(repo, 10, false, &removed_record_handle);
+    EXPECT_EQ(rc, 0);
+    EXPECT_EQ(removed_record_handle, 3);
+    EXPECT_EQ(pldm_pdr_get_record_count(repo), 2u);
+
+    pldm_pdr_destroy(repo);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
 TEST(PDRAccess, testGetTerminusHandle)
 {