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
162 changes: 161 additions & 1 deletion source/dcautil/dcautil.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include <sys/select.h>
#include <sys/inotify.h>
#include "dca.h"
#include "dcautil.h"
#include "telemetry2_0.h"
Expand All @@ -28,6 +34,130 @@
#include "legacyutils.h"
#include "persistence.h"

/**
* @brief Wait for the backup_logs completion sentinel before grepping PreviousLogs.
*
* Strategy:
* 1. Fast path: sentinel already present → return true immediately.
* 2. Set up inotify on BACKUP_LOGS_DONE_DIR (/tmp) for IN_CREATE | IN_MOVED_TO.
* 3. Re-check after watch is established to close the creation race window.
* 4. select() loop with 2 s heartbeat; exit when sentinel appears or
* BACKUP_LOGS_SYNC_TIMEOUT_S total seconds have elapsed.
*
* This is a **soft gate**: if the function returns false (timeout or inotify
* failure) the caller still proceeds — previous-log grep runs against whatever
* files are present, and a warning is logged.
*
* @return true sentinel detected within timeout
* @return false timeout elapsed or inotify initialisation failed
*/
static bool waitForBackupLogsDone(void)
{
/* Fast path: sentinel already written by backup_logs */
if (access(BACKUP_LOGS_DONE_FLAG, F_OK) == 0)
{
T2Info("backup_logs sentinel %s already present\n", BACKUP_LOGS_DONE_FLAG);
return true;
}

int ifd = inotify_init1(IN_CLOEXEC);
if (ifd < 0)
{
T2Error("inotify_init1 failed (errno=%d); proceeding without backup_logs sync\n", errno);
return false;
}

int wd = inotify_add_watch(ifd, BACKUP_LOGS_DONE_DIR, IN_CREATE | IN_MOVED_TO);
if (wd < 0)
{
T2Error("inotify_add_watch on %s failed (errno=%d); proceeding without backup_logs sync\n",
BACKUP_LOGS_DONE_DIR, errno);
close(ifd);
return false;
}

/* Re-check after watch is set — closes the race window between the first
* access() call and establishing the watch. */
if (access(BACKUP_LOGS_DONE_FLAG, F_OK) == 0)
{
T2Info("backup_logs sentinel detected (race resolved): %s\n", BACKUP_LOGS_DONE_FLAG);
inotify_rm_watch(ifd, wd);
close(ifd);
return true;
}

struct timespec deadline;
if (clock_gettime(CLOCK_MONOTONIC, &deadline) != 0)
{
T2Error("clock_gettime failed (errno=%d); proceeding without backup_logs sync\n", errno);
inotify_rm_watch(ifd, wd);
close(ifd);
return false;
}
deadline.tv_sec += BACKUP_LOGS_SYNC_TIMEOUT_S;

T2Info("Waiting up to %ds for backup_logs sentinel: %s\n",
BACKUP_LOGS_SYNC_TIMEOUT_S, BACKUP_LOGS_DONE_FLAG);

bool found = false;
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];

while (!found)
{
/* Check deadline */
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now) == 0 && now.tv_sec >= deadline.tv_sec)
{
T2Warning("backup_logs sentinel not present after %ds; proceeding without sync\n",
BACKUP_LOGS_SYNC_TIMEOUT_S);
break;
}
Comment on lines +108 to +114

struct timeval tv = {2, 0};
fd_set fds;
FD_ZERO(&fds);
FD_SET(ifd, &fds);

int ret = select(ifd + 1, &fds, NULL, NULL, &tv);
if (ret < 0)
{
if (errno == EINTR)
{
continue;
}
T2Error("select() failed (errno=%d) waiting for backup_logs sentinel\n", errno);
break;
}
if (ret == 0)
{
continue; /* 2 s heartbeat — loop back to check deadline */
}

ssize_t len = read(ifd, buf, sizeof(buf));
if (len <= 0)
{
continue;
}

