Compare commits

..
8 Commits
Author SHA1 Message Date
avjarami 55bb762479 Release v1.15.0
This release contains:

* A new header event_id, this value can be used by a probe or probes to
  group multiple records when these records are the result of a unique
  event in the system, i.e. an update could create multiple records and
  these records could be tagged with a single event id.

* Telemetry record logging, this change adds a log of records reported
  to telemetry server. The log can be accessed using telemctl journal
  sub-command.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-12 15:13:26 -07:00
avjarami be78434f2c journal dir creation
Adding entry to template for journal directory

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-12 14:45:42 -07:00
avjarami 8edd75df21 Running uncrustify
Fixing syntax and indentation inconsistencies.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-12 14:30:11 -07:00
avjarami dc4f4e67e4 Addressing review
Addressing comments from first code review and fixing travis-ci
check_journal error in prune test.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-09 13:16:34 -08:00
avjarami 41e9090dd8 Refactoring tests to use fixture
Using fixture for event id tests instead of creating a new record for
every event id test.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-09 13:16:34 -08:00
avjarami cbcc5e24d5 Tests for journal feature
Adding tests for journal exported functions.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-09 13:16:34 -08:00
avjarami 4a9d8212c8 Basic record journaling
This change adds a logging mechanism for records submitted to telemetry
client. A basic journal is added to keep track of the last 100 valid
records that the daemon processed.

This journal can be queried by a new command line interface that was
added as part of the change 'telem_journal'.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-09 13:16:34 -08:00
avjarami 8a9dbc7528 Adding event_id header
* An event_id header is needed to group records when multiple records
  are generated by same event.

* Adding new available parameter to telem_record_gen, making possible
  for this utility to tag multiple records with same event_id.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-02-28 21:54:46 -08:00
