Compare commits

..
Author SHA1 Message Date
avjarami f5114e9b36 Docs for new features
* Added brief description of new configuration values.
* Documenting new header.
* Describing use of features for local debug.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-04-09 21:03:31 -07:00
avjarami 03fbd65fbd Release v1.16.0
This release contains:

* record_retention_enabled configuration key: when configuration value
  is enabled copies of the last 100 telemetry record payload are kept
  locally.

* record_server_delivery_enabled configuration key: when this key is
  set to true (default value) records are reported to server. When key
  is set to false records are not sent. This configuration can be used
  with record_retention_enabled to retain debug information locally
  only.

* A new switch (-i/--include_record) for 'telemctl journal' command,
  this switch allows the inclusion of record contents when printing
  journal to standard output.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-23 10:22:49 -07:00
avjarami d1f11980be Record retention feature
This change contains:

* New configuration keys: record_retention_enabled and
  record_server_delivery_enabled. These keys are needed to control
  remote delivery of records and record retention. These keys are
  optional to preserve backward compatibility with existing custom
  configurations.

* Record copy implementation. This change allows to save copies of
  records locally when feature is enabled in configuration. This
  operation is independent of record spooling and record reporting
  to remote server.

* New telem_journal argument to allow record payload print from
  local copy (if it exists).

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-23 10:07:47 -07:00
avjarami d75e27a5c4 Fixing memory leaks
This change fixes memory leaks present in journal printing command line
interface and journal printing api.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-14 06:22:59 -07:00
avjarami 9baf5f821e Use shared random id function
Random id generation function in telemetry.c is equivalent to function
in util.c, this change reuses the function defined in util.c in
telemetry.c and removes the no longer needed function.

Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-14 06:22:59 -07:00
avjarami be98da141d Add items to ignore list
Signed-off-by: avjarami <alex.v.jaramillo@intel.com>
2018-03-14 06:22:59 -07:00
23 changed files with 477 additions and 82 deletions
+2
View File
@@ -38,6 +38,7 @@ autoscan.log
configure.scan configure.scan
tprobe tprobe
hprobe hprobe
bertprobe
crashprobe crashprobe
journalprobe journalprobe
telem-record-gen telem-record-gen
@@ -45,6 +46,7 @@ klogscanner
oopsprobe oopsprobe
pstoreclean pstoreclean
pstoreprobe pstoreprobe
telem_journal
make.out make.out
types_c.taghl types_c.taghl
src/recomp.sh src/recomp.sh
+77
View File
@@ -166,6 +166,17 @@ The daemon uses the following configuration options from the configuration file:
threshold has been reached. Currently the options are 'drop' or 'spool', with threshold has been reached. Currently the options are 'drop' or 'spool', with
spool being the default. If spool is chosen, records will be spooled and sent spool being the default. If spool is chosen, records will be spooled and sent
at a later time. at a later time.
* record_retention_enabled: When this key is enabled (true) the daemon saves a
copy of the payload on disk from all valid records. To avoid the excessive use
of disk space only the latest 100 records are kept. The default value for this
configuration key is false.
* record_server_delivery_enabled: This key controls the delivery of records to
```server```, when enabled (default value) the record will be posted to the
address in the configuration file. If this configuration key is disabled (false)
records will not be spooled or posted to backend. This configuration key can
be used in combination with ```record_retention_enabled``` to keep copies of
telemetry records locally only.
Machine id Machine id
--------------------- ---------------------
@@ -194,3 +205,69 @@ You can switch back to the rotating machine id by deleting the override file
and restarting the daemon. You can do a quick test to check that your and restarting the daemon. You can do a quick test to check that your
machine-id has changed by running "hprobe" and verifying that a record has machine-id has changed by running "hprobe" and verifying that a record has
landed on your backend telemetrics server, with the specified machine id. landed on your backend telemetrics server, with the specified machine id.
Event Id
----------------------
This is a 32 character lowercase hexadecimal string i.e. '5de9de8d5f3c6a7d445d75ba01cc3322'.
This header is used to group multiple records by an event id. Before this header
every single record could have been thought of an event, however this is not always
the case. There are "events" that trigger the creation of multiple records (i.e.
updates). The ```event_id``` header was added for probes with the capability to
detect events and group records based on such events. This header was added to
```telem-record-gen``` and can be specified using the ```-e``` (```--event-id```
long form) switch.
```{r, engine=bash, count_lines}
-e, --event-id Event id to use in the record
```
Debugging locally with telemetrics-client
-----------------------------------------------
The function of the telemetrics-client is to handle the transport of information
reported by a probe to a backend (see ```server``` key in configuration). This
information is helpful for developers to debug and fix reported crashes, however
developers not always have access to the backend in these case users can leverage
features added for local debugging. The following is a list of steps to enable
local debug:
* *Enabling record retention*: this step configures telemd to keep
copies of telemetry records locally. To enable record retention set the value of
```record_retention_enabled``` from ```false``` to ```true```. Optionally set
```record_server_delivery_enabled`` to ```false``` to keep records local only.
Remember to restart the daemon after configuration values are updated
(```telemctl restart```).
* *Creating a record*: run ```hprobe``` command to create a record for the purposes
of this step by step guide. Once we have the record or records that you need to
capture locally you can display the data.
* *Displaying record metadata*: telemd keeps metadata of any valid record,
to display this data a new option to telemctl was added ```telemctl journal```.
Assuming that the last record created was the record from previous step `hprobe` we
can use `tail -n 1` to print the last created record only, i.e.
```
$ sudo telemctl journal | tail -n 1
$ org.clearlinux/hello/world Mon 2018-04-02 17:48:01 UTC a19a0d41ba16788881e274b19b8a1be4 5de9de8d5f3c6a7d445d75ba01cc3322 60c014cd-4693-40f1-b334-548cd932949b
```
The headers for the metadata (along with other information) can be printed using the
```-V``` switch with ```telemctl journal``` command, i.e.
```
$ sudo telemctl journal -V | head -n 1
$ Classification Time stamp Record ID Event ID Boot ID
```
* *Displaying record payload*: to print the content of a record payload you can use
the ```-i``` (```--include_record``` long format) option to ```telemctl journal```
command. To print the specific record you created you can use the option ```-r```
(```--record_id``` long format) with the Record Id of the generated record, i.e.
```
$ sudo telemctl journal --record_id a19a0d41ba16788881e274b19b8a1be4 --include_record
$ org.clearlinux/hello/world Mon 2018-04-02 17:48:01 UTC a19a0d41ba16788881e274b19b8a1be4 5de9de8d5f3c6a7d445d75ba01cc3322 60c014cd-4693-40f1-b334-548cd932949b
$ hello
```
+1 -1
View File
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69]) AC_PREREQ([2.69])
AC_INIT([telemetrics-client], [1.15.0], [https://clearlinux.org/]) AC_INIT([telemetrics-client], [1.16.0], [https://clearlinux.org/])
AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects]) AM_INIT_AUTOMAKE([1.14 -Wall -Werror -Wno-extra-portability foreign subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
+1
View File
@@ -64,6 +64,7 @@
/* Journal */ /* Journal */
#define JOURNAL_PATH "/var/log/telemetry/journal" #define JOURNAL_PATH "/var/log/telemetry/journal"
#define JOURNAL_TMPDIR "/var/log/telemetry/" #define JOURNAL_TMPDIR "/var/log/telemetry/"
#define RECORD_RETENTION_DIR "/var/log/telemetry/records"
/* For internal library usage. Bump the version whenever we change the record /* 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 * structure (e.g. adding or removing a header field). Note that the value
+30 -4
View File
@@ -47,8 +47,8 @@ const char *config_key_int[] = { NULL, "record_expiry", "spool_max_size",
"byte_window_length", "record_burst_limit", "byte_window_length", "record_burst_limit",
"byte_burst_limit", NULL }; "byte_burst_limit", NULL };
const char *config_key_bool[] = { NULL, "rate_limit_enabled", const char *config_key_bool[] = { NULL, "rate_limit_enabled", "daemon_recycling_enabled",
"daemon_recycling_enabled", NULL }; "record_retention_enabled", "record_server_delivery_enabled", NULL };
static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL }; static struct configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
@@ -132,8 +132,22 @@ bool read_config_from_file(char *config_file, struct configuration *config)
config->boolValues[i] = true; config->boolValues[i] = true;
} }
} else { } else {
fprintf(stderr, "ERR: missing key with boolean value: %s\n", config_key_bool[i]); /* New configuration keys, CONF_RECORD_RETENTION_ENABLED and CONF_RECORD_SERVER_DELIVERY_ENABLED
return false; * default values, otherwise update will require changes in custom configurations */
if (i == CONF_RECORD_RETENTION_ENABLED) {
#ifdef DEBUG
fprintf(stderr, "WARN: missing boolean optional key: %s\n", config_key_bool[i]);
#endif
config->boolValues[i] = RECORD_RETENTION_ENABLED_DEFAULT;
} else if (i == CONF_RECORD_SERVER_DELIVERY_ENABLED) {
#ifdef DEBUG
fprintf(stderr, "WARN: missing boolean optional key: %s\n", config_key_bool[i]);
#endif
config->boolValues[i] = RECORD_SERVER_DELIVERY_ENABLED_DEFAULT;
} else {
fprintf(stderr, "ERR: missing key with boolean value: %s\n", config_key_bool[i]);
return false;
}
} }
} }
} }
@@ -356,4 +370,16 @@ bool daemon_recycling_enabled_config(void)
initialize_config(); initialize_config();
return config.boolValues[CONF_DAEMON_RECYCLING_ENABLED]; return config.boolValues[CONF_DAEMON_RECYCLING_ENABLED];
} }
bool record_retention_enabled_config(void)
{
initialize_config();
return config.boolValues[CONF_RECORD_RETENTION_ENABLED];
}
bool record_server_delivery_enabled_config(void)
{
initialize_config();
return config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED];
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */ /* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+10
View File
@@ -19,6 +19,8 @@
#include <stdint.h> #include <stdint.h>
#define TM_MAX_WINDOW_LENGTH (1 /*h*/ * 60 /*m*/) #define TM_MAX_WINDOW_LENGTH (1 /*h*/ * 60 /*m*/)
#define RECORD_RETENTION_ENABLED_DEFAULT false
#define RECORD_SERVER_DELIVERY_ENABLED_DEFAULT true
enum config_str_keys { enum config_str_keys {
CONF_STR_MIN = 0, CONF_STR_MIN = 0,
@@ -47,6 +49,8 @@ enum config_bool_keys {
CONF_BOOL_MIN = 0, CONF_BOOL_MIN = 0,
CONF_RATE_LIMIT_ENABLED, CONF_RATE_LIMIT_ENABLED,
CONF_DAEMON_RECYCLING_ENABLED, CONF_DAEMON_RECYCLING_ENABLED,
CONF_RECORD_RETENTION_ENABLED,
CONF_RECORD_SERVER_DELIVERY_ENABLED,
CONF_BOOL_MAX CONF_BOOL_MAX
}; };
@@ -117,4 +121,10 @@ const char *get_tidheader_config(void);
/* Gets whether recycling is enabled */ /* Gets whether recycling is enabled */
bool daemon_recycling_enabled_config(void); bool daemon_recycling_enabled_config(void);
/* Gets whether local retention of records is enabled */
bool record_retention_enabled_config(void);
/* Gets whether records should be sent to server_addr */
bool record_server_delivery_enabled_config(void);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */ /* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+46
View File
@@ -0,0 +1,46 @@
[settings]
server=http://127.0.0.1
cainfo=/tmp/cacert.crt
tidheader=X-Telemetry-TID:\s6907c830-eed9-4ce9-81ae-76daf8d88f0f
socket_path=/tmp/test_telem_socket
#record expiry time in minutes
record_expiry=1200
spool_dir=/tmp/spool
#maximum size of the spool directory in KB
spool_max_size=1024
#time in seconds for processing spool
spool_process_time=900
#rate limiting record burst limit
record_burst_limit=100
#rate limiting record window length
record_window_length = 15
#rate limiting byte burst limit
byte_burst_limit = 1000
#rate limiting byte window length
byte_window_length=20
#rate limit enabled
rate_limit_enabled=true
#rate limit strategy if record not send
rate_limit_strategy=spool
#daemon recycling enabled
daemon_recycling_enabled=true
#server delivery disable
record_server_delivery_enabled=false
#local copy enabled
record_retention_enabled=true
+1
View File
@@ -11,6 +11,7 @@ EXTRA_DIST += \
%D%/40-core-ulimit.conf \ %D%/40-core-ulimit.conf \
%D%/40-crash-probe.conf.in \ %D%/40-crash-probe.conf.in \
%D%/example.conf \ %D%/example.conf \
%D%/example.1.conf \
%D%/hprobe.service.in \ %D%/hprobe.service.in \
%D%/hprobe.timer \ %D%/hprobe.timer \
%D%/bert-probe.service.in \ %D%/bert-probe.service.in \
+1
View File
@@ -1,6 +1,7 @@
d @localstatedir@/lib/telemetry 0755 telemetry telemetry - d @localstatedir@/lib/telemetry 0755 telemetry telemetry -
d @localstatedir@/spool/telemetry 0750 telemetry telemetry - d @localstatedir@/spool/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry 0750 telemetry telemetry - d @localstatedir@/log/telemetry 0750 telemetry telemetry -
d @localstatedir@/log/telemetry/records 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry 0750 telemetry telemetry - d @localstatedir@/cache/telemetry 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/oops 0750 telemetry telemetry - d @localstatedir@/cache/telemetry/oops 0750 telemetry telemetry -
d @localstatedir@/cache/telemetry/pstore 0750 telemetry telemetry - d @localstatedir@/cache/telemetry/pstore 0750 telemetry telemetry -
+10
View File
@@ -55,3 +55,13 @@ rate_limit_strategy=spool
# has not any client nor spool data, then it exits. # has not any client nor spool data, then it exits.
# this is to ensure that latest code runs. # this is to ensure that latest code runs.
daemon_recycling_enabled=true daemon_recycling_enabled=true
# record server delivery enabled - when enabled records will be delivered
# to server otherwise records will be ignored. This configuration can be used
# with 'record_retention_enable' configuration value to keep records local only.
record_server_delivery_enabled=true
# record retention enabled - when enabled a copy of reported telemetry records
# will be kept locally. This configuration combined with 'record_server_delivery_enabled'
# value can be used to keep records local only.
record_retention_enabled=false
+14 -13
View File
@@ -27,12 +27,13 @@
void print_usage(void) void print_usage(void)
{ {
printf(" Usage\n"); printf(" Usage\n");
printf(" -r, --record_id Print record to stdout\n"); printf(" -r, --record_id Print record with specific record_id\n");
printf(" -e, --event_id List records with specific event_id\n"); printf(" -e, --event_id Print records with specific event_id\n");
printf(" -c, --classification List records with specific classification\n"); printf(" -c, --classification Print records with specific classification\n");
printf(" -b, --boot_id List records with specific boot_id\n"); printf(" -b, --boot_id Print records with specific boot_id\n");
printf(" -V, --verbose Verbose output\n"); printf(" -i, --include_record Include record content\n");
printf(" -h, --help Display this help message\n"); printf(" -V, --verbose Verbose output\n");
printf(" -h, --help Display this help message\n");
} }
int main(int argc, char **argv) int main(int argc, char **argv)
@@ -41,6 +42,7 @@ int main(int argc, char **argv)
int rc = EXIT_SUCCESS; int rc = EXIT_SUCCESS;
int count = 0; int count = 0;
int verbose_output = 0; int verbose_output = 0;
int record = 0;
char *boot_id = NULL; char *boot_id = NULL;
char *record_id = NULL; char *record_id = NULL;
char *event_id = NULL; char *event_id = NULL;
@@ -56,11 +58,12 @@ int main(int argc, char **argv)
{ "classification", 1, NULL, 'c' }, { "classification", 1, NULL, 'c' },
{ "boot_id", 1, NULL, 'b' }, { "boot_id", 1, NULL, 'b' },
{ "verbose", 0, NULL, 'V' }, { "verbose", 0, NULL, 'V' },
{ "include_record", 0, NULL, 'i' },
{ "help", 0, NULL, 'h' }, { "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
while ((c = getopt_long(argc, argv, "r:e:c:b:Vh", opts, &opt_index)) != -1) { while ((c = getopt_long(argc, argv, "r:e:c:b:Vih", opts, &opt_index)) != -1) {
switch (c) { switch (c) {
case 'r': case 'r':
record_id = optarg; record_id = optarg;
@@ -77,6 +80,9 @@ int main(int argc, char **argv)
case 'V': case 'V':
verbose_output = 1; verbose_output = 1;
break; break;
case 'i':
record = 1;
break;
case 'h': case 'h':
print_usage(); print_usage();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@@ -92,7 +98,7 @@ int main(int argc, char **argv)
fprintf(stdout, "%-30s %-27s %-32s %-32s %-36s\n", "Classification", "Time stamp", fprintf(stdout, "%-30s %-27s %-32s %-32s %-36s\n", "Classification", "Time stamp",
"Record ID", "Event ID", "Boot ID"); "Record ID", "Event ID", "Boot ID");
} }
count = print_journal(telem_journal, classification, record_id, event_id, boot_id); count = print_journal(telem_journal, classification, record_id, event_id, boot_id, record);
if (verbose_output) { if (verbose_output) {
fprintf(stdout, "Total records: %d\n", count); fprintf(stdout, "Total records: %d\n", count);
} }
@@ -102,10 +108,5 @@ int main(int argc, char **argv)
rc = EXIT_FAILURE; rc = EXIT_FAILURE;
} }
free(classification);
free(record_id);
free(event_id);
free(boot_id);
return rc; return rc;
} }
+68 -9
View File
@@ -19,6 +19,7 @@
#define ID_LEN 32 #define ID_LEN 32
#define BOOTID_LEN 37 // Includes the \n character at the end #define BOOTID_LEN 37 // Includes the \n character at the end
#define BOOTID_FILE "/proc/sys/kernel/random/boot_id" #define BOOTID_FILE "/proc/sys/kernel/random/boot_id"
#define MID_BUFF 1024
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -31,6 +32,7 @@
#include <unistd.h> #include <unistd.h>
#include <assert.h> #include <assert.h>
#include "log.h"
#include "util.h" #include "util.h"
#include "common.h" #include "common.h"
#include "journal.h" #include "journal.h"
@@ -246,17 +248,20 @@ static int read_boot_id(char buff[])
* *
* @param n Number of lines to skip. * @param n Number of lines to skip.
* @param fptr A file pointer. * @param fptr A file pointer.
* @param found_record A pointer to a function to be called
* every time a valid record is found, if
* pointer is not set to null.
* *
* @returns 0 on success, on failure -1 if n > existing * @returns 0 on success, on failure -1 if n > existing
* lines in file or errno if failure happens * lines in file or errno if failure happens
* during file operation. * during file operation.
*
*/ */
static int skip_n_lines(int n, FILE *fptr) static int skip_n_lines(int n, FILE *fptr, int (*found_record)(char *))
{ {
int rc = 0; int rc = 0;
size_t len = 0; size_t len = 0;
char *line = NULL; char *line = NULL;
struct JournalEntry *entry = NULL;
if (fptr == NULL) { if (fptr == NULL) {
#ifdef DEBUG #ifdef DEBUG
@@ -275,6 +280,14 @@ static int skip_n_lines(int n, FILE *fptr)
rc = -1; rc = -1;
break; break;
} }
if (found_record != NULL) {
deserialize_journal_entry(line, &entry);
if (entry) {
found_record(entry->record_id);
free_journal_entry(entry);
entry = NULL;
}
}
} }
free(line); free(line);
@@ -361,6 +374,8 @@ TelemJournal *open_journal(const char *journal_file)
telem_journal->boot_id = strndup(boot_id, BOOTID_LEN - 1); telem_journal->boot_id = strndup(boot_id, BOOTID_LEN - 1);
telem_journal->record_count = get_record_count(telem_journal); telem_journal->record_count = get_record_count(telem_journal);
telem_journal->record_count_limit = RECORD_LIMIT; telem_journal->record_count_limit = RECORD_LIMIT;
telem_journal->latest_record_id = NULL;
telem_journal->prune_entry_callback = NULL;
#ifdef DEBUG #ifdef DEBUG
printf("Records in db: %d\n", telem_journal->record_count); printf("Records in db: %d\n", telem_journal->record_count);
@@ -375,6 +390,7 @@ void close_journal(TelemJournal *telem_journal)
if (telem_journal) { if (telem_journal) {
free(telem_journal->boot_id); free(telem_journal->boot_id);
free(telem_journal->journal_file); free(telem_journal->journal_file);
free(telem_journal->latest_record_id);
fclose(telem_journal->fptr); fclose(telem_journal->fptr);
free(telem_journal); free(telem_journal);
} }
@@ -393,9 +409,44 @@ static int is_class_prefix(char *class)
return (strcmp((char *)(class + class_len - 2), "/*") == 0) ? 1 : 0; return (strcmp((char *)(class + class_len - 2), "/*") == 0) ? 1 : 0;
} }
/**
* Print records content
*
* @param record_id Unique record identifier
*
*/
static void print_record(char *record_id)
{
int rc = 0;
size_t read = 0;
char buff[MID_BUFF] = { '\0' };
char *filepath = NULL;
FILE *recordfp = NULL;
rc = asprintf(&filepath, "%s/%s", RECORD_RETENTION_DIR, record_id);
if (rc == -1) {
// just bail out, there are worse problems
return;
}
recordfp = fopen(filepath, "r");
if (!recordfp) {
telem_perror("Error when opening a record to print");
return;
}
while ((read = fread(buff, 1, sizeof(buff), recordfp)) > 0) {
fwrite(buff, 1, read, stdout);
}
free(filepath);
fclose(recordfp);
}
/* Exported function */ /* Exported function */
int print_journal(TelemJournal *telem_journal, char *classification, int print_journal(TelemJournal *telem_journal, char *classification,
char *record_id, char *event_id, char *boot_id) char *record_id, char *event_id, char *boot_id,
bool include_record)
{ {
int n = 0; int n = 0;
int rc = 0; int rc = 0;
@@ -419,7 +470,7 @@ int print_journal(TelemJournal *telem_journal, char *classification,
// Advance file pointer to line n // Advance file pointer to line n
n = telem_journal->record_count - telem_journal->record_count_limit; n = telem_journal->record_count - telem_journal->record_count_limit;
if (n > 0) { if (n > 0) {
rc = skip_n_lines(n, journal_fileptr); rc = skip_n_lines(n, journal_fileptr, NULL);
if (rc == -1) { if (rc == -1) {
return rc; return rc;
} else if (rc != 0) { } else if (rc != 0) {
@@ -432,13 +483,13 @@ int print_journal(TelemJournal *telem_journal, char *classification,
if (entry) { if (entry) {
/* filter entry out if one is provided */ /* filter entry out if one is provided */
if (record_id != NULL && strcmp(entry->record_id, record_id) != 0) { if (record_id != NULL && strcmp(entry->record_id, record_id) != 0) {
continue; goto skip_print;
} }
if (boot_id != NULL && strcmp(entry->boot_id, boot_id) != 0) { if (boot_id != NULL && strcmp(entry->boot_id, boot_id) != 0) {
continue; goto skip_print;
} }
if (event_id != NULL && strcmp(entry->event_id, event_id) != 0) { if (event_id != NULL && strcmp(entry->event_id, event_id) != 0) {
continue; goto skip_print;
} }
// In the case of class checking prefixes is an option // In the case of class checking prefixes is an option
if (classification != NULL) { if (classification != NULL) {
@@ -456,10 +507,15 @@ int print_journal(TelemJournal *telem_journal, char *classification,
if (strftime(str_time, sizeof(str_time), "%a %Y-%m-%d %H:%M:%S %Z", &ts) == 0) { if (strftime(str_time, sizeof(str_time), "%a %Y-%m-%d %H:%M:%S %Z", &ts) == 0) {
continue; continue;
} }
/* print record metadata */
fprintf(stdout, "%-30s %s %s %s %s\n", entry->classification, str_time, entry->record_id, entry->event_id, entry->boot_id); fprintf(stdout, "%-30s %s %s %s %s\n", entry->classification, str_time, entry->record_id, entry->event_id, entry->boot_id);
/* print record content */
if (include_record) {
print_record(entry->record_id);
}
count++; count++;
} }
skip_print:
free_journal_entry(entry); free_journal_entry(entry);
} }
free(line); free(line);
@@ -562,6 +618,9 @@ int new_journal_entry(TelemJournal *telem_journal, char *classification,
#endif #endif
} }
free(telem_journal->latest_record_id);
telem_journal->latest_record_id = strdup(entry->record_id);
quit: quit:
free_journal_entry(entry); free_journal_entry(entry);
@@ -585,7 +644,7 @@ int prune_journal(struct TelemJournal *telem_journal, char *tmp_dir)
count = telem_journal->record_count - telem_journal->record_count_limit; count = telem_journal->record_count - telem_journal->record_count_limit;
// jump to line# count // jump to line# count
if ((rc = skip_n_lines(count, telem_journal->fptr)) != 0) { if ((rc = skip_n_lines(count, telem_journal->fptr, telem_journal->prune_entry_callback)) != 0) {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "Error: skipping %d journal lines\n", count); fprintf(stderr, "Error: skipping %d journal lines\n", count);
#endif #endif
+6 -1
View File
@@ -22,6 +22,7 @@
#include <time.h> #include <time.h>
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
/* Journal entry type */ /* Journal entry type */
typedef struct JournalEntry { typedef struct JournalEntry {
@@ -37,8 +38,10 @@ typedef struct TelemJournal {
FILE *fptr; FILE *fptr;
char *journal_file; char *journal_file;
char *boot_id; char *boot_id;
char *latest_record_id;
int record_count; int record_count;
int record_count_limit; int record_count_limit;
int (*prune_entry_callback)(char *);
} TelemJournal; } TelemJournal;
/** /**
@@ -75,12 +78,14 @@ void close_journal(TelemJournal *telem_journal);
* @param record_id A pointer to string used as record_id 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 event_id A pointer to string used as event_id filter.
* @param boot_id A pointer to string used as boor_id filter. * @param boot_id A pointer to string used as boor_id filter.
* @param include_record A flag to control record content print.
* *
* @return the number of lines printed to stdout on success, -1 * @return the number of lines printed to stdout on success, -1
* on failure. * on failure.
*/ */
int print_journal(TelemJournal *telem_journal, char *classification, int print_journal(TelemJournal *telem_journal, char *classification,
char *record_id, char *event_id, char *boot_id); char *record_id, char *event_id, char *boot_id,
bool include_record);
/** /**
* Creates a new entry in journal. * Creates a new entry in journal.
+3 -1
View File
@@ -7,7 +7,9 @@ bin_PROGRAMS = %D%/telemd
%D%/spool.c \ %D%/spool.c \
%D%/spool.h \ %D%/spool.h \
%D%/journal/journal.c \ %D%/journal/journal.c \
%D%/journal/journal.h %D%/journal/journal.h \
%D%/retention.c \
%D%/retention.h
%C%_telemd_LDADD = $(CURL_LIBS) \ %C%_telemd_LDADD = $(CURL_LIBS) \
%D%/libtelem-shared.la %D%/libtelem-shared.la
+6
View File
@@ -49,6 +49,7 @@
#include "log.h" #include "log.h"
#include "telemdaemon.h" #include "telemdaemon.h"
#include "configuration.h" #include "configuration.h"
#include "retention.h"
#include "spool.h" #include "spool.h"
/* /*
@@ -122,6 +123,11 @@ int main(int argc, char **argv)
} }
initialize_daemon(&daemon); initialize_daemon(&daemon);
/* Register record retention delete action as a callback to prune entry */
if (daemon.record_journal) {
daemon.record_journal->prune_entry_callback = &delete_record_by_id;
}
sigemptyset(&mask); sigemptyset(&mask);
if (sigaddset(&mask, SIGHUP) != 0) { if (sigaddset(&mask, SIGHUP) != 0) {
+1 -1
View File
@@ -69,7 +69,7 @@ static void print_help(void)
printf(" -p, --payload Record body (max size = 8k)\n"); printf(" -p, --payload Record body (max size = 8k)\n");
printf(" -P, --payload-file File to read payload from\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(" -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(" -e, --event-id Event id to use in the record\n");
printf("\n"); printf("\n");
} }
+46
View File
@@ -0,0 +1,46 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "log.h"
#include "common.h"
int delete_record_by_id(char *record_id)
{
int ret = 0;
char *record_path = NULL;
ret = asprintf(&record_path, "%s/%s", RECORD_RETENTION_DIR, record_id);
if (ret == -1) {
return 1;
}
ret = unlink(record_path);
if (ret == -1) {
telem_perror("Error deleting saved record");
}
free(record_path);
return 0;
}
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+27
View File
@@ -0,0 +1,27 @@
/*
* 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
/**
* Delete record identified by record unique id
*
* @param record_id Unique identifier of entry
*
*/
int delete_record_by_id(char *record_id);
/* vi: set ts=8 sw=8 sts=4 et tw=80 cino=(0: */
+70 -13
View File
@@ -34,6 +34,7 @@
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "configuration.h" #include "configuration.h"
#include "retention.h"
void initialize_daemon(TelemDaemon *daemon) void initialize_daemon(TelemDaemon *daemon)
{ {
@@ -46,6 +47,7 @@ void initialize_daemon(TelemDaemon *daemon)
daemon->is_spool_valid = false; daemon->is_spool_valid = false;
daemon->record_journal = open_journal(JOURNAL_PATH); daemon->record_journal = open_journal(JOURNAL_PATH);
initialize_rate_limit(daemon); initialize_rate_limit(daemon);
initialize_record_delivery(daemon);
daemon->current_spool_size = 0; daemon->current_spool_size = 0;
} }
@@ -63,6 +65,12 @@ void initialize_rate_limit(TelemDaemon *daemon)
daemon->rate_limit_strategy = rate_limit_strategy_config(); daemon->rate_limit_strategy = rate_limit_strategy_config();
} }
void initialize_record_delivery(TelemDaemon *daemon)
{
daemon->record_retention_enabled = record_retention_enabled_config();
daemon->record_server_delivery_enabled = record_server_delivery_enabled_config();
}
client *add_client(client_list_head *client_head, int fd) client *add_client(client_list_head *client_head, int fd)
{ {
client *cl; client *cl;
@@ -264,6 +272,21 @@ void machine_id_replace(char **machine_header, char *machine_id_override)
free(old_header); free(old_header);
} }
void save_entry_to_journal(TelemDaemon *daemon, time_t t_stamp, char *headers[])
{
char *classification_value;
char *event_id_value;
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, t_stamp, 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);
}
void process_record(TelemDaemon *daemon, client *cl) void process_record(TelemDaemon *daemon, client *cl)
{ {
int i = 0; int i = 0;
@@ -281,10 +304,6 @@ void process_record(TelemDaemon *daemon, client *cl)
bool byte_check_passed = true; bool byte_check_passed = true;
bool record_burst_enabled = true; bool record_burst_enabled = true;
bool byte_burst_enabled = true; bool byte_burst_enabled = true;
/* values for journal */
char *classification_value;
char *event_id_value;
/* Gets the current minute of time */ /* Gets the current minute of time */
time_t temp = time(NULL); time_t temp = time(NULL);
struct tm *tm_s = localtime(&temp); struct tm *tm_s = localtime(&temp);
@@ -317,14 +336,20 @@ void process_record(TelemDaemon *daemon, client *cl)
body = msg + header_size; body = msg + header_size;
/* Save record in journal */ /* Save record in journal */
if (get_header_value(headers[TM_CLASSIFICATION], &classification_value) && save_entry_to_journal(daemon, temp, headers);
get_header_value(headers[TM_EVENT_ID], &event_id_value)) {
if (new_journal_entry(daemon->record_journal, classification_value, temp, event_id_value) != 0) { /* Save local copy */
telem_log(LOG_INFO, "new_journal_entry in process_record: failed saving record entry\n"); if (daemon->record_retention_enabled) {
} save_local_copy(daemon, body);
}
/* Bail out if server delivery is not enabled */
if (!daemon->record_server_delivery_enabled) {
#ifdef DEBUG
fprintf(stdout, "process_record: record server delivery disabled\n");
#endif
goto end;
} }
free(classification_value);
free(event_id_value);
if (inside_direct_spool_window(daemon, time(NULL))) { if (inside_direct_spool_window(daemon, time(NULL))) {
telem_log(LOG_INFO, "process_record: delivering directly to spool\n"); telem_log(LOG_INFO, "process_record: delivering directly to spool\n");
@@ -357,12 +382,12 @@ void process_record(TelemDaemon *daemon, client *cl)
daemon->rate_limit_enabled = false; daemon->rate_limit_enabled = false;
} }
} }
/* Sends record if rate limiting is disabled, or all checks passed */ /* Sends record if rate limiting is disabled, or all checks passed */
if (!daemon->rate_limit_enabled || (record_check_passed && byte_check_passed)) { if (!daemon->rate_limit_enabled || (record_check_passed && byte_check_passed)) {
/* Send the record as https post */ /* Send the record as https post */
record_sent = post_record_ptr(headers, body, true); record_sent = post_record_ptr(headers, body, true);
} }
// Get rate-limit strategy // Get rate-limit strategy
do_spool = spool_strategy_selected(daemon); do_spool = spool_strategy_selected(daemon);
@@ -558,6 +583,39 @@ bool post_record_http(char *headers[], char *body, bool spool)
return res ? false : true; return res ? false : true;
} }
void save_local_copy(TelemDaemon *daemon, char *body)
{
int ret = 0;
char *tmpbuf = NULL;
FILE *tmpfile = NULL;
if (daemon == NULL || daemon->record_journal == NULL ||
daemon->record_journal->latest_record_id == NULL) {
return;
}
ret = asprintf(&tmpbuf, "%s/%s", RECORD_RETENTION_DIR,
daemon->record_journal->latest_record_id);
if (ret == -1) {
telem_log(LOG_ERR, "Failed to allocate memory for record full path, aborting\n");
return;
}
tmpfile = fopen(tmpbuf, "w");
if (!tmpfile) {
telem_perror("Error opening local record copy temp file");
goto save_err;
}
// Save body
fprintf(tmpfile, "%s\n", body);
fclose(tmpfile);
save_err:
free(tmpbuf);
return;
}
void spool_record(TelemDaemon *daemon, char *headers[], char *body) void spool_record(TelemDaemon *daemon, char *headers[], char *body)
{ {
int ret = 0; int ret = 0;
@@ -599,7 +657,6 @@ void spool_record(TelemDaemon *daemon, char *headers[], char *body)
goto spool_err; goto spool_err;
} }
//fp = fopen(spool_dir_path, const char *mode);
tmpfile = fdopen(tmpfd, "a"); tmpfile = fdopen(tmpfd, "a");
if (!tmpfile) { if (!tmpfile) {
telem_perror("Error opening temp file"); telem_perror("Error opening temp file");
+20 -2
View File
@@ -69,10 +69,12 @@ typedef struct TelemDaemon {
int64_t byte_burst_limit; int64_t byte_burst_limit;
int byte_window_length; int byte_window_length;
const char *rate_limit_strategy; const char *rate_limit_strategy;
/* Spool configuration */
bool is_spool_valid; bool is_spool_valid;
long current_spool_size; long current_spool_size;
/* Record local copy and delivery */
bool record_retention_enabled;
bool record_server_delivery_enabled;
char *machine_id_override; char *machine_id_override;
} TelemDaemon; } TelemDaemon;
@@ -90,6 +92,14 @@ void initialize_daemon(TelemDaemon *daemon);
*/ */
void initialize_rate_limit(TelemDaemon *daemon); void initialize_rate_limit(TelemDaemon *daemon);
/**
* Initialize record delivery to remote and locally
* in the daemon
*
* @param daemon A pointer to the daemon structure
*/
void initialize_record_delivery(TelemDaemon *daemon);
/** /**
* Add poll fd struct to the array of pollfds. * Add poll fd struct to the array of pollfds.
* *
@@ -271,6 +281,14 @@ bool post_record_http(char *header_values[], char *body, bool spool);
*/ */
void spool_record(TelemDaemon * daemon, char *headers[], char *body); void spool_record(TelemDaemon * daemon, char *headers[], char *body);
/**
* Save a copy of the record payload locally
*
* @param body The payload of the record
*
*/
void save_local_copy(TelemDaemon *daemon, char *body);
/** /**
* Get random machine id stored in file * Get random machine id stored in file
* *
+3 -33
View File
@@ -615,37 +615,6 @@ static int set_bios_version_header(struct telem_ref *t_ref)
return status; 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 * 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 can share. This means that one event can lead to multiple
@@ -659,9 +628,9 @@ static int gen_event_id(char *buff)
static int set_event_id_header(struct telem_ref *t_ref) static int set_event_id_header(struct telem_ref *t_ref)
{ {
int rc = 0; int rc = 0;
char buff[33]; char *buff = NULL;
rc = gen_event_id(buff); rc = get_random_id(&buff);
if (rc == 0) { if (rc == 0) {
rc = set_header( rc = set_header(
@@ -669,6 +638,7 @@ static int set_event_id_header(struct telem_ref *t_ref)
TM_EVENT_ID_STR, buff, TM_EVENT_ID_STR, buff,
&(t_ref->record->header_size)); &(t_ref->record->header_size));
} }
free(buff);
return rc; return rc;
} }
+30
View File
@@ -55,6 +55,34 @@ START_TEST(check_read_valid_config)
} }
END_TEST END_TEST
START_TEST(check_read_valid_config_defaults)
{
char *config_file = TOPSRCDIR "/src/data/example.conf";
configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
int ret = read_config_from_file(config_file, &config);
ck_assert(ret == true);
// RECORD_RETENTION_ENABLED_DEFAULT = false
ck_assert(config.boolValues[CONF_RECORD_RETENTION_ENABLED] == RECORD_RETENTION_ENABLED_DEFAULT);
// RECORD_SERVER_DELIVERY_ENABLED_DEFAULT = true
ck_assert(config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED] == RECORD_SERVER_DELIVERY_ENABLED_DEFAULT);
}
END_TEST
START_TEST(check_read_valid_config_record_retention_delivery)
{
char *config_file = TOPSRCDIR "/src/data/example.1.conf";
configuration config = { { 0 }, { 0 }, { 0 }, false, NULL };
int ret = read_config_from_file(config_file, &config);
ck_assert(ret == true);
ck_assert(config.boolValues[CONF_RECORD_RETENTION_ENABLED] == true);
ck_assert(config.boolValues[CONF_RECORD_SERVER_DELIVERY_ENABLED] == false);
}
END_TEST
START_TEST(check_config_initialised) START_TEST(check_config_initialised)
{ {
char *config_file = ABSTOPSRCDIR "/src/data/example.conf"; char *config_file = ABSTOPSRCDIR "/src/data/example.conf";
@@ -89,6 +117,8 @@ Suite *config_suite(void)
TCase *t = tcase_create("config"); TCase *t = tcase_create("config");
tcase_add_test(t, check_read_config_for_invalid_file); tcase_add_test(t, check_read_config_for_invalid_file);
tcase_add_test(t, check_read_valid_config); tcase_add_test(t, check_read_valid_config);
tcase_add_test(t, check_read_valid_config_defaults);
tcase_add_test(t, check_read_valid_config_record_retention_delivery);
tcase_add_test(t, check_config_initialised); tcase_add_test(t, check_config_initialised);
// add more TCases here // add more TCases here
+4 -4
View File
@@ -159,28 +159,28 @@ void journal_entry_setup(void)
START_TEST(check_journal_print) START_TEST(check_journal_print)
{ {
int count = 0; int count = 0;
count = print_journal(journal, NULL, NULL, NULL, NULL); count = print_journal(journal, NULL, NULL, NULL, NULL, 0);
ck_assert_int_eq(count, K + 2); ck_assert_int_eq(count, K + 2);
} }
END_TEST END_TEST
START_TEST(check_journal_filter_by_class) START_TEST(check_journal_filter_by_class)
{ {
int result = print_journal(journal, "a/b/c", NULL, NULL, NULL); int result = print_journal(journal, "a/b/c", NULL, NULL, NULL, 0);
ck_assert_int_eq(result, 1); ck_assert_int_eq(result, 1);
} }
END_TEST END_TEST
START_TEST(check_journal_filter_by_class_prefix) START_TEST(check_journal_filter_by_class_prefix)
{ {
int result = print_journal(journal, "a/b/*", NULL, NULL, NULL); int result = print_journal(journal, "a/b/*", NULL, NULL, NULL, 0);
ck_assert_int_eq(result, 2); ck_assert_int_eq(result, 2);
} }
END_TEST END_TEST
START_TEST(check_journal_filter_by_event_id) START_TEST(check_journal_filter_by_event_id)
{ {
int result = print_journal(journal, NULL, NULL, eid, NULL); int result = print_journal(journal, NULL, NULL, eid, NULL, 0);
ck_assert(result == 2); ck_assert(result == 2);
} }
END_TEST END_TEST