ssize_t offset = 0;
while (offset < len)
{
struct inotify_event *ev = (struct inotify_event *)(buf + offset);
if (ev->len > 0 && strcmp(ev->name, BACKUP_LOGS_DONE_FILENAME) == 0)
{
T2Info("backup_logs sentinel created: %s\n", BACKUP_LOGS_DONE_FLAG);
found = true;
break;
}
offset += (ssize_t)(sizeof(struct inotify_event) + ev->len);
}
}

inotify_rm_watch(ifd, wd);
close(ifd);
return found;
}

/**
* @brief Get the Grep Results object. Main function called by rest of the consumers.
*
Expand All @@ -50,14 +180,44 @@
return T2ERROR_FAILURE;
}

/* Synchronize with backup_logs before grepping the previous-log directory.
* backup_logs populates PREVIOUS_LOGS_PATH; reading it before the write is
* complete yields incomplete or empty results. This is a soft gate — if the
* sentinel does not appear within BACKUP_LOGS_SYNC_TIMEOUT_S seconds we
* still proceed so telemetry is never permanently blocked. */
if (customLogPath != NULL && strcmp(customLogPath, PREVIOUS_LOGS_PATH) == 0)
{
if (!waitForBackupLogsDone())
Comment on lines +183 to +190
{
T2Warning("%s: backup_logs sentinel absent; grepping %s with potentially incomplete data\n",
__FUNCTION__, PREVIOUS_LOGS_PATH);
}
}
Comment on lines +183 to +195
Comment on lines +183 to +195
Comment on lines +188 to +195

getDCAResultsInVector(*GSP, markerList, check_rotated, customLogPath);
if (isClearSeekMap)
{
int count = (*GSP)->execCounter;
freeGrepSeekProfile(*GSP);
*GSP = createGrepSeekProfile(count);
}

/* Signal that telemetry previous-log grep is complete.
* Downstream consumers (e.g. uploadSTBLogs) wait for this sentinel
* before starting the log upload to avoid incomplete telemetry data. */
Comment on lines +204 to +206
if (customLogPath != NULL && strcmp(customLogPath, PREVIOUS_LOGS_PATH) == 0)
{
FILE *fp = fopen(TELEMETRY_PREVLOGS_DONE_FLAG, "w");

Check failure

Code scanning / CodeQL

File created without restricting permissions High

A file may be created here with mode 0666, which would make it world-writable.
if (fp != NULL)
{
fclose(fp);
T2Info("Created telemetry previous-logs sentinel: %s\n", TELEMETRY_PREVLOGS_DONE_FLAG);
}
Comment on lines +209 to +214
else
{
T2Error("%s: Failed to create sentinel %s (errno=%d)\n",
__FUNCTION__, TELEMETRY_PREVLOGS_DONE_FLAG, errno);
}
}
T2Debug("%s --out\n", __FUNCTION__);
return T2ERROR_SUCCESS;
}
Expand Down
10 changes: 10 additions & 0 deletions source/dcautil/dcautil.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@

#define TOPTEMP "/tmp/t2toplog"
#define DCADONEFLAG "/tmp/.dca_done"
#define TELEMETRY_PREVLOGS_DONE_FLAG "/tmp/.telemetry_prevlogs_done"

Comment on lines 31 to 34
#define PREVIOUS_LOG "PREVIOUS_LOG"
#define PREVIOUS_LOGS_VAL "1"
#define PREVIOUS_LOGS_PATH "/opt/logs/PreviousLogs/"
#define BACKUP_LOGS_DONE_FLAG "/tmp/.backup_logs_done"
#define BACKUP_LOGS_DONE_DIR "/tmp"
#define BACKUP_LOGS_DONE_FILENAME ".backup_logs_done"
Comment on lines +38 to +40

#ifdef GTEST_ENABLE
# define BACKUP_LOGS_SYNC_TIMEOUT_S 2
#else
# define BACKUP_LOGS_SYNC_TIMEOUT_S 60
#endif

typedef struct _GrepResult
{
Expand Down
Loading