27 changed files with 1633 additions and 126 deletions
+3 -3
View File
@@ -25,9 +25,9 @@ addons:
- libdw-dev
install:
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
- tar -xvf check-0.10.0.tar.gz
- pushd check-0.10.0 && ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu && make -j48 && sudo make install && popd
- wget https://github.com/libcheck/check/releases/download/0.12.0/check-0.12.0.tar.gz
- tar -xvf check-0.12.0.tar.gz
- pushd check-0.12.0 && ./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu && make -j48 && sudo make install && popd
script:
- ./configure && make && make check
+1
View File
@@ -20,6 +20,7 @@ include $(top_srcdir)/src/local.mk
include $(top_srcdir)/src/data/local.mk
include $(top_srcdir)/src/nica/local.mk
include $(top_srcdir)/src/probes/local.mk
include $(top_srcdir)/src/journal/local.mk
include $(top_srcdir)/tests/local.mk
release:
+1 -1
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [1.14.3], [https://clearlinux.org/])
AC_INIT([telemetrics-client], [1.15.0], [https://clearlinux.org/])
AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects])
AM_SILENT_RULES([yes])
+10 -2
View File
@@ -91,6 +91,10 @@ telem_restart() {
telem_start
}
telem_journal_cli() {
telem_journal "$@"
}
usage() {
format=' %-10s %s\n'
printf "\n"
@@ -102,12 +106,14 @@ usage() {
printf "$format" "is-active" "Checks if telemd is active"
printf "$format" "opt-in" "Opts in to telemetry, and starts telemetry services"
printf "$format" "opt-out" "Opts out of telemetry, and stops telemetry services"
printf "$format" "journal" "Prints telemd journal contents. Use -h argument with"
printf "$format" "" "command for more options"
printf "\n"
exit 2
}
if [ $# -ne 1 ]; then
usage
if [ "$1" != "journal" ] && [ $# -ne 1 ]; then
usage
fi
if [ $EUID -ne 0 ]; then
@@ -129,6 +135,8 @@ case $SUBCOMMAND in
telem_restart ;;
is-active)
telem_is_active ;;
journal)
telem_journal_cli "$@" ;;
*)
notice "Unknown command passed to $SCRIPT"
usage ;;
+2 -1
View File
@@ -36,7 +36,8 @@ static const char *header_names[] = {
TM_SYSTEM_NAME_STR,
TM_BOARD_NAME_STR,
TM_CPU_MODEL_STR,
TM_BIOS_VERSION_STR
TM_BIOS_VERSION_STR,
TM_EVENT_ID_STR
};
const char *get_header_name(int ind)
+11 -2
View File
@@ -38,6 +38,7 @@
#define TM_BOARD_NAME 11
#define TM_CPU_MODEL 12
#define TM_BIOS_VERSION 13
#define TM_EVENT_ID 14
#define TM_RECORD_VERSION_STR "record_format_version"
#define TM_CLASSIFICATION_STR "classification"
@@ -53,14 +54,22 @@
#define TM_BOARD_NAME_STR "board_name"
#define TM_CPU_MODEL_STR "cpu_model"
#define TM_BIOS_VERSION_STR "bios_version"
#define TM_EVENT_ID_STR "event_id"
#define NUM_HEADERS 14
#define NUM_HEADERS 15
#define EVENT_ID_ALPHAB "0123456789abcdef"
#define EVENT_ID_LEN 32
/* Journal */
#define JOURNAL_PATH "/var/log/telemetry/journal"
#define JOURNAL_TMPDIR "/var/log/telemetry/"
/* For internal library usage. Bump the version whenever we change the record
* structure (e.g. adding or removing a header field). Note that the value
* should be an unsigned int.
*/
static const uint32_t RECORD_FORMAT_VERSION = 3;
static const uint32_t RECORD_FORMAT_VERSION = 4;
#define TM_SITE_VERSION_FILE "/etc/os-release"
#define TM_DIST_VERSION_FILE "/usr/lib/os-release"
+1
View File
@@ -1,5 +1,6 @@
d @localstatedir@/lib/telemetry 0755 telemetry telemetry -
d @localstatedir@/spool/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/oops 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/pstore 0750 telemetry telemetry -
+111
View File
@@ -0,0 +1,111 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "common.h"
#include "journal.h"
void print_usage(void)
{
printf(" Usage\n");
printf(" -r, --record_id Print record to stdout\n");
printf(" -e, --event_id List records with specific event_id\n");
printf(" -c, --classification List records with specific classification\n");
printf(" -b, --boot_id List records with specific boot_id\n");
printf(" -V, --verbose Verbose output\n");
printf(" -h, --help Display this help message\n");
}
int main(int argc, char **argv)
{
int rc = EXIT_SUCCESS;
int count = 0;
int verbose_output = 0;
char *boot_id = NULL;
char *record_id = NULL;
char *event_id = NULL;
char *classification = NULL;
struct TelemJournal *telem_journal = NULL;
/** opts */
int c;
int opt_index = 0;
struct option opts[] = {
{ "record_id", 1, NULL, 'r' },
{ "event_id", 1, NULL, 'e' },
{ "classification", 1, NULL, 'c' },
{ "boot_id", 1, NULL, 'b' },
{ "verbose", 0, NULL, 'V' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
while ((c = getopt_long(argc, argv, "r:e:c:b:Vh", opts, &opt_index)) != -1) {
switch (c) {
case 'r':
record_id = optarg;
break;
case 'e':
event_id = optarg;
break;
case 'c':
classification = optarg;
break;
case 'b':
boot_id = optarg;
break;
case 'V':
verbose_output = 1;
break;
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case '?':
/** default */
print_usage();
exit(EXIT_FAILURE);
}
}
if ((telem_journal = open_journal(JOURNAL_PATH))) {
if (verbose_output) {
fprintf(stdout, "%-30s %-27s %-32s %-32s %-36s\n", "Classification", "Time stamp",
"Record ID", "Event ID", "Boot ID");
}
count = print_journal(telem_journal, classification, record_id, event_id, boot_id);
if (verbose_output) {
fprintf(stdout, "Total records: %d\n", count);
}
close_journal(telem_journal);
} else {
fprintf(stderr, "Unable to open journal\n");
rc = EXIT_FAILURE;
}
free(classification);
free(record_id);
free(event_id);
free(boot_id);
return rc;
}
+648
View File
@@ -0,0 +1,648 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
#define ID_LEN 32
#define BOOTID_LEN 37 // Includes the \n character at the end
#define BOOTID_FILE "/proc/sys/kernel/random/boot_id"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <inttypes.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include "util.h"
#include "common.h"
#include "journal.h"
/*
* Packs journal entry values into one line string separated
* by '\036' RS.
*
* @param entry A pointer to a JournalEntry struct with values
* to serialize.
* @param buff A pointer to be initialized and set with the
* serialized values.
*
* @return 0 on success, -1 on failure.
*/
static int serialize_journal_entry(struct JournalEntry *entry, char **buff)
{
int rc = 0;
if (entry == NULL) {
return -1;
}
if (asprintf(buff, "%s\036%lld\036%s\036%s\036%s", entry->record_id, (long long int)entry->timestamp,
entry->classification, entry->event_id, entry->boot_id) < 0) {
#ifdef DEBUG
fprintf(stderr, "Error: unable to serialize data in buff\n");
#endif
rc = -1;
}
return rc;
}
/**
* Frees journal entry struct members and journal entry pointer.
*
* @param the pointer to the entry structure to free.
*/
static void free_journal_entry(struct JournalEntry *entry)
{
if (entry) {
free(entry->classification);
free(entry->record_id);
free(entry->event_id);
free(entry->boot_id);
free(entry);
}
}
/**
* Unpacks a record that was stored in file and initializes a
* JournalEntry struct with parsed values.
*
* @param line A pointer to the data to unpack.
* @param entry A pointer to be initialized and set with values
* parsed from parameter line.
*
* @return 0 on success, -1 on failure.
*/
static int deserialize_journal_entry(char *line, struct JournalEntry **entry)
{
int rc = -1;
size_t len = 0;
size_t llen = 0;
size_t offset = 0;
struct JournalEntry *e = NULL;
if (line == NULL) {
return -1;
}
e = malloc(sizeof(struct JournalEntry));
if (!e) {
return -1;
}
llen = strlen(line);
for (int i = 0; i < 5; i++) {
char *out = NULL;
if (offset > llen) {
rc = -1;
break;
}
len = strcspn(line + offset, "\036\n");
out = strndup((line + offset), len);
offset += len + 1;
if (out) {
switch (i) {
case 0:
e->record_id = out;
break;
case 1:
e->timestamp = atoll(out);
free(out);
break;
case 2:
e->classification = out;
break;
case 3:
e->event_id = out;
break;
case 4:
e->boot_id = out;
rc = 0;
break;
default:
assert(i < 0 && i > 4);
free(out);
rc = 1;
}
} else {
rc = -1;
break;
}
}
if (rc == 0) {
*entry = e;
} else {
free_journal_entry(e);
}
return rc;
}
/**
* Counts the number of records in journal.
*
* @param telem_journal Pointer to a telemetry journal
* to get record count.
*
* @return record count
*
*/
static int get_record_count(struct TelemJournal *telem_journal)
{
int count = 0;
size_t len;
ssize_t read;
char *line = NULL;
rewind(telem_journal->fptr);
// read until the end line by line
while ((read = getline(&line, &len, telem_journal->fptr)) != -1) {
count++;
}
free(line);
return count;
}
/**
* Serializes JournalEntry and saves it to file.
*
* @param fptr A FILE pointer.
* @param entry A pointer to data.
*
* @return 0 on succes, !=0 on failure
*/
static int save_entry(FILE *fptr, struct JournalEntry *entry)
{
int rc = -1;
char *serialized_data = NULL;
if (fptr == NULL || entry == NULL) {
return rc;
}
if ((rc = serialize_journal_entry(entry, &serialized_data)) == 0) {
#ifdef DEBUG
printf("Saving: %s\n", serialized_data);
#endif
fprintf(fptr, "%s\n", serialized_data);
fflush(fptr);
free(serialized_data);
}
return rc;
}
/**
* Reads the boot unique identifier from BOOTID_FILE
*
* @param buff pointer to a BOOTID_LEN allocated
*
* @return 0 on success, -1 on failure
*/
static int read_boot_id(char buff[])
{
int rc = -1;
FILE *fs = NULL;
fs = fopen(BOOTID_FILE, "r");
if (!fs) {
#ifdef DEBUG
fprintf(stderr, "Unable to open %s for reading: %d\n", BOOTID_FILE, errno);
#endif
return rc;
}
if (fgets(buff, BOOTID_LEN, fs)) {
rc = 0;
}
fclose(fs);
return rc;
}
/**
* Moves the file position indicator n lines.
*
* @param n Number of lines to skip.
* @param fptr A file pointer.
*
* @returns 0 on success, on failure -1 if n > existing
* lines in file or errno if failure happens
* during file operation.
*
*/
static int skip_n_lines(int n, FILE *fptr)
{
int rc = 0;
size_t len = 0;
char *line = NULL;
if (fptr == NULL) {
#ifdef DEBUG
fprintf(stderr, "fptr argument to skip_n_lines is invalid\n");
#endif
// File descriptor in bad state
return EBADFD;
}
if (fseek(fptr, 0, 0) != 0) {
return errno;
}
for (int i = 0; i < n; i++) {
if (getline(&line, &len, fptr) == -1) {
rc = -1;
break;
}
}
free(line);
return rc;
}
/**
* Copies contents of fptr to a known location.
*
* @param fptr A file pointer from source file.
*
* @returns 0 on success, errno on failure
*/
static int copy_to_tmp(FILE *fptr, char *tmp_path)
{
int rc = 0;
size_t len;
char *line = NULL;
FILE *fptr_tmp = NULL;
if (fptr == NULL) {
return -1;
}
fptr_tmp = fopen(tmp_path, "w");
if (!fptr_tmp) {
return errno;
}
// Copy lines to new file
while (getline(&line, &len, fptr) > -1) {
if (fprintf(fptr_tmp, "%s", line) < 0) {
rc = errno;
break;
}
}
if (fclose(fptr_tmp) != 0) {
rc = errno;
#ifdef DEBUG
perror("Error: ");
#endif
}
free(line);
return rc;
}
/* Exported function */
TelemJournal *open_journal(const char *journal_file)
{
FILE *fptr = NULL;
char boot_id[BOOTID_LEN] = { '\0' };
struct TelemJournal *telem_journal;
// Use default location if journal_file parameter is NULL
fptr = (journal_file == NULL) ? fopen(JOURNAL_PATH, "a+") :
fopen(journal_file, "a+");
if (fptr == NULL) {
#ifdef DEBUG
perror("Error while opening journal file: ");
#endif
return NULL;
}
if (read_boot_id(boot_id) != 0) {
#ifdef DEBUG
perror("Error while reading boot_id: ");
#endif
return NULL;
}
telem_journal = malloc(sizeof(struct TelemJournal));
if (!telem_journal) {
fprintf(stderr, "Unable to allocate more memory\n");
return NULL;
}
telem_journal->fptr = fptr;
telem_journal->journal_file = \
(journal_file == NULL) ? strdup(JOURNAL_PATH) : strdup(journal_file);
/* boot_id includes \n at the end, strip RC during duplication */
telem_journal->boot_id = strndup(boot_id, BOOTID_LEN - 1);
telem_journal->record_count = get_record_count(telem_journal);
telem_journal->record_count_limit = RECORD_LIMIT;
#ifdef DEBUG
printf("Records in db: %d\n", telem_journal->record_count);
#endif
return telem_journal;
}
/* Exported function */
void close_journal(TelemJournal *telem_journal)
{
if (telem_journal) {
free(telem_journal->boot_id);
free(telem_journal->journal_file);
fclose(telem_journal->fptr);
free(telem_journal);
}
}
/**
* Checks if class is a prefix i.e. t/\* or t/t/\*
*
* @param class A pointer to string with classification
* value to check.
* @return 1 if class is a prefix, 0 otherwise
*/
static int is_class_prefix(char *class)
{
size_t class_len = strlen(class);
return (strcmp((char *)(class + class_len - 2), "/*") == 0) ? 1 : 0;
}
/* Exported function */
int print_journal(TelemJournal *telem_journal, char *classification,
char *record_id, char *event_id, char *boot_id)
{
int n = 0;
int rc = 0;
int count = 0;
char str_time[80] = { '\0' };
char *line = NULL;
size_t len = 0;
FILE *journal_fileptr = NULL;
struct tm ts;
struct JournalEntry *entry = NULL;
if (telem_journal == NULL) {
return -1;
}
journal_fileptr = fopen(telem_journal->journal_file, "r");
if (!journal_fileptr) {
return -1;
}
// Advance file pointer to line n
n = telem_journal->record_count - telem_journal->record_count_limit;
if (n > 0) {
rc = skip_n_lines(n, journal_fileptr);
if (rc == -1) {
return rc;
} else if (rc != 0) {
fprintf(stderr, "An error occurred while advancing journal file: %s\n", strerror(rc));
}
}
while (getline(&line, &len, journal_fileptr) != -1) {
deserialize_journal_entry(line, &entry);
if (entry) {
/* filter entry out if one is provided */
if (record_id != NULL && strcmp(entry->record_id, record_id) != 0) {
continue;
}
if (boot_id != NULL && strcmp(entry->boot_id, boot_id) != 0) {
continue;
}
if (event_id != NULL && strcmp(entry->event_id, event_id) != 0) {
continue;
}
// In the case of class checking prefixes is an option
if (classification != NULL) {
// Check prefixes when classification ends in /*, otherwise use strcomp
if (is_class_prefix(classification)) {
if (strncmp(entry->classification, classification, strlen(classification) - 1) != 0) {
continue;
}
} else if (strcmp(entry->classification, classification) != 0) {
continue;
}
}
/* end filters section */
ts = *localtime(&entry->timestamp);
if (strftime(str_time, sizeof(str_time), "%a %Y-%m-%d %H:%M:%S %Z", &ts) == 0) {
continue;
}
fprintf(stdout, "%-30s %s %s %s %s\n", entry->classification, str_time, entry->record_id, entry->event_id, entry->boot_id);
count++;
}
free_journal_entry(entry);
}
free(line);
fclose(journal_fileptr);
return count;
}
/**
* Validation for event id. Thic check makes sure
* that an event_id is a 32 hexadecimal chars long.
*
* @param event_id A pointer to id value.
*
* @return 0 on success, 1 on failure
*/
static int validate_event_id(char *event_id)
{
const char alphab[] = EVENT_ID_ALPHAB;
if (event_id == NULL) {
return 1;
}
if (strlen(event_id) != EVENT_ID_LEN) {
return 1;
}
if (strspn(event_id, alphab) != EVENT_ID_LEN) {
return 1;
}
return 0;
}
/* Exported function */
int new_journal_entry(TelemJournal *telem_journal, char *classification,
time_t timestamp, char *event_id)
{
int rc = 1;
char boot_id[BOOTID_LEN] = { '\0' };
char *record_id = NULL;
struct JournalEntry *entry = NULL;
if (telem_journal == NULL) {
#ifdef DEBUG
fprintf(stderr, "Error: telem_journal was not initialized\n");
#endif
return rc;
}
if (validate_classification(classification) != 0) {
return rc;
}
if (validate_event_id(event_id) != 0) {
return rc;
}
entry = malloc(sizeof(struct JournalEntry));
if (!entry) {
fprintf(stderr, "Error: unable to allocate more memory\n");
return rc;
}
entry->classification = NULL;
entry->record_id = NULL;
entry->event_id = NULL;
entry->boot_id = NULL;
if (get_random_id(&record_id) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: unable to generate random id\n");
#endif
goto quit;
}
if (read_boot_id(boot_id) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: unable to read boot_id\n");
#endif
goto quit;
}
if ((entry->classification = strdup(classification)) == NULL) {
goto quit;
}
if ((entry->event_id = strdup(event_id)) == NULL) {
goto quit;
}
entry->record_id = record_id;
/* boot_id includes \n at the end, strip RC during duplication */
entry->boot_id = strndup(boot_id, BOOTID_LEN - 1);
entry->timestamp = timestamp;
if ((rc = save_entry(telem_journal->fptr, entry)) == 0) {
telem_journal->record_count = telem_journal->record_count + 1;
#ifdef DEBUG
fprintf(stdout, "%d records in journal\n", telem_journal->record_count);
#endif
}
quit:
free_journal_entry(entry);
return rc;
}
/* Exported function */
int prune_journal(struct TelemJournal *telem_journal, char *tmp_dir)
{
int rc = 1;
int count = 0;
int deviation = DEVIATION;
char *tmp_file_path = NULL;
if (telem_journal == NULL) {
return rc;
}
if (telem_journal->record_count > (deviation + telem_journal->record_count_limit)) {
count = telem_journal->record_count - telem_journal->record_count_limit;
// jump to line# count
if ((rc = skip_n_lines(count, telem_journal->fptr)) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: skipping %d journal lines\n", count);
#endif
// if rc == -1 (n > #lines in file, change to errno style)
return (rc == -1) ? EADDRNOTAVAIL : rc;
}
// Use default if not tmp_dir is specified
if (tmp_dir == NULL) {
if ((asprintf(&tmp_file_path, "%s/.journal", JOURNAL_TMPDIR) == -1)) {
return ENOMEM;
}
} else {
if ((asprintf(&tmp_file_path, "%s/.journal", tmp_dir) == -1)) {
return ENOMEM;
}
}
// create new file with rest of file
if ((rc = copy_to_tmp(telem_journal->fptr, tmp_file_path)) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: copying partial journal to temp journal file\n");
#endif
goto quit;
}
// close file handler
if ((rc = fclose(telem_journal->fptr)) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: closing journal file handler\n");
#endif
goto quit;
}
// overwrite file
if ((rc = rename(tmp_file_path, telem_journal->journal_file)) != 0) {
#ifdef DEBUG
fprintf(stderr, "Error: while overwriting journal file\n");
#endif
goto quit;
}
// reopen file handler
telem_journal->fptr = fopen(telem_journal->journal_file, "a+");
if (!telem_journal->fptr) {
#ifdef DEBUG
fprintf(stderr, "Error: re-opening journal file\n");
#endif
return rc;
}
// update record count
telem_journal->record_count = telem_journal->record_count - count;
#ifdef DEBUG
fprintf(stdout, "record_count: %d\n", telem_journal->record_count);
#endif
}
rc = 0;
quit:
free(tmp_file_path);
return rc;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+112
View File
@@ -0,0 +1,112 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#define _GNU_SOURCE
/* default record limit */
#define RECORD_LIMIT 100
#define DEVIATION 50
#include <time.h>
#include <stdio.h>
/* Journal entry type */
typedef struct JournalEntry {
time_t timestamp;
char *classification;
char *record_id;
char *event_id;
char *boot_id;
} JournalEntry;
/* Telemetry journal type */
typedef struct TelemJournal {
FILE *fptr;
char *journal_file;
char *boot_id;
int record_count;
int record_count_limit;
} TelemJournal;
/**
* Telemetry journal initialization.
*
* @param journal_file A pointer to a string containing
* the full path to file used as journal storage.
* If journal_file is set to NULL a default value
* will be used.
*
* @returns a telemetry journal structure in success or
* NULL in case of failure.
*/
TelemJournal *open_journal(const char *journal_file);
/**
* Closes journal file and deallocates memory that was
* previously initialized by open_journal call.
*
* @param telem_journal A pointer to struct that has a
* reference to journal file pointer and other
* members.
*/
void close_journal(TelemJournal *telem_journal);
/**
* Prints journal contents to stdout. Use function parameters
* to filter journal entries to print.
*
* @param telem_journal A pointer to struct initialized
* by open_journal call.
* @param classification A pointer to string used as
* classification filter.
* @param record_id A pointer to string used as record_id filter.
* @param event_id A pointer to string used as event_id filter.
* @param boot_id A pointer to string used as boor_id filter.
*
* @return the number of lines printed to stdout on success, -1
* on failure.
*/
int print_journal(TelemJournal *telem_journal, char *classification,
char *record_id, char *event_id, char *boot_id);
/**
* Creates a new entry in journal.
*
* @param telem_journal A pointer to telemetry journal.
* @param classification A pointer to a classification value.
* @param timestamp Record creation time stamp value.
* @param event_id A pointer to a unique id value for record
* event.
*
* @return 0 on success 1 on failure.
*/
int new_journal_entry(TelemJournal *telem_journal, char *classification,
time_t timestamp, char *event_id);
/**
* Checks number of lines in log and prunes the oldest records
* if journal grows more than telem_journal->record_count_limit.
*
* @param telem_journal A pointer to telemetry journal struct
* returned by open_journal call.
* @param tmp_dir A pointer to a string with the path to use for
* temp file during pruning process.
*
* @return 0 on success, errno on failure
*/
int prune_journal(TelemJournal *telem_journal, char *tmp_dir);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+9
View File
@@ -0,0 +1,9 @@
bin_PROGRAMS += \
%D%/telem_journal
%C%_telem_journal_SOURCES = %D%/cli.c \
%D%/journal.c \
src/util.c \
src/common.c
# vim: filetype=automake tabstop=8 shiftwidth=8 noexpandtab
+5 -3
View File
@@ -5,7 +5,9 @@ bin_PROGRAMS = %D%/telemd
%D%/telemdaemon.c \
%D%/telemdaemon.h \
%D%/spool.c \
%D%/spool.h
%D%/spool.h \
%D%/journal/journal.c \
%D%/journal/journal.h
%C%_telemd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la
@@ -34,9 +36,9 @@ endif
endif
# set library version info
SHAREDLIB_CURRENT=3
SHAREDLIB_CURRENT=4
SHAREDLIB_REVISION=0
SHAREDLIB_AGE=0
SHAREDLIB_AGE=1
noinst_LTLIBRARIES = %D%/libtelem-shared.la
+12
View File
@@ -78,6 +78,7 @@ int main(int argc, char **argv)
int c;
char *config_file = NULL;
int opt_index = 0;
nfds_t initial_nfds = 0;
sigset_t mask;
//bool interrupted = false;
@@ -244,9 +245,18 @@ int main(int argc, char **argv)
time_t last_refresh_time = time(NULL);
/* Save initial count for non connection fds */
initial_nfds = daemon.nfds;
/* Loop to accept clients */
while (1) {
malloc_trim(0);
if (initial_nfds == daemon.nfds) {
ret = prune_journal(daemon.record_journal, JOURNAL_TMPDIR);
if (ret != 0) {
telem_log(LOG_WARNING, "Unable to prune journal\n");
}
}
ret = poll(daemon.pollfds, daemon.nfds, spool_process_time * 1000);
if (ret == -1) {
telem_perror("Failed to poll daemon file descriptors");
@@ -362,6 +372,8 @@ int main(int argc, char **argv)
}
clean_exit:
close_journal(daemon.record_journal);
/* Free memory before exiting */
while ((cl = LIST_FIRST(&(daemon.client_head))) != NULL) {
remove_client(&(daemon.client_head), cl);
+1 -2
View File
@@ -31,7 +31,6 @@ static const char telem_record_class[] = "org.clearlinux/bert/debug";
static uint32_t severity = 2;
static uint32_t payload_version = 1;
void print_usage(char *prog)
{
printf("%s: Usage\n", prog);
@@ -109,7 +108,7 @@ int main(int argc, char **argv)
goto fail;
}
if(!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_SIZE))) {
if (!(ret = nc_b64enc_filename(bert_record_file, payload, MAX_PAYLOAD_SIZE))) {
printf("Failed to read payload from: %s\n", bert_record_file);
goto fail;
}
+30 -1
View File
@@ -38,6 +38,7 @@ static char *opt_class = NULL;
static char *opt_payload = NULL;
static uint32_t payload_version = 1;
static char *opt_payload_file = NULL;
static char *opt_event_id = NULL;
static const struct option prog_opts[] = {
{ "help", no_argument, 0, 'h' },
@@ -48,6 +49,7 @@ static const struct option prog_opts[] = {
{ "payload", required_argument, 0, 'p' },
{ "payload-file", required_argument, 0, 'P' },
{ "record-version", required_argument, 0, 'R' },
{ "event-id", required_argument, 0, 'e' },
{ 0, 0, 0, 0 }
};
@@ -67,6 +69,7 @@ static void print_help(void)
printf(" -p, --payload Record body (max size = 8k)\n");
printf(" -P, --payload-file File to read payload from\n");
printf(" -R, --record-version Version number for format of payload (default 1)\n");
printf(" -e, --event-id Event id to use in the record\n");
printf("\n");
}
@@ -91,7 +94,7 @@ int parse_options(int argc, char **argv)
long unsigned int tmp = 0;
int opt;
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:", prog_opts, NULL)) != -1) {
while ((opt = getopt_long(argc, argv, "hc:Vs:c:p:P:R:e:", prog_opts, NULL)) != -1) {
switch (opt) {
case 'h':
print_help();
@@ -140,6 +143,12 @@ int parse_options(int argc, char **argv)
payload_version = (uint32_t)tmp;
break;
case 'e':
opt_event_id = strdup(optarg);
if (opt_event_id == NULL) {
goto fail;
}
break;
}
}
@@ -152,6 +161,7 @@ int validate_opts(void)
{
size_t len;
int ret = 0;
const char alphab[] = EVENT_ID_ALPHAB;
/* classification */
if (opt_class == NULL) {
@@ -187,6 +197,20 @@ int validate_opts(void)
return ret;
}
/* Event id */
if (opt_event_id) {
size_t result = 0;
if ((result = strlen(opt_event_id)) != EVENT_ID_LEN) {
fprintf(stderr, "Error: event_id length %zu it should have %d characters\n", result, EVENT_ID_LEN);
return ret;
}
if ((result = strspn(opt_event_id, alphab)) != EVENT_ID_LEN) {
fprintf(stderr, "Error: event_id contains invalid character '%c' at position %zu, valid characters are %s\n",
opt_event_id[result], result, alphab);
return ret;
}
}
return 1;
}
@@ -308,6 +332,10 @@ int send_record(char *payload)
goto out1;
}
if (opt_event_id && tm_set_event_id(t_ref, opt_event_id) < 0) {
goto out2;
}
if (tm_set_payload(t_ref, payload) < 0) {
goto out2;
}
@@ -348,6 +376,7 @@ fail:
free(config_file);
free(opt_class);
free(opt_payload);
free(opt_event_id);
return ret;
}
+22 -40
View File
@@ -44,6 +44,7 @@ void initialize_daemon(TelemDaemon *daemon)
daemon->client_head = head;
daemon->bypass_http_post_ts = 0;
daemon->is_spool_valid = false;
daemon->record_journal = open_journal(JOURNAL_PATH);
initialize_rate_limit(daemon);
daemon->current_spool_size = 0;
}
@@ -280,6 +281,9 @@ void process_record(TelemDaemon *daemon, client *cl)
bool byte_check_passed = true;
bool record_burst_enabled = true;
bool byte_burst_enabled = true;
/* values for journal */
char *classification_value;
char *event_id_value;
/* Gets the current minute of time */
time_t temp = time(NULL);
@@ -312,6 +316,16 @@ void process_record(TelemDaemon *daemon, client *cl)
/* TODO : check if the body is within the limits. */
body = msg + header_size;
/* Save record in journal */
if (get_header_value(headers[TM_CLASSIFICATION], &classification_value) &&
get_header_value(headers[TM_EVENT_ID], &event_id_value)) {
if (new_journal_entry(daemon->record_journal, classification_value, temp, event_id_value) != 0) {
telem_log(LOG_INFO, "new_journal_entry in process_record: failed saving record entry\n");
}
}
free(classification_value);
free(event_id_value);
if (inside_direct_spool_window(daemon, time(NULL))) {
telem_log(LOG_INFO, "process_record: delivering directly to spool\n");
spool_record(daemon, headers, body);
@@ -680,7 +694,7 @@ bool get_machine_id(char *machine_id)
return true;
}
int machine_id_write(uint64_t upper, uint64_t lower)
int machine_id_write(char *new_id)
{
FILE *fp;
int ret = 0;
@@ -692,7 +706,7 @@ int machine_id_write(uint64_t upper, uint64_t lower)
goto file_error;
}
ret = fprintf(fp, "%.16" PRIx64 "%.16" PRIx64, upper, lower);
ret = fprintf(fp, "%s", new_id);
if (ret < 0) {
telem_perror("Unable to write to machine id file");
goto file_error;
@@ -709,49 +723,17 @@ file_error:
int generate_machine_id(void)
{
int frandom = -1;
struct stat stat_buf;
int ret = 0;
struct iovec random_id[2];
uint64_t random_id_upper;
uint64_t random_id_lower;
int result = -1;
int result = 0;
char *new_id = NULL;
ret = stat("/dev/urandom", &stat_buf);
if (ret == -1) {
telem_log(LOG_ERR, "Unable to stat urandom device\n");
if ((result = get_random_id(&new_id)) != 0) {
goto rand_err;
}
if (!S_ISCHR(stat_buf.st_mode)) {
telem_log(LOG_ERR, "/dev/urandom is not a character device file\n");
goto rand_err;
}
/* TODO : check for major and minor numbers to be extra sure ??
((stat_buffer.st_rdev == makedev(1, 8)) || (stat_buffer.st_rdev == makedev(1, 9))*/
frandom = open("/dev/urandom", O_RDONLY);
if (frandom < 0) {
telem_perror("Error opening /dev/urandom");
goto rand_err;
}
random_id[0].iov_base = &random_id_upper;
random_id[0].iov_len = 8;
random_id[1].iov_base = &random_id_lower;
random_id[1].iov_len = 8;
if (readv(frandom, random_id, 2) < 0) {
telem_perror("Error while reading /dev/urandom");
goto rand_err;
}
result = machine_id_write(random_id_upper, random_id_lower);
result = machine_id_write(new_id);
rand_err:
if (frandom >= 0) {
close(frandom);
}
free(new_id);
return result;
}
+4 -2
View File
@@ -22,6 +22,7 @@
#include <sys/queue.h>
#include <stdbool.h>
#include <inttypes.h>
#include "journal/journal.h"
#define TM_MACHINE_ID_EXPIRY (3 /*d*/ * 24 /*h*/ * 60 /*m*/ * 60 /*s*/)
@@ -56,7 +57,8 @@ typedef struct TelemDaemon {
client_list_head client_head;
/* Time of last failed post */
time_t bypass_http_post_ts;
/* Telemetry Journal*/
TelemJournal *record_journal;
/* Rate limit record and byte arrays */
size_t record_burst_array[TM_RATE_LIMIT_SLOTS];
size_t byte_burst_array[TM_RATE_LIMIT_SLOTS];
@@ -267,7 +269,7 @@ bool post_record_http(char *header_values[], char *body, bool spool);
* @param body The payload of the record
*
*/
void spool_record(TelemDaemon *daemon, char *headers[], char *body);
void spool_record(TelemDaemon * daemon, char *headers[], char *body);
/**
* Get random machine id stored in file
+119 -25
View File
@@ -15,6 +15,7 @@
*/
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -30,6 +31,7 @@
#include <inttypes.h>
#include <ctype.h>
#include "util.h"
#include "common.h"
#include "configuration.h"
#include "telemetry.h"
@@ -137,38 +139,14 @@ static int set_severity_header(struct telem_ref *t_ref, uint32_t severity)
*/
static int set_classification_header(struct telem_ref *t_ref, char *classification)
{
size_t i, j;
int slashes = 0;
int x = 0;
j = strlen(classification);
if (j > MAX_CLASS_LENGTH) {
return -EINVAL;
}
for (i = 0, x = 0; i <= (j - 1); i++, x++) {
if (classification[i] == '/') {
slashes++;
x = 0;
} else {
if (x > MAX_SUBCAT_LENGTH) {
return -EINVAL;
}
}
}
if (slashes != 2) {
#ifdef DEBUG
fprintf(stderr, "ERR: Classification string should have two /s.\n");
#endif
if (validate_classification(classification) == 1) {
return -EINVAL;
}
return set_header(&(t_ref->record->headers[TM_CLASSIFICATION]),
TM_CLASSIFICATION_STR, classification,
&(t_ref->record->header_size));
}
/**
@@ -637,6 +615,64 @@ static int set_bios_version_header(struct telem_ref *t_ref)
return status;
}
/**
* Generates an alphanumeric id of length 32
*
* @param buff pointer to memory buffer, buffer should have
* at least 33 bytes of memory
*
* @return 0 if successful, or -1 if there's a problem
*
*/
static int gen_event_id(char *buff)
{
int frandom = -1;
int result = -1;
uint64_t random_id[2] = { 0 };
frandom = open("/dev/urandom", O_RDONLY);
if (frandom < 0) {
return -1;
}
if (read(frandom, &random_id, sizeof(random_id)) == sizeof(random_id)) {
if (sprintf(buff, "%.16" PRIx64 "%.16" PRIx64, random_id[0], random_id[1]) == 32) {
result = 0;
}
}
close(frandom);
return result;
}
/**
* Sets the event_id header, this id is an identifier that multiple
* records can share. This means that one event can lead to multiple
* records.
*
* @param t_ref Telemetry Record reference obtained from tm_create_record.
*
* @return 0 if successful, -1 on failure
*
*/
static int set_event_id_header(struct telem_ref *t_ref)
{
int rc = 0;
char buff[33];
rc = gen_event_id(buff);
if (rc == 0) {
rc = set_header(
&(t_ref->record->headers[TM_EVENT_ID]),
TM_EVENT_ID_STR, buff,
&(t_ref->record->header_size));
}
return rc;
}
/**
* Sets the hosttype header, which is a tuple of three values looked for
* in the dmi filesystem. System Vendor (sys_vendor), Product Name
@@ -888,6 +924,12 @@ int allocate_header(struct telem_ref *t_ref, uint32_t severity,
goto free_and_fail;
}
i++;
if ((ret = set_event_id_header(t_ref)) < 0) {
goto free_and_fail;
}
i++; /* Not necessary, but including for future expansion */
return ret;
@@ -1016,6 +1058,58 @@ int tm_set_payload(struct telem_ref *t_ref, char *payload)
return ret;
}
/**
* Checks for id to have length = 32 characters
* and hexadecimal characters only.
*
* @param id A pointer to string to be checked
*
* @return 0 on success, 1 on failure
*
*/
static int validate_event_id(char *id)
{
int rc = 0;
const char alphab[] = EVENT_ID_ALPHAB;
// make sure is not null
if (!id) {
return 1;
}
// 32 - 32 = 0
if ((rc = (int)(strlen(id) - EVENT_ID_LEN)) != 0) {
return 1;
}
// verify alphabet
// strspn checks for characters in alphab
if (strspn(id, alphab) != EVENT_ID_LEN) {
return 1;
}
return rc;
}
int tm_set_event_id(struct telem_ref *t_ref, char *event_id)
{
int rc = -1;
if (!validate_event_id(event_id)) {
if (t_ref && t_ref->record && t_ref->record->headers) {
// free default id before overriding
free(t_ref->record->headers[TM_EVENT_ID]);
// set new event_id
if (asprintf(&(t_ref->record->headers[TM_EVENT_ID]),
"%s: %s\n", TM_EVENT_ID_STR, event_id) > 0) {
rc = 0;
}
}
}
return rc;
}
/**
* Write nbytes from buf to fd. Used to send records to telemd.
*
+13
View File
@@ -85,6 +85,19 @@ void tm_set_config_file(char *c_file);
int tm_create_record(struct telem_ref **t_ref, uint32_t severity,
char *classification, uint32_t payload_version);
/**
* Sets the event_id to a user defined id
*
* @param t_ref A pointer to a telem_ref struct that was previously initialized
* this struct will have an event_id already initialized and it will be
* overriden by a user provided event_id
* @param event_id A pointer to a string that contains the 32 character length
* event_id
*
* @return 0 on success, or a negative errno-style value on error
*/
int tm_set_event_id(struct telem_ref *t_ref, char *event_id);
/**
* Set the payload field of a telemetrics record
*
+5
View File
@@ -16,3 +16,8 @@ TM_3_0_0 {
tm_set_payload;
tm_send_record;
} TM_2_0_0;
TM_4_0_0 {
global:
tm_set_event_id;
} TM_3_0_0;
+104
View File
@@ -16,16 +16,22 @@
#define _GNU_SOURCE
#define RANDOM_ID_LEN 32
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include "common.h"
#include "util.h"
bool get_header(const char *haystack, const char *needle, char **line, size_t len)
@@ -37,6 +43,28 @@ bool get_header(const char *haystack, const char *needle, char **line, size_t le
return false;
}
bool get_header_value(const char *header, char **value)
{
char *sep = NULL;
*value = NULL;
if (header == NULL) {
return false;
}
if ((sep = strchr(header, ':')) != NULL) {
sep++;
// skip space after colon if there's one
if (*sep == ' ') {
sep++;
}
*value = strdup(sep);
}
return (bool)(value != NULL);
}
void *reallocate(void **addr, size_t *allocated, size_t requested)
{
void *newaddr;
@@ -107,4 +135,80 @@ long get_directory_size(const char *dir_path)
return total_size;
}
/**
* Generates a 32 characters random id
*
* @param buffer pointer to allocate and copy data
*
*/
int get_random_id(char **buff)
{
int result = -1;
int frandom = -1;
uint64_t random_id[2] = { '\0' };
frandom = open("/dev/urandom", O_RDONLY);
if (frandom < 0) {
return -1;
}
if (read(frandom, &random_id, sizeof(random_id)) == sizeof(random_id)) {
if (asprintf(buff, "%.16" PRIx64 "%.16" PRIx64, random_id[0], random_id[1]) == RANDOM_ID_LEN) {
result = 0;
}
}
close(frandom);
return result;
}
/**
* Validate classification value. A valid classification
* is a string with 2 slashes, with max length of
* MAX_CLASS_LENGTH and strings betweeb slashes should
* have a max length of MAX_SUBCAT_LENGTH.
*
* @param classification A pointer to classification value
* to be validated.
*
* @return 0 on sucess and 1 on failure
*/
int validate_classification(char *classification)
{
size_t i, j;
int slashes = 0;
int x = 0;
if (classification == NULL) {
return 1;
}
j = strlen(classification);
if (j > MAX_CLASS_LENGTH) {
return 1;
}
for (i = 0, x = 0; i <= (j - 1); i++, x++) {
if (classification[i] == '/') {
slashes++;
x = 0;
} else {
if (x > MAX_SUBCAT_LENGTH) {
return 1;
}
}
}
if (slashes != 2) {
#ifdef DEBUG
fprintf(stderr, "ERR: Classification string should have two /s.\n");
#endif
return 1;
}
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+9
View File
@@ -24,7 +24,16 @@ void *reallocate(void **addr, size_t *allocated, size_t requested);
/* Check if haystacks begins with needle and return a copy of haystack */
bool get_header(const char *haystack, const char *needle, char **line, size_t len);
/* Get the value of a header */
bool get_header_value(const char *header, char **value);
/* Get the size of the directory */
long get_directory_size(const char *sdir);
/* Initialize buff and copy generated id */
int get_random_id(char **buff);
/* Validates classification value */
int validate_classification(char *classification);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+2 -1
View File
@@ -298,7 +298,8 @@ START_TEST(check_process_record_with_correct_size_and_data)
"system_name: clear-linux-os\n"
"board_name: Qemu|Intel\n"
"cpu_model: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz\n"
"bios_version: Qemu\n";
"bios_version: Qemu\n"
"event_id: 3a2d799826edc6266d72824d2aac6763\n";
char *post_body = "test message";
set_up_socket_pair(&client_fd, &server_fd);
+252
View File
@@ -0,0 +1,252 @@
/*
* This program is part of the Clear Linux Project
*
* Copyright 2018 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it under
* the terms and conditions of the GNU Lesser General Public License, as
* published by the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
#include <check.h>
#include <errno.h>
#include <stdio.h>
#include "common.h"
#include "journal/journal.h"
static struct TelemJournal *journal = NULL;
static char *journal_file = TESTOOPSDIR "/journal.txt";
static char *eid = "00007766547776eb7fc478eb0eb43e43";
static int K = 20;
START_TEST(check_open_journal)
{
struct TelemJournal *j = open_journal(journal_file);
ck_assert_ptr_nonnull(j);
ck_assert_ptr_nonnull(j->fptr);
ck_assert_ptr_nonnull(j->boot_id);
ck_assert_int_eq(j->record_count, 0);
close_journal(j);
}
END_TEST
START_TEST(check_unsuccessful_open_journal)
{
char *bad_journal_file = "/sys/firmware/acpi/tables/journal";
struct TelemJournal *j = open_journal(bad_journal_file);
ck_assert_ptr_null(j);
}
END_TEST
void new_entry_setup(void)
{
if (journal) {
return;
}
journal = open_journal(journal_file);
}
START_TEST(check_new_entry)
{
int ret;
ret = new_journal_entry(journal, "t/t/t", 1520054957,
"3bc17766547776eb7fc478eb0eb43e43");
ck_assert_int_eq(ret, 0);
close_journal(journal);
}
END_TEST
START_TEST(check_new_entry_bad_class)
{
int ret = 0;
/* has an invalid class */
ret = new_journal_entry(journal, "t/t", 1520054957,
"3bc17766547776eb7fc478eb0eb43e43");
ck_assert_int_eq(ret, 1);
}
END_TEST
START_TEST(check_new_entry_bad_id)
{
int ret = 0;
/* event_id is invalid */
ret = new_journal_entry(journal, "t/t/t", 1520054957,
"Xbc17766547776eb7fc478eb0eb43e43");
ck_assert_int_eq(ret, 1);
}
END_TEST
START_TEST(check_new_entry_null_class)
{
int ret = 0;
/* param is NULL */
ret = new_journal_entry(journal, NULL, 1520054957,
"3bc17766547776eb7fc478eb0eb43e43");
ck_assert_int_eq(ret, 1);
}
END_TEST
START_TEST(check_new_entry_null_id)
{
int ret = 0;
/* null id */
ret = new_journal_entry(journal, "t/t/t", 1520054957, NULL);
ck_assert_int_eq(ret, 1);
}
END_TEST
void new_entry_teardown(void)
{
if (journal) {
close_journal(journal);
journal = NULL;
}
}
void insert_n_records(int n, struct TelemJournal *j)
{
for (int i = 0; i < n; i++) {
new_journal_entry(j, "t/t/t", 1520054957 + i,
"3bc17766547776eb7fc478eb0eb43e43");
}
}
/*
failing in travis-ci
*/
START_TEST(check_journal_file_prune)
{
int rc = 0;
struct TelemJournal *j = open_journal(journal_file);
insert_n_records(j->record_count_limit * 2, j);
ck_assert_int_gt(j->record_count, j->record_count_limit);
rc = prune_journal(j, TESTOOPSDIR);
ck_assert(rc == 0);
// Record count should always be below the limit + hysteresis
ck_assert_int_lt(j->record_count, j->record_count_limit + DEVIATION);
close_journal(j);
}
END_TEST
void journal_entry_setup(void)
{
int result = 0;
const char *journal_print = TESTOOPSDIR "/journal.print.txt";
if (journal) {
close_journal(journal);
}
journal = open_journal(journal_print);
insert_n_records(K, journal);
/* Insert recors with specific values */
result = new_journal_entry(journal, "a/b/c", 1520054957, eid);
ck_assert(result == 0);
result = new_journal_entry(journal, "a/b/d", 1520054957, eid);
ck_assert(result == 0);
}
START_TEST(check_journal_print)
{
int count = 0;
count = print_journal(journal, NULL, NULL, NULL, NULL);
ck_assert_int_eq(count, K + 2);
}
END_TEST
START_TEST(check_journal_filter_by_class)
{
int result = print_journal(journal, "a/b/c", NULL, NULL, NULL);
ck_assert_int_eq(result, 1);
}
END_TEST
START_TEST(check_journal_filter_by_class_prefix)
{
int result = print_journal(journal, "a/b/*", NULL, NULL, NULL);
ck_assert_int_eq(result, 2);
}
END_TEST
START_TEST(check_journal_filter_by_event_id)
{
int result = print_journal(journal, NULL, NULL, eid, NULL);
ck_assert(result == 2);
}
END_TEST
void journal_entry_teardown(void)
{
close_journal(journal);
}
Suite *config_suite(void)
{
// A suite is comprised of test cases, defined below
Suite *s = suite_create("journal_feature");
// Individual unit tests are added to "test cases"
TCase *t = tcase_create("journal");
tcase_add_test(t, check_open_journal);
tcase_add_test(t, check_unsuccessful_open_journal);
suite_add_tcase(s, t);
t = tcase_create("new entry");
tcase_add_unchecked_fixture(t, new_entry_setup, new_entry_teardown);
tcase_add_test(t, check_new_entry);
tcase_add_test(t, check_new_entry_bad_class);
tcase_add_test(t, check_new_entry_bad_id);
tcase_add_test(t, check_new_entry_null_class);
tcase_add_test(t, check_new_entry_null_id);
suite_add_tcase(s, t);
t = tcase_create("prunning journal");
tcase_add_test(t, check_journal_file_prune);
suite_add_tcase(s, t);
t = tcase_create("print journal");
tcase_add_unchecked_fixture(t, journal_entry_setup,
journal_entry_teardown);
tcase_add_test(t, check_journal_print);
tcase_add_test(t, check_journal_filter_by_class);
tcase_add_test(t, check_journal_filter_by_class_prefix);
tcase_add_test(t, check_journal_filter_by_event_id);
suite_add_tcase(s, t);
return s;
}
int main(void)
{
Suite *s;
SRunner *sr;
s = config_suite();
sr = srunner_create(s);
// Use the TAP driver for now, so that each
// unit test will PASS/FAIL in the log output.
srunner_set_log(sr, NULL);
srunner_set_tap(sr, "-");
srunner_run_all(sr, CK_SILENT);
// failed = srunner_ntests_failed(sr);
srunner_free(sr);
// if you want the TAP driver to report a hard error based
// on certain conditions (e.g. number of failed tests, etc.),
// return non-zero here instead.
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+91
View File
@@ -24,6 +24,7 @@
#include "telemetry.h"
static struct telem_ref *ref = NULL;
static char *original_event_id = NULL;
void create_setup(void)
{
@@ -157,6 +158,87 @@ START_TEST(record_create_severity_overflow)
}
END_TEST
void event_id_setup(void)
{
int ret;
if (ref) {
return;
}
ret = tm_create_record(&ref, 1, "t/t/t", 2000);
original_event_id = strdup(ref->record->headers[TM_EVENT_ID]);
if (!original_event_id) {
return;
}
ck_assert_msg(ret != -ECONNREFUSED,
"Opt-out enabled. Opt in to run this test");
}
START_TEST(record_set_event_id)
{
int ret;
char *event_id = "aaaaaa00000033333344444466666622";
char *result;
if (asprintf(&result, "%s: %s\n", TM_EVENT_ID_STR, event_id) < 0) {
return;
}
ck_assert_str_ne(ref->record->headers[TM_EVENT_ID], result);
ret = tm_set_event_id(ref, event_id);
ck_assert_int_eq(ret, 0);
ck_assert_str_eq(ref->record->headers[TM_EVENT_ID], result);
free(result);
}
END_TEST
START_TEST(record_set_event_id_invalid_chars)
{
int ret;
char *event_id = "aaaaaa000000333333444444666666ZZ";
ret = tm_set_event_id(ref, event_id);
ck_assert_int_eq(ret, -1);
ck_assert_str_eq(ref->record->headers[TM_EVENT_ID], original_event_id);
}
END_TEST
START_TEST(record_set_event_id_null)
{
int ret;
char *event_id = NULL;
ret = tm_set_event_id(ref, event_id);
ck_assert_int_eq(ret, -1);
ck_assert_str_eq(ref->record->headers[TM_EVENT_ID], original_event_id);
}
END_TEST
START_TEST(record_set_event_id_short)
{
int ret;
char *event_id = "aaaa";
ret = tm_set_event_id(ref, event_id);
ck_assert_int_eq(ret, -1);
ck_assert_str_eq(ref->record->headers[TM_EVENT_ID], original_event_id);
}
END_TEST
START_TEST(record_set_event_id_long)
{
int ret;
char *event_id = "0000000000000000000000000000000000000000000";
ret = tm_set_event_id(ref, event_id);
ck_assert_int_eq(ret, -1);
ck_assert_str_eq(ref->record->headers[TM_EVENT_ID], original_event_id);
}
END_TEST
void event_id_teardown(void)
{
if (ref) {
free(ref);
}
free(original_event_id);
}
Suite *lib_suite(void)
{
Suite *s = suite_create("libtelemetry");
@@ -180,6 +262,15 @@ Suite *lib_suite(void)
tcase_add_test(t, record_create_severity_overflow);
suite_add_tcase(s, t);
t = tcase_create("event id");
tcase_add_unchecked_fixture(t, event_id_setup, event_id_teardown);
tcase_add_test(t, record_set_event_id);
tcase_add_test(t, record_set_event_id_invalid_chars);
tcase_add_test(t, record_set_event_id_null);
tcase_add_test(t, record_set_event_id_short);
tcase_add_test(t, record_set_event_id_long);
suite_add_tcase(s, t);
return s;
}
+37 -42
View File
@@ -14,14 +14,12 @@
* details.
*/
#define OUT_MAX_LEN 100
#include <stdio.h>
#include <check.h>
#include "nica/b64enc.h"
START_TEST(check_nc_b64_no_overflow)
{
size_t n = 2;
@@ -32,7 +30,6 @@ START_TEST(check_nc_b64_no_overflow)
}
END_TEST
START_TEST(check_nc_b64_enc_n_file_empty)
{
size_t n = OUT_MAX_LEN;
@@ -45,7 +42,6 @@ START_TEST(check_nc_b64_enc_n_file_empty)
}
END_TEST
START_TEST(check_nc_b64_enc_n_file_fo)
{
size_t n = OUT_MAX_LEN;
@@ -87,43 +83,43 @@ START_TEST(check_nc_b64_enc_n_file_long_text)
size_t out_len = 8000;
char out[out_len];
const char *expected_out = \
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIG5vbnVtbXkgdml0YWUsIGluIHZpdmFtdXMgc3Vz\n"
"cGVuZGlzc2UgYWMuIFNlbXBlciBzZWQgcGhhcmV0cmEgc2NlbGVyaXNxdWUuIEVnZXQgZWxlaWZl\n"
"bmQgYW1ldCB2ZWwgbnVuYyB2b2x1dHBhdCBjdXJzdXMsIGF1Y3RvciBwbGF0ZWEgcHJldGl1bSwg\n"
"bWF0dGlzIHJpc3VzIGZhY2lsaXNpcyBmYXVjaWJ1cywgc3VzcGVuZGlzc2UgZGlhbSwgbGVjdHVz\n"
"IG1ldHVzIG51bGxhLiBOdW5jIGp1c3RvIGZhY2lsaXNpIG5hbSBmZWxpcyB2ZWwgbGFvcmVldCwg\n"
"bmliaCBsZW8gbWFzc2Egc3VzcGVuZGlzc2UgYWNjdW1zYW4gY29udmFsbGlzLCBzZWQgcXVpcyBw\n"
"ZWxsZW50ZXNxdWUgZWdlc3RhcywgbG9yZW0gYW50ZSBtb3JiaSBtYXR0aXMgdml0YWUuIFByYWVz\n"
"ZW50IGluIHJ1dHJ1bSBlZ2V0IGFjLCB2aXZhbXVzIHBlZGUgc3VzcGVuZGlzc2UgbGVjdHVzIG51\n"
"bGxhbSwgZXQgcGVkZSBldSBvZGlvIHBlZGUsIGV0aWFtIGxlbyBlZ2VzdGFzIGluLCBub251bW15\n"
"IHNlbXBlci4gQSBhY2N1bXNhbiBkdWksIHZlc3RpYnVsdW0gcmlkaWN1bHVzIGV0IGluLCBqdXN0\n"
"byBldCBwbGFjZXJhdCBkdWlzIHV0IHZpdmFtdXMgbGliZXJvLiBWZXN0aWJ1bHVtIGVnZXQgdXQg\n"
"bW9sbGlzLCBudW5jIGEgc29sbGljaXR1ZGluLCBmcmluZ2lsbGEgZXJvcyBwb3N1ZXJlIG1vbGxp\n"
"cyBhYyBuYXRvcXVlIHBlZGUsIHJpc3VzIG51bGxhIHJ1dHJ1bSB0dXJwaXMgYSB2aXRhZSBpZC4g\n"
"SW5jZXB0b3MgdGluY2lkdW50IHF1aWRlbSBhcmN1IHR1cnBpcyBudW5jIHNvbGxpY2l0dWRpbi4g\n"
"UGhhc2VsbHVzIG1ldHVzIGVyYXQgcGxhY2VyYXQsIGluIGVyYXQgbG9yZW0gbWF1cmlzIG51bGxh\n"
"LCBhdCBsaWJlcm8gZXJhdCBzdXNwZW5kaXNzZSBzYXBpZW4gbW9udGVzIHJob25jdXMsIG1hZ25h\n"
"IHF1YW0uIEp1c3RvIHByYWVzZW50IGxhY3VzIG1hc3NhLCByZXByZWhlbmRlcml0IG51bmMsIHJp\n"
"c3VzIGRpZ25pc3NpbSBmZWxpcyBub24sIGRpZ25pc3NpbSBvZGlvIHRlbGx1cyBhbGlxdWFtLCBl\n"
"bmltIHNvZGFsZXMgdGVtcG9yIHF1aXNxdWUgc29kYWxlcyBtYWduaXMgcG9ydHRpdG9yLiBMaWd1\n"
"bGEgc2VkIHJpc3VzLCBldSBlc3QgcXVpc3F1ZSwgY2xhc3Mgbm9uIGF1dGUgZXUsIHBsYXRlYSBz\n"
"Y2VsZXJpc3F1ZSBpZCBpbnRlZ2VyIGRpZ25pc3NpbW9zIGludGVnZXIgc3VzY2lwaXQuClZlaGlj\n"
"dWxhIGVpdXMgbmVjLCBjb252YWxsaXMgbWFlY2VuYXMgbGVjdHVzIHB1cnVzIHF1aXNxdWUgYWxp\n"
"cXVhbSwgZXQgZXQsIGxhY2luaWEgZGlzIGRpZ25pc3NpbSBsb3JlbS4gVm9sdXB0YXRlbSB1dCwg\n"
"dml2ZXJyYSBhZW5lYW4gcGhhc2VsbHVzIGZlbGlzLCBmZXJtZW50dW0gbGlndWxhIGVnZXQgdWxs\n"
"YW1jb3JwZXIgYW1ldCwgbWF1cmlzIGxlbyBsdWN0dXMuIEV0IGV0aWFtIHNlbXBlciBwaGFyZXRy\n"
"YSBuaWJoIG1pIG1hdXJpcy4gSGVuZHJlcml0IHNlZCBhbGlxdWFtLCBlZ2V0IGlkIHRlbGx1cyBt\n"
"YWduYSwgc2VkIGxhb3JlZXQgdWx0cmljZXMgZW5pbSwgbW9sbGlzIHN1c3BlbmRpc3NlIGluLiBF\n"
"dSBudWxsYW0gbG9yZW0gc2l0IGV0aWFtLCBvcmNpIGluIGxpYmVyby4gTnVuYyBpcHN1bSBtYXVy\n"
"aXMgZXQgc2VtIGhhYy4gRGljdHVtIGZhdWNpYnVzIGRpcyB2aXRhZSBpbiB2b2x1dHBhdCBtb3Ji\n"
"aSwgYSBkaWN0dW0sIHF1aXNxdWUgbWF1cmlzLCBhdWN0b3IgbmVjIHZlbCBwZWxsZW50ZXNxdWUg\n"
"dWxsYW1jb3JwZXIgZXRpYW0uIEN1bSBsaWJlcm8gd2lzaSBhY2N1bXNhbiBhbGlxdWFtIGNvbnNl\n"
"Y3RldHVlciB0ZWxsdXMsIHBoYXNlbGx1cyBjdXJhZSwgYXQgaWxsdW0gYW50ZSBwcmV0aXVtIG5p\n"
"YmggbW9yYmkuIFNlZCBibGFuZGl0IHB1bHZpbmFyIHB1bHZpbmFyLiBEaWN0dW0gZXN0IG51bmMg\n"
"ZWxlaWZlbmQsIHZlbGl0IHZlbGl0IHRlbXBvciBhY2N1bXNhbiBsb2JvcnRpcyBsYW9yZWV0IGNv\n"
"bmd1ZS4gU2l0IGFtZXQsIHNlZCBuaWJoIHBvcnJvIG5lcXVlIGF1Y3RvciBoeW1lbmFlb3MgcG9z\n"
"dWVyZSwgb2RpbyBudWxsYSBibGFuZGl0IGNvbmd1ZSBlbGl0LiBJZCBmYXVjaWJ1cyBldCB0ZW1w\n"
"dXMgbWFsZXN1YWRhIHBsYXRlYS4=";
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIG5vbnVtbXkgdml0YWUsIGluIHZpdmFtdXMgc3Vz\n"
"cGVuZGlzc2UgYWMuIFNlbXBlciBzZWQgcGhhcmV0cmEgc2NlbGVyaXNxdWUuIEVnZXQgZWxlaWZl\n"
"bmQgYW1ldCB2ZWwgbnVuYyB2b2x1dHBhdCBjdXJzdXMsIGF1Y3RvciBwbGF0ZWEgcHJldGl1bSwg\n"
"bWF0dGlzIHJpc3VzIGZhY2lsaXNpcyBmYXVjaWJ1cywgc3VzcGVuZGlzc2UgZGlhbSwgbGVjdHVz\n"
"IG1ldHVzIG51bGxhLiBOdW5jIGp1c3RvIGZhY2lsaXNpIG5hbSBmZWxpcyB2ZWwgbGFvcmVldCwg\n"
"bmliaCBsZW8gbWFzc2Egc3VzcGVuZGlzc2UgYWNjdW1zYW4gY29udmFsbGlzLCBzZWQgcXVpcyBw\n"
"ZWxsZW50ZXNxdWUgZWdlc3RhcywgbG9yZW0gYW50ZSBtb3JiaSBtYXR0aXMgdml0YWUuIFByYWVz\n"
"ZW50IGluIHJ1dHJ1bSBlZ2V0IGFjLCB2aXZhbXVzIHBlZGUgc3VzcGVuZGlzc2UgbGVjdHVzIG51\n"
"bGxhbSwgZXQgcGVkZSBldSBvZGlvIHBlZGUsIGV0aWFtIGxlbyBlZ2VzdGFzIGluLCBub251bW15\n"
"IHNlbXBlci4gQSBhY2N1bXNhbiBkdWksIHZlc3RpYnVsdW0gcmlkaWN1bHVzIGV0IGluLCBqdXN0\n"
"byBldCBwbGFjZXJhdCBkdWlzIHV0IHZpdmFtdXMgbGliZXJvLiBWZXN0aWJ1bHVtIGVnZXQgdXQg\n"
"bW9sbGlzLCBudW5jIGEgc29sbGljaXR1ZGluLCBmcmluZ2lsbGEgZXJvcyBwb3N1ZXJlIG1vbGxp\n"
"cyBhYyBuYXRvcXVlIHBlZGUsIHJpc3VzIG51bGxhIHJ1dHJ1bSB0dXJwaXMgYSB2aXRhZSBpZC4g\n"
"SW5jZXB0b3MgdGluY2lkdW50IHF1aWRlbSBhcmN1IHR1cnBpcyBudW5jIHNvbGxpY2l0dWRpbi4g\n"
"UGhhc2VsbHVzIG1ldHVzIGVyYXQgcGxhY2VyYXQsIGluIGVyYXQgbG9yZW0gbWF1cmlzIG51bGxh\n"
"LCBhdCBsaWJlcm8gZXJhdCBzdXNwZW5kaXNzZSBzYXBpZW4gbW9udGVzIHJob25jdXMsIG1hZ25h\n"
"IHF1YW0uIEp1c3RvIHByYWVzZW50IGxhY3VzIG1hc3NhLCByZXByZWhlbmRlcml0IG51bmMsIHJp\n"
"c3VzIGRpZ25pc3NpbSBmZWxpcyBub24sIGRpZ25pc3NpbSBvZGlvIHRlbGx1cyBhbGlxdWFtLCBl\n"
"bmltIHNvZGFsZXMgdGVtcG9yIHF1aXNxdWUgc29kYWxlcyBtYWduaXMgcG9ydHRpdG9yLiBMaWd1\n"
"bGEgc2VkIHJpc3VzLCBldSBlc3QgcXVpc3F1ZSwgY2xhc3Mgbm9uIGF1dGUgZXUsIHBsYXRlYSBz\n"
"Y2VsZXJpc3F1ZSBpZCBpbnRlZ2VyIGRpZ25pc3NpbW9zIGludGVnZXIgc3VzY2lwaXQuClZlaGlj\n"
"dWxhIGVpdXMgbmVjLCBjb252YWxsaXMgbWFlY2VuYXMgbGVjdHVzIHB1cnVzIHF1aXNxdWUgYWxp\n"
"cXVhbSwgZXQgZXQsIGxhY2luaWEgZGlzIGRpZ25pc3NpbSBsb3JlbS4gVm9sdXB0YXRlbSB1dCwg\n"
"dml2ZXJyYSBhZW5lYW4gcGhhc2VsbHVzIGZlbGlzLCBmZXJtZW50dW0gbGlndWxhIGVnZXQgdWxs\n"
"YW1jb3JwZXIgYW1ldCwgbWF1cmlzIGxlbyBsdWN0dXMuIEV0IGV0aWFtIHNlbXBlciBwaGFyZXRy\n"
"YSBuaWJoIG1pIG1hdXJpcy4gSGVuZHJlcml0IHNlZCBhbGlxdWFtLCBlZ2V0IGlkIHRlbGx1cyBt\n"
"YWduYSwgc2VkIGxhb3JlZXQgdWx0cmljZXMgZW5pbSwgbW9sbGlzIHN1c3BlbmRpc3NlIGluLiBF\n"
"dSBudWxsYW0gbG9yZW0gc2l0IGV0aWFtLCBvcmNpIGluIGxpYmVyby4gTnVuYyBpcHN1bSBtYXVy\n"
"aXMgZXQgc2VtIGhhYy4gRGljdHVtIGZhdWNpYnVzIGRpcyB2aXRhZSBpbiB2b2x1dHBhdCBtb3Ji\n"
"aSwgYSBkaWN0dW0sIHF1aXNxdWUgbWF1cmlzLCBhdWN0b3IgbmVjIHZlbCBwZWxsZW50ZXNxdWUg\n"
"dWxsYW1jb3JwZXIgZXRpYW0uIEN1bSBsaWJlcm8gd2lzaSBhY2N1bXNhbiBhbGlxdWFtIGNvbnNl\n"
"Y3RldHVlciB0ZWxsdXMsIHBoYXNlbGx1cyBjdXJhZSwgYXQgaWxsdW0gYW50ZSBwcmV0aXVtIG5p\n"
"YmggbW9yYmkuIFNlZCBibGFuZGl0IHB1bHZpbmFyIHB1bHZpbmFyLiBEaWN0dW0gZXN0IG51bmMg\n"
"ZWxlaWZlbmQsIHZlbGl0IHZlbGl0IHRlbXBvciBhY2N1bXNhbiBsb2JvcnRpcyBsYW9yZWV0IGNv\n"
"bmd1ZS4gU2l0IGFtZXQsIHNlZCBuaWJoIHBvcnJvIG5lcXVlIGF1Y3RvciBoeW1lbmFlb3MgcG9z\n"
"dWVyZSwgb2RpbyBudWxsYSBibGFuZGl0IGNvbmd1ZSBlbGl0LiBJZCBmYXVjaWJ1cyBldCB0ZW1w\n"
"dXMgbWFsZXN1YWRhIHBsYXRlYS4=";
const char *filename = TOPSRCDIR "/tests/nc_b64enc_test_files/long_text";
ck_assert_msg(nc_b64enc_filename(filename, &out[0], out_len), "Error opening file");
@@ -195,4 +191,3 @@ int main(void)
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+18 -1
View File
@@ -15,6 +15,7 @@ check_PROGRAMS = \
%D%/check_daemon \
%D%/check_probes \
%D%/check_ncb64 \
%D%/check_journal \
%D%/check_libtelemetry
dist_check_SCRIPTS = \
@@ -34,7 +35,9 @@ dist_check_SCRIPTS = \
%C%_check_daemon_SOURCES = \
%D%/check_daemon.c \
src/telemdaemon.c \
src/telemdaemon.h
src/telemdaemon.h \
src/journal/journal.c \
src/journal/journal.h
%C%_check_daemon_CFLAGS = \
$(AM_CFLAGS) \
@@ -126,6 +129,20 @@ EXTRA_DIST += \
%D%/nc_b64enc_test_files/foobar \
%D%/nc_b64enc_test_files/long_text
%C%_check_journal_SOURCES = \
%D%/check_journal.c \
src/journal/journal.h \
src/journal/journal.c \
src/util.h \
src/util.c
%C%_check_journal_CFLAGS = \
$(AM_CFLAGS) \
@CHECK_CFLAGS@
%C%_check_journal_LDADD = \
@CHECK_LIBS@
%C%_check_libtelemetry_SOURCES = \
%D%/check_libtelemetry.c \
src/telemetry.c